mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Audit: geometry length measurement refuses coordinates the integrator cannot handle
This commit is contained in:
committed by
bflatastic
parent
b0f7c5a65a
commit
f4a30c1a94
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user