fix(input): refuse non-finite region bounds, keep narrow regions movable, republish hot handles
This commit is contained in:
1 parent
4681d7015b
commit
7cc06f07f5
7 files changed
+263
-19
No files matched your search
@@ -1641,3 +1641,66 @@ PLOT_TEST(removing_a_region_shifts_the_ids_and_keeps_the_statistics) {
|
||||
PLOT_CHECK(!p.region_stats(2u, tr).valid);
|
||||
PLOT_CHECK(!p.region_stats(9u, tr).valid);
|
||||
}
|
||||
|
||||
/// A bound that is not a number is not a bound. `std::min`/`std::max` propagate a NaN
|
||||
/// through *both* ends of the pair, so accepting one would not merely invert the interval:
|
||||
/// it would replace the finite partner too, and a `Free` region stored that way could never
|
||||
/// be dragged back -- every motion re-derives both bounds from the stored ones. The other
|
||||
/// bound setters in the file refuse a non-finite value outright, and these two write paths
|
||||
/// have to as well: `set_region` leaves the region on the interval it already held, and
|
||||
/// `add_region`, which does not route through it, adds nothing at all.
|
||||
PLOT_TEST(non_finite_region_bounds_are_refused) {
|
||||
plot::data::ArraySeries s(ramp(100), 0.0, 1.0);
|
||||
plot::Plot p;
|
||||
p.set_viewport({0.0f, 0.0f, 800.0f, 400.0f});
|
||||
const u32 ax = p.add_x_axis({"t", {0.0, 99.0}, false, plot::AxisPlace::Bottom, false});
|
||||
const u32 ay = p.add_y_axis({"v", {0.0, 99.0}, false, plot::AxisPlace::Left, false});
|
||||
const u32 tr = p.add_trace({&s, plot::Color::hex(0xFFFFFF), 1.0f, ax, ay});
|
||||
|
||||
plot::Region reg;
|
||||
reg.x0 = 10.0;
|
||||
reg.x1 = 20.0;
|
||||
reg.snap = plot::RegionSnap::Free;
|
||||
const u32 rid = p.add_region(reg);
|
||||
const f64 nan = std::numeric_limits<f64>::quiet_NaN();
|
||||
const f64 inf = std::numeric_limits<f64>::infinity();
|
||||
|
||||
// Each of the four ways a pair can be unusable, and the interval is the same one after
|
||||
// every one of them -- both bounds, not just the bound that arrived bad.
|
||||
p.set_region(rid, nan, 20.0);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x0, 10.0, 1e-12);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x1, 20.0, 1e-12);
|
||||
p.set_region(rid, 10.0, nan);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x0, 10.0, 1e-12);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x1, 20.0, 1e-12);
|
||||
p.set_region(rid, 30.0, inf);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x0, 10.0, 1e-12);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x1, 20.0, 1e-12);
|
||||
p.set_region(rid, -inf, 5.0);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x0, 10.0, 1e-12);
|
||||
PLOT_CHECK_NEAR(p.region(rid).x1, 20.0, 1e-12);
|
||||
// The interval it still holds is the one its statistics are over.
|
||||
PLOT_CHECK(p.region_stats(rid, tr).valid);
|
||||
PLOT_CHECK_NEAR(p.region_stats(rid, tr).min, 10.0, 1e-9);
|
||||
PLOT_CHECK_NEAR(p.region_stats(rid, tr).max, 20.0, 1e-9);
|
||||
|
||||
// A region whose bounds are not numbers is refused rather than stored: the count does not
|
||||
// move, no id is handed out, and the id that came back names nothing.
|
||||
const u32 before = p.region_count();
|
||||
plot::Region bad = reg;
|
||||
bad.x0 = nan; // the finite partner goes with it, which is why the pair is refused
|
||||
const u32 refused = p.add_region(bad);
|
||||
PLOT_CHECK(p.region_count() == before);
|
||||
PLOT_CHECK(refused != before);
|
||||
PLOT_CHECK_NEAR(p.region(refused).x0, 0.0, 1e-12);
|
||||
PLOT_CHECK_NEAR(p.region(refused).x1, 0.0, 1e-12);
|
||||
PLOT_CHECK(!p.region_stats(refused, tr).valid);
|
||||
// The next region a caller adds still takes the next free id, so a refusal leaves the plot
|
||||
// exactly as it was rather than a hole a later id would fall into.
|
||||
plot::Region good = reg;
|
||||
good.x0 = 30.0;
|
||||
good.x1 = 40.0;
|
||||
PLOT_CHECK(p.add_region(good) == before);
|
||||
PLOT_CHECK(p.region_count() == before + 1u);
|
||||
PLOT_CHECK_NEAR(p.region(before).x0, 30.0, 1e-12);
|
||||
}
|
||||
Reference in new issue
Block a user