From 5a36fa1a9b35c806a5c07b1c151b86201ed9e8a5 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 2 Oct 2026 08:16:35 -0400 Subject: [PATCH] M6.1: a panic inside the curve sweep (rare near-degenerate input) becomes PathOpsError::Degenerate instead of crashing (found by the randomized export test) Co-Authored-By: Claude Opus 5.5 --- crates/pathops/src/boolean.rs | 7 ++++++- crates/pathops/src/lib.rs | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/pathops/src/boolean.rs b/crates/pathops/src/boolean.rs index 41ed5d031..98e9fa738 100644 --- a/crates/pathops/src/boolean.rs +++ b/crates/pathops/src/boolean.rs @@ -196,7 +196,12 @@ impl Tidy { impl Arrangement { pub(crate) fn new(paths: &[(BezPath, FillRule)]) -> Result { let eps = eps_for(paths.iter().map(|p| &p.0))?; - let top = Topology::::from_paths(paths.iter().enumerate().map(|(i, (p, _))| (p, i)), eps).map_err(|_| PathOpsError::OpenPath)?; + // The sweep can panic on rare near-degenerate input; that must never take the app down, + // so it becomes an error (callers fall back to leaving the art unchanged). + let sweep = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + Topology::::from_paths(paths.iter().enumerate().map(|(i, (p, _))| (p, i)), eps) + })); + let top = sweep.map_err(|_| PathOpsError::Degenerate)?.map_err(|_| PathOpsError::OpenPath)?; let tidy = Tidy::keeping(DEFAULT_PRECISION, paths.iter().map(|p| &p.0)); Ok(Self { top, rules: paths.iter().map(|p| p.1).collect(), tidy }) } diff --git a/crates/pathops/src/lib.rs b/crates/pathops/src/lib.rs index 6908e26af..344c3963a 100644 --- a/crates/pathops/src/lib.rs +++ b/crates/pathops/src/lib.rs @@ -26,4 +26,6 @@ pub enum PathOpsError { NonFinite, #[error("input path could not be closed")] OpenPath, + #[error("the shapes are too degenerate to combine")] + Degenerate, }