Skip to content

Commit f408c94

Browse files
committed
wip
1 parent ccabb9f commit f408c94

File tree

11 files changed

+338
-15
lines changed

11 files changed

+338
-15
lines changed

frontend/courses/courses.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
{ "id": "poly", "title": "Polygon and Polyline" },
2727
{ "id": "fill", "title": "Stroke and Fill" },
2828
{ "id": "linestyle", "title": "Line Styles" },
29-
{ "id": "text", "title": "Text" }
29+
{ "id": "text", "title": "Text" },
30+
{ "id": "poly", "title": "Polygon and Polyline" },
31+
{ "id": "ellipse", "title": "Ellipse" },
32+
{ "id": "curve", "title": "Curve" }
3033
]
3134
},
3235
{
@@ -35,6 +38,7 @@
3538
"emoji": "🤹‍♀️",
3639
"units": [
3740
{ "id": "movingdot", "title": "Moving Red Dot" },
41+
{ "id": "bounce", "title": "Bounce" },
3842
{ "id": "juggle", "title": "Juggle" },
3943
{ "id": "splashtrig", "title": "Splash of Trig" },
4044
{ "id": "draw", "title": "Draw" }
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
background := "hsl(0deg 0% 0% / 10%)"
2+
x := 10
3+
y := 50
4+
s := 1
5+
width 1
6+
fill background
7+
stroke "red"
8+
9+
on animate
10+
clear background
11+
move x y
12+
circle 10
13+
x = x + s
14+
if x < 10 or x > 90
15+
s = -s
16+
end
17+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print "todo"
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
labels := ["x" "y" "radX" "radY" "rot°" "start°" "end°"]
2+
vals := [50 60 30 17 5 0 180]
3+
cnt := len vals
4+
cur := 1
5+
w := 11
6+
7+
print "Watermelon, anyone?"
8+
print "🍉"
9+
print "Use arrow keys or drag numbers"
10+
print "for your perfect piece."
11+
12+
draw
13+
14+
func draw
15+
clear "hsl(210deg 5% 15%)" // near black
16+
draw_ellipse
17+
draw_text
18+
draw_highlight cur
19+
end
20+
21+
func draw_ellipse
22+
width 2
23+
fill "red"
24+
stroke "darkgreen"
25+
ellipse vals[0] vals[1] vals[2] vals[3] vals[4] vals[5] vals[6]
26+
end
27+
28+
//TODO: issues!! w := 10 - need better eval error exposure
29+
func draw_text
30+
font "italic 300 2rem \"Fira Code\", monospace"
31+
textsize 2.7
32+
width 0.2
33+
fill "hsl(210deg 13% 72%)" // light grey
34+
move 2 15
35+
text "// "
36+
for i := range (len labels)
37+
move i*w+20 15
38+
text (sprintf "%5s" labels[i])
39+
end
40+
font "2rem \"Fira Code\", monospace"
41+
textsize 2.7
42+
fill "hsl(27deg 100% 74%)" // orange
43+
move 2 10
44+
text "ellipse"
45+
fill "hsl(204deg 100% 75%)" // ligth blue
46+
for i := range (len labels)
47+
move i*w+20 10
48+
text (sprintf "%5.0f" vals[i])
49+
end
50+
51+
end
52+
53+
func draw_highlight i:num
54+
fill "none"
55+
stroke "white"
56+
width 0.4
57+
x := i * w + 20
58+
move x-1 9
59+
rect 10 4
60+
end
61+
62+
// keyboard interaction
63+
on key k:string
64+
if k == "ArrowLeft"
65+
cur = (cur + cnt - 1) % cnt
66+
else if k == "ArrowRight"
67+
cur = (cur + 1) % cnt
68+
else if k == "ArrowUp"
69+
vals[cur] = vals[cur] + 1
70+
else if k == "ArrowDown"
71+
vals[cur] = max 0 vals[cur]-1
72+
end
73+
draw
74+
end
75+
76+
// pointer interaction
77+
78+
dragy := -1
79+
deltay := 0
80+
81+
on down x:num y:num
82+
if y < 20 and x > 18
83+
cur = min (floor (x - 18)/w) (len labels)-1
84+
dragy = y
85+
draw
86+
end
87+
end
88+
89+
on up _:num _:num
90+
dragy = -1
91+
deltay = 0
92+
end
93+
94+
on move _:num y:num
95+
if dragy == -1
96+
return
97+
end
98+
deltay = y - dragy
99+
if deltay < 0
100+
deltay = floor deltay
101+
else
102+
deltay = ceil deltay
103+
end
104+
end
105+
106+
on animate
107+
if deltay == 0
108+
return
109+
end
110+
if cur < 4
111+
val := vals[cur] + deltay / 10
112+
vals[cur] = max 0 (min 100 val)
113+
else
114+
vals[cur] = vals[cur] + deltay / 5
115+
end
116+
draw
117+
end

frontend/index.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ function newEvyGo() {
5757
clear,
5858
// advanced canvas
5959
poly,
60+
ellipse,
61+
curve,
6062
stroke,
6163
fill,
6264
dash,
@@ -97,6 +99,8 @@ function needsCanvas(f) {
9799
f.colour ||
98100
f.clear ||
99101
f.poly ||
102+
f.ellipse ||
103+
f.curve ||
100104
f.stroke ||
101105
f.fill ||
102106
f.dash ||
@@ -541,6 +545,90 @@ function parsePoints(s) {
541545
return points
542546
}
543547

548+
// ellipse is exported to evy go/wasm.
549+
// see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse
550+
function ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle) {
551+
const rad = Math.PI / 180
552+
const { ctx, fill, stroke } = canvas
553+
ctx.beginPath()
554+
ctx.ellipse(
555+
transformX(x),
556+
transformY(y),
557+
transformX(radiusX),
558+
transformX(radiusY),
559+
rotation * rad,
560+
startAngle * rad,
561+
endAngle * rad
562+
)
563+
fill && ctx.fill()
564+
stroke && ctx.stroke()
565+
}
566+
567+
// curve is exported to evy go/wasm.
568+
// curve draws connected curve segments encoded as string
569+
// representing 2 dimensional array, e.g.:
570+
// "1 2 3 4,5 6" => [[1,2,3,4], [5,6]]
571+
// the curve segments here are 1 2 3 4 and 5 6
572+
//
573+
// curve segments are interpreted differently depending on their
574+
// **length**:
575+
// 2: endX, endY - Line from current position end position x, y
576+
// 4: controlX, controlY, endX, endY: quadratic bezier curve, see
577+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo
578+
// 5: control1X, control1Y, control2X, control2Y, radius: arcTo, see
579+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo
580+
// 6: control1X, control1Y, control2X, control2Y, endX, endY: bezierCurveTo, see
581+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo
582+
//
583+
// This notation is very dense and is intended as escape hatch for advanced graphics.
584+
function curve(ptr, len) {
585+
const s = memToString(ptr, len)
586+
// parse "1 2 3 4,5 6" into [[1,2,3,4], [5,6]]:
587+
const curves = s.split(",").map((s) => s.split(" ").map(Number))
588+
589+
const { x, y, ctx, fill, stroke } = canvas
590+
591+
ctx.beginPath()
592+
ctx.moveTo(x, y)
593+
594+
for (const curve of courves) {
595+
switch (curve.length) {
596+
case 2:
597+
ctx.lineTo(transformX(curve[0]), transformY(curve[1]))
598+
break
599+
case 4:
600+
ctx.quadraticCurveTo(
601+
transformX(curve[0]), // controlPoint.x
602+
transformY(curve[1]), // controlPoint.y
603+
transformX(curve[2]), // endPoint.x
604+
transformY(curve[3]) // endPoint.y
605+
)
606+
break
607+
case 5:
608+
ctx.arcTo(
609+
transformX(curve[0]), // controlPoint1.x
610+
transformY(curve[1]), // controlPoint1.y
611+
transformX(curve[2]), // controlPoint2.x
612+
transformY(curve[3]), // controlPoint1.y
613+
transformX(curve[4]) // radius
614+
)
615+
break
616+
case 6:
617+
ctx.bezierCurveTo(
618+
transformX(curve[0]), // controlPoint1.x
619+
transformY(curve[1]), // controlPoint1.y
620+
transformX(curve[2]), // controlPoint2.x
621+
transformY(curve[3]), // controlPoint1.y
622+
transformX(curve[4]), // endPoint.x
623+
transformY(curve[5]) // endPoint.y
624+
)
625+
break
626+
}
627+
}
628+
fill && ctx.fill()
629+
stroke && ctx.stroke()
630+
}
631+
544632
// stroke is exported to evy go/wasm.
545633
function stroke(ptr, len) {
546634
const s = memToString(ptr, len)

frontend/module/yace-editor.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ const builtins = new Set([
475475
"colour",
476476
"clear",
477477
"poly",
478+
"ellipse",
479+
"curve",
478480
"stroke",
479481
"fill",
480482
"dash",

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ func format(r io.Reader, w io.StringWriter, checkOnly bool) error {
146146
parserBuiltins := evaluator.DefaultBuiltins(newCLIRuntime()).ParserBuiltins()
147147
prog, err := parser.Parse(in, parserBuiltins)
148148
if err != nil {
149+
fmt.Println("parser error")
149150
return fmt.Errorf("%w: %w", errParse, parser.TruncateError(err, 8))
150151
}
151152
out := prog.Format()

pkg/evaluator/builtin.go

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,15 @@ func DefaultBuiltins(rt Runtime) Builtins {
8686
"line": xyBuiltin("line", rt.Line),
8787
"rect": xyBuiltin("rect", rt.Rect),
8888
"circle": numBuiltin("circle", rt.Circle),
89+
8990
"width": numBuiltin("width", rt.Width),
9091
"color": stringBuiltin("color", rt.Color),
9192
"colour": stringBuiltin("colour", rt.Color),
9293
"clear": {Func: clearFunc(rt.Clear), Decl: clearDecl},
9394

94-
"poly": {Func: polyFunc(rt.Poly), Decl: polyDecl},
95+
"poly": {Func: polyFunc(rt.Poly), Decl: polyDecl},
96+
"ellipse": {Func: ellipseFunc(rt.Ellipse), Decl: ellipseDecl},
97+
"curve": {Func: curveFunc(rt.Curve), Decl: curveDecl},
9598

9699
"stroke": stringBuiltin("stroke", rt.Stroke),
97100
"fill": stringBuiltin("fill", rt.Fill),
@@ -152,6 +155,8 @@ type GraphicsRuntime interface {
152155

153156
// advanced graphics functions
154157
Poly(vertices [][]float64)
158+
Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64)
159+
Curve(sements [][]float64)
155160
Stroke(s string)
156161
Fill(s string)
157162
Dash(segments []float64)
@@ -189,6 +194,7 @@ func (rt *UnimplementedRuntime) Width(w float64) { rt.Unimplemented("w
189194
func (rt *UnimplementedRuntime) Color(s string) { rt.Unimplemented("color") }
190195
func (rt *UnimplementedRuntime) Clear(color string) { rt.Unimplemented("clear") }
191196
func (rt *UnimplementedRuntime) Poly(vertices [][]float64) { rt.Unimplemented("poly") }
197+
func (rt *UnimplementedRuntime) Curve(sements [][]float64) { rt.Unimplemented("curve") }
192198
func (rt *UnimplementedRuntime) Stroke(s string) { rt.Unimplemented("stroke") }
193199
func (rt *UnimplementedRuntime) Fill(s string) { rt.Unimplemented("fill") }
194200
func (rt *UnimplementedRuntime) Dash(segments []float64) { rt.Unimplemented("dash") }
@@ -197,6 +203,9 @@ func (rt *UnimplementedRuntime) Text(s string) { rt.Unimplemented("t
197203
func (rt *UnimplementedRuntime) Textsize(size float64) { rt.Unimplemented("textsize") }
198204
func (rt *UnimplementedRuntime) Font(s string) { rt.Unimplemented("font") }
199205
func (rt *UnimplementedRuntime) Fontfamily(s string) { rt.Unimplemented("fontfamily") }
206+
func (rt *UnimplementedRuntime) Ellipse(x, y, rX, rY, rotation, startAngle, endAngle float64) {
207+
rt.Unimplemented("ellipse")
208+
}
200209

201210
var readDecl = &parser.FuncDeclStmt{
202211
Name: "read",
@@ -607,6 +616,65 @@ func polyFunc(polyFn func([][]float64)) BuiltinFunc {
607616
}
608617
}
609618

619+
var ellipseDecl = &parser.FuncDeclStmt{
620+
Name: "ellipse",
621+
VariadicParam: &parser.Var{Name: "n", T: parser.NUM_TYPE},
622+
ReturnType: parser.NONE_TYPE,
623+
}
624+
625+
func ellipseFunc(ellipseFn func(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64)) BuiltinFunc {
626+
return func(_ *scope, args []Value) (Value, error) {
627+
argLen := len(args)
628+
if argLen < 3 || argLen == 6 || argLen > 7 {
629+
return nil, fmt.Errorf("%w: 'ellipse' requires 3,4,5,7 or arguments, found %d", ErrBadArguments, argLen)
630+
}
631+
x := args[0].(*Num).Val
632+
y := args[1].(*Num).Val
633+
radiusX := args[2].(*Num).Val
634+
radiusY := radiusX
635+
rotation := 0.0
636+
startAngle := 0.0
637+
endAngle := 360.0
638+
if argLen > 3 {
639+
radiusY = args[3].(*Num).Val
640+
}
641+
if argLen > 4 {
642+
rotation = args[4].(*Num).Val
643+
}
644+
if argLen > 6 {
645+
startAngle = args[5].(*Num).Val
646+
endAngle = args[6].(*Num).Val
647+
}
648+
ellipseFn(x, y, radiusX, radiusY, rotation, startAngle, endAngle)
649+
return nil, nil
650+
}
651+
}
652+
653+
var curveDecl = &parser.FuncDeclStmt{
654+
Name: "curve",
655+
VariadicParam: &parser.Var{Name: "n", T: numArrayType},
656+
ReturnType: parser.NONE_TYPE,
657+
}
658+
659+
func curveFunc(curveFn func([][]float64)) BuiltinFunc {
660+
return func(_ *scope, args []Value) (Value, error) {
661+
segments := make([][]float64, len(args))
662+
for i, arg := range args {
663+
vertex := arg.(*Array)
664+
elements := *vertex.Elements
665+
if len(elements) < 2 || len(elements) == 3 || len(elements) > 6 {
666+
return nil, fmt.Errorf("%w: 'curve' argument %d has %d elements, expected 2, 4, 5 or 6", ErrBadArguments, i+1, len(elements))
667+
}
668+
segments[i] = make([]float64, len(elements))
669+
for j, val := range elements {
670+
segments[i][j] = val.(*Num).Val
671+
}
672+
}
673+
curveFn(segments)
674+
return nil, nil
675+
}
676+
}
677+
610678
var dashDecl = &parser.FuncDeclStmt{
611679
Name: "dash",
612680
VariadicParam: &parser.Var{Name: "segments", T: parser.NUM_TYPE},

0 commit comments

Comments
 (0)