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("Membrane potential (Vm):" + parseFloat(rv.Vs[j][1]).toFixed(2) + " mV"); $(".monitor--iinj").html("Injected current (Iinj):" + parseFloat(rv.Iinj[j][1]).toFixed(2) + " nA/cm^2"); $(".monitor--ik").html("Potassium current (IK):" + parseFloat(rv.IK[j][1]).toFixed(2) + " nA/cm^2"); $(".monitor--gk").html("Potassium conductance (GK):" + parseFloat(rv.GK[j][1]).toFixed(2) + " mS/cm^2"); $(".monitor--ina").html("Sodium current (INa):" + parseFloat(rv.INa[j][1]).toFixed(2) + " nA/cm^2"); $(".monitor--gna").html("Sodium conductance (GNa):" + parseFloat(rv.GNa[j][1]).toFixed(2) + " 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(''); 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('simulation produced NaN, probably numerical instability or bug'); } 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); }); }