234 lines
8.4 KiB
JavaScript
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);
|
|
});
|
|
} |