From 585621a661ba283547a5a4bf3a9dfdd49bbc9bd4 Mon Sep 17 00:00:00 2001 From: adambelis Date: Mon, 9 Nov 2026 09:40:44 +0100 Subject: [PATCH] Blend: the spine keeps only the subpath it samples Spine stored a full clone of the blend's PathData but only ever read its first subpath. Store that SubPath instead, which drops the extra allocation and makes the field's purpose clear. Document the field and the two cases where Spine::new returns None. --- crates/doc/src/blend.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/doc/src/blend.rs b/crates/doc/src/blend.rs index d25be2c41..5ea0ff9bd 100644 --- a/crates/doc/src/blend.rs +++ b/crates/doc/src/blend.rs @@ -904,12 +904,16 @@ pub struct Spine { cum: Vec, anchors: Vec, closed: bool, - path: PathData, + /// The subpath the spine was flattened from, kept so [`Spine::at_length`] can read the + /// original curves' tangents. Only the first subpath is stored: the others never affect a + /// spine. + subpath: SubPath, } impl Spine { /// Build a spine from the first subpath of `path`: flatten it to a polyline, record cumulative - /// arc lengths, and the arc length at each anchor. + /// arc lengths, and the arc length at each anchor. `None` when `path` has no subpath, or when + /// the first one flattens to fewer than two points (so it has no length to walk). pub fn new(path: &PathData) -> Option { let sp = path.subpaths.first()?; let mut pts: Vec = Vec::new(); @@ -946,7 +950,7 @@ impl Spine { if pts.len() < 2 { return None; } - Some(Self { pts, cum, anchors, closed: sp.closed, path: path.clone() }) + Some(Self { pts, cum, anchors, closed: sp.closed, subpath: sp.clone() }) } /// The spine's flattened length in arc length. pub fn length(&self) -> f64 { @@ -1012,7 +1016,7 @@ impl Spine { /// `Align to Path` jump between chords instead of following the curve smoothly. pub fn at_length(&self, s: f64) -> (Point, f64) { let s = if s.is_finite() { s.clamp(0.0, self.length()) } else { 0.0 }; - let Some(sp) = self.path.subpaths.first() else { return (self.pts.first().copied().unwrap_or_default(), 0.0) }; + let sp = &self.subpath; let n = sp.segment_count(); let mut start = 0.0; for i in 0..n {