fix(plot): a crosshair readout quotes a sample, never a value between two
The readout's y was a linear interpolation between the two samples adjacent to the pointer, which answers "what is the signal at this instant" -- a number the series does not contain. On a source whose samples are already averaged or decimated at ingest that is a value nothing measured, and at a zoom past the sample spacing it is visibly not the datum under the pointer. `crosshair_span()`'s `t` becomes a selector rather than a blend factor: the nearer end wins, a tie reading the earlier sample (the bias the span already takes for a degenerate pair). `t` is measured on the samples' own x through `neighbours()`/`x_at()`, so "nearer" is exact on a non-uniform clock too, and the lookup still reads level 0, so the value is a stored sample and never an LOD bucket's aggregate. `crosshair_interpolate()` had no other caller and is gone. `CrosshairRow::marker` is placed from the same y, so the dot follows the reported sample: past the sample spacing it sits beside the polyline rather than on it, deliberately -- one y feeding both keeps the mark and the number from ever disagreeing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
26a7c82a80
commit
c028f48fa0
10 files changed
+150
-105
No files matched your search
@@ -101,8 +101,8 @@ struct PlotLayout {
|
||||
/// where a pointer can grab it cannot drift apart.
|
||||
std::vector<RegionLayout> regions;
|
||||
/// The crosshair's geometry, when one is active: the two lines' stroke boxes, the
|
||||
/// readout panel and one row per visible trace with the interpolated value the pointer
|
||||
/// is over. Placed here rather than derived by the renderer, exactly as the legend and
|
||||
/// readout panel and one row per visible trace with the value of the sample nearest the
|
||||
/// pointer. Placed here rather than derived by the renderer, exactly as the legend and
|
||||
/// the regions are -- and, like them, entirely inside `axes`: the panel is flipped and
|
||||
/// clamped against the rect it was measured for, so nothing of the readout can be drawn
|
||||
/// over a label strip or into a figure's neighbouring row.
|
||||
@@ -693,10 +693,13 @@ class Plot {
|
||||
/// coordinates it holds and the geometry `layout()` places from them cannot disagree.
|
||||
const Crosshair& crosshair() const noexcept { return crosshair_; }
|
||||
|
||||
/// The **interpolated** value of `trace`'s series at a data `x`: `crosshair_span()`
|
||||
/// between the two adjacent samples, then `crosshair_interpolate()` -- the same lookup
|
||||
/// the readout's rows are built from, exposed for a host that wants the number for
|
||||
/// itself (a status line, a tooltip of its own).
|
||||
/// The value of `trace`'s **nearest sample** to a data `x`: `crosshair_span()` brackets
|
||||
/// `x`, and the nearer of the two samples is the answer (`t <= 0.5` takes the earlier) --
|
||||
/// the same lookup the readout's rows are built from, exposed for a host that wants the
|
||||
/// number for itself (a status line, a tooltip of its own).
|
||||
///
|
||||
/// Never a value interpolated between two samples: see `CrosshairSpan` for why a readout
|
||||
/// quotes the data rather than the signal it implies.
|
||||
///
|
||||
/// False, leaving `out_y` untouched, for an id that names no trace, a trace with no
|
||||
/// series, and an `x` the series has no sample at -- the cases `CrosshairSpan::valid`
|
||||
@@ -816,7 +819,7 @@ class Plot {
|
||||
/// never names a trace.
|
||||
void build_legend();
|
||||
/// Measures the crosshair into `layout_.crosshair`: the two lines' stroke boxes, one row
|
||||
/// per visible trace with its interpolated value and marker, and the readout panel --
|
||||
/// per visible trace with its nearest sample's value and marker, and the readout panel --
|
||||
/// placed beside the crosshair, flipped where it would cross an edge and clamped, so
|
||||
/// every rect it produces is inside `axes`. A plot with no visible trace still gets its
|
||||
/// lines: the rows are the readout's, not the crosshair's.
|
||||
|
||||
Reference in new issue
Block a user