fix(data): the gather origin is the caller's, so a deep zoom draws on the sample
CI / debug (tests + all adapters) (push) Canceled after 0s
CI / coverage (floor + measured table) (push) Canceled after 0s
CI / asan (AddressSanitizer) (push) Canceled after 0s

`Series::gather()` stages x as f32 relative to `x0()` -- the start of the
*retained window* -- so the value's ULP was a fraction of data the view need
not even hold: 1.9 us over a 20 s ring. A readout quotes `x_at()` in exact
f64, so at a 10 us view across 700 px that put more than a hundred pixels
between where a sample is drawn and the x reported for it.

The origin is now a parameter, placed before `out_xy` so every call site is a
type error rather than a silent change of behaviour. `Renderer::draw_traces`
passes the visible range's minimum, which makes the error a fixed 2^-24 of the
span on screen -- 4e-5 of a pixel over a 700 px axis -- at every zoom.

`stage_xy()` loses its second origin as a consequence: the gather origin and
the draw origin are now always the same value, so the linear case hands the
gathered buffer straight to `add_polyline_xy()` with no per-point copy.

`Plot::crosshair_point()` names the query itself, the one origin that cannot
lose precision for a caller that might.

Guarded by `a_gather_origin_at_the_view_keeps_a_deep_zoom_on_the_sample_it_quotes`,
which measures the error in pixels both ways: under 0.05 px with the view's
minimum, over 10 px with `x0()`.
This commit is contained in:
Martino Ferrari committed 2026-10-05 18:43:50 +02:00
1 parent 59c2e45af7
commit c2c8c596f3
15 files changed
+150 -95

No files matched your search

+5 -4
View File
@@ -445,11 +445,12 @@ class Renderer {
/// `origin_x` back in, in f64, before the single f32 narrowing (B1), so a
/// large-magnitude x -- an epoch axis -- never loses sub-pixel precision.
void stage_segment(f64 origin_x, f64 x0, f64 y0, f64 x1, f64 y1, bool log_x, bool log_y);
/// Stages `n` interleaved `x,y` points of a gathered window (see
/// `Series::gather`): x **relative to the series' `x0()`**, y absolute. The
/// absolute data x is reconstructed in f64 (`series_x0 + rel`) and staged as
/// Stages `n` interleaved `x,y` points of a gathered window (see `Series::gather`): x
/// **relative to `origin_x`**, which the caller asked the gather for, y absolute. On linear
/// axes that is already what the batch wants and the buffer is handed over as it is; a log
/// axis reconstructs the absolute x in f64 per segment and stages
/// `to_space(x_abs) - to_space(origin_x)` -- O(visible pixels), not O(epoch).
void stage_xy(const f32* xy, u32 n, f64 series_x0, f64 origin_x, bool log_x, bool log_y);
void stage_xy(const f32* xy, u32 n, f64 origin_x, bool log_x, bool log_y);
/// Lays one run of text out at a framebuffer position and stages it into
/// `batch`, shifting it into viewport space and tinting it `color` (the
/// per-instance `a_colour`). Returns the pen past the run.