Skip to content

Fix panic on degenerate vertical/horizontal segments in BBox::crossing#11

Merged
taddyb merged 1 commit intomasterfrom
fix/crossing-degenerate-vertical
Apr 9, 2026
Merged

Fix panic on degenerate vertical/horizontal segments in BBox::crossing#11
taddyb merged 1 commit intomasterfrom
fix/crossing-degenerate-vertical

Conversation

@taddyb
Copy link
Copy Markdown
Contributor

@taddyb taddyb commented Apr 7, 2026

Summary

BBox::crossing() was panicking with unreachable!("vertical line doesn't exit box") when 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. 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:

thread '<unnamed>' panicked at extractrs-core/src/bbox.rs:165:17:
internal error: entered unreachable code: vertical line doesn't exit box

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() calls BBox::crossing(prev_original, c) when c is classified as Outside the cell. prev_original is coords[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. If prev_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:

  1. Upstream guard in Cell::take() — skip prev_original if it would form a degenerate collinear segment outside the cell, fall back to last_coordinate (which is always on or inside the cell boundary, so crossing() will compute correctly).

  2. Defense in depth in BBox::crossing() — replace the two unreachable!() 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.rs exercising the degenerate case in all four directions (vertical left/right, horizontal above/below). Pre-fix these would have panicked. All 64 extractrs-core tests 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 calling crossing() 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 --workspace passes
  • cargo clippy --package extractrs-core -- -D warnings clean
  • Rebuild Python wheel and re-run the AORC extraction that triggered the panic
  • Confirm the affected polygon now produces a result (even if approximate) instead of crashing

🤖 Generated with Claude Code

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.
@taddyb taddyb merged commit 5ff41f3 into master Apr 9, 2026
2 checks passed
@taddyb taddyb deleted the fix/crossing-degenerate-vertical branch April 9, 2026 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant