feat(plot): the readout marker snaps to the sample it quotes, and the value mode is a choice
The mark was placed at the *pointer's* x with the quoted sample's y, so with the nearest-sample readout it sat off the datum horizontally -- and further off the deeper the zoom. `CrosshairRow` now carries the data `x` the value belongs to and the marker is placed from it, so in `NearestSample` the mark lands on the sample the panel is quoting. `Style::crosshair_marker_px` (9) and `crosshair_marker_width` (1.5) replace the renderer's hard-coded 3 px arm and 1 px stroke: a snapped mark lands among a trace's own sample dots (`marker_px`, 3) and at that size would read as one more sample rather than as the answer to the question the hover asks. `CrosshairValue` makes the previous behaviour reachable rather than deleted. The two modes answer different questions -- `NearestSample` "which datum is under the pointer", `Interpolated` "what is the signal at this instant" -- and only the caller knows which it is asking, so the plot offers both with the sample as the default. `crosshair_interpolate()` is restored for it. `Plot::crosshair_point()` is the lookup with both coordinates, public because a host that draws instruments of its own needs the same snap and a second rule for it would drift from this one; `crosshair_value()` delegates to it.
This commit is contained in:
1 parent
c028f48fa0
commit
c960decaea
9 files changed
+372
-78
No files matched your search
@@ -179,18 +179,23 @@ these two is *how many* samples answer the question:
|
||||
and no query allocates. `Measure::evaluate` and `Plot::region_stats` are built on these,
|
||||
never on the `*_buckets` forms, which would silently report wrong extremes for an
|
||||
unaligned interval.
|
||||
- **The crosshair readout is a single sample — the nearest one.** `crosshair_span(series,
|
||||
x)` returns the two samples `x` lies between (`begin`, `end`, `t`), and the readout takes
|
||||
whichever of the two is nearer: `t <= 0.5` the earlier, otherwise the later. It answers
|
||||
"which datum is under the pointer", not "what is the signal *at* this instant" — a
|
||||
linearly interpolated y is a number the series does not contain, and on a source whose
|
||||
samples are already averaged or decimated at ingest it is a claim about a signal nobody
|
||||
measured. The lookup is O(1) and allocation-free, and it reads level 0, so the value is a
|
||||
stored sample and never an LOD bucket's aggregate; `Plot::crosshair_value(trace, x,
|
||||
out_y)` exposes the same number to a host, and `CrosshairRow::marker` is placed from that
|
||||
same y, so the dot and the number cannot disagree. A row whose trace has no sample at the
|
||||
crosshair's x shows `kCrosshairNoValue` (an em dash, `"—"`), and an x outside the series
|
||||
window yields no value rather than clamping to the first or last sample.
|
||||
- **The crosshair readout is a single sample — the nearest one — by default.**
|
||||
`crosshair_span(series, x)` returns the two samples `x` lies between (`begin`, `end`, `t`),
|
||||
and `CrosshairValue::NearestSample` takes whichever of the two is nearer: `t <= 0.5` the
|
||||
earlier, otherwise the later. It answers "which datum is under the pointer", not "what is
|
||||
the signal *at* this instant" — a linearly interpolated y is a number the series does not
|
||||
contain, and on a source whose samples are already averaged or decimated at ingest it is a
|
||||
claim about a signal nobody measured. That is why it is the default; a host whose x is a
|
||||
quantity it is willing to interpolate states `CrosshairValue::Interpolated` and gets
|
||||
`crosshair_interpolate(y_begin, y_end, t)` instead, at the crosshair's own x. The lookup is
|
||||
O(1) and allocation-free either way, and it reads level 0, so the value is a stored sample
|
||||
(or a blend of two) and never an LOD bucket's aggregate. `Plot::crosshair_point(trace, x,
|
||||
out_x, out_y)` exposes **both** coordinates to a host and `crosshair_value()` the y alone;
|
||||
`CrosshairRow::x`/`y` and its `marker` are placed from that same pair, so in
|
||||
`NearestSample` the mark snaps onto the quoted sample and the dot and the number cannot
|
||||
disagree. A row whose trace has no sample at the crosshair's x shows `kCrosshairNoValue`
|
||||
(an em dash, `"—"`), and an x outside the series window yields no value rather than
|
||||
clamping to the first or last sample.
|
||||
|
||||
`Measurement` (from `Plot::measurement()`) documents `integral` as a **left-Riemann sum**,
|
||||
`sum(begin, end) * dx` — the samples taken as the value at the left edge of each step. That
|
||||
|
||||
Reference in new issue
Block a user