Merge pull request #5393 from storytold/fix/sec-arclen-guarded-measures

Geometry: anchor positions and Simplify measure curves through the guarded arc length
This commit is contained in:
BFlat
2026-11-03 22:43:25 +07:00
committed by GitHub
2 changed files with 14 additions and 5 deletions
+9 -2
View File
@@ -278,7 +278,7 @@ impl SubPath {
}
let mut pos = vec![0.0];
for i in 0..self.segment_count() {
let len = kurbo::ParamCurveArclen::arclen(&self.segment(i), 1e-3);
let len = measured_arclen(&PathSeg::Cubic(self.segment(i)), 1e-3);
pos.push(pos.last().copied().unwrap_or(0.0) + len);
}
pos
@@ -621,7 +621,7 @@ 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 {
pub 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),
@@ -838,6 +838,13 @@ mod tests {
assert_eq!(c.kind, AnchorKind::Corner);
}
/// Handles too large to measure give the curve no length instead of subdividing it without end.
#[test]
fn anchor_positions_of_an_unmeasurable_curve_are_finite() {
let p = curve(Point::new(100.0, 1e300), Point::new(300.0, -1e300));
assert_eq!(p.subpaths[0].anchor_arc_positions(), [0.0, 0.0]);
}
#[test]
fn length_of_square() {
assert!((square().length() - 40.0).abs() < 1e-6);
+5 -3
View File
@@ -3,7 +3,8 @@
use std::collections::{BTreeMap, BTreeSet};
use kurbo::{CubicBez, ParamCurveArclen, Point, Rect, Vec2};
use kurbo::{CubicBez, PathSeg, Point, Rect, Vec2};
use vectorcraft_geom::path::measured_arclen;
use vectorcraft_geom::{Anchor, AnchorKind, PathData, SubPath};
use crate::boolean::{Seg, segs_to_subpath};
@@ -115,7 +116,8 @@ fn simplify_run(run: &[Seg], opts: &SimplifyOptions) -> Vec<Seg> {
}
let mut pts = Vec::new();
for (k, s) in piece.iter().enumerate() {
let len = s.c.arclen(1e-6);
// Guarded: kurbo subdivides without end on huge finite coordinates.
let len = measured_arclen(&PathSeg::Cubic(s.c), 1e-6);
let m = ((len / (tol * 0.5)).ceil() as usize).clamp(4, 256);
sample(&s.c, m, &mut pts, k == 0);
}
@@ -454,7 +456,7 @@ fn seam_error(original: &SubPath, before: &SubPath, fit: &SubPath, tol: f64) ->
let mut pts = Vec::new();
for j in (0..sp.segment_count()).filter(|&j| !seam_only || is_seam(j)) {
let c = sp.segment(j);
let m = ((c.arclen(1e-6) / (tol.max(1e-6) * 0.5)).ceil() as usize).clamp(4, 256);
let m = ((measured_arclen(&PathSeg::Cubic(c), 1e-6) / (tol.max(1e-6) * 0.5)).ceil() as usize).clamp(4, 256);
sample(&c, m, &mut pts, true);
}
pts