sandbox very modified wow

This commit is contained in:
Nicky Case 2020-04-22 18:48:22 -04:00
parent 769ee54b93
commit fe74ab9f56
8 changed files with 729 additions and 261 deletions

BIN
icons/e.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icons/i.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
icons/r.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
icons/s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -69,7 +69,7 @@
<p><strong>Click &quot;Start&quot; play the simulation! You can then change the &quot;generation time&quot;, and see how that changes the simulation:</strong></p>
<div class="sim">
<iframe src="sim?stage=01" width="800" height="520"></iframe>
<iframe src="sim?stage=01" width="800" height="540"></iframe>
</div>
<p>Starts small (&quot;it&#39;s just a flu&quot;), then explodes (&quot;oh right, flus don&#39;t break hospitals in rich countries&quot;). This is the &quot;J-shaped&quot; <strong>exponential growth curve</strong>.</p>

View File

@ -11,166 +11,200 @@
<div id="controls">
<div id="section_dynamics">
<div id="hide_on_first_playthrough">
<div id="section_dynamics">
On <i>average</i>,
each <icon i></icon>...
<br><br>
<div id="label_transmission">
Infects a <icon s></icon> every <span id="label_p_transmission">N</span> days
<br>
(when almost everyone's still <icon s></icon>)
<br>
<input class="sim_input" type="range" id="p_transmission" min="1" max="28" step="1" value="4">
</div>
<div id="label_c_exposed">
<input class="sim_checkbox" type="checkbox" id="c_exposed">
Takes <span id="label_p_exposed">N</span> days to go from <icon e></icon> to <icon i></icon>
<br>
<input class="sim_input" type="range" id="p_exposed" min="1" max="28" step="1" value="3">
</div>
<div id="label_c_recovery">
<input class="sim_checkbox" type="checkbox" id="c_recovery">
Recovers <icon r></icon> in <span id="label_p_recovery">N</span> days
<br>
<input class="sim_input" type="range" id="p_recovery" min="1" max="28" step="1" value="14">
</div>
<div id="label_c_waning">
<input class="sim_checkbox" type="checkbox" id="c_waning">
Loses immunity <icon s></icon> in <span id="label_p_waning">N</span> years
<br>
<input class="sim_input" type="range" id="p_waning" min="0.5" max="5" step="0.5" value="1">
</div>
<hr>
On <i>average</i>,
each infected person...
<br><br>
<div id="label_transmission">
Infects a new person every <span id="label_p_transmission">N</span> days
(in the beginning)
<br>
<input class="sim_input" type="range" id="p_transmission" min="1" max="28" step="1" value="4">
</div>
<div id="label_c_recovery">
<input class="sim_checkbox" type="checkbox" id="c_recovery">
Recovers in <span id="label_p_recovery">N</span> days
<br>
<input class="sim_input" type="range" id="p_recovery" min="1" max="28" step="1" value="14">
</div>
<div id="label_c_waning">
<input class="sim_checkbox" type="checkbox" id="c_waning">
Loses immunity in <span id="label_p_waning">N</span> years
<br>
<input class="sim_input" type="range" id="p_waning" min="0.5" max="5" step="0.5" value="1">
</div>
<hr>
<div id="section_r">
</div>
R0 is <span id="label_p_r0" toFixed="2"></span>
<input class="sim_input" type="range" id="p_r0" min="0" max="6" step="0.01" disabled>
<br>
<div id="section_r">
<span id="label_s">
But R is changed by...
<br>
Susceptible population
<input class="sim_input" type="range" id="p_s" min="0" max="1" step="0.001" value="1" disabled>
<br>
</span>
R0 is <span id="label_p_r0" toFixed="2"></span>
<input class="sim_input" type="range" id="p_r0" min="0" max="6" step="0.01" disabled>
<br>
<span id="int_block_0">
Personal Hygiene
<br>
<input class="sim_input recordable" type="range" id="p_hygiene" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_1">
Physical Distancing
<br>
<input class="sim_input recordable" type="range" id="p_distancing" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_2">
Isolating Cases
<br>
<input class="sim_input recordable" type="range" id="p_isolate" min="0" max="1" step="0.001" value="0">
<br>
Quarantining Contacts
<br>
<input class="sim_input recordable" type="range" id="p_quarantine" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_3">
Deep Cleaning
<br>
<input class="sim_input recordable" type="range" id="p_cleaning" min="0" max="1" step="0.001" value="0">
<br>
Face Masks
<br>
<input class="sim_input recordable" type="range" id="p_masks" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_4">
Summer
<br>
<input class="sim_input recordable" type="range" id="p_summer" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_5">
Vaccinations
<br>
<input class="sim_input recordable" type="range" id="p_vaccines" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="label_s">
But R is changed by...
<br>
Susceptible population
<input class="sim_input" type="range" id="p_s" min="0" max="1" step="0.001" disabled>
<br>
</span>
<span id="int_block_0">
Personal Hygiene
<br>
<input class="sim_input recordable" type="range" id="p_hygiene" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_1">
Physical Distancing
<br>
<input class="sim_input recordable" type="range" id="p_distancing" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_2">
Isolating Cases
<br>
<input class="sim_input recordable" type="range" id="p_isolate" min="0" max="1" step="0.001" value="0">
<br>
Quarantining Contacts
<br>
<input class="sim_input recordable" type="range" id="p_quarantine" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_3">
Deep Cleaning
<br>
<input class="sim_input recordable" type="range" id="p_cleaning" min="0" max="1" step="0.001" value="0">
<br>
Face Masks
<br>
<input class="sim_input recordable" type="range" id="p_masks" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_4">
Summer
<br>
<input class="sim_input recordable" type="range" id="p_summer" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="int_block_5">
Vaccinations
<br>
<input class="sim_input recordable" type="range" id="p_vaccines" min="0" max="1" step="0.001" value="0">
<br>
</span>
<span id="label_re">
R is now <span id="label_p_re" toFixed="2"></span>
<input class="sim_input" type="range" id="p_re" min="0" max="6" step="0.01" disabled>
</span>
<br>
<span id="hospital_capacity">
Hospital capacity at <span id="label_p_hospital">N</span>%
<br>
<input class="sim_input recordable" type="range" id="p_hospital" min="0" max="500" step="1" value="100">
</span>
<span id="label_re">
R is now <span id="label_p_re" toFixed="2"></span>
<input class="sim_input" type="range" id="p_re" min="0" max="6" step="0.01" disabled>
</span>
<hr>
<span id="hospital_capacity">
Hospital capacity at <span id="label_p_hospital">N</span>%
</div>
<div id="section_meta">
Simulate <span id="label_p_years" toFixed="1">N</span> years
<span id="section_meta_years">
<br>
<input class="sim_input" type="range" id="p_years" min="0.5" max="10" step="0.5" value="2">
<br>
</span>
in <span id="label_p_speed">N</span> seconds
<br>
<input class="sim_input recordable" type="range" id="p_hospital" min="0" max="500" step="1" value="100">
</span>
<hr>
</div>
<div id="section_meta">
Simulate <span id="label_p_years">N</span> years...
<br>
<input class="sim_input" type="range" id="p_years" min="0.5" max="10" step="0.5" value="2">
<br>
...in <span id="label_p_speed">N</span> seconds
<br>
<input class="sim_input" type="range" id="p_speed" min="1" max="60" step="1" value="30">
<hr>
<input class="sim_input" type="range" id="p_speed" min="1" max="60" step="1" value="30">
</div>
</div>
<div id="sim_controls">
<button id="start">start/stop</button>
<button id="restart">restart</button>
<button id="replay">replay recording</button>
<div class="big_button">
<div id="bb_start">▶ Start</div>
<div id="bb_pause">❙❙ Pause</div>
<div id="bb_continue">▶ Continue</div>
<div id="bb_reset">↺ Reset</div>
</div>
<div class="small_button">
<div id="sb_reset">↺ Reset</div>
<div id="sb_replay">↺ Replay what you just did</div>
<div id="sb_params">↺ Reset all sliders</div>
</div>
</div>
</div>
<div id="graph">
<canvas id="graphCanvas"></canvas>
<!--
<div id="months">
<div>dec 2019</div>
<div>mar 2020</div>
<div>jun 2020</div>
<div>sep 2020</div>
<div>dec 2020</div>
<div>mar 2021</div>
<div>jun 2021</div>
<div>sep 2021</div>
<div>dec 2021</div>
</div>
<div id="month_ticks">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<!--
<div><span>2020</span></div>
<div><span>2021</span></div>
-->
</div>
<div id="legend">
<icon s></icon> Susceptible<span id="show_percent_s"></span>
<span id="label_exposed"><br><icon e></icon> Exposed</span>
<br><icon i></icon> Infectious<span id="show_percent_i"></span>
<span id="label_removed"><br><icon r></icon> Removed</span>
<br>
<span id="label_herd_immunity">
- - - Herd Immunity
</span>
<br>
<span id="label_capacity">
&nbsp; Healthcare Capacity
</span>
</div>
-->
<img src="legend.png" width="500" height="500" style="position: absolute;top:0; left:0"></img>
</div>
</div>
<span id="month_names">
<span>jan</span>
<span>feb</span>
<span>mar</span>
<span>apr</span>
<span>may</span>
<span>jun</span>
<span>jul</span>
<span>aug</span>
<span>sep</span>
<span>oct</span>
<span>nov</span>
<span>dec</span>
</span>
</body>
<script src="sim.js"></script>

View File

@ -2,12 +2,19 @@ body{
margin: 0;
font-family: Helvetica, Arial, sans-serif;
font-size: 15px;
font-weight: normal;
background: #ddd;
}
div{
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
}
#sandbox{
width: 800px;
height: 520px;
height: 540px;
overflow: hidden;
background: #fff;
position: absolute;
@ -24,6 +31,77 @@ body{
float:left;
position: relative;
}
#sim_controls{
margin-top: 10px;
}
.big_button{
font-weight: 100;
background: #ff4040;
color: #fff;
padding:10px;
border-radius: 10px;
border-bottom: 4px solid rgba(0,0,0,0.2);
font-size: 30px;
cursor: pointer;
}
.big_button:hover{
background: #ff6060;
}
.big_button > div{
display: none;
}
.big_button[label='start'] > #bb_start{
display: block;
}
.big_button:not([label='start']){
background: #dddddd;
color: #555;
}
.big_button:not([label='start']):hover{
background: #dddddd;
}
.big_button[label='continue'] > #bb_continue{
display: block;
}
.big_button[label='pause'] > #bb_pause{
display: block;
}
.big_button[label='reset'] > #bb_reset{
display: block;
}
.small_button{
text-align: right;
margin-top: 3px;
color: #999;
font-size: 17px;
font-weight: 100;
cursor: pointer;
}
.small_button:hover{
color: #aaa;
}
.small_button > div{
display: none;
}
.small_button[label='reset'] > #sb_reset{
display: block;
}
.small_button[label='replay'] > #sb_replay{
display: block;
}
.small_button[label='params'] > #sb_params{
display: block;
}
hr{
border:none;
border-bottom: 2px dashed #ddd;
margin: 15px 0;
}
/*
#controls > div{
overflow: hidden;
@ -111,34 +189,66 @@ body{
height:500px;
border: 1px solid #ccc;
}
#graph > #months{
color: #bbb;
font-size: 10px;
width: 562.5px;
height: 50px;
text-align: right;
position: relative;
left: -70.5px;
top: 2px;
}
#graph > #months > div{
color: #bbb;
font-size: 10px;
width: 62.5px;
float: left;
transform-origin: top right;
transform: rotate(-30deg);
}
#graph > #month_ticks{
width: 562.5px;
height: 5px;
position: relative;
left: -61.5px;
top: -50px;
width: 500px;
height: 15px;
position: absolute;
left: 0;
top: 501px;
color: #bbb;
font-size: 14px;
font-weight: 100;
}
#graph > #month_ticks > div{
width: 61.5px;
width: 100px;
height: 100%;
border-right: 1px solid #bbb;
float: left;
border-left: 1px solid #bbb;
position: absolute;
}
#graph > #month_ticks > div > span{
position: absolute;
left:7px;
top:5px;
}
icon{
display: inline-block;
width: 1em;
height: 1em;
position: relative;
top:0.1em;
background-size: 100% 100%;
}
icon[s]{
background-image: url(../icons/s.png);
}
icon[e]{
background-image: url(../icons/e.png);
}
icon[i]{
background-image: url(../icons/i.png);
}
icon[r]{
background-image: url(../icons/r.png);
}
#legend{
position: absolute;
top: 10px;
left: 10px;
/* font-size: 17px; */
font-weight: 100;
line-height: 1.4em;
}
#hide_on_first_playthrough{
transition: height 0.5s ease-in-out;
overflow: hidden;
height:0px;
padding-right: 2px;
}
#month_names{
display:none;
}

