diff --git a/crates/path/src/ops.rs b/crates/path/src/ops.rs index 0e59046e6..ebc55505c 100644 --- a/crates/path/src/ops.rs +++ b/crates/path/src/ops.rs @@ -448,11 +448,16 @@ struct Sample { /// output keeps a short stack from multiplying a simple path into billions of points. pub const MAX_OPERATOR_SEGMENTS: usize = 1 << 20; -/// `want` pieces (or copies) per input segment, reduced so the whole output stays within -/// [`MAX_OPERATOR_SEGMENTS`] (never below 1, so the input itself always passes through). +/// `want` pieces (or copies) per input segment, reduced so the whole output (segments times +/// pieces, plus per-subpath overhead) stays within [`MAX_OPERATOR_SEGMENTS`] (never below 1, so the input itself always passes through). fn pieces_within_budget(paths: &[BezPath], want: usize) -> usize { - let segs: usize = paths.iter().map(|p| p.elements().len()).sum(); - want.min(MAX_OPERATOR_SEGMENTS / segs.max(1)).max(1) + // Count what is subdivided (drawn segments) and keep room for each subpath's own MoveTo/Close. + let (mut segs, mut overhead) = (0usize, 0usize); + for sub in paths.iter().flat_map(subs) { + segs = segs.saturating_add(sub.segs.len()); + overhead = overhead.saturating_add(2); + } + want.min(MAX_OPERATOR_SEGMENTS.saturating_sub(overhead) / segs.max(1)).max(1) } /// Split every segment into `m` pieces of equal arc length. Original vertices get the average of diff --git a/crates/path/src/ops/tests.rs b/crates/path/src/ops/tests.rs index de8321be6..5735d5724 100644 --- a/crates/path/src/ops/tests.rs +++ b/crates/path/src/ops/tests.rs @@ -438,3 +438,18 @@ fn stacked_expanding_operators_stay_within_the_segment_budget() { // Within the budget nothing changes: 4 segments × 11 pieces. assert_eq!(vertex_count(&zigzag(&[sq(0.0, 0.0, 100.0)], 1.0, 10.0, false)[0]), 44); } + +#[test] +fn many_short_open_paths_keep_their_requested_detail_within_the_budget() { + // 1000 open lines at 1000 ridges: 1,001,000 segments fit the budget, so nothing is reduced. + let lines: Vec = (0..1000) + .map(|i| { + let mut p = BezPath::new(); + p.move_to((0.0, i as f64)); + p.line_to((100.0, i as f64)); + p + }) + .collect(); + let z = zigzag(&lines, 1.0, 1000.0, false); + assert!(z.iter().all(|q| q.elements().len() == 1 + 1001)); +}