---
title: "bouncing ball"
order: 1
author:
- name: "Declan Naughton 🧮👨💻"
url: "https://calcwithdec.dev/about.html"
description: "bouncing ball story"
format:
html:
#sidebar: false
#page-layout: 'article'
resources:
- '../../models/bounce/*.js'
- '../../models/bounce/*.js.map'
- '../../models/bounce/*_esm/*.cul.js'
- '../../models/bounce/*_esm/*.mjs'
---
```{ojs}
import {calcuvegadata} from '@declann/raycasting-2-blah'
viz_spec = {
return ({
mark: 'point',
encoding: {
x: {field: 'x', type: 'quantitative'},
y: {field: 'y', type: 'quantitative'},
detail: {field: 't_in'},
color: {field: 'dampener_in'}
}
})
}
viz_spec2 = {
return ({
mark: 'text',
width: 100,
encoding: {
x: {field: 'formula', type: 'nominal'},
y: {field: 't_in', type: 'quantitative'},
text: {field: 'value', format:',.2f'},
column: {field: 'dampener_in'}
}
})
}
// calcuvizspec much more ergonomic for multiple vizes
scene_data = calcuvegadata({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
models: [bounce_6],
spec: viz_spec,
domains: {
t_in: _.range(0,10),
dampener_in: [1,0.9]
},
input_cursors: [
{
dx_in: 2, dampener_in: 0.6 // domains take precedence
}
]
})
// optional combine spec and Data call?
scene2_data = calcuvegadata({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
models: [bounce_6],
spec: viz_spec2,
domains: {
t_in: _.range(0,10),
dampener_in: [1,0.9],
formula: ['x','y']
},
input_cursors: [
{
dx_in: 2, dampener_in: 0.6 // domains take precedence
}
]
})
viewof viz = embed(viz_spec)
{
viz.data('source', scene_data).resize().run()
}
viewof viz2 = embed(viz_spec2)
{
viz2.data('source', scene2_data).resize().run()
}
Inputs.table(scene2_data)
```
# Bouncing Ball
Use the continue button to...continue!!
It will present code changes first, then update the visual. Read the code change and have a guess what it will do in advance! (but I will create titles or 'chapters')
```{ojs}
//| echo: false
viewof progress = Inputs.button("➡️ continue", {reduce: async () => {
// I can't use the values of viewof I'll update... (ANNOYING!)
if (step_cond) {
(viewof stepcode).value = stepcode_next;
(viewof stepcode).dispatchEvent(new CustomEvent('input'), {bubbles:true});
} else {
await animation.play('.chart-wrapper');
(viewof step).value = step_next;
(viewof step).dispatchEvent(new Event('input'), {bubbles:true})
}
}})
```
::: {layout-ncol=2}
::: {.viz}
```{ojs}
diff(cul[step],cul[stepcode]+'\n', {outputFormat, context:99999})
viewof outputFormat = Inputs.radio(["side-by-side", "line-by-line"], {label: "diff format", value: 'line-by-line'})
```
:::
::: {.code}
```{ojs}
specs = [bounce_0,bounce_1,bounce_2,bounce_3,bounce_4,bounce_5,bounce_6].map(m => gemini.vl2vg4gemini(calcuvizspec({
models: [m], // TODO show multiple models
input_cursors: [{dx_in:3,dampener_in:0.8}],
mark: 'point',
encodings: {
x: {name: 'x', type:'quantitative', grid:false, zero:false},
y: {name: 'y', type: 'quantitative', grid:false, zero:false},
//column: {name: 'dampener_in', type:'nominal', domain: [0.9,1,0.8]},
//row: {name: 'formula', type:'nominal', domain: formulae_not_inputs},
color: {name: 't_in', type: 'nominal', domain:_.range(0,40,1), legend: false},
},
width: 300, height:200
})))
embed(specs[step], {renderer:'svg'}) // is this better if step is hardcoded initially? Don't re-run this?
embed(calcuvizspec({
models: [bounce_6], // TODO show multiple models
input_cursors: [{dx_in:3}],
mark: 'text',
encodings: {
x: {name: 'formula', type:'nominal', grid:false, domain:['dy','y','bouncing']},
y: {name: 't_in', type: 'nominal', grid:false, zero:false, domain:_.range(0,60,1)},
text: {name:'value', type:'quantitative', format:',.3f'},
row: {name: 'dampener_in', type:'nominal', domain: [0.9,1,0.8,.88]},
//color: {name: 't_in', type: 'nominal', domain:_.range(0,60,1), legend: false},
},
width: 200, height:500
}))
```
:::
:::
# Appendix
```{ojs}
import { calcuvizspec } from "@declann/little-calcu-helpers"
embed = require('vega-embed');
//import {diff} from "@jobleonard/diff-tool"
import {diff} from "@declann/diff-tool"
// models
bounce_0 = require(`../../models/bounce/bounce-0.js`);
bounce_1 = require(`../../models/bounce/bounce-1.js`);
bounce_2 = require(`../../models/bounce/bounce-2.js`);
bounce_3 = require(`../../models/bounce/bounce-3.js`);
bounce_4 = require(`../../models/bounce/bounce-4.js`);
bounce_5 = require(`../../models/bounce/bounce-5.js`);
bounce_6 = require(`../../models/bounce/bounce-6.js`);
// calculang
bounce_0_cul_fetch = await fetch(`../../models/bounce/bounce-0-nomemo_esm/cul_scope_0.cul.js`)
bounce_0_cul = bounce_0_cul_fetch.text()
bounce_1_cul_fetch = await fetch(`../../models/bounce/bounce-1-nomemo_esm/cul_scope_0.cul.js`)
bounce_1_cul = bounce_1_cul_fetch.text()
bounce_2_cul_fetch = await fetch(`../../models/bounce/bounce-2-nomemo_esm/cul_scope_0.cul.js`)
bounce_2_cul = bounce_2_cul_fetch.text()
bounce_3_cul_fetch = await fetch(`../../models/bounce/bounce-3-nomemo_esm/cul_scope_0.cul.js`)
bounce_3_cul = bounce_3_cul_fetch.text()
bounce_4_cul_fetch = await fetch(`../../models/bounce/bounce-4-nomemo_esm/cul_scope_0.cul.js`)
bounce_4_cul = bounce_4_cul_fetch.text()
bounce_5_cul_fetch = await fetch(`../../models/bounce/bounce-5-nomemo_esm/cul_scope_0.cul.js`)
bounce_5_cul = bounce_5_cul_fetch.text()
bounce_6_cul_fetch = await fetch(`../../models/bounce/bounce-6-nomemo_esm/cul_scope_0.cul.js`)
bounce_6_cul = bounce_6_cul_fetch.text()
cul_raw = [bounce_0_cul,bounce_1_cul,bounce_2_cul,bounce_3_cul,bounce_4_cul,bounce_5_cul,bounce_6_cul]
cul = cul_raw.map(cul => cul.replace(/export const /g, ''))
```
Transitions:
```{ojs}
gemini = require('https://cdn.jsdelivr.net/gh/uwdata/gemini@v0.1-alpha/gemini.web.js')
viewof step = Inputs.range([0,10], {step:1, label:'step', value:0});
step_next = step+1
viewof stepcode = Inputs.range([0,10], {step:1, label:'step (code)', value:0});
stepcode_next = stepcode+1
step_cond = step == stepcode
gemSpec = ({
"timeline": {
"sync": [
{
"component": {"mark": "marks"},
"change": {"data": ["t_in"]},
"timing": {"duration": {"ratio": 1}}
},
{"component": {"axis": "x"}, "timing": {"duration": {"ratio": 0.5}}},
{"component": {"axis": "y"}, "timing": {"duration": {"ratio": 0.5}}},
]
},
"totalDuration": 600
})
animation = gemini.animate(specs[step], specs[step+1], gemSpec)
emojis = [/*"🅾️"*/"⏮️","➡️1️⃣","➡️2️⃣","➡️3️⃣","➡️4️⃣","➡️5️⃣","➡️6️⃣"]//➡️
```