Improved cursors functionality
This commit is contained in:
@@ -225,6 +225,7 @@ function setActiveSig(plotId, key) {
|
|||||||
b.classList.toggle('sig-badge-active', key != null && b.dataset.key === key));
|
b.classList.toggle('sig-badge-active', key != null && b.dataset.key === key));
|
||||||
const p = plots.find(q => q.id === plotId);
|
const p = plots.find(q => q.id === plotId);
|
||||||
if (p && p.uplot) p.uplot.redraw(false);
|
if (p && p.uplot) p.uplot.redraw(false);
|
||||||
|
updatePlotCursorReadouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark plots containing key dirty and refresh badge vscale text.
|
// Mark plots containing key dirty and refresh badge vscale text.
|
||||||
@@ -1287,8 +1288,10 @@ function drawCursorLines(u, p) {
|
|||||||
ctx.fillText(label, px + 14, bbox.top + 2);
|
ctx.fillText(label, px + 14, bbox.top + 2);
|
||||||
// Per-trace: diamond at crossing point + value label
|
// Per-trace: diamond at crossing point + value label
|
||||||
if (p) {
|
if (p) {
|
||||||
const DSZ = 5; // diamond half-size in px
|
const activeKey = plotActiveSignal[p.id];
|
||||||
p.traces.forEach((key, idx) => {
|
p.traces.forEach((key, idx) => {
|
||||||
|
const isActive = key === activeKey || (!activeKey && p.traces.length === 1);
|
||||||
|
const DSZ = isActive ? 9 : 5; // diamond half-size in px
|
||||||
const vNorm = interpAtTime(u, idx + 1, val);
|
const vNorm = interpAtTime(u, idx + 1, val);
|
||||||
if (vNorm === null) return;
|
if (vNorm === null) return;
|
||||||
const cy = u.valToPos(vNorm, 'y', true);
|
const cy = u.valToPos(vNorm, 'y', true);
|
||||||
@@ -2019,10 +2022,56 @@ document.getElementById('btn-cursor').addEventListener('click', () => {
|
|||||||
cursorsDirty = true;
|
cursorsDirty = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Format a signal value for the per-plot cursor readout.
|
||||||
|
function fmtVal(v) {
|
||||||
|
if (v === null || v === undefined) return '—';
|
||||||
|
return Math.abs(v) >= 10000 ? v.toExponential(2) : parseFloat(v.toPrecision(4)).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate the real-unit value of the active/sole signal in plot p at time t.
|
||||||
|
function getValueAtCursor(p, t) {
|
||||||
|
if (!p.uplot || t === null) return null;
|
||||||
|
const key = plotActiveSignal[p.id] || (p.traces.length === 1 ? p.traces[0] : null);
|
||||||
|
if (!key) return null;
|
||||||
|
const idx = p.traces.indexOf(key);
|
||||||
|
if (idx < 0) return null;
|
||||||
|
const vNorm = interpAtTime(p.uplot, idx + 1, t);
|
||||||
|
if (vNorm === null) return null;
|
||||||
|
// Un-normalize: y_norm = (y_raw - offset) / divValue + screenPos
|
||||||
|
const vs = sigVScale[p.id + ':' + key];
|
||||||
|
if (!vs) return vNorm;
|
||||||
|
const dv = vs._resolvedDiv != null ? vs._resolvedDiv : (vs.divValue || 1);
|
||||||
|
const ofs = vs._resolvedOffset != null ? vs._resolvedOffset : (vs.offset || 0);
|
||||||
|
const sp = vs.screenPos || 0;
|
||||||
|
return (vNorm - sp) * dv + ofs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update per-plot cursor value readouts (A, B, ΔV) for all plots.
|
||||||
|
function updatePlotCursorReadouts() {
|
||||||
|
plots.forEach(p => {
|
||||||
|
const el = document.getElementById('pcur-' + p.id);
|
||||||
|
if (!el) return;
|
||||||
|
// Show only when cursors are on and the plot has an active or sole signal.
|
||||||
|
const key = plotActiveSignal[p.id] || (p.traces.length === 1 ? p.traces[0] : null);
|
||||||
|
if (cursors.mode !== 'on' || !key || !p.uplot) {
|
||||||
|
el.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const vA = getValueAtCursor(p, cursors.tA);
|
||||||
|
const vB = getValueAtCursor(p, cursors.tB);
|
||||||
|
const dv = (vA !== null && vB !== null) ? vB - vA : null;
|
||||||
|
document.getElementById('pcur-a-' + p.id).textContent = 'A: ' + fmtVal(vA);
|
||||||
|
document.getElementById('pcur-b-' + p.id).textContent = 'B: ' + fmtVal(vB);
|
||||||
|
document.getElementById('pcur-dv-' + p.id).textContent = 'ΔV: ' + fmtVal(dv);
|
||||||
|
el.style.display = 'flex';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateCursorReadout() {
|
function updateCursorReadout() {
|
||||||
const ro = document.getElementById('cursor-readout');
|
const ro = document.getElementById('cursor-readout');
|
||||||
const active = cursors.mode === 'on';
|
const active = cursors.mode === 'on';
|
||||||
ro.classList.toggle('visible', active);
|
ro.classList.toggle('visible', active);
|
||||||
|
updatePlotCursorReadouts();
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
|
|
||||||
// Use the current visible x-range to pick the display unit.
|
// Use the current visible x-range to pick the display unit.
|
||||||
@@ -2605,6 +2654,13 @@ function addPlot() {
|
|||||||
<div class="plot-card-header">
|
<div class="plot-card-header">
|
||||||
<span class="plot-title" id="ptitle-${id}" title="Click to configure">Plot ${id}</span>
|
<span class="plot-title" id="ptitle-${id}" title="Click to configure">Plot ${id}</span>
|
||||||
<div class="sig-badges" id="badges-${id}"></div>
|
<div class="sig-badges" id="badges-${id}"></div>
|
||||||
|
<div class="plot-cursor-ro" id="pcur-${id}" style="display:none">
|
||||||
|
<span class="pcur-a" id="pcur-a-${id}">A: —</span>
|
||||||
|
<span class="pcur-sep">│</span>
|
||||||
|
<span class="pcur-b" id="pcur-b-${id}">B: —</span>
|
||||||
|
<span class="pcur-sep">│</span>
|
||||||
|
<span class="pcur-dv" id="pcur-dv-${id}">ΔV: —</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="plot-cfg-bar" id="pcfg-${id}" style="display:none">
|
<div class="plot-cfg-bar" id="pcfg-${id}" style="display:none">
|
||||||
<div class="vstb-header">
|
<div class="vstb-header">
|
||||||
@@ -2657,6 +2713,7 @@ function addTraceTo(plotId, signalKey) {
|
|||||||
// Recreate uPlot with new series list
|
// Recreate uPlot with new series list
|
||||||
createUPlot(p);
|
createUPlot(p);
|
||||||
p.needsRedraw = true;
|
p.needsRedraw = true;
|
||||||
|
updatePlotCursorReadouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeTraceFrom(plotId, signalKey) {
|
function removeTraceFrom(plotId, signalKey) {
|
||||||
@@ -2672,6 +2729,7 @@ function removeTraceFrom(plotId, signalKey) {
|
|||||||
createUPlot(p);
|
createUPlot(p);
|
||||||
p.needsRedraw = true;
|
p.needsRedraw = true;
|
||||||
if (!p.traces.length) document.querySelector('#hint-' + plotId).style.display = '';
|
if (!p.traces.length) document.querySelector('#hint-' + plotId).style.display = '';
|
||||||
|
updatePlotCursorReadouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addBadge(plotId, key) {
|
function addBadge(plotId, key) {
|
||||||
@@ -2843,6 +2901,9 @@ function renderDirtyPlots() {
|
|||||||
zoomGuard = false;
|
zoomGuard = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Keep per-plot cursor value readouts in sync with live data.
|
||||||
|
if (cursors.mode === 'on') updatePlotCursorReadouts();
|
||||||
|
|
||||||
requestAnimationFrame(renderDirtyPlots);
|
requestAnimationFrame(renderDirtyPlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -364,6 +364,20 @@ input[type=range].trig-range::-webkit-slider-thumb {
|
|||||||
.vstb-close:hover { color:var(--red); }
|
.vstb-close:hover { color:var(--red); }
|
||||||
.plot-vscale-bar { display:none; }
|
.plot-vscale-bar { display:none; }
|
||||||
|
|
||||||
|
/* ── Per-plot cursor value readout (in plot card header) ────────── */
|
||||||
|
.plot-cursor-ro {
|
||||||
|
margin-left:auto; flex-shrink:0;
|
||||||
|
align-items:center; gap:5px;
|
||||||
|
font-size:10px; font-family:monospace;
|
||||||
|
background:var(--surface0); border:1px solid var(--surface1);
|
||||||
|
border-radius:4px; padding:1px 7px; white-space:nowrap;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
.pcur-a { color:var(--sky); }
|
||||||
|
.pcur-b { color:var(--yellow); }
|
||||||
|
.pcur-dv { color:var(--subtext1); }
|
||||||
|
.pcur-sep { color:var(--surface2); }
|
||||||
|
|
||||||
/* ── Badge vscale info & active state ───────────────────────────── */
|
/* ── Badge vscale info & active state ───────────────────────────── */
|
||||||
.vscale-info {
|
.vscale-info {
|
||||||
font-size:9px; color:var(--overlay0); font-family:monospace; margin-left:2px; white-space:nowrap;
|
font-size:9px; color:var(--overlay0); font-family:monospace; margin-left:2px; white-space:nowrap;
|
||||||
|
|||||||
Reference in New Issue
Block a user