geo 0.33.1. A polygon containing a negative zero coordinate is reported as valid by check_validation(), but interior_point() then trips its own internal debug_assert! in the sweep line.
Minimal reproducer — only geo = "=0.33.1", with debug-assertions = true:
use geo::algorithm::{InteriorPoint, Validation};
use geo::{Coord, LineString, Polygon};
fn main() {
let ring = LineString(vec![
Coord { x: 5.0, y: 22.0 },
Coord { x: 2.0, y: 7.0 },
Coord { x: -0.0, y: 423.0 }, // negative zero
Coord { x: 0.0, y: 3.0 },
Coord { x: 9.0, y: 0.0 },
Coord { x: 5.0, y: 22.0 },
]);
let poly = Polygon::new(ring, vec![]);
assert!(poly.check_validation().is_ok()); // passes
let _ = poly.interior_point(); // panics
}
assertion failed: intervals_overlap(current_interval, overlapping_interval)
geo-0.33.1/src/algorithm/sweep/mod.rs:169
Replacing -0.0 with 0.0, leaving every other coordinate untouched, makes it return POINT(0.5084134615384616 211.5). The two polygons are numerically identical, since -0.0 == 0.0.
Root cause. src/algorithm/sweep/mod.rs mixes two incompatible orderings:
- line 266 sorts with total_cmp, and intervals_overlap (lines 277-280) compares with total_cmp — a total order, where -0.0 < +0.0;
- line 163 guards with the IEEE operator: if overlapping_interval.inserted_x > current_interval.deleted_x.
With deleted_x == -0.0 and inserted_x == 0.0 the IEEE guard is false, so the pair is not skipped, while intervals_overlap returns false because (-0.0).total_cmp(&0.0) is Less. The assertion then fails. Signed zero is precisely where the two orderings disagree on equal values.
Suggested fix. Make line 163 use the same ordering as the rest of the module:
if overlapping_interval.inserted_x.total_cmp(¤t_interval.deleted_x).is_gt() {
Why it matters beyond the panic. It is a debug_assert!, so in release builds the check is compiled out and the sweep proceeds with a violated invariant — interior_point() can then return a wrong point silently. We only noticed because cargo-fuzz enables debug assertions.
Found by fuzzing a WKT parsing and geometry operations pipeline; the input was POLYGON((5 22, 2 7, -0 423, 0 3, 9 0, 5 22)).
geo 0.33.1. A polygon containing a negative zero coordinate is reported as valid by check_validation(), but interior_point() then trips its own internal debug_assert! in the sweep line.
Minimal reproducer — only geo = "=0.33.1", with debug-assertions = true:
use geo::algorithm::{InteriorPoint, Validation};
use geo::{Coord, LineString, Polygon};
fn main() {
let ring = LineString(vec![
Coord { x: 5.0, y: 22.0 },
Coord { x: 2.0, y: 7.0 },
Coord { x: -0.0, y: 423.0 }, // negative zero
Coord { x: 0.0, y: 3.0 },
Coord { x: 9.0, y: 0.0 },
Coord { x: 5.0, y: 22.0 },
]);
let poly = Polygon::new(ring, vec![]);
}
assertion failed: intervals_overlap(current_interval, overlapping_interval)
geo-0.33.1/src/algorithm/sweep/mod.rs:169
Replacing -0.0 with 0.0, leaving every other coordinate untouched, makes it return POINT(0.5084134615384616 211.5). The two polygons are numerically identical, since -0.0 == 0.0.
Root cause. src/algorithm/sweep/mod.rs mixes two incompatible orderings:
With deleted_x == -0.0 and inserted_x == 0.0 the IEEE guard is false, so the pair is not skipped, while intervals_overlap returns false because (-0.0).total_cmp(&0.0) is Less. The assertion then fails. Signed zero is precisely where the two orderings disagree on equal values.
Suggested fix. Make line 163 use the same ordering as the rest of the module:
if overlapping_interval.inserted_x.total_cmp(¤t_interval.deleted_x).is_gt() {
Why it matters beyond the panic. It is a debug_assert!, so in release builds the check is compiled out and the sweep proceeds with a violated invariant — interior_point() can then return a wrong point silently. We only noticed because cargo-fuzz enables debug assertions.
Found by fuzzing a WKT parsing and geometry operations pipeline; the input was POLYGON((5 22, 2 7, -0 423, 0 3, 9 0, 5 22)).