Audit: geometry length measurement refuses coordinates the integrator cannot handle

This commit is contained in:
Dylan M. Taylor
2026-10-24 04:13:36 +07:00
committed by bflatastic
parent b0f7c5a65a
commit f4a30c1a94
3 changed files with 22 additions and 4 deletions
+3 -2
View File
@@ -22,7 +22,7 @@ impl ArcPath {
let mut segs = Vec::new();
let mut cum = 0.0;
for s in path.to_bezpath().segments() {
let l = s.arclen(ACCURACY);
let l = crate::path::measured_arclen(&s, ACCURACY);
if l.is_finite() && l > 1e-9 {
segs.push((s, cum, l));
cum += l;
@@ -51,7 +51,8 @@ impl ArcPath {
let s = if self.closed && self.len > 0.0 { s.rem_euclid(self.len) } else { s.clamp(0.0, self.len) };
let i = self.segs.partition_point(|(_, c, _)| *c <= s).saturating_sub(1);
let Some(&(seg, c, l)) = self.segs.get(i) else { return (Point::ZERO, Vec2::new(1.0, 0.0)) };
let t = seg.inv_arclen((s - c).min(l), ACCURACY).clamp(0.0, 1.0);
// (The solver's precision is relative to the segment's length: a long one needs a looser one.)
let t = seg.inv_arclen((s - c).min(l), ACCURACY.max(l * 1e-9)).clamp(0.0, 1.0);
let p = seg.eval(t);
let (t0, t1) = ((t - 1e-4).max(0.0), (t + 1e-4).min(1.0));
let d = seg.eval(t1) - seg.eval(t0);
+1 -1
View File
@@ -39,7 +39,7 @@ impl Side {
/// The side's length.
fn length(&self) -> f64 {
if self.straight() { self.curve.p0.distance(self.curve.p3) } else { self.curve.arclen(ARCLEN_ACCURACY) }
if self.straight() { self.curve.p0.distance(self.curve.p3) } else { crate::path::measured_arclen(&kurbo::PathSeg::Cubic(self.curve), ARCLEN_ACCURACY) }
}
/// The parameter `s` along the side from the corner, the point there and the direction the
+18 -1
View File
@@ -561,10 +561,27 @@ impl PathData {
}
/// Total length of all segments.
pub fn length(&self) -> f64 {
self.to_bezpath().segments().map(|s| kurbo::ParamCurveArclen::arclen(&s, 1e-6)).sum()
self.to_bezpath().segments().map(|s| measured_arclen(&s, 1e-6)).sum()
}
}
/// The largest x or y, in size, of a segment's control points for which its length is measured.
/// Document coordinates are far inside it; past it (or not finite) the integrator's arithmetic
/// overflows and it subdivides without end.
const MEASURE_LIMIT: f64 = 1e9;
/// The length of `seg` to `accuracy`; 0 for a segment whose control points are not finite or past
/// [`MEASURE_LIMIT`].
pub(crate) fn measured_arclen(seg: &PathSeg, accuracy: f64) -> f64 {
let ok = |p: Point| p.x.is_finite() && p.y.is_finite() && p.x.abs() <= MEASURE_LIMIT && p.y.abs() <= MEASURE_LIMIT;
let fits = match seg {
PathSeg::Line(l) => ok(l.p0) && ok(l.p1),
PathSeg::Quad(q) => ok(q.p0) && ok(q.p1) && ok(q.p2),
PathSeg::Cubic(c) => ok(c.p0) && ok(c.p1) && ok(c.p2) && ok(c.p3),
};
if fits { kurbo::ParamCurveArclen::arclen(seg, accuracy) } else { 0.0 }
}
/// The largest x or y, in size, of a segment's control points for which [`segment_bounds`]
/// measures the segment by its extrema. kurbo adds up to eight coordinates together when it finds a
/// segment's extrema and evaluates the segment there, and a sum of eight coordinates of this size or