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
@@ -65,6 +65,14 @@ bool covered_samples(const data::Series& series, f64 lo, f64 hi, u64& begin, u64
|
||||
return true;
|
||||
}
|
||||
|
||||
/// True when a pair of bounds can be a region's bounds: both of them finite. The one place
|
||||
/// this is decided, so `add_region()` and `set_region()` cannot disagree about what they
|
||||
/// will hold -- and so no NaN ever reaches the `std::min`/`std::max` below, which propagate
|
||||
/// it into *both* stored bounds and would leave a region no drag could recover from.
|
||||
bool finite_bounds(f64 x0, f64 x1) noexcept {
|
||||
return std::isfinite(x0) && std::isfinite(x1);
|
||||
}
|
||||
|
||||
const Measurement& invalid_measurement() noexcept {
|
||||
static const Measurement measurement{};
|
||||
return measurement;
|
||||
@@ -175,6 +183,12 @@ u32 Plot::add_annotation(const Annotation& annotation) {
|
||||
}
|
||||
|
||||
u32 Plot::add_region(const Region& region) {
|
||||
// A region whose bounds are not numbers is not an interval this plot can hold, and a
|
||||
// `Free` one stored that way could never be dragged back (every motion re-derives both
|
||||
// bounds from the stored ones), so it is refused rather than stored: nothing is added,
|
||||
// and the id handed back names no region -- `region()` reads a default for it and
|
||||
// `region_count()` does not count it.
|
||||
if (!finite_bounds(region.x0, region.x1)) return static_cast<u32>(kInvalid);
|
||||
// Ordered on the way in, so no caller -- and no drag -- can leave `x0 > x1`
|
||||
// behind: the store is the one place this invariant is enforced.
|
||||
Region stored = region;
|
||||
@@ -505,6 +519,12 @@ void Plot::invalidate_measurements() noexcept {
|
||||
|
||||
void Plot::set_region(u32 id, f64 x0, f64 x1) {
|
||||
if (id >= regions_.size()) return;
|
||||
// A bound that is not a number is not a bound, and `std::min`/`std::max` would carry it
|
||||
// through the whole pair: accepting one would replace the finite partner as well, so the
|
||||
// region could never be dragged back. Refused outright -- as the other bound setters in
|
||||
// this file refuse one -- which leaves the interval it already held, and with it the
|
||||
// statistics cached for that interval.
|
||||
if (!finite_bounds(x0, x1)) return;
|
||||
// min/max, so an inverted pair is stored ordered rather than clamped: a drag that
|
||||
// crosses one bound over the other *swaps* the interval, which is what keeps the
|
||||
// bound the pointer holds movable instead of pinned at the other one.
|
||||
|
||||
Reference in new issue
Block a user