mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 01:50:22 -06:00
SVG: a thin ellipse far from the origin imports with standard handles
An ellipse whose radius is smaller than the point tolerance at its coordinates, but at least two f32 steps, now imports as the four-anchor ellipse instead of usvg's curves with rounded handles.
This commit is contained in:
@@ -156,14 +156,16 @@ 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: 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()));
|
||||
// usvg works in f32: points match within a few of its steps at the size of the coordinates, and
|
||||
// a radius of fewer than two steps does not survive the conversion.
|
||||
let step = f64::from(f32::EPSILON) * [x0, x1, y0, y1].iter().fold(1.0f64, |m, v| m.max(v.abs()));
|
||||
let tol = 16.0 * step;
|
||||
let axes = [Point::new(x1, c.y), Point::new(c.x, y1), Point::new(x0, c.y), Point::new(c.x, y0)];
|
||||
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
|
||||
|| rx < 2.0 * step
|
||||
|| ry < 2.0 * step
|
||||
|| ends.first().is_none_or(|s| s.distance(axes[0]) > tol)
|
||||
|| !axes.iter().all(|a| ends.iter().any(|q| q.distance(*a) <= tol))
|
||||
|| !ends.iter().all(on_ellipse)
|
||||
|
||||
@@ -164,9 +164,10 @@ fn circles_and_ellipses_import_as_the_ellipse_drawn() {
|
||||
<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"/>
|
||||
<circle cx="40000000" cy="0" r="400"/></svg>"#;
|
||||
<circle cx="40000000" cy="0" r="400"/>
|
||||
<ellipse cx="1000000" cy="1000000" rx="36" ry="1"/></svg>"#;
|
||||
let back = paths(&import(svg).unwrap());
|
||||
assert_eq!(back.len(), 4);
|
||||
assert_eq!(back.len(), 5);
|
||||
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);
|
||||
@@ -182,6 +183,11 @@ fn circles_and_ellipses_import_as_the_ellipse_drawn() {
|
||||
// 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:?}");
|
||||
// A radius of a few of f32's steps at its coordinates, with handles of the standard length.
|
||||
let thin = &back[4].subpaths[0].anchors;
|
||||
let handle = thin.first().map(|a| a.h_out - a.p);
|
||||
assert!(thin.len() == 4 && thin[0].p.distance(Point::new(1_000_036.0, 1_000_000.0)) < 0.25, "{thin:?}");
|
||||
assert!(handle.is_some_and(|h| h.x.abs() < 1e-6 && (h.y - shapes::KAPPA).abs() < 1e-6), "{thin:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user