@@ 3,6 3,8 @@ let log = []
let eventLog = []
let ctx, canvas
let state = {}
+let rendered = {}
+let renderCalled = false
let errorBackoff = false
function initDevices () {
@@ 96,6 98,11 @@ function processEvent (payload) {
}
try {
run([...payload, state], runningProgram.patterns, runningProgram.definitions).forEach(handle)
+ if (renderCalled) {
+ renderCalled = false
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ drawRendered(rendered, {x: 0, y: 0})
+ }
} catch (e) {
reportError(e)
errorBackoff = true
@@ 106,17 113,31 @@ function processEvent (payload) {
}
}
-function draw (element) {
+function drawRendered (render, pos) {
+ // Render this if it has a graphics element
+ const newPos = (
+ render.el && render.el.pos
+ ? vecadd(pos, render.el.pos)
+ : pos
+ )
+ if (render.el) {
+ draw(render.el, newPos)
+ }
+ // Either way, draw each of the children as well
+ Object.values(render.children || {}).forEach(v => drawRendered(v, newPos))
+}
+
+function draw (element, pos) {
if (element.type === 'circle') {
ctx.beginPath()
- ctx.arc(element.pos.x, element.pos.y, element.radius, 0, Math.PI * 2, true)
+ ctx.arc(pos.x, pos.y, element.radius, 0, Math.PI * 2, true)
ctx.stroke()
} else if (element.type === 'clear') {
ctx.clearRect(0, 0, canvas.width, canvas.height);
} else if (element.type === 'line') {
ctx.beginPath()
- ctx.moveTo(element.start.x, element.start.y)
- ctx.lineTo(element.end.x, element.end.y)
+ ctx.moveTo(element.start.x + pos.x, element.start.y + pos.x)
+ ctx.lineTo(element.end.x + pos.y, element.end.y + pos.y)
ctx.stroke()
} else {
console.error('Unrecognized element type:', element.type)
@@ 130,14 151,25 @@ function handle (event) {
if (!Array.isArray(event)) {
return
}
- const [type, value] = event;
+ const [type, value, ...rest] = event;
if (type === 'log') {
logOutput(JSON.stringify(value))
} else if (type === 'draw') {
- draw(value)
+ draw(value, {x: 0, y: 0})
+ } else if (type === 'render') {
+ const paths = rest.flatMap(path => {
+ return ['children', path]
+ }).concat(['el'])
+ rendered = withSetAtPath(paths, rendered, value);
+ renderCalled = true
+ } else if (type === 'store') {
+ state = withSetAtPath(rest, state, value);
} else if (type === 'set_state') {
Object.keys(value).forEach(key => {
- state[key] = value[key]
+ state = {
+ ...state,
+ [key]: value[key]
+ }
})
}
}
@@ 36,3 36,21 @@ function setHiPPI(canvas, width, height) {
return canvas;
}
+
+function withSetAtPath (paths, partial, data) {
+ if (paths.length === 0) {
+ return data
+ }
+ const [path, ...otherPaths] = paths;
+ return {
+ ...(partial || {}),
+ [path]: withSetAtPath(otherPaths, (partial || {})[path], data)
+ }
+}
+
+function vecadd ({x: xa, y: ya}, {x: xb, y: yb}) {
+ return {
+ x: xa + xb,
+ y: ya + yb
+ }
+}