mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-26 11:26:50 -06:00
M1: dash limit counts the pattern restarting at each subpath (#1060)
kurbo restarts the dash pattern at every MoveTo, so many short subpaths with a pattern of tiny dashes before a long gap made ~1M dashes (5M path elements) while the length-based estimate allowed them. stroke_center now adds one pattern's length per subpath to the estimate: an upper bound on the dashes kurbo makes. Test: each_subpath_restarting_the_dash_pattern_counts_toward_the_limit (5M elements before). Reported by CodeRabbit on #1278.
This commit is contained in:
@@ -292,7 +292,9 @@ pub fn stroke_center(path: &BezPath, width: f64, props: &StrokeProps) -> BezPath
|
||||
.with_miter_limit(props.miter_limit.clamp(1.0, 100.0))
|
||||
.with_start_cap(kcap(props.cap_start))
|
||||
.with_end_cap(kcap(props.cap_end));
|
||||
if let Some(pat) = width::dash_pattern(props).filter(|p| width::dashes_fit(path.perimeter(1.0), p)) {
|
||||
// Each subpath restarts the pattern, so it can add up to one more pattern's worth of dashes.
|
||||
let subpaths = path.elements().iter().filter(|e| matches!(e, PathEl::MoveTo(_))).count() as f64;
|
||||
if let Some(pat) = width::dash_pattern(props).filter(|p| width::dashes_fit(path.perimeter(1.0) + subpaths * p.iter().sum::<f64>(), p)) {
|
||||
style = style.with_dashes(0.0, pat);
|
||||
}
|
||||
kurbo::stroke(path.iter(), &style, &kurbo::StrokeOpts::default(), 0.05)
|
||||
@@ -732,6 +734,22 @@ mod tests {
|
||||
assert!((s.area().abs() - 20_000.0).abs() < 10.0, "{}", s.area());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_subpath_restarting_the_dash_pattern_counts_toward_the_limit() {
|
||||
// The pattern restarts at every subpath: on 10,000 short lines its 199 tiny dashes come
|
||||
// round each time (~1M dashes), though the length alone suggests a few hundred.
|
||||
let mut path = BezPath::new();
|
||||
for i in 0..10_000 {
|
||||
path.move_to((0.0, f64::from(i) * 4.0));
|
||||
path.line_to((2.0, f64::from(i) * 4.0));
|
||||
}
|
||||
let mut dashes = vec![0.01; 199];
|
||||
dashes.push(10_000.0);
|
||||
let props = StrokeProps { weight: 1.0, dashes, align: StrokeAlign::Center, ..Default::default() };
|
||||
let s = stroke_center(&path, 1.0, &props);
|
||||
assert!(s.elements().len() < 100_000, "{} elements", s.elements().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inside_and_outside_strokes_respect_an_even_odd_hole() {
|
||||
// A ring: both loops run the same way, so only the even-odd rule leaves the hole open.
|
||||
|
||||
Reference in New Issue
Block a user