Fix panic on degenerate vertical/horizontal segments in BBox::crossing#11
Merged
Fix panic on degenerate vertical/horizontal segments in BBox::crossing#11
Conversation
BBox::crossing() panicked with "vertical line doesn't exit box" when the polygon traversal landed in a cell where prev_original and the current coordinate formed a vertical or horizontal segment lying entirely outside the cell on the perpendicular axis. This is a latent bug in the exactextract port (see isciences/exactextract#126) and crashes the entire process when triggered, rather than failing one polygon. Fix at two layers: 1. Cell::take() now skips prev_original if it would form a degenerate collinear segment outside the cell, falling back to last_coordinate (which is always inside or on the cell boundary). 2. BBox::crossing() replaces the unreachable!() panics with a graceful fallback that returns the closest box side, so any future caller that violates the precondition gets wrong coverage instead of a crash. Triggered in production by 346K MERIT polygons against the AORC ~0.0083° grid.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
BBox::crossing()was panicking withunreachable!("vertical line doesn't exit box")when polygon traversal landed in a cell whereprev_originaland the current coordinate formed a vertical (or horizontal) segment lying entirely outside the cell on the perpendicular axis. The panic killed the entire process rather than failing the affected polygon.Triggered in production by a MERIT polygon (one of 346K) against the AORC ~0.0083° grid:
This is a latent bug inherited from the exactextract port — see isciences/exactextract#126 where the same condition surfaces with low-quality polygon data.
Root cause
Cell::take()callsBBox::crossing(prev_original, c)whencis classified as Outside the cell.prev_originaliscoords[pos-1](the previous uninterpolated polygon vertex) used for floating-point robustness — but it can be far from the current cell when the traversal walks across many cells. Ifprev_original.x == c.x(exactly vertical, e.g. duplicate vertex or true vertical edge) AND that x is strictly outside the cell's[xmin, xmax], the segment doesn't intersect the cell at all, and the vertical-line branch's exit checks both fail.Fix
Two layers:
Upstream guard in
Cell::take()— skipprev_originalif it would form a degenerate collinear segment outside the cell, fall back tolast_coordinate(which is always on or inside the cell boundary, socrossing()will compute correctly).Defense in depth in
BBox::crossing()— replace the twounreachable!()panics with a graceful fallback that returns the box side closest to the segment. Any future caller violating the precondition gets wrong coverage for one polygon instead of crashing the process.Tests
Added 4 new unit tests in
bbox.rsexercising the degenerate case in all four directions (vertical left/right, horizontal above/below). Pre-fix these would have panicked. All 64extractrs-coretests pass; clippy clean.What this does NOT fix
The traversal logic in
intersection.rs::traverse_cells()may still place segments in cells they don't actually pass through (root cause of how we end up callingcrossing()in the degenerate state in the first place). Coverage results for degenerate polygons may be slightly off. A follow-up issue should audit that traversal logic — but for now this fix prevents process crashes, which was the immediate blocker.Test plan
cargo test --workspacepassescargo clippy --package extractrs-core -- -D warningsclean🤖 Generated with Claude Code