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

+46
View File
@@ -1365,3 +1365,49 @@ PLOT_TEST(a_static_plot_is_unaffected) {
PLOT_CHECK_NEAR(f.xr().min, frozen.min, 1e-9);
PLOT_CHECK_NEAR(f.xr().max, frozen.max, 1e-9);
}
/// A degenerate range must not be able to strand a live axis. `set_x_range` under
/// `Live` is read as a span and an offset, and a span that is not a span is not a
/// window: storing it would leave the axis with a range nothing can place -- every
/// layout rejects a non-positive span -- and, because every gesture refuses one too,
/// with a range nothing can move either, while `x_follow()` still said `Live`. So the
/// one setter that could produce that state refuses to.
PLOT_TEST(a_degenerate_range_cannot_freeze_a_live_axis) {
LiveFixture f(200, 40);
f.live();
const f64 span = f.xr().span();
const f64 newest = f.newest();
// A zero-width range is ignored: the axis keeps the span and the offset it holds.
f.graph.set_x_range(f.ax, {newest, newest});
PLOT_CHECK_NEAR(f.xr().span(), span, 1e-9);
PLOT_CHECK_NEAR(f.xr().max, newest, 1e-9);
PLOT_CHECK(f.graph.x_axis(f.ax).range.max > f.graph.x_axis(f.ax).range.min);
PLOT_CHECK(f.graph.x_follow(f.ax) == plot::XFollow::Live);
// ...and the stream carries on being followed, which is what the state was for.
f.feed(10);
f.graph.layout();
PLOT_CHECK_NEAR(f.xr().max, f.newest(), 1e-9);
// A non-finite endpoint is ignored for the same reason.
f.graph.set_x_range(f.ax, {f.newest() - 5.0, std::numeric_limits<f64>::quiet_NaN()});
PLOT_CHECK_NEAR(f.xr().span(), span, 1e-9);
PLOT_CHECK_NEAR(f.xr().max, f.newest(), 1e-9);
PLOT_CHECK(f.graph.x_follow(f.ax) == plot::XFollow::Live);
// An inverted range is a caller's ordering slip, not a request to break the axis'
// `min <= max` invariant -- every consumer of a range reads it as one -- so it is
// normalised, and the window still spans what the two endpoints enclose.
const f64 edge = f.newest();
f.graph.set_x_range(f.ax, {edge - 5.0, edge - 15.0});
PLOT_CHECK_NEAR(f.xr().span(), 10.0, 1e-9);
PLOT_CHECK_NEAR(f.xr().max, edge - 5.0, 1e-9);
PLOT_CHECK_NEAR(f.graph.x_offset(f.ax), 5.0, 1e-9);
PLOT_CHECK(f.graph.x_follow(f.ax) == plot::XFollow::Live);
// The offset that came with it is the one the next frames keep.
f.feed(20);
f.graph.layout();
PLOT_CHECK_NEAR(f.xr().max, f.newest() - 5.0, 1e-9);
PLOT_CHECK_NEAR(f.xr().span(), 10.0, 1e-9);
}