---
title: "projectile code"
order: 1
author:
- name: "Declan Naughton ๐งฎ๐จโ๐ป๐ง"
url: "https://calcwithdec.dev/about.html"
description: "projectile code"
format:
html:
echo: false
resources:
- '../../models/projectile/*.js'
- '../../models/projectile/*.js.map'
- '../../models/projectile/*.json'
---
<details open><summary>inputs โ๏ธ</summary>
```{ojs}
import {interval} from '@mootari/range-slider'
//| panel: input
viewof form_projectile = {
let form = Inputs.form({
angle_in: Inputs.range([0,3], {value: 1, step:0.01, label: "Angle" }),
power_in: Inputs.range([0,100], {value: 30, step:0.01, label: "Power" }),
g_in: Inputs.range([0,3], {value: 1, step:0.01, label: "Gravity factor" }),
drag_coefficient_in: Inputs.range([-0.1,0.1], {value: 0.01, step:0.001, label: "Drag coeff." }),
t_interval: interval([0,100], {step:1, label: 'time โ๏ธ', color: 'skyblue', value:[0,60]}),
});
let state = false;
form.oninput = () => {console.log('oninput');if (state == false) {mutable inputs_history_projectile = [...mutable inputs_history_projectile, form.value]; state = true;} mutable inputs_history_projectile = [...mutable inputs_history_projectile.slice(0,-1), form.value]}
form.onchange = () => { console.log('onchange');state = false; mutable inputs_history_projectile[mutable inputs_history_projectile.length-1] = form.value };
return form;
}
mutable inputs_history_projectile = [JSON.parse(`{"angle_in":1,"power_in":30,"g_in":1,"drag_coefficient_in":0.01,"t_interval":[0,60]}`
)]
```
</details>
```{ojs}
//| echo: false
// TODO value, impact, color code?
Inputs.table(
inputs_history_projectile.slice(1).map((d,i) => ({i:i+1, tag:Object.entries(d).filter(([k,v]) => v != inputs_history_projectile[i-1+1][k])[0]})).map(d => ({...d,tag:`${d.tag[0]} ${inputs_history_projectile[d.i-1][d.tag[0]]} -> ${d.tag[1]}`}))
)
```
::: {.panel-tabset}
## ๐ฏ scatterplot (w. history)
```{ojs}
viewof clip = Inputs.toggle({label: "clip", value: true})
embed(
calcuvizspec({
models: [main],
input_cursors: inputs_history_projectile,
mark: {type:'point',clip},
encodings: {
detail: {name: 't_in', type: 'quantitative', domain: _.range(form_projectile.t_interval[0],form_projectile.t_interval[1]+0.1,1)},
y: {name: 'y', type:'quantitative', sort: 'ascending', scale:{domain:[0,500], sort: 'descending'}},
x: {name: 'x', type: 'quantitative', scale:{domain:[0,40]}},
color: {name: 'input_cursor_id', type: 'nominal'},
},
width: 400, height: 220
}), {renderer: 'svg'})
```
## ๐ grid
```{ojs}
projectile_formulae = ['x','y','dx','dy','drag_x','drag_y']
viewof grid = embed(
calcuvizspec({
models: [main],
input_cursors: [form_projectile],
mark: 'text',
encodings: {
x: {name: 'formula', type:'nominal', domain: projectile_formulae},
y: {name: 't_in', type: 'nominal', domain: _.range(form_projectile.t_interval[0],form_projectile.t_interval[1]+0.1, 5)},
text: {name: 'value', type: 'quantitative', format:',.2f'},
color: {name: 'formula', type:'nominal', domain: projectile_formulae, legend:false},
},
width: 500, height: 220
}), {renderer: 'svg'})
```
## ๐ row facet bars
```{ojs}
viewof bars = embed(
calcuvizspec({
models: [main],
input_cursors: [form_projectile],
mark: 'bar',
encodings: {
x: {name: 't_in', type: 'quantitative', domain: _.range(form_projectile.t_interval[0],form_projectile.t_interval[1]+0.1,1)},
y: {name: 'value', type: 'quantitative', independent:true},
row: {name: 'formula', type:'nominal', domain: projectile_formulae},
color: {name: 'formula', type:'nominal', domain: projectile_formulae,legend:false},
},
width: 500, height:40
}), {renderer: 'svg'})
```
## ๐ row facet lines (w. history)
```{ojs}
viewof lines = embed(
calcuvizspec({
models: [main],
input_cursors: inputs_history_projectile,
mark: {type: 'line', point: true, strokeWidth: 0.5},
encodings: {
x: {name: 't_in', type: 'quantitative', domain: _.range(form_projectile.t_interval[0],form_projectile.t_interval[1]+0.1,2)},
y: {name: 'value', type: 'quantitative', independent:true},
row: {name: 'formula', type:'nominal', domain: projectile_formulae},
color: {name: 'input_cursor_id', type: 'nominal'},
},
width: 500, height:40
}), {renderer: 'svg'})
```
:::
<!-- code viewer -->
<details open><summary>*calculang formulae* ๐ช</summary>
*highlight or click a number in visual to see it's calculang formula below:*
```{ojs}
//| echo: false
// code viewer
formulae_objs = Object.values(introspection_nomemo.cul_functions).filter(d => d.reason == 'definition' && inputs.indexOf(d.name+'_in') == -1)
viewof code_opt_fv = Inputs.select(["๐ calculang source ๐ฌ", "โจ calculang output โจ for ๐ฅ๏ธ"], {label: "๐ง via ๐ฒs", value: "๐ calculang source ๐ฌ"})
viewof formula_select = Inputs.select(/*Object.entries(introspection_nomemo.cul_functions).filter(([a,b]) => b.reason == 'definition' && b.cul_source_scope_id==0).map(([a,b]) => b.name)*/formulae_not_inputs, {label: "formula"})
// force nomemo here
cul_fetch = await fetch(`../../${entrypoint_no_cul_js}${1 ? '-nomemo_esm' : '_esm'}/cul_scope_${0}.${code_opt_fv.indexOf('source') != -1 ? 'cul.js' : 'mjs'}`)
cul = cul_fetch.text()
//cs = (code_opt_fv.indexOf("source") != -1 ? calcuin_fv : calcuout).split('\n')
```
```{ojs}
// todo here: pull introspection-no-memo, use entrypoint.cul.js, publish as a helper? -> test in calcuvizspec
// ALSO will formula UI sit inside/outside? Inside is better, how to maintain interactivity?
code_viewer = async (entrypoint, formula) => {
const cul_fetch = await fetch(`../../${entrypoint_no_cul_js}${1 ? '-nomemo_esm' : '_esm'}/cul_scope_${0}.${code_opt_fv.indexOf('source') != -1 ? 'cul.js' : 'mjs'}`)
const cul = await cul_fetch.text()
return md`
~~~js
${
formulae_objs.filter(f => f.name == formula_select).map(f => cul.split('\n').filter((d,i) => i >= f.loc.start.line-1 && i < f.loc.end.line).join('\n').slice(13)).join('\n\n')
}
~~~
---
<br/>
<br/>
<br/>
<br/>
`
}
code_viewer('',formula_select)
```
```{ojs}
//| echo: false
grid.addEventListener('mousemove', (event, item) => {
viewof formula_select.value = item.datum.formula;
viewof formula_select.dispatchEvent(new CustomEvent("input"))
})
bars.addEventListener('mousemove', (event, item) => {
viewof formula_select.value = item.datum.formula;
viewof formula_select.dispatchEvent(new CustomEvent("input"))
})
lines.addEventListener('mousemove', (event, item) => {
viewof formula_select.value = item.datum.formula;
viewof formula_select.dispatchEvent(new CustomEvent("input"))
})
```
</details>
<!-- end code viewer -->
---
<br/>
<br/>
<br/>
<br/>
# Appendix
```{ojs}
import { calcuvizspec } from "@declann/little-calcu-helpers"
embed = require('vega-embed');
viewof entrypoint = Inputs.select(['models/projectile/projectile.cul.js'], {label:'entrypoint'})
entrypoint_no_cul_js = entrypoint.slice(0,-7)
main = require(`../../${entrypoint_no_cul_js}.js`);
introspection_fetch = await fetch(`../../${entrypoint_no_cul_js}.introspection.json`)
introspection = introspection_fetch.json({typed:true})
introspection_nomemo_fetch = await fetch(`../../${entrypoint_no_cul_js}-nomemo.introspection.json`)
introspection_nomemo = introspection_nomemo_fetch.json({typed:true})
inputs = Object.values(introspection.cul_functions).filter(d => d.reason == 'input definition').map(d => d.name).sort()
formulae = Object.values(introspection.cul_functions).filter(d => d.reason == 'definition').map(d => d.name)
// formulae excluding pure inputs
formulae_not_inputs = Object.values(introspection.cul_functions).filter(d => d.reason == 'definition' && inputs.indexOf(d.name+'_in') == -1).map(d => d.name)
```