Code Visualizer

by wio_dude

HTML

<canvas
  id="canvas"
  width="720"
  height="480"
/>

CSS

#canvas {
  background-color: lightgrey;
}

JavaScript

class CodeMachine {
	constructor(statement) {
  	this.statement = statement;
  }
  
  size(ctx, opts) {
  	const size = this.statement.size(ctx, opts);
    return [size[0], size[1] + 2 * opts.pointSize];
  }
  
  draw(ctx, opts, start) {
  	ctx.beginPath();
    ctx.arc(start[0], start[1] + opts.pointSize / 2, opts.pointSize / 2, Math.PI, 2 * Math.PI);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(start[0], start[1] + opts.pointSize / 2);
    ctx.lineTo(start[0], start[1] + opts.pointSize);
    ctx.stroke()
    let end = this.statement.draw(ctx, opts, [start[0], start[1] + opts.pointSize]);
    ctx.beginPath()
    ctx.moveTo(end[0], end[1]);
    ctx.lineTo(end[0], end[1] + opts.pointSize / 2);
    ctx.stroke();
  	ctx.beginPath();
    ctx.arc(end[0], end[1] + opts.pointSize / 2, opts.pointSize / 2, 0, Math.PI);
    ctx.closePath();
    ctx.fill();
  }
}


class CodeBranch {
	constructor(branches = []) {
  	this.branches = branches;
  }
  
  doesBranch() {
  	return true;
  }
  
  size(ctx, opts) {
  	let height = 0;
    let width = 0;
    for (const branch of this.branches) {
    	const child_size = branch.size(ctx, opts);
      height = Math.max(height, child_size[1]);
      width += child_size[0];
    }
    return [width, height + 2 * opts.gutterSize];
  }
  
  draw(ctx, opts, start) {
  	const sizes = this.branches.map(x => x.size(ctx, opts));
    const width = sizes.map(x => x[0]).reduce((x, y) => x + y);
    const height = sizes.map(x => x[1]).reduce((x, y) => Math.max(x, y));
    const end = [start[0], start[1] + height + 2 * opts.gutterSize];
    let offset = start[0] - width / 2;
    for (const [index, branch] of this.branches.entries()) {
    	const railOffset = !branch.doesBranch() ? opts.gutterSize / 2 : sizes[index][0] / 2;
    	const branchStart = [offset + railOffset, start[1] + opts.gutterSize];
      ctx.beginPath();
      ctx.moveTo(start[0], start[1]);
      ctx.bezierCurveTo(start[0], branchStart[1], branchStart[0],...