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
+34
-4
@@ -106,6 +106,7 @@ one per plot.
|
||||
- `set_crosshair_readout(bool)` switches the panel off — or back on — without moving the
|
||||
crosshair. `set_crosshair_style(const CrosshairStyle&)` overrides this plot's stroke and
|
||||
dash.
|
||||
- `set_crosshair_value_mode(CrosshairValue)` chooses what a readout *is* — see below.
|
||||
|
||||
In a `Figure`, the crosshair is applied to **every row** (one shared `x`): each row's readout
|
||||
lists its own traces at that instant. `Figure::set_crosshair(x, active)` is vertical-only
|
||||
@@ -113,10 +114,39 @@ across the figure, and `Figure::set_crosshair(x, hovered_row, y_in_row, active)`
|
||||
horizontal line **only in the row the pointer is in** — a figure's rows share x and not y, so
|
||||
a figure-wide horizontal line would be a value that means something different in each row.
|
||||
|
||||
Readout values are the **nearest sample's**, neither interpolated between two samples nor an
|
||||
interval statistic (see [data-and-performance.md](data-and-performance.md)); a row whose trace
|
||||
has no sample at the crosshair's x is listed with the em dash `"—"` and no marker, rather than a
|
||||
fabricated value.
|
||||
### What a readout is: the two value modes
|
||||
|
||||
`Crosshair::value_mode` (`set_crosshair_value_mode()`) answers two different questions, and
|
||||
which one a caller is asking decides the mode:
|
||||
|
||||
| mode | the value | the marker | answers |
|
||||
|---|---|---|---|
|
||||
| `CrosshairValue::NearestSample` (default) | the nearer bracketing sample's y (a tie reads the earlier) | **snapped to that sample's own x** | *which datum is under the pointer?* |
|
||||
| `CrosshairValue::Interpolated` | a linear blend of the two bracketing samples | at the crosshair's own x | *what is the signal at this instant?* |
|
||||
|
||||
The default is the sample, because a readout that quotes a number the data does not contain is
|
||||
a second estimate laid over the first — and once the view is zoomed past the sample spacing the
|
||||
polyline between two samples is a line the *renderer* drew, so reading a value off it reports
|
||||
the drawing rather than the signal. A host whose x is a continuous quantity it is willing to
|
||||
interpolate (a smooth analytic curve, a resampled series) states `Interpolated` and gets the
|
||||
blend, exactly at the pointer. Either way the value is a **stored** sample or a blend of two of
|
||||
them, never an LOD bucket's aggregate (see
|
||||
[data-and-performance.md](data-and-performance.md)).
|
||||
|
||||
`CrosshairRow` carries both halves: `x` and `y` are the data coordinate the value belongs to —
|
||||
the quoted sample's own in `NearestSample`, the crosshair's x in `Interpolated` — and `marker`
|
||||
is that point in screen space. `Plot::crosshair_point(trace, x, out_x, out_y)` is the same
|
||||
lookup for a host that draws instruments of its own; `Plot::crosshair_value()` is it with the x
|
||||
discarded.
|
||||
|
||||
The mark is drawn `Style::crosshair_marker_px` across (9 logical px) at
|
||||
`crosshair_marker_width`, both deliberately **larger than a trace's own sample dots**
|
||||
(`marker_px`, 3): in `NearestSample` the mark lands *among* those dots, and at the same size it
|
||||
would read as one more sample instead of as the one the panel is quoting. `0` across draws no
|
||||
mark.
|
||||
|
||||
A row whose trace has no sample at the crosshair's x is listed with the em dash `"—"` and no
|
||||
marker, rather than a fabricated value.
|
||||
|
||||
## The gesture table (exactly as implemented)
|
||||
|
||||
|
||||
Reference in new issue
Block a user