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.
notes
this 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);