this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add y-axis tick labels and grid lines to plot panels

Replaces the single hardcoded "0" label with auto-scaled tick marks,
right-aligned voltage labels, and horizontal grid lines at each tick.
The zero line remains slightly more prominent. Both panels share the
same voltage scale (vLo/vHi), with nice round step sizes (1-2-5 series).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+62 -14
+62 -14
render.js
··· 61 61 ctx.fillText(text, x, y); 62 62 } 63 63 64 + // Return a "nice" tick step for a given range and target number of ticks. 65 + function niceStep(range, targetCount) { 66 + const raw = range / targetCount; 67 + const mag = Math.pow(10, Math.floor(Math.log10(raw))); 68 + const norm = raw / mag; 69 + const nice = norm <= 1 ? 1 : norm <= 2 ? 2 : norm <= 5 ? 5 : 10; 70 + return nice * mag; 71 + } 72 + 64 73 // ---- circuit component primitives ---- 65 74 function drawResistor(ctx, x0, y0, x1, y1, zigZagCount, amp, theme) { 66 75 ctx.strokeStyle = theme.ink; ··· 340 349 ctx.font = "12px ui-sans-serif, system-ui"; 341 350 ctx.fillText("Voltage along the T-line", 12, 14); 342 351 343 - // Draw axes/border for one panel 344 - function drawPanelFrame(top, bot, yOfV, labelText) { 352 + // Draw axes/border for one panel, with y-axis tick marks and labels. 353 + function drawPanelFrame(top, bot, yOfV, labelText, vLo, vHi) { 354 + const step = niceStep(vHi - vLo, 5); 355 + const decimals = step >= 1 ? 0 : step >= 0.1 ? 1 : 2; 356 + // Index of first tick at or above vLo 357 + const iTick0 = Math.ceil(vLo / step); 358 + 359 + // Horizontal grid lines at each tick (drawn first, behind everything) 345 360 ctx.strokeStyle = theme.grid; 346 - ctx.lineWidth = 1; 361 + ctx.lineWidth = 1; 362 + for (let i = iTick0; ; i++) { 363 + const v = i * step; 364 + if (v > vHi + step * 0.01) break; 365 + const yv = yOfV(v); 366 + if (yv < top - 1 || yv > bot + 1) continue; 367 + line(ctx, xPlot0, yv, xPlot1, yv); 368 + } 369 + 370 + // Border (on top of grid lines) 371 + ctx.strokeStyle = theme.grid; 372 + ctx.lineWidth = 1; 347 373 line(ctx, xPlot0, top, xPlot1, top); 348 - line(ctx, xPlot0, bot, xPlot1, bot); 374 + line(ctx, xPlot0, bot, xPlot1, bot); 349 375 line(ctx, xPlot0, top, xPlot0, bot); 350 376 line(ctx, xPlot1, top, xPlot1, bot); 351 - ctx.strokeStyle = theme.muted; 352 - ctx.lineWidth = 1.2; 353 - line(ctx, xPlot0, yOfV(0), xPlot1, yOfV(0)); 377 + 378 + // Zero line — slightly more visible than other grid lines 379 + const yZero = yOfV(0); 380 + if (yZero >= top - 1 && yZero <= bot + 1) { 381 + ctx.strokeStyle = theme.muted; 382 + ctx.lineWidth = 1.2; 383 + line(ctx, xPlot0, yZero, xPlot1, yZero); 384 + } 385 + 386 + // Tick marks and labels on the left y-axis 387 + ctx.textAlign = "right"; 388 + ctx.font = "11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"; 389 + for (let i = iTick0; ; i++) { 390 + const v = i * step; 391 + if (v > vHi + step * 0.01) break; 392 + const yv = yOfV(v); 393 + if (yv < top - 1 || yv > bot + 1) continue; 394 + ctx.strokeStyle = theme.muted; 395 + ctx.lineWidth = 1; 396 + line(ctx, xPlot0 - 4, yv, xPlot0, yv); 397 + ctx.fillStyle = theme.muted; 398 + ctx.fillText(v.toFixed(decimals), xPlot0 - 7, yv + 4); 399 + } 400 + ctx.textAlign = "left"; 401 + 402 + // Panel title and axis labels 354 403 ctx.fillStyle = theme.muted; 355 - ctx.font = "12px ui-sans-serif, system-ui"; 356 - ctx.fillText(labelText, xPlot0 + 8, top + 14); 357 - ctx.fillText("0", xPlot0 - 14, yOfV(0) + 4); 358 - ctx.fillText("z", xPlot1 + 6, (top + bot) / 2 + 4); 404 + ctx.font = "12px ui-sans-serif, system-ui"; 405 + ctx.fillText(labelText, xPlot0 + 8, top + 14); 406 + ctx.fillText("z", xPlot1 + 6, (top + bot) / 2 + 4); 359 407 360 408 // Segment boundary tick marks (for multi-segment lines) 361 409 const N = segments.length; 362 410 if (N > 1) { 363 411 ctx.strokeStyle = theme.grid; 364 - ctx.lineWidth = 1; 412 + ctx.lineWidth = 1; 365 413 ctx.setLineDash([3, 4]); 366 414 for (let i = 1; i < N; i++) { 367 415 const xb = xOfZ(i / N); ··· 389 437 const top0 = PLOT_PAD_T; 390 438 const bot0 = top0 + panelH; 391 439 const y0 = (V) => top0 + 4 + (vHi - V) / (vHi - vLo) * (panelH - 8); 392 - drawPanelFrame(top0, bot0, y0, "Sum (all waves)"); 440 + drawPanelFrame(top0, bot0, y0, "Sum (all waves)", vLo, vHi); 393 441 if (smooth) { 394 442 drawSampledWave(ctx, xOfZ, y0, (z) => totalVoltageAt(z, launched, riseTimeTau), theme.ok, 2.4); 395 443 } else { ··· 401 449 const top1 = PLOT_PAD_T + panelH + PLOT_PANEL_GAP; 402 450 const bot1 = top1 + panelH; 403 451 const y1 = (V) => top1 + 4 + (vHi - V) / (vHi - vLo) * (panelH - 8); 404 - drawPanelFrame(top1, bot1, y1, "Components (all waves)"); 452 + drawPanelFrame(top1, bot1, y1, "Components (all waves)", vLo, vHi); 405 453 406 454 const waveStyles = [ 407 455 { color: theme.accent, dash: [] },