Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions frontend/courses/courses.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
{ "id": "squares", "title": "Squares" },
{ "id": "rainbow", "title": "Rainbow" },
{ "id": "poly", "title": "Polygon and Polyline" },
{ "id": "ellipse", "title": "Ellipse" },
{ "id": "fill", "title": "Stroke and Fill" },
{ "id": "linestyle", "title": "Line Styles" },
{ "id": "text", "title": "Text" }
{ "id": "text", "title": "Text" },
{ "id": "curve", "title": "Curve" }
]
},
{
Expand All @@ -36,9 +36,11 @@
"emoji": "🤹‍♀️",
"units": [
{ "id": "movingdot", "title": "Moving Red Dot" },
{ "id": "bounce", "title": "Bounce" },
{ "id": "draw", "title": "Draw" },
{ "id": "juggle", "title": "Juggle" },
{ "id": "splashtrig", "title": "Splash of Trig" },
{ "id": "draw", "title": "Draw" }
{ "id": "ellipse", "title": "Interactive Ellipse" }
]
},
{
Expand Down
17 changes: 17 additions & 0 deletions frontend/courses/sample/animate/bounce.evy
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
background := "hsl(0deg 0% 0% / 10%)"
x := 10
y := 50
s := 1
width 1
fill background
stroke "red"

on animate
clear background
move x y
circle 10
x = x + s
if x < 10 or x > 90
s = -s
end
end
1 change: 1 addition & 0 deletions frontend/courses/sample/draw/curve.evy
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print "todo"
67 changes: 67 additions & 0 deletions frontend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ function newEvyGo() {
// advanced canvas
poly,
ellipse,
curve,
stroke,
fill,
dash,
Expand Down Expand Up @@ -99,6 +100,7 @@ function needsCanvas(f) {
f.clear ||
f.poly ||
f.ellipse ||
f.curve ||
f.stroke ||
f.fill ||
f.dash ||
Expand Down Expand Up @@ -562,6 +564,71 @@ function ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle) {
stroke && ctx.stroke()
}

// curve is exported to evy go/wasm.
// curve draws connected curve segments encoded as string
// representing 2 dimensional array, e.g.:
// "1 2 3 4,5 6" => [[1,2,3,4], [5,6]]
// the curve segments here are 1 2 3 4 and 5 6
//
// curve segments are interpreted differently depending on their
// **length**:
// 2: endX, endY - Line from current position end position x, y
// 4: controlX, controlY, endX, endY: quadratic bezier curve, see
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo
// 5: control1X, control1Y, control2X, control2Y, radius: arcTo, see
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo
// 6: control1X, control1Y, control2X, control2Y, endX, endY: bezierCurveTo, see
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo
//
// This notation is very dense and is intended as escape hatch for advanced graphics.
function curve(ptr, len) {
const s = memToString(ptr, len)
// parse "1 2 3 4,5 6" into [[1,2,3,4], [5,6]]:
const curves = s.split(",").map((s) => s.split(" ").map(Number))

const { x, y, ctx, fill, stroke } = canvas

ctx.beginPath()
ctx.moveTo(x, y)

for (const curve of courves) {
switch (curve.length) {
case 2:
ctx.lineTo(transformX(curve[0]), transformY(curve[1]))
break
case 4:
ctx.quadraticCurveTo(
transformX(curve[0]), // controlPoint.x
transformY(curve[1]), // controlPoint.y
transformX(curve[2]), // endPoint.x
transformY(curve[3]) // endPoint.y
)
break
case 5:
ctx.arcTo(
transformX(curve[0]), // controlPoint1.x
transformY(curve[1]), // controlPoint1.y
transformX(curve[2]), // controlPoint2.x
transformY(curve[3]), // controlPoint1.y
transformX(curve[4]) // radius
)
break
case 6:
ctx.bezierCurveTo(
transformX(curve[0]), // controlPoint1.x
transformY(curve[1]), // controlPoint1.y
transformX(curve[2]), // controlPoint2.x
transformY(curve[3]), // controlPoint1.y
transformX(curve[4]), // endPoint.x
transformY(curve[5]) // endPoint.y
)
break
}
}
fill && ctx.fill()
stroke && ctx.stroke()
}

// stroke is exported to evy go/wasm.
function stroke(ptr, len) {
const s = memToString(ptr, len)
Expand Down
1 change: 1 addition & 0 deletions frontend/module/yace-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ const builtins = new Set([
"clear",
"poly",
"ellipse",
"curve",
"stroke",
"fill",
"dash",
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func format(r io.Reader, w io.StringWriter, checkOnly bool) error {
parserBuiltins := evaluator.DefaultBuiltins(newCLIRuntime()).ParserBuiltins()
prog, err := parser.Parse(in, parserBuiltins)
if err != nil {
fmt.Println("parser error")
return fmt.Errorf("%w: %w", errParse, parser.TruncateError(err, 8))
}
out := prog.Format()
Expand Down
29 changes: 29 additions & 0 deletions pkg/evaluator/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ func DefaultBuiltins(rt Runtime) Builtins {
"line": xyBuiltin("line", rt.Line),
"rect": xyBuiltin("rect", rt.Rect),
"circle": numBuiltin("circle", rt.Circle),

"width": numBuiltin("width", rt.Width),
"color": stringBuiltin("color", rt.Color),
"colour": stringBuiltin("colour", rt.Color),
"clear": {Func: clearFunc(rt.Clear), Decl: clearDecl},

"poly": {Func: polyFunc(rt.Poly), Decl: polyDecl},
"ellipse": {Func: ellipseFunc(rt.Ellipse), Decl: ellipseDecl},
"curve": {Func: curveFunc(rt.Curve), Decl: curveDecl},

"stroke": stringBuiltin("stroke", rt.Stroke),
"fill": stringBuiltin("fill", rt.Fill),
Expand Down Expand Up @@ -154,6 +156,7 @@ type GraphicsRuntime interface {
// advanced graphics functions
Poly(vertices [][]float64)
Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64)
Curve(sements [][]float64)
Stroke(s string)
Fill(s string)
Dash(segments []float64)
Expand Down Expand Up @@ -191,6 +194,7 @@ func (rt *UnimplementedRuntime) Width(w float64) { rt.Unimplemented("w
func (rt *UnimplementedRuntime) Color(s string) { rt.Unimplemented("color") }
func (rt *UnimplementedRuntime) Clear(color string) { rt.Unimplemented("clear") }
func (rt *UnimplementedRuntime) Poly(vertices [][]float64) { rt.Unimplemented("poly") }
func (rt *UnimplementedRuntime) Curve(sements [][]float64) { rt.Unimplemented("curve") }
func (rt *UnimplementedRuntime) Stroke(s string) { rt.Unimplemented("stroke") }
func (rt *UnimplementedRuntime) Fill(s string) { rt.Unimplemented("fill") }
func (rt *UnimplementedRuntime) Dash(segments []float64) { rt.Unimplemented("dash") }
Expand Down Expand Up @@ -646,6 +650,31 @@ func ellipseFunc(ellipseFn func(x, y, radiusX, radiusY, rotation, startAngle, en
}
}

var curveDecl = &parser.FuncDeclStmt{
Name: "curve",
VariadicParam: &parser.Var{Name: "n", T: numArrayType},
ReturnType: parser.NONE_TYPE,
}

func curveFunc(curveFn func([][]float64)) BuiltinFunc {
return func(_ *scope, args []Value) (Value, error) {
segments := make([][]float64, len(args))
for i, arg := range args {
vertex := arg.(*Array)
elements := *vertex.Elements
if len(elements) < 2 || len(elements) == 3 || len(elements) > 6 {
return nil, fmt.Errorf("%w: 'curve' argument %d has %d elements, expected 2, 4, 5 or 6", ErrBadArguments, i+1, len(elements))
}
segments[i] = make([]float64, len(elements))
for j, val := range elements {
segments[i][j] = val.(*Num).Val
}
}
curveFn(segments)
return nil, nil
}
}

var dashDecl = &parser.FuncDeclStmt{
Name: "dash",
VariadicParam: &parser.Var{Name: "segments", T: parser.NUM_TYPE},
Expand Down
5 changes: 4 additions & 1 deletion pkg/parser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,10 @@ func (i *IfStmt) AlwaysTerminates() bool {
}

func (e *EventHandlerStmt) String() string {
body := e.Body.String()
body := ""
if e.Body != nil {
body = e.Body.String()
}
return "on " + e.Name + " {\n" + body + "}\n"
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ func (p *parser) parseFunc(scope *scope) Node {
p.advance() // advance past FUNC
tok := p.cur // function name
funcName := p.cur.Literal

p.advancePastNL() // advance past signature, already parsed into p.funcs earlier
fd := p.funcs[funcName]
scope = newScopeWithReturnType(scope, fd, fd.ReturnType)
Expand All @@ -202,6 +201,7 @@ func (p *parser) parseFunc(scope *scope) Node {
if fd.ReturnType != NONE_TYPE && !block.AlwaysTerminates() {
p.appendError("missing return")
}

p.assertEnd()
p.advance()
p.recordComment(block)
Expand Down
33 changes: 23 additions & 10 deletions pkg/wasm/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ func (rt *jsRuntime) Ellipse(x, y, rX, rY, rotation, startAngle, endAngle float6
ellipse(x, y, rX, rY, rotation, startAngle, endAngle)
}

func (rt *jsRuntime) Curve(segments [][]float64) {
sStrings := make([]string, len(segments))
for i, segment := range segments {
sStrings[i] = floatsToString(segment)
}
curve(strings.Join(sStrings, ","))
}

func floatsToString(floats []float64) string {
if len(floats) == 0 {
return ""
Expand Down Expand Up @@ -180,16 +188,6 @@ func rect(dx, dy float64)
//export circle
func circle(r float64)

// width is imported from JS, setting the lineWidth
//
//export width
func width(w float64)

// color is imported from JS
//
//export color
func color(s string)

// clear is imported from JS
//
//export clear
Expand All @@ -205,6 +203,21 @@ func poly(s string)
//export ellipse
func ellipse(x, y, rX, rY, rotation, startAngle, endAngle float64)

// curve is imported from JS
//
//export curve
func curve(s string)

// width is imported from JS, setting the lineWidth
//
//export width
func width(w float64)

// color is imported from JS
//
//export color
func color(s string)

// stroke is imported from JS
//
//export stroke
Expand Down