fix: a cursor readout quotes the nearest stored sample, not a value between two
The crosshair and the cursors pane's mix values are one library call, and it interpolated linearly between the two samples bracketing the pointer. That is wrong twice over here. Ingest decimation is a boxcar average over `time_stride` samples, so a number computed between two stored samples is a second, undeclared estimate laid over the first that nothing on screen or in an export holds. And past the sample spacing the polyline is a line the renderer drew, so quoting a point on it reports the drawing rather than the data. The fix is upstream and unconditional (plotter c028f48): `crosshair_span()`'s `t` selects the nearer end instead of blending, with a tie reading the earlier sample. `CrosshairRow::marker` is placed from the same y, so the dot follows the quoted sample. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec93a9831d
commit
d03c9fb443
5 files changed
+37
-7
No files matched your search
@@ -749,6 +749,19 @@ far the largest file.
|
||||
again until the next latch. `PlotArea::create()` is handed `App::placeholder_plot`,
|
||||
never a row: the area keeps a reference to the plot it was created with, and a rebuild
|
||||
destroys every row.
|
||||
- **A readout quotes a stored sample, never a value between two of them.** The hover
|
||||
crosshair and the cursors pane's `mix` values are one call, `Plot::crosshair_value()`, and it
|
||||
reports the **nearer** of the two samples bracketing the x (a tie reads the earlier). Upstream
|
||||
interpolated linearly, and that is wrong twice over here: ingest decimation is a *boxcar
|
||||
average* over `time_stride` samples, so a number between two stored samples is a second
|
||||
estimate laid over the first that nothing on screen or in an export holds; and past the sample
|
||||
spacing the polyline is a line the renderer drew, so a point on it reports the drawing. The
|
||||
change is in `deps/plotter` and unconditional — no setting, because the library had no business
|
||||
offering a readout the data does not contain. `CrosshairRow::marker` is placed from the same y,
|
||||
so the dot lands on the quoted sample and **sits beside the polyline at a deep zoom, by
|
||||
design**: one y feeding both is what keeps the mark and the number from disagreeing. The guards
|
||||
are the library's `crosshair_value_is_the_nearest_sample` and `[cursors-probe]`'s `first mark at
|
||||
x=.. y=..` agreeing with the pane's first value.
|
||||
- **The cursors are read back from the plots only while a gesture is on that row.** `Cursor::x`
|
||||
is the *free* pointer position while a drag runs and the snapped value otherwise -- which is
|
||||
what the app itself wrote a frame earlier, or the snap of it. A tick that reads a row on
|
||||
|
||||
@@ -1139,10 +1139,24 @@ is what made a dragged cursor spring to where it had been. A ruler is per row: e
|
||||
two different values, and the pane names the row.
|
||||
|
||||
`mix` needs no rulers: it reports the value the trace actually has at each marker, read
|
||||
through the library's `Plot::crosshair_value()` — the two bracketing samples,
|
||||
interpolated — so it is the answer to "what is this signal at these two times" rather
|
||||
than "drag a ruler until it crosses the trace". A readout is `--` when the trace has
|
||||
nothing at that time and `partial` when only one of the two markers is over samples.
|
||||
through the library's `Plot::crosshair_value()` — **the nearer of the two bracketing
|
||||
samples**, never a number between them — so it is the answer to "which datum of this
|
||||
signal is at these two times" rather than "drag a ruler until it crosses the trace". A
|
||||
readout is `--` when the trace has nothing at that time and `partial` when only one of
|
||||
the two markers is over samples.
|
||||
|
||||
The value used to be a linear interpolation between the brackets, which is upstream's
|
||||
old convention and was wrong for this app in two ways at once. The samples here are
|
||||
**already averaged** — ingest decimation is a boxcar over `time_stride` samples (see
|
||||
"Decimation at ingest is an average") — so a number computed *between* two of them is a
|
||||
second, undeclared estimate laid over the first, and nothing on screen or in an export
|
||||
holds it. And at a zoom past the sample spacing it is visibly not the datum the pointer
|
||||
is over: the polyline between two samples is a straight line the renderer drew, and
|
||||
quoting a point on it reports the drawing rather than the data. A tie reads the earlier
|
||||
sample. The fix is upstream and unconditional (`deps/plotter`, `crosshair_span()`'s `t`
|
||||
is now a selector rather than a blend factor), so the library's own hover crosshair
|
||||
changed with the pane — which is the point, since both are the same lookup and a user
|
||||
comparing them must not see two numbers.
|
||||
|
||||
The readouts are a **table**: a header naming the columns, then one row per instrument —
|
||||
`| who | A | B | delta |`. The first row is the two times and their difference, and the
|
||||
|
||||
Vendored
+1
-1
Submodule deps/plotter updated: 26a7c82a80...c028f48fa0.
+2
-2
@@ -73,8 +73,8 @@ class ChannelBuffer final : public plot::data::Series {
|
||||
/// datagram leaves a gap, so index times must come from the time ring.
|
||||
f64 x_at(u64 i) const override;
|
||||
/// Bracketing samples by binary search over the time ring, which is monotonic,
|
||||
/// so a crosshair value is interpolated between the two samples actually either
|
||||
/// side of the pointer -- a grid walk would land on the wrong pair after a gap.
|
||||
/// so a crosshair reads whichever of the two samples actually either side of the
|
||||
/// pointer is nearer -- a grid walk would land on the wrong pair after a gap.
|
||||
bool neighbours(f64 x, u64& i0, u64& i1) const override;
|
||||
u64 snap_index(f64 x) const override;
|
||||
plot::Range x_bounds() const override;
|
||||
|
||||
@@ -19,6 +19,9 @@ owns a <b>Reset</b> button. Drag a marker to move it. The mode dropdown offers f
|
||||
• <tt>mix</tt> is the reading mode: one line per <i>bound</i> trace, with the trace's own values
|
||||
at each marker and their difference. A slot with a name but no channel behind it is not a
|
||||
trace and gets no line.
|
||||
• A value is the <i>nearest stored sample's</i>, never a number between two samples. Zoom in past
|
||||
the sample spacing and the line between two samples is something the plot drew, not data —
|
||||
so a reading off it would be the drawing. The hover crosshair reads the same way.
|
||||
• A marker past the newest sample reads <tt>--</tt>. With only one marker over data the row
|
||||
reads <tt>partial</tt>: a difference between a number and nothing is not a number.
|
||||
• In <tt>mix</tt> each marker also places a crossing annotation per trace, which is what lets
|
||||
|
||||
Reference in new issue
Block a user