diff --git a/crates/pathops/src/round.rs b/crates/pathops/src/round.rs index 40ab03759..385f1faba 100644 --- a/crates/pathops/src/round.rs +++ b/crates/pathops/src/round.rs @@ -114,10 +114,12 @@ pub fn round_corners(path: &BezPath, radius: f64, smoothing: f64) -> BezPath { return path.clone(); } let smoothing = if smoothing.is_finite() { smoothing.clamp(0.0, 1.0) } else { 0.0 }; - let bb = path.bounding_box(); - let weld = (bb.width().max(bb.height()) * 1e-6).max(1e-9); let mut out = BezPath::new(); for sub in figcraft_geom::split_subpaths(path) { + // Each contour's own size sets its cleanup tolerance, so a large contour can't weld away + // the real short edges of a small island or hole beside it. + let bb = sub.bounding_box(); + let weld = (bb.width().max(bb.height()) * 1e-6).max(1e-9); let closed = matches!(sub.elements().last(), Some(PathEl::ClosePath)); match closed.then(|| round_subpath(&sub, radius, smoothing, weld)).flatten() { Some(p) => out.extend(p.elements().iter().copied()), diff --git a/crates/pathops/src/round_tests.rs b/crates/pathops/src/round_tests.rs index eda74b464..0c22b5363 100644 --- a/crates/pathops/src/round_tests.rs +++ b/crates/pathops/src/round_tests.rs @@ -236,3 +236,21 @@ fn junk_never_panics() { zig.close_path(); assert!(all_finite(&round_corners(&zig, 2.0, 0.0))); } + +/// The cleanup tolerance is each contour's own: a contour a million units wide doesn't weld away +/// the half-unit notch of a separate small contour in the same path. +#[test] +fn a_large_contour_does_not_weld_a_small_ones_short_edges() { + let mut p = rect(0.0, 0.0, 1e6, 1e6); + let x = 2e6; + for (i, (dx, y)) in [(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (5.5, 10.0), (5.5, 9.5), (5.0, 9.5), (5.0, 10.0), (0.0, 10.0)].into_iter().enumerate() + { + if i == 0 { p.move_to((x + dx, y)) } else { p.line_to((x + dx, y)) } + } + p.close_path(); + let out = round_corners(&p, 0.001, 0.0); + let reaches = |q: Point| { + out.elements().iter().any(|el| matches!(el, PathEl::MoveTo(e) | PathEl::LineTo(e) | PathEl::CurveTo(_, _, e) if (*e - q).hypot() < 0.01)) + }; + assert!(reaches(pt(x + 5.0, 9.5)) && reaches(pt(x + 5.5, 9.5)), "the notch's floor is kept"); +}