=  q. get ('id' ) ??  '' =  q. get ('hidden' ) ==  'hidden'  ??  false =  html `<select style="font-size: 1.5em"> ${ examples. map (category =>  `<optgroup label=" ${ category. category } ">    ${ category. tiles . filter (tile =>  ((! tile. disabled ) ||  show_hidden)). map (d =>  `<option value=" ${ d. id } " ${ d. id  ==  default_example_id ?  ' selected="selected"'  :  '' } }> ${ d. title } </option>` )}    ${ category. tiles . filter (d =>  d. id  ==  default_example_id &&  d. disabled  &&  (! show_hidden)). map (d =>  `<option value=" ${ d. id } " ${ d. id  ==  default_example_id ?  ' selected="selected"'  :  '' } }> ${ d. title } </option>` )} </optgroup> ` )} </select>` md `* ${ example_details. subtitle } *` =  {if  (cul_js_cul)return  Object . values (introspection_nomemo. cul_scope_ids_to_resource ). map (loc =>  loc. slice (2 ). slice (0 , loc. indexOf ('?' ) !=  - 1  ?  loc. indexOf ('?' )- 2  :  999 )). map (d =>  d +  ((d. slice (- 3 ) !=  '.js' ) ?  '.js'  :  '' )). map (loc =>  dir+  loc)else  return  [... Array (Object . keys (introspection_nomemo. cul_scope_ids_to_resource ). length )]. map ((_,  i) =>  entrypoint_no_cul_js /* this is not modelname! this should be related to entrypoit, not id */ + (calculang_source_nomemo. length ?  '-nomemo'  :  '' ) + '_esm/' + 'cul_scope_' + i+ '.mjs' )//cul_resources =  {let  o =  []; . forEach (loc =>  {. push (fetch (loc). then (d =>  d. text ()))return  o//  TODO  replace duplicate (source) code by a message =  [await  output[0 ],  cul_resources. length  >  1  ?  await  output[1 ] :  '' ,  cul_resources. length  >  2  ?  await  output[2 ] :  '' ,  cul_resources. length  >  3  ?  await  output[3 ] :  '' ,  cul_resources. length  >  4  ?  await  output[4 ] :  '' ,  cul_resources. length  >  5  ?  await  output[5 ] :  '' ,  cul_resources. length  >  6  ?  await  output[6 ] :  ''  //  TODO  warn when overflows ,  cul_resources. length  >  7  ?  await  output[7 ] :  '' ]. filter (d =>  d !=  '' )//calculang // "x.cul.js formulae?" =  calculang. map (d =>  d. split (' \n ' )). map (d =>  [d. findIndex (e =>  e. indexOf ('inputs:'  +  (options. includes ('inputs' ) ?  'BLAH'  :  '' )) !=  - 1 ) ??  999 ,  ... d]). map (d =>  d. filter ((e, i) =>  i <=  d[0 ] ||  d[0 ] ==  - 1 )). map (d =>  d. slice (1 )). map (d =>  d. join (' \n ' )). map ((d, i) =>  `***[ ${ introspection_nomemo. cul_scope_ids_to_resource [i]. split ('?' )[0 ]. replaceAll ('-nomemo' ,  '' )} ]( ${ cul_resources[i]. replaceAll ('-nomemo' , '' )} )  ${ cul_js_cul ?  `-> [js]( ${ entrypoint_no_cul_js} _esm/cul_scope_ ${ i} .mjs)`  :  `` } *** ~~~js ${ d. replaceAll ('export const '  +  (options. includes ('export consts' )?  'BLAH'  :  '' ),  '' )} ~~~ ` md `<br/>**inputs โ๏ธ**  \`${ inputs. filter (d =>  fns0. includes (d)). join (' \` ,  \` ' )}\`${ not_inputs. length  ?  ' | ~~ \` '  :  '' }${ not_inputs. map ((d, i) =>  ` ${ d}\` ~~ ${ i !=  not_inputs. length - 1  ?  ', ~~ \` '  :  '' } ` ). join ('' )} ` =  inputs. filter (d =>  ! fns0. includes (d))dot ` ${ scope_id_graph} ` md ` ${ fff. join (' \n\n ' )} ` =  Inputs. checkbox (["export consts" ,  "inputs" /*, "lists (todo)"*/ ],  {label :  "unhide?" ,  value :  ["" ]})//md`---` =  Inputs. radio (["๐ calculang source ๐ฌ" ,  "โจ JS/ESM output โจ" ],  {label :  "๐ซถ" ,  value :  "๐ calculang source ๐ฌ" })=  cul_js ==  "๐ calculang source ๐ฌ"  ?  true  :  false =  Inputs. checkbox (["nomemo" ],  {value :  ["nomemo" ],  label :  'see memo? ๐' ,  disabled : cul_js== "๐ calculang source ๐ฌ" })import  {Tangle} from  "@declann/colored-tangle" =  Inputs. input (1 )md `*starting i*:  ${ Inputs. bind (Inputs. range ([- 10 ,  150 ],  {step : 1 ,  width :  50 }),  viewof i_start)} ` embed (calcuvizspec ({models :  modelname ==  'fizzbuzz'  ?  [model] :  [], input_cursors :  [{}], mark :  {type :  'text' ,  fontSize : 15 ,  fontWeight :  'bold' }, encodings :  {x :  {name :  'formula' ,  type : 'nominal' ,  domain :  ['fizz' , 'buzz' , 'fizzbuzz' ],  axis :  {labelFontSize :  60 },  sort :  ['fizzbuzz' ]}, y :  {name :  'i_in' ,  type : 'nominal' ,  domain :  _. range (i_start,  i_start+ 20 ),  axis :  {grid : true }}, text :  {name :  'value' ,  type :  'nominal' }, color :  {name :  'value' ,  type :  'nominal' ,  legend : false }, , width :  250 , // height:50, spec_post_process :  spec =>  { spec. background  =  "#fff" ;  spec. encoding . y . axis  =  {grid : true };  return  spec; }=  Inputs. input (1990 )md `*starting year*:  ${ Inputs. bind (Tangle ({min :  - 1000 ,  max : 5000 ,  step : 1 ,  power : 1.8 ,  color : 'darkblue' }),  viewof leap_year_start)} ` import  { calcuvegadata } from  "baafa4b071a5b66a"  // draft API! =  ({background :  'white' , "data" :  {"name" :  "data" }, "datasets" :  { "data" :  leap_year_data0}, "vconcat" :  [{"width" :  480 , "mark" :  {type :  "line" ,  point :  true  }, "encoding" :  {"x" :  {"field" :  "year_in" , "type" :  "quantitative" , "scale" :  {"domain" :  {"param" :  "brush" },  zero :  false ,  nice :  false }, "axis" :  {"title" :  "" ,  format : ".0f" ,  grid :  false ,  format :  '.0f' ,  ticks : true }, "y" :  {"field" :  "is_leap_year" ,  "type" :  "quantitative" , scale :  {domain :  [- 0.1 , 1.1 ]}, "axis" :  {format : ".0f" ,  grid :  false ,  values :  [0 , 1 ],  format :  '.0f' ,  ticks :  true }, "order" :  {"field" :  "year_in" }, "tooltip" :  {field :  "year_in" },  {"width" :  480 , "height" :  60 , "mark" :  {type :  "line" ,  size : 0.1 }, "params" :  [{"name" :  "brush" , value :  { x :  [leap_year_start,  leap_year_start+ 50 ]},  // initial value here fails whenever I switch to this model after loading a different one ... understand //"value": {"year_in": [2000, 2010]}, "select" :  {"type" :  "interval" ,  "encodings" :  ["x" ]}, "encoding" :  {"x" :  {"field" :  "year_in" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false }, axis :  {format : ".0f" ,  grid :  false ,  tickCount : 12 ,  ticks : true  }, "y" :  {"field" :  "is_leap_year" , "type" :  "quantitative" , "axis" :  { "grid" :  false ,  values :  [0 , 1 ],  format :  '.0f' }=  calcuvegadata ({models :  modelname ==  'leap-year'  ?  [model] :  [], spec :  leap_year_spec, domains :  { year_in :  _. range (leap_year_start,  leap_year_start+ 1001 ) }, input_cursors :  [{}]=  embed (leap_year_spec). data ("data" ,  leap_year_data). resize (). run (); =  [] // includes data in spec for when I Open in Vega Editor . html `<button onclick= ${ () =>  (mutable leap_year_data0 =  leap_year_data)} >insert data -> vega editor (dev)</button>` =  Scrubber (_. range (0 , 60 * 5 ),  {autoplay :  false ,  delay : 50 ,  label : 'frame' ,  initial :  2 })=  Inputs. range ([- 3 , 15 ],  {value : 3 ,  label :  'speed' ,  step : 0.01 })=  ({background :  'black' , //'lightblue', "data" :  {"name" :  "data" }, "datasets" :  { "data" :  starfield_data0}, "height" :  300 ,  width :  300 , layer :  [{"mark" :  {type :  "point" ,  tooltip :  true ,  clip :  false }, /*"params": [{       "name": "brush",        value: { x: [leap_year_start, leap_year_start+50]}, // initial value here fails whenever I switch to this model after loading a different one ... understand       //"value": {"year_in": [2000, 2010]},       "select": {"type": "interval", "encodings": ["x"]}     }],*/ "encoding" :  {"x" :  {"field" :  "x" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false ,  domain : [- 50 , 50 ]}, //axis: {format:".0f", grid: false, tickCount:12, ticks:true } , "y" :  {"field" :  "y" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false ,  domain : [- 50 , 50 ]}, //"axis": { "grid": false, values: [0,1], format: '.0f'} , "size" :  {"field" :  "z" , "type" :  "quantitative" , scale :  {reverse : true }, legend : false //scale: {zero:false, nice: false, domain:[-50,50]}, //"axis": { "grid": false, values: [0,1], format: '.0f'} , color :  {value : 'white' },  //opacity: {value:1} , mark :  'rule' , encoding :  {"x" :  {"field" :  "x" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false ,  domain : [- 50 , 50 ]}, //axis: {format:".0f", grid: false, tickCount:12, ticks:true } , "y" :  {"field" :  "y" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false ,  domain : [- 50 , 50 ]}, //"axis": { "grid": false, values: [0,1], format: '.0f'} , "x2" :  {"field" :  "px" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false ,  domain : [- 50 , 50 ]}, //axis: {format:".0f", grid: false, tickCount:12, ticks:true } , "y2" :  {"field" :  "py" , "type" :  "quantitative" , scale :  {zero : false ,  nice :  false ,  domain : [- 50 , 50 ]}, //"axis": { "grid": false, values: [0,1], format: '.0f'} , color :  {value : 'white' }=  calcuvegadata ({models :  modelname ==  'starfield'  ?  [model] :  [], spec :  starfield_spec, domains :  { star_in :  _. range (0 ,  100 ) }, input_cursors :  [{speed_in,  frame_in,  random_seed_in : 'hello' }]=  embed (starfield_spec). data ("data" ,  starfield_data)/*.resize()*/ . run (); =  [] // includes data in spec for when I Open in Vega Editor . html `<button onclick= ${ () =>  (mutable starfield_data0 =  starfield_data)} >insert data -> vega editor (dev)</button>` =  [33 ,  59 ,  92 ,  97 ,  80 ,  25 ,  71 ,  67 ,  29 ,  36 ,  76 ,  22 ,  58 ,  14 ,  42 ], 26 ,  2 ,  87 ,  64 ,  40 ,  76 ,  70 ,  75 ,  24 ,  19 ,  94 ,  86 ,  87 ,  40 ,  25 ], 26 ,  21 ,  57 ,  63 ,  100 ,  96 ,  21 ,  82 ,  22 ,  93 ,  15 ,  57 ,  49 ,  81 ,  73 ], 68 ,  55 ,  11 ,  67 ,  46 ,  73 ,  44 ,  32 ,  84 ,  100 ,  51 ,  87 ,  18 ,  75 ,  82 ], 80 ,  41 ,  36 ,  11 ,  43 ,  74 ,  39 ,  93 ,  35 ,  53 ,  73 ,  59 ,  27 ,  8 ,  30 ], 28 ,  86 ,  21 ,  75 ,  69 ,  27 ,  41 ,  88 ,  84 ,  83 ,  75 ,  90 ,  60 ,  11 ,  12 ], 77 ,  87 ,  0 ,  59 ,  35 ,  5 ,  79 ,  37 ,  45 ,  2 ,  29 ,  4 ,  99 ,  4 ,  43 ], 55 ,  91 ,  69 ,  24 ,  9 ,  67 ,  84 ,  100 ,  99 ,  41 ,  29 ,  62 ,  65 ,  27 ,  87 ], 30 ,  27 ,  12 ,  86 ,  21 ,  8 ,  40 ,  81 ,  81 ,  39 ,  56 ,  82 ,  40 ,  36 ,  79 ], 72 ,  6 ,  94 ,  64 ,  51 ,  42 ,  97 ,  93 ,  74 ,  1 ,  70 ,  97 ,  68 ,  42 ,  72 ], 0 ,  16 ,  20 ,  11 ,  1 ,  59 ,  48 ,  41 ,  87 ,  51 ,  53 ,  35 ,  25 ,  46 ,  21 ], 34 ,  57 ,  0 ,  98 ,  86 ,  30 ,  69 ,  25 ,  82 ,  84 ,  86 ,  2 ,  17 ,  67 ,  25 ], 88 ,  100 ,  23 ,  72 ,  12 ,  73 ,  51 ,  47 ,  4 ,  61 ,  44 ,  44 ,  49 ,  53 ,  27 ], 95 ,  31 ,  31 ,  39 ,  55 ,  64 ,  61 ,  52 ,  41 ,  33 ,  86 ,  0 ,  40 ,  48 ,  90 ], 79 ,  3 ,  62 ,  88 ,  27 ,  93 ,  29 ,  0 ,  91 ,  24 ,  17 ,  49 ,  2 ,  68 ,  33 ], ; embed (calcuvizspec ({models :  modelname ==  'maze'  ?  [model] :  [], input_cursors :  [{maze_in}], mark :  {type :  'text' ,  fontSize : 11 ,  fontWeight :  'bold' }, encodings :  {row :  {name :  'formula' ,  type : 'nominal' ,  domain :  ['value' , 'max' ,  'arrow' ,  'arrow_path' ,  'arrow_path_pretty' ],  axis :  {labelFontSize :  60 },  sort :  ['value' ]}, x :  {name :  'x_in' ,  type : 'nominal' ,  domain :  _. range (0 ,  15 ),  axis :  {grid : false }}, y :  {name :  'y_in' ,  type : 'nominal' ,  domain :  _. range (0 , 15 ),  axis :  {grid : false }}, text :  {name :  'value' ,  type :  'nominal' }, //color: {name: 'value', type: 'quantitative', independent:true, legend:false}, , width :  400 , // height:50, //spec_post_process: spec => { spec.background = 'white'; spec.encoding.color.scale = {scheme:'viridis'}; return spec;} spec_post_process :  spec =>  { spec. encoding . row . header  =  {labelLimit :  100 }; spec. background  =  'white' ;  spec. encoding . color  =  {value :  'darkblue' };  return  spec; }=  Inputs. range ([0 , 100 ],  {label : '# snowflakes' ,  value :  20 ,  step :  1 })=  Inputs. range ([0 , 2 ],  {label : 'angular speed (w_in)' ,  value :  0.6 ,  step :  0.01 })=  Inputs. range ([100 , 500 ],  {label : 'height' ,  value :  200 ,  step :  20 })=  Scrubber (_. range (0 , 60 * 100 ),  {autoplay :  false ,  delay : 10 ,  label : 'frame' })embed (calcuvizspec ({models :  modelname ==  'snowflakes'  ?  [model] :  [], input_cursors :  [{f_in,  w_in,  width_in : 400 ,  height_in}], mark :  {type : 'text' , clip : true }, encodings :  {x :  { name :  'x' ,  type :  'quantitative' ,  scale :  { domain :  [0 , 400 ] } }, y :  { name :  'y' ,  type :  'quantitative' ,  sort : 'descending' ,  scale :  { domain :  [0 , height_in] } }, size :  { name :  'size' ,  type :  'quantitative' ,  legend :  false  }, angle :  {name : 'x' ,  type :  'quantitative' ,  scale :  {range :  [0 , 300 ] } }, // different snowflakes: detail :  {name :  'p_in' ,  type : 'nominal' ,  domain :  _. range (0 , num_snowflakes, 1 ) }, , width :  300 , height :  260 , // viz customizations: spec_post_process :  spec =>  {. encoding . text  =  { value :  'โ๏ธ' }. background  =  'darkred' ; return  spec,  {theme : 'vox' ,  renderer :  'canvas' })embed (calcuvizspec ({models :  modelname ==  'snowflakes'  ?  [model] :  [], input_cursors :  [{ w_in,  width_in : 400 ,  height_in}], mark :  {type : 'line' ,  point :  true ,  strokeWidth : '1px' }, encodings :  {x :  { name :  'f_in' ,  type :  'quantitative' ,  domain :  _. range (0 , 501 , 5 ) }, y :  { name :  'value' ,  type :  'quantitative' ,  independent :  true  }, color :  {name :  'p_in' ,  type : 'nominal' ,  domain :  [0 , 1 ,  snowflake_in] }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  ['x' , 'y' , 'angle' , 'size' , 'radius' , 'initial_angle' ],  sort :  'descending'  }, , width :  200 , height :  30 , // viz customizations: spec_post_process :  spec =>  {. config  =  { point :  { size :  10  } }. encoding . y . title  =  null ; return  spec,  {})=  Inputs. range ([0 , num_snowflakes],  {value :  10 ,  label :  'chosen extra โ๏ธ' ,  step : 1 })=  Inputs. range ([0 , 50 ],  {label : '# petals' ,  value :  7 ,  step :  1 })=  Inputs. range ([0 , 50 ],  {label : 'petal size' ,  value :  30 ,  step : 1 })=  Inputs. range ([0 , 2 ],  {label : 'petals roundy/spikiness' ,  value :  0.5 ,  step : 0.1 })=  Inputs. toggle ({label : 'big?' ,  value :  false })embed (calcuvizspec ({models :  modelname ==  'flower'  ?  [model] :  [], input_cursors :  [{petal_num_in, petal_size_in,  petals_roundy_spikiness_in}], mark :  'rect' ,  // heatmap encodings :  {x :  { name :  'column_in' ,  type :  'nominal' ,  domain :  _. range (0 , 101 , 1 ) }, y :  { name :  'row_in' ,  type :  'nominal' ,  domain :  _. range (0 , 101 , 1 ) }, color :  {'name' :  'value' ,  type :  'quantitative' ,  scale :  {scheme :  'brownbluegreen' ,  reverse :  true },  legend :  false ,   }, row :  {name :  'formula' ,  sort : 'descending' , domain :  ['result' ,  'petals' ,  'head' , 'face' ], width :  300 * (flower_big ?  1.5  :  0.5 ), height :  250 * (flower_big ?  1.5  :  0.5 ), // viz customizations: spec_post_process :  spec =>  {. encoding . x . axis  =  { grid :  false ,  domain :  false ,  ticks :  0 ,  labels :  false  }. encoding . y . axis  =  { grid :  false ,  domain :  false ,  ticks :  0 ,  labels :  false  }return  spec=  Inputs. select (["start of today" ,  "start of tomorrow" ],  {label : 'from' })md `** ${ (d3. format ('.2%' )(model. fractionLeftInYear ({date_in :  new  Date (),  timing_in})))} ** of year reamining, or visually:` . plot ({//marginLeft: 90, height : 80 , //width:5000, color :  { legend :  true  }, marks :  [    Plot. axisX ({anchor :  "top" }), , Plot. barX (sort :  1 ,  status : 'remaining' , percent : 100 * model. fractionLeftInYear ({date_in :  new  Date (),  timing_in})}, sort :  0 ,  status : 'complete' , percent : 100 * model. fractionIntoYear ({date_in :  new  Date (),  timing_in}), { x :  "percent" ,  fill :  "status" ,  sort : 'sort'  })]embed (calcuvizspec ({models :  modelname ==  'monte-carlo-pension-calculator'  ?  [model] :  [], input_cursors :  [{random_seed_in :  "something really random" , "age_0_in" :  30 , "fund_value_0_in" :  0 , "retirement_age_in" :  65 , "missed_contribution_age_in" :  0 , "salary_age_0_in" :  30 , "salary_0_in" :  30000 , "salary_inflation_rate_in" :  0.02 , "empee_contribution_rate_in" :  0.1 , "emper_matching_rate_in" :  1 , "contribution_charge_rate_in" : 0.1 , "management_charge_rate_in" :  0.01 }], mark :  {type :  'text' ,  fontSize : 20 ,  fontWeight :  'bold' }, encodings :  {x :  {name :  'formula' ,  type : 'nominal' ,  domain :  ['retirement_fund_value' ],  axis :  {labelFontSize :  60 }}, y :  {name :  'simulation_in' ,  type :  'ordinal' ,  domain : _. range (0 , 10 , 1 )}, text :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' }, //color: {name: 'formula', type:'nominal', domain: formulae_not_inputs, legend:false}, , width :  90 , // height:50, embed (calcuvizspec ({models :  modelname ==  'monte-carlo-pension-calculator'  ?  [model] :  [], input_cursors :  [{random_seed_in :  "something really random" , "age_0_in" :  30 , "fund_value_0_in" :  0 , "retirement_age_in" :  65 , "missed_contribution_age_in" :  0 , "salary_age_0_in" :  30 , "salary_0_in" :  30000 , "salary_inflation_rate_in" :  0.02 , "empee_contribution_rate_in" :  0.1 , "emper_matching_rate_in" :  1 , "contribution_charge_rate_in" : 0.1 , "management_charge_rate_in" :  0.01 }], mark :  {type :  'line' ,  point :  false ,  fontSize : 20 ,  fontWeight :  'bold' }, encodings :  {//x: {name: 'formula', type:'nominal', domain: ['retirement_fund_value'], axis: {labelFontSize: 60}}, x :  {name :  'simulation_in' ,  type :  'quantitative' ,  domain : _. range (0 , 100 , 1 )}, y :  {name :  'retirement_fund_value' ,  type :  'quantitative' ,  format : ',.0f' }, //color: {name: 'formula', type:'nominal', domain: formulae_not_inputs, legend:false}, , width :  300 ,  height : 100 , embed (calcuvizspec ({models :  modelname ==  'monte-carlo-pension-calculator'  ?  [model] :  [], input_cursors :  [{random_seed_in :  "something really random" , "age_0_in" :  30 , "fund_value_0_in" :  0 , "retirement_age_in" :  65 , "missed_contribution_age_in" :  0 , "salary_age_0_in" :  30 , "salary_0_in" :  30000 , "salary_inflation_rate_in" :  0.02 , "empee_contribution_rate_in" :  0.1 , "emper_matching_rate_in" :  1 , "contribution_charge_rate_in" : 0.1 , "management_charge_rate_in" :  0.01 }], mark :  {type :  'line' ,  point :  false ,  fontSize : 20 ,  fontWeight :  'bold' }, encodings :  {//x: {name: 'formula', type:'nominal', domain: ['retirement_fund_value'], axis: {labelFontSize: 60}}, color :  {name :  'simulation_in' ,  type :  'nominal' ,  domain : _. range (0 , 100 , 1 )}, x :  {name :  'age_in' ,  type :  'quantitative' ,  domain : _. range (20 , 70 , 1 )}, y :  {name :  'fund_value' ,  type :  'quantitative' ,  format : ',.0f' }, //color: {name: 'formula', type:'nominal', domain: formulae_not_inputs, legend:false}, , width :  300 ,  height : 400 , =  Inputs. toggle ({ value :  false ,  label :  "persist interaction ๐จ"  })=  {let  form =  Inputs. form ({age_0_in :  Inputs. range ([18 , 65 ],  {label : 'starting age' ,  value : q. get ('age_0_in' ) ??  50 ,  step: 1 }), retirement_age_in :  Inputs. range ([50 , 75 ],  {label : 'retirement age' ,  value : 65 ,  step : 1 }), fund_value_0_in :  Inputs. range ([0 , 1000000 ],  {label : 'starting fund value' ,  value : 0 ,  step : 1000 }), empee_contribution_rate_in :  Inputs. range ([0.00 , 0.30 ],  {label : 'empee contribution as % of salary' ,  value : 0.1 ,  step : 0.01 }), emper_matching_rate_in :  Inputs. range ([0.00 , 2 ],  {label : 'emper matching rate' ,  value : q. get ('emper_matching_rate_in' ) ??  1 ,  step: 0.02 }), salary_0_in :  Inputs. range ([5000 , 300000 ],  {label : 'salary' ,  value : q. get ('salary_0_in' ) ??  50000 ,  step: 1000 }), salary_age_0_in :  Inputs. range ([18 , 65 ],  {label : 'salary ref. age' ,  value : q. get ('salary_age_0_in' ) ??  50 ,  step: 1 }), salary_inflation_rate_in :  Inputs. range ([- 0.02 , 0.10 ],  {label : 'salary growth rate' ,  value : 0.02 ,  step : 0.002 }), unit_growth_rate_in :  Inputs. range ([- 0.02 * 0 /*banned (along with neg charges) for technical reasons with the visual !!*/ , 0.10 ],  {label : 'investment growth rate' ,  value : q. get ('unit_growth_rate_in' ) ??  0.05 ,  step: 0.002 }), contribution_charge_rate_in :  Inputs. range ([- 0.02 * 0 , 0.10 ],  {label : 'contribution charge rate' ,  value : 0.04 ,  step : 0.01 }), management_charge_rate_in :  Inputs. range ([- 0.02 * 0 , 0.10 ],  {label : 'management charge rate' ,  value : 0.01 ,  step : 0.002 }), missed_contribution_age_in :  Inputs. range ([15 , 75 ],  {label : 'Missed contribution (scenario)' ,  value : 15 ,  step : 1 }), ,  {template}); let  state =  false ;  // need to avoid circ. defn. . oninput  =  (d) =>  {console . log ('oninput' , d); if  (state ==  false ) {mutable inputs_history =  [... mutable inputs_history,  form. value ];  state =  true ;  mutable statey =  true ;  mutable changing =  d. srcElement . parentElement . previousElementSibling . textContent } mutable inputs_history =  [... mutable inputs_history. slice (0 ,- 1 ),  form. value ]}. onchange  =  () =>  { console . log ('onchange' ); state =  false ;  mutable statey =  s /* false */ ;  mutable inputs_history[mutable inputs_history. length - 1 ] =  form. value   }; return  form; md `**retirement fund projection = <span style="color:darkorange"><span style="font-size:2em">โฌ ${ f (new_value)} **</span></span> <span style="color: ${ good_guy ?  'green'  :  'red' } ; font-size:13px">** ${ statey ?  (good_guy ?  'โ'  :  'โ' )+ f (Math . abs (new_value -  old_value)) :  ' ' } **</span> *The retirement fund projection is the **pre-retirement area under inflows less the pre-retirement area under outflows** below.*` if  (! statey) return  md `.................................................................................................................................................................... ....................................................................................................................................................................` ; else  return  md `impact <span style="color: ${ good_guy ?  'green'  :  'red' } ">โฌ** ${ statey ?  (good_guy ?  'โ'  :  'โ' )+ f (Math . abs (new_value -  old_value)) :  ' ' } **</span> due to **ฮ  ${ changing} ** ( ${ d3. format (',.2f' )(old_in)}  ->  ${ d3. format (',.2f' )(new_in)} ) is visualised as **<span style="color:green">green boxes</span> - <span style="color:red">red boxes</span>** (inflows and outflows). ๐ฆ๐ฆ` =  calcudata ({models :  modelname ==  'pension-calculator'  ?  [model] :  [], input_domains :  {age_in : _. range (Math . min (inputs_history[input_cursor_id]. age_0_in , inputs_history[input_cursor_id- 1 ]. age_0_in )- 1 , Math . max (inputs_history[input_cursor_id]. retirement_age_in , inputs_history[input_cursor_id- 1 ]. retirement_age_in )+ addl_years, 1 )}, input_cursors :  inputs_history. map (d =>  ({... d,  /*age_in:d.retirement_age_in*/  /*  TODO  hacky? */ })), outputs :  [... formulae,  'check_cfs_vs_fund_value' ], pivot :  false //.map(d => d.formula == 'fund_charges' ? ({...d, value:-d.value}) : d) // TEMP9 . filter (d =>  d. input_cursor_id  ==  input_cursor_id ||  (d. input_cursor_id  ==  input_cursor_id- 1  &&  statey))// check_cfs_vs_fund_value is weakened as a check embed ({"$schema" :  "https://vega.github.io/schema/vega/v5.json" , //"background": "#eef", "padding" :  5 , "width" :  400 , //"height": 500, "style" :  "cell" , signals :   [      {"name" :  "offset" ,  "value" :  80 }, "name" :  "cellHeight" ,  "value" :  150 }, "name" :  "height" ,  "update" :  "2 * (offset + cellHeight)" }, "data" :  ["name" :  "data" , "values" :  data_all2, "transform" :  [type :  'formula' , //expr: 'datum.value >=0', This is bloody hard to debug and do correctly, and must be correct for posneg swaps etc. So just banning negative charges for now and will fix this later! expr :  'indexof(["management_charge","contribution_charge"],datum.formula) == -1' , as :  'posneg' , type :  'formula' ,  expr :  'abs(datum.value)' ,  as :  'value' , "type" :  "stack" , "groupby" :  ["age_in" ], "field" :  "value" , "sort" :  {"field" :  ["formula" ,  "input_cursor_id" ],  "order" :  ["ascending" , "descending" ]}, "as" :  ["value1_start" ,  "value1_end" ], "offset" :  "zero" , , name :  'data_old' , source :  'data' , "transform" :  [type :  'filter' , expr :  `datum.input_cursor_id ==  ${ input_cursor_id- 1 } ` ,  // eliminates chk , "type" :  "stack" , "groupby" :  ["age_in" ], "field" :  "value" , "sort" :  {"field" :  ["formula" ,  "input_cursor_id" ],  "order" :  ["ascending" , "descending" ]}, "as" :  ["value1_start" ,  "value1_end" ], "offset" :  "zero" , , scales :  [    {"name" :  "gscale" , "type" :  "band" , "range" :  [0 ,  {"signal" :  "height" }], "round" :  true , "domain" :  {"data" :  "data" , "field" :  "posneg" , /*"sort": {           "field": "posneg",           "op": "median",           "order": "descending"         }*/ , "name" :  "y_shared" , "type" :  "linear" , domainMin :  '0' , // domainMax: '60000', // doesn't generalise "domain" :  {"data" :  statey? "data_old"  :  'data' ,  "fields" :  ["value1_start" ,  "value1_end" ]},  // give this a cushion ?? "range" :  [{"signal" :  "cellHeight" },  0 ], "nice" :  false , "zero" :  true , , "name" :  "y_bad" ,  // how to share y scale on the stack total ?! "type" :  "linear" , domainMin :  '0' ,  domainMax :  {signal :  "domain('yyy')[1]*2" },  // doesn't generalise. Can I not x2 or update when dragging? "domain" :  {"data" :  "data" ,  "fields" :  ["value1_start" ,  "value1_end" ]},  // give this a cushion ?? "range" :  [{"signal" :  "cellHeight" },  0 ], "nice" :  false , "zero" :  true , , "name" :  "color" , "type" :  "ordinal" , "domain" :  {"data" :  "data" ,  "field" :  "formula" ,  "sort" :  ["empee_contribution_cost" , "check_cfs_vs_fund_value" ]}, "range" :  "category" , , "marks" :  [type :  'group' , name :  'posneg' , from :  {facet :  {data :  'data' , name :  'posnegs' , groupby :  'posneg' , data :  [{name :  'data_windowed' ,  source : 'posnegs' ,  transform :  ["type" :  "window" , "params" :  [null ], "as" :  ["B_value" ], "ops" :  ["lag" ], "fields" :  ["value" ], "sort" :  {"field" :  ["input_cursor_id" ], "order" :  ["descending" ], "groupby" :  ["formula" ,  "age_in" ], "frame" :  [- 1 ,  0 ], "type" :  "formula" , "expr" :  "datum.value-datum.B_value" , "as" :  "impact" , "type" :  "formula" ,  "expr" :  `datum.value > datum.B_value ? 0 : datum.B_value - datum.value` ,  "as" :  "adj" },  // I need to record the adj in id-1 for use in calc for id w. a follow-up window fn ! "type" :  "formula" ,  "expr" :  `datum.input_cursor_id ==  ${ input_cursor_id- 1 }  ? datum.value > datum.B_value ? datum.value - datum.B_value : datum.B_value - datum.value : datum.value` ,  "as" :  "BplusAminusB" },  // I need to record the adj in id-1 for use in calc for id w. a follow-up window fn ! "type" :  "formula" ,  "expr" :  `datum.adj` ,  "as" :  "adjposneg" }, "type" :  "window" , "params" :  [null ], "as" :  ["B_adj" ], "ops" :  ["lag" ], "fields" :  ["adj" ], "sort" :  {"field" :  ["input_cursor_id" ], "order" :  ["ascending" ], "groupby" :  ["formula" ,  "age_in" ], "frame" :  [- 1 ,  0 ], "type" :  "formula" , "expr" :  "datum.BplusAminusB-datum.B_adj" , "as" :  "BplusAminusB" , "type" :  "filter" ,  "expr" :  "abs(datum.BplusAminusB)>=0.01" }, ,  "name" :  "data_0" , "source" :  "data_windowed" , "transform" :  ["type" :  "stack" , "groupby" :  ["age_in" ], "field" :  "BplusAminusB" , "sort" :  {"field" :  ["formula" ,  "input_cursor_id" ],  "order" :  ["ascending" , "descending" ]}, "as" :  ["value_start" ,  "value_end" ], "offset" :  "zero" , type :  'formula' ,  'expr' :  'abs(datum.value_end)' ,  as :  'abs_value_end' }, type :  'formula' ,  'expr' :  'abs(datum.value_start)' ,  as :  'abs_value_start' }, /*{           "type": "filter",           "expr": "isValid(datum[\"age_in\"]) && isFinite(+datum[\"age_in\"]) && isValid(datum[\"value\"]) && isFinite(+datum[\"value\"])"         }*/ , "encode" :  {"enter" :  {"y" :  {"scale" :  "gscale" ,  "field" :  "posneg" ,  "offset" :  {"signal" :  "offset" }}, "height" :  {"signal" :  "cellHeight" }, "width" :  {"signal" :  "width" }, "stroke" :  {"value" :  "#ccc" }, marks :  ["name" :  "marks" , "type" :  "rect" , "style" :  ["bar" ], "from" :  {"data" :  "data_0" }, "encode" :  {"update" :  {"stroke" :  { "signal" :  `datum.posneg ? (datum.impact > 0 ? 'red' : 'green') : (datum.impact > 0 ? 'green' : 'red')` }, //"stroke": { "signal": `datum.posneg ? (datum.input_cursor_id == ${input_cursor_id} ? 100 : -100) : (datum.input_cursor_id == ${input_cursor_id} ? -100 : 100)`}, "strokeOpacity" :  {"value" :  1 }, "tooltip" :  {"signal" :  "{formula: datum['formula'], value: format(datum.value, ',.2f'), Age: datum['age_in']}" , "fill" :  {"scale" :  "color" ,  "field" :  "formula" }, "opacity" :  {"scale" :  "opacity" ,  "field" :  "input_cursor_id" }, "strokeWidth" :  {"scale" :  "strokeWidth" ,  "field" :  "input_cursor_id" }, "ariaRoleDescription" :  {"value" :  "bar" }, "description" :  {"signal" :  " \" age_in:  \"  + (format(datum[ \" age_in \" ],  \"\" )) +  \" ; value:  \"  + (format(datum[ \" value \" ],  \"\" )) +  \" ; formula:  \"  + (isValid(datum[ \" formula \" ]) ? datum[ \" formula \" ] :  \"\" +datum[ \" formula \" ])" , "x" :  {"scale" :  "x" ,  "field" :  "age_in" }, "width" :  {"signal" :  "max(0.25, bandwidth('x'))" }, "y" :  {"scale" :  y,  "field" :  "abs_value_end" }, "y2" :  {"scale" :  y,  "field" :  "abs_value_start" }, "scales" :  [    {"name" :  "y" , "type" :  "linear" , domainMin :  '0' , // domainMax: '60000', // doesn't generalise "domain" :  {"data" :  'data_0' ,  "fields" :  ["abs_value_start" ,  "abs_value_end" ]},  // give this a cushion ?? "range" :  [{"signal" :  "cellHeight" },  0 ], "nice" :  false , "zero" :  true , , "name" :  "x" , "type" :  "band" , "domain" :  {"data" :  "data_0" ,  "field" :  "age_in" ,  "sort" :  true }, "range" :  [0 ,  {"signal" :  "width" }], "paddingInner" :  0.2 , "paddingOuter" :  0.1 , name :  'opacity' ,  type :  'ordinal' ,  "domain" :  [input_cursor_id- 1  ,  input_cursor_id]  ,  range :  [1 , statey ?  0.3  :  1 ]}, name :  'strokeWidth' ,  type :  'ordinal' ,  "domain" :  [input_cursor_id,  input_cursor_id- 1 ]  ,  range :  [0 , 2 ]}, "name" :  "threshold" , "type" :  "threshold" , "domain" :  [- 99999999 ,  1 ], "range" :  ["green" ,  "red" ,  "green" ], "axes" :  ["scale" :  "x" , "orient" :  "bottom" , "grid" :  false , //"title": "age_in", //"labelAlign": "center", //"labelAngle": -10, "labelBaseline" :  "top" , "zindex" :  0 , ticks :  true , values :  _. range (15 , 105 , 5 ), "scale" :  y, "orient" :  "left" , "grid" :  true , gridOpacity :  0.2 , "title" :  ` ${ statey ?  'ฮ '  :  '' } fund value` , "labelOverlap" :  true , "tickCount" :  5 , "zindex" :  0 , , "type" :  "text" , "from" :  {"data" :  "posneg" }, "encode" :  {"enter" :  {"x" :  {"field" :  "width" ,  "mult" :  0.5 }, "y" :  {"field" :  "y" ,  offset :- 25 }, "fontSize" :  {"value" :  31 }, opacity :  {value :  0.7 }, //"fontWeight": {"value": "bold"}, "text" :  {"signal" :  "datum.datum.posneg ? 'Inflows ๐ฐ' : 'Outflows ๐ธ' " }, "align" :  {"value" :  "center" }, "baseline" :  {"value" :  "top" }, "fill" :  {"value" :  "#000" }, "legends" :  [{"fill" :  "color" ,  "symbolType" :  "square" ,  "title" :  "formula" }], "config" :  {"legend" :  {"orient" :  "right" ,  "layout" :  {"right" :  {"anchor" :  "middle" }}}=  Inputs. toggle ({label :  'shared y scale?' ,  value :  true })md `<br/>` . download (serialize (data_all2),  "pension_savings_calculator.csv" ,  "csv download" )md `<br/>` . download (new  Blob ([unpivot. toCSV ()],  {type :  "text/csv" }),  "pension_savings_calculator_unpivot.csv" ,  "csv download (pivoted)" )=  inputs_history[inputs_history. length - 2 ][changing_i_in]=  inputs_history[inputs_history. length - 1 ][changing_i_in]=  '' =  Array . from (viewof form. children . item (0 ). children ). map ((d, i) =>  ({d,  i})). find (e =>  e. d . textContent . includes (changing)). i =  Object . entries (viewof form. value )[changing_i][0 ]=  ['empee_contribution_tax_relief' ,  'empee_contribution_cost' ,  'emper_contribution' ,  'fund_growth' ,  'contribution_charge' ,  'management_charge' ]=  shared_y_scale ?  'y_shared'  :  'y' =  d3. format (',.2f' )=  false ; =  [JSON . parse (`{"age_0_in": ${ q. get ('age_0_in' ) ??  50 } ,"fund_value_0_in":0,"unit_growth_rate_in": ${ q. get ('unit_growth_rate_in' ) ??  0.05 } ,"retirement_age_in":65,"salary_0_in": ${ q. get ('salary_0_in' ) ??  50000 } ,"salary_inflation_rate_in":0.02,"empee_contribution_rate_in":0.1,"emper_matching_rate_in": ${ q. get ('emper_matching_rate_in' ) ??  1 } ,"contribution_charge_rate_in":0.04,"management_charge_rate_in":0.01,"salary_age_0_in": ${ q. get ('salary_age_0_in' ) ??  50 } ,"missed_contribution_age_in":15}` , JSON . parse (`{"age_0_in": ${ q. get ('age_0_in' ) ??  50 } ,"fund_value_0_in":0,"unit_growth_rate_in": ${ q. get ('unit_growth_rate_in' ) ??  0.05 } ,"retirement_age_in":65,"salary_0_in": ${ q. get ('salary_0_in' ) ??  50000 } ,"salary_inflation_rate_in":0.02,"empee_contribution_rate_in":0.1,"emper_matching_rate_in": ${ q. get ('emper_matching_rate_in' ) ??  1 } ,"contribution_charge_rate_in":0.04,"management_charge_rate_in":0.01,"salary_age_0_in": ${ q. get ('salary_age_0_in' ) ??  50 } ,"missed_contribution_age_in":15}` =  inputs_history. length - 1 =  2  // 2 =  model. retirement_fund_value (inputs_history[inputs_history. length - 1 ])=  model. retirement_fund_value (inputs_history[inputs_history. length - 2 ])=  new_value -  old_value >  0 =  Inputs. select (['shop-sliders' , 'shop-x-sales-price' ],  {value : 'shop-sliders' , label : 'Application โ' ,  width : 150 })=  Inputs. range ([5 , 7 ],  {step : 0.05 ,  label : 'purchase_price' ,  value : 5 ,  width : 200 })=  Inputs. range ([5 , 7 ],  {step : 0.05 ,  label : 'sales_price' ,  value : 6 ,  width : 200 ,  disabled :  shop_application ==  'shop-sliders'  ?  false  :  true })=  Inputs. range ([0 , 20000 ],  {step : 1000 ,  label : 'expenses' ,  value : 15000 ,  width : 200 })=  Inputs. range ([0 , 50000 ],  {step : 1000 ,  label : 'units' ,  value : 20000 ,  width : 200 })embed (calcuvizspec ({models :  modelname ==  'shop'  ?  [model] :  [], input_cursors :  [{sales_price_in, purchase_price_in, expenses_in, units_in}], mark :  {type :  'text' ,  fontSize : 20 ,  fontWeight :  'bold' }, encodings :  {y :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  axis :  {labelFontSize :  60 }}, text :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  legend : false }, , width :  90 , // height:50, spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . y . sort  =  ["profit" ];  spec. encoding . y . axis  =  {labelFontSize :  20 ,  labelFontWeight :  'bold' };  return  spec; }embed (calcuvizspec ({models :  modelname ==  'shop'  ?  [model] :  [], input_cursors :  [{purchase_price_in, expenses_in, units_in}], mark :  {type :  'text' ,  fontSize : 15 ,  fontWeight :  'bold' }, encodings :  {x :  {name :  'sales_price_in' ,  type : 'quantitative' ,  domain :  [5 , 6 , 7 ],   scale :  {domain : [4 , 7 ]}}, y :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  axis :  {labelFontSize :  60 }}, text :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  legend : false }, , width :  200 , // height:50, spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . y . sort  =  ["profit" ];  spec. encoding . y . axis  =  {labelFontSize :  15 ,  labelFontWeight :  'bold' };  return  spec; }embed (calcuvizspec ({models :  modelname ==  'shop'  ?  [model] :  [], input_cursors :  [{purchase_price_in, expenses_in, units_in}], mark :  'bar' , encodings :  {x :  {name :  'sales_price_in' ,  type : 'nominal' ,  domain :  _. range (5 , 7.01 ,. 1 ),  format : ',.2f' }, y :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' ,  independent :  true ,  zero :  false }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  legend : false }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  axis :  {labelFontSize :  60 }}, , width :  230 ,  height : 50 , spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . row . sort  =  ["profit" ];  spec. encoding . row . title  =  null ;  spec. encoding . row . header  =  {labelFontSize :  16 ,  labelFontWeight :  'bold' };   return  spec; }embed (calcuvizspec ({models :  modelname ==  'shop'  ?  [model] :  [], input_cursors :  [{purchase_price_in, expenses_in, units_in}], mark :  {type : 'line' ,  point : true }, encodings :  {x :  {name :  'sales_price_in' ,  type : 'quantitative' ,  domain :  _. range (5 , 7.01 ,. 2 ),  format : ',.2f' }, y :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' ,  independent :  true ,  zero :  false }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  legend : false }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  axis :  {labelFontSize :  60 }}, , width :  230 ,  height : 50 , spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . row . sort  =  ["profit" ];  spec. encoding . row . title  =  null ;  spec. encoding . row . header  =  {labelFontSize :  16 ,  labelFontWeight :  'bold' };   return  spec; }. bind (Inputs. range ([5 , 7 ],  {step : 0.20 ,  label : 'purchase_price_in' ,  value : 5 ,  width : 200 }),  viewof purchase_price_in). bind (Inputs. range ([0 , 20000 ],  {step : 1000 ,  label : 'expenses_in' ,  value : 15000 ,  width : 200 }),  viewof expenses_in)=  Object . values (introspection. cul_functions ). filter (d =>  d. reason  ==  'definition'  &&  inputs. indexOf (d. name + '_in' ) ==  - 1 ). map (d =>  d. name )//formulae_not_inputs = //['profit','sales','purchases','expenses'] import  {radio} from  "@jgrunik/inputs" embed (calcuvizspec ({models :  [modelname ==  'shop-demand-curve'  ?  model :  (modelname ==  'shop-demand-curve-modular'  ?  model :  {})], input_cursors :  [{purchase_price_in, expenses_in}], mark :  'bar' , encodings :  {x :  {name :  'sales_price_in' ,  type : 'nominal' ,  domain :  _. range (5 , 7.01 ,. 1 ),  format : ',.2f' }, y :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' ,  independent :  true ,  zero :  false }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  ff,  legend : false }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  ff}, , width :  230 ,  height : 50 , spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . row . sort  =  ["profit" ];  spec. encoding . row . title  =  null ;  spec. encoding . row . header  =  {labelFontSize :  16 ,  labelFontWeight :  'bold' };   return  spec; }=  [... formulae_not_inputs,  'units' ]embed (calcuvizspec ({models :  [modelname ==  'shop-demand-curve'  ?  model :  (modelname ==  'shop-demand-curve-modular'  ?  model :  {})], input_cursors :  [{purchase_price_in, expenses_in}], mark :  {type : 'line' ,  point : true }, encodings :  {x :  {name :  'sales_price_in' ,  type : 'quantitative' ,  domain :  _. range (5 , 7.01 ,. 2 )}, y :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' ,  independent :  true ,  zero :  false }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  ff,  legend : false }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  ff}, , width :  230 ,  height : 50 , spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . row . sort  =  ["profit" ];  spec. encoding . row . title  =  null ;  spec. encoding . row . header  =  {labelFontSize :  16 ,  labelFontWeight :  'bold' };   return  spec; }/*viewof annual_payment_in = Inputs.range([0,1000], {step: 100, value:1000, label: 'annual_payment_in'}) viewof interest_rate_in = Inputs.range([0,0.5], {step:0.01, label: 'interest_rate_in', value: 0.02}) viewof duration_in = Inputs.range([0,10], {step:1, value:5, label:'duration_in'})*/ // trying out lil-gui controls function  r2 (){let  preset =  {}let  savings_options =  {annual_payment_in :  1000 , interest_rate :  2 , duration_in :  5 , savePreset :  () =>  {// save current values to an object =  gui. save (); . enable (); , loadPreset :  () =>  {. load ( preset ); const  container = DOM. element ("div" ); const  form= DOM. element ("form" )   //autoPlace is true by default, which will display outside cell,so change to false. const  gui =  new  dat. GUI ({ autoPlace :  false ,  closeOnTop :  true  }). add (savings_options,  'annual_payment_in' ,  0 ,  1000 ). onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true })))  . add (savings_options,  'interest_rate' ,  - 1 ,  10 ,  1 ). onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true })))  . add (savings_options,  'duration_in' ,  0 ,  10 ,  1 ). onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true })))  . add ( savings_options,  'savePreset'  ); const  loadButton =  gui. add ( savings_options,  'loadPreset'  ); . disable (); . append (gui. domElement ). value = savings_options. append (form)return   container=  r2 ()embed (calcuvizspec ({models :  modelname ==  'savings'  ?  [model] :  [], input_cursors :  [{... c,  interest_rate_in :  c. interest_rate / 100 }], mark :  {type :  'text' ,  fontSize : 15 ,  fontWeight :  'bold' }, encodings :  {x :  {name :  'formula' ,  type : 'nominal' ,  domain :  ['balance' , 'interest' , 'deposits' , 'interest_rate' ],  axis :  {labelFontSize :  60 }}, y :  {name :  'year_in' ,  type : 'nominal' ,  domain :  _. range (0 , c. duration_in + 1 , 1 ),  sort : 'ascending' }, text :  {name :  'value' ,  type :  'quantitative' ,  format : ',.2f' }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  ['balance' , 'interest' , 'deposits' , 'interest_rate' ],  legend : false }, , "width" :  340 ,  height : 160 /*viewof annual_payment_in = Inputs.range([0,1000], {step: 100, value:1000, label: 'annual_payment_in'}) viewof interest_rate_in = Inputs.range([0,0.5], {step:0.01, label: 'interest_rate_in', value: 0.02}) viewof duration_in = Inputs.range([0,10], {step:1, value:5, label:'duration_in'})*/ function  r3 (){let  preset =  {}let  options =  {skewness_in :  - 1 , step_size :  0.25 , savePreset :  () =>  {// save current values to an object =  gui. save (); . enable (); , loadPreset :  () =>  {. load ( preset ); const  container = DOM. element ("div" ); const  form= DOM. element ("form" )   //autoPlace is true by default, which will display outside cell,so change to false. const  gui =  new  dat. GUI ({ autoPlace :  false ,  closeOnTop :  true  })/*      !!! IMPORTANT:     special handle for color,or observable will not observe the change imediately.        same with range control     so you can simplely add .onChange for all control to dispatchEvent again   */ //gui.addColor(options,'color').onChange(v=>form.dispatchEvent(new CustomEvent("input", {bubbles: true})))   //gui.add(options,'types',['one','two','three']) //gui.add(options,'speed',{slow:1,fast:40}) //gui.add(options,'age',1,40) //gui.addFolder('inputs โ๏ธ') . add (options,  'skewness_in' ,  - 2 ,  2 ). onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true })))  . add (options,  'step_size' ,  0.15 ,  0.3 ). onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true })))  . add ( options,  'savePreset'  ); =  gui. save (); const  loadButton =  gui. add ( options,  'loadPreset'  ); //loadButton.disable(); . append (gui. domElement ). value = options. append (form)return   container=  r3 ()=  calcudata ({models : modelname ==  'sp-padrao'  ?  [model] :  [], input_domains :  {x_in :  _. range (9 , 21 , sp_padrao_options. step_size ),  y_in :  _. range (- 4 , 7 , sp_padrao_options. step_size )}, input_cursors :  [{... sp_padrao_options}],  outputs :  ['x' , 'y' , 'color' ], pivot : true //Inputs.table(sp_padrao_data) embed ({"data" :  {"name" :  "projection" , "mark" :  {"type" :  "point" , "tooltip" :  true , "strokeWidth" :  5 , "clip" :  true , "encoding" :  {"x" :  {"field" :  "x_in" , "type" :  "quantitative" , "scale" :  {"zero" :  false , "domain" :  [9 , 21 , "axis" :  {"labelAngle" :  0 , "orient" :  "top" , "y" :  {"field" :  "y" , "type" :  "quantitative" , "scale" :  {"zero" :  false , "domain" :  [- 3.5 , 6.7 , "detail" :  {"field" :  "x_in" , "type" :  "nominal" , "shape" :  {"field" :  "y_in" , "type" :  "nominal" , "scale" :  {"range" :  ["square" , "color" :  {"field" :  "color" , "type" :  "nominal" , "scale" :  {"range" :  (color_range. includes ("๐ข๐ก๐ต" ) ?  ["green" , "yellow" , "blue" ]:  (color_range. includes ("xyz" ) ?  ["black" ,           "#f0f0f0" ]:  [          "black" ,           "#f0f0f0"         ])), "size" :  {"value" :  100 , "opacity" :  . length  ?  { "field" :  "y_in" } :  {}, "config" :  {"legend" :  {"disable" :  true , "width" :  300  *  (sp_padrao_size+ 1 ), "height" :  250  *  (sp_padrao_size+ 1 ), "datasets" :  {'projection' : sp_padrao_data}=  Inputs. select (["classic โซโช" ,  "๐ข๐ก๐ต" ],  {label :  "color scheme" ,  value : "classic โซโช" })=  Inputs. checkbox (["opacity" ],  {label :  "effects" ,  value :  ["opacity" ]})=  Inputs. toggle ({label : 'large' })// trying out lil-gui controls function  r5 (){let  preset =  {}let  options =  {operation_in :  'union' , i_in :  10 , alpha_in :  69 , beta_in :  38 , dist_in :  1.88 , fov_in :  34 , screen_width_in :  30 , screen_height_in :  30 , shape_A_in :  'cube' ,  shape_x_A_in :  0 ,  shape_y_A_in : 0 ,  shape_z_A_in : 0 ,  shape_size_A_in :  0.4 , shape_B_in :  'sphere' ,  shape_x_B_in :  0 ,  shape_y_B_in : 0.3 ,  shape_z_B_in : 0 ,  shape_size_B_in :  0.4 , savePreset :  () =>  {// save current values to an object =  gui. save (); . enable (); , loadPreset :  () =>  {. load ( preset ); const  container = DOM. element ("div" ); const  form= DOM. element ("form" )   //autoPlace is true by default, which will display outside cell,so change to false. const  gui =  new  dat. GUI ({ autoPlace :  false ,  closeOnTop :  true  }). add (options,  'i_in' ,  0 ,  50 ,  1 ). name ('# raymarching iterations' ); . add (options,  'operation_in' ,  ['union' ,  'intersection' ,  'B-A' ,  'A-B' ]). name ('operation?' ); const  shape_A =  gui. addFolder ('shape A' ); . add (options,  'shape_A_in' ,  ['sphere' ,  'cube' ,  'teapot' ]); . add (options,  'shape_x_A_in' ,  - 1 ,  1 ,  0.1 ); . add (options,  'shape_y_A_in' ,  - 1 ,  1 ,  0.1 ); . add (options,  'shape_z_A_in' ,  - 1 ,  1 ,  0.1 ); . add (options,  'shape_size_A_in' ,  0 ,  1 ,  0.1 ); const  shape_B =  gui. addFolder ('shape B' ); . add (options,  'shape_B_in' ,  ['sphere' ,  'cube' /*, 'teapot'*/ ]); . add (options,  'shape_x_B_in' ,  - 1 ,  1 ,  0.1 ); . add (options,  'shape_y_B_in' ,  - 1 ,  1 ,  0.1 ); . add (options,  'shape_z_B_in' ,  - 1 ,  1 ,  0.1 ); . add (options,  'shape_size_B_in' ,  0 ,  1 ,  0.1 ); . onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true }))); . add ( options,  'savePreset'  ); =  gui. save (); const  loadButton =  gui. add ( options,  'loadPreset'  ); //loadButton.disable(); . append (gui. domElement ). value = options. append (form)return   container=  r5 ()=  Inputs. range ([- 180 , 180 ],  {value :  69 ,  step : 1 ,  label :  "alpha_in" })=  Inputs. range ([- 180 ,  180 ],  {value :  38 ,  step : 1 ,  label :  "beta_in" })=  Inputs. range ([1 ,  3 ],  {value :  1.88 ,  step : 0.01 ,  label :  "dist_in" })=  Inputs. range ([1 , 180 ],  {value :  34 ,  step : 1 ,  label :  "fov_in" })=  Inputs. select (['sphere' , 'cube' , 'cube_with_holes' , 'cube_with_holes2' , 'cube_with_holes3' , 'cube_with_holes4' , 'cube_and_torus' , 'teapot' , ,  {value :  'cube_with_holes4' ,  label :  "sdf_shape" ,  disabled :  true })=  Inputs. range ([0 , 150 ],  {value :  30 ,  step : 10 ,  label :  "screen_width_in" })=  Inputs. range ([0 , 150 ],  {value :  30 ,  step : 10 ,  label :  "screen_height_in" })embed (calcuvizspec ({models :  modelname ==  'raymarching'  ?  [model] :  [], input_cursors :  [{alpha_in,  beta_in,  dist_in,  fov_in,  shape_A_in :  'cube' ,  screen_width_in,  screen_height_in,... raymarching_inputs}], mark :  'rect' ,  // heatmap encodings :  {x :  {'name' :  'xq_in' ,  type : 'nominal' ,  domain :  _. range (0 , screen_width_in, 1 )}, y :  {'name' :  'yq_in' ,  type : 'nominal' ,  domain :  _. range (0 , screen_height_in, 1 )}, color :  {'name' :  'value' ,  type : 'quantitative' }, row :  {'name' :  'formula' ,  domain :  ['brightness_2' /* can put more formulae here */ ]}, spec_post_process :  spec =>  {. width  =  300 ; . height  =  250 ; . encoding . y . sort  =  'descending' . encoding . x . axis  =  { grid :  false ,  domain :  false ,  ticks :  0 ,  labels :  false  }. encoding . y . axis  =  { grid :  false ,  domain :  false ,  ticks :  0 ,  labels :  false  }. config  =  {"legend" :  {"disable" :  true }}. resolve  =  {scale :  {color : 'independent' }}. mark . clip  =  true ; return  spec; =  require ('/models/raymarching/sdf_composed.js' )=  calcudata ({models :  modelname ==  'raymarching'  ?  [sdf] :  [], input_cursors :  [{z_in :  0 ,  alpha_in,  beta_in,  dist_in,  fov_in,  shape_A_in :  'cube' ,  screen_width_in,  screen_height_in,... raymarching_inputs,  i_in : 100 }], input_domains :   { x_in :  _. range (- 2 , 2.001 , 0.1 / 4 ), y_in :  _. range (- 2.5 , 2.001 , 0.1 / 4 ) }, outputs : ['sdf' ]md `<br/>Plot of sdf_composed sdf @ z=0, iteration 100:` =  Plot. plot ({//width:400, //height:260, style :  {background : 'rgba(0,0,0,0)'  // not applied to legend , color :  {legend : true ,  reverse :  true ,  label :  `sdf` }, x :  { label : 'x_in' ,  tickFormat : d3. format ('.2f' ),  ticks : _. uniq (data_sdf. map (d =>  d. x_in ))}, y :  { label :  'y_in' ,  tickFormat : d3. format ('.2f' ),  ticks : _. uniq (data_sdf. map (d =>  d. y_in ))}, marks :  [. contour (data_sdf,  { // https://observablehq.com/plot/marks/contour x :  "x_in" , y :  "y_in" , fill :  "value" ,  interpolate :  Plot. interpolateNearest , blur : false , interval : 0.15 , stroke :  "black" =  {let  form =  Inputs. form ({mrr_in :  Inputs. range ([0 ,  500000 ],  {label :  "monthly revenue (mrr)" ,  step :  5000 ,  value : 155000 }), mrr_growth_in :  Inputs. range ([- 0.3 ,  . 8 ],  {label :  "mrr growth factor" ,  step :  0.01 ,  value : 0.15 }), vc_1_in :  Inputs. range ([0 ,  1e7 ],  {label :  "venture capital R1 (vc)" ,  step :  100000 ,  value :  1000000  }), vc_2_in :  Inputs. range ([0 ,  1e7 ],  {label :  "venture capital R2 (vc)" ,  step :  100000 ,  value :  2000000  }), salary_per_employee_in :  Inputs. range ([0 ,  30000 ],  {label :  "salary/employee" ,  step :  1000 }), employees_0_in :  Inputs. range ([0 ,  50 ],  {label :  "employees @ start" ,  step :  1 ,  value : 26 }), new_employees_per_month_in :  Inputs. range ([0 ,  10 ],  {label :  "new employees per mth" ,  step :  1 ,  value : 2 }), rent_in :  Inputs. range ([0 ,  300000 ],  {label :  "rent" ,  step :  10000 ,  value : 200000 }), ,  /*{template}*/ ); return  form=  Inputs. range ([0 ,  36 ],  {label :  "last month (0-index)" ,  step :  1 ,  value : 17 })=  Inputs. range ([- 0.1 ,  . 2 ],  {label :  "npv interest rate (monthly!)" ,  step :  0.001 ,  value : 0.0 })=  Inputs. checkbox (["โฐ gridlines โฐ" ],  {label :  "" ,  value :  ["" /*"โฐ gridlines โฐ"*/ ]})=  calcudata ({models : modelname ==  'saas-cashflows'  ?  [model] :  [], input_domains :  {month_in :  _. range (- 1 , last_month_in+ 1 , 1 )}, input_cursors :  [{... saas_form,  npv_i_in,  last_month_in}],  outputs :  formulae_not_inputs, //pivot:true md `The NPV over the  ${ last_month_in+ 1 }  months is **โฌ ${ d3. format (',.2f' )(model. npv ({... saas_form,  last_month_in,  npv_i_in,  month_in :- 1 }))} **` embed ({"$schema" :  "https://vega.github.io/schema/vega-lite/v5.json" , "data" :  {"name" :  "projection" }, "datasets" :  {projection : saas_data}, "height" :  200 ,  "width" :  230 , "transform" :  [{'filter' :  'indexof(datum.formula, "_cf") != -1' }], "encoding" :  {"x" :  {"field" :  "month_in" }, "y" :  {"field" :  "value" ,  "type" :  "quantitative" ,  "axis" :  {"grid" :  grid. length ,  labelAngle : 90 }}, , "layer" :  ["transform" :  [{"filter" :  "datum.formula != 'total_cf'" }], "mark" :  {"type" :  "bar" ,  "tooltip" :  true }, "encoding" :  {"color" :  {"field" :  "formula" ,  "type" :  "nominal" }, "transform" :  [{"filter" :  "datum.formula == 'total_cf'" }], "mark" :  {"type" :  "line" ,  "point" :  true ,  size : 3 ,  "tooltip" :  true }, "encoding" :  {"color" :  {"value" :  "black" }, "opacity" :  {"value" :  1 }, "strokeWidth" :  {"value" :  2 }, "y" :  {"field" :  "value" ,  "type" :  "quantitative" ,  "title" :  "total_cf" },  {theme : 'fivethirtyeight' })embed (calcuvizspec ({models :  modelname ==  'saas-cashflows'  ?  [model] :  [], input_cursors :  [{... saas_form,  npv_i_in,  last_month_in}], mark :  'bar' , encodings :  {x :  {name :  'month_in' ,  type : 'nominal' ,  domain :  _. range (- 1 , last_month_in+ 1 , 1 )}, y :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' ,  independent :  true ,  zero :  false }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  legend : false }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  axis :  {labelFontSize :  60 }}, , width :  230 ,  height : 50 , spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . row . sort  =  ["profit" ];  spec. encoding . row . title  =  null ;  spec. encoding . row . header  =  {labelFontSize :  16 ,  labelFontWeight :  'bold' };   return  spec; }embed (calcuvizspec ({models :  modelname ==  'saas-cashflows'  ?  [model] :  [], input_cursors :  [{... saas_form,  npv_i_in,  last_month_in}], mark :  {type : 'line' ,  point : true /*, interpolate: 'step-after'*/ }, encodings :  {x :  {name :  'month_in' ,  type : 'nominal' ,  domain :  _. range (- 1 , last_month_in+ 1 , 1 )}, y :  {name :  'value' ,  type :  'quantitative' ,  format : ',.0f' ,  independent :  true ,  zero :  false }, color :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  legend : false }, row :  {name :  'formula' ,  type : 'nominal' ,  domain :  formulae_not_inputs,  axis :  {labelFontSize :  60 }}, , width :  230 ,  height : 50 , spec_post_process :  spec =>  { spec. encoding . y . title  =  null ;  spec. encoding . row . sort  =  ["profit" ];  spec. encoding . row . title  =  null ;  spec. encoding . row . header  =  {labelFontSize :  16 ,  labelFontWeight :  'bold' };   return  spec; }=  location_ui ({value :  [- 6.475492 ,  53.694712 ],  label :  "Co-ords" })=  Inputs. toggle ({label :  "clip1" ,  value :  true })=  Inputs. toggle ({label :  "clip2" ,  value :  true })import  {  location as  location_ui } from  "@roelandschoukens/inputs" =  Inputs. date ({label :  "start date" ,  value :  new  Date (2023 , 11 , 22 , 18 , 0 , 0 )})md `**offset days:**` =  date_fns. addDays (date_in,  offset)=  Inputs. toggle ({label : 'fast' ,  value : false })=  Scrubber (_. range (0 , 365 , fast?  2  : 1 ),  {autoplay :  false ,  delay : fast ?  3  :  60 ,  label : 'offset' })=   d3. timeFormat ('%B' )(d)embed (calcuvizspec ({models :  modelname ==  'sunsets'  ?  [model] :  [], input_cursors :  [{lat_in : point[1 ],  lng_in : point[0 ],  obj_in : 'sun' , , mark :  {type : 'point' , size : 2000 , filled : true , tooltip : true ,  clip : clip1}, encodings :  {y :  {grid :  false , name :  'altitude_obj' ,  type : 'quantitative' ,  scale :  {zero :  false ,  domain :  [0 , 1 ]}}, x :  {grid : false , name :  'azimuth_obj' ,  type :  'quantitative' ,  scale :  {zero :  false ,  domain :  [0 , 2 ]}}, color :  {name :  'date_in' ,  sort : 'ascending' ,  type :  'quantitative' ,  legend : true ,  domain :  . timeHour . range (new  Date (d). setHours (clip2 ?  13  :  0 ), new  Date (d). setHours (clip2?  22  :  24 ), 1 , }//_.range(-60,24*60,30).map(d => date_fns.addMinutes(new Date(date_in), d))}, // row: additional months ..? , width : 500 *. 6  *  (clip1 ?  1  :  0.5 ), height : 400 *. 6  *  (clip1 ?  1  :  0.5 ), spec_post_process :  s =>  { s. encoding . color . timeUnit  =  'hours' ;  s. title  =  title;  s. titleFontSize  =  40 ; . encoding . color . scale  =  {"scheme" :  "lightmulti" };  return  s },  {actions : true ,  config :  {view :  {fill : '#fdfaff' }}})/*viewof annual_payment_in = Inputs.range([0,1000], {step: 100, value:1000, label: 'annual_payment_in'}) viewof interest_rate_in = Inputs.range([0,0.5], {step:0.01, label: 'interest_rate_in', value: 0.02}) viewof duration_in = Inputs.range([0,10], {step:1, value:5, label:'duration_in'})*/ function  r4 (){let  preset =  {}let  options =  {no_points :  10000 , savePreset :  () =>  {// save current values to an object =  gui. save (); . enable (); , loadPreset :  () =>  {. load ( preset ); const  container = DOM. element ("div" ); const  form= DOM. element ("form" )   //autoPlace is true by default, which will display outside cell,so change to false. const  gui =  new  dat. GUI ({ autoPlace :  false ,  closeOnTop :  true  })/*      !!! IMPORTANT:     special handle for color,or observable will not observe the change imediately.        same with range control     so you can simplely add .onChange for all control to dispatchEvent again   */ //gui.addColor(options,'color').onChange(v=>form.dispatchEvent(new CustomEvent("input", {bubbles: true})))   //gui.add(options,'types',['one','two','three']) //gui.add(options,'speed',{slow:1,fast:40}) //gui.add(options,'age',1,40) . add (options,  'no_points' ,  0 ,  20000 ). onChange (v=> form. dispatchEvent (new  CustomEvent ("input" ,  {bubbles :  true })))  . add ( options,  'savePreset'  ); =  gui. save (); const  loadButton =  gui. add ( options,  'loadPreset'  ); //loadButton.disable(); . append (gui. domElement ). value = options. append (form)return   container=  r4 ()=  calcudata ({models : modelname ==  'copacabana'  ?  [model] :  [], input_domains :  {p_in : _. range (0 , copacabana_options. no_points )}, input_cursors :  [{}],  outputs :  ['x' , 'y' , 'color' ], pivot : true . table (copacabana_data)embed ({"data" :  {"name" :  "projection" }, "mark" :  {"type" :  "point" ,  "tooltip" :  true ,  "opacity" :  0.7 ,  "strokeWidth" :  5 }, "encoding" :  {"x" :  {"field" :  "x" , "type" :  "quantitative" , "scale" :  {"zero" :  false }, "axis" :  {"labelAngle" :  0 ,  "orient" :  "top" }, "y" :  {"field" :  "y" ,  "type" :  "quantitative" ,  "scale" :  {"zero" :  false }}, "color" :  {"field" :  "color" ,  "type" :  "nominal" ,  scale}, "shape" :  {"field" :  "p_in" ,  "type" :  "nominal" }, "size" :  {"value" :  50 }, "config" :  {"legend" :  {"disable" :  true }}, "width" :  300 * (copacabana_size+ 1 ), "height" :  250 * (copacabana_size+ 1 ), "datasets" :  {projection : copacabana_data}})=  {if  (copacabana_color_range ==  'classic โซโช' )return  ({"range" :  ["#dddde9" , "black" ]})else  if  (copacabana_color_range ==  'colorful ๐จ' )return  ({scheme :  'category10' })else  if  (copacabana_color_range ==  '๐ข๐ก๐ต' )return  ({range :  ["green" , "yellow" , "blue" ]})else return  ({scheme :  copacabana_color_range})=  Inputs. select (["classic โซโช" ,  "colorful ๐จ" ,  "๐ข๐ก๐ต" ,  "blues" ,  "browns" ],  {label :  "color scheme" ,  value : "classic โซโช" })=  Inputs. toggle ({label : 'large' })=  Inputs. range ([- 1 , 6 ],  {value : 2 ,  label : 'actual_interest_rate_rate_co_in' ,  step : 1 })=  calcudata ({models : modelname ==  'savings-rec'  ?  [model] :  [], input_domains :  {year_in : _. range (0 , 6 ),  actual_interest_rate_co_in :  [actual_interest_rate_co_in- 1 , actual_interest_rate_co_in]}, input_cursors :  [{annual_payment_in : 1000 }],  outputs :  ['balance' , 'interest' , 'deposits' , 'interest_rate' ]=  embed ("data" :  {"name" :  "data" }, "transform" :  ["window" :  [{"op" :  "lag" ,  "field" :  "value" ,  "as" :  "A_value" }], "groupby" :  ["formula" , "year_in" ], "sort" :  [{"field" :  "actual_interest_rate_co_in" }], "frame" :  [- 1 ,  0 ], "calculate" :  "datum.value-datum.A_value" ,  "as" :  "impact" }, , "encoding" :  {"y" :  {"field" :  "year_in" ,  "type" :  "nominal" ,  "sort" :  "ascending" }, "color" :  {"field" :  "formula" ,  "type" :  "nominal" }, "x" :  {"field" :  "formula" , "type" :  "nominal" , "axis" :  {"labelAngle" :  0 ,  "orient" :  "top" ,  "labelLimit" :  90 }, layer :  [{"params" :  [{"name" :  "y" ,  "select" :  { "type" :  "point" ,  "on" :  "mouseover" ,  "nearest" :  true ,  "encodings" :  ["y" ],  "toggle" : false }}], "mark" :  {"type" :  "text" , "fontSize" :  15 , "fontWeight" :  "bold" , "tooltip" :  false , "transform" :  [{"filter" :  `datum.actual_interest_rate_co_in !=  ${ actual_interest_rate_co_in- 1 } ` }, //{"calculate": "datum.year_in > datum.actual_interest_rate_co_in", "as": "fut"} , encoding :  {"opacity" :              {"condition" :  {"test" :  "datum.year_in > datum.actual_interest_rate_co_in" ,  "value" :  0.5 },  "value" :  1 }, // {"field": "fut", "type": "nominal", "sort": "descending"}, "text" :  {"field" :  "value" , "type" :  "quantitative" , "format" :  ",.2f" , "axis" :  {"format" :  ",.2f" }, , {"mark" :  {"type" :  "text" ,  dx : 20 ,  dy :  - 10 , "fontSize" :  12 , "fontWeight" :  "bold" , // "tooltip" :  false , "transform" :  [{"filter" :  `abs(datum.impact) >= 0.00001 && datum.actual_interest_rate_co_in !=  ${ actual_interest_rate_co_in- 1 } ` }], encoding :  {"color" :  {"value" :  "green" ,  "condition" :  {"test" :  "datum.impact < 0" ,  "value" :  "red" }}, "opacity" :              {"condition" :  {"test" :  "datum.year_in > datum.actual_interest_rate_co_in" ,  "value" :  0.5 },  "value" :  1 }, "text" :  {"field" :  "impact" , "type" :  "quantitative" , "format" :  ",.2f" , "axis" :  {"format" :  ",.2f" }, , "config" :  {"legend" :  {"disable" :  true }}, "datasets" :  { data :  savings_rec_data, "width" :  340 ,  height : 160 . addSignalListener ("y" ,  (_,  r) =>  { // this didn't improve perf a lot vs event listener: nearly every mousemove is a signal change, I suppose //if (observable_world == 0) return; . value  =  r. year_in [0 ]; . dispatchEvent (new  CustomEvent ('input' ),  {bubbles : true }); &&  1 =  ({x_in :  _. range (- 3 , 3.001 , 0.25 ), y_in :  _. range (- 3.5 , 5.001 , 0.25 ), =  calcudata ({models : modelname ==  'heart-contour'  ?  [model] :  [], input_cursors :  [{}], , outputs : ['f' ]=  Plot. plot ({//width:400, //height:260, style :  {background : 'rgba(0,0,0,0)'  // not applied to legend , color :  {legend,  reverse :  true ,  label :  `f(x_in,y_in)` }, x :  { label : 'x_in' ,  tickFormat : d3. format ('.2f' ),  ticks : _. uniq (data. map (d =>  d. x_in ))}, y :  { label :  'y_in' ,  tickFormat : d3. format ('.2f' ),  ticks : _. uniq (data. map (d =>  d. y_in ))}, marks :  [. contour (data,  { // https://observablehq.com/plot/marks/contour x :  "x_in" , y :  "y_in" , fill :  "value" ,  interpolate :  Plot. interpolateNearest , blur, interval : intervals=  Inputs. toggle ({value : false ,  label : 'legend' })=  Inputs. range ([1 , 10 ], {value : 4 , label :  'contour intervals' ,  step : 0.1 ,  width : 100 })=  Inputs. range ([0 , 50 ], {value : 4 , label :  'blur' ,  step : 0.5 ,  width : 100 })import  {interval} from  '@mootari/range-slider' =  {let  form =  Inputs. form ({angle_in :  Inputs. range ([- 2 , 2 ],  {value :  1 ,  step : 0.002 ,  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 :  "Air resistance"  }), t_interval :  interval ([0 , 100 ],  {step : 1 ,  label :  'time โ๏ธ' ,  color :  'skyblue' ,  value : [0 , 60 ],  width : 200 }), ; let  state =  false ; . 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 ]}. onchange  =  () =>  { console . log ('onchange' ); state =  false ;  mutable inputs_history_projectile[mutable inputs_history_projectile. length - 1 ] =  form. value   }; return  form; =  [JSON . parse (`{"angle_in":1,"power_in":30,"g_in":1,"drag_coefficient_in":0.01,"t_interval":[0,60]}` =  true //  NOTE : adding some hurried customisation and experimentation for fireworks effect! should be cleaner embed (calcuvizspec ({models :  modelname ==  'projectile'  ?  [model] :  [], 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 )}, //opacity: {name: 't_in', type: 'quantitative', sort:'descending', scale:{domain:[0,100]}, legend:null/*not working => spec_post_process*/, domain: _.range(form_projectile.t_interval[0],form_projectile.t_interval[1]+0.1,1)}, y :  {name :  'y' ,  type : 'quantitative' ,  sort :  'ascending' ,  scale : {domain : [- 1200 , 800 ],  sort :  'descending' }}, x :  {name :  'x' ,  grid : false ,  type :  'quantitative' ,  scale : {domain : [- 30 , 40 ]}}, color :  {name :  'input_cursor_id' ,  type :  'nominal' }, , width :  300 ,  height :  220 , spec_post_process :  (spec) =>  {spec. encoding . y . axis  =  {tickCount : 1 ,  grid : true };  spec. datasets . tags  =  tags; . encoding . x . axis  =  {tickCount : 3 ,  grid : false }; . transform  =  ["lookup" :  "input_cursor_id" ,  "from" :  {"data" :  {"name" :  "tags" },  "key" : "i" ,  "fields" :  ["tag" ]},  "as" :  ["tag" ]}; . encoding . color  =  {"field" :  "tag" ,  "type" :  "nominal" ,  "sort" :  {"field" :  "input_cursor_id" },  "title" :  "scenario" }return  spec}=  [{i : 0 ,  tag : 'initial' },... 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 ]} ` }))]=  Inputs. range ([- 1 , 2 ],  {value :  0.90 ,  step : 0.01 ,  label :  "dampener factor" })//viewof sales_price_in = Inputs.range([5,7], {step:0.20, label:'sales_price_in', value:6, disabled: true}) =  Inputs. range ([- 5 , 5 ],  {value :  3 ,  step : 0.5 ,  label :  "dx" })=  interval ([0 , 120 ],  {step :  1 , value :  [0 , 60 ], label :  'time' , width : 200 =  Inputs. range ([0 , 60 ],  {step :  5 ,  value : 40 ,  label :  'frame rate' })=  calcudata ({models : modelname ==  'bounce-6'  ?  [model] :  [], input_domains :  {t_in :  _. range (t[0 ],  t[1 ],  1 )}, input_cursors :  [{dx_in, dampener_in}],  outputs :  ['x' , 'y' , 'dy' , 'bouncing' ], pivot :  true p5 (sketch =>  {//sync;  . setup  =  function  () {. createCanvas (300 ,  120 ); . frameRate (frame_rate); ; var  t =  0 ,  w,  h; . draw  =  function  () {. background ("#fed" ); . stroke ("black" ); if  (bounce_data[t] ==  undefined ) t =  0 ; =  30 ;  h =  30 ; if  (bounce_data[t]. bouncing ) { w =  36 ;  h =  18 ;  }. ellipse (100 + bounce_data[t]. x ,  5 + 100  -  bounce_data[t]. y ,  w,  h);  // poor on move to calcudata but works // just for fun //if(t>0) { sketch.stroke("red"); sketch.line(bounce_data[t].x, bounce_data[t].y, bounce_data[t-1].x, bounce_data[t-1].y); }  ++; import  {p5} from  "@tmcw/p5" . table (bounce_data)=  interval ([- 3 , 3 ],  {step :  0.005 , value :  [- 1 - Math . PI / 2 , 1 - Math . PI / 2 ], label :  'field of view' , width : 200 =  embed (calcuvizspec ({models :  modelname ==  'raycasting'  ?  [model] :  [], input_cursors :  [{player_x_in, player_y_in}], mark :  {type : 'bar' ,  point : false ,  clip : true ,  tooltip : false }, encodings :  {x :  {grid :  false ,  name :  'ray_angle_in' ,  type :  'quantitative' ,  domain :  [clamped_ray_angle_in,  ... _. range (fov2[0 ], fov2[1 ], ray_angle_in_step_size)],  nice : false ,  ticks : 2 }, color :  {name :  'ray_hit_color' ,  type : 'nominal' ,  legend : false }, y :  {grid :  false ,  name :  'inverse_ray_length' ,  type :  'quantitative' }, opacity :  {name :  'inverse_ray_length' ,  type :  'quantitative' ,  legend : false }, , width :  400 ,  height : 150 , spec_post_process :  spec =>  {. encoding . y . scale  =  {domain : [0 , 0.3 ], }; . encoding . x . axis  =  labels ?  {tickCount : 2 ,  grid : false } :  null . encoding . y . axis  =  labels ?  {grid : false } :  null . params  =  [{"name" :  "ray_angle_in" ,  "value" :  { "ray_angle_in" :  0 },  "select" :  { "type" :  "point" ,  "on" :  "mouseover" ,  "nearest" :  true ,  "encodings" :  ["x" ],  toggle : false }}]; . encoding . color . condition  =  {"test" :  `datum.ray_angle_in== ${ clamped_ray_angle_in ||  999 } ` , "value" :  "red" }. encoding . opacity . condition  =  {"test" :  `datum.ray_angle_in== ${ clamped_ray_angle_in ||  999 } ` , "value" :  1 }return  spec=  Inputs. toggle ({label :  "labels" ,  value :  true })=  embed (calcuvizspec ({models :  modelname ==  'raycasting'  ?  [model] :  [], input_cursors :  [{player_x_in, player_y_in, ray_angle_in :  clamped_ray_angle_in/*:(fov[0]+fov[1])/2*/ , fov_in : fov2}], mark :  {type : 'rect' , tooltip : false }, encodings :  {x :  {grid :  false ,  name :  'level_x_in' ,  type : 'nominal' ,  domain :  _. range (0 , 63.1 , 1 )}, y :  {grid :  false ,  name :  'level_y_in' ,  type :  'nominal' ,  domain :  _. range (0 , 63.1 , 1 )}, color :  {name :  (observable_world ?  'level_player_ray_fov'  :  'level_player' ),  type :  'quantitative' ,  legend : false }, //row: {name: 'formula', type:'nominal', domain: ['level', 'level_plus_player', 'level_plus_player_plus_ray']}, , width : 280 ,  height : 280 , spec_post_process :  spec =>  {. params  =  [{"name" :  "xy" ,  "select" :  { "type" :  "point" ,  "on" :  "mouseover" ,  "nearest" :  true ,  "encodings" :  ["x" ,  "y" ],  toggle : false }}]; let  a =  spec. encoding . x . axis ;  spec. encoding . x . axis  =  {... a,  ticks : false ,  labels : false }=  spec. encoding . y . axis ;  spec. encoding . y . axis  =  {... a,  ticks : false ,  labels : false }. encoding . color . scale  =  {scheme :  'turbo' }return  spec; =  Inputs. range ([3 , 61 ],  {step : 1 ,  value : 32 ,  label : 'player x' })=  Inputs. range ([3 , 61 ],  {step : 1 ,  value : 32 ,  label : 'player y' })//viewof observable_world_v = Inputs.checkbox(["overlay field of view"], {value: true}) =  true import  {viewof keys} from  'd/867e1424e1d093ab' =  Inputs. range ([0 , keys. length ], {step : 1 , value : keys. length , label : 'time_in' })//time_in = keys.length =  model. fov_calcd ({time_in, keys_in : keys. map (d =>  d. key ). filter ((d, i) =>  i <  time_in),  speed_in : 3 ,  fov_0_in : [- 1 - Math . PI / 2 , 1 - Math . PI / 2 ]})md `keys:  ${ keys. length  ==  0  ?  'โ'  :  keys. map (d =>  d. key ). reverse (). slice (0 , 10 ). map (d =>  d ==  'ArrowUp'  ?  'โฌ๏ธ'  :  d ==  'ArrowDown'  ?  'โฌ๏ธ'  :  d ==  'ArrowLeft'  ?  'โฌ
๏ธ'  :  d ==  'ArrowRight'  ?  'โก๏ธ'  :  d). join (' ' )} ` =  model. inverse_ray_length$m . cache Object . keys (e). length =  embed (calcuvizspec ({models :  modelname ==  'raycasting-playable'  ?  [model] :  [], input_cursors :  [{time_in, keys_in : keys. map (d =>  d. key ). filter ((d, i) =>  i <  time_in),  speed_in : 3 ,  fov_0_in : [- 1 - Math . PI / 2 , 1 - Math . PI / 2 ],   }], mark :  {type : 'bar' ,  point : false ,  clip : true ,  tooltip : false }, encodings :  {x :  {grid :  false ,  name :  'ray_angle_in' ,  type :  'quantitative' ,  domain :  ,  ... _. range (fov[0 ], fov[1 ], ray_angle_in_step_size)]//[1,1.1,1.2] ,  nice : false ,  ticks : 2 }, //color: {name: 'ray_hit_color', type:'nominal', legend:false}, y :  {grid :  false ,  name :  'inverse_ray_length' ,  type :  'quantitative' }, opacity :  {name :  'inverse_ray_length' ,  type :  'quantitative' ,  legend : false }, , width :  400 ,  height : 150 , spec_post_process :  spec =>  {. encoding . y . scale  =  {domain : [0 , 0.3 ], }; . encoding . x . axis  =  labels2 ?  {tickCount : 2 ,  grid : false } :  null . encoding . y . axis  =  labels2 ?  {grid : false } :  null . params  =  [{"name" :  "ray_angle_in" ,  "value" :  { "ray_angle_in" :  0 },  "select" :  { "type" :  "point" ,  "on" :  "mouseover" ,  "nearest" :  true ,  "encodings" :  ["x" ],  toggle : false }}]; //spec.encoding.color.condition = {"test": `datum.ray_angle_in==${clamped_ray_angle_in2 || 999}`,"value": "red"} . encoding . opacity . condition  =  {"test" :  `datum.ray_angle_in== ${ clamped_ray_angle_in2 ||  999 } ` , "value" :  1 }return  spec=  Inputs. toggle ({label :  "labels" ,  value :  true })4 + 4 =  calcuvegadata ({models :  modelname ==  'raycasting-playable'  ?  [model] :  [], spec :  raycasting_playable_level_spec. layer [0 ], domains :  { level_x_in :  _. range (0 , 63.1 , 1 ), level_y_in :  _. range (0 , 63.1 , 1 ) }, input_cursors :  [{ }]=  calcuvegadata ({models :  modelname ==  'raycasting-playable'  ?  [model] :  [], spec :  raycasting_playable_level_spec. layer [1 ], domains :  {}, input_cursors :  [{time_in, keys_in : keys. map (d =>  d. key ),  speed_in : 3 ,  fov_0_in : [- 1 - Math . PI / 2 , 1 - Math . PI / 2 ] }]=  ({//"data": {"name": "data"}, "datasets" :  { data :  raycasting_playable_level_data0, player :  raycasting_playable_level_player_data0 }, layer :  [{"data" :  {"name" :  "data" }, "mark" :  {"type" :  "rect" ,  "tooltip" :  false }, "encoding" :  {"x" :  {"field" :  "level_x_in" , "type" :  "nominal" , "scale" :  {"rangeStep" :  12 }, "axis" :  {"grid" :  false ,  "ticks" :  false ,  "labels" :  false }, "y" :  {"field" :  "level_y_in" , "type" :  "nominal" , "scale" :  {"rangeStep" :  12 }, "axis" :  {"grid" :  false ,  "ticks" :  false ,  "labels" :  false }, "color" :  {"field" :  "level" , "type" :  "quantitative" , "scale" :  {"scheme" :  "turbo" }, , "data" :  {"name" :  "player" }, mark :  'text' , encoding :  {x :  {field : 'player_x_calcd' }, y :  {field : 'player_y_calcd' }, text :  {value : '๐ง' }, , "config" :  {"legend" :  {"disable" :  true }}, width : 280 ,  height : 280 , =  [] // includes data in spec for when I Open in Vega Editor =  [] . html `<button onclick= ${ () =>  {mutable raycasting_playable_level_data0 =  raycasting_playable_level_data;  mutable raycasting_playable_level_player_data0 =  raycasting_playable_level_player_data}} >LEVEL insert data -> vega editor (dev)</button>` =  embed (raycasting_playable_level_spec). data ("data" ,  raycasting_playable_level_data). resize (). run (); console . log (Object . entries (model. inverse_ray_length$m . cache ). length )console . log (Object . entries (model. player_x_raw$m . cache ). length ). data ("player" ,  raycasting_playable_level_player_data). resize (). run (); =  Inputs. checkbox (["overlay field of view" ],  {value :  true })=  observable_world_v2. length =  Inputs. range ([0 , 5 ],  {step :  0.2 ,  value : 3 ,  label :  'climate_sensitivity_in' })=  Inputs. range ([0 , 0.001 * 2 ],  {step :  0.001 / 5 ,  value : 0.001 ,  label :  'drawdown_factor_in' })md `**45%** emissions absorption currently hardcoded` =  embed ("$schema" :  "https://vega.github.io/schema/vega/v5.json" , "width" :  350 , "height" :  200  , //+ 0* climate_sensitivity_in, "padding" :  5 , "data" :  [// using climate_results makes this responsive but resets visual on refresh "name" :  "results2" ,  "values" :  [{"year_in" : 2100 ,  "temperature_delta" : 1.23  /*BAD*/ }]}, "name" :  "table" , "values" :  ["category" :  1960 ,  "amount" :  4 }, "category" :  1990 ,  "amount" :  8 }, "category" :  2020 ,  "amount" :  28 }, "category" :  2025 ,  "amount" :  29 }, "category" :  2030 ,  "amount" :  30 }, "category" :  2035 ,  "amount" :  32 }, "category" :  2040 ,  "amount" :  33 }, "category" :  2045 ,  "amount" :  34 }, "category" :  2050 ,  "amount" :  38 }, "category" :  2100 ,  "amount" :  90 }, "category" :  2150 ,  "amount" :  5 }, "name" :  "seq" , "transform" :  ["type" :  "sequence" ,  "start" :  1960 ,  "stop" :  2200 ,  "step" :  5 }, "type" :  "filter" ,  "expr" :  "datum.data >= bau.category" }, "name" :  "seq2" , "transform" :  ["type" :  "sequence" ,  "start" :  1960 ,  "stop" :  2200 ,  "step" :  5 }, "type" :  "lookup" , "from" :  "table" , "key" :  "category" , "fields" :  ["data" ], "as" :  ["table" ], "type" :  "filter" ,  "expr" :  "datum.table != null" }, "type" :  "window" ,  "ops" :  ["lag" ],  "fields" :  ["table" ]}, "type" :  "filter" ,  "expr" :  "datum.lag_table != null" }, "type" :  "formula" , "expr" :  "datum.lag_table.amount+(datum.table.amount-datum.lag_table.amount)*(datum.table.category-datum.lag_table.category)" , "as" :  "interpolated" , "name" :  "interpolated" , "transform" :  ["type" :  "sequence" ,  "start" :  1960 ,  "stop" :  2200 ,  "step" :  1 }, "type" :  "lookup" , "from" :  "table" , "key" :  "category" , "fields" :  ["data" ], "values" :  ["category" ], "as" :  ["group" ], "type" :  "window" ,  "ops" :  ["next_value" ],  "fields" :  ["group" ]}, "type" :  "lookup" , "from" :  "seq2" , "key" :  "data" , "fields" :  ["next_value_group" ], "as" :  ["group" ], "type" :  "filter" ,  "expr" :  "datum.group != null" }, "type" :  "formula" , "expr" :  "max(0,datum.data <= bau.category ? datum.group.lag_table.amount+(datum.group.table.amount-datum.group.lag_table.amount)*(datum.data-datum.group.lag_table.category)/(datum.group.table.category-datum.group.lag_table.category) : bau.amount+(0-bau.amount)*(datum.data-bau.category)/(zero_category-bau.category))" , "as" :  "interpolated" , "type" :  "formula" , "expr" :  "datum.data < 2020 ? 'old' : 'future'" , "as" :  "old" , "name" :  "interpolated_baseline" , "transform" :  ["type" :  "sequence" ,  "start" :  1960 ,  "stop" :  2200 ,  "step" :  1 }, "type" :  "lookup" , "from" :  "table" , "key" :  "category" , "fields" :  ["data" ], "values" :  ["category" ], "as" :  ["group" ], "type" :  "window" ,  "ops" :  ["next_value" ],  "fields" :  ["group" ]}, "type" :  "lookup" , "from" :  "seq2" , "key" :  "data" , "fields" :  ["next_value_group" ], "as" :  ["group" ], "type" :  "filter" ,  "expr" :  "datum.group != null" }, "type" :  "formula" , "expr" :  "datum.group.lag_table.amount+(datum.group.table.amount-datum.group.lag_table.amount)*(datum.data-datum.group.lag_table.category)/(datum.group.table.category-datum.group.lag_table.category)" , "as" :  "interpolated" , "name" :  "seq3" , "transform" :  ["type" :  "sequence" ,  "start" :  1960 ,  "stop" :  2200 ,  "step" :  5 }, "type" :  "lookup" , "from" :  "seq2" , "key" :  "data" , "fields" :  ["data" ], "as" :  ["table" ], "type" :  "window" , "ops" :  ["first_value" ], "sort" :  {"field" :  "table" }, "fields" :  ["table" ], "type" :  "formula" , "as" :  "aaa" , "expr" :  "isValid(datum.table) ?  0 :  datum.lag_table" , "name" :  "table-first" , "source" :  "table" , "transform" :  [{"type" :  "filter" ,  "expr" :  "datum.category == 1960" }], "name" :  "table-baus" , "source" :  "table" , "transform" :  ["type" :  "filter" ,  "expr" :  "datum.category <= bau.category" }, "signals" :  ["name" :  "bau" , "value" :  {"category" :  2030 ,  "amount" :  30 }, "on" :  [{"events" :  "@baus:mouseover" ,  "update" :  "datum" }], "name" :  "zero_category" , "update" :  "max(zero_category,bau.category)" , "value" :  2050 , "on" :  ["events" :  "@zeros:mouseover" ,  "update" :  "max(datum.data,bau.category)" }, "name" :  "history" , "value" :  "2030|2050" , "on" :  ["events" :  "@baus:mouseover,@zeros:mouseover" , "update" :  "history+','+bau.category+'|'+zero_category" , , "scales" :  ["name" :  "xscale" , "type" :  "linear" , "zero" :  false , "domain" :  {"data" :  "table" ,  "field" :  "category" }, "range" :  "width" , "padding" :  0.05 , "round" :  true , "name" :  "yscale" , "domain" :  {"data" :  "table" ,  "field" :  "amount" }, "nice" :  true , "range" :  "height" , "name" :  "emissions" , "type" :  "ordinal" , "domain" :  ["old" ,  "future" ], "range" :  ["darkgrey" ,  "steelblue" ], "name" :  "bemissions" , "type" :  "ordinal" , "domain" :  ["old" ,  "future" ], "range" :  ["brown" ,  "blue" ], "axes" :  ["orient" :  "bottom" , "scale" :  "xscale" , "format" :  ".0f" , "labelFontSize" :  20 , "tickCount" :  6 , "orient" :  "left" ,  "scale" :  "yscale" }, "marks" :  ["type" :  "rect" , "from" :  {"data" :  "interpolated_baseline" }, "encode" :  {"enter" :  {"fill" :  {"value" :  "pink" },  "opacity" :  {"value" :  0.1 }}, "update" :  {"x" :  {"scale" :  "xscale" ,  "field" :  "data" }, "width" :  {"value" :  10 }, "y" :  {"scale" :  "yscale" ,  "field" :  "interpolated" }, "y2" :  {"scale" :  "yscale" ,  "value" :  0 }, "type" :  "rect" , "from" :  {"data" :  "interpolated" }, "encode" :  {"enter" :  {"fill" :  {"scale" :  "emissions" ,  "field" :  "old" }, "opacity" :  {"value" :  0.5 }, "update" :  {"x" :  {"scale" :  "xscale" ,  "field" :  "data" }, "width" :  {"value" :  9 }, "y" :  {"scale" :  "yscale" ,  "field" :  "interpolated" }, "y2" :  {"scale" :  "yscale" ,  "value" :  0 }, "type" :  "rect" , "from" :  {"data" :  "table" }, "encode" :  {"enter" :  {"x" :  {"scale" :  "xscale" ,  "field" :  "category" }, "width" :  {"scale" :  "xscale" ,  "band" :  1 }, "y" :  {"scale" :  "yscale" ,  "field" :  "amount" }, "y2" :  {"scale" :  "yscale" ,  "value" :  0 }, "opacity" :  {"value" :  0.1 }, "update" :  {"fill" :  {"value" :  "steelblue" }}, "type" :  "line" , "from" :  {"data" :  "table-baus" }, "encode" :  {"enter" :  {"strokeWidth" :  {"value" :  5 }, "stroke" :  {"value" :  "black" }, "interpolate" :  {"value" :  "linear" }, "strokeCap" :  {"value" :  "round" }, "update" :  {"x" :  {"scale" :  "xscale" ,  "field" :  "category" ,  "band" :  1 }, "y" :  {"scale" :  "yscale" ,  "field" :  "amount" }, "type" :  "rule" , "encode" :  {"enter" :  {"strokeWidth" :  {"value" :  5 },  "strokeCap" :  {"value" :  "round" }}, "update" :  {"x" :  {"scale" :  "xscale" ,  "signal" :  "bau.category" ,  "band" :  1 }, "y" :  {"scale" :  "yscale" ,  "signal" :  "bau.amount" ,  "offset" :  0 }, "x2" :  {"scale" :  "xscale" ,  "signal" :  "zero_category" ,  "band" :  1 }, "y2" :  {"scale" :  "yscale" ,  "value" :  0 ,  "offset" :  0 }, "type" :  "rule" , "encode" :  {"enter" :  {"strokeWidth" :  {"value" :  5 },  "strokeCap" :  {"value" :  "round" }}, "update" :  {"x2" :  {"scale" :  "xscale" ,  "value" :  2200 ,  "band" :  1 }, "x" :  {"scale" :  "xscale" ,  "signal" :  "zero_category" ,  "band" :  1 }, "y" :  {"scale" :  "yscale" ,  "value" :  0 ,  "offset" :  0 }, "type" :  "rule" , "from" :  {"data" :  "table-first" }, "encode" :  {"enter" :  {"strokeWidth" :  {"value" :  5 },  "strokeCap" :  {"value" :  "round" }}, "update" :  {"x2" :  {"scale" :  "xscale" ,  "field" :  "category" ,  "band" :  1 }, "x" :  {"scale" :  "xscale" ,  "field" :  "category" ,  "band" :  0 }, "y" :  {"scale" :  "yscale" ,  "field" :  "amount" ,  "offset" :  0 }, "type" :  "symbol" , "name" :  "zeros" , "from" :  {"data" :  "seq" }, "encode" :  {"enter" :  {"size" :  {"value" :  500 }, "fill" :  {"value" :  "lightblue" }, "stroke" :  {"value" :  "black" }, "shape" :  {"value" :  "triangle" }, "opacity" :  {"value" :  0.8 }, "update" :  {"x" :  {"scale" :  "xscale" ,  "field" :  "data" ,  "band" :  1 }, "y" :  {"scale" :  "yscale" ,  "value" :  "0" ,  "offset" :  0 }, "type" :  "text" , "name" :  "results" , "from" :  {"data" :  "results2" }, "encode" :  {"enter" :  {"fontSize" :  {"value" :  50 }, "fill" :  {"value" :  "red" }, "stroke" :  {"value" :  "black" }, "align" :  {"value" :  "center" }, "fontWeight" :  {"value" :  "bold" }, "text" :  {"signal" :  "datum.temperature_delta == 999 ? '-' : 'max ฮ T โ ' + format(datum.temperature_delta, ',.2f')" }, , "update" :  {//"x": {"scale": "xscale", "field": "year_in", "band": 1,}, "x" :  {"scale" :  "xscale" ,  "value" :  2075 }, "y" :  {"scale" :  "yscale" ,  "value" :  "-30" ,  "offset" :  0 }, "type" :  "symbol" , "name" :  "baus" , "from" :  {"data" :  "table" }, "encode" :  {"enter" :  {"size" :  {"value" :  500 }, "fill" :  {"value" :  "pink" }, "stroke" :  {"value" :  "black" }, "shape" :  {"value" :  "square" }, "opacity" :  {"value" :  0.8 }, "update" :  {"x" :  {"scale" :  "xscale" ,  "field" :  "category" ,  "band" :  1 }, "y" :  {"scale" :  "yscale" ,  "field" :  "amount" ,  "offset" :  0 }//viewof emissions = Inputs.input([]) =  emissions. data ('interpolated' )=  calcudata ({models :  modelname ==  'simple-climate'  ?  [model] :  [], input_cursors :  [{emissions_table_in :  emissions_. map (d =>  ({year_in : d. data ,  emissions_rate : d. interpolated })), climate_sensitivity_in, ppm_to_GtC_in : 2.3 , drawdown_factor_in}], input_domains :  {year_in :  [2015 , 2016 ,... _. range (2020 , 2151 , 10 )], , outputs :  ['emissions_rate' , 'temperature_delta' ,  'CO2_concentration' ,  'concentration_factor' ]// "gradual calculation" // this should be calcd in-model =  d3. greatest (climate1,  d =>  d. value ). year_in =  calcudata ({models :  modelname ==  'simple-climate'  ?  [model] :  [], input_cursors :  [{emissions_table_in :  emissions_. map (d =>  ({year_in : d. data ,  emissions_rate : d. interpolated })), climate_sensitivity_in, ppm_to_GtC_in : 2.3 , drawdown_factor_in}], input_domains :  {year_in :  _. range (Math . max (2015 , Tmax_year_1- 10 ), Tmax_year_1+ 11 , 1 ), , outputs :  ['temperature_delta' ]=  d3. greatest (climate2,  d =>  d. value )//.year_in // used in layer in table viz =  calcudata ({models :  modelname ==  'simple-climate'  ?  [model] :  [], input_cursors :  [{emissions_table_in :  emissions_. map (d =>  ({year_in : d. data ,  emissions_rate : d. interpolated })), climate_sensitivity_in, ppm_to_GtC_in : 2.3 , drawdown_factor_in, year_in :  Tmax_year_2. year_in }], input_domains :  {//year_in: [2015,2016,..._.range(2020,2151,10)], , outputs :  ['emissions_rate' , 'temperature_delta' ,  'CO2_concentration' ,  'concentration_factor' ]embed ({width : 380 ,  height : 340 , "data" :  {"name" :  "data" }, "encoding" :  {"y" :  {"field" :  "year_in" , "type" :  "quantitative" , "scale" :  {"zero" :  false }, "sort" :  "ascending" , "x" :  {"field" :  "formula" , "type" :  "nominal" , "axis" :  {"labelAngle" :  0 ,  "orient" :  "top" ,  "labelLimit" :  90 }, "color" :  {"field" :  "formula" ,  "type" :  "nominal" }, "opacity" :  {"value" :  0.7 }, "text" :  {"field" :  "value" , "type" :  "quantitative" , "format" :  ".2f" , "axis" :  {"format" :  ".2f" }, "layer" :  [  "transform" :  [{"filter" :  "datum.year_in == " + Tmax_year_2. year_in }], "mark" :  {"type" :  "text" , "fontSize" :  15 , "fontWeight" :  "bold" , "tooltip" :  true , //"dx": 1, "dy": 1 , "encoding" :  {//"color": {"value": "red"}, "opacity" :  {"value" : 1 }}, "transform" :  [{"filter" :  "datum.year_in <= 2100" }], "mark" :  {"type" :  "text" , "fontSize" :  16 , "fontWeight" :  "bold" , "tooltip" :  true , , "config" :  {"legend" :  {"disable" :  true }}, "datasets" :  {"data" :  [... climate1,... climate_Tmax]=  (name,  value) =>  {if  (value !=  undefined )=  value; /*emissions.change('results2',emissions.insert('results2',calcudata({     models: modelname == 'simple-climate' ? [model] : [],     input_domains: {year_in: [2015,2100]},     input_cursors: [{emissions_table_in: emissions_.map(d => ({year_in:d.data, emissions_rate:d.interpolated})),climate_sensitivity_in,ppm_to_GtC_in:2.3,drawdown_factor_in}],     outputs: ['temperature_delta'],     pivot:true   }).map(({year_in,temperature_delta}) => ({year_in,temperature_delta})))).remove('results2',d => true).run()*/ . data ('results2' ,  /*calcudata({       models: modelname == 'simple-climate' ? [model] : [],       input_domains: {year_in: [2100]}, // should show more, to demo drawdown       input_cursors: [{emissions_table_in: emissions_.map(d => ({year_in:d.data, emissions_rate:d.interpolated})),climate_sensitivity_in,ppm_to_GtC_in:2.3,drawdown_factor_in}],       outputs: ['temperature_delta'],       pivot:true     }).map(({year_in,temperature_delta}) => ({year_in,temperature_delta}))*/ . map (d =>  ({... d,  temperature_delta : d. value })). run (). addDataListener ('interpolated' ,  update_climate) }; ; ;  // force recalc/coordination on input change update_climate ()//emissions.insert('interpolated',[]).run()