View File

@ -8,61 +8,6 @@ window.$all = (query,el=document)=>{
return [...el.querySelectorAll(query)];
};
/////////////////////////////////////
// PARAMETERS ///////////////////////
/////////////////////////////////////
let S,I,R;
let params = {};
// Sliders
let DONT_RECORD_HISTORY = true;
$all('.sim_input').forEach((slider)=>{
let id = slider.id;
let label = $('#label_'+id);
let isRecordable = slider.classList.contains('recordable');
let onChange = ()=>{
// Change label & param
let val = parseFloat(slider.value);
if(label){
let digits = parseInt(label.getAttribute('toFixed'));
label.innerHTML = digits ? val.toFixed(digits) : val;
}
params[id] = val;
// Record history (@ this day)
if(isRecordable){
if(!DONT_RECORD_HISTORY){
recordedHistory.push([id, val, Math.round(daysCurrent)]);
}
}
};
slider.oninput = onChange;
onChange();
});
DONT_RECORD_HISTORY = false;
// Checkboxes
$all('.sim_checkbox').forEach((checkbox)=>{
let id = checkbox.id;
let label = $('#label_'+id);
let onChange = ()=>{
if(label){
if(!checkbox.checked){
label.setAttribute("strikethrough","yes");
}else{
label.removeAttribute("strikethrough");
}
}
params[id] = checkbox.checked;
};
checkbox.oninput = onChange;
onChange();
});
/////////////////////////////////////
// UPDATE ///////////////////////////
/////////////////////////////////////
@ -78,18 +23,23 @@ let int = {
vaccines: 0
};
let daysCurrent, daysDrawn, daysTotal, daysPerFrame;
let r0_dom = $('#p_r0');
let s_dom = $('#p_s');
let re_dom = $('#p_re');
let updateModel = (days, fake)=>{
let real_S=S, real_I=I, real_R=R;
let real_S=S, real_E=E, real_I=I, real_R=R;
let transmissionRate = 1/params.p_transmission,
incubationRate = 1/params.p_exposed,
recoveryRate = 1/params.p_recovery,
immunityLossRate = 1/(params.p_waning*365);
// R0
let r0_dom = $('#p_r0');
r0_dom.value = transmissionRate/recoveryRate;
r0_dom.oninput();
if(r0_dom.oninput) r0_dom.oninput();
// Transmission affected by interventions
@ -125,23 +75,32 @@ let updateModel = (days, fake)=>{
int.vaccines = params.p_vaccines;
// S...
let s_dom = $('#p_s');
if(!s_dom.disabled){
S = parseFloat(s_dom.value);
}
// Update Model
let newlyInfected;
let newlyExposed;
if(params.EXPONENTIAL){
newlyInfected = I*transmissionRate*days;
newlyExposed = I*transmissionRate*days;
}else{
newlyInfected = S*I*transmissionRate*days;
newlyExposed = S*I*transmissionRate*days;
}
let newlyInfectious = params.c_exposed ? (E*incubationRate*days) : 0;
let newlyRecovered = params.c_recovery ? (I*recoveryRate*days) : 0;
let newlySusceptible = params.c_waning ? (R*immunityLossRate*days) : 0;
S -= newlyInfected;
I += newlyInfected;
S -= newlyExposed;
E += newlyExposed;
if(params.c_exposed){
E -= newlyInfectious;
I += newlyInfectious;
}else{
I += E; // instant transfer
E = 0;
}
I -= newlyRecovered;
R += newlyRecovered;
@ -157,19 +116,160 @@ let updateModel = (days, fake)=>{
if(s_dom.disabled){
s_dom.value = S;
}
let re_dom = $('#p_re');
re_dom.value = newlyInfected/newlyRecovered;
re_dom.oninput();
re_dom.value = newlyExposed/newlyRecovered;
if(re_dom.oninput) re_dom.oninput();
// IF FAKE, UNDO EVERYTHING
if(fake){
S = real_S;
E = real_E;
I = real_I;
R = real_R;
}
}
/////////////////////////////////////
// PARAMETERS ///////////////////////
/////////////////////////////////////
let S,E,I,R;
let params = {};
// Update Month Ticks!
let mn = [...$('#month_names').children].map((dom)=>{
return dom.innerHTML;
});
let updateMonthTicks = ()=>{
let ticks;
switch(params.p_years){
case 0.5:
ticks = [
mn[0]+" 2020",
mn[1]+" 2020",
mn[2]+" 2020",
mn[3]+" 2020",
mn[4]+" 2020",
mn[5]+" 2020",
null
];
break;
case 1.0:
ticks = [
mn[0]+" 2020",
mn[3]+" 2020",
mn[6]+" 2020",
mn[9]+" 2020",
null
];
break;
default:
ticks = [];
for(let time=0; time<=params.p_years; time+=0.5){
if(time%1 != 0){
ticks.push(null);
}else{
ticks.push(time+2020);
}
}
ticks[ticks.length-1] = null;
break;
}
let monthTicksDOM = $('#month_ticks');
monthTicksDOM.innerHTML = '';
ticks.forEach((tick,i)=>{
if(i==ticks.length-1 || !tick) return;
let tickDOM = document.createElement('div');
let tickSpanDOM = document.createElement('span');
tickSpanDOM.innerHTML = tick;
tickDOM.style.left = ((i/(ticks.length-1))*500)+"px";
tickDOM.appendChild(tickSpanDOM);
monthTicksDOM.appendChild(tickDOM);
});
console.log(JSON.stringify(ticks));
}
// Sliders
let DONT_RECORD_HISTORY = true;
let _DO_NOT_RECURSE = true;
let yearsSlider = $('#p_years');
$all('.sim_input').forEach((slider)=>{
let id = slider.id;
let label = $('#label_'+id);
let isRecordable = slider.classList.contains('recordable');
let onChange = ()=>{
// Change label & param
let val = parseFloat(slider.value);
if(label){
let digits = parseInt(label.getAttribute('toFixed'));
label.innerHTML = digits ? val.toFixed(digits) : val;
}
params[id] = val;
// Record history (@ this day)
if(isRecordable){
if(!DONT_RECORD_HISTORY){
recordedHistory.push([id, val, Math.round(daysCurrent)]);
}
}
// Just to update other random crap
if(!_DO_NOT_RECURSE){
_DO_NOT_RECURSE = true;
updateModel(1, true); // one day
_DO_NOT_RECURSE = false;
}
// HACK: If this is years, RESET SIM & CHANGE THE MONTHS
if(slider==yearsSlider){
if(daysCurrent>0){
_resetTheSim();
_updateButtons();
}
updateMonthTicks();
}
// MORE HAX
if(daysCurrent==0){
sbDOM.setAttribute('label','params');
}
};
slider.oninput = onChange;
onChange();
});
DONT_RECORD_HISTORY = false;
_DO_NOT_RECURSE = false;
// Checkboxes
$all('.sim_checkbox').forEach((checkbox)=>{
let id = checkbox.id;
let label = $('#label_'+id);
let onChange = ()=>{
if(label){
if(!checkbox.checked){
label.setAttribute("strikethrough","yes");
}else{
label.removeAttribute("strikethrough");
}
}
params[id] = checkbox.checked;
};
checkbox.oninput = onChange;
onChange();
});
/////////////////////////////////////
// DRAW /////////////////////////////
/////////////////////////////////////
@ -181,7 +281,6 @@ canvas.height = 1000;
canvas.style.width = (canvas.width/2)+"px";
canvas.style.height = (canvas.height/2)+"px";
let daysCurrent, daysDrawn, daysTotal, daysPerFrame;
let interventionColors = [
['hygiene', 'hsl(230,100%,63%)', 0.1],
@ -198,12 +297,28 @@ let draw = ()=>{
// Redraw
requestAnimationFrame(draw);
updateModel(1, true); // one day
// Update SI
if(params._HACK_SHOW_SI_PERCENTS){
$('#show_percent_s').innerHTML = ': '+(S*100).toFixed(5)+'%';
$('#show_percent_i').innerHTML = ': '+(I*100).toFixed(5)+'%';
}
// Paused? At the end?
if(!IS_PLAYING) return;
if(params.FROZEN_IN_TIME) return;
if(daysCurrent > daysTotal) return;
if(daysCurrent > daysTotal){
IS_PLAYING = false;
_updateButtons();
return;
}
// HACK
if(params._HACK_RESET_WHEN_I_100=="ready" && I>=0.99999999){
params._HACK_RESET_WHEN_I_100 = "go";
bbDOM.setAttribute('label','reset');
sbDOM.setAttribute('label',params.CANNOT_REPLAY_HISTORY ? '' : 'replay');
}
// Replay History!
if(IS_REPLAYING_HISTORY){
@ -254,6 +369,12 @@ let draw = ()=>{
ctx.fillStyle = "#cccccc";
ctx.fillRect(0,y,w,h);
// E
y += h;
h = E * canvas.height;
ctx.fillStyle = "#FF9393";
ctx.fillRect(0,y,w,h);
// I
y += h;
h = I * canvas.height;
@ -271,10 +392,26 @@ let draw = ()=>{
});
// ICU bed capacity
y = (1-((params.p_hospital/100)*0.02))*canvas.height;
h = 2;
ctx.fillStyle = "#000000";
ctx.fillRect(0,y,w,h);
if(params.p_hospital){
y = (1-((params.p_hospital/100)*0.02))*canvas.height;
h = 2;
ctx.fillStyle = "#000000";
ctx.fillRect(0,y,w,h);
}
// Herd Immunity
if(params.c_recovery && !params.DO_NOT_SHOW_HERD_IMMUNITY){
if((daysDrawn/daysTotal)*100 % 1 < 0.5){
let transmissionRate = 1/params.p_transmission,
recoveryRate = 1/params.p_recovery,
r0 = transmissionRate/recoveryRate;
let herdImmunity = 1 - (1/r0);
y = (1-herdImmunity)*canvas.height;
h = 2;
ctx.fillStyle = "#000000";
ctx.fillRect(0,y,w,h);
}
}
// RESET
ctx.setTransform(1,0,0,1,0,0);
@ -291,15 +428,12 @@ requestAnimationFrame(draw);
/////////////////////////////////////
let IS_PLAYING = false;
let togglePlaying = ()=>{
IS_PLAYING = !IS_PLAYING;
};
$('#start').onclick = togglePlaying;
let recordedHistory = [];
let IS_REPLAYING_HISTORY = false;
let START_S = 0.999,
START_E = 0.001,
START_I = 0.001,
START_R = 0.000;
@ -307,6 +441,7 @@ let restart = ()=>{
// Model
S = START_S;
E = START_E;
I = START_I;
R = START_R;
@ -320,6 +455,119 @@ let restart = ()=>{
ctx.clearRect(0,0,canvas.width,canvas.height);
};
/////////////////////////////////////
// BIG & SMALL BUTTONS //////////////
/////////////////////////////////////
let bbDOM = $('.big_button');
let sbDOM = $('.small_button');
bbDOM.onclick = ()=>{
if(daysCurrent>daysTotal || params._HACK_RESET_WHEN_I_100=="go"){
_resetTheSim();
params._HACK_RESET_WHEN_I_100 = undefined;
}else{
if(daysCurrent==0){
restart();
}
IS_PLAYING = !IS_PLAYING;
}
_updateButtons();
};
let defaultParams = [
["p_transmission", 4],
["p_exposed", 3],
["p_recovery", 14],
["p_waning", 1],
["p_hospital", 100],
["p_years", 2],
["p_speed", 30],
];
sbDOM.onclick = ()=>{
if(daysCurrent==0){
changeSliders(defaultParams);
changeSliders(CURRENT_STAGE.inputs);
}else if(daysCurrent>daysTotal){
_replayTheSim();
}else{
_resetTheSim();
}
_updateButtons();
};
let _updateButtons = ()=>{
if(daysCurrent > daysTotal){
bbDOM.setAttribute('label','reset');
sbDOM.setAttribute('label',params.CANNOT_REPLAY_HISTORY ? '' : 'replay');
}else if(IS_PLAYING){
bbDOM.setAttribute('label','pause');
sbDOM.setAttribute('label','reset');
}else{
if(daysCurrent==0){
bbDOM.setAttribute('label','start');
sbDOM.setAttribute('label','NONE');
}else{
bbDOM.setAttribute('label','continue');
sbDOM.setAttribute('label','reset');
}
}
};
let hofp = $('#hide_on_first_playthrough');
let _showAllControls = ()=>{
let originalHeight = hofp.getBoundingClientRect().height;
if(originalHeight>0) return;
hofp.style.height = "auto";
hofp.style.position = "absolute";
hofp.style.top = "-1000px";
setTimeout(()=>{
let newHeight = hofp.getBoundingClientRect().height;
hofp.style.position = "";
hofp.style.top = "";
hofp.style.height = originalHeight+"px";
setTimeout(()=>{
hofp.style.marginTop = "";
hofp.style.height = newHeight+"px";
},20);
},20);
};
let _hideAllControls = ()=>{
hofp.style.height = "0px";
};
let _resetTheSim = ()=>{
_showAllControls();
IS_REPLAYING_HISTORY = false;
recordedHistory = [];
restart();
IS_PLAYING = false;
};
let _replayTheSim = ()=>{
_hideAllControls();
IS_REPLAYING_HISTORY = true;
restart();
IS_PLAYING = true;
};
/*
$('#restart').onclick = ()=>{
IS_REPLAYING_HISTORY = false;
recordedHistory = [];
@ -329,6 +577,7 @@ $('#replay').onclick = ()=>{
IS_REPLAYING_HISTORY = true;
restart();
};
*/
/////////////////////////////////////
// SIM STAGES ///////////////////////
@ -361,34 +610,71 @@ SB Full Sandbox
const STAGES = {
"00": {
hide: [
"section_dynamics",
"c_recovery","label_c_waning","section_meta_years","c_exposed",
"int_block_1","int_block_2","int_block_3","int_block_4","int_block_5","hospital_capacity"
],
inputs: [
["p_transmission",4],
["p_years",1],
["p_speed",5],
["TIME_DELTA", 0.5],
//["EXPONENTIAL",true],
["CANNOT_REPLAY_HISTORY", true]
],
checkboxes: [
["c_recovery", true],
["c_exposed",true]
],
},
//////////////////////////////////////////
// THE SIMULATION ////////////////////////
//////////////////////////////////////////
"01": {
hide: ["section_r","section_meta","label_c_recovery","label_c_waning"],
hide: [
"section_r","label_c_recovery","label_c_waning","section_meta_years","label_c_exposed",
"label_exposed","label_removed",
"label_herd_immunity","label_capacity"
],
inputs: [
["p_years",0.5],
["p_speed",10],
["p_speed",20],
["p_hospital", 0],
["TIME_DELTA", 0.1],
["EXPONENTIAL",true]
]
["EXPONENTIAL",true],
["CANNOT_REPLAY_HISTORY", true],
["_HACK_RESET_WHEN_I_100","ready"],
["_HACK_SHOW_SI_PERCENTS",true]
],
SIR: [0.9999999,0.0000001,0]
},
"02": {
hide: ["section_r","section_meta","label_c_recovery","label_c_waning"],
hide: [
"section_r","section_meta","label_c_recovery","label_c_waning",
"label_exposed","label_removed",
"label_herd_immunity","label_capacity"
],
inputs: [
["p_years",0.5],
["p_speed",10],
["p_speed",20],
["TIME_DELTA", 0.1],
]
["p_hospital", 0],
["_HACK_RESET_WHEN_I_100","ready"],
["_HACK_SHOW_SI_PERCENTS",true]
],
SIR: [0.9999999,0.0000001,0]
},
"03": {
hide: ["section_r","section_meta","label_transmission","label_c_waning","c_recovery"],
inputs: [
["p_years",0.5],
["p_speed",10],
["p_speed",20],
["TIME_DELTA", 0.1],
],
checkboxes: [
@ -398,15 +684,25 @@ const STAGES = {
},
"04": {
hide: ["section_r","section_meta","label_c_waning","c_recovery"],
hide: [
"section_r","label_c_waning","c_recovery","label_c_exposed","section_meta_years",
//"section_meta",
"label_exposed",
"label_herd_immunity","label_capacity"
],
inputs: [
["p_years",0.5],
["p_speed",10],
["TIME_DELTA", 0.1],
["p_speed",20],
["p_hospital", 0],
//["TIME_DELTA", 0.1],
["CANNOT_REPLAY_HISTORY", true],
["DO_NOT_SHOW_HERD_IMMUNITY", true]
],
checkboxes: [
["c_recovery", true]
]
],
SIR: [0.9999999,0.0000001,0],
//SHOW_ALL_AT_START: true,
},
"05a": {
@ -427,7 +723,7 @@ const STAGES = {
"05b": {
hide: [
"section_meta","label_c_waning","c_recovery",
"section_meta","label_c_waning","c_recovery","label_c_exposed",
"int_block_0","int_block_1","int_block_2","int_block_3","int_block_4","int_block_5","hospital_capacity",
"graph",
"sim_controls"
@ -661,9 +957,32 @@ const STAGES = {
};
let changeSliders = (idValPair)=>{
DONT_RECORD_HISTORY = true;
_DO_NOT_RECURSE = true;
idValPair.forEach((idValPair)=>{
let [id,val] = idValPair;
let slider = $('#'+id);
if(slider){
slider.value = val;
slider.oninput();
}
params[id] = val;
});
DONT_RECORD_HISTORY = false;
_DO_NOT_RECURSE = false;
};
let CURRENT_STAGE;
let setStage = (stageID)=>{
let stage = STAGES[stageID];
CURRENT_STAGE = stage;
// Hide what
stage.hide = stage.hide || [];
@ -673,17 +992,7 @@ let setStage = (stageID)=>{
// Sliders
stage.inputs = stage.inputs || [];
DONT_RECORD_HISTORY = true;
stage.inputs.forEach((idValPair)=>{
let [id,val] = idValPair;
let slider = $('#'+id);
if(slider){
slider.value = val;
slider.oninput();
}
params[id] = val;
});
DONT_RECORD_HISTORY = false;
changeSliders(stage.inputs);
// Checkboxes
stage.checkboxes = stage.checkboxes || [];
@ -706,13 +1015,28 @@ let setStage = (stageID)=>{
// Start SIR
if(stage.SIR){
START_S = stage.SIR[0];
START_E = 0;
START_I = stage.SIR[1];
START_R = stage.SIR[2];
}
// Show all?
if(stage.SHOW_ALL_AT_START){
_showAllControls();
}
};
let stageParams = new URLSearchParams(location.search);
if(stageParams.has('stage')) setStage(stageParams.get('stage'));
/////////////////////////////////////
// INIT! ////////////////////////////
/////////////////////////////////////
restart();
s_dom.oninput();
_updateButtons();