fix(plot): a degenerate range cannot strand a live axis, and the demo's follow check fails the run
This commit is contained in:
1 parent
77e861f79e
commit
7d2349555d
6 files changed
+146
-13
No files matched your search
@@ -681,8 +681,8 @@ PLOT_TEST(drag_zoom_is_logical_the_wheel_is_not_scaled) {
|
||||
const f32 drag_px = 20.0f * scale; // 20 *logical* px at both scales
|
||||
c.on_pointer({strip_x, strip_y, 0, 0, plot::input::Button::Middle, 0, true, true, false});
|
||||
PLOT_CHECK(c.drag_mode() == plot::input::DragMode::AxisZoomX);
|
||||
c.on_pointer({strip_x, strip_y + drag_px, 0, drag_px, plot::input::Button::Middle, 0, true, false,
|
||||
false});
|
||||
c.on_pointer({strip_x, strip_y + drag_px, 0, drag_px, plot::input::Button::Middle, 0, true,
|
||||
false, false});
|
||||
c.on_pointer({strip_x, strip_y + drag_px, 0, 0, plot::input::Button::Middle, 0, false, false,
|
||||
true});
|
||||
PLOT_CHECK_NEAR(p.x_range(ax).span(), 100.0 * std::pow(10.0, -20.0 / 200.0), 1e-6);
|
||||
@@ -692,3 +692,32 @@ PLOT_TEST(drag_zoom_is_logical_the_wheel_is_not_scaled) {
|
||||
PLOT_CHECK_NEAR(p.x_range(ax).span(), span_before * std::exp(-0.15), 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
/// The consequences of a degenerate range on a live axis, from the gesture side: the
|
||||
/// wheel, the pan and the axis zoom all refuse a span that is not a span, so the
|
||||
/// frozen axis a stored zero-span left behind was one no interaction could move --
|
||||
/// while `x_follow()` said `Live`. With the guard in `set_x_range` the slips a caller
|
||||
/// can actually make leave the gestures working.
|
||||
PLOT_TEST(live_gestures_survive_a_degenerate_range) {
|
||||
LiveFixture f(200, 40);
|
||||
f.live();
|
||||
const plot::Rect axes = std::as_const(f.graph).layout().axes;
|
||||
const f64 span = f.xr().span();
|
||||
const f64 newest = f.newest();
|
||||
const plot::input::WheelEvent wheel = strip_wheel(f.graph, 1.0f);
|
||||
|
||||
f.graph.set_x_range(f.ax, {newest, newest}); // the programmatic slip
|
||||
PLOT_CHECK_NEAR(f.xr().span(), span, 1e-9);
|
||||
f.graph.set_x_range(f.ax, {newest - 5.0, newest - 15.0}); // and an inverted one
|
||||
PLOT_CHECK_NEAR(f.xr().span(), 10.0, 1e-9); // normalised, not stored inverted
|
||||
|
||||
f.controller.on_wheel(wheel);
|
||||
PLOT_CHECK_NEAR(f.xr().span(), 10.0 * std::exp(-0.15), 1e-9); // the wheel still zooms
|
||||
PLOT_CHECK(f.graph.x_follow(f.ax) == plot::XFollow::Live);
|
||||
const f64 span_after_wheel = f.xr().span();
|
||||
drag(f.controller, axes, 60.0f); // a left drag: the pan gesture
|
||||
PLOT_CHECK(f.graph.x_offset(f.ax) > 0.0); // the pan still pans
|
||||
PLOT_CHECK_NEAR(f.xr().span(), span_after_wheel, 1e-9);
|
||||
PLOT_CHECK_NEAR(f.xr().max, f.newest() - f.graph.x_offset(f.ax), 1e-9);
|
||||
PLOT_CHECK(f.graph.x_follow(f.ax) == plot::XFollow::Live);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user