fix(plot): a degenerate range cannot strand a live axis, and the demo's follow check fails the run

This commit is contained in:
Martino Ferrari committed 2026-09-28 13:14:50 +02:00
1 parent 77e861f79e
commit 7d2349555d
6 files changed
+146 -13

No files matched your search

+17 -4
View File
@@ -167,8 +167,7 @@ void Plot::set_x_range(u32 axis, Range r) {
if (axis >= x_axes_.size()) return;
Axis& a = x_axes_[axis];
Range live{};
if (a.follow == XFollow::Live && live_x_bounds(axis, live) && r.span() > 0.0 &&
std::isfinite(r.min) && std::isfinite(r.max)) {
if (a.follow == XFollow::Live && live_x_bounds(axis, live)) {
// Under Live the caller is stating a *span* and where the window sits relative
// to the live edge, not a pair of absolute positions: the next layout()
// recomputes the window from the series' newest x, so storing `r` verbatim
@@ -178,8 +177,22 @@ void Plot::set_x_range(u32 axis, Range r) {
// drag's data delta -- and place_live_x_window() clamps it and stores the
// window that follows from it, so the state is consistent before the next
// layout rather than only after it.
a.range = r;
a.offset = live.max - r.max;
//
// This branch is taken unconditionally on a live axis, and what it refuses to
// store is what would strand it: `place_live_x_window()` needs a positive span,
// and every gesture keeps a non-positive one out of its arithmetic, so a
// degenerate range stored here would leave the axis following nothing, movable
// by nothing, while `x_follow()` still said Live. An inverted range is a
// caller's ordering slip, not a request to break the axis' `min <= max`
// invariant -- the rest of the library reads a range as a range -- so it is
// normalised; a span that is not a span is ignored outright, leaving the span
// and offset the axis holds. `auto_fit_x()` is how a window is framed either
// way.
const f64 lo = std::min(r.min, r.max);
const f64 hi = std::max(r.min, r.max);
if (!std::isfinite(lo) || !std::isfinite(hi) || !(hi > lo)) return;
a.range = Range{lo, hi};
a.offset = live.max - hi;
place_live_x_window(axis);
a.auto_scale = false; // as ever: a range a caller chose is not an auto-fit
return;