neuromodeljs/js/script.js
2019-11-03 10:56:24 +01:00

234 lines
8.4 KiB
JavaScript

let legendContainer;
let axes;
let monitorPos;
let chLocked;
// Initialize function
$(function() {
let modelParams = getModelPreset('squid-axon');
let simParams = getSimulationParams();
legendContainer = document.getElementById("nmjs-legend");
chLocked = false;
setInputs(modelParams, simParams);
$("[name=stardurButton]").click(function() {
refreshPlot();
});
$("input").change(function() {
refreshPlot();
});
refreshPlot();
});
function setInputs(modelParams, simParams) {
// Set input values
$("input[name=C]").val(parseFloat(modelParams.C));
$("input[name=GKMax]").val(parseFloat(modelParams.K.GMax));
$("input[name=GNaMax]").val(parseFloat(modelParams.Na.GMax));
$("input[name=Gm]").val(parseFloat(modelParams.L.G));
$("input[name=EK]").val(parseFloat(modelParams.K.E));
$("input[name=ENa]").val(parseFloat(modelParams.Na.E));
$("input[name=VRest]").val(parseFloat(modelParams.VRest));
$("input[name=V0]").val(parseFloat(modelParams.V0));
$("input[name=dur]").val(parseFloat(simParams.dur));
$("input[name=inj1Start]").val(parseFloat(simParams.inj1Start));
$("input[name=inj1Dur]").val(parseFloat(simParams.inj1Dur));
$("input[name=inj1Amp]").val(parseFloat(simParams.inj1Amp));
$("input[name=inj2Start]").val(parseFloat(simParams.inj2Start));
$("input[name=inj2Dur]").val(parseFloat(simParams.inj2Dur));
$("input[name=inj2Amp]").val(parseFloat(simParams.inj2Amp));
}
function getInputs(modelParams, simParams) {
// Get input values
modelParams.C = parseFloat($("input[name=C]").val());
modelParams.K.GMax = parseFloat($("input[name=GKMax]").val());
modelParams.Na.GMax = parseFloat($("input[name=GNaMax]").val());
modelParams.L.G = parseFloat($("input[name=Gm]").val());
modelParams.K.E = parseFloat($("input[name=EK]").val());
modelParams.Na.E = parseFloat($("input[name=ENa]").val());
modelParams.VRest = parseFloat($("input[name=VRest]").val());
modelParams.V0 = parseFloat($("input[name=V0]").val());
simParams.dur = parseFloat($("input[name=dur]").val());
simParams.inj1Start = parseFloat($("input[name=inj1Start]").val());
simParams.inj1Dur = parseFloat($("input[name=inj1Dur]").val());
simParams.inj1Amp = parseFloat($("input[name=inj1Amp]").val());
simParams.inj2Start = parseFloat($("input[name=inj2Start]").val());
simParams.inj2Dur = parseFloat($("input[name=inj2Dur]").val());
simParams.inj2Amp = parseFloat($("input[name=inj2Amp]").val());
return {
modelParams: modelParams,
simParams: simParams
}
}
function updateMonitor(rv, j) {
// Update the monitor values
$(".monitor--mp").html("<span class='label'>Membrane potential (Vm):</span><span class='value'>" + parseFloat(rv.Vs[j][1]).toFixed(2) + "</span> mV");
$(".monitor--iinj").html("<span class='label'>Injected current (Iinj):</span><span class='value'>" + parseFloat(rv.Iinj[j][1]).toFixed(2) + "</span> nA/cm^2");
$(".monitor--ik").html("<span class='label'>Potassium current (IK):</span><span class='value'>" + parseFloat(rv.IK[j][1]).toFixed(2) + "</span> nA/cm^2");
$(".monitor--gk").html("<span class='label'>Potassium conductance (GK):</span><span class='value'>" + parseFloat(rv.GK[j][1]).toFixed(2) + "</span> mS/cm^2");
$(".monitor--ina").html("<span class='label'>Sodium current (INa):</span><span class='value'>" + parseFloat(rv.INa[j][1]).toFixed(2) + "</span> nA/cm^2");
$(".monitor--gna").html("<span class='label'>Sodium conductance (GNa):</span><span class='value'>" + parseFloat(rv.GNa[j][1]).toFixed(2) + "</span> mS/cm^2");
}
function takesnapshot() {
// Take a snapshot of the plot and put it underneath
let width = $('.flot-base').prop('width');
let height = $('.flot-base').prop('height');
let newCanvas = $('#modelSim--snapshots').append('<canvas class="flot-base-extra" width="'+width+'" height="'+height+'">');
newCanvas = $(newCanvas[0]).children('canvas');
newCanvas = newCanvas[0];
let context = newCanvas.getContext('2d');
context.drawImage(document.querySelector('.flot-base'), 0, 0);
}
function runSimulation(modelParams, simParams) {
let hodgkinHuxley = new HodgkinHuxleySim(modelParams, simParams);
let run = hodgkinHuxley.run();
if (isNaN(run.var.Vs[run.var.Vs.length-1][1])) {
$('#errorDiv').html('<b>simulation produced NaN, probably numerical instability or bug</b>');
} else {
$('#errorDiv').html();
}
return run;
}
function refreshPlot() {
let modelParams = getModelPreset('squid-axon');
let simParams = getSimulationParams();
let inp = getInputs(modelParams, simParams);
modelParams = inp.modelParams;
simParams = inp.simParams;
let run = runSimulation(modelParams, simParams);
let plot = $.plot("#modelSim", [
{ // Data
data: run.var.Vs,
label: "membrane voltage (mV)",
yaxis: 1,
lines: {
lineWidth: 2 * $('#disp--mp').prop('checked')
}
}, {
data: run.var.Iinj,
label: "injected current (nA/cm^2)",
yaxis: 3,
lines: {
lineWidth: 2 * $('#disp--iinj').prop('checked')
}
}, {
data: run.var.IK,
label: "potassium current (nA/cm^2)",
yaxis: 3,
lines: {
lineWidth: 2 * $('#disp--ik').prop('checked')
}
}, {
data: run.var.GK,
label: "potassium conductance (mS/cm^2)",
yaxis: 2,
lines: {
lineWidth: 2 * $('#disp--gk').prop('checked')
}
}, {
data: run.var.INa,
label: "sodium current (nA/cm^2)",
yaxis: 3,
lines: {
lineWidth: 2 * $('#disp--ina').prop('checked')
}
}, {
data: run.var.GNa,
label: "sodium conductance (mS/cm^2)",
yaxis: 2,
lines: {
lineWidth: 2 * $('#disp--gna').prop('checked')
}
}, {
data: run.var.EKdata,
label: "EK (mV)",
yaxis: 1,
lines: {
lineWidth: 1 * $('#disp--ek').prop('checked')
}
}, {
data: run.var.ENadata,
label: "ENa (mV)",
yaxis: 1,
lines: {
lineWidth: 1 * $('#disp--ena').prop('checked')
}
}
], { // Options
series: {
lines: { show: true },
points: { show: false }
},
xaxis: {
autoScale: "none",
},
yaxis: {
autoScale: "none",
},
xaxes: [{
min: 0, max: run.sim.dur
}],
yaxes: [
{ min:-100, max:100, ticks:10,
axisLabel:"Membrane potential (mV)"},
{ min:-40, max:40, position: 'right', axisLabel: "Conductance (mS/cm^2)" },
{ min:-1000, max:1000, position: 'right', axisLabel: "Current (nA/cm^2)" }
],
grid: {
hoverable: true,
clickable: true,
autoHighlight: false
},
legend: {
show: false,
backgroundColor: "#ffffff",
backgroundOpacity: 0.5,
noColumns: 1,
position: "ne",
container: legendContainer
},
crosshair: {
mode: "x"
},
colors: ['#000000', '#F44336', '#1565C0', '#64B5F6', '#2E7D32', '#81C784', '#1565C0', '#2E7D32']
});
plot.draw();
axes = plot.getAxes();
updateMonitor(run.var, Math.ceil(run.var.Vs.length)/2);
$("#modelSim").bind("plothover", function (event, pos, item) {
if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
return;
}
if (chLocked == false) {
monitorPos = Math.round(pos.x / run.sim.dt) + Math.ceil(run.var.Vs.length/2);
}
updateMonitor(run.var, monitorPos);
}).bind("plotclick", function (event, pos, item) {
monitorPosNew = Math.round(pos.x / run.sim.dt) + Math.ceil(run.var.Vs.length/2);
if (monitorPosNew == monitorPos && chLocked == true) {
plot.unlockCrosshair();
chLocked = false;
} else {
plot.lockCrosshair(pos);
chLocked = true;
}
monitorPos = monitorPosNew;
updateMonitor(run.var, monitorPos);
});
}