const canvas = document.getElementById("simplePlot"); const ctx = canvas.getContext("2d"); const zSlider = document.getElementById("z"); const zValue = document.getElementById("zValue"); const showHalf = document.getElementById("showHalf"); const showFormula = document.getElementById("showFormula"); const resetBtn = document.getElementById("resetSimple"); const animateBtn = document.getElementById("animateSimple"); const stats = document.getElementById("simpleStats"); const MARGIN = { left: 72, right: 24, top: 24, bottom: 58 }; const Z_MIN = -10; const Z_MAX = 10; let timer = null; let direction = 1; function sigmoid(z) { return 1 / (1 + Math.exp(-z)); } function zToX(z) { const width = canvas.width - MARGIN.left - MARGIN.right; return MARGIN.left + ((z - Z_MIN) / (Z_MAX - Z_MIN)) * width; } function yToCanvas(y) { const height = canvas.height - MARGIN.top - MARGIN.bottom; return canvas.height - MARGIN.bottom - y * height; } function drawAxes() { const left = MARGIN.left; const right = canvas.width - MARGIN.right; const top = MARGIN.top; const bottom = canvas.height - MARGIN.bottom; ctx.strokeStyle = "#c9d9df"; ctx.lineWidth = 1; for (let z = -10; z <= 10; z += 2) { const x = zToX(z); ctx.beginPath(); ctx.moveTo(x, top); ctx.lineTo(x, bottom); ctx.stroke(); } for (let y = 0; y <= 1.001; y += 0.1) { const py = yToCanvas(y); ctx.beginPath(); ctx.moveTo(left, py); ctx.lineTo(right, py); ctx.stroke(); } ctx.strokeStyle = "#14343f"; ctx.lineWidth = 1.4; ctx.beginPath(); ctx.moveTo(left, yToCanvas(0)); ctx.lineTo(right, yToCanvas(0)); ctx.stroke(); ctx.beginPath(); ctx.moveTo(zToX(0), top); ctx.lineTo(zToX(0), bottom); ctx.stroke(); ctx.fillStyle = "#14343f"; ctx.font = "13px 'Avenir Next', sans-serif"; for (let z = -10; z <= 10; z += 2) { ctx.fillText(String(z), zToX(z) - 8, yToCanvas(0) + 19); } for (let y = 0; y <= 1.001; y += 0.2) { ctx.fillText(y.toFixed(1), left - 40, yToCanvas(y) + 4); } ctx.fillText("z", right - 12, yToCanvas(0) + 38); ctx.fillText("y", zToX(0) + 12, top + 12); } function drawSigmoid() { ctx.strokeStyle = "#0a8f7b"; ctx.lineWidth = 3; ctx.beginPath(); const steps = 600; for (let i = 0; i <= steps; i += 1) { const z = Z_MIN + (i / steps) * (Z_MAX - Z_MIN); const y = sigmoid(z); const x = zToX(z); const py = yToCanvas(y); if (i === 0) ctx.moveTo(x, py); else ctx.lineTo(x, py); } ctx.stroke(); } function drawHalfLine() { ctx.strokeStyle = "#d8534f"; ctx.lineWidth = 1.3; ctx.setLineDash([7, 5]); ctx.beginPath(); ctx.moveTo(MARGIN.left, yToCanvas(0.5)); ctx.lineTo(canvas.width - MARGIN.right, yToCanvas(0.5)); ctx.stroke(); ctx.setLineDash([]); } function drawProbe(z) { const y = sigmoid(z); const px = zToX(z); const py = yToCanvas(y); ctx.strokeStyle = "#14343f"; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(px, yToCanvas(0)); ctx.lineTo(px, py); ctx.stroke(); ctx.setLineDash([]); ctx.fillStyle = "#14343f"; ctx.beginPath(); ctx.arc(px, py, 6, 0, Math.PI * 2); ctx.fill(); if (showFormula.checked) { ctx.font = "13px 'Avenir Next', sans-serif"; ctx.fillText(`z=${z.toFixed(2)}, y=${y.toFixed(4)}`, px + 8, py - 8); } } function renderStats(z) { const y = sigmoid(z); const inverse = Math.log(y / (1 - y)); stats.innerHTML = ` Function: y = 1 / (1 + np.exp(-z)) Current z: ${z.toFixed(2)} Output y: ${y.toFixed(4)} Cross-check logit(y): ${inverse.toFixed(4)} `; } function render() { const z = Number(zSlider.value); zValue.textContent = z.toFixed(2); ctx.clearRect(0, 0, canvas.width, canvas.height); drawAxes(); drawSigmoid(); if (showHalf.checked) drawHalfLine(); drawProbe(z); renderStats(z); } function stopAnimation() { if (timer) { clearInterval(timer); timer = null; } animateBtn.textContent = "Animate z"; } function startAnimation() { if (timer) return; animateBtn.textContent = "Stop Animation"; timer = setInterval(() => { let next = Number(zSlider.value) + direction * 0.18; if (next >= Z_MAX) { next = Z_MAX; direction = -1; } else if (next <= Z_MIN) { next = Z_MIN; direction = 1; } zSlider.value = next; render(); }, 30); } function reset() { zSlider.value = 0; showHalf.checked = true; showFormula.checked = true; stopAnimation(); render(); } zSlider.addEventListener("input", render); showHalf.addEventListener("input", render); showFormula.addEventListener("input", render); resetBtn.addEventListener("click", reset); animateBtn.addEventListener("click", () => { if (timer) stopAnimation(); else startAnimation(); }); reset();