Ref: Up and Down the Ladder of Abstraction by Bret Victor

"The keep-the-car-on-the-road algorithm is a favorite example of Alan Kay when demonstrating Etoys" ...

and some other wip things

inputs ui:


inputs cursor:


appendix & source code of 🎨

showing selected source code of 🎨

For complete source see GitHub.

notesthis is Javascript and using tools and patterns that I repeat, but remember that calculang is unopinionated!
calculang/output is also highly portable and uniform.

visual 1 (main output, with mouse interactivity)

const vs_i = {
  encodings: {
    x: 'x_in',
    y: 'y_in',
    color: f0,
    size: '',
    shape: '',
    opacity: ''
  }
}

const domains = {
    formula: /*formulae_not_inputs*/ ["road",/*"road2","roadm2",*/ "a1","a2","a3"],
    x_in: _.range(0,25,0.3),
    y_in: _.range(-3,7,0.3)
  };

const spec = ({
  // vega-lite
  layer: [{
    mark: {type:'rect', tooltip:true},
    encoding: {
      x: { field: vs_i.encodings.x, type: 'nominal', axis:null },
      y: { field: vs_i.encodings.y, type: 'nominal', axis:null, sort: 'descending' },
      color: {field:vs_i.encodings.color, type: 'nominal', scale: {range:'diverging'}, legend: false},
      //detail: {field:vs_i.encodings.detail}, // I guess works
      //row: {field:vs_i.encodings.row}, // TODO (this vega lite)
      //col: {field:vs_i.encodings.col}, // TODO (this vega lite)
      size: {field:vs_i.encodings.size},
      shape: {field:vs_i.encodings.shape},
      //anim__: {field:vs_i.encodings.anim},
      opacity: vs_i.encodings.shape != '' ? {field:vs_i.encodings.shape} : {}, // OVERRIDES APPROACH?
    },
    data: { name: "data" },
  },
  {
    mark: {type:'point', tooltip:true},
    encoding: {
      x: { field: 'carx', type: 'quantitative', scale: { domain: d3.extent(domains.x_in) } },
      y: { field: 'cary', type: 'quantitative', scale: { domain: d3.extent(domains.y_in) } },
      detail: { field: 't_in', type: 'nominal', sort: 'descending' },
      color: {value: 'green'},
            trick__: {field: 'car_angle'}

      //color: { field: 'car_offroad', type: 'nominal' },
    },
    data: { name: "data2" },
  },
  {
  "mark": {"type": "image", "width": 50, "height": 50},
    transform: [{filter: "datum.t_in == t_in"}], // detect t_in by datum.thing search?
    encoding: {
      x: { field: 'carx', type: 'quantitative', scale: { domain: d3.extent(domains.x_in) } },
      y: { field: 'cary', type: 'quantitative', scale: { domain: d3.extent(domains.y_in) } },
      url: { value: '/_file/ffox.png',  type: 'nominal' }, // from https://freepngimg.com/png/11315-car-free-download-png
      color: {value: 'green'},
      size: {value:30},
      opacity: {value:0.02}
      //angle: {value: -10}
      //color: { field: 'car_offroad', type: 'nominal' },
    },
    data: { name: "data2" },
  },
  {
  "mark": {"type": "text", "width": 50, "height": 50, fontWeight:'bold', baseline: 'middle', align:'center'},
    transform: [{filter: "datum.t_in == t_in"}, {calculate: "-datum.car_angle*57", "as" :"car_angle2"}],
    encoding: {
      x: { field: 'carx', type: 'quantitative', scale: { domain: d3.extent(domains.x_in) } },
      y: { field: 'cary', type: 'quantitative', scale: { domain: d3.extent(domains.y_in) } },
      text: { value: '🚗',  type: 'nominal' }, // from https://freepngimg.com/png/11315-car-free-download-png
      color: {value: 'red'},
      size: {value:30},
      //detail: {field: 't_in'}
      //angle: {value: 90},
      angle: {field: "car_angle2", type:'quantitative', scale: {domain:[0,360],range:[0,360]}},
      //color: { field: 'car_offroad', type: 'nominal' },
    },
    data: { name: "data2" },
  },
  /*{
    transform: [{filter: `0 && datum.t_in == animation`}], // TODO lookup anim encoding
    mark: {type:'point', point: false, filled: false, strokeWidth:5, tooltip:true},
    encoding: {
      opacity: {value: .5},
      x: { field:vs_i.encodings.x, type: 'quantitative' },
      y: { field:vs_i.encodings.y, type: 'quantitative' },
      size: {value: 800},
      //color: {field:vs_i.encodings.color, type: 'nominal'}
    },
    data: { name: "data" },
  }*/
  ],
  // autosize breaks consistency when mappings change
  //autosize: { "type": "fit", "contains": "padding"},
  width: 400,//Math.min(400,rhs_width-30),
  height: 150,
  background:'rgba(0,0,0,0)',
})

// interactivity via vega signals and listeners
const viz = embed('#viz', spec, {patch: [{
  path: "/signals",
  op: "add",
  value: [{
    name: "t_in",
    //value: 8,
  }]
}]})

new messing:

import * as traverseObj from "npm:object-traversal";
import {uniq} from "npm:underscore";


const p = (spec) => {

  const mappings = (spec) => {
  let v = [];
  traverseObj.traverse(spec, (a) => {
    //console.log(a);
    if (a.key == "field") {
      v.push({ field: a.value, input_domain: a.parent.input_domain });
      //console.log("CAPTURE ", a); // CAPTURE MARK AND TYPE ALSO?
    }
  });
  return /*_.*/uniq(v.map((d) => d.field)); // return {mapped, summary}
  }

  console.log('dn', mappings(spec))
}

p(spec)
const data_source = calcuvegadata({
  models: [model],
  spec: spec.layer[0],
  domains,
  input_cursors: [
    cursor
  ]
})

display(spec)

display(Inputs.table(data_source2))//).filter(d => d.x_in <0)))
//display(Inputs.table(data_source_cursor))
viz.view.data("data", data_source).resize().run(); // turn off resize
viz.view.signal('t_in', t_in).run()

const size_in = 16

const t_in_Input = Inputs.range([0,1000],{label:'t_in', step:1, value:0})//input(0);
const t_in = Generators.input(t_in_Input);