mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 13:07:41 +00:00
SVG: a small circle far from the origin imports as a four-anchor ellipse
The tolerance that compares the points usvg makes of a <circle> or <ellipse> grew with the center's distance from the origin, so a circle of radius 400 at x 40,000,000 fell back to eight anchors. The tolerance is now a few f32 steps at the size of the coordinates, and the on-ellipse check allows the same steps relative to the smaller radius.
This commit is contained in:
@@ -156,10 +156,11 @@ pub(super) fn drawn_ellipse(p: &usvg::tiny_skia_path::Path) -> Option<PathData>
|
||||
let (x0, x1) = ends.iter().fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), q| (lo.min(q.x), hi.max(q.x)));
|
||||
let (y0, y1) = ends.iter().fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), q| (lo.min(q.y), hi.max(q.y)));
|
||||
let (c, rx, ry) = (Point::new((x0 + x1) / 2.0, (y0 + y1) / 2.0), (x1 - x0) / 2.0, (y1 - y0) / 2.0);
|
||||
// usvg works in f32: the tolerance follows the size of the numbers.
|
||||
let tol = 1e-5 * (c.x.abs() + c.y.abs() + rx + ry).max(1.0);
|
||||
// usvg works in f32: points match within a few of its steps at the size of the coordinates.
|
||||
let tol = 16.0 * f64::from(f32::EPSILON) * [x0, x1, y0, y1].iter().fold(1.0f64, |m, v| m.max(v.abs()));
|
||||
let axes = [Point::new(x1, c.y), Point::new(c.x, y1), Point::new(x0, c.y), Point::new(c.x, y0)];
|
||||
let on_ellipse = |q: &Point| (((q.x - c.x) / rx).powi(2) + ((q.y - c.y) / ry).powi(2) - 1.0).abs() <= 1e-3;
|
||||
let off = 1e-3 + 4.0 * tol / rx.min(ry);
|
||||
let on_ellipse = |q: &Point| (((q.x - c.x) / rx).powi(2) + ((q.y - c.y) / ry).powi(2) - 1.0).abs() <= off;
|
||||
if !closed
|
||||
|| rx <= tol
|
||||
|| ry <= tol
|
||||
|
||||
@@ -163,9 +163,10 @@ fn circles_and_ellipses_import_as_the_ellipse_drawn() {
|
||||
let svg = r#"<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
|
||||
<circle cx="50" cy="50" r="40" transform="translate(10 20)"/>
|
||||
<ellipse cx="100" cy="100" rx="60" ry="20" transform="rotate(30 100 100)"/>
|
||||
<ellipse cx="36.255" cy="154.118" rx="36.255" ry="5.421"/></svg>"#;
|
||||
<ellipse cx="36.255" cy="154.118" rx="36.255" ry="5.421"/>
|
||||
<circle cx="40000000" cy="0" r="400"/></svg>"#;
|
||||
let back = paths(&import(svg).unwrap());
|
||||
assert_eq!(back.len(), 3);
|
||||
assert_eq!(back.len(), 4);
|
||||
let circle = &back[0].subpaths[0].anchors;
|
||||
let want: Vec<Point> = [(100.0, 70.0), (60.0, 110.0), (20.0, 70.0), (60.0, 30.0)].map(|(x, y)| Point::new(x, y)).into();
|
||||
assert_eq!(circle.len(), 4);
|
||||
@@ -178,4 +179,7 @@ fn circles_and_ellipses_import_as_the_ellipse_drawn() {
|
||||
// However many curves the arcs of a flat ellipse make.
|
||||
let flat = &back[2].subpaths[0].anchors;
|
||||
assert!(flat.len() == 4 && flat[0].p.distance(Point::new(72.51, 154.118)) < 1e-3, "{flat:?}");
|
||||
// A small circle far from the origin too.
|
||||
let far = &back[3].subpaths[0].anchors;
|
||||
assert!(far.len() == 4 && far[0].p.distance(Point::new(40_000_400.0, 0.0)) < 8.0, "{far:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user