|
|
(66 intermediate revisions not shown) |
Line 1: |
Line 1: |
- | <html> | + | <html><div class="simbox" data-width="785" data-height="300" data-load="https://2013.igem.org/Team:Paris_Saclay/</html>{{{load}}}<html>">Loading simulation...</div></html> |
- | <script> | + | |
- | simulations= new Array();
| + | |
- | | + | |
- | function simboxes_load()
| + | |
- | {
| + | |
- | var simboxes= document.getElementsByClassName("simbox");
| + | |
- | | + | |
- | for(var i=0 ; i < simboxes.length ; ++i)
| + | |
- | init_simbox(simboxes[i], i);
| + | |
- | }
| + | |
- | | + | |
- | function molecule()
| + | |
- | {
| + | |
- | this.name= undefined;
| + | |
- | this.init_qtty= 0;
| + | |
- | this.quantity= 0;
| + | |
- | this.curve_show= false;
| + | |
- | this.curve_color= "#000000";
| + | |
- | this.const= false;
| + | |
- | this.adjustable= false;
| + | |
- | this.history= new Array();
| + | |
- | }
| + | |
- | | + | |
- | function reaction()
| + | |
- | {
| + | |
- | this.inputs= new Array();
| + | |
- | this.outputs= new Array();
| + | |
- | this.probability= 0.0;
| + | |
- | }
| + | |
- | | + | |
- | function simulation()
| + | |
- | {
| + | |
- | this.id= undefined;
| + | |
- | this.volume= undefined;
| + | |
- | this.timestep= undefined;
| + | |
- | this.molecules= new Array();
| + | |
- | this.reactions= new Array();
| + | |
- | this.reac_order= new Array();
| + | |
- | this.running= false;
| + | |
- | this.refresh_run_time= 50;
| + | |
- | this.refresh_interval= 100;
| + | |
- | this.timeout= null;
| + | |
- | this.curtime= 0.0;
| + | |
- | | + | |
- | this.canv= null;
| + | |
- | this.ctx= null;
| + | |
- | this.draw_min_t= 0.0;
| + | |
- | this.draw_max_t= 100.0;
| + | |
- | this.draw_min_v= 0.0;
| + | |
- | this.draw_max_v= 100.0;
| + | |
- | }
| + | |
- | | + | |
- | function simbox_get_molecule_id(simid, name)
| + | |
- | {
| + | |
- | for(var i= 0 ; i < simulations[simid].molecules.length ; ++i)
| + | |
- | {
| + | |
- | if(simulations[simid].molecules[i].name == name)
| + | |
- | return i;
| + | |
- | }
| + | |
- | return null;
| + | |
- | }
| + | |
- | | + | |
- | function init_simbox(sb, id)
| + | |
- | {
| + | |
- | simulations[id]= new simulation();
| + | |
- | | + | |
- | //Get parameters
| + | |
- | var xmlfile= 'https://2013.igem.org/Team:Paris_Saclay/'+sb.getAttribute('data-load')+'?action=raw&ctype=text/css';
| + | |
- | var width= sb.getAttribute('data-width');
| + | |
- | var height= sb.getAttribute('data-height');
| + | |
- | | + | |
- | //Open XML data file
| + | |
- | var xmldata= null;
| + | |
- | try
| + | |
- | {
| + | |
- | var xhr= new XMLHttpRequest();
| + | |
- | xhr.open("GET", xmlfile, false);
| + | |
- | xhr.send();
| + | |
- | var parser= new DOMParser();
| + | |
- | xmldata= parser.parseFromString(xhr.responseText, "application/xml");
| + | |
- | if(xmldata.documentElement.nodeName != 'sim')
| + | |
- | throw 'XML parsing error';
| + | |
- | }
| + | |
- | catch(err)
| + | |
- | {
| + | |
- | sb.innerHTML='Error : ' + err;
| + | |
- | return false;
| + | |
- | }
| + | |
- | | + | |
- | //Load simulation parameters
| + | |
- | simulations[id].volume= xmldata.getElementsByTagName("volume")[0].childNodes[0].nodeValue;
| + | |
- | simulations[id].timestep= xmldata.getElementsByTagName("timestep")[0].childNodes[0].nodeValue;
| + | |
- | | + | |
- | //Load molecules
| + | |
- | var molecule_tags= xmldata.getElementsByTagName("molecule");
| + | |
- | for(var i= 0 ; i < molecule_tags.length ; ++i)
| + | |
- | {
| + | |
- | simulations[id].molecules[i]= new molecule();
| + | |
- | simulations[id].molecules[i].name= molecule_tags[i].getAttribute('name');
| + | |
- | if(molecule_tags[i].hasAttribute('quantity'))
| + | |
- | {
| + | |
- | simulations[id].molecules[i].init_qtty= molecule_tags[i].getAttribute('quantity');
| + | |
- | simulations[id].molecules[i].quantity= molecule_tags[i].getAttribute('quantity');
| + | |
- | }
| + | |
- | if(molecule_tags[i].hasAttribute('curve_show'))
| + | |
- | simulations[id].molecules[i].curve_show= (molecule_tags[i].getAttribute('curve_show') == 'true');
| + | |
- | if(molecule_tags[i].hasAttribute('curve_color'))
| + | |
- | simulations[id].molecules[i].curve_color= molecule_tags[i].getAttribute('curve_color');
| + | |
- | if(molecule_tags[i].hasAttribute('const'))
| + | |
- | simulations[id].molecules[i].const= (molecule_tags[i].getAttribute('const') == 'true');
| + | |
- | if(molecule_tags[i].hasAttribute('adjustable'))
| + | |
- | simulations[id].molecules[i].adjustable= (molecule_tags[i].getAttribute('adjustable') == 'true');
| + | |
- | }
| + | |
- | | + | |
- | //Load reactions
| + | |
- | var reaction_tags= xmldata.getElementsByTagName("reaction");
| + | |
- | for(var i= 0 ; i < reaction_tags.length ; ++i)
| + | |
- | {
| + | |
- | simulations[id].reactions[i]= new reaction();
| + | |
- | if(reaction_tags[i].hasAttribute('probability'))
| + | |
- | simulations[id].reactions[i].probability= reaction_tags[i].getAttribute('probability');
| + | |
- |
| + | |
- | var input_tags= reaction_tags[i].getElementsByTagName("in");
| + | |
- | for(var j= 0 ; j < input_tags.length ; ++j)
| + | |
- | {
| + | |
- | var tmpid= simbox_get_molecule_id(id, input_tags[j].childNodes[0].nodeValue);
| + | |
- | if(tmpid == null)
| + | |
- | {
| + | |
- | sb.innerHTML='Error : molecule "'+input_tags[j].childNodes[0].nodeValue+'" not defined.';
| + | |
- | return false;
| + | |
- | }
| + | |
- | simulations[id].reactions[i].inputs[j]= tmpid;
| + | |
- | }
| + | |
- | | + | |
- | var output_tags= reaction_tags[i].getElementsByTagName("out");
| + | |
- | for(var j= 0 ; j < output_tags.length ; ++j)
| + | |
- | {
| + | |
- | var tmpid= simbox_get_molecule_id(id, output_tags[j].childNodes[0].nodeValue);
| + | |
- | if(tmpid == null)
| + | |
- | {
| + | |
- | sb.innerHTML='Error : molecule "'+output_tags[j].childNodes[0].nodeValue+'" not defined.';
| + | |
- | return false;
| + | |
- | }
| + | |
- | simulations[id].reactions[i].outputs[j]= tmpid;
| + | |
- | }
| + | |
- |
| + | |
- | //precompute index array for easy shuffling
| + | |
- | simulations[id].reac_order[i]= i;
| + | |
- | }
| + | |
- |
| + | |
- | | + | |
- | //Create canvas
| + | |
- | simulations[id].canv= document.createElement('canvas');
| + | |
- | simulations[id].ctx= simulations[id].canv.getContext("2d");
| + | |
- | simulations[id].canv.id= "simbox_canv_"+id;
| + | |
- | simulations[id].canv.width= width;
| + | |
- | simulations[id].canv.height= height;
| + | |
- | sb.appendChild(simulations[id].canv);
| + | |
- |
| + | |
- | //Init param box
| + | |
- | var paramdiv= document.createElement('div');
| + | |
- | sb.appendChild(paramdiv);
| + | |
- | //legend
| + | |
- | var reshtmlparamdiv= '';
| + | |
- | for(var i= 0 ; i < simulations[id].molecules.length ; ++i)
| + | |
- | {
| + | |
- | if(simulations[id].molecules[i].curve_show)
| + | |
- | reshtmlparamdiv += '<span style="font-weight:bold;padding:0.1em 0.3em;color:#FFFFFF;background-color:'+simulations[id].molecules[i].curve_color+';">'+simulations[id].molecules[i].name+'</span> ';
| + | |
- | }
| + | |
- | //quantity controls
| + | |
- | for(var i= 0 ; i < simulations[id].molecules.length ; ++i)
| + | |
- | {
| + | |
- | if(simulations[id].molecules[i].adjustable)
| + | |
- | {
| + | |
- | reshtmlparamdiv += '<br /><br />';
| + | |
- | reshtmlparamdiv += simulations[id].molecules[i].name + ' = <input id="simbox_ctl_'+id+'_'+i+'" type="number" min="0" value="'+simulations[id].molecules[i].quantity+'"/>';
| + | |
- | }
| + | |
- | }
| + | |
- | reshtmlparamdiv += '<br /><br />';
| + | |
- | //controls
| + | |
- | reshtmlparamdiv += '<button onclick="simbox_startclick(this,'+id+')" id="simbox_start_'+id+'">PLAY</button><button onclick="simbox_resetclick(this,'+id+')" id="simbox_reset_'+id+'">RESET</button>';
| + | |
- | paramdiv.innerHTML= reshtmlparamdiv;
| + | |
- | | + | |
- | return true;
| + | |
- | }
| + | |
- | | + | |
- | function is_posint(str)
| + | |
- | {
| + | |
- | var parsed= parseInt(str);
| + | |
- | if(isNaN(parsed)) return false;
| + | |
- | if(!isFinite(parsed)) return false;
| + | |
- | if(parsed < 0) return false;
| + | |
- | return true;
| + | |
- | }
| + | |
- | | + | |
- | function simbox_startclick(btn, simid)
| + | |
- | {
| + | |
- | if(simulations[simid].running)
| + | |
- | {
| + | |
- | window.clearTimeout(simulations[simid].timeout);
| + | |
- | simulations[simid].running= false;
| + | |
- | btn.innerHTML= 'PLAY';
| + | |
- | }
| + | |
- | else
| + | |
- | {
| + | |
- | for(var i= 0 ; i < simulations[simid].molecules.length ; ++i)
| + | |
- | {
| + | |
- | if(simulations[simid].molecules[i].adjustable)
| + | |
- | {
| + | |
- | var tmpval= document.getElementById('simbox_ctl_'+simid+'_'+i).value;
| + | |
- | if(!is_posint(tmpval))
| + | |
- | {
| + | |
- | alert('Invalid quantity set for molecule "'+simulations[simid].molecules[i].name+'".');
| + | |
- | return false;
| + | |
- | }
| + | |
- | simulations[simid].molecules[i].quantity= parseInt(tmpval);
| + | |
- | }
| + | |
- | }
| + | |
- | simulations[simid].running= true;
| + | |
- | btn.innerHTML= 'PAUSE';
| + | |
- | simbox_refresh(simid);
| + | |
- | }
| + | |
- | simbox_update_controls(simid);
| + | |
- | return true;
| + | |
- | }
| + | |
- | | + | |
- | function simbox_resetclick(btn, simid)
| + | |
- | {
| + | |
- | if(simulations[simid].running)
| + | |
- | {
| + | |
- | window.clearTimeout(simulations[simid].timeout);
| + | |
- | simulations[simid].running= false;
| + | |
- | document.getElementById('simbox_start_'+simid).innerHTML= 'PLAY';
| + | |
- | }
| + | |
- |
| + | |
- | //Reset quantities, clear curves
| + | |
- | simulations[simid].curtime= 0;
| + | |
- | for(var i= 0 ; i < simulations[simid].molecules.length ; ++i)
| + | |
- | {
| + | |
- | simulations[simid].molecules[i].quantity= simulations[simid].molecules[i].init_qtty;
| + | |
- | simulations[simid].molecules[i].history.length= 0;
| + | |
- | }
| + | |
- | | + | |
- | //Update controls
| + | |
- | simbox_update_controls(simid);
| + | |
- | }
| + | |
- | | + | |
- | function simbox_update_controls(simid)
| + | |
- | {
| + | |
- | for(var i= 0 ; i < simulations[simid].molecules.length ; ++i)
| + | |
- | {
| + | |
- | if(simulations[simid].molecules[i].adjustable)
| + | |
- | {
| + | |
- | document.getElementById('simbox_ctl_'+simid+'_'+i).value= simulations[simid].molecules[i].quantity;
| + | |
- | document.getElementById('simbox_ctl_'+simid+'_'+i).disabled= simulations[simid].running;
| + | |
- | }
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | function simbox_refresh(simid)
| + | |
- | {
| + | |
- | var tmpdate= new Date();
| + | |
- | var time1= tmpdate.getTime();
| + | |
- | | + | |
- | while(tmpdate.getTime() - time1 < simulations[simid].refresh_run_time)
| + | |
- | {
| + | |
- | simulation_step(simid);
| + | |
- | tmpdate= new Date();
| + | |
- | }
| + | |
- |
| + | |
- | simbox_draw_graph(simid);
| + | |
- | simbox_update_controls(simid);
| + | |
- | | + | |
- | simulations[simid].timeout= setTimeout(function() {simbox_refresh(simid);}, simulations[simid].refresh_interval);
| + | |
- | }
| + | |
- | | + | |
- | function shuffle_array(array)
| + | |
- | {
| + | |
- | for(var i= array.length-1; i > 0 ; --i)
| + | |
- | {
| + | |
- | var j= Math.floor(Math.random() * (i + 1));
| + | |
- | var tmp= array[i];
| + | |
- | array[i]= array[j];
| + | |
- | array[j]= tmp;
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | function simulation_step(simid)
| + | |
- | {
| + | |
- | var sim= simulations[simid];
| + | |
- | | + | |
- | //Shuffle reaction order
| + | |
- | shuffle_array(sim.reac_order);
| + | |
- | | + | |
- | for(var i= 0 ; i < sim.reac_order.length ; ++i)
| + | |
- | {
| + | |
- | var usedmols= new Array();
| + | |
- | | + | |
- | var reac= sim.reactions[sim.reac_order[i]];
| + | |
- | var proba= reac.probability * sim.timestep;
| + | |
- | for(var j= 0 ; j < reac.inputs.length ; ++j)
| + | |
- | {
| + | |
- | var inid= reac.inputs[j];
| + | |
- | if(!sim.molecules[inid].const)
| + | |
- | {
| + | |
- | if(usedmols[inid] == undefined)
| + | |
- | usedmols[inid]= 1;
| + | |
- | else
| + | |
- | usedmols[inid] ++;
| + | |
- | }
| + | |
- | proba *= sim.molecules[inid].quantity;
| + | |
- | }
| + | |
- | for(var j= 0 ; j < reac.outputs.length ; ++j)
| + | |
- | {
| + | |
- | var outid= reac.outputs[j];
| + | |
- | if(usedmols[outid] != undefined)
| + | |
- | usedmols[outid]--;
| + | |
- | }
| + | |
- | proba /= Math.pow(sim.volume, reac.inputs.length);
| + | |
- | | + | |
- | //alert('BEFORE : reac '+i+ ' proba=' + proba);
| + | |
- | | + | |
- | var entier= Math.floor(proba);
| + | |
- | var frac= proba - entier;
| + | |
- | for(var elt in usedmols)
| + | |
- | {
| + | |
- | if( entier * usedmols[elt] >= sim.molecules[elt].quantity )
| + | |
- | {
| + | |
- | var tmpe= Math.floor(sim.molecules[elt].quantity / usedmols[elt]);
| + | |
- | if(tmpe < entier)
| + | |
- | entier= tmpe;
| + | |
- | frac= 0;
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | //alert('AFTER : reac '+i+ ' proba=' + (entier+frac));
| + | |
- | | + | |
- | for(var j= 0 ; j < reac.inputs.length ; ++j)
| + | |
- | {
| + | |
- | if(!sim.molecules[reac.inputs[j]].const)
| + | |
- | sim.molecules[reac.inputs[j]].quantity -= entier;
| + | |
- | }
| + | |
- | for(var j= 0 ; j < reac.outputs.length ; ++j)
| + | |
- | {
| + | |
- | if(!sim.molecules[reac.outputs[j]].const)
| + | |
- | sim.molecules[reac.outputs[j]].quantity += entier;
| + | |
- | }
| + | |
- | | + | |
- | var lastone= false;
| + | |
- | if(frac > 0)
| + | |
- | lastone= (Math.random() < frac);
| + | |
- | if(lastone)
| + | |
- | {
| + | |
- | for(var j= 0 ; j < reac.inputs.length ; ++j)
| + | |
- | {
| + | |
- | if(!sim.molecules[reac.inputs[j]].const)
| + | |
- | sim.molecules[reac.inputs[j]].quantity --;
| + | |
- | }
| + | |
- | for(var j= 0 ; j < reac.outputs.length ; ++j)
| + | |
- | {
| + | |
- | if(!sim.molecules[reac.outputs[j]].const)
| + | |
- | sim.molecules[reac.outputs[j]].quantity ++;
| + | |
- | }
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | for(var i= 0 ; i < sim.molecules.length ; ++i)
| + | |
- | sim.molecules[i].history.push(sim.molecules[i].quantity);
| + | |
- | | + | |
- | sim.curtime += sim.timestep;
| + | |
- | }
| + | |
- | | + | |
- | function simbox_drawXcoord(simid, t)
| + | |
- | {
| + | |
- | var tmin= simulations[simid].draw_min_t;
| + | |
- | var tmax= simulations[simid].draw_max_t;
| + | |
- | return simulations[simid].canv.width*(t-tmin)/(tmax-tmin);
| + | |
- | }
| + | |
- | function simbox_drawYcoord(simid, v)
| + | |
- | {
| + | |
- | var vmin= simulations[simid].draw_min_v;
| + | |
- | var vmax= simulations[simid].draw_max_v;
| + | |
- | return simulations[simid].canv.height*(v-vmin)/(vmax-vmin);
| + | |
- | }
| + | |
- | | + | |
- | function simbox_draw_graph(simid)
| + | |
- | {
| + | |
- | var sim= simulations[simid];
| + | |
- | var canv= sim.canv;
| + | |
- | var ctx= sim.ctx;
| + | |
- | | + | |
- | ctx.clearRect(0, 0, canv.width, canv.height);
| + | |
- | | + | |
- | for(var i= 0 ; i < sim.molecules.length ; ++i)
| + | |
- | {
| + | |
- | if(sim.molecules[i].curve_show)
| + | |
- | {
| + | |
- | var dta= sim.molecules[i].history;
| + | |
- | var tstart= Math.floor(sim.draw_min_t/sim.timestep);
| + | |
- | var tend= Math.ceil(sim.draw_max_t/sim.timestep);
| + | |
- | | + | |
- | if(dta.length < 2) continue;
| + | |
- |
| + | |
- | for(var ti= tstart ; ti < tend ; ++ti)
| + | |
- | {
| + | |
- | if(ti > tstart)
| + | |
- | ctx.lineTo( simbox_drawXcoord(simid, ti*sim.timestep), simbox_drawYcoord(simid, dta[ti]) );
| + | |
- | else
| + | |
- | ctx.moveTo( simbox_drawXcoord(simid, ti*sim.timestep), simbox_drawYcoord(simid, dta[ti]) );
| + | |
- | }
| + | |
- | | + | |
- | ctx.stroke();
| + | |
- | }
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | | + | |
- | window.addEventListener("load", simboxes_load, false);
| + | |
- | </script>
| + | |
- | | + | |
- | <div class="simbox" data-width="</html>{{{width}}}<html>" data-height="</html>{{{height}}}<html>" data-load="</html>{{{load}}}<html>"></div>
| + | |
- | | + | |
- | </html> | + | |
Loading simulation...