Keep reconstructed label corridors finite and inside blockers
This commit is contained in:
@@ -59,7 +59,7 @@ pub(super) fn widest_clear(
|
|||||||
}
|
}
|
||||||
let candidate = Rectangle {
|
let candidate = Rectangle {
|
||||||
x: start,
|
x: start,
|
||||||
width: end - start,
|
width: contained_width(start, end),
|
||||||
..band
|
..band
|
||||||
};
|
};
|
||||||
// Use f64 only for comparison distance: subtracting two admitted f32
|
// Use f64 only for comparison distance: subtracting two admitted f32
|
||||||
@@ -83,6 +83,18 @@ pub(super) fn widest_clear(
|
|||||||
Ok(best)
|
Ok(best)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finite f32 endpoints can subtract to infinity or round across a blocker.
|
||||||
|
/// Compute in f64, cap to finite f32, and round inward if nearest rounding
|
||||||
|
/// overshot the admitted interval's end.
|
||||||
|
fn contained_width(start: f32, end: f32) -> f32 {
|
||||||
|
let exact = f64::from(end) - f64::from(start);
|
||||||
|
let mut width = exact.min(f64::from(f32::MAX)) as f32;
|
||||||
|
if f64::from(start) + f64::from(width) > f64::from(end) {
|
||||||
|
width = width.next_down();
|
||||||
|
}
|
||||||
|
width
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -186,4 +198,34 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert_eq!(widest_clear(band, &[], f32::INFINITY), Err(InvalidGeometry));
|
assert_eq!(widest_clear(band, &[], f32::INFINITY), Err(InvalidGeometry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finite_band_cannot_reconstruct_an_infinite_width() {
|
||||||
|
let band = rect(f32::from_bits(0xfe79636e), 0., f32::MAX, 20.);
|
||||||
|
let right = band.x + band.width;
|
||||||
|
assert!(right.is_finite());
|
||||||
|
assert!(
|
||||||
|
(right - band.x).is_infinite(),
|
||||||
|
"counterexample must exercise f32 subtraction overflow"
|
||||||
|
);
|
||||||
|
let result = widest_clear(band, &[], 0.).unwrap().unwrap();
|
||||||
|
assert!(result.width.is_finite() && result.width > 0.);
|
||||||
|
assert!(result.x + result.width <= right);
|
||||||
|
assert!(f64::from(result.x) + f64::from(result.width) <= f64::from(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reconstructed_width_rounds_inward_at_a_blocker() {
|
||||||
|
let band = rect(-999_948.562_5, 0., 1_000_000., 20.);
|
||||||
|
let blocker = rect(2.1, 0., 10., 20.);
|
||||||
|
assert!(
|
||||||
|
band.x + (blocker.x - band.x) > blocker.x,
|
||||||
|
"counterexample must cross the blocker under naive reconstruction"
|
||||||
|
);
|
||||||
|
let result = widest_clear(band, &[blocker], 0.).unwrap().unwrap();
|
||||||
|
assert_eq!(result.x, band.x);
|
||||||
|
assert!(result.width > 999_000.);
|
||||||
|
assert!(result.x + result.width <= blocker.x);
|
||||||
|
assert!(f64::from(result.x) + f64::from(result.width) <= f64::from(blocker.x));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user