xxxxxxxxxx
373
info = ' note: in console can click on LINKS\n';
info += ' \n';
info += ' kll: yes it has to do with the corona virus epidemiology \n';
info += ' i try to find / learn some math, first only use:\n';
info += ' https://stackoverflow.com/questions/41195063/general-smoothstep-equation \n';
info += ' here try Pascal Triangle solution from Tushar Ghosh answer\n';
info += ' \n';
info += ' you are in the edit window and can press the (>) RUN button \n';
info += ' or go to present view https://editor.p5js.org/kll/present/kKdj9HDS1 \n\n';
info += ' operation: change slider to see day details ( white ) or adjust other settings numbers.\n\n';
info += ' copyleft: KLL engineering ( CC BY SA )\n';
info += ' more: http://kll.engineering-news.org/kllfusion01/articles.php?article_id=168 \n';
//________________________________________________________ VARIABLES
var x = 0;
var xmax = 100; //______ x axis 0 .. 100 ( timeline like in DAYs )
var xstart = 10; //_____ start step function at day
var xstop = 70; //______ stop step function at day
var xs = 0.0; //________ 0.0 .. 1.0 for the graph function
var dxss = 100.0 / (xstop - xstart) / xmax; // stepsize in the smooth window
var xss = 0; //_________ 0.0 .. 1.0 for the smooth step range calc function
var y = 0;
var ymax = 100.0; //____ y axis 0 .. 100.0% / like of
var yreal = 331092264; // people in a country 69808350 THAILAND / 83783942 GERMANY / 331092264 USA
var yzoom = 2; //_______ right axis range for "R" https://en.wikipedia.org/wiki/Basic_reproduction_number
var ystart = 0; //______ start step from population %
var ystop = 80; //______ stop step at population ( assume epidemic level where infection stops alone )
var ys = 0.0; //________ 0.0 .. 1.0 for the calc function
var ydt = 18; //________ after 18 days of infection you are dead or ?healed?immune?
var ymt = 7.0; //_______ pct of infected die ( USA 19.7 https://www.worldometers.info/coronavirus/country/us/ )
var astep = 3.0; //_____ for generalSmoothStep(a, x)
// init data arrays
var xi = [100]; //_______ 0 .. 100 x ( 6 pix ) in graph area ( days )
var yi = [100]; //_______ 0 .. 100.0 y step function ( total infections )
var ydi = [100]; //______ numeric derivative of y data ( new infections )
var yti = [100]; //______ timespaned (ydt) infected ( active cases )
var ymd = [100]; //______ mortal cases that day ( not show in graph )
var ymi = [100]; //______ mortal cases
var yRi = [100]; //______ R reproduction rate
// garph area settings
var gx0 = 130;
var gy0 = 400;
var gxw = 600;
var gyh = 300;
let sSlider; //___________________________________________ day select slider
let ss = 0; //____________________________________________ select slider setpoint
let inp1,inp2,inp3,inp4,inp5,inp6,inp7,inp8;
//________________________________________________________ SETUP
function setup() {
createCanvas(800, 520+120); // add operation menu
console.log(info);
create_DOM();
// test_print();
calc_list(); //_________________________________________ fill data arrays xi,yi ( and print )
}
function create_DOM() {
sSlider = createSlider(0, 99, ss, 1);
sSlider.size(gxw);
sSlider.position(gx0, 530);
inp1 = createInput(str(xstart));
inp1.changed(myInputXSTART);
inp1.position(50,520+50);
inp1.size(40);
inp2 = createInput(str(xstop));
inp2.changed(myInputXSTOP);
inp2.position(150,520+50);
inp2.size(40);
inp3 = createInput(str(ystart));
inp3.changed(myInputYSTART);
inp3.position(250,520+50);
inp3.size(40);
inp4 = createInput(str(ystop));
inp4.changed(myInputYSTOP);
inp4.position(350,520+50);
inp4.size(40);
inp5 = createInput(str(astep));
inp5.changed(myInputASTEP);
inp5.position(450,520+50);
inp5.size(40);
inp6 = createInput(str(ydt));
inp6.changed(myInputYDT);
inp6.position(50,520+90);
inp6.size(40);
inp7 = createInput(str(ymt));
inp7.changed(myInputYMT);
inp7.position(150,520+90);
inp7.size(40);
inp8 = createInput(str(yreal));
inp8.changed(myInputYREAL);
inp8.position(250,520+90);
inp8.size(80);
}
// change number and press ENTER to get that going
function myInputXSTART() {
xstart = int(this.value());
calc_list();
console.log('xstart: ', xstart);
}
function myInputXSTOP() {
xstop = int(this.value());
calc_list();
console.log('xstop: ', xstop);
}
function myInputYSTART() {
ystart = int(this.value());
calc_list();
console.log('ystart: ', ystart);
}
function myInputYSTOP() {
ystop = int(this.value());
calc_list();
console.log('ystop: ', ystop);
}
function myInputASTEP() {
astep = float(this.value());
calc_list();
console.log('slope: ', astep);
}
function myInputYDT() {
ydt = int(this.value());
calc_list();
console.log('sick days: ', ydt);
}
function myInputYMT() {
ymt = float(this.value());
calc_list();
console.log('mortality : ', ymt,'%' );
}
function myInputYREAL() {
yreal = int(this.value());
calc_list();
console.log('population: ', yreal );
}
//________________________________________________________ DRAW
function draw() {
background(150, 150, 0);
ss = sSlider.value();
draw_graph();
}
//________________________________________________________ GRAPH
function draw_axis(ls = 10) {
textSize(12);
for (var i = 0; i <= ls; i++) {
stroke(0,0, 80);
line(gx0 - 20, gy0 - i * gyh / ls, gx0 - 10, gy0 - i * gyh / ls);
noStroke();
text(nf(i * ymax / ls, 3, 0), gx0 - 50, gy0 - i * gyh / ls + 4);
text('pct', gx0 - 45, gy0 - gyh - 15);
text(nf(i * yreal / ls, 8, 0), gx0 - 115, gy0 - i * gyh / ls + 4);
text('people', gx0 - 115, gy0 - gyh - 15);
text(nf(i * yzoom / ls, 0, 1), gx0 + gxw + 10, gy0 - i * gyh / ls + 4);
text('R', gx0 + gxw + 10, gy0 - gyh - 15);
stroke(0.0, 80);
line(gx0 + i * gxw / ls, gy0 + 10, gx0 + i * gxw / ls, gy0 + 20);
noStroke();
text(nf(i * xmax / ls, 0, 0), gx0 + i * gxw / ls - 3, gy0 + 35);
text('days', gx0 + gxw - 10, gy0 + 50);
}
strokeWeight(6); //_____________________________________ Make the points n pixels in size
stroke('purple'); //____________________________________ total infections
point(gx0, gy0 + 55);
noStroke();
fill(0);
text('total infection / '+nf(xstart,0,0)+'..'+nf(xstop,0,0)+' days '+nf(ystart,0,0)+'..'+nf(ystop,0,0)+' %', gx0 + 15, gy0 + 60);
fill('white');
text(nf(yi[ss],0,1)+' % '+nf(yi[ss]*yreal/100,0,0),gx0 + 220, gy0 + 60);
stroke('red'); //______________________________________ step derivative new infections
//noFill();
fill(0, 70, 80);
strokeWeight(1);
circle(gx0, gy0 + 70, 5);
noStroke();
fill(0);
text('new infections per day', gx0 + 15, gy0 + 75);
fill('white');
text(nf(ydi[ss],0,1)+' % '+nf(ydi[ss]*yreal/100,0,0),gx0 + 220, gy0 + 75);
//noFill();
fill(0, 70, 80);
stroke('cyan'); //______________________________________ active cases
rectMode(RADIUS);
strokeWeight(0.5);
rect(gx0, gy0 + 85, 3, 3);
noStroke();
fill(0);
text('active cases ( avg ' + nf(ydt, 0, 0) + ' days sick )', gx0 + 15, gy0 + 90);
fill('white');
text(nf(yti[ss],0,1)+' % '+nf(yti[ss]*yreal/100,0,0),gx0 + 220, gy0 + 90);
stroke('black'); //_____________________________________ dead
noFill();
circle(gx0, gy0 + 100, 5);
fill(0);
noStroke();
//text('+',gx0-3, gy0 + 100+5);
noStroke();
fill(0);
text('fatal cases ( avg ' + nf(ymt, 0, 1) + ' pct mortality)', gx0 + 15, gy0 + 105);
fill('white');
text(nf(ymi[ss],0,1)+' % '+nf(ymi[ss]*yreal/100,0,0),gx0 + 220, gy0 + 105);
noFill(); //____________________________________________ R value ( right Y axis )
stroke(200, 0, 0);
strokeWeight(3);
point(gx0 + gxw - 220, gy0 + 70);
noStroke();
fill(0);
text('R value ( last 4 day / prev 4 day )', gx0 + gxw - 205, gy0 + 75);
fill('white');
text(nf(yRi[ss],0,2),gx0 +gxw -20, gy0 + 75);
strokeWeight(1); //_____________________________________ slider position
stroke('white');
line(gx0+ss*gxw/100,gy0,gx0+ss*gxw/100,gy0-gyh-10);
line(0,520,800,520);
fill('white');
noStroke();
text('select day: '+nf(ss,0,0),gx0-80,530+15);
text('Operation',10,520+15);
text('infection simulation (Smooth Step Parameter)',10,520+45);
text('xstart',10,520+65);
text('xstop',110,520+65);
text('ystart',210,520+65);
text('ystop',310,520+65);
text('slope',410,520+65);
text('epidemiologic Parameter:',10,520+85);
text('sickday',10,520+105);
text('mortal',110,520+105);
text('people',210,520+105);
}
function draw_graph() {
fill(0, 80, 0);
stroke(100, 0, 0);
strokeWeight(2);
textSize(30);
text("simple epidemiological math", 30, 30);
fill(0, 70, 80);
stroke(0, 200, 0);
rectMode(CORNER);
rect(gx0 - 3, gy0 + 3, gxw + 6, -gyh - 6); //___________ graph background 6 pix bigger
draw_axis(10);
rectMode(RADIUS);
for (var s = 0; s < 100; s++) {
strokeWeight(6); //_____________________________________ total infections ( from smooth step function )
stroke('purple');
point(gx0 + xi[s] * gxw / 100.0, gy0 - yi[s] * gyh / 100.0); // use array data in plot window
stroke('red'); //_______________________________________ new infections
noFill();
strokeWeight(1);
circle(gx0 + xi[s] * gxw / 100.0, gy0 - ydi[s] * gyh / 100.0, 6); // use array data in plot window
noFill(); //____________________________________________ R value
stroke('red');
strokeWeight(3);
if ( yRi[s] > 0.0 && yRi[s] < 2.0 )
point(gx0 + xi[s] * gxw / 100.0, gy0 - yRi[s] * gyh / 2.0);
stroke('cyan'); //______________________________________ active cases
strokeWeight(0.5);
rect(gx0 + xi[s] * gxw / 100.0, gy0 - yti[s] * gyh / 100.0, 3, 3); // use array data in plot window
//fill(0); //_____________________________________________ fatal cases
//noStroke();
//text('+',gx0 + xi[s] * gxw / 100.0 -3, gy0 - ymi[s] * gyh / 100.0 +5);
stroke('black'); //_______________________________________ new infections
noFill();
strokeWeight(1);
circle(gx0 + xi[s] * gxw / 100.0, gy0 - ymi[s] * gyh / 100.0, 6); // use array data in plot window
}
}
function calc_list() {
// new version allow recalc after change of settings
x = 0;
xs = 0.0;
xss = 0;
ys = 0;
y = 0;
var yd = 0.0;
dxss = 100.0 / (xstop - xstart) / xmax;
for (var s = 0; s < 100; s++) {
xs = s * 0.01;
x = xs * 100;
if (xs > xstart / xmax && xs <= xstop / xmax) {
xss += dxss;
ys = generalSmoothStep(astep, xss); //________________ 0.0 .. 1.0 over x 0.0 .. 1.0
}
y = ystart + ys * (ystop - ystart); //__________________ in y limited range
xi[s]=x;
yi[s]=y; //__________________________________________ total infections
if (s > 0) yd = y - yi[s - 1];
else yd = 0.0;
ydi[s]=yd; //________________________________________ new cases
yti[s]=0; //_________________________________________ init empty / calc later in a second run
ymd[s]=0; //_________________________________________ init empty / calc later in a second run
ymi[s]=0; //_________________________________________ init empty / calc later in a second run
yRi[s]=0; //_________________________________________ init empty / calc later in a second run
//console.log('s', s + 1, 'xs', nf(xs, 0, 2), 'x', nf(x, 0, 2), 'xss', nf(xss, 0, 3), 'ys', nf(ys, 0, 3), 'y', nf(y, 0, 2), 'yd', nf(yd, 0, 2));
}
// second run to calculate active cases
for (s = ydt; s < 100; s++)
for (var si = s - ydt; si <= s; si++) // accumulate all not older as ydt days
yti[s] += ydi[si];
// third run to calculate Deaths per day
for (s = 0; s < 100; s++)
if (s > ydt) ymd[s + ydt] = ydi[s] * ymt / 100.0;
// 4th run to calculate accumulated Deaths
for (s = 1; s < 100; s++) {
ymi[s] = ymi[s - 1] + ymd[s]; // total deaths
}
// 5th run calc R
for (s = 7; s < 100; s++) {
var last4 = ydi[s - 3] + ydi[s - 2] + ydi[s - 1] + ydi[s];
var prev4 = ydi[s - 7] + ydi[s - 6] + ydi[s - 5] + ydi[s - 4];
if ( prev4 > 0 ) yRi[s] = last4 / prev4;
}
}
//________________________________________________________ MATH
function generalSmoothStep(a, x) { //_____________________ Generalized smoothstep
var result = 0;
for (var n = 0; n <= a - 1; n++)
result += pascalTriangle(-a, n) * pascalTriangle(2 * a - 1, a - n - 1) * Math.pow(x, a + n);
return result;
}
function pascalTriangle(a, b) {
var result = 1;
for (var i = 1; i <= b; i++)
result *= ((a - (i - 1)) / i);
return result;
}
// unused
function smoothStep(x) { //_______________________________ Normal smoothstep
return -2 * Math.pow(x, 3) + 3 * Math.pow(x, 2);
}
function test_print() {
console.log("smoothStep(0.3) = " + smoothStep(0.3));
console.log("generalSmoothStep(2, 0.3) = " + generalSmoothStep(2, 0.3));
}