Path Precision Tools: a place at the end of a closed path's last segment is on the stretch at its first anchor

`span_at` read the end of a closed subpath's closing segment as past
every stretch, so a click that landed there could leave the path
unchanged. It now reads that place as the first anchor.
This commit is contained in:
dotCooCoo
2026-11-13 11:00:06 -08:00
parent e25795f73e
commit ec907e75c6
2 changed files with 6 additions and 0 deletions
@@ -585,6 +585,10 @@ fn spans_divide_a_subpath_at_its_places() {
assert_eq!(span_at(&open, &three, 0, f64::NAN), None);
let two = spans(&ring, &[(3, 0.5), (0, 0.5)]);
assert_eq!((span_at(&ring, &two, 0, 0.25), span_at(&ring, &two, 3, 0.75), span_at(&ring, &two, 2, 0.0)), (Some(1), Some(1), Some(0)));
// The closing segment's end is the first anchor: on the stretch that starts there, or runs through it.
let at_first = spans(&ring, &[(0, 0.0), (2, 0.0)]);
assert_eq!((span_at(&ring, &at_first, 3, 1.0), span_at(&ring, &at_first, 0, 0.0)), (Some(0), Some(0)));
assert_eq!((span_at(&ring, &two, 3, 1.0), span_at(&ring, &spans(&ring, &[]), 3, 1.0)), (Some(1), Some(0)));
}
#[test]
@@ -83,6 +83,8 @@ pub fn span_at(sp: &SubPath, stretches: &[Span], seg: usize, t: f64) -> Option<u
return None;
}
let u = (seg as f64 + t.clamp(0.0, 1.0)).min(nf);
// On a closed subpath the closing segment's end is the first anchor.
let u = if sp.closed && u >= nf { u - nf } else { u };
let on = |&(from, to): &Span| (from <= u && u < to) || (sp.closed && from <= u + nf && u + nf < to);
stretches.iter().position(on).or_else(|| (!sp.closed && u >= nf).then(|| stretches.len().checked_sub(1)).flatten())
}