address review: count open-subpath overhead as one element in the operator budget

This commit is contained in:
Dylan M. Taylor
2026-11-04 03:43:26 +00:00
parent 52ce9a125b
commit 9d8611299b
2 changed files with 19 additions and 3 deletions
+10 -3
View File
@@ -390,8 +390,10 @@ pub fn twist(paths: &[BezPath], angle_deg: f64, center: [f64; 2]) -> Vec<BezPath
// The pieces a segment asks for; the uniform cap applies only when the requests together
// would pass the budget (long segments keep their detail next to many short ones otherwise).
let want = |len: f64| ((a.abs() * len / rmax) / 0.15 + len / 50.0).ceil().clamp(1.0, 64.0) as usize;
let requested =
paths.iter().flat_map(subs).fold(0usize, |n, sub| sub.segs.iter().fold(n.saturating_add(2), |n, seg| n.saturating_add(want(seg.arclen(ACC)))));
let requested = paths
.iter()
.flat_map(subs)
.fold(0usize, |n, sub| sub.segs.iter().fold(n.saturating_add(overhead_of(&sub)), |n, seg| n.saturating_add(want(seg.arclen(ACC)))));
let most = if requested <= MAX_OPERATOR_SEGMENTS { 64 } else { pieces_within_budget(paths, 64) };
let rot = |v: Vec2, th: f64| {
let (s, co) = th.sin_cos();
@@ -453,6 +455,11 @@ struct Sample {
/// output keeps a short stack from multiplying a simple path into billions of points.
pub const MAX_OPERATOR_SEGMENTS: usize = 1 << 20;
/// Elements a subpath adds besides its segments: a `MoveTo`, and a `ClosePath` only when closed.
fn overhead_of(sub: &Sub) -> usize {
if sub.closed { 2 } else { 1 }
}
/// `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 {
@@ -460,7 +467,7 @@ fn pieces_within_budget(paths: &[BezPath], want: usize) -> usize {
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);
overhead = overhead.saturating_add(overhead_of(&sub));
}
want.min(MAX_OPERATOR_SEGMENTS.saturating_sub(overhead) / segs.max(1)).max(1)
}
+9
View File
@@ -469,3 +469,12 @@ fn many_short_open_paths_keep_their_requested_detail_within_the_budget() {
let z = zigzag(&lines, 1.0, 1000.0, false);
assert!(z.iter().all(|q| q.elements().len() == 1 + 1001));
}
#[test]
fn open_subpaths_reserve_one_element_of_overhead() {
// 349,525 open lines: the move-to is the only extra element, so 2 copies fit the budget
// (a closed subpath's extra close-path would leave room for 1).
let line = BezPath::from_vec(vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::LineTo((1.0, 0.0).into())]);
let lines = vec![line; 349_525];
assert_eq!(pieces_within_budget(&lines, 3), 2);
}