fix: a zoomed trace is drawn on the x its readout quotes

gather() staged x as f32 relative to the series' own x0() -- the start of
the whole retained window -- so the quantisation was a fraction of the
ring rather than of the view: 1.9 us at 20 s of retained data, which at a
10 us view across a 716 px axis is over 100 px between where a sample is
drawn and the x a readout quotes for it (crosshair_point() reads x_at()
in exact f64). It halved on every zoom out, which is what made it read as
"zooming in makes the crosshair select a point beside the data".

The origin is now the caller's (plotter c2c8c59): the renderer passes the
visible range's minimum, so the error is a fixed 2^-24 of what is on
screen -- about 4e-5 px over a 700 px axis -- at any zoom. The parameter
sits before out_xy so every call site is a compile error rather than a
silent change of behaviour.

The renderer no longer reads series.x0() at paint time at all, which
removes two of the three symptoms of the torn window_x0_ read by
construction; the ink probe's now-vacuous kept_fixed_x0 walk goes with
them, replaced by a drawn-vs-quoted pixel figure. neighbours() still
tests the visible range against x_at(0), so the cache stays load-bearing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-10-05 18:49:54 +02:00
1 parent 39f30d913f
commit 2f4530c128
7 files changed
+176 -90

No files matched your search

+9 -6
View File
@@ -80,9 +80,11 @@ class ChannelBuffer final : public plot::data::Series {
plot::Range x_bounds() const override;
plot::Range y_bounds() const override;
const plot::data::MinMaxPyramid& pyramid() const override { return pyramid_; }
/// x is emitted **relative to `x0()`** (the library's contract): staging an
/// absolute epoch time as f32 would quantise it far coarser than a pixel.
void gather(u32 level, u64 begin, u64 end, std::vector<f32>& out_xy,
/// x is emitted **relative to `x_origin`** (the library's contract): staging an
/// absolute epoch time as f32 would quantise it far coarser than a pixel, and
/// staging it relative to the whole *retained window* quantises it coarser than a
/// pixel of a zoomed view -- see `plot::data::Series::gather()`.
void gather(u32 level, u64 begin, u64 end, f64 x_origin, std::vector<f32>& out_xy,
plot::data::GatherMode mode = plot::data::GatherMode::Envelope) const override;
/// `copy_raw`, published so the retargetable trace series can forward the
@@ -109,8 +111,9 @@ class ChannelBuffer final : public plot::data::Series {
/// slot cannot be overwritten under the read. Everything else on this object is read at
/// *paint* time with no lock, and this one slot is the ring's oldest: the one the producer is
/// about to overwrite. Reading it there returned a stamp a whole ring-span too new whenever
/// the producer lapped it between the bounds check and the copy -- and `x0()` is the origin
/// the renderer reconstructs every trace point from (`gather[i] + series.x0()`), so one such
/// the producer lapped it between the bounds check and the copy -- and `x0()` is what
/// `neighbours()` tests the visible range against, as well as the origin the renderer
/// reconstructed every trace point from before the caller named one, so one such
/// read moved the entire polyline a ring-span off its axis (an invisible trace) or, caught
/// inside the renderer's increasing-suffix filter, made it drop most of the points. That is
/// the "live plot flickers as if there were no data" defect. The cached value is also the
@@ -163,7 +166,7 @@ class SlotSeries final : public plot::data::Series {
plot::Range x_bounds() const override;
plot::Range y_bounds() const override;
const plot::data::MinMaxPyramid& pyramid() const override;
void gather(u32 level, u64 begin, u64 end, std::vector<f32>& out_xy,
void gather(u32 level, u64 begin, u64 end, f64 x_origin, std::vector<f32>& out_xy,
plot::data::GatherMode mode = plot::data::GatherMode::Envelope) const override;
bool streaming() const noexcept override { return event_ == nullptr && buffer_ != nullptr; }