From c8266b42f30abe78d1845403fc99847d1776e6dd Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:41:18 +0100 Subject: [PATCH 01/31] run rustfmt --- benches/sweep.rs | 68 +- src/arc/circle.rs | 126 +- src/arc/mod.rs | 2 +- src/bezier/basis.rs | 65 +- src/bezier/bounds.rs | 37 +- src/bezier/characteristics.rs | 288 +- src/bezier/curve.rs | 176 +- src/bezier/deform.rs | 62 +- src/bezier/derivative.rs | 19 +- src/bezier/distort.rs | 86 +- src/bezier/fit.rs | 291 +- src/bezier/intersection/curve_curve_clip.rs | 212 +- src/bezier/intersection/curve_line.rs | 125 +- src/bezier/intersection/fat_line.rs | 542 ++- src/bezier/intersection/mod.rs | 4 +- src/bezier/intersection/self_intersection.rs | 34 +- src/bezier/length.rs | 35 +- src/bezier/mod.rs | 52 +- src/bezier/normal.rs | 46 +- src/bezier/offset.rs | 8 +- src/bezier/offset_lms.rs | 102 +- src/bezier/offset_scaling.rs | 247 +- src/bezier/overlaps.rs | 38 +- src/bezier/path/algorithms/fill_concave.rs | 241 +- src/bezier/path/algorithms/fill_convex.rs | 247 +- src/bezier/path/algorithms/fill_settings.rs | 20 +- src/bezier/path/algorithms/mod.rs | 4 +- src/bezier/path/arithmetic/add.rs | 93 +- src/bezier/path/arithmetic/chain.rs | 54 +- src/bezier/path/arithmetic/chain_add.rs | 33 +- src/bezier/path/arithmetic/cut.rs | 51 +- src/bezier/path/arithmetic/full_intersect.rs | 74 +- src/bezier/path/arithmetic/intersect.rs | 51 +- src/bezier/path/arithmetic/mod.rs | 14 +- src/bezier/path/arithmetic/ray_cast.rs | 126 +- src/bezier/path/arithmetic/sub.rs | 51 +- src/bezier/path/bounds.rs | 14 +- src/bezier/path/graph_path/edge.rs | 117 +- src/bezier/path/graph_path/edge_ref.rs | 33 +- src/bezier/path/graph_path/mod.rs | 496 ++- src/bezier/path/graph_path/path_collision.rs | 515 ++- src/bezier/path/graph_path/ray_collision.rs | 111 +- src/bezier/path/graph_path/test.rs | 337 +- src/bezier/path/intersection.rs | 74 +- src/bezier/path/is_clockwise.rs | 18 +- src/bezier/path/mod.rs | 38 +- src/bezier/path/path.rs | 72 +- src/bezier/path/path_builder.rs | 24 +- src/bezier/path/point.rs | 131 +- src/bezier/path/ray.rs | 596 ++- src/bezier/path/to_curves.rs | 21 +- src/bezier/search.rs | 41 +- src/bezier/section.rs | 85 +- src/bezier/solve.rs | 71 +- src/bezier/subdivide.rs | 23 +- src/bezier/tangent.rs | 17 +- src/bezier/walk.rs | 148 +- src/debug/graph_path_debug.rs | 136 +- src/debug/mod.rs | 4 +- src/debug/path_to_string.rs | 28 +- src/geo/bounding_box.rs | 45 +- src/geo/coordinate.rs | 134 +- src/geo/coordinate_ext.rs | 14 +- src/geo/geo.rs | 2 +- src/geo/has_bounds.rs | 6 +- src/geo/mod.rs | 21 +- src/geo/sweep.rs | 166 +- src/lib.rs | 23 +- src/line/coefficients.rs | 42 +- src/line/intersection.rs | 101 +- src/line/line.rs | 60 +- src/line/mod.rs | 12 +- src/line/to_curve.rs | 15 +- src/test_assert.rs | 7 +- tests/bezier/algorithms/fill_concave.rs | 332 +- tests/bezier/algorithms/fill_convex.rs | 129 +- tests/bezier/algorithms/mod.rs | 2 +- tests/bezier/basis.rs | 6 +- tests/bezier/bounds.rs | 14 +- tests/bezier/characteristics.rs | 32 +- tests/bezier/curve_intersection_clip.rs | 646 +++- tests/bezier/deform.rs | 170 +- tests/bezier/distort.rs | 36 +- tests/bezier/intersection.rs | 291 +- tests/bezier/length.rs | 52 +- tests/bezier/mod.rs | 54 +- tests/bezier/normal.rs | 120 +- tests/bezier/offset.rs | 260 +- tests/bezier/overlaps.rs | 134 +- tests/bezier/path/arithmetic_add.rs | 404 +- tests/bezier/path/arithmetic_chain_add.rs | 219 +- .../path/arithmetic_complicated_paths.rs | 3290 ++++++++++++++--- tests/bezier/path/arithmetic_cut.rs | 27 +- tests/bezier/path/arithmetic_intersect.rs | 304 +- tests/bezier/path/arithmetic_sub.rs | 340 +- tests/bezier/path/bounds.rs | 2 +- tests/bezier/path/graph_path.rs | 667 ++-- tests/bezier/path/intersection.rs | 53 +- tests/bezier/path/is_clockwise.rs | 2 +- tests/bezier/path/mod.rs | 20 +- tests/bezier/path/path.rs | 2 +- tests/bezier/path/point.rs | 62 +- tests/bezier/path/rays.rs | 383 +- tests/bezier/path/svg.rs | 28 +- tests/bezier/path/to_curves.rs | 48 +- tests/bezier/search.rs | 12 +- tests/bezier/section.rs | 72 +- tests/bezier/self_intersection.rs | 44 +- tests/bezier/solve.rs | 67 +- tests/bezier/subdivide.rs | 8 +- tests/bezier/tangent.rs | 10 +- tests/bezier/walk.rs | 229 +- tests/bounds.rs | 2 +- tests/coordinates.rs | 15 +- tests/line/coefficients.rs | 50 +- tests/line/intersection.rs | 82 +- tests/line/mod.rs | 4 +- tests/line/to_curve.rs | 8 +- tests/readme.rs | 11 +- tests/sweep.rs | 139 +- 120 files changed, 11427 insertions(+), 4977 deletions(-) diff --git a/benches/sweep.rs b/benches/sweep.rs index c030aded..dfb68763 100644 --- a/benches/sweep.rs +++ b/benches/sweep.rs @@ -1,38 +1,51 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use flo_curves::geo::*; use flo_curves::bezier::path::*; +use flo_curves::geo::*; use rand::prelude::*; -use std::cmp::{Ordering}; +use std::cmp::Ordering; fn sweep(n: usize) { - let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let mut bounds = (0..n).into_iter() + let mut rng = StdRng::from_seed([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]); + let mut bounds = (0..n) + .into_iter() .map(|_| { let x = rng.gen::() * 900.0; let y = rng.gen::() * 900.0; let w = rng.gen::() * 400.0; let h = rng.gen::() * 400.0; - Bounds::from_min_max(Coord2(x, y), Coord2(x+w, y+h)) + Bounds::from_min_max(Coord2(x, y), Coord2(x + w, y + h)) }) .collect::>(); - bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let _ = sweep_self(bounds.iter()).collect::>(); } fn sweep_slow(n: usize) { - let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let bounds = (0..n).into_iter() + let mut rng = StdRng::from_seed([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]); + let bounds = (0..n) + .into_iter() .map(|_| { let x = rng.gen::() * 900.0; let y = rng.gen::() * 900.0; let w = rng.gen::() * 400.0; let h = rng.gen::() * 400.0; - Bounds::from_min_max(Coord2(x, y), Coord2(x+w, y+h)) + Bounds::from_min_max(Coord2(x, y), Coord2(x + w, y + h)) }) .collect::>(); @@ -40,7 +53,9 @@ fn sweep_slow(n: usize) { for i1 in 0..bounds.len() { for i2 in 0..i1 { - if i1 == i2 { continue; } + if i1 == i2 { + continue; + } if bounds[i1].overlaps(&bounds[i2]) { slow_collisions.push((&bounds[i1], &bounds[i2])); @@ -50,9 +65,9 @@ fn sweep_slow(n: usize) { } fn create_graph_path(rng: &mut StdRng, n: usize) -> GraphPath { - let mut x = 100.0; - let mut y = 100.0; - let mut path_builder = BezierPathBuilder::::start(Coord2(x, y)); + let mut x = 100.0; + let mut y = 100.0; + let mut path_builder = BezierPathBuilder::::start(Coord2(x, y)); for _ in 0..n { let xo = rng.gen::() * 50.0; @@ -64,8 +79,8 @@ fn create_graph_path(rng: &mut StdRng, n: usize) -> GraphPath { path_builder = path_builder.line_to(Coord2(x, y)); } - let path = path_builder.build(); - let graph_path = GraphPath::from_path(&path, ()); + let path = path_builder.build(); + let graph_path = GraphPath::from_path(&path, ()); graph_path } @@ -79,12 +94,19 @@ fn merge_paths(path1: GraphPath, path2: GraphPath) { } fn criterion_benchmark(c: &mut Criterion) { - let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let graph_path = create_graph_path(&mut rng, 1000); - let merge_path = create_graph_path(&mut rng, 500); - - c.bench_function("detect_collisions 1000", |b| b.iter(|| detect_collisions(black_box(graph_path.clone())))); - c.bench_function("merge_paths 1000", |b| b.iter(|| merge_paths(black_box(graph_path.clone()), black_box(merge_path.clone())))); + let mut rng = StdRng::from_seed([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]); + let graph_path = create_graph_path(&mut rng, 1000); + let merge_path = create_graph_path(&mut rng, 500); + + c.bench_function("detect_collisions 1000", |b| { + b.iter(|| detect_collisions(black_box(graph_path.clone()))) + }); + c.bench_function("merge_paths 1000", |b| { + b.iter(|| merge_paths(black_box(graph_path.clone()), black_box(merge_path.clone()))) + }); c.bench_function("sweep 10", |b| b.iter(|| sweep(black_box(10)))); c.bench_function("sweep_slow 10", |b| b.iter(|| sweep_slow(black_box(10)))); @@ -93,7 +115,9 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("sweep_slow 100", |b| b.iter(|| sweep_slow(black_box(100)))); c.bench_function("sweep 1000", |b| b.iter(|| sweep(black_box(1000)))); - c.bench_function("sweep_slow 1000", |b| b.iter(|| sweep_slow(black_box(1000)))); + c.bench_function("sweep_slow 1000", |b| { + b.iter(|| sweep_slow(black_box(1000))) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/src/arc/circle.rs b/src/arc/circle.rs index b3dcabcd..79536c38 100644 --- a/src/arc/circle.rs +++ b/src/arc/circle.rs @@ -1,5 +1,5 @@ -use super::super::bezier::*; use super::super::bezier::path::*; +use super::super::bezier::*; use std::f64; @@ -8,21 +8,21 @@ use std::f64; /// /// Represents a circle in 2 dimensions -/// +/// #[derive(Clone, Copy)] -pub struct Circle { +pub struct Circle { /// The center of this circle pub center: Coord, /// The radius of this circle - pub radius: f64 + pub radius: f64, } /// /// Represents an arc of a circle in 2 dimensions -/// +/// #[derive(Clone, Copy)] -pub struct CircularArc<'a, Coord: 'a+Coordinate2D+Coordinate> { +pub struct CircularArc<'a, Coord: 'a + Coordinate2D + Coordinate> { /// The circle that this is an arc of circle: &'a Circle, @@ -30,89 +30,97 @@ pub struct CircularArc<'a, Coord: 'a+Coordinate2D+Coordinate> { start_radians: f64, /// The end point of this arc, in radians - end_radians: f64 + end_radians: f64, } -impl Circle { +impl Circle { /// /// Creates a new circle with a center and a radius - /// + /// pub fn new(center: Coord, radius: f64) -> Circle { Circle { center: center, - radius: radius + radius: radius, } } /// /// Returns an object representing an arc from this circle - /// + /// pub fn arc<'a>(&'a self, start_radians: f64, end_radians: f64) -> CircularArc<'a, Coord> { CircularArc { - circle: self, - start_radians: start_radians, - end_radians: end_radians + circle: self, + start_radians: start_radians, + end_radians: end_radians, } } /// /// Returns a set of bezier curves that approximate this circle - /// - pub fn to_curves>(&self) -> Vec { + /// + pub fn to_curves>(&self) -> Vec { // Angles to put the curves at (we need 4 curves for a decent approximation of a circle) - let start_angle = f64::consts::PI/4.0; - let section_angle = f64::consts::PI/2.0; - let angles = [ - start_angle, - start_angle + section_angle, - start_angle + section_angle*2.0, - start_angle + section_angle*3.0]; - + let start_angle = f64::consts::PI / 4.0; + let section_angle = f64::consts::PI / 2.0; + let angles = [ + start_angle, + start_angle + section_angle, + start_angle + section_angle * 2.0, + start_angle + section_angle * 3.0, + ]; + // Convert the angles into curves - angles.iter() - .map(|angle| self.arc(*angle, angle+section_angle).to_bezier_curve()) + angles + .iter() + .map(|angle| self.arc(*angle, angle + section_angle).to_bezier_curve()) .collect() } /// /// Returns a path that approximates this circle - /// - pub fn to_path>(&self) -> P { + /// + pub fn to_path>(&self) -> P { let curves = self.to_curves::>(); - P::from_points(curves[0].start_point(), curves.into_iter().map(|curve| { - let (cp1, cp2) = curve.control_points(); - let end_point = curve.end_point(); + P::from_points( + curves[0].start_point(), + curves.into_iter().map(|curve| { + let (cp1, cp2) = curve.control_points(); + let end_point = curve.end_point(); - (cp1, cp2, end_point) - })) + (cp1, cp2, end_point) + }), + ) } } -impl<'a, Coord: Coordinate2D+Coordinate> CircularArc<'a, Coord> { +impl<'a, Coord: Coordinate2D + Coordinate> CircularArc<'a, Coord> { /// /// Converts this arc to a bezier curve - /// + /// /// If this arc covers an angle > 90 degrees, the curve will /// be very inaccurate. - /// - pub fn to_bezier_curve>(&self) -> Curve { + /// + pub fn to_bezier_curve>(&self) -> Curve { // Algorithm described here: https://www.tinaja.com/glib/bezcirc2.pdf // Curve for the unit arc with its center at (1,0) - let theta = self.end_radians - self.start_radians; - let (x0, y0) = ((theta/2.0).cos(), (theta/2.0).sin()); - let (x1, y1) = ((4.0-x0)/3.0, ((1.0-x0)*(3.0-x0)/(3.0*y0))); - let (x2, y2) = (x1, -y1); - let (x3, y3) = (x0, -y0); + let theta = self.end_radians - self.start_radians; + let (x0, y0) = ((theta / 2.0).cos(), (theta / 2.0).sin()); + let (x1, y1) = ((4.0 - x0) / 3.0, ((1.0 - x0) * (3.0 - x0) / (3.0 * y0))); + let (x2, y2) = (x1, -y1); + let (x3, y3) = (x0, -y0); // Rotate so the curve starts at start_radians fn rotate(x: f64, y: f64, theta: f64) -> (f64, f64) { let (cos_theta, sin_theta) = (theta.cos(), theta.sin()); - (x*cos_theta + y*sin_theta, x*-sin_theta + y*cos_theta) + ( + x * cos_theta + y * sin_theta, + x * -sin_theta + y * cos_theta, + ) } - let angle = -(f64::consts::PI/2.0-(theta/2.0)); + let angle = -(f64::consts::PI / 2.0 - (theta / 2.0)); let angle = angle + self.start_radians; let (x0, y0) = rotate(x0, y0, angle); @@ -122,17 +130,17 @@ impl<'a, Coord: Coordinate2D+Coordinate> CircularArc<'a, Coord> { // Scale by radius let radius = self.circle.radius; - let (x0, y0) = (x0*radius, y0*radius); - let (x1, y1) = (x1*radius, y1*radius); - let (x2, y2) = (x2*radius, y2*radius); - let (x3, y3) = (x3*radius, y3*radius); + let (x0, y0) = (x0 * radius, y0 * radius); + let (x1, y1) = (x1 * radius, y1 * radius); + let (x2, y2) = (x2 * radius, y2 * radius); + let (x3, y3) = (x3 * radius, y3 * radius); // Translate by center let center = &self.circle.center; - let (x0, y0) = (x0+center.x(), y0+center.y()); - let (x1, y1) = (x1+center.x(), y1+center.y()); - let (x2, y2) = (x2+center.x(), y2+center.y()); - let (x3, y3) = (x3+center.x(), y3+center.y()); + let (x0, y0) = (x0 + center.x(), y0 + center.y()); + let (x1, y1) = (x1 + center.x(), y1 + center.y()); + let (x2, y2) = (x2 + center.x(), y2 + center.y()); + let (x3, y3) = (x3 + center.x(), y3 + center.y()); // Create the curve let p0 = Coord::from_components(&[x0, y0]); @@ -151,8 +159,8 @@ mod test { #[test] fn can_convert_unit_arc() { - let circle = Circle::new(Coord2(0.0, 0.0), 1.0); - let arc = circle.arc(0.0, f64::consts::PI/2.0); + let circle = Circle::new(Coord2(0.0, 0.0), 1.0); + let arc = circle.arc(0.0, f64::consts::PI / 2.0); let curve: Curve<_> = arc.to_bezier_curve(); assert!(curve.start_point().distance_to(&Coord2(0.0, 1.0)) < 0.01); @@ -165,9 +173,9 @@ mod test { for curve in circle.to_curves::>() { for t in 0..=10 { - let t = (t as f64)/10.0; + let t = (t as f64) / 10.0; let p = curve.point_at_pos(t); - assert!((p.distance_to(&Coord2(0.0, 0.0))-1.0).abs() < 0.01); + assert!((p.distance_to(&Coord2(0.0, 0.0)) - 1.0).abs() < 0.01); } } } @@ -178,10 +186,10 @@ mod test { for curve in path_to_curves::<_, Curve<_>>(&circle.to_path::()) { for t in 0..=10 { - let t = (t as f64)/10.0; + let t = (t as f64) / 10.0; let p = curve.point_at_pos(t); - assert!((p.distance_to(&Coord2(5.0, 5.0))-4.0).abs() < 0.01); + assert!((p.distance_to(&Coord2(5.0, 5.0)) - 4.0).abs() < 0.01); } } } -} \ No newline at end of file +} diff --git a/src/arc/mod.rs b/src/arc/mod.rs index 3d8be2ad..0f222ff2 100644 --- a/src/arc/mod.rs +++ b/src/arc/mod.rs @@ -1,6 +1,6 @@ //! //! # Describing circular arcs -//! +//! //! The `arc` module provides routines for describing circular arcs and converting them to bezier //! curves. //! diff --git a/src/bezier/basis.rs b/src/bezier/basis.rs index e4248afd..378de1e3 100644 --- a/src/bezier/basis.rs +++ b/src/bezier/basis.rs @@ -2,67 +2,78 @@ use super::super::geo::*; /// /// Computes the bezier coefficients (A, B, C, D) for a bezier curve -/// -pub fn bezier_coefficients(dimension: usize, w1: &Point, w2: &Point, w3: &Point, w4: &Point) -> (f64, f64, f64, f64) { +/// +pub fn bezier_coefficients( + dimension: usize, + w1: &Point, + w2: &Point, + w3: &Point, + w4: &Point, +) -> (f64, f64, f64, f64) { let w1 = w1.get(dimension); let w2 = w2.get(dimension); let w3 = w3.get(dimension); let w4 = w4.get(dimension); ( - w4-(3.0*w3)+(3.0*w2)-w1, - (3.0*w3)-(6.0*w2)+3.0*w1, - 3.0*w2-3.0*w1, - w1 + w4 - (3.0 * w3) + (3.0 * w2) - w1, + (3.0 * w3) - (6.0 * w2) + 3.0 * w1, + 3.0 * w2 - 3.0 * w1, + w1, ) } /// /// The cubic bezier weighted basis function -/// +/// #[inline] pub fn basis(t: f64, w1: Point, w2: Point, w3: Point, w4: Point) -> Point { - let t_squared = t*t; - let t_cubed = t_squared*t; + let t_squared = t * t; + let t_cubed = t_squared * t; - let one_minus_t = 1.0-t; - let one_minus_t_squared = one_minus_t*one_minus_t; - let one_minus_t_cubed = one_minus_t_squared*one_minus_t; + let one_minus_t = 1.0 - t; + let one_minus_t_squared = one_minus_t * one_minus_t; + let one_minus_t_cubed = one_minus_t_squared * one_minus_t; - w1*one_minus_t_cubed - + w2*3.0*one_minus_t_squared*t - + w3*3.0*one_minus_t*t_squared - + w4*t_cubed + w1 * one_minus_t_cubed + + w2 * 3.0 * one_minus_t_squared * t + + w3 * 3.0 * one_minus_t * t_squared + + w4 * t_cubed } - /// /// de Casteljau's algorithm for cubic bezier curves -/// +/// #[inline] -pub fn de_casteljau4(t: f64, w1: Point, w2: Point, w3: Point, w4: Point) -> Point { - let wn1 = w1*(1.0-t) + w2*t; - let wn2 = w2*(1.0-t) + w3*t; - let wn3 = w3*(1.0-t) + w4*t; +pub fn de_casteljau4( + t: f64, + w1: Point, + w2: Point, + w3: Point, + w4: Point, +) -> Point { + let wn1 = w1 * (1.0 - t) + w2 * t; + let wn2 = w2 * (1.0 - t) + w3 * t; + let wn3 = w3 * (1.0 - t) + w4 * t; de_casteljau3(t, wn1, wn2, wn3) } /// /// de Casteljau's algorithm for quadratic bezier curves -/// +/// #[inline] pub fn de_casteljau3(t: f64, w1: Point, w2: Point, w3: Point) -> Point { - let wn1 = w1*(1.0-t) + w2*t; - let wn2 = w2*(1.0-t) + w3*t; + let wn1 = w1 * (1.0 - t) + w2 * t; + let wn2 = w2 * (1.0 - t) + w3 * t; de_casteljau2(t, wn1, wn2) } /// /// de Casteljau's algorithm for lines -/// +/// #[inline] pub fn de_casteljau2(t: f64, w1: Point, w2: Point) -> Point { - w1*(1.0-t) + w2*t + w1 * (1.0 - t) + w2 * t } diff --git a/src/bezier/bounds.rs b/src/bezier/bounds.rs index f8b1e5a4..9229d91a 100644 --- a/src/bezier/bounds.rs +++ b/src/bezier/bounds.rs @@ -1,10 +1,10 @@ -use super::basis::*; use super::super::geo::*; +use super::basis::*; /// /// Finds the t values of the extremities of a curve (these are the points at which /// the x or y value is at a minimum or maximum) -/// +/// pub fn find_extremities(w1: Point, w2: Point, w3: Point, w4: Point) -> Vec { // The 't' values where this curve has extremities we need to examine let mut t_extremes = vec![1.0]; @@ -18,24 +18,28 @@ pub fn find_extremities(w1: Point, w2: Point, w3: Point, w4: let p4 = w4.get(component_index); // Compute the bezier coefficients - let a = (-p1 + p2*3.0 - p3*3.0 + p4)*3.0; - let b = (p1 - p2*2.0 + p3)*6.0; - let c = (p2 - p1)*3.0; + let a = (-p1 + p2 * 3.0 - p3 * 3.0 + p4) * 3.0; + let b = (p1 - p2 * 2.0 + p3) * 6.0; + let c = (p2 - p1) * 3.0; // Extremities are points at which the curve has a 0 gradient (in any of its dimensions) - let root1 = (-b + f64::sqrt(b*b - a*c*4.0)) / (a*2.0); - let root2 = (-b - f64::sqrt(b*b - a*c*4.0)) / (a*2.0); + let root1 = (-b + f64::sqrt(b * b - a * c * 4.0)) / (a * 2.0); + let root2 = (-b - f64::sqrt(b * b - a * c * 4.0)) / (a * 2.0); - if root1 > 0.0 && root1 < 1.0 { t_extremes.push(root1); } - if root2 > 0.0 && root2 < 1.0 { t_extremes.push(root2); } + if root1 > 0.0 && root1 < 1.0 { + t_extremes.push(root1); + } + if root2 > 0.0 && root2 < 1.0 { + t_extremes.push(root2); + } // We also solve for the second derivative - let aa = 2.0*(b-a); - let bb = 2.0*(c-a); + let aa = 2.0 * (b - a); + let bb = 2.0 * (c - a); // Solve for a'*t+b = 0 (0-b/a') if aa != 0.0 { - let root3 = -bb/aa; + let root3 = -bb / aa; if root3 > 0.0 && root3 < 1.0 { t_extremes.push(root3); } @@ -47,8 +51,13 @@ pub fn find_extremities(w1: Point, w2: Point, w3: Point, w4: /// /// Finds the upper and lower points in a cubic curve's bounding box -/// -pub fn bounding_box4>(w1: Point, w2: Point, w3: Point, w4: Point) -> Bounds { +/// +pub fn bounding_box4>( + w1: Point, + w2: Point, + w3: Point, + w4: Point, +) -> Bounds { // The 't' values where this curve has extremities we need to examine let t_extremes = find_extremities(w1, w2, w3, w4); diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index 762b6ec1..21015d03 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -1,8 +1,8 @@ -use super::curve::*; -use super::intersection::*; +use super::super::consts::*; use super::super::geo::*; use super::super::line::*; -use super::super::consts::*; +use super::curve::*; +use super::intersection::*; use std::f64; @@ -39,7 +39,7 @@ pub enum CurveCategory { Cusp, /// A curve containing a loop - Loop + Loop, } /// @@ -69,38 +69,42 @@ pub enum CurveFeatures { Cusp, /// A curve containing a loop and the two t values where it self-intersects - Loop(f64, f64) + Loop(f64, f64), } /// /// Computes an affine transform that translates from an arbitrary bezier curve to one that has the first three control points /// fixed at w1 = (0,0), w2 = (0, 1) and w3 = (1, 1). -/// +/// /// Bezier curves maintain their properties when transformed so this provides a curve with equivalent properties to the input /// curve but only a single free point (w4). This will return 'None' for the degenerate cases: where two points overlap or /// where the points are collinear. /// -fn canonical_curve_transform(w1: &Point, w2: &Point, w3: &Point) -> Option<(f64, f64, f64, f64, f64, f64)> { +fn canonical_curve_transform( + w1: &Point, + w2: &Point, + w3: &Point, +) -> Option<(f64, f64, f64, f64, f64, f64)> { // Fetch the coordinates let (x0, y0) = (w1.x(), w1.y()); let (x1, y1) = (w2.x(), w2.y()); let (x2, y2) = (w3.x(), w3.y()); - let a_divisor = (y2-y1)*(x0-x1)-(x2-x1)*(y0-y1); + let a_divisor = (y2 - y1) * (x0 - x1) - (x2 - x1) * (y0 - y1); if a_divisor.abs() > SMALL_DIVISOR { // Transform is: - // + // // [ a, b, c ] [ x ] // [ d, e, f ] . [ y ] // [ 0, 0, 1 ] [ 1 ] - // + // // This will move w1 to 0,0, w2 to 0, 1 and w3 to 1, 1, which will form our canonical curve that we use for the classification algorithm - let a = (-(y0-y1)) / a_divisor; - let b = (-(x0-x1)) / ((x2-x1)*(y0-y1)-(y2-y1)*(x0-x1)); - let c = -a*x0 - b*y0; - let d = (y1-y2) / ((x0-x1)*(y2-y1) - (x2-x1)*(y0-y1)); - let e = (x1-x2) / ((y0-y1)*(x2-x1) - (y2-y1)*(x0-x1)); - let f = -d*x0 - e*y0; + let a = (-(y0 - y1)) / a_divisor; + let b = (-(x0 - x1)) / ((x2 - x1) * (y0 - y1) - (y2 - y1) * (x0 - x1)); + let c = -a * x0 - b * y0; + let d = (y1 - y2) / ((x0 - x1) * (y2 - y1) - (x2 - x1) * (y0 - y1)); + let e = (x1 - x2) / ((y0 - y1) * (x2 - x1) - (y2 - y1) * (x0 - x1)); + let f = -d * x0 - e * y0; Some((a, b, c, d, e, f)) } else { @@ -111,19 +115,24 @@ fn canonical_curve_transform(w1: &Point, w2: &Po /// /// Converts a set of points to a 'canonical' curve -/// +/// /// This is the curve such that w1 = (0.0), w2 = (1, 0) and w3 = (1, 1), if such a curve exists. The return value is the point w4 /// for this curve. /// -fn to_canonical_curve(w1: &Point, w2: &Point, w3: &Point, w4: &Point) -> Option { +fn to_canonical_curve( + w1: &Point, + w2: &Point, + w3: &Point, + w4: &Point, +) -> Option { // Retrieve the affine transform for the curve if let Some((a, b, c, d, e, f)) = canonical_curve_transform(w1, w2, w3) { // Calculate the free point w4 based on the transform - let x4 = w4.x(); - let y4 = w4.y(); + let x4 = w4.x(); + let y4 = w4.y(); - let x = a*x4 + b*y4 + c; - let y = d*x4 + e*y4 + f; + let x = a * x4 + b * y4 + c; + let y = d * x4 + e * y4 + f; Some(Point::from_components(&[x, y])) } else { @@ -137,8 +146,8 @@ fn to_canonical_curve(w1: &Point, w2: &Point, w3 #[inline] fn characterize_from_canonical_point(b4: (f64, f64)) -> CurveCategory { // These coefficients can be used to characterise the curve - let (x, y) = b4; - let delta = x*x - 2.0*x + 4.0*y - 3.0; + let (x, y) = b4; + let delta = x * x - 2.0 * x + 4.0 * y - 3.0; if delta.abs() <= f64::EPSILON { // Curve has a cusp (but we don't know if it's in the range 0<=t<=1) @@ -158,8 +167,8 @@ fn characterize_from_canonical_point(b4: (f64, f64)) -> CurveCategory { } else { CurveCategory::Arch } - } else if x*x - 3.0*x + 3.0*y >= 0.0 { - if x*x + y*y + x*y - 3.0*x >= 0.0 { + } else if x * x - 3.0 * x + 3.0 * y >= 0.0 { + if x * x + y * y + x * y - 3.0 * x >= 0.0 { // Curve lies within the loop region CurveCategory::Loop } else { @@ -176,7 +185,7 @@ fn characterize_from_canonical_point(b4: (f64, f64)) -> CurveCategory { } else if x <= 0.0 { CurveCategory::DoubleInflectionPoint } else { - if (x-3.0).abs() <= f64::EPSILON && (y-0.0).abs() <= f64::EPSILON { + if (x - 3.0).abs() <= f64::EPSILON && (y - 0.0).abs() <= f64::EPSILON { CurveCategory::Parabolic } else { CurveCategory::Arch @@ -189,13 +198,18 @@ fn characterize_from_canonical_point(b4: (f64, f64)) -> CurveCategory { /// Determines the characteristics of a particular bezier curve: whether or not it is an arch, or changes directions /// (has inflection points), or self-intersects (has a loop) /// -pub fn characterize_cubic_bezier(w1: &Point, w2: &Point, w3: &Point, w4: &Point) -> CurveCategory { - // b4 is the end point of an equivalent curve with the other control points fixed at (0, 0), (0, 1) and (1, 1) - let b4 = to_canonical_curve(w1, w2, w3, w4); +pub fn characterize_cubic_bezier( + w1: &Point, + w2: &Point, + w3: &Point, + w4: &Point, +) -> CurveCategory { + // b4 is the end point of an equivalent curve with the other control points fixed at (0, 0), (0, 1) and (1, 1) + let b4 = to_canonical_curve(w1, w2, w3, w4); if let Some(b4) = b4 { - let x = b4.x(); - let y = b4.y(); + let x = b4.x(); + let y = b4.y(); characterize_from_canonical_point((x, y)) } else { @@ -214,10 +228,10 @@ pub fn characterize_cubic_bezier(w1: &Point, w2: CurveCategory::Linear } else { // w2 and w3 are the same. If w1, w2, w3 and w4 are collinear then we have a straight line, otherwise we have a curve with an inflection point. - let line = (w1.clone(), w3.clone()); - let (a, b, c) = line_coefficients_2d(&line); + let line = (w1.clone(), w3.clone()); + let (a, b, c) = line_coefficients_2d(&line); - let distance = a*w4.x() + b*w4.y() + c; + let distance = a * w4.x() + b * w4.y() + c; if distance.abs() < SMALL_DISTANCE { // w1, w3 and w4 are collinear (and w2 is the same as w3) CurveCategory::Linear @@ -234,8 +248,8 @@ pub fn characterize_cubic_bezier(w1: &Point, w2: if let Some(b1) = b1 { // w4 is not co-linear with w1, w2, w3 - let x = b1.x(); - let y = b1.y(); + let x = b1.x(); + let y = b1.y(); characterize_from_canonical_point((x, y)) } else { @@ -252,7 +266,7 @@ pub fn characterize_cubic_bezier(w1: &Point, w2: enum InflectionPoints { Zero, One(f64), - Two(f64, f64) + Two(f64, f64), } /// @@ -260,20 +274,20 @@ enum InflectionPoints { /// fn find_inflection_points(b4: (f64, f64)) -> InflectionPoints { // Compute coefficients - let (x4, y4) = b4; - let a = -3.0+x4+y4; - let b = 3.0-x4; + let (x4, y4) = b4; + let a = -3.0 + x4 + y4; + let b = 3.0 - x4; if a.abs() <= f64::EPSILON { // No solution InflectionPoints::Zero } else { // Solve the quadratic for this curve - let lhs = (-b)/(2.0*a); - let rhs = (4.0*a + b*b).sqrt()/(2.0*a); + let lhs = (-b) / (2.0 * a); + let rhs = (4.0 * a + b * b).sqrt() / (2.0 * a); - let t1 = lhs - rhs; - let t2 = lhs + rhs; + let t1 = lhs - rhs; + let t2 = lhs + rhs; // Want points between 0 and 1 if t1 < 0.0 || t1 > 1.0 { @@ -296,9 +310,9 @@ impl Into for InflectionPoints { #[inline] fn into(self) -> CurveFeatures { match self { - InflectionPoints::Zero => CurveFeatures::Arch, - InflectionPoints::One(t) => CurveFeatures::SingleInflectionPoint(t), - InflectionPoints::Two(t1, t2) => CurveFeatures::DoubleInflectionPoint(t1, t2) + InflectionPoints::Zero => CurveFeatures::Arch, + InflectionPoints::One(t) => CurveFeatures::SingleInflectionPoint(t), + InflectionPoints::Two(t1, t2) => CurveFeatures::DoubleInflectionPoint(t1, t2), } } } @@ -306,21 +320,31 @@ impl Into for InflectionPoints { /// /// Returns the features from a curve where we have discovered the canonical point /// -fn features_from_canonical_point(x: f64, y: f64, w1: &Point, w2: &Point, w3: &Point, w4: &Point, accuracy: f64) -> CurveFeatures { +fn features_from_canonical_point( + x: f64, + y: f64, + w1: &Point, + w2: &Point, + w3: &Point, + w4: &Point, + accuracy: f64, +) -> CurveFeatures { match characterize_from_canonical_point((x, y)) { - CurveCategory::Arch => CurveFeatures::Arch, - CurveCategory::Linear => CurveFeatures::Linear, - CurveCategory::Cusp => CurveFeatures::Cusp, - CurveCategory::Parabolic => CurveFeatures::Parabolic, - CurveCategory::Point => CurveFeatures::Point, - CurveCategory::DoubleInflectionPoint | - CurveCategory::SingleInflectionPoint => find_inflection_points((x, y)).into(), - CurveCategory::Loop => { - let curve = Curve::from_points(w1.clone(), (w2.clone(), w3.clone()), w4.clone()); - let loop_pos = find_self_intersection_point(&curve, accuracy); + CurveCategory::Arch => CurveFeatures::Arch, + CurveCategory::Linear => CurveFeatures::Linear, + CurveCategory::Cusp => CurveFeatures::Cusp, + CurveCategory::Parabolic => CurveFeatures::Parabolic, + CurveCategory::Point => CurveFeatures::Point, + CurveCategory::DoubleInflectionPoint | CurveCategory::SingleInflectionPoint => { + find_inflection_points((x, y)).into() + } + CurveCategory::Loop => { + let curve = Curve::from_points(w1.clone(), (w2.clone(), w3.clone()), w4.clone()); + let loop_pos = find_self_intersection_point(&curve, accuracy); // TODO: if we can't find the loop_pos, we could probably find a cusp position instead - loop_pos.map(|(t1, t2)| CurveFeatures::Loop(t1, t2)) + loop_pos + .map(|(t1, t2)| CurveFeatures::Loop(t1, t2)) .unwrap_or(CurveFeatures::Arch) } } @@ -330,15 +354,21 @@ fn features_from_canonical_point(x: f64, y: f64, /// Determines the characteristics of a paritcular bezier curve: whether or not it is an arch, or changes directions /// (has inflection points), or self-intersects (has a loop) /// -pub fn features_for_cubic_bezier(w1: &Point, w2: &Point, w3: &Point, w4: &Point, accuracy: f64) -> CurveFeatures { - // b4 is the end point of an equivalent curve with the other control points fixed at (0, 0), (0, 1) and (1, 1) - let b4 = to_canonical_curve(w1, w2, w3, w4); +pub fn features_for_cubic_bezier( + w1: &Point, + w2: &Point, + w3: &Point, + w4: &Point, + accuracy: f64, +) -> CurveFeatures { + // b4 is the end point of an equivalent curve with the other control points fixed at (0, 0), (0, 1) and (1, 1) + let b4 = to_canonical_curve(w1, w2, w3, w4); if let Some(b4) = b4 { // For the inflection points, we rely on the fact that the canonical curve is generated by an affine transform of the original // (and the features are invariant in such a situation) - let x = b4.x(); - let y = b4.y(); + let x = b4.x(); + let y = b4.y(); features_from_canonical_point(x, y, w1, w2, w3, w4, accuracy) } else { @@ -357,10 +387,10 @@ pub fn features_for_cubic_bezier(w1: &Point, w2: CurveFeatures::Linear } else { // w2 and w3 are the same. If w1, w2, w3 and w4 are collinear then we have a straight line, otherwise we have a curve with an inflection point. - let line = (w1.clone(), w3.clone()); - let (a, b, c) = line_coefficients_2d(&line); + let line = (w1.clone(), w3.clone()); + let (a, b, c) = line_coefficients_2d(&line); - let distance = a*w4.x() + b*w4.y() + c; + let distance = a * w4.x() + b * w4.y() + c; if distance.abs() < SMALL_DISTANCE { // w1, w3 and w4 are collinear (and w2 is the same as w3) CurveFeatures::Linear @@ -372,19 +402,23 @@ pub fn features_for_cubic_bezier(w1: &Point, w2: } else { // w1, w2, w3 must be collinear (w2 and w3 are known not to overlap) // w4 may or may not be co-linear: determine the features of the curve when reversed - let b1 = to_canonical_curve(w4, w3, w2, w1); + let b1 = to_canonical_curve(w4, w3, w2, w1); if let Some(b1) = b1 { // w4 is not co-linear with w2 and w3 - let x = b1.x(); - let y = b1.y(); + let x = b1.x(); + let y = b1.y(); // Reverse the curve coordinates for the features match features_from_canonical_point(x, y, w1, w2, w3, w4, accuracy) { - CurveFeatures::SingleInflectionPoint(t) => CurveFeatures::SingleInflectionPoint(1.0-t), - CurveFeatures::DoubleInflectionPoint(t1, t2) => CurveFeatures::DoubleInflectionPoint(1.0-t1, 1.0-t2), - CurveFeatures::Loop(t1, t2) => CurveFeatures::Loop(1.0-t1, 1.0-t2), - other => other + CurveFeatures::SingleInflectionPoint(t) => { + CurveFeatures::SingleInflectionPoint(1.0 - t) + } + CurveFeatures::DoubleInflectionPoint(t1, t2) => { + CurveFeatures::DoubleInflectionPoint(1.0 - t1, 1.0 - t2) + } + CurveFeatures::Loop(t1, t2) => CurveFeatures::Loop(1.0 - t1, 1.0 - t2), + other => other, } } else { // w1, w2 and w3 are co-linear and w2, w3 and w4 are co-linear, so all of w1, w2, w3 and w4 must be along the same line @@ -396,14 +430,16 @@ pub fn features_for_cubic_bezier(w1: &Point, w2: /// /// Discovers the 'character' of a particular bezier curve, returning a value indicating what kinds of features -/// it has (for example, whether it has a loop or a cusp) +/// it has (for example, whether it has a loop or a cusp) /// #[inline] pub fn characterize_curve(curve: &C) -> CurveCategory -where C::Point: Coordinate+Coordinate2D { +where + C::Point: Coordinate + Coordinate2D, +{ let start_point = curve.start_point(); - let (cp1, cp2) = curve.control_points(); - let end_point = curve.end_point(); + let (cp1, cp2) = curve.control_points(); + let end_point = curve.end_point(); characterize_cubic_bezier(&start_point, &cp1, &cp2, &end_point) } @@ -413,10 +449,12 @@ where C::Point: Coordinate+Coordinate2D { /// #[inline] pub fn features_for_curve(curve: &C, accuracy: f64) -> CurveFeatures -where C::Point: Coordinate+Coordinate2D { +where + C::Point: Coordinate + Coordinate2D, +{ let start_point = curve.start_point(); - let (cp1, cp2) = curve.control_points(); - let end_point = curve.end_point(); + let (cp1, cp2) = curve.control_points(); + let end_point = curve.end_point(); features_for_cubic_bezier(&start_point, &cp1, &cp2, &end_point, accuracy) } @@ -428,27 +466,27 @@ mod test { #[test] fn canonical_curve_coeffs_are_valid_1() { // Mapping the three control points via the affine transform should leave them at (0,0), (0,1) and (1,1) - let w1 = Coord2(1.0, 1.0); - let w2 = Coord2(2.0, 3.0); - let w3 = Coord2(5.0, 2.0); + let w1 = Coord2(1.0, 1.0); + let w2 = Coord2(2.0, 3.0); + let w3 = Coord2(5.0, 2.0); - let (a, b, c, d, e, f) = canonical_curve_transform(&w1, &w2, &w3).unwrap(); + let (a, b, c, d, e, f) = canonical_curve_transform(&w1, &w2, &w3).unwrap(); - let w1_new_x = w1.x()*a + w1.y()*b + c; - let w1_new_y = w1.x()*d + w1.y()*e + f; - let w2_new_x = w2.x()*a + w2.y()*b + c; - let w2_new_y = w2.x()*d + w2.y()*e + f; - let w3_new_x = w3.x()*a + w3.y()*b + c; - let w3_new_y = w3.x()*d + w3.y()*e + f; + let w1_new_x = w1.x() * a + w1.y() * b + c; + let w1_new_y = w1.x() * d + w1.y() * e + f; + let w2_new_x = w2.x() * a + w2.y() * b + c; + let w2_new_y = w2.x() * d + w2.y() * e + f; + let w3_new_x = w3.x() * a + w3.y() * b + c; + let w3_new_y = w3.x() * d + w3.y() * e + f; - assert!((w1_new_x-0.0).abs() < 0.0001); - assert!((w1_new_y-0.0).abs() < 0.0001); + assert!((w1_new_x - 0.0).abs() < 0.0001); + assert!((w1_new_y - 0.0).abs() < 0.0001); - assert!((w2_new_x-0.0).abs() < 0.0001); - assert!((w2_new_y-1.0).abs() < 0.0001); + assert!((w2_new_x - 0.0).abs() < 0.0001); + assert!((w2_new_y - 1.0).abs() < 0.0001); - assert!((w3_new_x-1.0).abs() < 0.0001); - assert!((w3_new_y-1.0).abs() < 0.0001); + assert!((w3_new_x - 1.0).abs() < 0.0001); + assert!((w3_new_y - 1.0).abs() < 0.0001); } #[test] @@ -471,9 +509,11 @@ mod test { match features_for_cubic_bezier(&w1, &w2, &w3, &w4, 0.01) { CurveFeatures::Loop(t1, t2) => { let curve = Curve::from_points(w1, (w2, w3), w4); - assert!(curve.point_at_pos(t1).is_near_to(&curve.point_at_pos(t2), 0.01)); - }, - _ => assert!(false) + assert!(curve + .point_at_pos(t1) + .is_near_to(&curve.point_at_pos(t2), 0.01)); + } + _ => assert!(false), } } @@ -564,10 +604,18 @@ mod test { let w3 = Coord2(73.0, 221.0); let w4 = Coord2(249.0, 136.0); - assert!(characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::SingleInflectionPoint); + assert!( + characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::SingleInflectionPoint + ); } - fn is_inflection_point(w1: &Point, w2: &Point, w3: &Point, w4: &Point, t: f64) -> bool { + fn is_inflection_point( + w1: &Point, + w2: &Point, + w3: &Point, + w4: &Point, + t: f64, + ) -> bool { let a = 3.0 * (w2.x() - w1.x()); let b = 3.0 * (w3.x() - w2.x()); let c = 3.0 * (w4.x() - w3.x()); @@ -580,12 +628,12 @@ mod test { let w = 2.0 * (e - d); let z = 2.0 * (f - e); - let bx1 = a * (1.0-t)*(1.0-t) + 2.0 * b * (1.0-t)*t + c * t*t; - let bx2 = u * (1.0-t) + v*t; - let by1 = d * (1.0-t)*(1.0-t) + 2.0 * e * (1.0-t)*t + f * t*t; - let by2 = w * (1.0-t) + z*t; + let bx1 = a * (1.0 - t) * (1.0 - t) + 2.0 * b * (1.0 - t) * t + c * t * t; + let bx2 = u * (1.0 - t) + v * t; + let by1 = d * (1.0 - t) * (1.0 - t) + 2.0 * e * (1.0 - t) * t + f * t * t; + let by2 = w * (1.0 - t) + z * t; - let curvature = bx1*by2 - by1*bx2; + let curvature = bx1 * by2 - by1 * bx2; curvature.abs() < 0.0001 } @@ -599,8 +647,8 @@ mod test { match features_for_cubic_bezier(&w1, &w2, &w3, &w4, 0.01) { CurveFeatures::SingleInflectionPoint(t) => { assert!(is_inflection_point(&w1, &w2, &w3, &w4, t)); - }, - _ => assert!(false) + } + _ => assert!(false), } } @@ -631,7 +679,9 @@ mod test { let w3 = Coord2(108.0, 233.0); let w4 = Coord2(329.0, 129.0); - assert!(characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::DoubleInflectionPoint); + assert!( + characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::DoubleInflectionPoint + ); } #[test] @@ -645,8 +695,8 @@ mod test { CurveFeatures::DoubleInflectionPoint(t1, t2) => { assert!(is_inflection_point(&w1, &w2, &w3, &w4, t1)); assert!(is_inflection_point(&w1, &w2, &w3, &w4, t2)); - }, - _ => assert!(false) + } + _ => assert!(false), } } @@ -717,7 +767,9 @@ mod test { let w3 = Coord2(72.0, 172.0); let w4 = Coord2(128.0, 162.0); - assert!(characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::DoubleInflectionPoint); + assert!( + characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::DoubleInflectionPoint + ); } #[test] @@ -731,8 +783,8 @@ mod test { CurveFeatures::DoubleInflectionPoint(t1, t2) => { assert!(is_inflection_point(&w1, &w2, &w3, &w4, t1)); assert!(is_inflection_point(&w1, &w2, &w3, &w4, t2)); - }, - _ => assert!(false) + } + _ => assert!(false), } } @@ -743,6 +795,8 @@ mod test { let w3 = Coord2(290.0, 200.0); let w4 = Coord2(290.0, 95.0); - assert!(characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::SingleInflectionPoint); + assert!( + characterize_cubic_bezier(&w1, &w2, &w3, &w4) == CurveCategory::SingleInflectionPoint + ); } } diff --git a/src/bezier/curve.rs b/src/bezier/curve.rs index a9d51253..43b22b39 100644 --- a/src/bezier/curve.rs +++ b/src/bezier/curve.rs @@ -1,35 +1,43 @@ -use super::fit::*; use super::basis::*; -use super::solve::*; +use super::bounds::*; +use super::characteristics::*; +use super::fit::*; use super::length::*; use super::search::*; -use super::bounds::*; use super::section::*; +use super::solve::*; use super::subdivide::*; -use super::characteristics::*; use crate::geo::*; /// /// Trait implemented by bezier curves that can create new versions of themselves -/// +/// pub trait BezierCurveFactory: BezierCurve { /// /// Creates a new bezier curve of the same type from some points - /// - fn from_points(start: Self::Point, control_points: (Self::Point, Self::Point), end: Self::Point) -> Self; + /// + fn from_points( + start: Self::Point, + control_points: (Self::Point, Self::Point), + end: Self::Point, + ) -> Self; /// /// Creates a new bezier curve of this type from an equivalent curve of another type - /// + /// #[inline] - fn from_curve>(curve: &Curve) -> Self { - Self::from_points(curve.start_point(), curve.control_points(), curve.end_point()) + fn from_curve>(curve: &Curve) -> Self { + Self::from_points( + curve.start_point(), + curve.control_points(), + curve.end_point(), + ) } /// /// Generates a curve by attempting to find a best fit against a set of points - /// + /// #[inline] fn fit_from_points(points: &[Self::Point], max_error: f64) -> Option> { fit_curve(points, max_error) @@ -38,38 +46,44 @@ pub trait BezierCurveFactory: BezierCurve { /// /// Trait implemented by things representing a cubic bezier curve -/// -pub trait BezierCurve: Geo+Clone+Sized { +/// +pub trait BezierCurve: Geo + Clone + Sized { /// /// The start point of this curve - /// + /// fn start_point(&self) -> Self::Point; /// /// The end point of this curve - /// + /// fn end_point(&self) -> Self::Point; /// /// The control points in this curve - /// + /// fn control_points(&self) -> (Self::Point, Self::Point); /// /// Reverses the direction of this curve - /// - fn reverse>(self) -> Curve { + /// + fn reverse>(self) -> Curve { let (cp1, cp2) = self.control_points(); Curve::from_points(self.end_point(), (cp2, cp1), self.start_point()) } /// /// Given a value t from 0 to 1, returns a point on this curve - /// + /// #[inline] fn point_at_pos(&self, t: f64) -> Self::Point { let control_points = self.control_points(); - basis(t, self.start_point(), control_points.0, control_points.1, self.end_point()) + basis( + t, + self.start_point(), + control_points.0, + control_points.1, + self.end_point(), + ) } /// @@ -83,46 +97,58 @@ pub trait BezierCurve: Geo+Clone+Sized { /// /// Given a value t from 0 to 1, finds a point on this curve and subdivides it, returning the two resulting curves - /// + /// #[inline] - fn subdivide>(&self, t: f64) -> (Curve, Curve) { - let control_points = self.control_points(); - let (first_curve, second_curve) = subdivide4(t, self.start_point(), control_points.0, control_points.1, self.end_point()); - - (Curve::from_points(first_curve.0, (first_curve.1, first_curve.2), first_curve.3), - Curve::from_points(second_curve.0, (second_curve.1, second_curve.2), second_curve.3)) + fn subdivide>(&self, t: f64) -> (Curve, Curve) { + let control_points = self.control_points(); + let (first_curve, second_curve) = subdivide4( + t, + self.start_point(), + control_points.0, + control_points.1, + self.end_point(), + ); + + ( + Curve::from_points(first_curve.0, (first_curve.1, first_curve.2), first_curve.3), + Curve::from_points( + second_curve.0, + (second_curve.1, second_curve.2), + second_curve.3, + ), + ) } /// /// Computes the bounds of this bezier curve - /// - fn bounding_box>(&self) -> Bounds { + /// + fn bounding_box>(&self) -> Bounds { // Fetch the various points and the derivative of this curve - let start = self.start_point(); - let end = self.end_point(); - let (cp1, cp2) = self.control_points(); + let start = self.start_point(); + let end = self.end_point(); + let (cp1, cp2) = self.control_points(); bounding_box4(start, cp1, cp2, end) } - + /// /// Faster but less accurate bounding box for a curve - /// + /// /// This will produce a bounding box that contains the curve but which may be larger than necessary - /// + /// #[inline] - fn fast_bounding_box>(&self) -> Bounds { - let start = self.start_point(); - let end = self.end_point(); - let control_points = self.control_points(); + fn fast_bounding_box>(&self) -> Bounds { + let start = self.start_point(); + let end = self.end_point(); + let control_points = self.control_points(); - let min = Self::Point::from_smallest_components(start, end); - let min = Self::Point::from_smallest_components(min, control_points.0); - let min = Self::Point::from_smallest_components(min, control_points.1); + let min = Self::Point::from_smallest_components(start, end); + let min = Self::Point::from_smallest_components(min, control_points.0); + let min = Self::Point::from_smallest_components(min, control_points.1); - let max = Self::Point::from_biggest_components(start, end); - let max = Self::Point::from_biggest_components(max, control_points.0); - let max = Self::Point::from_biggest_components(max, control_points.1); + let max = Self::Point::from_biggest_components(start, end); + let max = Self::Point::from_biggest_components(max, control_points.0); + let max = Self::Point::from_biggest_components(max, control_points.1); Bounds::from_min_max(min, max) } @@ -130,12 +156,16 @@ pub trait BezierCurve: Geo+Clone+Sized { /// /// Given a function that determines if a searched-for point is within a bounding box, searches the /// curve for the t values for the corresponding points - /// - fn search_with_bounds bool>(&self, max_error: f64, match_fn: MatchFn) -> Vec { + /// + fn search_with_bounds bool>( + &self, + max_error: f64, + match_fn: MatchFn, + ) -> Vec { // Fetch the various points and the derivative of this curve - let start = self.start_point(); - let end = self.end_point(); - let (cp1, cp2) = self.control_points(); + let start = self.start_point(); + let end = self.end_point(); + let (cp1, cp2) = self.control_points(); // Perform the search search_bounds4(max_error, start, cp1, cp2, end, match_fn) @@ -143,19 +173,19 @@ pub trait BezierCurve: Geo+Clone+Sized { /// /// Finds the t values where this curve has extremities - /// + /// #[inline] fn find_extremities(&self) -> Vec { - let start = self.start_point(); - let end = self.end_point(); - let (cp1, cp2) = self.control_points(); + let start = self.start_point(); + let end = self.end_point(); + let (cp1, cp2) = self.control_points(); find_extremities(start, cp1, cp2, end) } /// /// Attempts to estimate the length of this curve - /// + /// fn estimate_length(&self) -> f64 { curve_length(self, 0.01) } @@ -163,7 +193,7 @@ pub trait BezierCurve: Geo+Clone+Sized { /// /// Create a section from this curve. Consider calling `subsection` for curves /// that are already `CurveSections`. - /// + /// fn section<'a>(&'a self, t_min: f64, t_max: f64) -> CurveSection<'a, Self> { CurveSection::new(self, t_min, t_max) } @@ -171,12 +201,12 @@ pub trait BezierCurve: Geo+Clone+Sized { /// /// Represents a Bezier curve -/// +/// #[derive(Clone, Copy, Debug, PartialEq)] pub struct Curve { - pub start_point: Coord, - pub end_point: Coord, - pub control_points: (Coord, Coord) + pub start_point: Coord, + pub end_point: Coord, + pub control_points: (Coord, Coord), } impl Geo for Curve { @@ -184,11 +214,15 @@ impl Geo for Curve { } impl BezierCurveFactory for Curve { - fn from_points(start: Coord, (control_point1, control_point2): (Coord, Coord), end: Coord) -> Self { + fn from_points( + start: Coord, + (control_point1, control_point2): (Coord, Coord), + end: Coord, + ) -> Self { Curve { - start_point: start, + start_point: start, control_points: (control_point1, control_point2), - end_point: end + end_point: end, } } } @@ -213,8 +247,8 @@ impl BezierCurve for Curve { impl HasBoundingBox for Curve { /// /// Computes the bounds of this bezier curve - /// - fn get_bounding_box>(&self) -> Bounds { + /// + fn get_bounding_box>(&self) -> Bounds { self.bounding_box() } } @@ -235,12 +269,14 @@ pub trait BezierCurve2D: BezierCurve { } impl BezierCurve2D for T -where T::Point: Coordinate+Coordinate2D { +where + T::Point: Coordinate + Coordinate2D, +{ #[inline] fn characteristics(&self) -> CurveCategory { let start_point = self.start_point(); - let end_point = self.end_point(); - let (cp1, cp2) = self.control_points(); + let end_point = self.end_point(); + let (cp1, cp2) = self.control_points(); characterize_cubic_bezier(&start_point, &cp1, &cp2, &end_point) } @@ -248,8 +284,8 @@ where T::Point: Coordinate+Coordinate2D { #[inline] fn features(&self, accuracy: f64) -> CurveFeatures { let start_point = self.start_point(); - let end_point = self.end_point(); - let (cp1, cp2) = self.control_points(); + let end_point = self.end_point(); + let (cp1, cp2) = self.control_points(); features_for_cubic_bezier(&start_point, &cp1, &cp2, &end_point, accuracy) } diff --git a/src/bezier/deform.rs b/src/bezier/deform.rs index 1b3d82af..77f5cb2c 100644 --- a/src/bezier/deform.rs +++ b/src/bezier/deform.rs @@ -1,35 +1,43 @@ -use super::curve::*; use super::super::geo::*; +use super::curve::*; /// /// Moves the point at 't' on the curve by the offset vector -/// +/// /// This recomputes the control points such that the point at t on the original curve /// is moved by the vector specified by `offset`. -/// -pub fn move_point, CurveOut: BezierCurveFactory>(curve: &CurveIn, t: f64, offset: &P) -> CurveOut { +/// +pub fn move_point< + P: Coordinate, + CurveIn: BezierCurve, + CurveOut: BezierCurveFactory, +>( + curve: &CurveIn, + t: f64, + offset: &P, +) -> CurveOut { // Fetch the points from the curve - let w1 = curve.start_point(); - let w4 = curve.end_point(); - let (w2, w3) = curve.control_points(); + let w1 = curve.start_point(); + let w4 = curve.end_point(); + let (w2, w3) = curve.control_points(); - let one_minus_t = 1.0-t; - let one_minus_t_cubed = one_minus_t*one_minus_t*one_minus_t; - let t_cubed = t*t*t; + let one_minus_t = 1.0 - t; + let one_minus_t_cubed = one_minus_t * one_minus_t * one_minus_t; + let t_cubed = t * t * t; // Point 'C' is fixed for the transformation and is along the line w1-w4 let u = one_minus_t_cubed / (t_cubed + one_minus_t_cubed); - let c = w1*u + w4*(1.0-u); + let c = w1 * u + w4 * (1.0 - u); // Construct the de Casteljau points for the point we're moving - let wn1 = w1*(1.0-t) + w2*t; - let wn2 = w2*(1.0-t) + w3*t; - let wn3 = w3*(1.0-t) + w4*t; + let wn1 = w1 * (1.0 - t) + w2 * t; + let wn2 = w2 * (1.0 - t) + w3 * t; + let wn3 = w3 * (1.0 - t) + w4 * t; - let wnn1 = wn1*(1.0-t) + wn2*t; - let wnn2 = wn2*(1.0-t) + wn3*t; + let wnn1 = wn1 * (1.0 - t) + wn2 * t; + let wnn2 = wn2 * (1.0 - t) + wn3 * t; - let p = wnn1*(1.0-t) + wnn2*t; + let p = wnn1 * (1.0 - t) + wnn2 * t; // Translating wnn1 and wnn2 by the offset will give us a new p that is also translated by the offset let pb = p + *offset; @@ -39,20 +47,20 @@ pub fn move_point, CurveOut: Bezier // The line c->pb->wn2b has the same ratios as the line c->p->wn2, so we can compute wn2b // There's a trick to calculating this for cubic curves (which is handy as it means this will work with straight lines as well as curves) - let ratio = ((t_cubed+one_minus_t_cubed)/(t_cubed + one_minus_t_cubed-1.0)).abs(); - let wn2b = ((pb-c)*ratio) + pb; + let ratio = ((t_cubed + one_minus_t_cubed) / (t_cubed + one_minus_t_cubed - 1.0)).abs(); + let wn2b = ((pb - c) * ratio) + pb; // We can now calculate wn1b and wn3b - let inverse_t = 1.0/t; - let inverse_tminus1 = 1.0/(t-1.0); - - let wn1b = (wn2b*t - wnn1b)*inverse_tminus1; - let wn3b = (wn2b*-1.0 + wn2b*t + wnn2b)*inverse_t; + let inverse_t = 1.0 / t; + let inverse_tminus1 = 1.0 / (t - 1.0); + + let wn1b = (wn2b * t - wnn1b) * inverse_tminus1; + let wn3b = (wn2b * -1.0 + wn2b * t + wnn2b) * inverse_t; // ... and the new control points - let w2b = (w1*-1.0 + w1*t + wn1b)*inverse_t; - let w3b = (w4*t-wn3b)*inverse_tminus1; + let w2b = (w1 * -1.0 + w1 * t + wn1b) * inverse_t; + let w3b = (w4 * t - wn3b) * inverse_tminus1; // Use the values to construct the curve with the moved point CurveOut::from_points(w1, (w2b, w3b), w4) -} \ No newline at end of file +} diff --git a/src/bezier/derivative.rs b/src/bezier/derivative.rs index dfc4e04e..9949dbb5 100644 --- a/src/bezier/derivative.rs +++ b/src/bezier/derivative.rs @@ -2,21 +2,26 @@ use super::super::geo::*; /// /// Returns the 1st derivative of a cubic bezier curve -/// -pub fn derivative4(w1: Point, w2: Point, w3: Point, w4: Point) -> (Point, Point, Point) { - ((w2-w1)*3.0, (w3-w2)*3.0, (w4-w3)*3.0) +/// +pub fn derivative4( + w1: Point, + w2: Point, + w3: Point, + w4: Point, +) -> (Point, Point, Point) { + ((w2 - w1) * 3.0, (w3 - w2) * 3.0, (w4 - w3) * 3.0) } /// /// Returns the 1st derivative of a quadratic bezier curve (or the 2nd derivative of a cubic curve) -/// +/// pub fn derivative3(wn1: Point, wn2: Point, wn3: Point) -> (Point, Point) { - ((wn2-wn1)*2.0, (wn3-wn2)*2.0) + ((wn2 - wn1) * 2.0, (wn3 - wn2) * 2.0) } /// /// Returns the 3rd derivative of a cubic bezier curve (2nd of a quadratic) -/// +/// pub fn derivative2(wnn1: Point, wnn2: Point) -> Point { - wnn2-wnn1 + wnn2 - wnn1 } diff --git a/src/bezier/distort.rs b/src/bezier/distort.rs index 46bdc577..66f519b6 100644 --- a/src/bezier/distort.rs +++ b/src/bezier/distort.rs @@ -1,8 +1,8 @@ -use super::fit::*; -use super::walk::*; -use super::path::*; use super::curve::*; +use super::fit::*; use super::normal::*; +use super::path::*; +use super::walk::*; use crate::geo::*; use std::iter; @@ -10,19 +10,26 @@ use std::iter; /// /// Distorts a curve using an arbitrary function /// -pub fn distort_curve(curve: &CurveIn, distort_fn: DistortFn, step_len: f64, max_error: f64) -> Option> +pub fn distort_curve( + curve: &CurveIn, + distort_fn: DistortFn, + step_len: f64, + max_error: f64, +) -> Option> where -CurveIn: BezierCurve, -CurveIn::Point: Normalize+Coordinate2D, -CurveOut: BezierCurveFactory, -DistortFn: Fn(CurveIn::Point, f64) -> CurveOut::Point { + CurveIn: BezierCurve, + CurveIn::Point: Normalize + Coordinate2D, + CurveOut: BezierCurveFactory, + DistortFn: Fn(CurveIn::Point, f64) -> CurveOut::Point, +{ // Walk the curve at roughly step_len increments - let sections = walk_curve_evenly(curve, step_len, step_len / 4.0); + let sections = walk_curve_evenly(curve, step_len, step_len / 4.0); // Generate the points to fit to using the distortion function - let fit_points = sections.map(|section| { - let (t, _) = section.original_curve_t_values(); - let pos = curve.point_at_pos(t); + let fit_points = sections + .map(|section| { + let (t, _) = section.original_curve_t_values(); + let pos = curve.point_at_pos(t); (pos, t) }) @@ -37,28 +44,39 @@ DistortFn: Fn(CurveIn::Point, f64) -> CurveOut::Point { /// /// Distorts a path using an arbitrary function /// -pub fn distort_path(path: &PathIn, distort_fn: DistortFn, step_len: f64, max_error: f64) -> Option +pub fn distort_path( + path: &PathIn, + distort_fn: DistortFn, + step_len: f64, + max_error: f64, +) -> Option where -PathIn: BezierPath, -PathOut: BezierPathFactory, -DistortFn: Fn(PathIn::Point, &Curve, f64) -> PathOut::Point { + PathIn: BezierPath, + PathOut: BezierPathFactory, + DistortFn: Fn(PathIn::Point, &Curve, f64) -> PathOut::Point, +{ // The initial point is derived from the first curve - let start_point = path.start_point(); - let mut path_points = path.points(); - let mut current_point = path_points.next()?; - let mut current_curve = Curve::from_points(start_point, (current_point.0, current_point.1), current_point.2); - let start_point = distort_fn(start_point, ¤t_curve, 0.0); + let start_point = path.start_point(); + let mut path_points = path.points(); + let mut current_point = path_points.next()?; + let mut current_curve = Curve::from_points( + start_point, + (current_point.0, current_point.1), + current_point.2, + ); + let start_point = distort_fn(start_point, ¤t_curve, 0.0); // Process the remaining points to generate the new path - let mut new_points = vec![]; + let mut new_points = vec![]; loop { // Distort the current curve - let sections = walk_curve_evenly(¤t_curve, step_len, step_len / 4.0); + let sections = walk_curve_evenly(¤t_curve, step_len, step_len / 4.0); - let fit_points = sections.map(|section| { - let (t, _) = section.original_curve_t_values(); - let pos = current_curve.point_at_pos(t); + let fit_points = sections + .map(|section| { + let (t, _) = section.original_curve_t_values(); + let pos = current_curve.point_at_pos(t); (pos, t) }) @@ -67,16 +85,24 @@ DistortFn: Fn(PathIn::Point, &Curve, f64) -> PathOut::Point { .collect::>(); // Fit the points to generate the new curves - let new_curves = fit_curve::>(&fit_points, max_error)?; + let new_curves = fit_curve::>(&fit_points, max_error)?; new_points.extend(new_curves.into_iter().map(|curve| { let (cp1, cp2) = curve.control_points(); (cp1, cp2, curve.end_point()) })); // Move to the next curve (stopping once we reach the end of the list of the points) - let next_start_point = current_curve.end_point(); - current_point = if let Some(point) = path_points.next() { point } else { break; }; - current_curve = Curve::from_points(next_start_point, (current_point.0, current_point.1), current_point.2); + let next_start_point = current_curve.end_point(); + current_point = if let Some(point) = path_points.next() { + point + } else { + break; + }; + current_curve = Curve::from_points( + next_start_point, + (current_point.0, current_point.1), + current_point.2, + ); } // Create the new path from the result diff --git a/src/bezier/fit.rs b/src/bezier/fit.rs index 5a3b37e1..b4a30a39 100644 --- a/src/bezier/fit.rs +++ b/src/bezier/fit.rs @@ -1,5 +1,5 @@ -use super::curve::*; use super::basis::*; +use super::curve::*; use crate::geo::*; /// Maximum number of iterations to perform when trying to improve the curve fit @@ -13,17 +13,20 @@ const MAX_POINTS_TO_FIT: usize = 100; /// /// Creates a bezier curve that fits a set of points with a particular error -/// +/// /// Algorithm from Philip J. Schneider, Graphics Gems -/// +/// /// There are a few modifications from the original algorithm: -/// -/// * The 'small' error used to determine if we should use Newton-Raphson is now +/// +/// * The 'small' error used to determine if we should use Newton-Raphson is now /// just a multiplier of the max error /// * We only try to fit a certain number of points at once as the algorithm runs /// in quadratic time otherwise -/// -pub fn fit_curve(points: &[Curve::Point], max_error: f64) -> Option> { +/// +pub fn fit_curve( + points: &[Curve::Point], + max_error: f64, +) -> Option> { // Need at least 2 points to fit anything if points.len() < 2 { // Insufficient points for this curve @@ -32,28 +35,30 @@ pub fn fit_curve(points: &[Curve::Point], let mut curves = vec![]; // Divide up the points into blocks containing MAX_POINTS_TO_FIT items - let num_blocks = ((points.len()-1) / MAX_POINTS_TO_FIT)+1; + let num_blocks = ((points.len() - 1) / MAX_POINTS_TO_FIT) + 1; for point_block in 0..num_blocks { // Pick the set of points that will be in this block - let start_point = point_block * MAX_POINTS_TO_FIT; - let mut num_points = MAX_POINTS_TO_FIT; + let start_point = point_block * MAX_POINTS_TO_FIT; + let mut num_points = MAX_POINTS_TO_FIT; - if start_point+num_points > points.len() { + if start_point + num_points > points.len() { num_points = points.len() - start_point; } // Edge case: one point outside of a block (we ignore these blocks) - if num_points < 2 { continue; } + if num_points < 2 { + continue; + } // Need the start and end tangents so we know how the curve continues - let block_points = &points[start_point..start_point+num_points]; + let block_points = &points[start_point..start_point + num_points]; - let start_tangent = start_tangent(block_points); - let end_tangent = if start_point+num_points < points.len() { - end_tangent(&points[start_point..start_point+num_points+1]) - } else { - end_tangent(block_points) + let start_tangent = start_tangent(block_points); + let end_tangent = if start_point + num_points < points.len() { + end_tangent(&points[start_point..start_point + num_points + 1]) + } else { + end_tangent(block_points) }; let fit = fit_curve_cubic(block_points, &start_tangent, &end_tangent, max_error); @@ -82,35 +87,40 @@ pub fn fit_curve(points: &[Curve::Point], /// The algorithm here is to attempt to fit a single bezier curve against the points, estimate the point which has the highest error, and if too high /// subdivide at that point and try again. /// -pub fn fit_curve_cubic(points: &[Curve::Point], start_tangent: &Curve::Point, end_tangent: &Curve::Point, max_error: f64) -> Vec { +pub fn fit_curve_cubic( + points: &[Curve::Point], + start_tangent: &Curve::Point, + end_tangent: &Curve::Point, + max_error: f64, +) -> Vec { if points.len() <= 2 { // 2 points is a line (less than 2 points is an error here) fit_line(&points[0], &points[1]) } else { // Perform an initial estimate of the 't' values corresponding to the chords of the curve - let mut chords = chords_for_points(points); + let mut chords = chords_for_points(points); // Use the least-squares method to fit against the initial set of chords - let mut curve = generate_bezier(points, &chords, start_tangent, end_tangent); + let mut curve = generate_bezier(points, &chords, start_tangent, end_tangent); // Reparameterise the chords (which will probably be quite a bad estimate initially) - chords = reparameterize(points, &chords, &curve); - curve = generate_bezier(points, &chords, start_tangent, end_tangent); + chords = reparameterize(points, &chords, &curve); + curve = generate_bezier(points, &chords, start_tangent, end_tangent); // Estimate the error after the reparameterization - let (mut error, mut split_pos) = max_error_for_curve(points, &chords, &curve); + let (mut error, mut split_pos) = max_error_for_curve(points, &chords, &curve); // Try iterating to improve the fit if we're not too far out - if error > max_error && error < max_error*FIT_ATTEMPT_RATIO { + if error > max_error && error < max_error * FIT_ATTEMPT_RATIO { for _iteration in 1..MAX_ITERATIONS { // Recompute the chords and the curve chords = reparameterize(points, &chords, &curve); - curve = generate_bezier(points, &chords, start_tangent, end_tangent); + curve = generate_bezier(points, &chords, start_tangent, end_tangent); // Recompute the error let (new_error, new_split_pos) = max_error_for_curve(points, &chords, &curve); - error = new_error; - split_pos = new_split_pos; + error = new_error; + split_pos = new_split_pos; if error <= max_error { break; @@ -123,11 +133,25 @@ pub fn fit_curve_cubic(points: &[Curve::P vec![curve] } else { // If error still too large, split the points and create two curves - let center_tangent = tangent_between(&points[split_pos-1], &points[split_pos], &points[split_pos+1]); + let center_tangent = tangent_between( + &points[split_pos - 1], + &points[split_pos], + &points[split_pos + 1], + ); // Fit the two sides - let lhs = fit_curve_cubic(&points[0..split_pos+1], start_tangent, ¢er_tangent, max_error); - let rhs = fit_curve_cubic(&points[split_pos..points.len()], &(center_tangent*-1.0), end_tangent, max_error); + let lhs = fit_curve_cubic( + &points[0..split_pos + 1], + start_tangent, + ¢er_tangent, + max_error, + ); + let rhs = fit_curve_cubic( + &points[split_pos..points.len()], + &(center_tangent * -1.0), + end_tangent, + max_error, + ); // Collect the result lhs.into_iter().chain(rhs.into_iter()).collect() @@ -137,29 +161,29 @@ pub fn fit_curve_cubic(points: &[Curve::P /// /// Creates a curve representing a line between two points -/// +/// fn fit_line(p1: &Curve::Point, p2: &Curve::Point) -> Vec { // Any bezier curve where the control points line up forms a straight line; we use points around 1/3rd of the way along in our generation here - let direction = *p2 - *p1; - let cp1 = *p1 + (direction * 0.33); - let cp2 = *p1 + (direction * 0.66); + let direction = *p2 - *p1; + let cp1 = *p1 + (direction * 0.33); + let cp2 = *p1 + (direction * 0.66); vec![Curve::from_points(*p1, (cp1, cp2), *p2)] } /// /// Chord-length parameterizes a set of points -/// +/// /// This is an estimate of the 't' value for these points on the final curve. -/// +/// fn chords_for_points(points: &[Point]) -> Vec { - let mut distances = vec![]; - let mut total_distance = 0.0; + let mut distances = vec![]; + let mut total_distance = 0.0; // Compute the distances for each point distances.push(total_distance); for p in 1..points.len() { - total_distance += points[p-1].distance_to(&points[p]); + total_distance += points[p - 1].distance_to(&points[p]); distances.push(total_distance); } @@ -173,23 +197,31 @@ fn chords_for_points(points: &[Point]) -> Vec { /// /// Generates a bezier curve using the least-squares method -/// -fn generate_bezier(points: &[Curve::Point], chords: &[f64], start_tangent: &Curve::Point, end_tangent: &Curve::Point) -> Curve { +/// +fn generate_bezier( + points: &[Curve::Point], + chords: &[f64], + start_tangent: &Curve::Point, + end_tangent: &Curve::Point, +) -> Curve { // Precompute the RHS as 'a' - let a: Vec<_> = chords.iter().map(|chord| { - let inverse_chord = 1.0 - chord; + let a: Vec<_> = chords + .iter() + .map(|chord| { + let inverse_chord = 1.0 - chord; - let b1 = 3.0 * chord * (inverse_chord*inverse_chord); - let b2 = 3.0 * chord * chord * inverse_chord; + let b1 = 3.0 * chord * (inverse_chord * inverse_chord); + let b2 = 3.0 * chord * chord * inverse_chord; - (*start_tangent*b1, *end_tangent*b2) - }).collect(); + (*start_tangent * b1, *end_tangent * b2) + }) + .collect(); // Create the 'C' and 'X' matrices - let mut c = [[ 0.0, 0.0 ], [ 0.0, 0.0 ]]; + let mut c = [[0.0, 0.0], [0.0, 0.0]]; let mut x = [0.0, 0.0]; - let last_point = points[points.len()-1]; + let last_point = points[points.len() - 1]; for point in 0..points.len() { c[0][0] += a[point].0.dot(&a[point].0); @@ -197,140 +229,175 @@ fn generate_bezier(points: &[Curve::Point], chords: & c[1][0] = c[0][1]; c[1][1] += a[point].1.dot(&a[point].1); - let chord = chords[point]; - let inverse_chord = 1.0 - chord; - let b0 = inverse_chord*inverse_chord*inverse_chord; - let b1 = 3.0 * chord * (inverse_chord*inverse_chord); - let b2 = 3.0 * chord * chord * inverse_chord; - let b3 = chord*chord*chord; + let chord = chords[point]; + let inverse_chord = 1.0 - chord; + let b0 = inverse_chord * inverse_chord * inverse_chord; + let b1 = 3.0 * chord * (inverse_chord * inverse_chord); + let b2 = 3.0 * chord * chord * inverse_chord; + let b3 = chord * chord * chord; - let tmp = points[point] - - ((points[0] * b0) + (points[0] * b1) + (last_point*b2) + (last_point*b3)); + let tmp = points[point] + - ((points[0] * b0) + (points[0] * b1) + (last_point * b2) + (last_point * b3)); x[0] += a[point].0.dot(&tmp); x[1] += a[point].1.dot(&tmp); } // Compute their determinants - let det_c0_c1 = c[0][0]*c[1][1] - c[1][0]*c[0][1]; - let det_c0_x = c[0][0]*x[1] - c[1][0]*x[0]; - let det_x_c1 = x[0]*c[1][1] - x[1]*c[0][1]; + let det_c0_c1 = c[0][0] * c[1][1] - c[1][0] * c[0][1]; + let det_c0_x = c[0][0] * x[1] - c[1][0] * x[0]; + let det_x_c1 = x[0] * c[1][1] - x[1] * c[0][1]; // Derive alpha values - let alpha_l = if f64::abs(det_c0_c1)<1.0e-4 { 0.0 } else { det_x_c1/det_c0_c1 }; - let alpha_r = if f64::abs(det_c0_c1)<1.0e-4 { 0.0 } else { det_c0_x/det_c0_c1 }; + let alpha_l = if f64::abs(det_c0_c1) < 1.0e-4 { + 0.0 + } else { + det_x_c1 / det_c0_c1 + }; + let alpha_r = if f64::abs(det_c0_c1) < 1.0e-4 { + 0.0 + } else { + det_c0_x / det_c0_c1 + }; // Use the Wu/Barsky heuristic if alpha-negative - let seg_length = points[0].distance_to(&last_point); - let epsilon = 1.0e-6*seg_length; + let seg_length = points[0].distance_to(&last_point); + let epsilon = 1.0e-6 * seg_length; if alpha_l < epsilon || alpha_r < epsilon { // Much less accurate means of estimating a curve - let dist = seg_length/3.0; - Curve::from_points(points[0], (points[0]+(*start_tangent*dist), last_point+(*end_tangent*dist)), last_point) + let dist = seg_length / 3.0; + Curve::from_points( + points[0], + ( + points[0] + (*start_tangent * dist), + last_point + (*end_tangent * dist), + ), + last_point, + ) } else { // The control points are positioned an alpha distance out along the tangent vectors - Curve::from_points(points[0], (points[0]+(*start_tangent*alpha_l), last_point+(*end_tangent*alpha_r)), last_point) + Curve::from_points( + points[0], + ( + points[0] + (*start_tangent * alpha_l), + last_point + (*end_tangent * alpha_r), + ), + last_point, + ) } } /// /// Computes the maximum error for a curve fit against a given set of points -/// +/// /// The chords indicate the estimated t-values corresponding to the points. -/// +/// /// Returns the maximum error and the index of the point with that error. -/// -fn max_error_for_curve(points: &[Curve::Point], chords: &[f64], curve: &Curve) -> (f64, usize) { - let errors = points.iter().zip(chords.iter()) - .map(|(point, chord)| { - // Get the actual position of this point and the offset - let actual = curve.point_at_pos(*chord); - let offset = *point - actual; - - // The dot product of an item with itself is the square of the distance - offset.dot(&offset) - }); - +/// +fn max_error_for_curve( + points: &[Curve::Point], + chords: &[f64], + curve: &Curve, +) -> (f64, usize) { + let errors = points.iter().zip(chords.iter()).map(|(point, chord)| { + // Get the actual position of this point and the offset + let actual = curve.point_at_pos(*chord); + let offset = *point - actual; + + // The dot product of an item with itself is the square of the distance + offset.dot(&offset) + }); + // Search the errors for the biggest one let mut biggest_error_squared = 0.0; - let mut biggest_error_offset = 0; + let mut biggest_error_offset = 0; for (current_point, error_squared) in errors.enumerate() { if error_squared > biggest_error_squared { biggest_error_squared = error_squared; - biggest_error_offset = current_point; + biggest_error_offset = current_point; } } - - // Indicate the biggest error and where it was + + // Indicate the biggest error and where it was (f64::sqrt(biggest_error_squared), biggest_error_offset) } /// /// Returns the unit tangent at the start of the curve -/// +/// fn start_tangent(points: &[Point]) -> Point { - (points[1]-points[0]).to_unit_vector() + (points[1] - points[0]).to_unit_vector() } /// /// Returns the unit tangent at the end of the curve -/// +/// fn end_tangent(points: &[Point]) -> Point { - (points[points.len()-2]-points[points.len()-1]).to_unit_vector() + (points[points.len() - 2] - points[points.len() - 1]).to_unit_vector() } /// -/// Estimates the tangent between three points +/// Estimates the tangent between three points /// fn tangent_between(p1: &Point, p2: &Point, p3: &Point) -> Point { let v1 = *p1 - *p2; let v2 = *p2 - *p3; - ((v1+v2)*0.5).to_unit_vector() + ((v1 + v2) * 0.5).to_unit_vector() } /// /// Applies the newton-raphson method in order to improve the t values of a curve -/// -fn reparameterize(points: &[Curve::Point], chords: &[f64], curve: &Curve) -> Vec { - points.iter().zip(chords.iter()) +/// +fn reparameterize( + points: &[Curve::Point], + chords: &[f64], + curve: &Curve, +) -> Vec { + points + .iter() + .zip(chords.iter()) .map(|(point, chord)| newton_raphson_root_find(curve, point, *chord)) .collect() } /// /// Uses newton-raphson to find a root for a curve -/// -fn newton_raphson_root_find(curve: &Curve, point: &Curve::Point, estimated_t: f64) -> f64 { - let start = curve.start_point(); - let end = curve.end_point(); - let (cp1, cp2) = curve.control_points(); +/// +fn newton_raphson_root_find( + curve: &Curve, + point: &Curve::Point, + estimated_t: f64, +) -> f64 { + let start = curve.start_point(); + let end = curve.end_point(); + let (cp1, cp2) = curve.control_points(); // Compute Q(t) (where Q is our curve) - let qt = curve.point_at_pos(estimated_t); - + let qt = curve.point_at_pos(estimated_t); + // Generate control vertices - let qn1 = (cp1-start)*3.0; - let qn2 = (cp2-cp1)*3.0; - let qn3 = (end-cp2)*3.0; + let qn1 = (cp1 - start) * 3.0; + let qn2 = (cp2 - cp1) * 3.0; + let qn3 = (end - cp2) * 3.0; - let qnn1 = (qn2-qn1)*2.0; - let qnn2 = (qn3-qn2)*2.0; + let qnn1 = (qn2 - qn1) * 2.0; + let qnn2 = (qn3 - qn2) * 2.0; // Compute Q'(t) and Q''(t) - let qnt = de_casteljau3(estimated_t, qn1, qn2, qn3); - let qnnt = de_casteljau2(estimated_t, qnn1, qnn2); + let qnt = de_casteljau3(estimated_t, qn1, qn2, qn3); + let qnnt = de_casteljau2(estimated_t, qnn1, qnn2); // Compute f(u)/f'(u) - let numerator = (qt-*point).dot(&qnt); - let denominator = qnt.dot(&qnt) + (qt-*point).dot(&qnnt); + let numerator = (qt - *point).dot(&qnt); + let denominator = qnt.dot(&qnt) + (qt - *point).dot(&qnnt); // u = u - f(u)/f'(u) if denominator == 0.0 { estimated_t } else { - estimated_t - (numerator/denominator) + estimated_t - (numerator / denominator) } } diff --git a/src/bezier/intersection/curve_curve_clip.rs b/src/bezier/intersection/curve_curve_clip.rs index cac23e0b..af8ffa34 100644 --- a/src/bezier/intersection/curve_curve_clip.rs +++ b/src/bezier/intersection/curve_curve_clip.rs @@ -1,25 +1,25 @@ -use super::fat_line::*; use super::curve_line::*; -use crate::geo::*; -use crate::bezier::*; +use super::fat_line::*; use crate::bezier::solve::*; +use crate::bezier::*; +use crate::geo::*; use smallvec::*; /// /// Determines the length of a curve's hull as a sum of squares -/// +/// fn curve_hull_length_sq<'a, C: BezierCurve>(curve: &CurveSection<'a, C>) -> f64 { if curve.is_tiny() { 0.0 } else { - let start = curve.start_point(); - let end = curve.end_point(); - let (cp1, cp2) = curve.control_points(); + let start = curve.start_point(); + let end = curve.end_point(); + let (cp1, cp2) = curve.control_points(); - let offset1 = cp1-start; - let offset2 = cp2-cp1; - let offset3 = cp2-end; + let offset1 = cp1 - start; + let offset2 = cp2 - cp1; + let offset3 = cp2 - end; offset1.dot(&offset1) + offset2.dot(&offset2) + offset3.dot(&offset3) } @@ -28,14 +28,20 @@ fn curve_hull_length_sq<'a, C: BezierCurve>(curve: &CurveSection<'a, C>) -> f64 /// /// Given a line representing a linear section of a curve, finds the intersection with a curved section and returns the t values /// -fn intersections_with_linear_section<'a, C: BezierCurve>(linear_section: &CurveSection<'a, C>, curved_section: &CurveSection<'a, C>) -> SmallVec<[(f64, f64); 4]> -where C::Point: 'a+Coordinate2D { +fn intersections_with_linear_section<'a, C: BezierCurve>( + linear_section: &CurveSection<'a, C>, + curved_section: &CurveSection<'a, C>, +) -> SmallVec<[(f64, f64); 4]> +where + C::Point: 'a + Coordinate2D, +{ // Treat the linear section as a ray based on the start and the end point and find where on the curved section the ray intersects the linear section - let ray = (linear_section.start_point(), linear_section.end_point()); - let ray_intersections = curve_intersects_ray(curved_section, &ray); + let ray = (linear_section.start_point(), linear_section.end_point()); + let ray_intersections = curve_intersects_ray(curved_section, &ray); // Attempt to find where the 't' value is for each ray intersection against the linear section - let curve_intersections = ray_intersections.iter() + let curve_intersections = ray_intersections + .iter() .filter_map(|(curved_t, _ray_t, pos)| { let linear_t = solve_curve_for_t(linear_section, pos); @@ -46,17 +52,21 @@ where C::Point: 'a+Coordinate2D { // Rarely: the linear section might be very short and the solver might miss that it's essentially a point if curve_intersections.len() == 0 && ray_intersections.len() != 0 { // If the linear section seems short - if linear_section.point_at_pos(0.0).is_near_to(&linear_section.point_at_pos(1.0), 0.1) { - let midpoint = linear_section.point_at_pos(0.5); - let curve_intersections = ray_intersections.iter() - .filter_map(|(curved_t, _ray_t, pos)| { - if pos.is_near_to(&midpoint, CLOSE_ENOUGH) { - Some((0.5, *curved_t)) - } else { - None - } - }) - .collect::>(); + if linear_section + .point_at_pos(0.0) + .is_near_to(&linear_section.point_at_pos(1.0), 0.1) + { + let midpoint = linear_section.point_at_pos(0.5); + let curve_intersections = ray_intersections + .iter() + .filter_map(|(curved_t, _ray_t, pos)| { + if pos.is_near_to(&midpoint, CLOSE_ENOUGH) { + Some((0.5, *curved_t)) + } else { + None + } + }) + .collect::>(); return curve_intersections; } @@ -72,18 +82,23 @@ where C::Point: 'a+Coordinate2D { enum ClipResult { None, Some((f64, f64)), - SecondCurveIsLinear + SecondCurveIsLinear, } /// /// Performs the fat-line clipping algorithm on two curves, returning the t values if they overlap -/// +/// #[inline] -fn clip<'a, C: BezierCurve>(curve_to_clip: &CurveSection<'a, C>, curve_to_clip_against: &CurveSection<'a, C>) -> ClipResult -where C::Point: 'a+Coordinate2D { +fn clip<'a, C: BezierCurve>( + curve_to_clip: &CurveSection<'a, C>, + curve_to_clip_against: &CurveSection<'a, C>, +) -> ClipResult +where + C::Point: 'a + Coordinate2D, +{ // Clip against the fat line - let fat_line = FatLine::from_curve(curve_to_clip_against); - let clip_t = fat_line.clip_t(curve_to_clip); + let fat_line = FatLine::from_curve(curve_to_clip_against); + let clip_t = fat_line.clip_t(curve_to_clip); if fat_line.is_flat() { return ClipResult::SecondCurveIsLinear; @@ -91,8 +106,8 @@ where C::Point: 'a+Coordinate2D { let clip_t = if let Some(clip_t) = clip_t { // Also try clipping against the perpendicular line - let perpendicular_line = FatLine::from_curve_perpendicular(curve_to_clip_against); - let clip_t_perpendicular = perpendicular_line.clip_t(curve_to_clip); + let perpendicular_line = FatLine::from_curve_perpendicular(curve_to_clip_against); + let clip_t_perpendicular = perpendicular_line.clip_t(curve_to_clip); // Use the perpendicular version if better if let Some(clip_t_perpendicular) = clip_t_perpendicular { @@ -116,16 +131,29 @@ where C::Point: 'a+Coordinate2D { // t1 and t2 must not match (exact matches produce an invalid curve) match clip_t { - ClipResult::Some((t1, t2)) => if t1 == t2 { ClipResult::Some(((t1-0.005).max(0.0), (t2+0.005).min(1.0))) } else { ClipResult::Some((t1, t2)) } - other => other + ClipResult::Some((t1, t2)) => { + if t1 == t2 { + ClipResult::Some(((t1 - 0.005).max(0.0), (t2 + 0.005).min(1.0))) + } else { + ClipResult::Some((t1, t2)) + } + } + other => other, } } /// /// Given a set of intersections found on a left and right curve, joins them in a way that eliminates duplicates -/// -fn join_subsections<'a, C: BezierCurve>(curve1: &CurveSection<'a, C>, left: SmallVec<[(f64, f64); 8]>, right: SmallVec<[(f64, f64); 8]>, accuracy_squared: f64) -> SmallVec<[(f64, f64); 8]> -where C::Point: Coordinate2D { +/// +fn join_subsections<'a, C: BezierCurve>( + curve1: &CurveSection<'a, C>, + left: SmallVec<[(f64, f64); 8]>, + right: SmallVec<[(f64, f64); 8]>, + accuracy_squared: f64, +) -> SmallVec<[(f64, f64); 8]> +where + C::Point: Coordinate2D, +{ if left.len() == 0 { // No further work to do right @@ -134,22 +162,22 @@ where C::Point: Coordinate2D { left } else { // The last intersection in left might be the same as the first in right - let (left_t1, _left_t2) = left[left.len()-1]; - let (right_t1, _right_t2) = right[0]; + let (left_t1, _left_t2) = left[left.len() - 1]; + let (right_t1, _right_t2) = right[0]; // We use t1 and curve1 to determine this - let left_t1 = curve1.section_t_for_original_t(left_t1); - let right_t1 = curve1.section_t_for_original_t(right_t1); + let left_t1 = curve1.section_t_for_original_t(left_t1); + let right_t1 = curve1.section_t_for_original_t(right_t1); - if (right_t1-left_t1).abs() < 0.1 { + if (right_t1 - left_t1).abs() < 0.1 { // Could be the same point let p1 = curve1.point_at_pos(left_t1); let p2 = curve1.point_at_pos(right_t1); - let offset = p2-p1; - let distance_squared = offset.dot(&offset); + let offset = p2 - p1; + let distance_squared = offset.dot(&offset); - if distance_squared <= (accuracy_squared*2.0) { + if distance_squared <= (accuracy_squared * 2.0) { // First and last points are the same: only use the version of the LHS let mut combined = left; combined.extend(right.into_iter().skip(1)); @@ -171,9 +199,15 @@ where C::Point: Coordinate2D { /// /// Determines the points at which two curves intersect using the Bezier clipping algorithm -/// -fn curve_intersects_curve_clip_inner<'a, C: BezierCurve>(curve1: CurveSection<'a, C>, curve2: CurveSection<'a, C>, accuracy_squared: f64) -> SmallVec<[(f64, f64); 8]> -where C::Point: 'a+Coordinate2D { +/// +fn curve_intersects_curve_clip_inner<'a, C: BezierCurve>( + curve1: CurveSection<'a, C>, + curve2: CurveSection<'a, C>, + accuracy_squared: f64, +) -> SmallVec<[(f64, f64); 8]> +where + C::Point: 'a + Coordinate2D, +{ // Overlapping curves should be treated separately (the clipping algorithm will just match all of the points) let overlaps = overlapping_region(&curve1, &curve2); if let Some(((c1_t1, c1_t2), (c2_t1, c2_t2))) = overlaps { @@ -201,22 +235,28 @@ where C::Point: 'a+Coordinate2D { let mut curve2_last_len = curve_hull_length_sq(&curve2); // Edge case: 0-length curves have no match - if curve1_last_len == 0.0 { return smallvec![]; } - if curve2_last_len == 0.0 { return smallvec![]; } + if curve1_last_len == 0.0 { + return smallvec![]; + } + if curve2_last_len == 0.0 { + return smallvec![]; + } // Iterate to refine the match loop { let curve2_len = if curve2_last_len > accuracy_squared { // Clip curve2 against curve1 - let clip_t = clip(&curve2, &curve1); - let clip_t = match clip_t { - ClipResult::None => { return smallvec![]; }, - ClipResult::Some(clip_t) => clip_t, - ClipResult::SecondCurveIsLinear => { + let clip_t = clip(&curve2, &curve1); + let clip_t = match clip_t { + ClipResult::None => { + return smallvec![]; + } + ClipResult::Some(clip_t) => clip_t, + ClipResult::SecondCurveIsLinear => { return intersections_with_linear_section(&curve1, &curve2) .into_iter() .map(|(t1, t2)| (curve1.t_for_t(t1), curve2.t_for_t(t2))) - .collect(); + .collect(); } }; @@ -224,21 +264,23 @@ where C::Point: 'a+Coordinate2D { // Work out the length of the new curve curve_hull_length_sq(&curve2) - } else { + } else { curve2_last_len }; let curve1_len = if curve1_last_len > accuracy_squared { // Clip curve1 against curve2 - let clip_t = clip(&curve1, &curve2); - let clip_t = match clip_t { - ClipResult::None => { return smallvec![]; }, - ClipResult::Some(clip_t) => clip_t, - ClipResult::SecondCurveIsLinear => { + let clip_t = clip(&curve1, &curve2); + let clip_t = match clip_t { + ClipResult::None => { + return smallvec![]; + } + ClipResult::Some(clip_t) => clip_t, + ClipResult::SecondCurveIsLinear => { return intersections_with_linear_section(&curve2, &curve1) .into_iter() .map(|(t2, t1)| (curve1.t_for_t(t1), curve2.t_for_t(t2))) - .collect(); + .collect(); } }; @@ -252,31 +294,37 @@ where C::Point: 'a+Coordinate2D { if curve1_len <= accuracy_squared && curve2_len <= accuracy_squared { // Found a point to the required accuracy: return it, in coordinates relative to the original curve - if curve1.fast_bounding_box::>().overlaps(&curve2.fast_bounding_box::>()) { + if curve1 + .fast_bounding_box::>() + .overlaps(&curve2.fast_bounding_box::>()) + { let (t_min1, t_max1) = curve1.original_curve_t_values(); let (t_min2, t_max2) = curve2.original_curve_t_values(); - return smallvec![((t_min1+t_max1)*0.5, (t_min2+t_max2)*0.5)]; + return smallvec![((t_min1 + t_max1) * 0.5, (t_min2 + t_max2) * 0.5)]; } else { // Clipping algorithm found a point, but the two curves do not actually overlap, so reject them return smallvec![]; } } - if (curve1_last_len*0.8) <= curve1_len && (curve2_last_len*0.8) <= curve2_len { + if (curve1_last_len * 0.8) <= curve1_len && (curve2_last_len * 0.8) <= curve2_len { // If neither curve shrunk by 20%, then subdivide the one that shrunk the least - if curve1_len/curve1_last_len > curve2_len/curve2_last_len { + if curve1_len / curve1_last_len > curve2_len / curve2_last_len { // Curve1 shrunk less than curve2 - let (left, right) = (curve1.subsection(0.0, 0.5), curve1.subsection(0.5, 1.0)); - let left = curve_intersects_curve_clip_inner(left, curve2.clone(), accuracy_squared); - let right = curve_intersects_curve_clip_inner(right, curve2, accuracy_squared); + let (left, right) = (curve1.subsection(0.0, 0.5), curve1.subsection(0.5, 1.0)); + let left = + curve_intersects_curve_clip_inner(left, curve2.clone(), accuracy_squared); + let right = curve_intersects_curve_clip_inner(right, curve2, accuracy_squared); return join_subsections(&curve1, left, right, accuracy_squared); } else { // Curve2 shrunk less than curve1 - let (left, right) = (curve2.subsection(0.0, 0.5), curve2.subsection(0.5, 1.0)); - let left = curve_intersects_curve_clip_inner(curve1.clone(), left, accuracy_squared); - let right = curve_intersects_curve_clip_inner(curve1.clone(), right, accuracy_squared); + let (left, right) = (curve2.subsection(0.0, 0.5), curve2.subsection(0.5, 1.0)); + let left = + curve_intersects_curve_clip_inner(curve1.clone(), left, accuracy_squared); + let right = + curve_intersects_curve_clip_inner(curve1.clone(), right, accuracy_squared); return join_subsections(&curve1, left, right, accuracy_squared); } @@ -291,13 +339,19 @@ where C::Point: 'a+Coordinate2D { /// /// Determines the points at which two curves intersect using the Bezier clipping /// algorihtm -/// -pub fn curve_intersects_curve_clip<'a, C: BezierCurve>(curve1: &'a C, curve2: &'a C, accuracy: f64) -> SmallVec<[(f64, f64); 8]> -where C::Point: 'a+Coordinate2D { +/// +pub fn curve_intersects_curve_clip<'a, C: BezierCurve>( + curve1: &'a C, + curve2: &'a C, + accuracy: f64, +) -> SmallVec<[(f64, f64); 8]> +where + C::Point: 'a + Coordinate2D, +{ // Start with the entire span of both curves let curve1 = curve1.section(0.0, 1.0); let curve2 = curve2.section(0.0, 1.0); // Perform the clipping algorithm on these curves - curve_intersects_curve_clip_inner(curve1, curve2, accuracy*accuracy) + curve_intersects_curve_clip_inner(curve1, curve2, accuracy * accuracy) } diff --git a/src/bezier/intersection/curve_line.rs b/src/bezier/intersection/curve_line.rs index d9ccf960..1bd7ce4b 100644 --- a/src/bezier/intersection/curve_line.rs +++ b/src/bezier/intersection/curve_line.rs @@ -1,11 +1,11 @@ -use super::super::curve::*; use super::super::basis::*; +use super::super::curve::*; +use crate::consts::*; use crate::geo::*; use crate::line::*; -use crate::consts::*; -use smallvec::*; use roots::{find_roots_cubic, find_roots_quadratic, Roots}; +use smallvec::*; /// /// Solves the roots for a set of cubic coefficients @@ -36,84 +36,90 @@ fn solve_roots(p: (f64, f64, f64, f64)) -> Roots { /// /// Return value is a vector of (curve_t, line_t, intersection_point) values. The `line_t` value can be outside the /// original line, so this will return all the points on the curve that lie on a line of infinite length. -/// -pub fn curve_intersects_ray>(curve: &C, line: &L) -> SmallVec<[(f64, f64, C::Point); 4]> -where C::Point: Coordinate2D { +/// +pub fn curve_intersects_ray>( + curve: &C, + line: &L, +) -> SmallVec<[(f64, f64, C::Point); 4]> +where + C::Point: Coordinate2D, +{ // Based upon https://www.particleincell.com/2013/cubic-line-intersection/ // Line coefficients - let (p1, p2) = line.points(); - let a = p2.y()-p1.y(); - let b = p1.x()-p2.x(); - let c = p1.x()*(p1.y()-p2.y()) + p1.y()*(p2.x()-p1.x()); + let (p1, p2) = line.points(); + let a = p2.y() - p1.y(); + let b = p1.x() - p2.x(); + let c = p1.x() * (p1.y() - p2.y()) + p1.y() * (p2.x() - p1.x()); if a == 0.0 && b == 0.0 { return smallvec![]; } // Bezier coefficients - let (w2, w3) = curve.control_points(); - let (w1, w4) = (curve.start_point(), curve.end_point()); - let bx = bezier_coefficients(0, &w1, &w2, &w3, &w4); - let by = bezier_coefficients(1, &w1, &w2, &w3, &w4); - - let p = ( - a*bx.0+b*by.0, - a*bx.1+b*by.1, - a*bx.2+b*by.2, - a*bx.3+b*by.3+c + let (w2, w3) = curve.control_points(); + let (w1, w4) = (curve.start_point(), curve.end_point()); + let bx = bezier_coefficients(0, &w1, &w2, &w3, &w4); + let by = bezier_coefficients(1, &w1, &w2, &w3, &w4); + + let p = ( + a * bx.0 + b * by.0, + a * bx.1 + b * by.1, + a * bx.2 + b * by.2, + a * bx.3 + b * by.3 + c, ); - let roots = solve_roots(p); - let roots: SmallVec<[f64; 4]> = match roots { - Roots::No(_) => smallvec![], - Roots::One(r) => SmallVec::from_slice(&r), - Roots::Two(r) => SmallVec::from_slice(&r), + let roots = solve_roots(p); + let roots: SmallVec<[f64; 4]> = match roots { + Roots::No(_) => smallvec![], + Roots::One(r) => SmallVec::from_slice(&r), + Roots::Two(r) => SmallVec::from_slice(&r), Roots::Three(r) => SmallVec::from_slice(&r), - Roots::Four(r) => SmallVec::from_buf(r) + Roots::Four(r) => SmallVec::from_buf(r), }; let mut result = smallvec![]; for t in roots.into_iter() { // Allow a small amount of 'slop' for items at the start/end as the root finding is not exact - let t = - if t < 0.0 && t > -0.01 { - // If the line passes close enough to the start of the curve, set t to 0 - let factor = (a*a + b*b).sqrt(); - let (a, b, c) = (a/factor, b/factor, c/factor); - let start_point = &w1; - - if (start_point.x()*a + start_point.y()*b + c).abs() < SMALL_DISTANCE { - 0.0 - } else { - t - } - } else if t > 1.0 && t < 1.01 { - // If the line passes close enough to the end of the curve, set t to 1 - let factor = (a*a + b*b).sqrt(); - let (a, b, c) = (a/factor, b/factor, c/factor); - let end_point = &w4; - - if (end_point.x()*a + end_point.y()*b + c).abs() < SMALL_DISTANCE { - 1.0 - } else { - t - } - } else { t }; + let t = if t < 0.0 && t > -0.01 { + // If the line passes close enough to the start of the curve, set t to 0 + let factor = (a * a + b * b).sqrt(); + let (a, b, c) = (a / factor, b / factor, c / factor); + let start_point = &w1; + + if (start_point.x() * a + start_point.y() * b + c).abs() < SMALL_DISTANCE { + 0.0 + } else { + t + } + } else if t > 1.0 && t < 1.01 { + // If the line passes close enough to the end of the curve, set t to 1 + let factor = (a * a + b * b).sqrt(); + let (a, b, c) = (a / factor, b / factor, c / factor); + let end_point = &w4; + + if (end_point.x() * a + end_point.y() * b + c).abs() < SMALL_DISTANCE { + 1.0 + } else { + t + } + } else { + t + }; if t >= 0.0 && t <= 1.0 { // Calculate the position on the curve let pos = de_casteljau4(t, w1, w2, w3, w4); // Coordinates on the curve - let x = pos.x(); - let y = pos.y(); + let x = pos.x(); + let y = pos.y(); // Solve for the position on the line let s = if b.abs() > a.abs() { - (x-p1.x())/(p2.x()-p1.x()) + (x - p1.x()) / (p2.x() - p1.x()) } else { - (y-p1.y())/(p2.y()-p1.y()) + (y - p1.y()) / (p2.y() - p1.y()) }; test_assert!(!s.is_nan()); @@ -130,9 +136,14 @@ where C::Point: Coordinate2D { /// Find the t values where a curve intersects a line /// /// Return value is a vector of (curve_t, line_t, intersection_point) values -/// -pub fn curve_intersects_line>(curve: &C, line: &L) -> SmallVec<[(f64, f64, C::Point); 4]> -where C::Point: Coordinate2D { +/// +pub fn curve_intersects_line>( + curve: &C, + line: &L, +) -> SmallVec<[(f64, f64, C::Point); 4]> +where + C::Point: Coordinate2D, +{ let mut ray_intersections = curve_intersects_ray(curve, line); ray_intersections.retain(|(_t, s, _pos)| s >= &mut 0.0 && s <= &mut 1.0); diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index ce18edca..524d5bb4 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -1,14 +1,14 @@ -use super::super::curve::*; +use super::super::super::consts::*; use super::super::super::geo::*; use super::super::super::line::*; -use super::super::super::consts::*; +use super::super::curve::*; use std::f64; /// /// A 'fat line' is a line with a width. It's used in bezier intersection algorithms, /// in particular the clipping algorithm described by Sederberg and Nishita -/// +/// #[derive(Debug)] pub struct FatLine { /// The distance from the line to the upper part of the 'fat line' @@ -18,68 +18,78 @@ pub struct FatLine { d_max: f64, /// The coefficients (a, b, c) in the equation ax+bx+c (where a^2+b^2 = 0) - coeff: (f64, f64, f64) + coeff: (f64, f64, f64), } impl FatLine { /// /// Returns the distance between the point and the central line - /// + /// #[inline] pub fn distance(&self, point: &Point) -> f64 { let (a, b, c) = self.coeff; - a*point.x() + b*point.y() + c + a * point.x() + b * point.y() + c } /// /// Given a bezier curve, returns another curve whose X axis is the distance /// from the central line and the Y axis varies from 0 to 1, with a uniform /// distribution of t values. - /// + /// /// This is used in the bezier clipping algorithm to discover where a bezier /// curve clips against this line. - /// - pub fn distance_curve>(&self, curve: &FromCurve) -> ToCurve - where FromCurve::Point: Coordinate2D { - let (cp1, cp2) = curve.control_points(); - - let start = FromCurve::Point::from_components(&[self.distance(&curve.start_point()), 0.0]); - let end = FromCurve::Point::from_components(&[self.distance(&curve.end_point()), 1.0]); - let cp1 = FromCurve::Point::from_components(&[self.distance(&cp1), 1.0/3.0]); - let cp2 = FromCurve::Point::from_components(&[self.distance(&cp2), 2.0/3.0]); + /// + pub fn distance_curve< + FromCurve: BezierCurve, + ToCurve: BezierCurveFactory, + >( + &self, + curve: &FromCurve, + ) -> ToCurve + where + FromCurve::Point: Coordinate2D, + { + let (cp1, cp2) = curve.control_points(); + + let start = FromCurve::Point::from_components(&[self.distance(&curve.start_point()), 0.0]); + let end = FromCurve::Point::from_components(&[self.distance(&curve.end_point()), 1.0]); + let cp1 = FromCurve::Point::from_components(&[self.distance(&cp1), 1.0 / 3.0]); + let cp2 = FromCurve::Point::from_components(&[self.distance(&cp2), 2.0 / 3.0]); ToCurve::from_points(start, (cp1, cp2), end) } /// /// Returns the convex hull of a curve returned by distance_curve - /// + /// /// We can use some of the properties of the distance_curve to simplify how this /// is worked out (specifically, we know the points are sorted vertically already /// so we only need to know if the two control points are on the same side or not) - /// - fn distance_curve_convex_hull(distance_curve: &C) -> Vec - where C::Point: Coordinate2D { + /// + fn distance_curve_convex_hull(distance_curve: &C) -> Vec + where + C::Point: Coordinate2D, + { // Read the points from the curve - let start = distance_curve.start_point(); - let (cp1, cp2) = distance_curve.control_points(); - let end = distance_curve.end_point(); + let start = distance_curve.start_point(); + let (cp1, cp2) = distance_curve.control_points(); + let end = distance_curve.end_point(); // Compute the x component of the distances of cp1 and cp2 from the central line defined by start->end // These are the m and c values for y=mx+c assuming that start.y() = 0 and end.y() = 1 which is true for the distance curve - let m = end.x()-start.x(); + let m = end.x() - start.x(); let c = start.x(); - let dx1 = cp1.x() - (m*(1.0/3.0)+c); - let dx2 = cp2.x() - (m*(2.0/3.0)+c); + let dx1 = cp1.x() - (m * (1.0 / 3.0) + c); + let dx2 = cp2.x() - (m * (2.0 / 3.0) + c); // If they have the same sign, they're on the same side - let on_same_side = dx1*dx2 >= 0.0; + let on_same_side = dx1 * dx2 >= 0.0; // Ordering on the convex hull depends only on if cp1 and cp2 are on the same side or not if on_same_side { // cp1 or cp2 might be inside the hull - let dist_ratio = dx1/dx2; + let dist_ratio = dx1 / dx2; if dist_ratio >= 2.0 { // cp2 is in the hull (between the line cp1->end and start->end) @@ -99,7 +109,7 @@ impl FatLine { /// /// Rounds values very close to 0 or 1 to 0 or 1 - /// + /// #[inline] fn round_y_value(y: f64) -> f64 { if y < 0.00001 && y > -0.001 { @@ -113,17 +123,28 @@ impl FatLine { /// /// Given an x pos on a line, solves for the y point - /// + /// #[inline] - fn solve_line_y((x1, x2): (f64, f64), (p1, p2): (&Point, &Point)) -> (Option, Option) { + fn solve_line_y( + (x1, x2): (f64, f64), + (p1, p2): (&Point, &Point), + ) -> (Option, Option) { let min_x = p1.x().min(p2.x()); let max_x = p1.x().max(p2.x()); - let m = (p2.y()-p1.y())/(p2.x()-p1.x()); + let m = (p2.y() - p1.y()) / (p2.x() - p1.x()); let c = p1.y() - m * p1.x(); - let y1 = if x1 >= min_x && x1 <= max_x { Some(Self::round_y_value(m*x1 + c)) } else { None }; - let y2 = if x2 >= min_x && x2 <= max_x { Some(Self::round_y_value(m*x2 + c)) } else { None }; + let y1 = if x1 >= min_x && x1 <= max_x { + Some(Self::round_y_value(m * x1 + c)) + } else { + None + }; + let y2 = if x2 >= min_x && x2 <= max_x { + Some(Self::round_y_value(m * x2 + c)) + } else { + None + }; (y1, y2) } @@ -131,52 +152,63 @@ impl FatLine { /// /// Clips the curve against this fat line. This attempts to find two new t values such /// that values outside that range are guaranteed not to lie within this fat line. - /// + /// /// (This doesn't guarantee that the t values lie precisely within the line, though it's /// usually possible to iterate to improve the precision of the match) - /// - pub fn clip_t(&self, curve: &C) -> Option<(f64, f64)> - where C::Point: Coordinate2D { + /// + pub fn clip_t(&self, curve: &C) -> Option<(f64, f64)> + where + C::Point: Coordinate2D, + { // The 'distance' curve is a bezier curve where 'x' is the distance to the central line from the curve and 'y' is the t value where that distance occurs - let distance_curve = self.distance_curve::<_, Curve>(curve); + let distance_curve = self.distance_curve::<_, Curve>(curve); // The convex hull encloses the distance curve, and can be used to find the y values where it's between d_min and d_max // As y=t due to how we construct the distance curve these are also the t values // We make use of the fact that the hull always has the start point at the start - let distance_convex_hull = Self::distance_curve_convex_hull(&distance_curve); + let distance_convex_hull = Self::distance_curve_convex_hull(&distance_curve); // To solve for t, we need to find where the two edge lines cross d_min and d_max - let num_points = distance_convex_hull.len(); - let mut t1 = f64::MAX; - let mut t2 = f64::MIN; - let d_min = self.d_min; - let d_max = self.d_max; + let num_points = distance_convex_hull.len(); + let mut t1 = f64::MAX; + let mut t2 = f64::MIN; + let d_min = self.d_min; + let d_max = self.d_max; for idx in 0..num_points { // Solve where this part of the convex hull crosses this line - let (p1, p2) = (&distance_convex_hull[idx], &distance_convex_hull[(idx+1)%num_points]); - let hull_line = (p1, p2); - let (t1a, t2a) = Self::solve_line_y((d_min, d_max), hull_line); + let (p1, p2) = ( + &distance_convex_hull[idx], + &distance_convex_hull[(idx + 1) % num_points], + ); + let hull_line = (p1, p2); + let (t1a, t2a) = Self::solve_line_y((d_min, d_max), hull_line); // The y axis indicates where the hull crosses from inside to outside the fat line if let Some(t1a) = t1a { // Line crossed d_min - if t1a >= 0.0 && t1a <= 1.0 { + if t1a >= 0.0 && t1a <= 1.0 { t1 = t1.min(t1a); t2 = t2.max(t1a); } } - + if let Some(t2a) = t2a { // Line crossed d_max - if t2a >= 0.0 && t2a <= 1.0 { + if t2a >= 0.0 && t2a <= 1.0 { t1 = t1.min(t2a); t2 = t2.max(t2a); } } // If the start or end point is inside the fat line then it is also within the clipping area - if p1.x() <= d_max && p1.x() >= d_min { t1 = t1.min(p1.y()); t2 = t2.max(p1.y()); } - if p2.x() <= d_max && p2.x() >= d_min { t1 = t1.min(p2.y()); t2 = t2.max(p2.y()); } + if p1.x() <= d_max && p1.x() >= d_min { + t1 = t1.min(p1.y()); + t2 = t2.max(p1.y()); + } + if p2.x() <= d_max && p2.x() >= d_min { + t1 = t1.min(p2.y()); + t2 = t2.max(p2.y()); + } } if t1 > t2 { @@ -186,9 +218,12 @@ impl FatLine { Some((0.0, t2)) } else { // No part of the hull crossed the line (either entirely inside or outside) - let hull_x = distance_convex_hull.into_iter().map(|p| p.x()).collect::>(); - let hull_min_x = hull_x.iter().cloned().fold(f64::INFINITY, f64::min); - let hull_max_x = hull_x.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + let hull_x = distance_convex_hull + .into_iter() + .map(|p| p.x()) + .collect::>(); + let hull_min_x = hull_x.iter().cloned().fold(f64::INFINITY, f64::min); + let hull_max_x = hull_x.iter().cloned().fold(f64::NEG_INFINITY, f64::max); if self.d_min > hull_max_x || self.d_max < hull_min_x { // Convex hull is outside the line @@ -235,50 +270,54 @@ impl FatLine { impl FatLine { /// /// Creates a new fatline from a central line and two points representing its outer edges - /// + /// fn from_line_and_points(line: L, p1: L::Point, p2: L::Point) -> FatLine - where L::Point: Coordinate+Coordinate2D { + where + L::Point: Coordinate + Coordinate2D, + { // Coefficients for the line - let (a, b, c) = line_coefficients_2d(&line); + let (a, b, c) = line_coefficients_2d(&line); // Compute the distances to the control points - let d1 = a*p1.x() + b*p1.y() + c; - let d2 = a*p2.x() + b*p2.y() + c; + let d1 = a * p1.x() + b * p1.y() + c; + let d2 = a * p2.x() + b * p2.y() + c; // This is the 'estimated fit' shortcut suggested by Sederberg/Nishta in their paper rather than the tighest fitting line - let (d_min, d_max) = if d1*d2 > 0.0 { + let (d_min, d_max) = if d1 * d2 > 0.0 { // Both control points on the same side of the line ( - (3.0/4.0) * (d1.min(d2).min(0.0)), - (3.0/4.0) * (d1.max(d2).max(0.0)) + (3.0 / 4.0) * (d1.min(d2).min(0.0)), + (3.0 / 4.0) * (d1.max(d2).max(0.0)), ) } else { // Control points on opposite sides of the line ( - (4.0/9.0) * (d1.min(d2).min(0.0)), - (4.0/9.0) * (d1.max(d2).max(0.0)) + (4.0 / 9.0) * (d1.min(d2).min(0.0)), + (4.0 / 9.0) * (d1.max(d2).max(0.0)), ) }; FatLine { - d_min: d_min, - d_max: d_max, - coeff: (a, b, c) + d_min: d_min, + d_max: d_max, + coeff: (a, b, c), } } /// /// Creates a new fatline from a curve - /// + /// pub fn from_curve(curve: &C) -> FatLine - where C::Point: Coordinate+Coordinate2D { + where + C::Point: Coordinate + Coordinate2D, + { // Line between the start and end points of the curve - let line = (curve.start_point(), curve.end_point()); - let (cp1, cp2) = curve.control_points(); + let line = (curve.start_point(), curve.end_point()); + let (cp1, cp2) = curve.control_points(); // If the start point and the end point are at the same location, use the gap between the control points to set the line direction instead let line = if line.0.is_near_to(&line.1, 0.0000001) { - (curve.start_point(), curve.start_point() + (cp2-cp1)) + (curve.start_point(), curve.start_point() + (cp2 - cp1)) } else { line }; @@ -288,50 +327,52 @@ impl FatLine { /// /// Creates a perpendicular fatline from a curve - /// + /// pub fn from_curve_perpendicular(curve: &C) -> FatLine - where C::Point: Coordinate+Coordinate2D { + where + C::Point: Coordinate + Coordinate2D, + { let (start_point, end_point) = (curve.start_point(), curve.end_point()); // If the start point and the end point are at the same location, use the gap between the control points to set the line direction instead let end_point = if start_point.is_near_to(&end_point, 0.0000001) { let (cp1, cp2) = curve.control_points(); - start_point + (cp2-cp1) + start_point + (cp2 - cp1) } else { end_point }; // Line between the start and end points of the curve - let line = (start_point, end_point); + let line = (start_point, end_point); // Mid-point of the line - let mid_point = line.point_at_pos(0.5); + let mid_point = line.point_at_pos(0.5); // Target point to generate a perpendicular line - let offset = mid_point - start_point; - let offset = C::Point::from_components(&[offset.y(), offset.x()]); - let target_point = mid_point + offset; + let offset = mid_point - start_point; + let offset = C::Point::from_components(&[offset.y(), offset.x()]); + let target_point = mid_point + offset; // Perpendicular line - let line = (mid_point, target_point); + let line = (mid_point, target_point); // Compute the distances to all of the points - let (cp1, cp2) = curve.control_points(); - let (a, b, c) = line_coefficients_2d(&line); + let (cp1, cp2) = curve.control_points(); + let (a, b, c) = line_coefficients_2d(&line); - let d1 = a*start_point.x() + b*start_point.y() + c; - let d2 = a*cp1.x() + b*cp1.y() + c; - let d3 = a*cp2.x() + b*cp2.y() + c; - let d4 = a*end_point.x() + b*end_point.y() + c; + let d1 = a * start_point.x() + b * start_point.y() + c; + let d2 = a * cp1.x() + b * cp1.y() + c; + let d3 = a * cp2.x() + b * cp2.y() + c; + let d4 = a * end_point.x() + b * end_point.y() + c; // No approximation to improve the line fit here - let d_min = d1.min(d2).min(d3).min(d4); - let d_max = d1.max(d2).max(d3).max(d4); + let d_min = d1.min(d2).min(d3).min(d4); + let d_max = d1.max(d2).max(d3).max(d4); FatLine { - coeff: (a, b, c), - d_min: d_min, - d_max: d_max + coeff: (a, b, c), + d_min: d_min, + d_max: d_max, } } } @@ -343,15 +384,17 @@ mod test { impl FatLine { /// /// Creates a new fat line - /// + /// pub fn new(line: L, d_min: f64, d_max: f64) -> FatLine - where L::Point: Coordinate2D { - let (a, b, c) = line_coefficients_2d(&line); + where + L::Point: Coordinate2D, + { + let (a, b, c) = line_coefficients_2d(&line); FatLine { - d_min: d_min, - d_max: d_max, - coeff: (a, b, c) + d_min: d_min, + d_max: d_max, + coeff: (a, b, c), } } @@ -359,12 +402,17 @@ mod test { /// Clips a bezier curve against this fat line. This returns a new bezier curve where /// parts that are outside the line have been clipped away, but does not necessarily /// guarantee that it's just the portion within the line. - /// + /// /// This call can be iterated to improve the fit in many cases, and will return none /// in the case where the curve is not within the line. - /// - pub fn clip>(&self, curve: &FromCurve) -> Option - where FromCurve::Point: Coordinate2D { + /// + pub fn clip>( + &self, + curve: &FromCurve, + ) -> Option + where + FromCurve::Point: Coordinate2D, + { if let Some((t1, t2)) = self.clip_t(curve) { Some(ToCurve::from_curve(&curve.section(t1, t2))) } else { @@ -375,120 +423,152 @@ mod test { #[test] fn distance_to_horizontal_line() { - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); - assert!((fat_line.distance(&Coord2(0.0, 8.0))-4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(0.0, 0.0))- -4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(0.0, 8.0)) - 4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(0.0, 0.0)) - -4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(3.0, 8.0))-4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(3.0, 0.0))- -4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(3.0, 8.0)) - 4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(3.0, 0.0)) - -4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(5.0, 8.0))-4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(5.0, 0.0))- -4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(5.0, 8.0)) - 4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(5.0, 0.0)) - -4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(200.0, 8.0))-4.0).abs() < 0.0001); - assert!((fat_line.distance(&Coord2(200.0, 0.0))- -4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(200.0, 8.0)) - 4.0).abs() < 0.0001); + assert!((fat_line.distance(&Coord2(200.0, 0.0)) - -4.0).abs() < 0.0001); } #[test] fn convex_hull_basic() { - let hull_curve = Curve::from_points(Coord2(1.0, 0.0), (Coord2(5.0, 1.0/3.0), Coord2(6.0, 2.0/3.0)), Coord2(4.0, 1.0)); - let hull = FatLine::distance_curve_convex_hull(&hull_curve); + let hull_curve = Curve::from_points( + Coord2(1.0, 0.0), + (Coord2(5.0, 1.0 / 3.0), Coord2(6.0, 2.0 / 3.0)), + Coord2(4.0, 1.0), + ); + let hull = FatLine::distance_curve_convex_hull(&hull_curve); println!("{:?}", hull); - assert!(hull.len()==4); + assert!(hull.len() == 4); assert!(hull[0].distance_to(&Coord2(1.0, 0.0)) < 0.001); - assert!(hull[1].distance_to(&Coord2(5.0, 1.0/3.0)) < 0.001); - assert!(hull[2].distance_to(&Coord2(6.0, 2.0/3.0)) < 0.001); + assert!(hull[1].distance_to(&Coord2(5.0, 1.0 / 3.0)) < 0.001); + assert!(hull[2].distance_to(&Coord2(6.0, 2.0 / 3.0)) < 0.001); assert!(hull[3].distance_to(&Coord2(4.0, 1.0)) < 0.001); } #[test] fn convex_hull_concave_cp2() { - let hull_curve = Curve::from_points(Coord2(1.0, 0.0), (Coord2(4.0, 1.0/3.0), Coord2(3.0, 2.0/3.0)), Coord2(4.0, 1.0)); - let hull = FatLine::distance_curve_convex_hull(&hull_curve); + let hull_curve = Curve::from_points( + Coord2(1.0, 0.0), + (Coord2(4.0, 1.0 / 3.0), Coord2(3.0, 2.0 / 3.0)), + Coord2(4.0, 1.0), + ); + let hull = FatLine::distance_curve_convex_hull(&hull_curve); println!("{:?}", hull); - assert!(hull.len()==3); + assert!(hull.len() == 3); assert!(hull[0].distance_to(&Coord2(1.0, 0.0)) < 0.001); - assert!(hull[1].distance_to(&Coord2(4.0, 1.0/3.0)) < 0.001); + assert!(hull[1].distance_to(&Coord2(4.0, 1.0 / 3.0)) < 0.001); assert!(hull[2].distance_to(&Coord2(4.0, 1.0)) < 0.001); } #[test] fn convex_hull_concave_cp1() { - let hull_curve = Curve::from_points(Coord2(1.0, 0.0), (Coord2(4.0, 1.0/3.0), Coord2(8.0, 2.0/3.0)), Coord2(4.0, 1.0)); - let hull = FatLine::distance_curve_convex_hull(&hull_curve); + let hull_curve = Curve::from_points( + Coord2(1.0, 0.0), + (Coord2(4.0, 1.0 / 3.0), Coord2(8.0, 2.0 / 3.0)), + Coord2(4.0, 1.0), + ); + let hull = FatLine::distance_curve_convex_hull(&hull_curve); println!("{:?}", hull); - assert!(hull.len()==3); + assert!(hull.len() == 3); assert!(hull[0].distance_to(&Coord2(1.0, 0.0)) < 0.001); - assert!(hull[1].distance_to(&Coord2(8.0, 2.0/3.0)) < 0.001); + assert!(hull[1].distance_to(&Coord2(8.0, 2.0 / 3.0)) < 0.001); assert!(hull[2].distance_to(&Coord2(4.0, 1.0)) < 0.001); } #[test] fn convex_hull_opposite_sides() { - let hull_curve = Curve::from_points(Coord2(1.0, 0.0), (Coord2(4.0, 1.0/3.0), Coord2(1.0, 2.0/3.0)), Coord2(4.0, 1.0)); - let hull = FatLine::distance_curve_convex_hull(&hull_curve); + let hull_curve = Curve::from_points( + Coord2(1.0, 0.0), + (Coord2(4.0, 1.0 / 3.0), Coord2(1.0, 2.0 / 3.0)), + Coord2(4.0, 1.0), + ); + let hull = FatLine::distance_curve_convex_hull(&hull_curve); println!("{:?}", hull); - assert!(hull.len()==4); + assert!(hull.len() == 4); assert!(hull[0].distance_to(&Coord2(1.0, 0.0)) < 0.001); - assert!(hull[1].distance_to(&Coord2(4.0, 1.0/3.0)) < 0.001); + assert!(hull[1].distance_to(&Coord2(4.0, 1.0 / 3.0)) < 0.001); assert!(hull[2].distance_to(&Coord2(4.0, 1.0)) < 0.001); - assert!(hull[3].distance_to(&Coord2(1.0, 2.0/3.0)) < 0.001); + assert!(hull[3].distance_to(&Coord2(1.0, 2.0 / 3.0)) < 0.001); } #[test] fn distance_curve_1() { // Horizontal line, with a y range of 2.0 to 7.0 - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); - let clip_curve = line_to_bezier::<_, Curve<_>>(&(Coord2(0.0, 0.0), Coord2(5.0, 8.0))); - let distance_curve = fat_line.distance_curve::<_, Curve>(&clip_curve); - - println!("{:?} {:?}", distance_curve.point_at_pos(0.0), distance_curve.point_at_pos(1.0)); - - assert!((distance_curve.point_at_pos(0.0).x()- -4.0).abs() < 0.0001); - assert!((distance_curve.point_at_pos(1.0).x()-4.0).abs() < 0.0001); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); + let clip_curve = line_to_bezier::<_, Curve<_>>(&(Coord2(0.0, 0.0), Coord2(5.0, 8.0))); + let distance_curve = fat_line.distance_curve::<_, Curve>(&clip_curve); + + println!( + "{:?} {:?}", + distance_curve.point_at_pos(0.0), + distance_curve.point_at_pos(1.0) + ); + + assert!((distance_curve.point_at_pos(0.0).x() - -4.0).abs() < 0.0001); + assert!((distance_curve.point_at_pos(1.0).x() - 4.0).abs() < 0.0001); } #[test] fn clip_line_1() { // Horizontal line, with a y range of 2.0 to 7.0 - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); - let clip_curve = line_to_bezier::<_, Curve<_>>(&(Coord2(0.0, 0.0), Coord2(5.0, 8.0))); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); + let clip_curve = line_to_bezier::<_, Curve<_>>(&(Coord2(0.0, 0.0), Coord2(5.0, 8.0))); - let clipped = fat_line.clip::<_, Curve>(&clip_curve).unwrap(); - let clipped = fat_line.clip::<_, Curve>(&clipped).unwrap(); + let clipped = fat_line.clip::<_, Curve>(&clip_curve).unwrap(); + let clipped = fat_line.clip::<_, Curve>(&clipped).unwrap(); let start_point = clipped.point_at_pos(0.0); - let end_point = clipped.point_at_pos(1.0); + let end_point = clipped.point_at_pos(1.0); println!("{:?} {:?}", start_point, end_point); println!("{:?}", fat_line.clip_t(&clip_curve)); - assert!((start_point.y()-2.0).abs() < 0.0001); - assert!((end_point.y()-7.0).abs() < 0.0001); + assert!((start_point.y() - 2.0).abs() < 0.0001); + assert!((end_point.y() - 7.0).abs() < 0.0001); } #[test] fn clip_t_1() { // Horizontal line, with a y range of 2.0 to 7.0 - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); - let distance_curve = fat_line.distance_curve::<_, Curve>(&clip_curve); - - let (t1, t2) = fat_line.clip_t(&clip_curve).unwrap(); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); + let distance_curve = fat_line.distance_curve::<_, Curve>(&clip_curve); + + let (t1, t2) = fat_line.clip_t(&clip_curve).unwrap(); let start_point = clip_curve.point_at_pos(t1); - let end_point = clip_curve.point_at_pos(t2); + let end_point = clip_curve.point_at_pos(t2); println!("Points on curve: {:?} {:?}", start_point, end_point); - println!("Distance-x: {:?} {:?}", distance_curve.point_at_pos(t1).x(), distance_curve.point_at_pos(t2).x()); - println!("Distance-y: {:?} {:?}", distance_curve.point_at_pos(t1).y(), distance_curve.point_at_pos(t2).y()); + println!( + "Distance-x: {:?} {:?}", + distance_curve.point_at_pos(t1).x(), + distance_curve.point_at_pos(t2).x() + ); + println!( + "Distance-y: {:?} {:?}", + distance_curve.point_at_pos(t1).y(), + distance_curve.point_at_pos(t2).y() + ); println!("T: {:?}", fat_line.clip_t(&clip_curve)); assert!(start_point.y() <= 2.0); @@ -498,8 +578,12 @@ mod test { #[test] fn clip_curve_1() { // Horizontal line, with a y range of 2.0 to 7.0 - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); let mut clipped = clip_curve.clone(); @@ -510,88 +594,104 @@ mod test { } let start_point = clipped.point_at_pos(0.0); - let end_point = clipped.point_at_pos(1.0); + let end_point = clipped.point_at_pos(1.0); println!("{:?} {:?}", start_point, end_point); println!("{:?}", fat_line.clip_t(&clip_curve)); - assert!((start_point.y()-2.0).abs() < 0.0001); - assert!((end_point.y()-7.0).abs() < 0.0001); + assert!((start_point.y() - 2.0).abs() < 0.0001); + assert!((end_point.y() - 7.0).abs() < 0.0001); } #[test] fn clip_curve_in_line() { - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -16.0, 16.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -16.0, 16.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); let clipped = fat_line.clip::<_, Curve>(&clip_curve); assert!(clipped.is_some()); let clipped = clipped.unwrap(); let start_point = clipped.point_at_pos(0.0); - let end_point = clipped.point_at_pos(1.0); + let end_point = clipped.point_at_pos(1.0); println!("{:?} {:?}", start_point, end_point); println!("{:?}", fat_line.clip_t(&clip_curve)); - assert!((start_point.x()-0.0).abs() < 0.0001); - assert!((end_point.x()-5.0).abs() < 0.0001); + assert!((start_point.x() - 0.0).abs() < 0.0001); + assert!((end_point.x() - 5.0).abs() < 0.0001); - assert!((start_point.y()-0.0).abs() < 0.0001); - assert!((end_point.y()-8.0).abs() < 0.0001); + assert!((start_point.y() - 0.0).abs() < 0.0001); + assert!((end_point.y() - 8.0).abs() < 0.0001); } #[test] fn clip_curve_start_in_line() { // If the start point is inside the fat line, we should only clip the end point - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -16.0, 3.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -16.0, 3.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); let clipped = fat_line.clip::<_, Curve>(&clip_curve); assert!(clipped.is_some()); let clipped = clipped.unwrap(); let start_point = clipped.point_at_pos(0.0); - let end_point = clipped.point_at_pos(1.0); + let end_point = clipped.point_at_pos(1.0); println!("{:?} {:?}", start_point, end_point); println!("{:?}", fat_line.clip_t(&clip_curve)); - assert!((start_point.x()-0.0).abs() < 0.0001); + assert!((start_point.x() - 0.0).abs() < 0.0001); assert!(end_point.x() <= 5.0); - assert!((start_point.y()-0.0).abs() < 0.0001); + assert!((start_point.y() - 0.0).abs() < 0.0001); assert!(end_point.y() <= 8.0); } #[test] fn clip_curve_end_in_line() { // If the end point is inside the fat line, we should only clip the start point - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 16.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 16.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); let clipped = fat_line.clip::<_, Curve>(&clip_curve); assert!(clipped.is_some()); let clipped = clipped.unwrap(); let start_point = clipped.point_at_pos(0.0); - let end_point = clipped.point_at_pos(1.0); + let end_point = clipped.point_at_pos(1.0); println!("{:?} {:?}", start_point, end_point); println!("{:?}", fat_line.clip_t(&clip_curve)); assert!(start_point.x() >= 0.0); - assert!((end_point.x()-5.0).abs() < 0.0001); + assert!((end_point.x() - 5.0).abs() < 0.0001); assert!(start_point.y() >= 0.0); - assert!((end_point.y()-8.0).abs() < 0.0001); + assert!((end_point.y() - 8.0).abs() < 0.0001); } #[test] fn clip_curve_outside_line() { // If the curve is entirely outside the line, we should return None - let fat_line = FatLine::new((Coord2(0.0, 20.0), Coord2(5.0, 20.0)), -2.0, 2.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); + let fat_line = FatLine::new((Coord2(0.0, 20.0), Coord2(5.0, 20.0)), -2.0, 2.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); let clipped = fat_line.clip::<_, Curve>(&clip_curve); assert!(clipped.is_none()); @@ -600,8 +700,12 @@ mod test { #[test] fn can_always_refine() { // Horizontal line, with a y range of 2.0 to 7.0 - let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); - let clip_curve = Curve::from_points(Coord2(0.0, 0.0), (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), Coord2(5.0, 8.0)); + let fat_line = FatLine::new((Coord2(0.0, 4.0), Coord2(5.0, 4.0)), -2.0, 3.0); + let clip_curve = Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 5.0), Coord2(5.0, 4.0)), + Coord2(5.0, 8.0), + ); let mut clipped = clip_curve.clone(); @@ -612,20 +716,28 @@ mod test { } let start_point = clipped.point_at_pos(0.0); - let end_point = clipped.point_at_pos(1.0); + let end_point = clipped.point_at_pos(1.0); println!("{:?} {:?}", start_point, end_point); println!("{:?}", fat_line.clip_t(&clip_curve)); - assert!((start_point.y()-2.0).abs() < 0.0001); - assert!((end_point.y()-7.0).abs() < 0.0001); + assert!((start_point.y() - 2.0).abs() < 0.0001); + assert!((end_point.y() - 7.0).abs() < 0.0001); } #[test] fn clip_curves_1() { // Two curves that clipped incorrectly from the clip intersection test - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); - let curve2 = Curve::from_points(Coord2(67.25, 113.48), (Coord2(146.18, 85.98), Coord2(109.35, 211.01)), Coord2(181.38, 199.44)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); + let curve2 = Curve::from_points( + Coord2(67.25, 113.48), + (Coord2(146.18, 85.98), Coord2(109.35, 211.01)), + Coord2(181.38, 199.44), + ); // Clip curve1 against curve2 let fat_line = FatLine::from_curve(&curve2); @@ -652,7 +764,11 @@ mod test { println!("{} pos {:?}, dist {:?}, actual {:?}", t, p1, d1, d2); } - println!("{:?} {:?}", (t1, t2), (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y())); + println!( + "{:?} {:?}", + (t1, t2), + (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y()) + ); assert!(curve1.point_at_pos(t1).x() < 81.79); assert!(curve1.point_at_pos(t2).x() > 179.86); @@ -667,8 +783,16 @@ mod test { // Coord2(179.87, 199.67) // Two curves that clipped incorrectly from the clip intersection test - let curve1 = Curve::from_points(Coord2(67.25, 113.48), (Coord2(155.03, 82.90), Coord2(99.65, 240.93)), Coord2(210.0, 190.0)); - let curve2 = Curve::from_points(Coord2(77.5, 103.75), (Coord2(97.5, 132.5), Coord2(130.0, 180.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(67.25, 113.48), + (Coord2(155.03, 82.90), Coord2(99.65, 240.93)), + Coord2(210.0, 190.0), + ); + let curve2 = Curve::from_points( + Coord2(77.5, 103.75), + (Coord2(97.5, 132.5), Coord2(130.0, 180.0)), + Coord2(220.0, 220.0), + ); // Clip curve1 against curve2 let fat_line = FatLine::from_curve(&curve2); @@ -689,7 +813,11 @@ mod test { println!("{} pos {:?}, dist {:?}, actual {:?}", t, p1, d1, d2); } - println!("{:?} {:?}", (t1, t2), (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y())); + println!( + "{:?} {:?}", + (t1, t2), + (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y()) + ); assert!(curve1.point_at_pos(t1).x() < 81.79); assert!(curve1.point_at_pos(t2).x() > 179.86); @@ -703,8 +831,16 @@ mod test { // Curve1 here forms a line that intercepts close to the start of Curve2, which seems to cause some accuracy issues - let curve1 = Curve::from_points(Coord2(80.317, 107.796), (Coord2(82.851, 111.424), Coord2(85.591, 115.301)), Coord2(88.615, 119.383)); - let curve2 = Curve::from_points(Coord2(81.248, 109.971), (Coord2(118.038, 104.934), Coord2(122.245, 142.970)), Coord2(134.936, 171.219)); + let curve1 = Curve::from_points( + Coord2(80.317, 107.796), + (Coord2(82.851, 111.424), Coord2(85.591, 115.301)), + Coord2(88.615, 119.383), + ); + let curve2 = Curve::from_points( + Coord2(81.248, 109.971), + (Coord2(118.038, 104.934), Coord2(122.245, 142.970)), + Coord2(134.936, 171.219), + ); // Clip curve1 against curve2 let fat_line = FatLine::from_curve(&curve2); @@ -725,7 +861,11 @@ mod test { println!("{} pos {:?}, dist {:?}, actual {:?}", t, p1, d1, d2); } - println!("{:?} {:?}", (t1, t2), (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y())); + println!( + "{:?} {:?}", + (t1, t2), + (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y()) + ); assert!(curve1.point_at_pos(t1).x() < 81.79); assert!(curve1.point_at_pos(t2).x() > 81.78); @@ -737,8 +877,16 @@ mod test { // // Coord2(81.78, 109.88) - let curve2 = Curve::from_points(Coord2(80.317, 107.796), (Coord2(82.851, 111.424), Coord2(85.591, 115.301)), Coord2(88.615, 119.383)); - let curve1 = Curve::from_points(Coord2(81.248, 109.971), (Coord2(118.038, 104.934), Coord2(122.245, 142.970)), Coord2(134.936, 171.219)); + let curve2 = Curve::from_points( + Coord2(80.317, 107.796), + (Coord2(82.851, 111.424), Coord2(85.591, 115.301)), + Coord2(88.615, 119.383), + ); + let curve1 = Curve::from_points( + Coord2(81.248, 109.971), + (Coord2(118.038, 104.934), Coord2(122.245, 142.970)), + Coord2(134.936, 171.219), + ); // Clip curve1 against curve2 let fat_line = FatLine::from_curve(&curve2); @@ -759,7 +907,11 @@ mod test { println!("{} pos {:?}, dist {:?}, actual {:?}", t, p1, d1, d2); } - println!("{:?} {:?}", (t1, t2), (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y())); + println!( + "{:?} {:?}", + (t1, t2), + (curve1.point_at_pos(t2).x(), curve1.point_at_pos(t2).y()) + ); assert!(curve1.point_at_pos(t1).x() < 81.79); assert!(curve1.point_at_pos(t2).x() > 81.78); diff --git a/src/bezier/intersection/mod.rs b/src/bezier/intersection/mod.rs index f6c845f3..f62c1024 100644 --- a/src/bezier/intersection/mod.rs +++ b/src/bezier/intersection/mod.rs @@ -1,8 +1,8 @@ -mod curve_line; mod curve_curve_clip; +mod curve_line; mod fat_line; mod self_intersection; -pub use self::curve_line::*; pub use self::curve_curve_clip::*; +pub use self::curve_line::*; pub use self::self_intersection::*; diff --git a/src/bezier/intersection/self_intersection.rs b/src/bezier/intersection/self_intersection.rs index 5dd09c74..14da6338 100644 --- a/src/bezier/intersection/self_intersection.rs +++ b/src/bezier/intersection/self_intersection.rs @@ -1,14 +1,16 @@ -use super::curve_curve_clip::*; +use super::super::super::geo::*; +use super::super::characteristics::*; use super::super::curve::*; use super::super::section::*; -use super::super::characteristics::*; -use super::super::super::geo::*; +use super::curve_curve_clip::*; /// /// If a cubic curve contains a loop, finds the t values where the curve self-intersects /// pub fn find_self_intersection_point(curve: &C, accuracy: f64) -> Option<(f64, f64)> -where C::Point: Coordinate+Coordinate2D { +where + C::Point: Coordinate + Coordinate2D, +{ let curve_type = curve.characteristics(); if curve_type == CurveCategory::Loop { @@ -22,13 +24,18 @@ where C::Point: Coordinate+Coordinate2D { /// /// Given a curve known to have a loop in it, subdivides it in order to determine where the intersection lies /// -fn find_intersection_point_in_loop(curve: CurveSection, accuracy: f64) -> Option<(f64, f64)> -where C::Point: Coordinate+Coordinate2D { +fn find_intersection_point_in_loop( + curve: CurveSection, + accuracy: f64, +) -> Option<(f64, f64)> +where + C::Point: Coordinate + Coordinate2D, +{ use self::CurveCategory::*; // The algorithm here is to divide the curve into two. We'll either find a smaller curve with a loop or split the curve in the middle of the loop // If we split in the middle of the loop, we use the bezier clipping algorithm to find where the two sides intersect - let (left, right) = (curve.subsection(0.0, 0.5), curve.subsection(0.5, 1.0)); + let (left, right) = (curve.subsection(0.0, 0.5), curve.subsection(0.5, 1.0)); let (left_type, right_type) = (left.characteristics(), right.characteristics()); match (left_type, right_type) { @@ -40,7 +47,7 @@ where C::Point: Coordinate+Coordinate2D { (Loop, _) => { // Loop is in the left side find_intersection_point_in_loop(left, accuracy) - }, + } (_, Loop) => { // Loop is in the right side @@ -51,7 +58,9 @@ where C::Point: Coordinate+Coordinate2D { // Can find the intersection by using the clipping algorithm let intersections = curve_intersects_curve_clip(&left, &right, accuracy); - if intersections.len() == 0 && left.start_point().is_near_to(&right.end_point(), accuracy) { + if intersections.len() == 0 + && left.start_point().is_near_to(&right.end_point(), accuracy) + { // Didn't find an intersection but the left and right curves start and end at the same position return Some((left.t_for_t(0.0), right.t_for_t(1.0))); } @@ -60,11 +69,14 @@ where C::Point: Coordinate+Coordinate2D { if intersections.len() == 1 { // Only found a single intersection - intersections.into_iter().nth(0) + intersections + .into_iter() + .nth(0) .map(|(t1, t2)| (left.t_for_t(t1), right.t_for_t(t2))) } else { // Intersection may include the point between the left and right curves (ignore any point that's at t=1 on the left or t=0 on the right) - intersections.into_iter() + intersections + .into_iter() .filter(|(t1, t2)| *t1 < 1.0 && *t2 > 0.0) .nth(0) .map(|(t1, t2)| (left.t_for_t(t1), right.t_for_t(t2))) diff --git a/src/bezier/length.rs b/src/bezier/length.rs index 7d58e166..fff3eca1 100644 --- a/src/bezier/length.rs +++ b/src/bezier/length.rs @@ -6,21 +6,19 @@ use crate::geo::*; /// Returns the length of the control polygon for a bezier curve /// pub fn control_polygon_length(curve: &Curve) -> f64 { - let p1 = curve.start_point(); - let (p2, p3) = curve.control_points(); - let p4 = curve.end_point(); + let p1 = curve.start_point(); + let (p2, p3) = curve.control_points(); + let p4 = curve.end_point(); - p1.distance_to(&p2) - + p2.distance_to(&p3) - + p3.distance_to(&p4) + p1.distance_to(&p2) + p2.distance_to(&p3) + p3.distance_to(&p4) } /// /// Returns the length of the chord of a bezier curve /// pub fn chord_length(curve: &Curve) -> f64 { - let p1 = curve.start_point(); - let p2 = curve.end_point(); + let p1 = curve.start_point(); + let p2 = curve.end_point(); p1.distance_to(&p2) } @@ -37,31 +35,32 @@ pub fn curve_length(curve: &Curve, max_error: f64) -> f64 { /// fn section_length<'a, Curve>(section: CurveSection<'a, Curve>, max_error: f64) -> f64 where -Curve: BezierCurve { + Curve: BezierCurve, +{ // This algorithm is described in Graphics Gems V IV.7 // The MIN_ERROR guards against cases where the length of a section fails to converge for some reason const MIN_ERROR: f64 = 1e-12; // Algorithm is recursive, but we use a vec as a stack to avoid overflowing (and to make the number of iterations easy to count) - let mut waiting = vec![(section, max_error)]; - let mut total_length = 0.0; + let mut waiting = vec![(section, max_error)]; + let mut total_length = 0.0; while let Some((section, max_error)) = waiting.pop() { // Estimate the error for the length of the curve - let polygon_length = control_polygon_length(§ion); - let chord_length = chord_length(§ion); + let polygon_length = control_polygon_length(§ion); + let chord_length = chord_length(§ion); - let error = (polygon_length - chord_length) * (polygon_length - chord_length); + let error = (polygon_length - chord_length) * (polygon_length - chord_length); // If the error is low enough, return the estimated length if error < max_error || max_error <= MIN_ERROR { - total_length += (2.0*chord_length + 2.0*polygon_length)/4.0; + total_length += (2.0 * chord_length + 2.0 * polygon_length) / 4.0; } else { // Subdivide the curve (each half has half the error tolerance) - let left = section.subsection(0.0, 0.5); - let right = section.subsection(0.5, 1.0); - let subsection_error = max_error / 2.0; + let left = section.subsection(0.0, 0.5); + let right = section.subsection(0.5, 1.0); + let subsection_error = max_error / 2.0; waiting.push((left, subsection_error)); waiting.push((right, subsection_error)); diff --git a/src/bezier/mod.rs b/src/bezier/mod.rs index 2de04d26..fbb98250 100644 --- a/src/bezier/mod.rs +++ b/src/bezier/mod.rs @@ -1,65 +1,65 @@ //! //! # Routines for describing, querying and manipulating Bezier curves -//! +//! //! Bezier curves are described by types that implement the `BezierCurve` trait, as a start point, an end point //! and two control points. The `Curve` type is provided as a base implementation but as with the other traits, //! the primary trait can be implemented on any suitable data structure. `BezierCurveFactory` is provided for //! types that can create new instances of themselves. -//! +//! //! Even for types that don't support the factory method, the `section()` method can be used to represent curve //! subsections efficiently. -//! +//! //! The `fit_curve()` function provides a way to fit a series of Bezier curves to one or more points using a //! least-mean-squared algorithm. -//! +//! //! The various `curve_intersects_X()` functions provide ways to determine where a curve meets another kind //! of object. //! -mod curve; -mod section; mod basis; -mod subdivide; -mod derivative; -mod tangent; -mod normal; mod bounds; +mod characteristics; +mod curve; mod deform; +mod derivative; +mod distort; mod fit; +mod intersection; +mod length; +mod normal; mod offset; mod offset_lms; mod offset_scaling; +mod overlaps; mod search; +mod section; mod solve; -mod overlaps; -mod intersection; -mod characteristics; -mod length; +mod subdivide; +mod tangent; mod walk; -mod distort; pub mod path; -pub use self::curve::*; -pub use self::section::*; pub use self::basis::*; -pub use self::subdivide::*; -pub use self::derivative::*; -pub use self::tangent::*; -pub use self::normal::*; pub use self::bounds::*; +pub use self::characteristics::*; +pub use self::curve::*; pub use self::deform::*; +pub use self::derivative::*; +pub use self::distort::*; pub use self::fit::*; +pub use self::intersection::*; +pub use self::length::*; +pub use self::normal::*; pub use self::offset::*; pub use self::offset_lms::*; pub use self::offset_scaling::*; +pub use self::overlaps::*; pub use self::search::*; +pub use self::section::*; pub use self::solve::*; -pub use self::overlaps::*; -pub use self::intersection::*; -pub use self::characteristics::*; -pub use self::length::*; +pub use self::subdivide::*; +pub use self::tangent::*; pub use self::walk::*; -pub use self::distort::*; pub use super::geo::*; diff --git a/src/bezier/normal.rs b/src/bezier/normal.rs index 966047c1..f68e296a 100644 --- a/src/bezier/normal.rs +++ b/src/bezier/normal.rs @@ -1,13 +1,13 @@ -use super::curve::*; +use super::super::geo::*; use super::basis::*; +use super::curve::*; use super::derivative::*; -use super::super::geo::*; // TODO: normalize should be a trait associated with coordinate rather than bezier curves (move outwards) /// /// Changes a point and its tangent into a normal -/// +/// pub trait Normalize { /// Computes the normal at a point, given its tangent fn to_normal(point: &Self, tangent: &Self) -> Vec; @@ -54,8 +54,8 @@ impl Normalize for Coordinate3D { /// /// Trait implemented by bezier curves where we can compute the normal -/// -pub trait NormalCurve : BezierCurve { +/// +pub trait NormalCurve: BezierCurve { /// /// Computes the tangent vector to the curve at the specified t value /// @@ -78,47 +78,49 @@ pub trait NormalCurve : BezierCurve { } impl NormalCurve for Curve -where Curve::Point: Normalize { +where + Curve::Point: Normalize, +{ fn tangent_at_pos(&self, t: f64) -> Curve::Point { // Extract the points that make up this curve - let w1 = self.start_point(); - let (w2, w3) = self.control_points(); - let w4 = self.end_point(); + let w1 = self.start_point(); + let (w2, w3) = self.control_points(); + let w4 = self.end_point(); - // If w1 == w2 or w3 == w4 there will be an anomaly at t=0.0 and t=1.0 + // If w1 == w2 or w3 == w4 there will be an anomaly at t=0.0 and t=1.0 // (it's probably mathematically correct to say there's no tangent at these points but the result is surprising and probably useless in a practical sense) - let t = if t == 0.0 { f64::EPSILON } else { t }; - let t = if t == 1.0 { 1.0-f64::EPSILON } else { t }; + let t = if t == 0.0 { f64::EPSILON } else { t }; + let t = if t == 1.0 { 1.0 - f64::EPSILON } else { t }; // Get the deriviative let (d1, d2, d3) = derivative4(w1, w2, w3, w4); // Get the tangent and the point at the specified t value - let tangent = de_casteljau3(t, d1, d2, d3); + let tangent = de_casteljau3(t, d1, d2, d3); tangent } fn normal_at_pos(&self, t: f64) -> Curve::Point { // Extract the points that make up this curve - let w1 = self.start_point(); - let (w2, w3) = self.control_points(); - let w4 = self.end_point(); + let w1 = self.start_point(); + let (w2, w3) = self.control_points(); + let w4 = self.end_point(); - // If w1 == w2 or w3 == w4 there will be an anomaly at t=0.0 and t=1.0 + // If w1 == w2 or w3 == w4 there will be an anomaly at t=0.0 and t=1.0 // (it's probably mathematically correct to say there's no normal at these points but the result is surprising and probably useless in a practical sense) - let t = if t == 0.0 { f64::EPSILON } else { t }; - let t = if t == 1.0 { 1.0-f64::EPSILON } else { t }; + let t = if t == 0.0 { f64::EPSILON } else { t }; + let t = if t == 1.0 { 1.0 - f64::EPSILON } else { t }; // Get the deriviative let (d1, d2, d3) = derivative4(w1, w2, w3, w4); // Get the tangent and the point at the specified t value - let point = de_casteljau4(t, w1, w2, w3, w4); - let tangent = de_casteljau3(t, d1, d2, d3); + let point = de_casteljau4(t, w1, w2, w3, w4); + let tangent = de_casteljau3(t, d1, d2, d3); // Compute the normal - let normal = Curve::Point::to_normal(&point, &tangent); + let normal = Curve::Point::to_normal(&point, &tangent); Curve::Point::from_components(&normal) } diff --git a/src/bezier/offset.rs b/src/bezier/offset.rs index a98db123..c97d2ed3 100644 --- a/src/bezier/offset.rs +++ b/src/bezier/offset.rs @@ -1,13 +1,15 @@ +use super::super::geo::*; use super::curve::*; use super::normal::*; use super::offset_scaling::*; -use super::super::geo::*; /// /// Computes a series of curves that approximate an offset curve from the specified origin curve. /// pub fn offset(curve: &Curve, initial_offset: f64, final_offset: f64) -> Vec -where Curve: BezierCurveFactory+NormalCurve, - Curve::Point: Normalize+Coordinate2D { +where + Curve: BezierCurveFactory + NormalCurve, + Curve::Point: Normalize + Coordinate2D, +{ offset_scaling(curve, initial_offset, final_offset) } diff --git a/src/bezier/offset_lms.rs b/src/bezier/offset_lms.rs index 65f75e5d..fd9af198 100644 --- a/src/bezier/offset_lms.rs +++ b/src/bezier/offset_lms.rs @@ -1,7 +1,7 @@ -use super::fit::*; +use super::characteristics::*; use super::curve::*; +use super::fit::*; use super::normal::*; -use super::characteristics::*; use crate::geo::*; use smallvec::*; @@ -14,18 +14,40 @@ use std::iter; /// produce good results). Too few subdivisions can result in flat sections in the curve, and too many can /// result in artifacts caused by overfitting. /// -pub fn offset_lms_sampling(curve: &Curve, normal_offset_for_t: NormalOffsetFn, tangent_offset_for_t: TangentOffsetFn, subdivisions: u32, max_error: f64) -> Option> -where Curve: BezierCurveFactory+NormalCurve, - Curve::Point: Normalize+Coordinate2D, - NormalOffsetFn: Fn(f64) -> f64, - TangentOffsetFn: Fn(f64) -> f64 { - if subdivisions < 2 { return None; } +pub fn offset_lms_sampling( + curve: &Curve, + normal_offset_for_t: NormalOffsetFn, + tangent_offset_for_t: TangentOffsetFn, + subdivisions: u32, + max_error: f64, +) -> Option> +where + Curve: BezierCurveFactory + NormalCurve, + Curve::Point: Normalize + Coordinate2D, + NormalOffsetFn: Fn(f64) -> f64, + TangentOffsetFn: Fn(f64) -> f64, +{ + if subdivisions < 2 { + return None; + } // Subdivide the curve by its major features - let sections: SmallVec<[_; 4]> = match features_for_curve(curve, 0.01) { - CurveFeatures::DoubleInflectionPoint(t1, t2) => { - let t1 = if t1 > 0.9999 { 1.0 } else if t1 < 0.0001 { 0.0 } else { t1 }; - let t2 = if t2 > 0.9999 { 1.0 } else if t2 < 0.0001 { 0.0 } else { t2 }; + let sections: SmallVec<[_; 4]> = match features_for_curve(curve, 0.01) { + CurveFeatures::DoubleInflectionPoint(t1, t2) => { + let t1 = if t1 > 0.9999 { + 1.0 + } else if t1 < 0.0001 { + 0.0 + } else { + t1 + }; + let t2 = if t2 > 0.9999 { + 1.0 + } else if t2 < 0.0001 { + 0.0 + } else { + t2 + }; if t2 > t1 { smallvec![(0.0, t1), (t1, t2), (t2, 1.0)] @@ -35,9 +57,21 @@ where Curve: BezierCurveFactory+NormalCurve, } CurveFeatures::Loop(t1, t3) => { - let t1 = if t1 > 0.9999 { 1.0 } else if t1 < 0.0001 { 0.0 } else { t1 }; - let t3 = if t3 > 0.9999 { 1.0 } else if t3 < 0.0001 { 0.0 } else { t3 }; - let t2 = (t1+t3)/2.0; + let t1 = if t1 > 0.9999 { + 1.0 + } else if t1 < 0.0001 { + 0.0 + } else { + t1 + }; + let t3 = if t3 > 0.9999 { + 1.0 + } else if t3 < 0.0001 { + 0.0 + } else { + t3 + }; + let t2 = (t1 + t3) / 2.0; if t3 > t1 { smallvec![(0.0, t1), (t1, t2), (t2, t3), (t3, 1.0)] @@ -54,34 +88,44 @@ where Curve: BezierCurveFactory+NormalCurve, } } - _ => { smallvec![(0.0, 1.0)] } + _ => { + smallvec![(0.0, 1.0)] + } }; // Each section is subdivided in turn subdivisions times to produce a set of sample points to fit against - let sections = sections.into_iter() + let sections = sections + .into_iter() .filter(|(t1, t2)| t1 != t2) .flat_map(|(t1, t2)| { - let step = (t2-t1)/(subdivisions as f64); - (0..subdivisions).into_iter().map(move |x| t1 + step * (x as f64)) + let step = (t2 - t1) / (subdivisions as f64); + (0..subdivisions) + .into_iter() + .map(move |x| t1 + step * (x as f64)) }) .chain(iter::once(1.0)); // Take a sample at each point - let sample_points = sections + let sample_points = sections .map(|t| { - let original_point = curve.point_at_pos(t); - let unit_tangent = curve.tangent_at_pos(t).to_unit_vector(); - let unit_normal = Curve::Point::to_normal(&original_point, &unit_tangent); - let unit_normal = Curve::Point::from_components(&unit_normal); - let normal_offset = normal_offset_for_t(t); - let tangent_offset = tangent_offset_for_t(t); + let original_point = curve.point_at_pos(t); + let unit_tangent = curve.tangent_at_pos(t).to_unit_vector(); + let unit_normal = Curve::Point::to_normal(&original_point, &unit_tangent); + let unit_normal = Curve::Point::from_components(&unit_normal); + let normal_offset = normal_offset_for_t(t); + let tangent_offset = tangent_offset_for_t(t); original_point + (unit_normal * normal_offset) + (unit_tangent * tangent_offset) }) .collect::>(); // Generate a curve using the sample points - let start_tangent = curve.tangent_at_pos(0.0).to_unit_vector(); - let end_tangent = curve.tangent_at_pos(1.0).to_unit_vector() * -1.0; - Some(fit_curve_cubic(&sample_points, &start_tangent, &end_tangent, max_error)) + let start_tangent = curve.tangent_at_pos(0.0).to_unit_vector(); + let end_tangent = curve.tangent_at_pos(1.0).to_unit_vector() * -1.0; + Some(fit_curve_cubic( + &sample_points, + &start_tangent, + &end_tangent, + max_error, + )) } diff --git a/src/bezier/offset_scaling.rs b/src/bezier/offset_scaling.rs index 8753e9a6..2b9ac944 100644 --- a/src/bezier/offset_scaling.rs +++ b/src/bezier/offset_scaling.rs @@ -1,30 +1,30 @@ +use super::characteristics::*; use super::curve::*; use super::normal::*; -use super::characteristics::*; +use crate::bezier::CurveSection; use crate::geo::*; use crate::line::*; -use crate::bezier::{CurveSection}; -use smallvec::*; use itertools::*; +use smallvec::*; // This is loosely based on the algorithm described at: https://pomax.github.io/bezierinfo/#offsetting, // with numerous changes to allow for variable-width offsets and consistent behaviour (in particular, // a much more reliable method of subdividing the curve) -// +// // This algorithm works by subdividing the original curve into arches. We use the characteristics of the // curve to do this: by subdividing a curve at its inflection point, we turn it into a series of arches. -// Arches have a focal point that the normal vectors along the curve roughly converge to, so we can -// scale around this point to generate an approximate offset curve (every point of the curve will move +// Arches have a focal point that the normal vectors along the curve roughly converge to, so we can +// scale around this point to generate an approximate offset curve (every point of the curve will move // away from the focal point along its normal axis). // // As the focal point is approximate, using the start and end points to compute its location ensures that -// the offset is exact at the start and end of the curve. +// the offset is exact at the start and end of the curve. // // Edge cases: curves with inflection points at the start or end, arches where the normal vectors at the // start and end are in parallel. // -// Not all arches have normal vectors that converge (close to) a focal point. We can spot these quickly +// Not all arches have normal vectors that converge (close to) a focal point. We can spot these quickly // because the focal point of any two points is in general not equidistant from those two points: this // also results in uneven scaling of the start and end points. // @@ -41,13 +41,27 @@ use itertools::*; /// errors, especially if the initial and final offsets are very different from one another. /// pub fn offset_scaling(curve: &Curve, initial_offset: f64, final_offset: f64) -> Vec -where Curve: BezierCurveFactory+NormalCurve, - Curve::Point: Normalize+Coordinate2D { +where + Curve: BezierCurveFactory + NormalCurve, + Curve::Point: Normalize + Coordinate2D, +{ // Split at the location of any features the curve might have - let sections: SmallVec<[_; 4]> = match features_for_curve(curve, 0.01) { - CurveFeatures::DoubleInflectionPoint(t1, t2) => { - let t1 = if t1 > 0.9999 { 1.0 } else if t1 < 0.0001 { 0.0 } else { t1 }; - let t2 = if t2 > 0.9999 { 1.0 } else if t2 < 0.0001 { 0.0 } else { t2 }; + let sections: SmallVec<[_; 4]> = match features_for_curve(curve, 0.01) { + CurveFeatures::DoubleInflectionPoint(t1, t2) => { + let t1 = if t1 > 0.9999 { + 1.0 + } else if t1 < 0.0001 { + 0.0 + } else { + t1 + }; + let t2 = if t2 > 0.9999 { + 1.0 + } else if t2 < 0.0001 { + 0.0 + } else { + t2 + }; if t2 > t1 { smallvec![(0.0, t1), (t1, t2), (t2, 1.0)] @@ -57,9 +71,21 @@ where Curve: BezierCurveFactory+NormalCurve, } CurveFeatures::Loop(t1, t3) => { - let t1 = if t1 > 0.9999 { 1.0 } else if t1 < 0.0001 { 0.0 } else { t1 }; - let t3 = if t3 > 0.9999 { 1.0 } else if t3 < 0.0001 { 0.0 } else { t3 }; - let t2 = (t1+t3)/2.0; + let t1 = if t1 > 0.9999 { + 1.0 + } else if t1 < 0.0001 { + 0.0 + } else { + t1 + }; + let t3 = if t3 > 0.9999 { + 1.0 + } else if t3 < 0.0001 { + 0.0 + } else { + t3 + }; + let t2 = (t1 + t3) / 2.0; if t3 > t1 { smallvec![(0.0, t1), (t1, t2), (t2, t3), (t3, 1.0)] @@ -76,21 +102,28 @@ where Curve: BezierCurveFactory+NormalCurve, } } - _ => { smallvec![(0.0, 1.0)] } + _ => { + smallvec![(0.0, 1.0)] + } }; - let sections = sections.into_iter() + let sections = sections + .into_iter() .filter(|(t1, t2)| t1 != t2) .map(|(t1, t2)| curve.section(t1, t2)) .collect::>(); // Offset the set of curves that we retrieved - let offset_distance = final_offset-initial_offset; + let offset_distance = final_offset - initial_offset; - sections.into_iter() + sections + .into_iter() .flat_map(|section| { // Compute the offsets for this section (TODO: use the curve length, not the t values) - let (t1, t2) = section.original_curve_t_values(); - let (offset1, offset2) = (t1*offset_distance+initial_offset, t2*offset_distance+initial_offset); + let (t1, t2) = section.original_curve_t_values(); + let (offset1, offset2) = ( + t1 * offset_distance + initial_offset, + t2 * offset_distance + initial_offset, + ); subdivide_offset(§ion, offset1, offset2, 0) }) @@ -100,71 +133,77 @@ where Curve: BezierCurveFactory+NormalCurve, /// /// Attempts a simple offset of a curve, and subdivides it if the midpoint is too far away from the expected distance /// -fn subdivide_offset<'a, CurveIn, CurveOut>(curve: &CurveSection<'a, CurveIn>, initial_offset: f64, final_offset: f64, depth: usize) -> SmallVec<[CurveOut; 2]> -where CurveIn: NormalCurve+BezierCurve, - CurveOut: BezierCurveFactory, - CurveIn::Point: Coordinate2D+Normalize { +fn subdivide_offset<'a, CurveIn, CurveOut>( + curve: &CurveSection<'a, CurveIn>, + initial_offset: f64, + final_offset: f64, + depth: usize, +) -> SmallVec<[CurveOut; 2]> +where + CurveIn: NormalCurve + BezierCurve, + CurveOut: BezierCurveFactory, + CurveIn::Point: Coordinate2D + Normalize, +{ const MAX_DEPTH: usize = 5; // Fetch the original points - let start = curve.start_point(); - let end = curve.end_point(); + let start = curve.start_point(); + let end = curve.end_point(); // The normals at the start and end of the curve define the direction we should move in - let normal_start = curve.normal_at_pos(0.0); - let normal_end = curve.normal_at_pos(1.0); - let normal_start = normal_start.to_unit_vector(); - let normal_end = normal_end.to_unit_vector(); + let normal_start = curve.normal_at_pos(0.0); + let normal_end = curve.normal_at_pos(1.0); + let normal_start = normal_start.to_unit_vector(); + let normal_end = normal_end.to_unit_vector(); // If we can we want to scale the control points around the intersection of the normals - let intersect_point = ray_intersects_ray(&(start, start+normal_start), &(end, end+normal_end)); + let intersect_point = + ray_intersects_ray(&(start, start + normal_start), &(end, end + normal_end)); if intersect_point.is_none() { if characterize_curve(curve) != CurveCategory::Linear && depth < MAX_DEPTH { // Collinear normals - let divide_point = 0.5; + let divide_point = 0.5; - let mid_offset = initial_offset + (final_offset - initial_offset) * divide_point; - let left_curve = curve.subsection(0.0, divide_point); - let right_curve = curve.subsection(divide_point, 1.0); + let mid_offset = initial_offset + (final_offset - initial_offset) * divide_point; + let left_curve = curve.subsection(0.0, divide_point); + let right_curve = curve.subsection(divide_point, 1.0); - let left_offset = subdivide_offset(&left_curve, initial_offset, mid_offset, depth+1); - let right_offset = subdivide_offset(&right_curve, mid_offset, final_offset, depth+1); + let left_offset = subdivide_offset(&left_curve, initial_offset, mid_offset, depth + 1); + let right_offset = subdivide_offset(&right_curve, mid_offset, final_offset, depth + 1); - return left_offset.into_iter() - .chain(right_offset) - .collect(); + return left_offset.into_iter().chain(right_offset).collect(); } } if let Some(intersect_point) = intersect_point { // Subdivide again if the intersection point is too close to one or other of the normals - let start_distance = intersect_point.distance_to(&start); - let end_distance = intersect_point.distance_to(&end); - let distance_ratio = start_distance.min(end_distance) / start_distance.max(end_distance); + let start_distance = intersect_point.distance_to(&start); + let end_distance = intersect_point.distance_to(&end); + let distance_ratio = start_distance.min(end_distance) / start_distance.max(end_distance); // TODO: the closer to 1 this value is, the better the quality of the offset (0.99 produces good results) // but the number of subdivisions tends to be too high: we need to find either a way to generate a better offset // curve for an arch with a non-centered intersection point, or a better way to pick the subdivision point if distance_ratio < 0.995 && depth < MAX_DEPTH { // Try to subdivide at the curve's extremeties - let mut extremeties = curve.find_extremities(); + let mut extremeties = curve.find_extremities(); extremeties.retain(|item| item > &0.01 && item < &0.99); if extremeties.len() == 0 || true { // No extremeties (or they're all too close to the edges) - let divide_point = 0.5; + let divide_point = 0.5; - let mid_offset = initial_offset + (final_offset - initial_offset) * divide_point; - let left_curve = curve.subsection(0.0, divide_point); - let right_curve = curve.subsection(divide_point, 1.0); + let mid_offset = initial_offset + (final_offset - initial_offset) * divide_point; + let left_curve = curve.subsection(0.0, divide_point); + let right_curve = curve.subsection(divide_point, 1.0); - let left_offset = subdivide_offset(&left_curve, initial_offset, mid_offset, depth+1); - let right_offset = subdivide_offset(&right_curve, mid_offset, final_offset, depth+1); + let left_offset = + subdivide_offset(&left_curve, initial_offset, mid_offset, depth + 1); + let right_offset = + subdivide_offset(&right_curve, mid_offset, final_offset, depth + 1); - left_offset.into_iter() - .chain(right_offset) - .collect() + left_offset.into_iter().chain(right_offset).collect() } else { let mut extremeties = extremeties; extremeties.insert(0, 0.0); @@ -174,22 +213,34 @@ where CurveIn: NormalCurve+BezierCurve, .into_iter() .tuple_windows() .flat_map(|(t1, t2)| { - let subsection = curve.subsection(t1, t2); - let offset1 = initial_offset + (final_offset - initial_offset) * t1; - let offset2 = initial_offset + (final_offset - initial_offset) * t2; - let res = subdivide_offset(&subsection, offset1, offset2, depth+1); + let subsection = curve.subsection(t1, t2); + let offset1 = initial_offset + (final_offset - initial_offset) * t1; + let offset2 = initial_offset + (final_offset - initial_offset) * t2; + let res = subdivide_offset(&subsection, offset1, offset2, depth + 1); res }) .collect() } } else { // Event intersection point - smallvec![offset_by_scaling(curve, initial_offset, final_offset, intersect_point, normal_start, normal_end)] + smallvec![offset_by_scaling( + curve, + initial_offset, + final_offset, + intersect_point, + normal_start, + normal_end + )] } - } else { // No intersection point - smallvec![offset_by_moving(curve, initial_offset, final_offset, normal_start, normal_end)] + smallvec![offset_by_moving( + curve, + initial_offset, + final_offset, + normal_start, + normal_end + )] } } @@ -197,27 +248,37 @@ where CurveIn: NormalCurve+BezierCurve, /// Offsets a curve by scaling around a central point /// #[inline] -fn offset_by_scaling(curve: &CurveIn, initial_offset: f64, final_offset: f64, intersect_point: CurveIn::Point, unit_normal_start: CurveIn::Point, unit_normal_end: CurveIn::Point) -> CurveOut -where CurveIn: NormalCurve+BezierCurve, - CurveOut: BezierCurveFactory, - CurveIn::Point: Coordinate2D+Normalize { - let start = curve.start_point(); - let end = curve.end_point(); - let (cp1, cp2) = curve.control_points(); +fn offset_by_scaling( + curve: &CurveIn, + initial_offset: f64, + final_offset: f64, + intersect_point: CurveIn::Point, + unit_normal_start: CurveIn::Point, + unit_normal_end: CurveIn::Point, +) -> CurveOut +where + CurveIn: NormalCurve + BezierCurve, + CurveOut: BezierCurveFactory, + CurveIn::Point: Coordinate2D + Normalize, +{ + let start = curve.start_point(); + let end = curve.end_point(); + let (cp1, cp2) = curve.control_points(); // The control points point at an intersection point. We want to scale around this point so that start and end wind up at the appropriate offsets - let new_start = start + (unit_normal_start * initial_offset); - let new_end = end + (unit_normal_end * final_offset); + let new_start = start + (unit_normal_start * initial_offset); + let new_end = end + (unit_normal_end * final_offset); - let start_scale = (intersect_point.distance_to(&new_start))/(intersect_point.distance_to(&start)); - let end_scale = (intersect_point.distance_to(&new_end))/(intersect_point.distance_to(&end)); + let start_scale = + (intersect_point.distance_to(&new_start)) / (intersect_point.distance_to(&start)); + let end_scale = (intersect_point.distance_to(&new_end)) / (intersect_point.distance_to(&end)); // When the scale is changing, the control points are effectively 1/3rd and 2/3rds of the way along the curve - let cp1_scale = (end_scale - start_scale) * (1.0/3.0) + start_scale; - let cp2_scale = (end_scale - start_scale) * (2.0/3.0) + start_scale; + let cp1_scale = (end_scale - start_scale) * (1.0 / 3.0) + start_scale; + let cp2_scale = (end_scale - start_scale) * (2.0 / 3.0) + start_scale; - let new_cp1 = ((cp1-intersect_point) * cp1_scale) + intersect_point; - let new_cp2 = ((cp2-intersect_point) * cp2_scale) + intersect_point; + let new_cp1 = ((cp1 - intersect_point) * cp1_scale) + intersect_point; + let new_cp2 = ((cp2 - intersect_point) * cp2_scale) + intersect_point; CurveOut::from_points(new_start, (new_cp1, new_cp2), new_end) } @@ -226,19 +287,27 @@ where CurveIn: NormalCurve+BezierCurve, /// Given a curve where the start and end normals do not intersect at a point, calculates the offset (by moving the start and end points along the normal) /// #[inline] -fn offset_by_moving(curve: &CurveIn, initial_offset: f64, final_offset: f64, unit_normal_start: CurveIn::Point, unit_normal_end: CurveIn::Point) -> CurveOut -where CurveIn: NormalCurve+BezierCurve, - CurveOut: BezierCurveFactory, - CurveIn::Point: Coordinate2D+Normalize { - let start = curve.start_point(); - let end = curve.end_point(); - let (cp1, cp2) = curve.control_points(); +fn offset_by_moving( + curve: &CurveIn, + initial_offset: f64, + final_offset: f64, + unit_normal_start: CurveIn::Point, + unit_normal_end: CurveIn::Point, +) -> CurveOut +where + CurveIn: NormalCurve + BezierCurve, + CurveOut: BezierCurveFactory, + CurveIn::Point: Coordinate2D + Normalize, +{ + let start = curve.start_point(); + let end = curve.end_point(); + let (cp1, cp2) = curve.control_points(); // Offset start & end by the specified amounts to create the first approximation of a curve - let new_start = start + (unit_normal_start * initial_offset); - let new_cp1 = cp1 + (unit_normal_start * initial_offset); - let new_cp2 = cp2 + (unit_normal_end * final_offset); - let new_end = end + (unit_normal_end * final_offset); + let new_start = start + (unit_normal_start * initial_offset); + let new_cp1 = cp1 + (unit_normal_start * initial_offset); + let new_cp2 = cp2 + (unit_normal_end * final_offset); + let new_end = end + (unit_normal_end * final_offset); CurveOut::from_points(new_start, (new_cp1, new_cp2), new_end) } diff --git a/src/bezier/overlaps.rs b/src/bezier/overlaps.rs index 96ab6955..58b56f85 100644 --- a/src/bezier/overlaps.rs +++ b/src/bezier/overlaps.rs @@ -1,20 +1,25 @@ -use super::curve::*; +use super::super::consts::*; use super::super::geo::*; use super::super::line::*; -use super::super::consts::*; +use super::curve::*; /// /// If `curve2` overlaps `curve1`, returns two sets of `t` values (those for `curve1` and those for `curve2`) /// -pub fn overlapping_region(curve1: &C1, curve2: &C2) -> Option<((f64, f64), (f64, f64))> -where C1::Point: Coordinate+Coordinate2D, - C2: BezierCurve { +pub fn overlapping_region( + curve1: &C1, + curve2: &C2, +) -> Option<((f64, f64), (f64, f64))> +where + C1::Point: Coordinate + Coordinate2D, + C2: BezierCurve, +{ let mut c2_t1 = 0.0; let mut c2_t2 = 1.0; // The start and end points of curve1 should be on curve2 - let c2_start = curve2.start_point(); - let c2_end = curve2.end_point(); + let c2_start = curve2.start_point(); + let c2_end = curve2.end_point(); let c1_t1 = if let Some(t) = curve1.t_for_point(&c2_start) { // Start point is on the curve @@ -41,21 +46,24 @@ where C1::Point: Coordinate+Coordinate2D, }; // If we just found one point where the curve overlaps, then say that they didn't - if (c1_t1-c1_t2).abs() < SMALL_T_DISTANCE || (c2_t1-c2_t2).abs() < SMALL_T_DISTANCE { + if (c1_t1 - c1_t2).abs() < SMALL_T_DISTANCE || (c2_t1 - c2_t2).abs() < SMALL_T_DISTANCE { return None; } // If curve1 and curve2 are collinear - two overlapping lines - we've already got the results (and the control points will differ anyway) #[inline] fn is_collinear(p: &P, &(a, b, c): &(f64, f64, f64)) -> bool { - (a*p.x() + b*p.y() + c).abs() < SMALL_DISTANCE + (a * p.x() + b * p.y() + c).abs() < SMALL_DISTANCE } - let coeff = (curve1.start_point(), curve1.end_point()).coefficients(); - let (c1_cp1, c1_cp2) = curve1.control_points(); + let coeff = (curve1.start_point(), curve1.end_point()).coefficients(); + let (c1_cp1, c1_cp2) = curve1.control_points(); - if is_collinear(&c1_cp1, &coeff) && is_collinear(&c1_cp2, &coeff) - && is_collinear(&curve2.start_point(), &coeff) && is_collinear(&curve2.end_point(), &coeff) { + if is_collinear(&c1_cp1, &coeff) + && is_collinear(&c1_cp2, &coeff) + && is_collinear(&curve2.start_point(), &coeff) + && is_collinear(&curve2.end_point(), &coeff) + { let (c2_cp1, c2_cp2) = curve2.control_points(); if is_collinear(&c2_cp1, &coeff) && is_collinear(&c2_cp2, &coeff) { @@ -72,7 +80,9 @@ where C1::Point: Coordinate+Coordinate2D, // Get the control points for the two curves #[inline] fn control_points(curve: &C, t1: f64, t2: f64) -> (C::Point, C::Point) - where C::Point: Coordinate+Coordinate2D, { + where + C::Point: Coordinate + Coordinate2D, + { if t2 < t1 { let (cp2, cp1) = curve.section(t2, t1).control_points(); (cp1, cp2) diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index f4b8e28a..ec25cd04 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -1,10 +1,10 @@ use super::fill_convex::*; use super::fill_settings::*; +use crate::bezier::path::*; +use crate::bezier::*; use crate::geo::*; use crate::line::*; -use crate::bezier::*; -use crate::bezier::path::*; use std::f64; @@ -17,14 +17,14 @@ enum ConcaveItem { Edge(Item), /// Intersection with an edge detected in an earlier raycasting operation - SelfIntersection(usize) + SelfIntersection(usize), } impl Into> for ConcaveItem { fn into(self) -> Option { match self { - ConcaveItem::Edge(item) => Some(item), - ConcaveItem::SelfIntersection(_) => None + ConcaveItem::Edge(item) => Some(item), + ConcaveItem::SelfIntersection(_) => None, } } } @@ -33,37 +33,42 @@ impl Into> for ConcaveItem { /// Represents a long edge that we want to raycast from /// struct LongEdge { - start: Coord, - end: Coord, - edge_index: (usize, usize), - ray_collided: bool + start: Coord, + end: Coord, + edge_index: (usize, usize), + ray_collided: bool, } /// /// Retrieves the 'long' edges from a set of edges returned by a raycast tracing operation /// -fn find_long_edges(edges: &[RayCollision], edge_min_len_squared: f64) -> Vec> -where Coord: Coordinate+Coordinate2D { +fn find_long_edges( + edges: &[RayCollision], + edge_min_len_squared: f64, +) -> Vec> +where + Coord: Coordinate + Coordinate2D, +{ // Find the edges where we need to cast extra rays - let mut long_edges = vec![]; + let mut long_edges = vec![]; for edge_num in 0..edges.len() { // Get the length of this edge - let last_edge = if edge_num == 0 { + let last_edge = if edge_num == 0 { edges.len() - 1 } else { - edge_num-1 + edge_num - 1 }; - let edge_offset = edges[last_edge].position - edges[edge_num].position; - let edge_distance_squared = edge_offset.dot(&edge_offset); + let edge_offset = edges[last_edge].position - edges[edge_num].position; + let edge_distance_squared = edge_offset.dot(&edge_offset); // Add to the list of long edges if it's long enough to need further ray-casting if edge_distance_squared >= edge_min_len_squared { - long_edges.push(LongEdge { - start: edges[last_edge].position.clone(), - end: edges[edge_num].position.clone(), - edge_index: (last_edge, edge_num), - ray_collided: false + long_edges.push(LongEdge { + start: edges[last_edge].position.clone(), + end: edges[edge_num].position.clone(), + edge_index: (last_edge, edge_num), + ray_collided: false, }) } } @@ -75,7 +80,9 @@ where Coord: Coordinate+Coordinate2D { /// Determines if the 'to' position is further away from the 'center' position than the 'from' position /// fn ray_is_moving_outwards(center: &Coord, from: &Coord, to: &Coord) -> bool -where Coord: Coordinate+Coordinate2D { +where + Coord: Coordinate + Coordinate2D, +{ // Determine where the 'to' point is along this ray let ray = (center.clone(), from.clone()); let pos = ray.pos_for_point(to); @@ -92,10 +99,16 @@ where Coord: Coordinate+Coordinate2D { /// If they're closer than the minimum size, we can remove the edge by moving all the points that were found on the other /// side into a line. /// -fn remove_small_gaps(center: &Coord, edges: &mut Vec>, long_edges: &mut Vec>, min_gap_size: f64) -where Coord: Coordinate+Coordinate2D { +fn remove_small_gaps( + center: &Coord, + edges: &mut Vec>, + long_edges: &mut Vec>, + min_gap_size: f64, +) where + Coord: Coordinate + Coordinate2D, +{ // To avoid calculating a lot of square roots, square the min gap size - let min_gap_sq = min_gap_size * min_gap_size; + let min_gap_sq = min_gap_size * min_gap_size; // List of long edges to remove after we've edited the points let mut long_edges_to_remove = vec![]; @@ -103,24 +116,30 @@ where Coord: Coordinate+Coordinate2D { // Inspect the 'long edges' as pairs (they need to be in order for this to work) for edge1_idx in 0..long_edges.len() { // Going to measure the distance between this edge and the following one - let edge2_idx = if edge1_idx < long_edges.len()-1 { edge1_idx + 1 } else { 0 }; - let edge1 = &long_edges[edge1_idx]; - let edge2 = &long_edges[edge2_idx]; + let edge2_idx = if edge1_idx < long_edges.len() - 1 { + edge1_idx + 1 + } else { + 0 + }; + let edge1 = &long_edges[edge1_idx]; + let edge2 = &long_edges[edge2_idx]; // Edge1 must be moving out from the center - if ray_is_moving_outwards(center, &edge1.start, &edge1.end) && !ray_is_moving_outwards(center, &edge2.start, &edge2.end) { + if ray_is_moving_outwards(center, &edge1.start, &edge1.end) + && !ray_is_moving_outwards(center, &edge2.start, &edge2.end) + { // Work out the gap between the start and the end of this gap - let start_pos = &edge1.start; - let end_pos = &edge2.end; - let offset = *end_pos - *start_pos; + let start_pos = &edge1.start; + let end_pos = &edge2.end; + let offset = *end_pos - *start_pos; let distance_sq = offset.dot(&offset); // If it's less than the min gap size, add it to the list of edges to remove if distance_sq <= min_gap_sq { // Move all the points between the two 'long' edges onto a line between the start and end point // Alternatively: could remove the points here to produce a smoother shape later on - let gap_line = (edge1.start.clone(), edge2.end.clone()); - let mut edge_num = edge1.edge_index.1; + let gap_line = (edge1.start.clone(), edge2.end.clone()); + let mut edge_num = edge1.edge_index.1; loop { // Stop once we reach the end of the final edge @@ -129,13 +148,16 @@ where Coord: Coordinate+Coordinate2D { } // Map this edge to the gap line - let edge = &mut edges[edge_num]; - let edge_ray = (center.clone(), edge.position.clone()); - edge.position = line_intersects_ray(&edge_ray, &gap_line).unwrap_or_else(|| edge.position.clone()); + let edge = &mut edges[edge_num]; + let edge_ray = (center.clone(), edge.position.clone()); + edge.position = line_intersects_ray(&edge_ray, &gap_line) + .unwrap_or_else(|| edge.position.clone()); // Move to the next edge edge_num += 1; - if edge_num >= edges.len() { edge_num = 0; } + if edge_num >= edges.len() { + edge_num = 0; + } } // Remove these edges from consideration for future raycast operations @@ -168,29 +190,35 @@ where Coord: Coordinate+Coordinate2D { /// Collisions generated internally will have 'None' set for the `what` field of the ray collision (this is why the field is made /// optional by this call) /// -pub fn trace_outline_concave(center: Coord, options: &FillSettings, cast_ray: RayFn) -> Vec>> -where Coord: Coordinate+Coordinate2D, - RayList: IntoIterator>, - RayFn: Fn(Coord, Coord) -> RayList { +pub fn trace_outline_concave( + center: Coord, + options: &FillSettings, + cast_ray: RayFn, +) -> Vec>> +where + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, + RayFn: Fn(Coord, Coord) -> RayList, +{ // Modify the raycasting function to return concave items (so we can distinguish between edges we introduced and ones matched by the original raycasting algorithm) // TODO: this just ensures we return optional items - let cast_ray = &cast_ray; - let cast_ray = &|from, to| { - cast_ray(from, to).into_iter().map(|collision| { - RayCollision { - position: collision.position, - what: ConcaveItem::Edge(collision.what) - } - }) + let cast_ray = &cast_ray; + let cast_ray = &|from, to| { + cast_ray(from, to) + .into_iter() + .map(|collision| RayCollision { + position: collision.position, + what: ConcaveItem::Edge(collision.what), + }) }; // The edge min length is the length of edge we need to see before we'll 'look around' a corner - let edge_min_len = options.step * 4.0; - let edge_min_len_squared = edge_min_len * edge_min_len; + let edge_min_len = options.step * 4.0; + let edge_min_len_squared = edge_min_len * edge_min_len; // Distance to move past a self-intersection (so we fully close the path). This can be reasonably large (as we'll use the // edge from the ray casting function if it's nearer) - let self_intersection_distance = options.step; + let self_intersection_distance = options.step; // Perform the initial convex ray-casting let mut edges = trace_outline_convex(center, options, cast_ray); @@ -201,7 +229,7 @@ where Coord: Coordinate+Coordinate2D, } // Find the edges where we need to cast extra rays - let mut long_edges = find_long_edges(&edges, edge_min_len_squared); + let mut long_edges = find_long_edges(&edges, edge_min_len_squared); // Remove any gaps that are too small for the rays to escape through if let Some(min_gap) = options.min_gap { @@ -218,36 +246,39 @@ where Coord: Coordinate+Coordinate2D, if !next_edge.ray_collided { // Pick the center point let center_point = (next_edge.start + next_edge.end) * 0.5; - let offset = next_edge.start - next_edge.end; + let offset = next_edge.start - next_edge.end; // Find the angle of the next edge - let line_angle = offset.x().atan2(offset.y()); + let line_angle = offset.x().atan2(offset.y()); // Generate a version of the raycasting function that inspects the existing list of long edges - let cast_ray_to_edges = |from: Coord, to: Coord| { + let cast_ray_to_edges = |from: Coord, to: Coord| { // Generate the edge collisions from the main raycasting function - let edge_collisions = cast_ray(from.clone(), to.clone()); - let ray_line = (from.clone(), to.clone()); + let edge_collisions = cast_ray(from.clone(), to.clone()); + let ray_line = (from.clone(), to.clone()); // Generate the collisions with the 'long edges' where we'll be considering casting more rays later on - let extra_collisions = long_edges.iter() + let extra_collisions = long_edges + .iter() .enumerate() .filter(|(edge_index, _edge)| *edge_index != long_edge_index) .filter_map(move |(edge_index, edge)| { // Create lines from the ray and the lines - let edge_line = (edge.start.clone(), edge.end.clone()); + let edge_line = (edge.start.clone(), edge.end.clone()); // Detect where they intersect - if let Some(intersection_point) = line_intersects_ray(&edge_line, &ray_line) { + if let Some(intersection_point) = line_intersects_ray(&edge_line, &ray_line) + { // Move the intersection point slightly inside the shape along the direction of the ray (so we can add the final result up properly) - let length = to.distance_to(&from); - let direction = (to-from) * (4.0/length); - let intersection_point = intersection_point + (direction * self_intersection_distance); + let length = to.distance_to(&from); + let direction = (to - from) * (4.0 / length); + let intersection_point = + intersection_point + (direction * self_intersection_distance); // Generate a colision at this point Some(RayCollision { - position: intersection_point, - what: ConcaveItem::SelfIntersection(edge_index) + position: intersection_point, + what: ConcaveItem::SelfIntersection(edge_index), }) } else { None @@ -259,7 +290,12 @@ where Coord: Coordinate+Coordinate2D, }; // Perform raycasting over a 180 degree angle to get the next set of edges - let mut new_edges = trace_outline_convex_partial(center_point, options, line_angle..(line_angle+f64::consts::PI), cast_ray_to_edges); + let mut new_edges = trace_outline_convex_partial( + center_point, + options, + line_angle..(line_angle + f64::consts::PI), + cast_ray_to_edges, + ); if new_edges.len() > 2 { // We ignore the first point as it will be the point along the existing edge (ie, will be the start point we already know) @@ -274,7 +310,8 @@ where Coord: Coordinate+Coordinate2D, } // Find new long edges in the new edges - let mut new_long_edges = find_long_edges(&new_edges[0..(new_edges.len())], edge_min_len_squared); + let mut new_long_edges = + find_long_edges(&new_edges[0..(new_edges.len())], edge_min_len_squared); // Don't count the edge ending at point 0 (that's the edge we just came from) new_long_edges.retain(|edge| edge.edge_index.1 != 0); @@ -284,11 +321,13 @@ where Coord: Coordinate+Coordinate2D, remove_small_gaps(¢er, &mut new_edges, &mut new_long_edges, min_gap); } - // Insert the new edges into the existing edge list (except the first which will be a duplicate) - let edge_index = next_edge_index; - let num_new_edges = new_edges.len()-1; - edges.splice(edge_index..edge_index, new_edges.into_iter().take(num_new_edges)); + let edge_index = next_edge_index; + let num_new_edges = new_edges.len() - 1; + edges.splice( + edge_index..edge_index, + new_edges.into_iter().take(num_new_edges), + ); // Update the remaining long edge indexes for update_idx in long_edge_index..long_edges.len() { @@ -307,7 +346,7 @@ where Coord: Coordinate+Coordinate2D, edge.edge_index.1 += edge_index; } - long_edges.splice((long_edge_index+1)..(long_edge_index+1), new_long_edges); + long_edges.splice((long_edge_index + 1)..(long_edge_index + 1), new_long_edges); } } @@ -316,48 +355,64 @@ where Coord: Coordinate+Coordinate2D, } // The edges we retrieved are the result - edges.into_iter() - .map(|collision| RayCollision { - position: collision.position, - what: collision.what.into() + edges + .into_iter() + .map(|collision| RayCollision { + position: collision.position, + what: collision.what.into(), }) .collect() } /// /// Creates a Bezier path by flood-filling a convex area whose bounds can be determined by ray-casting. -/// +/// /// This won't fill areas that cannot be directly reached by a straight line from the center point. If the /// area is not entirely closed (from the point of view of the ray-casting function), then a line will be /// generated between the gaps. /// -pub fn flood_fill_concave(center: Coord, options: &FillSettings, cast_ray: RayFn) -> Option> -where Path: BezierPathFactory, - Coord: Coordinate+Coordinate2D, - RayList: IntoIterator>, - RayFn: Fn(Coord, Coord) -> RayList { +pub fn flood_fill_concave( + center: Coord, + options: &FillSettings, + cast_ray: RayFn, +) -> Option> +where + Path: BezierPathFactory, + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, + RayFn: Fn(Coord, Coord) -> RayList, +{ // Trace where the ray casting algorithm indicates collisions with the specified center let collisions = trace_outline_concave(center, options, cast_ray); // Build a path using the LMS algorithm - let curves = fit_curve::>(&collisions.iter().map(|collision| collision.position.clone()).collect::>(), options.fit_error); + let curves = fit_curve::>( + &collisions + .iter() + .map(|collision| collision.position.clone()) + .collect::>(), + options.fit_error, + ); if let Some(curves) = curves { if curves.len() > 0 { // Convert the curves into a path - let initial_point = curves[0].start_point(); - let overlapped_path = Path::from_points(initial_point, curves.into_iter().map(|curve| { - let (cp1, cp2) = curve.control_points(); - let end_point = curve.end_point(); - (cp1, cp2, end_point) - })); - + let initial_point = curves[0].start_point(); + let overlapped_path = Path::from_points( + initial_point, + curves.into_iter().map(|curve| { + let (cp1, cp2) = curve.control_points(); + let end_point = curve.end_point(); + (cp1, cp2, end_point) + }), + ); + // Remove any interior points that the path might have (this happens when the fill path overlaps itself) Some(path_remove_interior_points(&vec![overlapped_path], 0.01)) } else { // No curves in the path None - } + } } else { // Failed to fit a curve to these points None diff --git a/src/bezier/path/algorithms/fill_convex.rs b/src/bezier/path/algorithms/fill_convex.rs index e6151c1f..f223090c 100644 --- a/src/bezier/path/algorithms/fill_convex.rs +++ b/src/bezier/path/algorithms/fill_convex.rs @@ -1,26 +1,30 @@ -use super::fill_settings::*; -use super::super::*; -use super::super::super::*; use super::super::super::super::geo::*; +use super::super::super::*; +use super::super::*; +use super::fill_settings::*; use std::f64; -use std::ops::{Range}; +use std::ops::Range; /// /// Represents a collision between a ray and an object /// #[derive(Clone)] pub struct RayCollision -where Coord: Coordinate+Coordinate2D { +where + Coord: Coordinate + Coordinate2D, +{ /// Where this collision occurred pub position: Coord, /// The object that this ray colided with - pub what: Item + pub what: Item, } impl RayCollision -where Coord: Coordinate+Coordinate2D { +where + Coord: Coordinate + Coordinate2D, +{ /// /// Creates a new collision at a specific point /// @@ -30,44 +34,58 @@ where Coord: Coordinate+Coordinate2D { } /// -/// Given a ray-casting function, traces the outline of a shape containing the specified center point -/// -/// `center` is a point known to be contained in the shape (it's the origin of the region to be filled) -/// +/// Given a ray-casting function, traces the outline of a shape containing the specified center point +/// +/// `center` is a point known to be contained in the shape (it's the origin of the region to be filled) +/// /// The ray-casting function has the type `Fn(Coord, Coord) -> RayList`, where the two coordinates /// that are passed in represents the direction of the ray. It should return at least one intersection /// along this ray. If there is an intersection, the returned list should always include the closest /// intersection in the direction of the ray defined by the two coordinates. /// -pub fn trace_outline_convex(center: Coord, options: &FillSettings, cast_ray: RayFn) -> Vec> -where Coord: Coordinate+Coordinate2D, - RayList: IntoIterator>, - RayFn: Fn(Coord, Coord) -> RayList { - trace_outline_convex_partial(center, options, (0.0)..(2.0*f64::consts::PI), cast_ray) +pub fn trace_outline_convex( + center: Coord, + options: &FillSettings, + cast_ray: RayFn, +) -> Vec> +where + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, + RayFn: Fn(Coord, Coord) -> RayList, +{ + trace_outline_convex_partial(center, options, (0.0)..(2.0 * f64::consts::PI), cast_ray) } /// /// Finds the nearest collision and the square of its distance from the center from the results of a ray-casting operation /// -fn find_nearest_collision(candidates: RayList, center: Coord, ray_vector: Coord) -> Option<(RayCollision, f64)> -where Coord: Coordinate+Coordinate2D, - RayList: IntoIterator> { +fn find_nearest_collision( + candidates: RayList, + center: Coord, + ray_vector: Coord, +) -> Option<(RayCollision, f64)> +where + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, +{ // Pick the first positive collision in the direction of the ray - let mut nearest_collision = None; - let mut nearest_distance_squared = f64::MAX; + let mut nearest_collision = None; + let mut nearest_distance_squared = f64::MAX; for ray_collision in candidates { - let collision_vector = ray_collision.position - center; + let collision_vector = ray_collision.position - center; // Ignore collisions in the opposite direction of our ray - let direction = collision_vector.dot(&ray_vector); - if direction < 0.0 { continue; } + let direction = collision_vector.dot(&ray_vector); + if direction < 0.0 { + continue; + } // If this collision is closer to the center than before, then it becomes the nearest collision - let distance = collision_vector.dot(&collision_vector); + let distance = collision_vector.dot(&collision_vector); if distance < nearest_distance_squared { - nearest_collision = Some(ray_collision); - nearest_distance_squared = distance; + nearest_collision = Some(ray_collision); + nearest_distance_squared = distance; } } @@ -77,17 +95,23 @@ where Coord: Coordinate+Coordinate2D, /// /// Performs a raycast from a center point at a particular angle /// -fn perform_ray_cast(center: Coord, theta: f64, cast_ray: RayFn) -> Option<(RayCollision, f64)> -where Coord: Coordinate+Coordinate2D, - RayList: IntoIterator>, - RayFn: Fn(Coord, Coord) -> RayList { +fn perform_ray_cast( + center: Coord, + theta: f64, + cast_ray: RayFn, +) -> Option<(RayCollision, f64)> +where + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, + RayFn: Fn(Coord, Coord) -> RayList, +{ // Work out the direction of the ray - let ray_vector = [1.0 * theta.sin(), 1.0 * theta.cos()]; - let ray_vector = Coord::from_components(&ray_vector); - let ray_target = center + ray_vector; + let ray_vector = [1.0 * theta.sin(), 1.0 * theta.cos()]; + let ray_vector = Coord::from_components(&ray_vector); + let ray_target = center + ray_vector; // Cast this ray and get the list of collisions - let ray_collisions = cast_ray(center, ray_target); + let ray_collisions = cast_ray(center, ray_target); // Pick the first positive collision in the direction of the ray find_nearest_collision(ray_collisions, center, ray_vector) @@ -96,73 +120,84 @@ where Coord: Coordinate+Coordinate2D, /// /// Ray traces around a specified range of angles to find the shape of the outline. Angles are in radians /// -pub (super) fn trace_outline_convex_partial(center: Coord, options: &FillSettings, angles: Range, cast_ray: RayFn) -> Vec> -where Coord: Coordinate+Coordinate2D, - RayList: IntoIterator>, - RayFn: Fn(Coord, Coord) -> RayList { +pub(super) fn trace_outline_convex_partial( + center: Coord, + options: &FillSettings, + angles: Range, + cast_ray: RayFn, +) -> Vec> +where + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, + RayFn: Fn(Coord, Coord) -> RayList, +{ // The minimum number of radians to move forward when a ray does not find a collision - let min_step = 0.02; + let min_step = 0.02; // The number of pixels to put between points when tracing the outline - let step_size = options.step; - let max_step = step_size * 2.0; - let max_step_squared = max_step * max_step; + let step_size = options.step; + let max_step = step_size * 2.0; + let max_step_squared = max_step * max_step; // Collisions we're including in the result - let mut collisions = vec![]; + let mut collisions = vec![]; // Create a stack to track the state - let mut stack = vec![]; - struct StackEntry { - angle: Range, - start_pos: Option<(RayCollision, f64)>, - end_pos: Option + let mut stack = vec![]; + struct StackEntry { + angle: Range, + start_pos: Option<(RayCollision, f64)>, + end_pos: Option, } // Ray cast a few points to get the initial stack of points to check for check_point in 0..4 { - let check_point = (3-check_point) as f64; - let theta = angles.start + (angles.end-angles.start)/4.0 * check_point; - let end_theta = theta + (angles.end-angles.start)/4.0; + let check_point = (3 - check_point) as f64; + let theta = angles.start + (angles.end - angles.start) / 4.0 * check_point; + let end_theta = theta + (angles.end - angles.start) / 4.0; - let start_pos = perform_ray_cast(center, theta, &cast_ray); - let end_pos = perform_ray_cast(center, end_theta, &cast_ray); + let start_pos = perform_ray_cast(center, theta, &cast_ray); + let end_pos = perform_ray_cast(center, end_theta, &cast_ray); stack.push(StackEntry { - angle: theta..end_theta, - start_pos: start_pos, - end_pos: end_pos.map(|(end_pos, _distance)| end_pos.position) + angle: theta..end_theta, + start_pos: start_pos, + end_pos: end_pos.map(|(end_pos, _distance)| end_pos.position), }); } // Divide up the check points until the gap between them becomes small enough that it's less than the maximum gap size while let Some(entry) = stack.pop() { - if let (Some((start_pos, _start_distance_squared)), Some(end_pos)) = (entry.start_pos.as_ref(), entry.end_pos) { + if let (Some((start_pos, _start_distance_squared)), Some(end_pos)) = + (entry.start_pos.as_ref(), entry.end_pos) + { // Check the distance between the start and the end - let offset = end_pos - start_pos.position; - let distance_squared = offset.dot(&offset); + let offset = end_pos - start_pos.position; + let distance_squared = offset.dot(&offset); if distance_squared < max_step_squared { // This point is close enough to its following point to be added to the result collisions.push(entry.start_pos.unwrap().0); } else { // Divide the entry into two by casting a ray between the two points - let mid_point = (entry.angle.start + entry.angle.end) / 2.0; - let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); - let mid_ray_pos = mid_ray.as_ref().map(|(collision, _)| collision.position.clone()); + let mid_point = (entry.angle.start + entry.angle.end) / 2.0; + let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); + let mid_ray_pos = mid_ray + .as_ref() + .map(|(collision, _)| collision.position.clone()); // If there's a discontinuity (eg, a corner we can't see around), we'll see that the mid point is very close to the end point and far from the start point if let Some(mid_ray_pos) = mid_ray_pos { // Compute the distance from the start to the mid-point and the mid-point to the end - let mid_to_end = end_pos - mid_ray_pos; - let mid_to_end_sq = mid_to_end.dot(&mid_to_end); + let mid_to_end = end_pos - mid_ray_pos; + let mid_to_end_sq = mid_to_end.dot(&mid_to_end); // If the end is very close to the mid-point ... if mid_to_end_sq < (step_size * step_size) { // ... and is over 3/4 from the start point ... - let three_quarters_sq = (9.0*distance_squared)/16.0; - let start_to_mid = mid_ray_pos - start_pos.position; - let start_to_mid_sq = start_to_mid.dot(&start_to_mid); + let three_quarters_sq = (9.0 * distance_squared) / 16.0; + let start_to_mid = mid_ray_pos - start_pos.position; + let start_to_mid_sq = start_to_mid.dot(&start_to_mid); if start_to_mid_sq >= three_quarters_sq { // ... we've hit an edge and won't be able to find a point closer to the start position @@ -174,36 +209,36 @@ where Coord: Coordinate+Coordinate2D, // Divide into two pairs of ranges (process the earlier one first) stack.push(StackEntry { - angle: mid_point..entry.angle.end, - start_pos: mid_ray, - end_pos: entry.end_pos + angle: mid_point..entry.angle.end, + start_pos: mid_ray, + end_pos: entry.end_pos, }); stack.push(StackEntry { - angle: entry.angle.start..mid_point, - start_pos: entry.start_pos, - end_pos: mid_ray_pos + angle: entry.angle.start..mid_point, + start_pos: entry.start_pos, + end_pos: mid_ray_pos, }) } - } else { - // One or both of the rays did not find a collision if entry.angle.end - entry.angle.start > min_step { // Cast a ray between the two points - let mid_point = (entry.angle.start + entry.angle.end) / 2.0; - let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); - let mid_ray_pos = mid_ray.as_ref().map(|(collision, _)| collision.position.clone()); + let mid_point = (entry.angle.start + entry.angle.end) / 2.0; + let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); + let mid_ray_pos = mid_ray + .as_ref() + .map(|(collision, _)| collision.position.clone()); // Divide into two pairs of ranges (process the earlier one first) stack.push(StackEntry { - angle: mid_point..entry.angle.end, - start_pos: mid_ray, - end_pos: entry.end_pos + angle: mid_point..entry.angle.end, + start_pos: mid_ray, + end_pos: entry.end_pos, }); stack.push(StackEntry { - angle: entry.angle.start..mid_point, - start_pos: entry.start_pos, - end_pos: mid_ray_pos + angle: entry.angle.start..mid_point, + start_pos: entry.start_pos, + end_pos: mid_ray_pos, }) } } @@ -212,38 +247,52 @@ where Coord: Coordinate+Coordinate2D, collisions } - /// /// Creates a Bezier path by flood-filling a convex area whose bounds can be determined by ray-casting. -/// +/// /// This won't fill areas that cannot be directly reached by a straight line from the center point. If the /// area is not entirely closed (from the point of view of the ray-casting function), then a line will be /// generated between the gaps. /// -pub fn flood_fill_convex(center: Coord, options: &FillSettings, cast_ray: RayFn) -> Option -where Path: BezierPathFactory, - Coord: Coordinate+Coordinate2D, - RayList: IntoIterator>, - RayFn: Fn(Coord, Coord) -> RayList { +pub fn flood_fill_convex( + center: Coord, + options: &FillSettings, + cast_ray: RayFn, +) -> Option +where + Path: BezierPathFactory, + Coord: Coordinate + Coordinate2D, + RayList: IntoIterator>, + RayFn: Fn(Coord, Coord) -> RayList, +{ // Trace where the ray casting algorithm indicates collisions with the specified center let collisions = trace_outline_convex(center, options, cast_ray); // Build a path using the LMS algorithm - let curves = fit_curve::>(&collisions.iter().map(|collision| collision.position.clone()).collect::>(), options.fit_error); + let curves = fit_curve::>( + &collisions + .iter() + .map(|collision| collision.position.clone()) + .collect::>(), + options.fit_error, + ); if let Some(curves) = curves { if curves.len() > 0 { // Convert the curves into a path let initial_point = curves[0].start_point(); - Some(Path::from_points(initial_point, curves.into_iter().map(|curve| { - let (cp1, cp2) = curve.control_points(); - let end_point = curve.end_point(); - (cp1, cp2, end_point) - }))) + Some(Path::from_points( + initial_point, + curves.into_iter().map(|curve| { + let (cp1, cp2) = curve.control_points(); + let end_point = curve.end_point(); + (cp1, cp2, end_point) + }), + )) } else { // No curves in the path None - } + } } else { // Failed to fit a curve to these points None diff --git a/src/bezier/path/algorithms/fill_settings.rs b/src/bezier/path/algorithms/fill_settings.rs index 01f98ebe..563a895e 100644 --- a/src/bezier/path/algorithms/fill_settings.rs +++ b/src/bezier/path/algorithms/fill_settings.rs @@ -1,25 +1,25 @@ /// /// Options that affect the fill algorithm -/// +/// /// The default options are created using `FillOptions::default()`. These can be used to tweak /// settings like this step size. /// #[derive(Clone, Copy, PartialEq, Debug)] pub struct FillSettings { /// The distance between one ray and the next - pub (crate) step: f64, + pub(crate) step: f64, /// The maximum error to allow when performing curve fitting - pub (crate) fit_error: f64, + pub(crate) fit_error: f64, /// For concave fills, the minimum gap size that a fill can escape through - pub (crate) min_gap: Option + pub(crate) min_gap: Option, } impl FillSettings { /// /// Creates a new fill options from this one by setting the step - /// + /// /// The step size defines how accurately the flood-filled region reflects the area defined by the /// ray-casting function. Higher steps will result in a faster but less accurate result. /// @@ -31,7 +31,7 @@ impl FillSettings { /// /// Creates a new fill options from this one by setting the curve fitting error - /// + /// /// The curve fitting error indicates how precisely the generated curve fits against the points /// returned by the ray casting algorithm. Increasing this value reduces the precision of the /// fit, which may produce a simpler (and smoother) resulting path but which will not necessarily @@ -61,9 +61,9 @@ impl Default for FillSettings { /// fn default() -> FillSettings { FillSettings { - step: 2.0, - fit_error: 0.5, - min_gap: Some(5.0) + step: 2.0, + fit_error: 0.5, + min_gap: Some(5.0), } - } + } } diff --git a/src/bezier/path/algorithms/mod.rs b/src/bezier/path/algorithms/mod.rs index 5d530999..df520362 100644 --- a/src/bezier/path/algorithms/mod.rs +++ b/src/bezier/path/algorithms/mod.rs @@ -1,7 +1,7 @@ -mod fill_convex; mod fill_concave; +mod fill_convex; mod fill_settings; -pub use self::fill_convex::*; pub use self::fill_concave::*; +pub use self::fill_convex::*; pub use self::fill_settings::*; diff --git a/src/bezier/path/arithmetic/add.rs b/src/bezier/path/arithmetic/add.rs index 70b21996..eba3f4a2 100644 --- a/src/bezier/path/arithmetic/add.rs +++ b/src/bezier/path/arithmetic/add.rs @@ -1,22 +1,24 @@ -use super::ray_cast::*; -use super::super::path::*; -use super::super::graph_path::*; use super::super::super::super::geo::*; +use super::super::graph_path::*; +use super::super::path::*; +use super::ray_cast::*; // // This uses a simple ray casting algorithm to perform the addition -// +// // Basic idea is to cast a ray at an edge which is currently uncategorised, and mark the edges it crosses as interior or // exterior depending on whether or not we consider it as crossing into or out of the final shape. // -impl GraphPath { +impl GraphPath { /// /// Given a labelled graph path, marks exterior edges by adding `PathSource::Path1` and `PathSource::Path2` /// pub fn set_exterior_by_adding(&mut self) { // Use an even-odd winding rule (all edges are considered 'external') - self.set_edge_kinds_by_ray_casting(|path_crossings| (path_crossings[0]&1) != 0 || (path_crossings[1]&1) != 0); + self.set_edge_kinds_by_ray_casting(|path_crossings| { + (path_crossings[0] & 1) != 0 || (path_crossings[1] & 1) != 0 + }); } /// @@ -24,37 +26,52 @@ impl GraphPath { /// pub fn set_exterior_by_removing_interior_points(&mut self) { // All points inside the path are considered 'interior' (non-zero winding rule) - self.set_edge_kinds_by_ray_casting(|path_crossings| path_crossings[0] != 0 || path_crossings[1] != 0); + self.set_edge_kinds_by_ray_casting(|path_crossings| { + path_crossings[0] != 0 || path_crossings[1] != 0 + }); } } /// /// Generates the path formed by adding two sets of paths -/// +/// /// The input vectors represent the external edges of the path to add (a single BezierPath cannot have any holes in it, so a set of them /// effectively represents a path intended to be rendered with an even-odd winding rule) /// -pub fn path_add(path1: &Vec, path2: &Vec, accuracy: f64) -> Vec -where P1::Point: Coordinate+Coordinate2D, - P2: BezierPath, - POut: BezierPathFactory { +pub fn path_add( + path1: &Vec, + path2: &Vec, + accuracy: f64, +) -> Vec +where + P1::Point: Coordinate + Coordinate2D, + P2: BezierPath, + POut: BezierPathFactory, +{ // If either path is empty, short-circuit by returning the other if path1.len() == 0 { - return path2.iter() - .map(|path| POut::from_path(path)) - .collect(); + return path2.iter().map(|path| POut::from_path(path)).collect(); } else if path2.len() == 0 { - return path1.iter() - .map(|path| POut::from_path(path)) - .collect(); + return path1.iter().map(|path| POut::from_path(path)).collect(); } // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path1.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path1 + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide with the target side to generate a full path - merged_path = merged_path.collide(GraphPath::from_merged_paths(path2.into_iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), accuracy); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path2 + .into_iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + accuracy, + ); merged_path.round(accuracy); // Set the exterior edges using the 'add' algorithm @@ -66,21 +83,29 @@ where P1::Point: Coordinate+Coordinate2D, } /// -/// Generates the path formed by removing any interior points from an existing path. This considers only the outermost edges of the +/// Generates the path formed by removing any interior points from an existing path. This considers only the outermost edges of the /// path to be the true edges, so if there are sub-paths inside an outer path, they will be removed. /// /// This is a strict version of the 'non-zero' winding rule. It's useful for things like a path that was generated from a brush stroke -/// and might self-overlap: this can be passed a drawing of a loop made by overlapping the ends and it will output two non-overlapping +/// and might self-overlap: this can be passed a drawing of a loop made by overlapping the ends and it will output two non-overlapping /// subpaths. /// /// See `path_remove_overlapped_points()` for a version that considers all edges within the path to be exterior edges. /// -pub fn path_remove_interior_points(path: &Vec, accuracy: f64) -> Vec -where P1::Point: Coordinate+Coordinate2D, - POut: BezierPathFactory { +pub fn path_remove_interior_points( + path: &Vec, + accuracy: f64, +) -> Vec +where + P1::Point: Coordinate + Coordinate2D, + POut: BezierPathFactory, +{ // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path.into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide the path with itself to find the intersections merged_path.self_collide(accuracy); @@ -110,12 +135,20 @@ where P1::Point: Coordinate+Coordinate2D, /// Note that calling 'subtract' is a more reliable way to cut a hole in an existing path than relying on a winding rule, as /// winding rules presuppose you can tell if a subpath is inside or outside of an existing path. /// -pub fn path_remove_overlapped_points(path: &Vec, accuracy: f64) -> Vec -where P1::Point: Coordinate+Coordinate2D, - POut: BezierPathFactory { +pub fn path_remove_overlapped_points( + path: &Vec, + accuracy: f64, +) -> Vec +where + P1::Point: Coordinate + Coordinate2D, + POut: BezierPathFactory, +{ // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path.into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide the path with itself to find the intersections merged_path.self_collide(accuracy); diff --git a/src/bezier/path/arithmetic/chain.rs b/src/bezier/path/arithmetic/chain.rs index adcb6438..934d214f 100644 --- a/src/bezier/path/arithmetic/chain.rs +++ b/src/bezier/path/arithmetic/chain.rs @@ -1,9 +1,9 @@ +use super::super::super::super::geo::*; +use super::super::path::*; use super::add::*; -use super::sub::*; use super::chain_add::*; use super::intersect::*; -use super::super::path::*; -use super::super::super::super::geo::*; +use super::sub::*; /// /// Description of an arithmetic operation to perform on a bezier path @@ -13,7 +13,9 @@ use super::super::super::super::geo::*; /// #[derive(Clone, Debug)] pub enum PathCombine -where P::Point : Coordinate+Coordinate2D { +where + P::Point: Coordinate + Coordinate2D, +{ /// Sets the result to a particular path Path(Vec

), @@ -21,7 +23,7 @@ where P::Point : Coordinate+Coordinate2D { /// /// Everything within the outermost boundary of the path will be removed: the path will be left with no /// 'holes' in it. This is useful for cleaning up things like paths generated by brush strokes that may - /// self-overlap. See also `path_remove_overlapped_points()` for a way to clean up paths where every edge + /// self-overlap. See also `path_remove_overlapped_points()` for a way to clean up paths where every edge /// is intended to be an exterior edge. /// /// This can be considered to be a slightly stricter version of the non-zero winding rule: the intention is @@ -36,42 +38,54 @@ where P::Point : Coordinate+Coordinate2D { Subtract(Vec>), /// Intersects a series a paths (with the first path) - Intersect(Vec>) + Intersect(Vec>), } /// /// Performs a series of path combining operations to generate an output path /// pub fn path_combine(operation: PathCombine

, accuracy: f64) -> Vec

-where P::Point: Coordinate+Coordinate2D { +where + P::Point: Coordinate + Coordinate2D, +{ // TODO: it's probably possible to combine add, subtract and intersect into a single ray-casting operation using a similar technique to how path_add_chain works match operation { - PathCombine::Path(result) => result, + PathCombine::Path(result) => result, PathCombine::RemoveInteriorPoints(path) => path_remove_interior_points(&path, accuracy), - PathCombine::Add(paths) => path_add_chain(&paths.into_iter().map(|path| path_combine(path, accuracy)).collect(), accuracy), + PathCombine::Add(paths) => path_add_chain( + &paths + .into_iter() + .map(|path| path_combine(path, accuracy)) + .collect(), + accuracy, + ), - PathCombine::Subtract(paths) => { - let mut path_iter = paths.into_iter(); - let result = path_iter.next().unwrap_or_else(|| PathCombine::Path(vec![])); - let mut result = path_combine(result, accuracy); + PathCombine::Subtract(paths) => { + let mut path_iter = paths.into_iter(); + let result = path_iter + .next() + .unwrap_or_else(|| PathCombine::Path(vec![])); + let mut result = path_combine(result, accuracy); while let Some(to_subtract) = path_iter.next() { let to_subtract = path_combine(to_subtract, accuracy); - result = path_sub(&result, &to_subtract, accuracy); + result = path_sub(&result, &to_subtract, accuracy); } result } - PathCombine::Intersect(paths) => { - let mut path_iter = paths.into_iter(); - let result = path_iter.next().unwrap_or_else(|| PathCombine::Path(vec![])); - let mut result = path_combine(result, accuracy); + PathCombine::Intersect(paths) => { + let mut path_iter = paths.into_iter(); + let result = path_iter + .next() + .unwrap_or_else(|| PathCombine::Path(vec![])); + let mut result = path_combine(result, accuracy); while let Some(to_intersect) = path_iter.next() { - let to_intersect = path_combine(to_intersect, accuracy); - result = path_intersect(&result, &to_intersect, accuracy); + let to_intersect = path_combine(to_intersect, accuracy); + result = path_intersect(&result, &to_intersect, accuracy); } result diff --git a/src/bezier/path/arithmetic/chain_add.rs b/src/bezier/path/arithmetic/chain_add.rs index a8cb9dc2..cf22568c 100644 --- a/src/bezier/path/arithmetic/chain_add.rs +++ b/src/bezier/path/arithmetic/chain_add.rs @@ -1,20 +1,31 @@ -use super::ray_cast::*; -use super::super::path::*; -use super::super::graph_path::*; use super::super::super::super::geo::*; +use super::super::graph_path::*; +use super::super::path::*; +use super::ray_cast::*; /// /// Adds multiple paths in a single operation /// -pub fn path_add_chain(paths: &Vec>, accuracy: f64) -> Vec -where P::Point: Coordinate+Coordinate2D, - POut: BezierPathFactory { +pub fn path_add_chain( + paths: &Vec>, + accuracy: f64, +) -> Vec +where + P::Point: Coordinate + Coordinate2D, + POut: BezierPathFactory, +{ // Build up the graph path from the supplied list let mut merged_path = GraphPath::new(); for (path_idx, path) in paths.iter().enumerate() { - let path_idx = path_idx as u32; - merged_path = merged_path.collide(GraphPath::from_merged_paths(path.into_iter().map(|path| (path, PathLabel(path_idx, PathDirection::from(path))))), accuracy); + let path_idx = path_idx as u32; + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path.into_iter() + .map(|path| (path, PathLabel(path_idx, PathDirection::from(path)))), + ), + accuracy, + ); } merged_path.round(accuracy); @@ -22,9 +33,9 @@ where P::Point: Coordinate+Coordinate2D, // Set the exterior edges using the 'add' algorithm (all edges are considered 'external' here) merged_path.set_edge_kinds_by_ray_casting(|path_crossings| { for count in path_crossings.iter() { - if (count&1) != 0 { - return true; - } + if (count & 1) != 0 { + return true; + } } return false; }); diff --git a/src/bezier/path/arithmetic/cut.rs b/src/bezier/path/arithmetic/cut.rs index 467eab79..147ea15b 100644 --- a/src/bezier/path/arithmetic/cut.rs +++ b/src/bezier/path/arithmetic/cut.rs @@ -1,7 +1,7 @@ -use super::ray_cast::*; -use super::super::path::*; -use super::super::graph_path::*; use super::super::super::super::geo::*; +use super::super::graph_path::*; +use super::super::path::*; +use super::ray_cast::*; /// /// The result of a path cut operation @@ -11,36 +11,53 @@ pub struct PathCut { pub interior_path: Vec

, /// The path that was outside of the 'cut' path - pub exterior_path: Vec

+ pub exterior_path: Vec

, } /// /// Cuts a path (`path1`) into two along another path (`path2`), returning the part of `path1` that was interior to `path2` and /// the part that was exterior in one operation /// -pub fn path_cut(path1: &Vec, path2: &Vec, accuracy: f64) -> PathCut -where P1::Point: Coordinate+Coordinate2D, - P2: BezierPath, - POut: BezierPathFactory { +pub fn path_cut( + path1: &Vec, + path2: &Vec, + accuracy: f64, +) -> PathCut +where + P1::Point: Coordinate + Coordinate2D, + P2: BezierPath, + POut: BezierPathFactory, +{ // If path1 is empty, then there are no points in the result. If path2 is empty, then all points are exterior if path1.len() == 0 { - return PathCut { - interior_path: vec![], - exterior_path: vec![] + return PathCut { + interior_path: vec![], + exterior_path: vec![], }; } else if path2.len() == 0 { - return PathCut { - interior_path: vec![], - exterior_path: path1.iter().map(|path| POut::from_path(path)).collect() + return PathCut { + interior_path: vec![], + exterior_path: path1.iter().map(|path| POut::from_path(path)).collect(), }; } // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path1.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path1 + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide with the target side to generate a full path - merged_path = merged_path.collide(GraphPath::from_merged_paths(path2.into_iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), accuracy); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path2 + .into_iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + accuracy, + ); merged_path.round(accuracy); // The interior edges are those found by intersecting the second path with the first @@ -64,6 +81,6 @@ where P1::Point: Coordinate+Coordinate2D, PathCut { interior_path, - exterior_path + exterior_path, } } diff --git a/src/bezier/path/arithmetic/full_intersect.rs b/src/bezier/path/arithmetic/full_intersect.rs index 47377299..e3179486 100644 --- a/src/bezier/path/arithmetic/full_intersect.rs +++ b/src/bezier/path/arithmetic/full_intersect.rs @@ -1,7 +1,7 @@ -use super::ray_cast::*; -use super::super::path::*; -use super::super::graph_path::*; use super::super::super::super::geo::*; +use super::super::graph_path::*; +use super::super::path::*; +use super::ray_cast::*; /// /// The result of a path cut operation @@ -12,35 +12,58 @@ pub struct PathIntersection { pub intersecting_path: Vec

, /// The path that was outside of the 'cut' path for the two input paths - pub exterior_paths: [Vec

; 2] + pub exterior_paths: [Vec

; 2], } /// /// Intersects two paths, returning both the path that is the intersection and the paths that are outside /// -pub fn path_full_intersect(path1: &Vec, path2: &Vec, accuracy: f64) -> PathIntersection -where P1::Point: Coordinate+Coordinate2D, - P2: BezierPath, - POut: BezierPathFactory { +pub fn path_full_intersect( + path1: &Vec, + path2: &Vec, + accuracy: f64, +) -> PathIntersection +where + P1::Point: Coordinate + Coordinate2D, + P2: BezierPath, + POut: BezierPathFactory, +{ // If path1 is empty, then there are no points in the result. If path2 is empty, then all points are exterior if path1.len() == 0 { - return PathIntersection { - intersecting_path: vec![], - exterior_paths: [vec![], path2.iter().map(|path| POut::from_path(path)).collect()] + return PathIntersection { + intersecting_path: vec![], + exterior_paths: [ + vec![], + path2.iter().map(|path| POut::from_path(path)).collect(), + ], }; } else if path2.len() == 0 { - return PathIntersection { - intersecting_path: vec![], - exterior_paths: [path1.iter().map(|path| POut::from_path(path)).collect(), vec![]] + return PathIntersection { + intersecting_path: vec![], + exterior_paths: [ + path1.iter().map(|path| POut::from_path(path)).collect(), + vec![], + ], }; } // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path1.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path1 + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide with the target side to generate a full path - merged_path = merged_path.collide(GraphPath::from_merged_paths(path2.into_iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), accuracy); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path2 + .into_iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + accuracy, + ); merged_path.round(accuracy); // The interior edges are those found by intersecting the second path with the first @@ -66,8 +89,19 @@ where P1::Point: Coordinate+Coordinate2D, // TODO: it would be faster to re-use the existing merged paths here, but this will fail to properly generate a subtracted paths // in the case where edges of the two paths overlap. let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path2.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); - merged_path = merged_path.collide(GraphPath::from_merged_paths(path1.into_iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), accuracy); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path2 + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path1 + .into_iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + accuracy, + ); merged_path.round(accuracy); merged_path.set_exterior_by_subtracting(); @@ -77,7 +111,7 @@ where P1::Point: Coordinate+Coordinate2D, let exterior_from_path_2 = merged_path.exterior_paths(); PathIntersection { - intersecting_path: intersecting_path, - exterior_paths: [exterior_from_path_1, exterior_from_path_2] + intersecting_path: intersecting_path, + exterior_paths: [exterior_from_path_1, exterior_from_path_2], } } diff --git a/src/bezier/path/arithmetic/intersect.rs b/src/bezier/path/arithmetic/intersect.rs index ce6ce7ee..0800723a 100644 --- a/src/bezier/path/arithmetic/intersect.rs +++ b/src/bezier/path/arithmetic/intersect.rs @@ -1,45 +1,60 @@ -use super::ray_cast::*; -use super::super::path::*; -use super::super::graph_path::*; use super::super::super::super::geo::*; +use super::super::graph_path::*; +use super::super::path::*; +use super::ray_cast::*; -impl GraphPath { +impl GraphPath { /// /// Given a labelled graph path, marks exterior edges by intersecting `PathSource::Path1` and `PathSource::Path2` /// pub fn set_exterior_by_intersecting(&mut self) { // Use an even-odd winding rule (all edges are considered 'external') - self.set_edge_kinds_by_ray_casting(|path_crossings| (path_crossings[0]&1) != 0 && (path_crossings[1]&1) != 0); + self.set_edge_kinds_by_ray_casting(|path_crossings| { + (path_crossings[0] & 1) != 0 && (path_crossings[1] & 1) != 0 + }); } } /// /// Generates the path formed by intersecting two sets of paths -/// +/// /// The input vectors represent the external edges of the path to intersect (a single BezierPath cannot have any holes in it, so a set of them /// effectively represents a path intended to be rendered with an even-odd winding rule) /// -pub fn path_intersect(path1: &Vec, path2: &Vec, accuracy: f64) -> Vec -where P1::Point: Coordinate+Coordinate2D, - P2: BezierPath, - POut: BezierPathFactory { +pub fn path_intersect( + path1: &Vec, + path2: &Vec, + accuracy: f64, +) -> Vec +where + P1::Point: Coordinate + Coordinate2D, + P2: BezierPath, + POut: BezierPathFactory, +{ // If either path is empty, short-circuit by returning the other if path1.len() == 0 { - return path2.iter() - .map(|path| POut::from_path(path)) - .collect(); + return path2.iter().map(|path| POut::from_path(path)).collect(); } else if path2.len() == 0 { - return path1.iter() - .map(|path| POut::from_path(path)) - .collect(); + return path1.iter().map(|path| POut::from_path(path)).collect(); } // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path1.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path1 + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide with the target side to generate a full path - merged_path = merged_path.collide(GraphPath::from_merged_paths(path2.into_iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), accuracy); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path2 + .into_iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + accuracy, + ); merged_path.round(accuracy); // Set the exterior edges using the 'intersect' algorithm diff --git a/src/bezier/path/arithmetic/mod.rs b/src/bezier/path/arithmetic/mod.rs index 82970f2c..6d6715bd 100644 --- a/src/bezier/path/arithmetic/mod.rs +++ b/src/bezier/path/arithmetic/mod.rs @@ -1,17 +1,17 @@ -mod ray_cast; -mod intersect; mod add; -mod chain_add; -mod sub; mod chain; +mod chain_add; mod cut; mod full_intersect; +mod intersect; +mod ray_cast; +mod sub; -pub use self::ray_cast::*; -pub use self::intersect::*; pub use self::add::*; -pub use self::sub::*; pub use self::chain::*; pub use self::chain_add::*; pub use self::cut::*; pub use self::full_intersect::*; +pub use self::intersect::*; +pub use self::ray_cast::*; +pub use self::sub::*; diff --git a/src/bezier/path/arithmetic/ray_cast.rs b/src/bezier/path/arithmetic/ray_cast.rs index a56105c6..7a5f97ab 100644 --- a/src/bezier/path/arithmetic/ray_cast.rs +++ b/src/bezier/path/arithmetic/ray_cast.rs @@ -1,9 +1,9 @@ -use crate::bezier::path::path::*; -use crate::bezier::path::graph_path::*; -use crate::bezier::path::is_clockwise::*; -use crate::bezier::curve::*; -use crate::bezier::normal::*; -use crate::geo::*; +use super::super::super::super::geo::*; +use super::super::super::curve::*; +use super::super::super::normal::*; +use super::super::graph_path::*; +use super::super::is_clockwise::*; +use super::super::path::*; use crate::line::*; use smallvec::*; @@ -14,11 +14,13 @@ use smallvec::*; #[derive(Copy, Clone, PartialEq, Debug)] pub enum PathDirection { Clockwise, - Anticlockwise + Anticlockwise, } impl<'a, P: BezierPath> From<&'a P> for PathDirection -where P::Point: Coordinate2D { +where + P::Point: Coordinate2D, +{ #[inline] fn from(path: &'a P) -> PathDirection { if path.is_clockwise() { @@ -31,29 +33,32 @@ where P::Point: Coordinate2D { /// /// Label attached to a path used for arithmetic -/// +/// /// The parameters are the path number (counting from 0) and the winding direction of the path -/// +/// #[derive(Clone, Copy, Debug)] pub struct PathLabel(pub u32, pub PathDirection); -impl GraphPath { +impl GraphPath { /// /// Returns the ray collisions with an ordering algorithm applied so that the rays enters and exits sets of overlapping edges /// in a consistent order. /// - pub fn ordered_ray_collisions>(&self, ray: &L) -> Vec<(GraphRayCollision, f64, f64, Point)> { - let mut collisions = self.ray_collisions(ray); + pub fn ordered_ray_collisions>( + &self, + ray: &L, + ) -> Vec<(GraphRayCollision, f64, f64, Point)> { + let mut collisions = self.ray_collisions(ray); // There should always be an even number of collisions on a particular ray cast through a closed shape - test_assert!((collisions.len()&1) == 0); + test_assert!((collisions.len() & 1) == 0); // For collisions that overlap, ensure that the first shape is outermost so that subtractions work (swap based on the direction) // This interacts with the ordering chosen in ray_collisions: if that ordering changes this may no longer be correct if collisions.len() > 0 { - for collision_idx in 0..(collisions.len()-1) { - let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx+0]; - let (collision_b, _curve_t, line_t_b, _pos) = &collisions[collision_idx+1]; + for collision_idx in 0..(collisions.len() - 1) { + let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx + 0]; + let (collision_b, _curve_t, line_t_b, _pos) = &collisions[collision_idx + 1]; if line_t_a == line_t_b { let edge_a = collision_a.edge(); @@ -61,11 +66,15 @@ impl GraphPath { // Swap if the earlier of the two edges is moving in the appropriate direction if edge_a.start_idx == edge_b.start_idx { - let earlier_edge = if edge_a.edge_idx < edge_b.edge_idx { edge_a } else { edge_b }; - let PathLabel(_, edge_direction) = self.edge_label(earlier_edge); + let earlier_edge = if edge_a.edge_idx < edge_b.edge_idx { + edge_a + } else { + edge_b + }; + let PathLabel(_, edge_direction) = self.edge_label(earlier_edge); if edge_direction == PathDirection::Anticlockwise { - collisions.swap(collision_idx, collision_idx+1); + collisions.swap(collision_idx, collision_idx + 1); } } } @@ -77,12 +86,15 @@ impl GraphPath { /// /// Sets the edge kinds by performing ray casting - /// + /// /// The function passed in to this method takes two parameters: these are the number of times edges have been crossed in /// path 1 and path 2. It should return true if this number of crossings represents a point inside the final shape, or false /// if it represents a point outside of the shape. /// - pub fn set_edge_kinds_by_ray_casting) -> bool>(&mut self, is_inside: FnIsInside) { + pub fn set_edge_kinds_by_ray_casting) -> bool>( + &mut self, + is_inside: FnIsInside, + ) { for point_idx in 0..self.num_points() { for next_edge in self.edge_refs_for_point(point_idx) { // Only process edges that have not yet been categorised @@ -91,8 +103,8 @@ impl GraphPath { } // Cast a ray at this edge - let real_edge = self.get_edge(next_edge); - let next_point = real_edge.point_at_pos(0.5); + let real_edge = self.get_edge(next_edge); + let next_point = real_edge.point_at_pos(0.5); let next_normal = real_edge.normal_at_pos(0.5); // Mark the next edge as visited (this prevents an infinite loop in the event the edge we're aiming at has a length of 0 and thus will always be an intersection) @@ -103,28 +115,63 @@ impl GraphPath { let mut path_crossings: SmallVec<[i32; 8]> = smallvec![0, 0]; // Cast a ray at the target edge - let ray = (next_point - next_normal, next_point); - let ray_direction = ray.1 - ray.0; - let collisions = self.ordered_ray_collisions(&ray); + let ray = (next_point - next_normal, next_point); + let ray_direction = ray.1 - ray.0; + let mut collisions = self.ray_collisions(&ray); + + // There should always be an even number of collisions on a particular ray cast through a closed shape + test_assert!((collisions.len() & 1) == 0); + + // For collisions that overlap, ensure that the first shape is outermost so that subtractions work (swap based on the direction) + // This interacts with the ordering chosen in ray_collisions: if that ordering changes this may no longer be correct + if collisions.len() > 0 { + for collision_idx in 0..(collisions.len() - 1) { + let (collision_a, _curve_t, line_t_a, _pos) = + &collisions[collision_idx + 0]; + let (collision_b, _curve_t, line_t_b, _pos) = + &collisions[collision_idx + 1]; + + if line_t_a == line_t_b { + let edge_a = collision_a.edge(); + let edge_b = collision_b.edge(); + + // Swap if the earlier of the two edges is moving in the appropriate direction + if edge_a.start_idx == edge_b.start_idx { + let earlier_edge = if edge_a.edge_idx < edge_b.edge_idx { + edge_a + } else { + edge_b + }; + let PathLabel(_, edge_direction) = self.edge_label(earlier_edge); + + if edge_direction == PathDirection::Anticlockwise { + collisions.swap(collision_idx, collision_idx + 1); + } + } + } + } + } // Work out which edges are interior or exterior for every edge the ray has crossed for (collision, curve_t, _line_t, _pos) in collisions { let is_intersection = collision.is_intersection(); - let edge = collision.edge(); + let edge = collision.edge(); let PathLabel(path_number, direction) = self.edge_label(edge); // The relative direction of the tangent to the ray indicates the direction we're crossing in - let normal = self.get_edge(edge).normal_at_pos(curve_t); + let normal = self.get_edge(edge).normal_at_pos(curve_t); - let side = ray_direction.dot(&normal).signum() as i32; - let side = match direction { - PathDirection::Clockwise => { side }, - PathDirection::Anticlockwise => { -side } + let side = ray_direction.dot(&normal).signum() as i32; + let side = match direction { + PathDirection::Clockwise => side, + PathDirection::Anticlockwise => -side, }; - // Extend the path_crossings vector to accomodate all of the paths included by this ray - while path_crossings.len() <= path_number as usize { path_crossings.push(0); } + // Extend the path_crossings vector + while path_crossings.len() <= path_number as usize { + path_crossings.push(0); + } let was_inside = is_inside(&path_crossings); if side < 0 { @@ -139,7 +186,10 @@ impl GraphPath { // If this isn't an intersection, set whether or not the edge is exterior let edge_kind = self.edge_kind(edge); - if !is_intersection && (edge_kind == GraphPathEdgeKind::Uncategorised || edge_kind == GraphPathEdgeKind::Visited) { + if !is_intersection + && (edge_kind == GraphPathEdgeKind::Uncategorised + || edge_kind == GraphPathEdgeKind::Visited) + { // Exterior edges move from inside to outside or vice-versa if curve_t > 0.1 && curve_t < 0.9 { if was_inside ^ is_inside { @@ -167,7 +217,9 @@ impl GraphPath { } // The ray should exit and enter the path an even number of times - test_assert!(path_crossings.into_iter().all(|crossing_count| crossing_count == 0)); + test_assert!(path_crossings + .into_iter() + .all(|crossing_count| crossing_count == 0)); } } } diff --git a/src/bezier/path/arithmetic/sub.rs b/src/bezier/path/arithmetic/sub.rs index 176f1c8f..875d86e3 100644 --- a/src/bezier/path/arithmetic/sub.rs +++ b/src/bezier/path/arithmetic/sub.rs @@ -1,45 +1,60 @@ -use super::ray_cast::*; -use super::super::path::*; -use super::super::graph_path::*; use super::super::super::super::geo::*; +use super::super::graph_path::*; +use super::super::path::*; +use super::ray_cast::*; -impl GraphPath { +impl GraphPath { /// /// Given a labelled graph path, marks exterior edges by subtracting `PathSource::Path2` from `PathSource::Path1` /// pub fn set_exterior_by_subtracting(&mut self) { // Use an even-odd winding rule (all edges are considered 'external') - self.set_edge_kinds_by_ray_casting(|path_crossings| (path_crossings[0]&1) != 0 && (path_crossings[1]&1) == 0); + self.set_edge_kinds_by_ray_casting(|path_crossings| { + (path_crossings[0] & 1) != 0 && (path_crossings[1] & 1) == 0 + }); } } /// /// Generates the path formed by subtracting two sets of paths -/// +/// /// The input vectors represent the external edges of the path to subtract (a single BezierPath cannot have any holes in it, so a set of them /// effectively represents a path intended to be rendered with an even-odd winding rule) /// -pub fn path_sub(path1: &Vec, path2: &Vec, accuracy: f64) -> Vec -where P1::Point: Coordinate+Coordinate2D, - P2: BezierPath, - POut: BezierPathFactory { +pub fn path_sub( + path1: &Vec, + path2: &Vec, + accuracy: f64, +) -> Vec +where + P1::Point: Coordinate + Coordinate2D, + P2: BezierPath, + POut: BezierPathFactory, +{ // If either path is empty, short-circuit by returning the other if path1.len() == 0 { - return path2.iter() - .map(|path| POut::from_path(path)) - .collect(); + return path2.iter().map(|path| POut::from_path(path)).collect(); } else if path2.len() == 0 { - return path1.iter() - .map(|path| POut::from_path(path)) - .collect(); + return path1.iter().map(|path| POut::from_path(path)).collect(); } // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path1.into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path1 + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide with the target side to generate a full path - merged_path = merged_path.collide(GraphPath::from_merged_paths(path2.into_iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), accuracy); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + path2 + .into_iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + accuracy, + ); merged_path.round(accuracy); // Set the exterior edges using the 'subtract' algorithm diff --git a/src/bezier/path/bounds.rs b/src/bezier/path/bounds.rs index 5777a000..b99fbaae 100644 --- a/src/bezier/path/bounds.rs +++ b/src/bezier/path/bounds.rs @@ -1,12 +1,12 @@ +use super::super::super::geo::*; +use super::super::curve::*; use super::path::*; use super::to_curves::*; -use super::super::curve::*; -use super::super::super::geo::*; /// /// Finds the bounds of a path -/// -pub fn path_bounding_box>(path: &P) -> Bounds { +/// +pub fn path_bounding_box>(path: &P) -> Bounds { path_to_curves(path) .map(|curve: Curve| curve.bounding_box()) .reduce(|first: Bounds, second| first.union_bounds(second)) @@ -15,8 +15,10 @@ pub fn path_bounding_box>(pat /// /// Finds the bounds of a path using the looser 'fast' algorithm -/// -pub fn path_fast_bounding_box>(path: &P) -> Bounds { +/// +pub fn path_fast_bounding_box>( + path: &P, +) -> Bounds { path_to_curves(path) .map(|curve: Curve| curve.fast_bounding_box()) .reduce(|first: Bounds, second| first.union_bounds(second)) diff --git a/src/bezier/path/graph_path/edge.rs b/src/bezier/path/graph_path/edge.rs index b55112aa..d1841820 100644 --- a/src/bezier/path/graph_path/edge.rs +++ b/src/bezier/path/graph_path/edge.rs @@ -1,19 +1,31 @@ -use super::{GraphPath, GraphEdgeRef, GraphEdge, GraphPathEdge, GraphPathEdgeKind}; -use crate::bezier::curve::*; +use super::{GraphEdge, GraphEdgeRef, GraphPath, GraphPathEdge, GraphPathEdgeKind}; use crate::bezier::bounds::*; +use crate::bezier::curve::*; use crate::geo::*; -use std::fmt; use std::cell::*; +use std::fmt; impl GraphPathEdge { /// /// Creates a new graph path edge - /// + /// #[inline] - pub (crate) fn new(kind: GraphPathEdgeKind, (cp1, cp2): (Point, Point), end_idx: usize, label: Label, following_edge_idx: usize) -> GraphPathEdge { + pub(crate) fn new( + kind: GraphPathEdgeKind, + (cp1, cp2): (Point, Point), + end_idx: usize, + label: Label, + following_edge_idx: usize, + ) -> GraphPathEdge { GraphPathEdge { - label, kind, cp1, cp2, end_idx, following_edge_idx, bbox: RefCell::new(None) + label, + kind, + cp1, + cp2, + end_idx, + following_edge_idx, + bbox: RefCell::new(None), } } @@ -21,23 +33,26 @@ impl GraphPathEdge { /// Invalidates the cache for this edge /// #[inline] - pub (crate) fn invalidate_cache(&self) { + pub(crate) fn invalidate_cache(&self) { (*self.bbox.borrow_mut()) = None; } } -impl<'a, Point: 'a, Label: 'a+Copy> GraphEdge<'a, Point, Label> { +impl<'a, Point: 'a, Label: 'a + Copy> GraphEdge<'a, Point, Label> { /// /// Creates a new graph edge (with an edge kind of 'exterior') - /// + /// #[inline] - pub (crate) fn new(graph: &'a GraphPath, edge: GraphEdgeRef) -> GraphEdge<'a, Point, Label> { + pub(crate) fn new( + graph: &'a GraphPath, + edge: GraphEdgeRef, + ) -> GraphEdge<'a, Point, Label> { test_assert!(edge.start_idx < graph.points.len()); test_assert!(edge.edge_idx < graph.points[edge.start_idx].forward_edges.len()); GraphEdge { - graph: graph, - edge: edge + graph: graph, + edge: edge, } } @@ -59,14 +74,14 @@ impl<'a, Point: 'a, Label: 'a+Copy> GraphEdge<'a, Point, Label> { /// /// Returns if this is an interior or an exterior edge in the path - /// + /// pub fn kind(&self) -> GraphPathEdgeKind { self.edge().kind } /// /// Returns the index of the start point of this edge - /// + /// #[inline] pub fn start_point_index(&self) -> usize { if self.edge.reverse { @@ -78,7 +93,7 @@ impl<'a, Point: 'a, Label: 'a+Copy> GraphEdge<'a, Point, Label> { /// /// Returns the index of the end point of this edge - /// + /// #[inline] pub fn end_point_index(&self) -> usize { if self.edge.reverse { @@ -97,14 +112,14 @@ impl<'a, Point: 'a, Label: 'a+Copy> GraphEdge<'a, Point, Label> { } } -impl<'a, Point: 'a+Coordinate, Label: 'a> Geo for GraphEdge<'a, Point, Label> { +impl<'a, Point: 'a + Coordinate, Label: 'a> Geo for GraphEdge<'a, Point, Label> { type Point = Point; } -impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> BezierCurve for GraphEdge<'a, Point, Label> { +impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> BezierCurve for GraphEdge<'a, Point, Label> { /// /// The start point of this curve - /// + /// #[inline] fn start_point(&self) -> Self::Point { self.graph.points[self.start_point_index()].position.clone() @@ -112,7 +127,7 @@ impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> BezierCurve for GraphEdge<'a, Poi /// /// The end point of this curve - /// + /// #[inline] fn end_point(&self) -> Self::Point { self.graph.points[self.end_point_index()].position.clone() @@ -120,7 +135,7 @@ impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> BezierCurve for GraphEdge<'a, Poi /// /// The control points in this curve - /// + /// #[inline] fn control_points(&self) -> (Self::Point, Self::Point) { let edge = self.edge(); @@ -131,34 +146,34 @@ impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> BezierCurve for GraphEdge<'a, Poi (edge.cp1.clone(), edge.cp2.clone()) } } - + /// /// Faster but less accurate bounding box for a curve - /// + /// /// This will produce a bounding box that contains the curve but which may be larger than necessary - /// + /// #[inline] - fn fast_bounding_box>(&self) -> Bounds { - let edge = self.edge(); + fn fast_bounding_box>(&self) -> Bounds { + let edge = self.edge(); - let mut bbox = edge.bbox.borrow_mut(); + let mut bbox = edge.bbox.borrow_mut(); if let Some((ref min, ref max)) = *bbox { Bounds::from_min_max(*min, *max) } else { - let start = self.graph.points[self.edge.start_idx].position; - let end = self.graph.points[edge.end_idx].position; - let control_points = (edge.cp1, edge.cp2); + let start = self.graph.points[self.edge.start_idx].position; + let end = self.graph.points[edge.end_idx].position; + let control_points = (edge.cp1, edge.cp2); - let min = Self::Point::from_smallest_components(start, end); - let min = Self::Point::from_smallest_components(min, control_points.0); - let min = Self::Point::from_smallest_components(min, control_points.1); + let min = Self::Point::from_smallest_components(start, end); + let min = Self::Point::from_smallest_components(min, control_points.0); + let min = Self::Point::from_smallest_components(min, control_points.1); - let max = Self::Point::from_biggest_components(start, end); - let max = Self::Point::from_biggest_components(max, control_points.0); - let max = Self::Point::from_biggest_components(max, control_points.1); + let max = Self::Point::from_biggest_components(start, end); + let max = Self::Point::from_biggest_components(max, control_points.0); + let max = Self::Point::from_biggest_components(max, control_points.1); - let bounds = Bounds::from_min_max(min, max); + let bounds = Bounds::from_min_max(min, max); *bbox = Some((bounds.min(), bounds.max())); bounds @@ -167,14 +182,14 @@ impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> BezierCurve for GraphEdge<'a, Poi /// /// Computes the bounds of this bezier curve - /// + /// #[inline] - fn bounding_box>(&self) -> Bounds { - let edge = self.edge(); + fn bounding_box>(&self) -> Bounds { + let edge = self.edge(); - let start = self.graph.points[self.edge.start_idx].position; - let end = self.graph.points[edge.end_idx].position; - let (cp1, cp2) = (edge.cp1, edge.cp2); + let start = self.graph.points[self.edge.start_idx].position; + let end = self.graph.points[edge.end_idx].position; + let (cp1, cp2) = (edge.cp1, edge.cp2); let bounds: Bounds = bounding_box4(start, cp1, cp2, end); @@ -182,15 +197,25 @@ impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> BezierCurve for GraphEdge<'a, Poi } } -impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> HasBoundingBox for GraphEdge<'a, Point, Label> { +impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> HasBoundingBox for GraphEdge<'a, Point, Label> { #[inline] - fn get_bounding_box>(&self) -> Bounds { + fn get_bounding_box>(&self) -> Bounds { self.fast_bounding_box() } } -impl<'a, Point: fmt::Debug, Label: 'a+Copy> fmt::Debug for GraphEdge<'a, Point, Label> { +impl<'a, Point: fmt::Debug, Label: 'a + Copy> fmt::Debug for GraphEdge<'a, Point, Label> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}: {:?} -> {:?} ({:?} -> {:?} ({:?}, {:?}))", self.kind(), self.edge.start_idx, self.edge().end_idx, self.graph.points[self.edge.start_idx].position, self.graph.points[self.edge().end_idx].position, self.edge().cp1, self.edge().cp2) + write!( + f, + "{:?}: {:?} -> {:?} ({:?} -> {:?} ({:?}, {:?}))", + self.kind(), + self.edge.start_idx, + self.edge().end_idx, + self.graph.points[self.edge.start_idx].position, + self.graph.points[self.edge().end_idx].position, + self.edge().cp1, + self.edge().cp2 + ) } } diff --git a/src/bezier/path/graph_path/edge_ref.rs b/src/bezier/path/graph_path/edge_ref.rs index fd63d1a0..ac0cc0d5 100644 --- a/src/bezier/path/graph_path/edge_ref.rs +++ b/src/bezier/path/graph_path/edge_ref.rs @@ -1,4 +1,4 @@ -use super::{GraphPath, GraphEdge, GraphEdgeRef}; +use super::{GraphEdge, GraphEdgeRef, GraphPath}; use crate::geo::*; impl GraphEdgeRef { @@ -14,7 +14,9 @@ impl GraphEdgeRef { /// /// A GraphEdgeRef can be created from a GraphEdge in order to release the borrow /// -impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> From> for GraphEdgeRef { +impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> From> + for GraphEdgeRef +{ fn from(edge: GraphEdge<'a, Point, Label>) -> GraphEdgeRef { edge.edge } @@ -23,13 +25,15 @@ impl<'a, Point: 'a+Coordinate, Label: 'a+Copy> From> /// /// A GraphEdgeRef can be created from a GraphEdge in order to release the borrow /// -impl<'a, 'b, Point: 'a+Coordinate, Label: 'a+Copy> From<&'b GraphEdge<'a, Point, Label>> for GraphEdgeRef { +impl<'a, 'b, Point: 'a + Coordinate, Label: 'a + Copy> From<&'b GraphEdge<'a, Point, Label>> + for GraphEdgeRef +{ fn from(edge: &'b GraphEdge<'a, Point, Label>) -> GraphEdgeRef { edge.edge } } -impl GraphPath { +impl GraphPath { /// /// Given an edge ref, returns the edge ref that follows it /// @@ -38,13 +42,17 @@ impl GraphPath { if edge_ref.reverse { // Need to search in reverse for the edge for connected_from in self.points[edge_ref.start_idx].connected_from.iter() { - for (edge_idx, edge) in self.points[*connected_from].forward_edges.iter().enumerate() { + for (edge_idx, edge) in self.points[*connected_from] + .forward_edges + .iter() + .enumerate() + { if edge.end_idx == edge_ref.start_idx { return GraphEdgeRef { - start_idx: *connected_from, - edge_idx: edge_idx, - reverse: true - } + start_idx: *connected_from, + edge_idx: edge_idx, + reverse: true, + }; } } } @@ -55,11 +63,10 @@ impl GraphPath { let edge = &self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx]; GraphEdgeRef { - start_idx: edge.end_idx, - edge_idx: edge.following_edge_idx, - reverse: false + start_idx: edge.end_idx, + edge_idx: edge.following_edge_idx, + reverse: false, } } } } - diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index 690e1667..f69c1d25 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -1,31 +1,32 @@ use super::path::*; use crate::bezier::curve::*; -use crate::geo::*; use crate::consts::*; +use crate::geo::*; use smallvec::*; -use std::fmt; use std::cell::*; +use std::fmt; mod edge; mod edge_ref; -mod ray_collision; mod path_collision; +mod ray_collision; -#[cfg(test)] pub (crate) mod test; +#[cfg(test)] +pub(crate) mod test; pub use self::edge::*; pub use self::edge_ref::*; -pub use self::ray_collision::*; pub use self::path_collision::*; +pub use self::ray_collision::*; /// Maximum number of edges to traverse when 'healing' gaps found in an external path const MAX_HEAL_DEPTH: usize = 3; /// /// Kind of a graph path edge -/// +/// #[derive(Clone, Copy, Debug, PartialEq)] pub enum GraphPathEdgeKind { /// An edge that hasn't been categorised yet @@ -35,14 +36,14 @@ pub enum GraphPathEdgeKind { Visited, /// An exterior edge - /// + /// /// These edges represent a transition between the inside and the outside of the path - Exterior, + Exterior, /// An interior edge - /// + /// /// These edges are on the inside of the path - Interior + Interior, } /// @@ -51,18 +52,18 @@ pub enum GraphPathEdgeKind { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct GraphEdgeRef { /// The index of the point this edge starts from - pub (crate) start_idx: usize, + pub(crate) start_idx: usize, /// The index of the edge within the point - pub (crate) edge_idx: usize, + pub(crate) edge_idx: usize, /// True if this reference is for the reverse of this edge - pub (crate) reverse: bool + pub(crate) reverse: bool, } /// /// Enum representing an edge in a graph path -/// +/// #[derive(Clone, Debug)] struct GraphPathEdge { /// The label attached to this edge @@ -84,7 +85,7 @@ struct GraphPathEdge { end_idx: usize, /// The bounding box of this edge, if it has been calculated - bbox: RefCell> + bbox: RefCell>, } /// @@ -99,15 +100,23 @@ struct GraphPathPoint { forward_edges: SmallVec<[GraphPathEdge; 2]>, /// The points with edges connecting to this point - connected_from: SmallVec<[usize; 2]> + connected_from: SmallVec<[usize; 2]>, } impl GraphPathPoint { /// /// Creates a new graph path point /// - fn new(position: Point, forward_edges: SmallVec<[GraphPathEdge; 2]>, connected_from: SmallVec<[usize; 2]>) -> GraphPathPoint { - GraphPathPoint { position, forward_edges, connected_from } + fn new( + position: Point, + forward_edges: SmallVec<[GraphPathEdge; 2]>, + connected_from: SmallVec<[usize; 2]>, + ) -> GraphPathPoint { + GraphPathPoint { + position, + forward_edges, + connected_from, + } } } @@ -115,14 +124,14 @@ impl GraphPathPoint { /// A graph path is a path where each point can have more than one connected edge. Edges are categorized /// into interior and exterior edges depending on if they are on the outside or the inside of the combined /// shape. -/// +/// #[derive(Clone)] pub struct GraphPath { /// The points in this graph and their edges. Each 'point' here consists of two control points and an end point points: Vec>, /// The index to assign to the next path added to this path - next_path_index: usize + next_path_index: usize, } /// @@ -134,28 +143,31 @@ pub enum CollidedGraphPath { Collided(GraphPath), /// None of the edges has collisions in them - Merged(GraphPath) + Merged(GraphPath), } impl Geo for GraphPath { type Point = Point; } -impl GraphPath { +impl GraphPath { /// /// Creates a new graph path with no points /// pub fn new() -> GraphPath { GraphPath { - points: vec![], - next_path_index: 0 + points: vec![], + next_path_index: 0, } } /// /// Creates a graph path from a bezier path - /// - pub fn from_path>(path: &P, label: Label) -> GraphPath { + /// + pub fn from_path>( + path: &P, + label: Label, + ) -> GraphPath { // All edges are exterior for a single path let mut points = vec![]; @@ -164,15 +176,17 @@ impl GraphPath { points.push(GraphPathPoint::new(start_point, smallvec![], smallvec![])); // We'll add edges to the previous point - let mut last_point_pos = start_point; - let mut last_point_idx = 0; - let mut next_point_idx = 1; + let mut last_point_pos = start_point; + let mut last_point_idx = 0; + let mut next_point_idx = 1; // Iterate through the points in the path for (cp1, cp2, end_point) in path.points() { // Ignore points that are too close to the last point if end_point.is_near_to(&last_point_pos, CLOSE_DISTANCE) { - if cp1.is_near_to(&last_point_pos, CLOSE_DISTANCE) && cp2.is_near_to(&cp1, CLOSE_DISTANCE) { + if cp1.is_near_to(&last_point_pos, CLOSE_DISTANCE) + && cp2.is_near_to(&cp1, CLOSE_DISTANCE) + { continue; } } @@ -181,12 +195,20 @@ impl GraphPath { points.push(GraphPathPoint::new(end_point, smallvec![], smallvec![])); // Add an edge from the last point to the next point - points[last_point_idx].forward_edges.push(GraphPathEdge::new(GraphPathEdgeKind::Uncategorised, (cp1, cp2), next_point_idx, label, 0)); + points[last_point_idx] + .forward_edges + .push(GraphPathEdge::new( + GraphPathEdgeKind::Uncategorised, + (cp1, cp2), + next_point_idx, + label, + 0, + )); // Update the last/next pooints - last_point_idx += 1; - next_point_idx += 1; - last_point_pos = end_point; + last_point_idx += 1; + next_point_idx += 1; + last_point_pos = end_point; } // Close the path @@ -201,11 +223,19 @@ impl GraphPath { points[last_point_idx].forward_edges[0].end_idx = 0; } else { // Need to draw a line to the last point (as there is always a single following edge, the following edge index is always 0 here) - let close_vector = points[last_point_idx].position - start_point; - let cp1 = close_vector * 0.33 + start_point; - let cp2 = close_vector * 0.66 + start_point; - - points[last_point_idx].forward_edges.push(GraphPathEdge::new(GraphPathEdgeKind::Uncategorised, (cp1, cp2), 0, label, 0)); + let close_vector = points[last_point_idx].position - start_point; + let cp1 = close_vector * 0.33 + start_point; + let cp2 = close_vector * 0.66 + start_point; + + points[last_point_idx] + .forward_edges + .push(GraphPathEdge::new( + GraphPathEdgeKind::Uncategorised, + (cp1, cp2), + 0, + label, + 0, + )); } } else { // Just a start point and no edges: remove the start point as it doesn't really make sense @@ -214,8 +244,8 @@ impl GraphPath { // Create the graph path from the points let mut path = GraphPath { - points: points, - next_path_index: 1 + points: points, + next_path_index: 1, }; path.recalculate_reverse_connections(); path @@ -224,13 +254,19 @@ impl GraphPath { /// /// Creates a new graph path by merging (not colliding) a set of paths with their labels /// - pub fn from_merged_paths<'a, P: 'a+BezierPath, PathIter: IntoIterator>(paths: PathIter) -> GraphPath { + pub fn from_merged_paths< + 'a, + P: 'a + BezierPath, + PathIter: IntoIterator, + >( + paths: PathIter, + ) -> GraphPath { // Create an empty path let mut merged_path = GraphPath::new(); // Merge each path in turn for (path, label) in paths { - let path = GraphPath::from_path(path, label); + let path = GraphPath::from_path(path, label); merged_path = merged_path.merge(path); } @@ -263,7 +299,7 @@ impl GraphPath { /// /// Returns the number of points in this graph. Points are numbered from 0 to this value. - /// + /// #[inline] pub fn num_points(&self) -> usize { self.points.len() @@ -273,7 +309,7 @@ impl GraphPath { /// Returns an iterator of all edges in this graph /// #[inline] - pub fn all_edges<'a>(&'a self) -> impl 'a+Iterator> { + pub fn all_edges<'a>(&'a self) -> impl 'a + Iterator> { (0..(self.points.len())) .into_iter() .flat_map(move |point_num| self.edges_for_point(point_num)) @@ -283,37 +319,55 @@ impl GraphPath { /// Returns an iterator of all the edges in this graph, as references /// #[inline] - pub fn all_edge_refs<'a>(&'a self) -> impl 'a+Iterator { + pub fn all_edge_refs<'a>(&'a self) -> impl 'a + Iterator { (0..(self.points.len())) .into_iter() - .flat_map(move |point_idx| (0..(self.points[point_idx].forward_edges.len())) - .into_iter() - .map(move |edge_idx| GraphEdgeRef { - start_idx: point_idx, - edge_idx: edge_idx, - reverse: false - })) + .flat_map(move |point_idx| { + (0..(self.points[point_idx].forward_edges.len())) + .into_iter() + .map(move |edge_idx| GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }) + }) } /// /// Returns an iterator of the edges that leave a particular point - /// + /// /// Edges are directional: this will provide the edges that leave the supplied point /// #[inline] - pub fn edges_for_point<'a>(&'a self, point_num: usize) -> impl 'a+Iterator> { + pub fn edges_for_point<'a>( + &'a self, + point_num: usize, + ) -> impl 'a + Iterator> { (0..(self.points[point_num].forward_edges.len())) .into_iter() - .map(move |edge_idx| GraphEdge::new(self, GraphEdgeRef { start_idx: point_num, edge_idx: edge_idx, reverse: false })) + .map(move |edge_idx| { + GraphEdge::new( + self, + GraphEdgeRef { + start_idx: point_num, + edge_idx: edge_idx, + reverse: false, + }, + ) + }) } /// /// Returns the edge refs for a particular point /// - pub fn edge_refs_for_point(&self, point_num: usize) -> impl Iterator { + pub fn edge_refs_for_point(&self, point_num: usize) -> impl Iterator { (0..(self.points[point_num].forward_edges.len())) .into_iter() - .map(move |edge_idx| GraphEdgeRef { start_idx: point_num, edge_idx: edge_idx, reverse: false }) + .map(move |edge_idx| GraphEdgeRef { + start_idx: point_num, + edge_idx: edge_idx, + reverse: false, + }) } /// @@ -326,12 +380,16 @@ impl GraphPath { /// /// Returns an iterator of the edges that arrive at a particular point - /// + /// /// Edges are directional: this will provide the edges that connect to the supplied point /// - pub fn reverse_edges_for_point<'a>(&'a self, point_num: usize) -> impl 'a+Iterator> { + pub fn reverse_edges_for_point<'a>( + &'a self, + point_num: usize, + ) -> impl 'a + Iterator> { // Fetch the points that connect to this point - self.points[point_num].connected_from + self.points[point_num] + .connected_from .iter() .flat_map(move |connected_from| { let connected_from = *connected_from; @@ -340,8 +398,13 @@ impl GraphPath { (0..(self.points[connected_from].forward_edges.len())) .into_iter() .filter_map(move |edge_idx| { - if self.points[connected_from].forward_edges[edge_idx].end_idx == point_num { - Some(GraphEdgeRef { start_idx: connected_from, edge_idx: edge_idx, reverse: true }) + if self.points[connected_from].forward_edges[edge_idx].end_idx == point_num + { + Some(GraphEdgeRef { + start_idx: connected_from, + edge_idx: edge_idx, + reverse: true, + }) } else { None } @@ -352,35 +415,34 @@ impl GraphPath { /// /// Merges in another path - /// - /// This adds the edges in the new path to this path without considering if they are internal or external + /// + /// This adds the edges in the new path to this path without considering if they are internal or external /// pub fn merge(self, merge_path: GraphPath) -> GraphPath { // Copy the points from this graph - let mut new_points = self.points; - let next_path_idx = self.next_path_index; + let mut new_points = self.points; + let next_path_idx = self.next_path_index; // Add in points from the merge path - let offset = new_points.len(); - new_points.extend(merge_path.points.into_iter() - .map(|mut point| { - // Update the offsets in the edges - for mut edge in &mut point.forward_edges { - edge.end_idx += offset; - } + let offset = new_points.len(); + new_points.extend(merge_path.points.into_iter().map(|mut point| { + // Update the offsets in the edges + for mut edge in &mut point.forward_edges { + edge.end_idx += offset; + } - for previous_point in &mut point.connected_from { - *previous_point += offset; - } + for previous_point in &mut point.connected_from { + *previous_point += offset; + } - // Generate the new edge - point - })); + // Generate the new edge + point + })); // Combined path GraphPath { - points: new_points, - next_path_index: next_path_idx + merge_path.next_path_index + points: new_points, + next_path_index: next_path_idx + merge_path.next_path_index, } } @@ -388,14 +450,14 @@ impl GraphPath { /// Returns true if the specified edge is very short (starts and ends at the same point and does not cover a significant amount of ground) /// fn edge_is_very_short(&self, edge_ref: GraphEdgeRef) -> bool { - let edge = &self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx]; + let edge = &self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx]; if edge_ref.start_idx == edge.end_idx { // Find the points on this edge let start_point = &self.points[edge_ref.start_idx].position; - let cp1 = &edge.cp1; - let cp2 = &edge.cp2; - let end_point = &self.points[edge.end_idx].position; + let cp1 = &edge.cp1; + let cp2 = &edge.cp2; + let end_point = &self.points[edge.end_idx].position; // If all the points are close to each other, then this is a short edge start_point.is_near_to(end_point, CLOSE_DISTANCE) @@ -409,7 +471,7 @@ impl GraphPath { /// /// Removes an edge by updating the previous edge to point at its next edge - /// + /// /// Control points are not updated so the shape will be distorted if the removed edge is very long /// fn remove_edge(&mut self, edge_ref: GraphEdgeRef) { @@ -417,20 +479,36 @@ impl GraphPath { self.check_following_edge_consistency(); // Find the next edge - let next_point_idx = self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx].end_idx; - let next_edge_idx = self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx].following_edge_idx; + let next_point_idx = + self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx].end_idx; + let next_edge_idx = + self.points[edge_ref.start_idx].forward_edges[edge_ref.edge_idx].following_edge_idx; // Edge shouldn't just loop around to itself test_assert!(next_point_idx != edge_ref.start_idx || next_edge_idx != edge_ref.edge_idx); // ... and the preceding edge (by searching all of the connected points) - let previous_edge_ref = self.points[edge_ref.start_idx].connected_from + let previous_edge_ref = self.points[edge_ref.start_idx] + .connected_from .iter() - .map(|point_idx| { let point_idx = *point_idx; self.points[point_idx].forward_edges.iter().enumerate().map(move |(edge_idx, edge)| (point_idx, edge_idx, edge)) }) + .map(|point_idx| { + let point_idx = *point_idx; + self.points[point_idx] + .forward_edges + .iter() + .enumerate() + .map(move |(edge_idx, edge)| (point_idx, edge_idx, edge)) + }) .flatten() .filter_map(|(point_idx, edge_idx, edge)| { - if edge.end_idx == edge_ref.start_idx && edge.following_edge_idx == edge_ref.edge_idx { - Some(GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }) + if edge.end_idx == edge_ref.start_idx + && edge.following_edge_idx == edge_ref.edge_idx + { + Some(GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }) } else { None } @@ -440,15 +518,27 @@ impl GraphPath { test_assert!(previous_edge_ref.is_some()); if let Some(previous_edge_ref) = previous_edge_ref { - test_assert!(self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx].end_idx == edge_ref.start_idx); - test_assert!(self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx].following_edge_idx == edge_ref.edge_idx); + test_assert!( + self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx] + .end_idx + == edge_ref.start_idx + ); + test_assert!( + self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx] + .following_edge_idx + == edge_ref.edge_idx + ); // Reconnect the previous edge to the next edge - self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx].end_idx = next_point_idx; - self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx].following_edge_idx = next_edge_idx; + self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx] + .end_idx = next_point_idx; + self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx] + .following_edge_idx = next_edge_idx; // Remove the old edge from the list - self.points[edge_ref.start_idx].forward_edges.remove(edge_ref.edge_idx); + self.points[edge_ref.start_idx] + .forward_edges + .remove(edge_ref.edge_idx); // For all the connected points, update the following edge refs let mut still_connected = false; @@ -457,7 +547,8 @@ impl GraphPath { self.points[edge_ref.start_idx].connected_from.dedup(); for connected_point_idx in self.points[edge_ref.start_idx].connected_from.clone() { for edge_idx in 0..(self.points[connected_point_idx].forward_edges.len()) { - let connected_edge = &mut self.points[connected_point_idx].forward_edges[edge_idx]; + let connected_edge = + &mut self.points[connected_point_idx].forward_edges[edge_idx]; // Only interested in edges on the point we just changed if connected_edge.end_idx != edge_ref.start_idx { @@ -481,7 +572,9 @@ impl GraphPath { // If the two points are not still connected, remove the previous point from the connected list if !still_connected { - self.points[edge_ref.start_idx].connected_from.retain(|point_idx| *point_idx != edge_ref.start_idx); + self.points[edge_ref.start_idx] + .connected_from + .retain(|point_idx| *point_idx != edge_ref.start_idx); } // Edges should be consistent again @@ -491,7 +584,7 @@ impl GraphPath { /// /// Removes any edges that appear to be 'very short' from this graph - /// + /// /// 'Very short' edges are edges that start and end at the same point and have control points very close to the start position /// fn remove_all_very_short_edges(&mut self) { @@ -499,7 +592,11 @@ impl GraphPath { let mut edge_idx = 0; while edge_idx < self.points[point_idx].forward_edges.len() { // Remove this edge if it's very short - let edge_ref = GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }; + let edge_ref = GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }; if self.edge_is_very_short(edge_ref) { self.remove_edge(edge_ref); } else { @@ -512,24 +609,32 @@ impl GraphPath { /// /// Collides this path against another, generating a merged path - /// + /// /// Anywhere this graph intersects the second graph, a point with two edges will be generated. All edges will be left as /// interior or exterior depending on how they're set on the graph they originate from. - /// + /// /// Working out the collision points is the first step to performing path arithmetic: the resulting graph can be altered /// to specify edge types - knowing if an edge is an interior or exterior edge makes it possible to tell the difference /// between a hole cut into a shape and an intersection. - /// + /// /// Unlike collide(), this will indicate if any collisions were detected or if the two paths merged without collisions - /// - pub fn collide_or_merge(mut self, collide_path: GraphPath, accuracy: f64) -> CollidedGraphPath { + /// + pub fn collide_or_merge( + mut self, + collide_path: GraphPath, + accuracy: f64, + ) -> CollidedGraphPath { // Generate a merged path with all of the edges - let collision_offset = self.points.len(); - self = self.merge(collide_path); + let collision_offset = self.points.len(); + self = self.merge(collide_path); // Search for collisions between our original path and the new one let total_points = self.points.len(); - if self.detect_collisions(0..collision_offset, collision_offset..total_points, accuracy) { + if self.detect_collisions( + 0..collision_offset, + collision_offset..total_points, + accuracy, + ) { CollidedGraphPath::Collided(self) } else { CollidedGraphPath::Merged(self) @@ -538,22 +643,30 @@ impl GraphPath { /// /// Collides this path against another, generating a merged path - /// + /// /// Anywhere this graph intersects the second graph, a point with two edges will be generated. All edges will be left as /// interior or exterior depending on how they're set on the graph they originate from. - /// + /// /// Working out the collision points is the first step to performing path arithmetic: the resulting graph can be altered /// to specify edge types - knowing if an edge is an interior or exterior edge makes it possible to tell the difference /// between a hole cut into a shape and an intersection. - /// - pub fn collide(mut self, collide_path: GraphPath, accuracy: f64) -> GraphPath { + /// + pub fn collide( + mut self, + collide_path: GraphPath, + accuracy: f64, + ) -> GraphPath { // Generate a merged path with all of the edges - let collision_offset = self.points.len(); - self = self.merge(collide_path); + let collision_offset = self.points.len(); + self = self.merge(collide_path); // Search for collisions between our original path and the new one let total_points = self.points.len(); - self.detect_collisions(0..collision_offset, collision_offset..total_points, accuracy); + self.detect_collisions( + 0..collision_offset, + collision_offset..total_points, + accuracy, + ); // Return the result self @@ -567,8 +680,12 @@ impl GraphPath { self.points[point_idx].position.round(accuracy); for edge_idx in 0..(self.points[point_idx].forward_edges.len()) { - self.points[point_idx].forward_edges[edge_idx].cp1.round(accuracy); - self.points[point_idx].forward_edges[edge_idx].cp2.round(accuracy); + self.points[point_idx].forward_edges[edge_idx] + .cp1 + .round(accuracy); + self.points[point_idx].forward_edges[edge_idx] + .cp2 + .round(accuracy); } } } @@ -620,7 +737,7 @@ impl GraphPath { pub fn edge_label(&self, edge: GraphEdgeRef) -> Label { self.points[edge.start_idx].forward_edges[edge.edge_idx].label } - + /// /// Resets the edge kinds in this path by setting them all to uncategorised /// @@ -636,8 +753,8 @@ impl GraphPath { /// Sets the kind of an edge and any connected edge where there are no intersections (only one edge) /// pub fn set_edge_kind_connected(&mut self, edge: GraphEdgeRef, kind: GraphPathEdgeKind) { - let mut current_edge = edge; - let mut visited = vec![false; self.points.len()]; + let mut current_edge = edge; + let mut visited = vec![false; self.points.len()]; // Move forward loop { @@ -646,8 +763,9 @@ impl GraphPath { visited[current_edge.start_idx] = true; // Pick the next edge - let end_idx = self.points[current_edge.start_idx].forward_edges[current_edge.edge_idx].end_idx; - let edges = &self.points[end_idx].forward_edges; + let end_idx = + self.points[current_edge.start_idx].forward_edges[current_edge.edge_idx].end_idx; + let edges = &self.points[end_idx].forward_edges; if edges.len() != 1 { // At an intersection @@ -655,9 +773,9 @@ impl GraphPath { } else { // Move on current_edge = GraphEdgeRef { - start_idx: end_idx, - edge_idx: 0, - reverse: false + start_idx: end_idx, + edge_idx: 0, + reverse: false, } } @@ -678,15 +796,18 @@ impl GraphPath { break; } else { // There's a single preceding point (but maybe more than one edge) - let current_point_idx = current_edge.start_idx; - let previous_point_idx = self.points[current_edge.start_idx].connected_from[0]; + let current_point_idx = current_edge.start_idx; + let previous_point_idx = self.points[current_edge.start_idx].connected_from[0]; // Find the index of the preceding edge - let mut previous_edges = (0..(self.points[previous_point_idx].forward_edges.len())) + let mut previous_edges = (0..(self.points[previous_point_idx].forward_edges.len())) .into_iter() - .filter(|edge_idx| self.points[previous_point_idx].forward_edges[*edge_idx].end_idx == current_point_idx); + .filter(|edge_idx| { + self.points[previous_point_idx].forward_edges[*edge_idx].end_idx + == current_point_idx + }); - let previous_edge_idx = previous_edges.next().expect("Previous edge"); + let previous_edge_idx = previous_edges.next().expect("Previous edge"); if previous_edges.next().is_some() { // There is more than one edge connecting these two points break; @@ -694,9 +815,9 @@ impl GraphPath { // Move on to the next edge current_edge = GraphEdgeRef { - start_idx: previous_point_idx, - edge_idx: previous_edge_idx, - reverse: false + start_idx: previous_point_idx, + edge_idx: previous_edge_idx, + reverse: false, }; // Change its kind @@ -717,7 +838,8 @@ impl GraphPath { self.edges_for_point(point_idx) .chain(self.reverse_edges_for_point(point_idx)) .filter(|edge| edge.kind() == GraphPathEdgeKind::Exterior) - .count() == 1 + .count() + == 1 } /// @@ -725,18 +847,27 @@ impl GraphPath { /// fn edge_has_gap(&self, edge: GraphEdgeRef) -> bool { // Interior edges have no gaps - if self.points[edge.start_idx].forward_edges[edge.edge_idx].kind != GraphPathEdgeKind::Exterior { + if self.points[edge.start_idx].forward_edges[edge.edge_idx].kind + != GraphPathEdgeKind::Exterior + { false } else { // Get the end point index for this edge let (start_idx, end_idx) = if edge.reverse { - (self.points[edge.start_idx].forward_edges[edge.edge_idx].end_idx, edge.start_idx) + ( + self.points[edge.start_idx].forward_edges[edge.edge_idx].end_idx, + edge.start_idx, + ) } else { - (edge.start_idx, self.points[edge.start_idx].forward_edges[edge.edge_idx].end_idx) + ( + edge.start_idx, + self.points[edge.start_idx].forward_edges[edge.edge_idx].end_idx, + ) }; // Result is true if there is no edge attached to the end point that is marked exterior (other than the edge leading back to the initial point) - !self.edges_for_point(end_idx) + !self + .edges_for_point(end_idx) .chain(self.reverse_edges_for_point(end_idx)) .filter(|following_edge| following_edge.end_point_index() != start_idx) .any(|following_edge| following_edge.kind() == GraphPathEdgeKind::Exterior) @@ -752,10 +883,10 @@ impl GraphPath { let end_point_idx = self.points[point_idx].forward_edges[edge_idx].end_idx; // State of the algorithm - let mut preceding_edge = vec![None; self.points.len()]; - let mut points_to_process = vec![(point_idx, end_point_idx)]; - let mut current_depth = 0; - let mut target_point_idx = None; + let mut preceding_edge = vec![None; self.points.len()]; + let mut points_to_process = vec![(point_idx, end_point_idx)]; + let mut current_depth = 0; + let mut target_point_idx = None; // Iterate until we hit the maximum depth while current_depth < max_depth && target_point_idx.is_none() { @@ -765,24 +896,36 @@ impl GraphPath { // Process all the points found in the previous pass for (from_point_idx, next_point_idx) in points_to_process { // Stop once we find a point - if target_point_idx.is_some() { break; } + if target_point_idx.is_some() { + break; + } // Process all edges connected to this point - for next_edge in self.edges_for_point(next_point_idx) /*.chain(self.reverse_edges_for_point(next_point_idx)) */ { - let edge_end_point_idx = next_edge.end_point_index(); - let next_edge_ref = GraphEdgeRef::from(&next_edge); - let edge_start_idx = next_edge.start_point_index(); + for next_edge in self.edges_for_point(next_point_idx) + /*.chain(self.reverse_edges_for_point(next_point_idx)) */ + { + let edge_end_point_idx = next_edge.end_point_index(); + let next_edge_ref = GraphEdgeRef::from(&next_edge); + let edge_start_idx = next_edge.start_point_index(); // Don't go back the way we came - if edge_end_point_idx == from_point_idx { continue; } + if edge_end_point_idx == from_point_idx { + continue; + } // Don't revisit points we already have a trail for - if preceding_edge[edge_end_point_idx].is_some() { continue; } + if preceding_edge[edge_end_point_idx].is_some() { + continue; + } // Ignore exterior edges (except exterior edges where edge_has_gap is true, which indicate we've crossed our gap) let mut reversed_edge_ref = next_edge_ref; reversed_edge_ref.reverse = !reversed_edge_ref.reverse; - if next_edge.kind() == GraphPathEdgeKind::Exterior && !self.edge_has_gap(reversed_edge_ref) { continue; } + if next_edge.kind() == GraphPathEdgeKind::Exterior + && !self.edge_has_gap(reversed_edge_ref) + { + continue; + } // Add this as a preceding edge preceding_edge[edge_end_point_idx] = Some(next_edge_ref); @@ -807,14 +950,17 @@ impl GraphPath { } if let Some(target_point_idx) = target_point_idx { - // Target_point represents the final point in the + // Target_point represents the final point in the let mut current_point_idx = target_point_idx; while current_point_idx != end_point_idx { - let previous_edge_ref = preceding_edge[current_point_idx].expect("Previous point during gap healing"); + let previous_edge_ref = + preceding_edge[current_point_idx].expect("Previous point during gap healing"); // Mark this edge as exterior - self.points[previous_edge_ref.start_idx].forward_edges[previous_edge_ref.edge_idx].kind = GraphPathEdgeKind::Exterior; + self.points[previous_edge_ref.start_idx].forward_edges + [previous_edge_ref.edge_idx] + .kind = GraphPathEdgeKind::Exterior; // Move to the previous point let previous_edge = self.get_edge(previous_edge_ref); @@ -831,7 +977,7 @@ impl GraphPath { /// /// Finds any gaps in the edges marked as exterior and attempts to 'heal' them by finding a route to another /// part of the path with a missing edge - /// + /// /// Returns true if all the gaps that were found were 'healed' /// pub fn heal_exterior_gaps(&mut self) -> bool { @@ -841,7 +987,11 @@ impl GraphPath { for point_idx in 0..(self.points.len()) { for edge_idx in 0..(self.points[point_idx].forward_edges.len()) { // If this edge has a gap... - if self.edge_has_gap(GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }) { + if self.edge_has_gap(GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }) { // ... try to heal it if !self.heal_edge_with_gap(point_idx, edge_idx, MAX_HEAL_DEPTH) { all_healed = false; @@ -856,15 +1006,15 @@ impl GraphPath { /// /// Finds the exterior edges and turns them into a series of paths /// - pub fn exterior_paths>(&self) -> Vec { + pub fn exterior_paths>(&self) -> Vec { // List of paths returned by this function let mut exterior_paths = vec![]; // Array of points visited on a path that we've added to the result let mut visited = vec![false; self.points.len()]; - let mut previous_point = vec![None; self.points.len()]; - let mut points_to_check: SmallVec<[_; 16]> = smallvec![]; + let mut previous_point = vec![None; self.points.len()]; + let mut points_to_check: SmallVec<[_; 16]> = smallvec![]; for point_idx in 0..(self.points.len()) { // Ignore this point if we've already visited it as part of a path @@ -896,7 +1046,8 @@ impl GraphPath { for (previous_point_idx, current_point_idx) in points_to_check { let mut edges = if current_point_idx == point_idx { // For the first point, only search forward - self.reverse_edges_for_point(current_point_idx).collect::>() + self.reverse_edges_for_point(current_point_idx) + .collect::>() } else { // For all other points, search all edges self.edges_for_point(current_point_idx) @@ -905,7 +1056,12 @@ impl GraphPath { }; // Only follow exterior edges... - if current_point_idx == point_idx || edges.iter().any(|edge| edge.kind() == GraphPathEdgeKind::Exterior && edge.end_point_index() != previous_point_idx) { + if current_point_idx == point_idx + || edges.iter().any(|edge| { + edge.kind() == GraphPathEdgeKind::Exterior + && edge.end_point_index() != previous_point_idx + }) + { // ... unless the only exterior edge is the one we arrived on, in which case we'll follow interior edges to try to bridge gaps as a backup measure edges.retain(|edge| edge.kind() == GraphPathEdgeKind::Exterior); } else { @@ -942,12 +1098,12 @@ impl GraphPath { // If we found a loop, generate a path if previous_point[point_idx].is_some() { - let mut path_points = vec![]; - let mut cur_point_idx = point_idx; + let mut path_points = vec![]; + let mut cur_point_idx = point_idx; while let Some((last_point_idx, ref edge)) = previous_point[cur_point_idx] { // Push to the path points (we're following the edges in reverse, so points are in reverse order) - let (cp1, cp2) = edge.control_points(); + let (cp1, cp2) = edge.control_points(); let start_point = edge.start_point(); path_points.push((cp2, cp1, start_point)); @@ -967,7 +1123,7 @@ impl GraphPath { // Start point of the path is the initial point we checked let start_point = self.points[point_idx].position.clone(); - let new_path = POut::from_points(start_point, path_points); + let new_path = POut::from_points(start_point, path_points); exterior_paths.push(new_path); } } @@ -979,17 +1135,19 @@ impl GraphPath { /// /// Represents an edge in a graph path -/// +/// #[derive(Clone)] pub struct GraphEdge<'a, Point: 'a, Label: 'a> { /// The graph that this point is for graph: &'a GraphPath, /// A reference to the edge this point is for - edge: GraphEdgeRef + edge: GraphEdgeRef, } -impl fmt::Debug for GraphPath { +impl fmt::Debug + for GraphPath +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for point_idx in 0..(self.points.len()) { write!(f, "\nPoint {:?}:", point_idx)?; diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index a70a5f58..d11073d8 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -1,14 +1,14 @@ -use super::{GraphPath, GraphEdge, GraphEdgeRef, GraphPathPoint, GraphPathEdge}; +use super::{GraphEdge, GraphEdgeRef, GraphPath, GraphPathEdge, GraphPathPoint}; use crate::bezier::curve::*; use crate::bezier::intersection::*; -use crate::geo::*; use crate::consts::*; +use crate::geo::*; use smallvec::*; +use std::cmp::Ordering; use std::mem; use std::ops::Range; -use std::cmp::Ordering; /// /// Struct describing a collision between two edges @@ -25,29 +25,42 @@ struct Collision { edge_1_t: f64, /// The location on edge2 of the collision - edge_2_t: f64 + edge_2_t: f64, } -impl GraphPath { - /// +impl GraphPath { + /// /// True if the t value is effectively at the start of the curve - /// + /// #[inline] - fn t_is_zero(t: f64) -> bool { t <= 0.0 } + fn t_is_zero(t: f64) -> bool { + t <= 0.0 + } /// /// True if the t value is effective at the end of the curve - /// + /// #[inline] - fn t_is_one(t: f64) -> bool { t >= 1.0 } + fn t_is_one(t: f64) -> bool { + t >= 1.0 + } /// /// Retrieves the ordered graph edges for a range of points /// fn get_ordered_edges<'a>(&'a self, points: Range) -> Vec> { - let mut ordered_edges = points.into_iter() - .flat_map(|point_idx| (0..self.points[point_idx].forward_edges.len()).into_iter().map(move |edge_idx| (point_idx, edge_idx))) - .map(|(point_idx, edge_idx)| GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }) + let mut ordered_edges = points + .into_iter() + .flat_map(|point_idx| { + (0..self.points[point_idx].forward_edges.len()) + .into_iter() + .map(move |edge_idx| (point_idx, edge_idx)) + }) + .map(|(point_idx, edge_idx)| GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }) .map(|edge_ref| GraphEdge::new(self, edge_ref)) .collect::>(); @@ -55,7 +68,10 @@ impl GraphPath { let bb1 = edge1.get_bounding_box::>(); let bb2 = edge2.get_bounding_box::>(); - bb1.min().x().partial_cmp(&bb2.min().x()).unwrap_or(Ordering::Equal) + bb1.min() + .x() + .partial_cmp(&bb2.min().x()) + .unwrap_or(Ordering::Equal) }); ordered_edges @@ -66,7 +82,7 @@ impl GraphPath { /// #[inline] fn snap_points(p1: &Point, p2: &Point) -> Point { - Point::from_components(&[(p1.x() + p2.x())/2.0, (p1.y() + p2.y())/2.0]) + Point::from_components(&[(p1.x() + p2.x()) / 2.0, (p1.y() + p2.y()) / 2.0]) } /// @@ -74,8 +90,8 @@ impl GraphPath { /// #[inline] fn point_is_near(p1: &Point, p2: &Point, max_distance_squared: f64) -> bool { - let offset = *p1 - *p2; - let squared_distance = offset.dot(&offset); + let offset = *p1 - *p2; + let squared_distance = offset.dot(&offset); squared_distance <= max_distance_squared } @@ -93,33 +109,38 @@ impl GraphPath { for (src_curve, tgt_curve) in sweep_self(ordered_edges.iter()) { // Find any collisions between the two edges (to the required accuracy) let mut edge_collisions = curve_intersects_curve_clip(src_curve, tgt_curve, accuracy); - if edge_collisions.len() == 0 { continue; } + if edge_collisions.len() == 0 { + continue; + } // Remove any pairs of collisions that are too close together remove_and_round_close_collisions(&mut edge_collisions, src_curve, tgt_curve); // Turn into collisions, filtering out the collisions that occur at the ends (where one edge joins another). // For cases where we get a collision at the end of an edge, wait for the one at the beginning of the next one - let edge_collisions = edge_collisions.into_iter() - .filter(|(src_t, tgt_t)| !(Self::t_is_one(*src_t) || Self::t_is_one(*tgt_t) || (Self::t_is_zero(*src_t) && Self::t_is_zero(*tgt_t)))) - .map(|(src_t, tgt_t)| { - Collision { - edge_1: src_curve.edge, - edge_2: tgt_curve.edge, - edge_1_t: src_t, - edge_2_t: tgt_t - } + let edge_collisions = edge_collisions + .into_iter() + .filter(|(src_t, tgt_t)| { + !(Self::t_is_one(*src_t) + || Self::t_is_one(*tgt_t) + || (Self::t_is_zero(*src_t) && Self::t_is_zero(*tgt_t))) + }) + .map(|(src_t, tgt_t)| Collision { + edge_1: src_curve.edge, + edge_2: tgt_curve.edge, + edge_1_t: src_t, + edge_2_t: tgt_t, }) .map(|mut collision| { // If the collision is at the end of the edge, move it to the start of the following edge if Self::t_is_one(collision.edge_1_t) { - collision.edge_1 = self.following_edge_ref(collision.edge_1); - collision.edge_1_t = 0.0; + collision.edge_1 = self.following_edge_ref(collision.edge_1); + collision.edge_1_t = 0.0; } if Self::t_is_one(collision.edge_2_t) { - collision.edge_2 = self.following_edge_ref(collision.edge_2); - collision.edge_2_t = 0.0; + collision.edge_2 = self.following_edge_ref(collision.edge_2); + collision.edge_2_t = 0.0; } collision @@ -135,10 +156,10 @@ impl GraphPath { if let Some((t1, t2)) = find_self_intersection_point(&edge, accuracy) { if !(t1 <= 0.0 && t2 >= 1.0) && !(t1 >= 1.0 && t2 <= 0.0) { collisions.push(Collision { - edge_1: edge.edge, - edge_2: edge.edge, - edge_1_t: t1, - edge_2_t: t2 + edge_1: edge.edge, + edge_2: edge.edge, + edge_1_t: t1, + edge_2_t: t2, }); } } @@ -150,7 +171,12 @@ impl GraphPath { /// /// Finds any collisions that might exist between two ranges of points /// - fn find_collisions(&self, collide_from: Range, collide_to: Range, accuracy: f64) -> Vec { + fn find_collisions( + &self, + collide_from: Range, + collide_to: Range, + accuracy: f64, + ) -> Vec { if collide_from == collide_to { return self.find_self_collisions(collide_from, accuracy); } @@ -164,34 +190,39 @@ impl GraphPath { for (src_curve, tgt_curve) in sweep_against(collide_src.iter(), collide_tgt.iter()) { // Find any collisions between the two edges (to the required accuracy) - let mut edge_collisions = curve_intersects_curve_clip(src_curve, tgt_curve, accuracy); - if edge_collisions.len() == 0 { continue; } + let mut edge_collisions = curve_intersects_curve_clip(src_curve, tgt_curve, accuracy); + if edge_collisions.len() == 0 { + continue; + } // Remove any pairs of collisions that are too close together remove_and_round_close_collisions(&mut edge_collisions, src_curve, tgt_curve); // Turn into collisions, filtering out the collisions that occur at the ends (where one edge joins another). // For cases where we get a collision at the end of an edge, wait for the one at the beginning of the next one - let edge_collisions = edge_collisions.into_iter() - .filter(|(src_t, tgt_t)| !(Self::t_is_one(*src_t) || Self::t_is_one(*tgt_t) || (Self::t_is_zero(*src_t) && Self::t_is_zero(*tgt_t)))) - .map(|(src_t, tgt_t)| { - Collision { - edge_1: src_curve.edge, - edge_2: tgt_curve.edge, - edge_1_t: src_t, - edge_2_t: tgt_t - } + let edge_collisions = edge_collisions + .into_iter() + .filter(|(src_t, tgt_t)| { + !(Self::t_is_one(*src_t) + || Self::t_is_one(*tgt_t) + || (Self::t_is_zero(*src_t) && Self::t_is_zero(*tgt_t))) + }) + .map(|(src_t, tgt_t)| Collision { + edge_1: src_curve.edge, + edge_2: tgt_curve.edge, + edge_1_t: src_t, + edge_2_t: tgt_t, }) .map(|mut collision| { // If the collision is at the end of the edge, move it to the start of the following edge if Self::t_is_one(collision.edge_1_t) { - collision.edge_1 = self.following_edge_ref(collision.edge_1); - collision.edge_1_t = 0.0; + collision.edge_1 = self.following_edge_ref(collision.edge_1); + collision.edge_1_t = 0.0; } if Self::t_is_one(collision.edge_2_t) { - collision.edge_2 = self.following_edge_ref(collision.edge_2); - collision.edge_2_t = 0.0; + collision.edge_2 = self.following_edge_ref(collision.edge_2); + collision.edge_2_t = 0.0; } collision @@ -222,14 +253,14 @@ impl GraphPath { collision.edge_2.start_idx } else { // Create a new point - let edge = self.get_edge(collision.edge_1); - let new_point_pos = edge.point_at_pos(collision.edge_1_t); - let new_point_idx = self.points.len(); + let edge = self.get_edge(collision.edge_1); + let new_point_pos = edge.point_at_pos(collision.edge_1_t); + let new_point_idx = self.points.len(); self.points.push(GraphPathPoint { - position: new_point_pos, - forward_edges: smallvec![], - connected_from: smallvec![] + position: new_point_pos, + forward_edges: smallvec![], + connected_from: smallvec![], }); new_point_idx @@ -244,25 +275,29 @@ impl GraphPath { /// /// Given a list of collisions and the point where they end, organizes them by edge - /// + /// /// Return type is a vector of edges for each point, where each edge is a list of collisions, as 't' value on the edge and the /// index of the end point /// - fn organize_collisions_by_edge(&self, collisions: Vec<(Collision, usize)>) -> Vec; 2]>>> { + fn organize_collisions_by_edge( + &self, + collisions: Vec<(Collision, usize)>, + ) -> Vec; 2]>>> { // Initially there are no collisions for any point - let mut points: Vec; 2]>>> = vec![None; self.num_points()]; + let mut points: Vec; 2]>>> = + vec![None; self.num_points()]; // Iterate through the collisions and store them per edge. Every collision affects two edges for (collision, end_point_idx) in collisions.iter() { // First edge let point = points[collision.edge_1.start_idx].get_or_insert_with(|| smallvec![smallvec![]; self.points[collision.edge_1.start_idx].forward_edges.len()]); - let edge = &mut point[collision.edge_1.edge_idx]; + let edge = &mut point[collision.edge_1.edge_idx]; edge.push((collision.edge_1_t, *end_point_idx)); // Second edge let point = points[collision.edge_2.start_idx].get_or_insert_with(|| smallvec![smallvec![]; self.points[collision.edge_2.start_idx].forward_edges.len()]); - let edge = &mut point[collision.edge_2.edge_idx]; + let edge = &mut point[collision.edge_2.edge_idx]; edge.push((collision.edge_2_t, *end_point_idx)); } @@ -273,14 +308,19 @@ impl GraphPath { /// /// Searches two ranges of points in this object and detects collisions between them, subdividing the edges /// and creating branch points at the appropriate places. - /// + /// /// collide_from must indicate indices lower than collide_to - /// + /// /// Returns true if any collisions were found - /// - pub (crate) fn detect_collisions(&mut self, collide_from: Range, collide_to: Range, accuracy: f64) -> bool { + /// + pub(crate) fn detect_collisions( + &mut self, + collide_from: Range, + collide_to: Range, + accuracy: f64, + ) -> bool { // Find all of the collision points - let all_collisions = self.find_collisions(collide_from, collide_to, accuracy); + let all_collisions = self.find_collisions(collide_from, collide_to, accuracy); if all_collisions.len() == 0 { let collided_at_point = self.combine_overlapping_points(accuracy); self.remove_all_very_short_edges(); @@ -288,29 +328,39 @@ impl GraphPath { } // Add in any extra points that are required by the collisions we found - let all_collisions = self.create_collision_points(all_collisions); + let all_collisions = self.create_collision_points(all_collisions); // Organize the collisions by edge - let collisions_by_edge = self.organize_collisions_by_edge(all_collisions); + let collisions_by_edge = self.organize_collisions_by_edge(all_collisions); // Limit to just points with collisions - let collisions_by_point = collisions_by_edge.into_iter() - .enumerate() - .filter_map(|(point_idx, collisions)| collisions.map(|collisions| (point_idx, collisions))); + let collisions_by_point = + collisions_by_edge + .into_iter() + .enumerate() + .filter_map(|(point_idx, collisions)| { + collisions.map(|collisions| (point_idx, collisions)) + }); // Actually divide the edges by collision for (point_idx, edge_collisions) in collisions_by_point { for (edge_idx, mut collisions) in edge_collisions.into_iter().enumerate() { // Skip edges with no collisions - if collisions.len() == 0 { continue; } + if collisions.len() == 0 { + continue; + } self.check_following_edge_consistency(); // Create a copy of the edge. Our future edges will all have the same kind and label as the edge that's being divided - let edge = self.get_edge(GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }); - let kind = edge.kind(); - let label = edge.label(); - let edge = Curve::from_curve(&edge); + let edge = self.get_edge(GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }); + let kind = edge.kind(); + let label = edge.label(); + let edge = Curve::from_curve(&edge); // Sort collisions by t value collisions.sort_by(|(t1, _end_point_idx1), (t2, _end_point_idx2)| { @@ -324,87 +374,111 @@ impl GraphPath { }); // We'll progressively split bits from the edge - let mut remaining_edge = edge; - let mut remaining_t = 1.0; - let final_point_idx = self.points[point_idx].forward_edges[edge_idx].end_idx; - let final_following_edge_idx = self.points[point_idx].forward_edges[edge_idx].following_edge_idx; - let mut last_point_idx = point_idx; - let mut previous_edge = None; - let mut found_collisions = false; + let mut remaining_edge = edge; + let mut remaining_t = 1.0; + let final_point_idx = self.points[point_idx].forward_edges[edge_idx].end_idx; + let final_following_edge_idx = + self.points[point_idx].forward_edges[edge_idx].following_edge_idx; + let mut last_point_idx = point_idx; + let mut previous_edge = None; + let mut found_collisions = false; // Iterate through the collisions (skipping any at t=0) - let mut collisions = collisions.into_iter() - .filter(|(t, _)| !Self::t_is_zero(*t)); + let mut collisions = collisions.into_iter().filter(|(t, _)| !Self::t_is_zero(*t)); // First collision is special as we need to edit the existing edge instead of adding a new one if let Some((t, end_point_idx)) = collisions.next() { // Subdivide the edge let (next_edge, new_remaining_edge) = remaining_edge.subdivide::>(t); - let following_edge_idx = self.points[end_point_idx].forward_edges.len(); - let (cp1, cp2) = next_edge.control_points(); + let following_edge_idx = self.points[end_point_idx].forward_edges.len(); + let (cp1, cp2) = next_edge.control_points(); - test_assert!(next_edge.start_point().is_near_to(&self.points[point_idx].position, 0.1)); - test_assert!(next_edge.end_point().is_near_to(&self.points[end_point_idx].position, 0.1)); + test_assert!(next_edge + .start_point() + .is_near_to(&self.points[point_idx].position, 0.1)); + test_assert!(next_edge + .end_point() + .is_near_to(&self.points[end_point_idx].position, 0.1)); // Update the control points and end point index - let old_edge = &mut self.points[point_idx].forward_edges[edge_idx]; + let old_edge = &mut self.points[point_idx].forward_edges[edge_idx]; - old_edge.cp1 = cp1; - old_edge.cp2 = cp2; - old_edge.end_idx = end_point_idx; + old_edge.cp1 = cp1; + old_edge.cp2 = cp2; + old_edge.end_idx = end_point_idx; old_edge.following_edge_idx = following_edge_idx; old_edge.invalidate_cache(); // Move on to the next edge - previous_edge = Some((point_idx, edge_idx)); - remaining_t = 1.0-t; - remaining_edge = new_remaining_edge; - last_point_idx = end_point_idx; - found_collisions = true; + previous_edge = Some((point_idx, edge_idx)); + remaining_t = 1.0 - t; + remaining_edge = new_remaining_edge; + last_point_idx = end_point_idx; + found_collisions = true; } // Deal with the rest of the collisions for (t, end_point_idx) in collisions { // Point the previous edge at the new edge we're adding let new_edge_idx = self.points[last_point_idx].forward_edges.len(); - previous_edge.map(|(point_idx, edge_idx)| self.points[point_idx].forward_edges[edge_idx].following_edge_idx = new_edge_idx); + previous_edge.map(|(point_idx, edge_idx)| { + self.points[point_idx].forward_edges[edge_idx].following_edge_idx = + new_edge_idx + }); // Subdivide the remaining edge - let t2 = (t - (1.0-remaining_t))/remaining_t; + let t2 = (t - (1.0 - remaining_t)) / remaining_t; let (next_edge, new_remaining_edge) = remaining_edge.subdivide::>(t2); - let (cp1, cp2) = next_edge.control_points(); + let (cp1, cp2) = next_edge.control_points(); - test_assert!(next_edge.start_point().is_near_to(&self.points[last_point_idx].position, 0.1)); - test_assert!(next_edge.end_point().is_near_to(&self.points[end_point_idx].position, 0.1)); + test_assert!(next_edge + .start_point() + .is_near_to(&self.points[last_point_idx].position, 0.1)); + test_assert!(next_edge + .end_point() + .is_near_to(&self.points[end_point_idx].position, 0.1)); // Add the new edge to the previous point - let new_edge = GraphPathEdge::new(kind, (cp1, cp2), end_point_idx, label, 0); + let new_edge = GraphPathEdge::new(kind, (cp1, cp2), end_point_idx, label, 0); self.points[last_point_idx].forward_edges.push(new_edge); // Move on to the next edge - previous_edge = Some((last_point_idx, new_edge_idx)); - remaining_t = 1.0-t; - remaining_edge = new_remaining_edge; - last_point_idx = end_point_idx; - found_collisions = true; + previous_edge = Some((last_point_idx, new_edge_idx)); + remaining_t = 1.0 - t; + remaining_edge = new_remaining_edge; + last_point_idx = end_point_idx; + found_collisions = true; } // Provided there was at least one collision (ie, not just one at t=0), add the final edge if found_collisions { // Point the previous edge at the new edge we're adding let new_edge_idx = self.points[last_point_idx].forward_edges.len(); - previous_edge.map(|(point_idx, edge_idx)| self.points[point_idx].forward_edges[edge_idx].following_edge_idx = new_edge_idx); + previous_edge.map(|(point_idx, edge_idx)| { + self.points[point_idx].forward_edges[edge_idx].following_edge_idx = + new_edge_idx + }); // This edge ends where the original edge ended - let end_point_idx = final_point_idx; - let following_edge_idx = final_following_edge_idx; - let (cp1, cp2) = remaining_edge.control_points(); + let end_point_idx = final_point_idx; + let following_edge_idx = final_following_edge_idx; + let (cp1, cp2) = remaining_edge.control_points(); - test_assert!(remaining_edge.start_point().is_near_to(&self.points[last_point_idx].position, 0.1)); - test_assert!(remaining_edge.end_point().is_near_to(&self.points[end_point_idx].position, 0.1)); + test_assert!(remaining_edge + .start_point() + .is_near_to(&self.points[last_point_idx].position, 0.1)); + test_assert!(remaining_edge + .end_point() + .is_near_to(&self.points[end_point_idx].position, 0.1)); // Add to the final point - let final_edge = GraphPathEdge::new(kind, (cp1, cp2), end_point_idx, label, following_edge_idx); + let final_edge = GraphPathEdge::new( + kind, + (cp1, cp2), + end_point_idx, + label, + following_edge_idx, + ); self.points[last_point_idx].forward_edges.push(final_edge); } } @@ -427,11 +501,11 @@ impl GraphPath { /// /// Return value is a list of nearby points /// - fn sweep_for_nearby_points(&mut self, accuracy: f64) -> impl Iterator { + fn sweep_for_nearby_points(&mut self, accuracy: f64) -> impl Iterator { // Structure to attach a bounding box to a point within this graph: this limits us as to the maximum distance we can use as it's used for sweeping struct PointArea<'a, Point, Label>(&'a GraphPath, usize); - impl<'a, Point: Coordinate+Coordinate2D, Label> PointArea<'a, Point, Label> { + impl<'a, Point: Coordinate + Coordinate2D, Label> PointArea<'a, Point, Label> { #[inline] fn pos(&self) -> &Point { let PointArea(graph, point_idx) = self; @@ -447,24 +521,27 @@ impl GraphPath { } } - impl<'a, Point: Coordinate+Coordinate2D, Label> Geo for PointArea<'a, Point, Label> { + impl<'a, Point: Coordinate + Coordinate2D, Label> Geo for PointArea<'a, Point, Label> { type Point = Point; } - impl<'a, Point: Coordinate+Coordinate2D, Label> HasBoundingBox for PointArea<'a, Point, Label> { - fn get_bounding_box>(&self) -> Bounds { + impl<'a, Point: Coordinate + Coordinate2D, Label> HasBoundingBox for PointArea<'a, Point, Label> { + fn get_bounding_box>(&self) -> Bounds { let PointArea(graph, point_idx) = self; - let point = &graph.points[*point_idx]; - let lower = Point::from_components(&[point.position.x()-1.0, point.position.y()-1.0]); - let upper = Point::from_components(&[point.position.x()+1.0, point.position.y()+1.0]); + let point = &graph.points[*point_idx]; + let lower = + Point::from_components(&[point.position.x() - 1.0, point.position.y() - 1.0]); + let upper = + Point::from_components(&[point.position.x() + 1.0, point.position.y() + 1.0]); Bounds::from_min_max(lower, upper) } } // Collect all of the points in the graph, and order them by min_x - let mut all_points = (0..self.points.len()).into_iter() + let mut all_points = (0..self.points.len()) + .into_iter() .map(|idx| PointArea(self, idx)) .collect::>(); all_points.sort_by(|point1, point2| { @@ -475,39 +552,36 @@ impl GraphPath { }); // Sweep to find the points that might be colliding - let min_distance_squared = accuracy * accuracy; - let colliding_points = sweep_self(all_points.iter()) - .filter(|(point1, point2)| { - if point1.idx() == point2.idx() { - // A point cannot overlap itself - false - } else { - // Work out the distances between the points and - let p1 = point1.pos(); - let p2 = point2.pos(); + let min_distance_squared = accuracy * accuracy; + let colliding_points = sweep_self(all_points.iter()).filter(|(point1, point2)| { + if point1.idx() == point2.idx() { + // A point cannot overlap itself + false + } else { + // Work out the distances between the points and + let p1 = point1.pos(); + let p2 = point2.pos(); - let (x1, y1) = (p1.x(), p1.y()); - let (x2, y2) = (p2.x(), p2.y()); - let (dx, dy) = (x2-x1, y2-y1); + let (x1, y1) = (p1.x(), p1.y()); + let (x2, y2) = (p2.x(), p2.y()); + let (dx, dy) = (x2 - x1, y2 - y1); - let distance_squared = dx*dx + dy*dy; + let distance_squared = dx * dx + dy * dy; - distance_squared < min_distance_squared - } - }); + distance_squared < min_distance_squared + } + }); // Result is the indexes of the points that are 'close enough' to collide colliding_points - .map(|(point1, point2)| { - (point1.idx(), point2.idx()) - }) + .map(|(point1, point2)| (point1.idx(), point2.idx())) .collect::>() .into_iter() } /// /// Finds any points that have approximately the same coordinates and combines them - /// + /// /// Accuracy indicates the maximum difference in the x or y coordinate for two points to be considered the same. /// #[inline(never)] @@ -515,14 +589,14 @@ impl GraphPath { // Move any points that are connected by an edge and very close to each other on top of each other for point_idx in 0..self.points.len() { for edge_idx in 0..(self.points[point_idx].forward_edges.len()) { - let end_point_idx = self.points[point_idx].forward_edges[edge_idx].end_idx; + let end_point_idx = self.points[point_idx].forward_edges[edge_idx].end_idx; if end_point_idx == point_idx { // A point is always close to itself, so we don't want to try to move it in this case continue; } - let start_point = &self.points[point_idx].position; - let end_point = &self.points[end_point_idx].position; + let start_point = &self.points[point_idx].position; + let end_point = &self.points[end_point_idx].position; if start_point.is_near_to(end_point, accuracy) { self.points[end_point_idx].position = self.points[point_idx].position.clone(); @@ -535,32 +609,40 @@ impl GraphPath { if let Some(nearby_point) = nearby_points.next() { // Remap points according to whatever is nearest - let min_distance_squared = accuracy * accuracy; - let mut remapped_points = (0..self.points.len()) + let min_distance_squared = accuracy * accuracy; + let mut remapped_points = (0..self.points.len()) .into_iter() - .map(|idx| (idx, None)) // Target index (= point index if not remapped and new position, or None if unmoved) + .map(|idx| (idx, None)) // Target index (= point index if not remapped and new position, or None if unmoved) .collect::)>>(); - let mut nearby_point = nearby_point; + let mut nearby_point = nearby_point; loop { // Index is of two points that are close enough to overlap let (p1_orig_idx, p2_orig_idx) = nearby_point; - debug_assert!(p1_orig_idx != p2_orig_idx); // Guaranteed by the implementation of sweep_for_nearby_points() + debug_assert!(p1_orig_idx != p2_orig_idx); // Guaranteed by the implementation of sweep_for_nearby_points() // Point may be remapped - let (p1_idx, p1_pos) = &remapped_points[p1_orig_idx]; - let (p2_idx, p2_pos) = &remapped_points[p2_orig_idx]; + let (p1_idx, p1_pos) = &remapped_points[p1_orig_idx]; + let (p2_idx, p2_pos) = &remapped_points[p2_orig_idx]; // To prevent averaging a whole bunch of points down to the same point because they're all close together, we re-check the distance if the one of the two close points has already been remapped - let moved = p1_pos.is_some() || p2_pos.is_some(); + let moved = p1_pos.is_some() || p2_pos.is_some(); - let p1_pos = if let Some(pos) = p1_pos { pos.clone() } else { self.points[*p1_idx].position.clone() }; - let p2_pos = if let Some(pos) = p2_pos { pos.clone() } else { self.points[*p2_idx].position.clone() }; + let p1_pos = if let Some(pos) = p1_pos { + pos.clone() + } else { + self.points[*p1_idx].position.clone() + }; + let p2_pos = if let Some(pos) = p2_pos { + pos.clone() + } else { + self.points[*p2_idx].position.clone() + }; if !moved || Self::point_is_near(&p1_pos, &p2_pos, min_distance_squared) { // Remap both points to a common target position - let pos = Self::snap_points(&p1_pos, &p2_pos); - let remap_idx = usize::min(*p1_idx, *p2_idx); + let pos = Self::snap_points(&p1_pos, &p2_pos); + let remap_idx = usize::min(*p1_idx, *p2_idx); remapped_points[p1_orig_idx] = (remap_idx, Some(pos.clone())); remapped_points[p2_orig_idx] = (remap_idx, Some(pos)); @@ -583,13 +665,15 @@ impl GraphPath { self.points[original_idx].position = new_pos.clone(); // If this is the target point, then don't move any edges - if *new_idx == original_idx { continue; } + if *new_idx == original_idx { + continue; + } // Trace the new index to its final point (which is the point still mapped to itself: this should always exist because we always prefer the lowest point) let mut new_idx = *new_idx; loop { - let (next_idx, _) = &remapped_points[new_idx]; - let next_idx = *next_idx; + let (next_idx, _) = &remapped_points[new_idx]; + let next_idx = *next_idx; if next_idx == new_idx { break; @@ -600,13 +684,18 @@ impl GraphPath { } // Move the edges into the new index - let forward_edges = mem::take(&mut self.points[original_idx].forward_edges); - let connected_from = mem::take(&mut self.points[original_idx].connected_from); - - following_edge_idx_offset[original_idx] = self.points[new_idx].forward_edges.len(); - - self.points[new_idx].forward_edges.extend(forward_edges.into_iter()); - self.points[new_idx].connected_from.extend(connected_from.into_iter()); + let forward_edges = mem::take(&mut self.points[original_idx].forward_edges); + let connected_from = mem::take(&mut self.points[original_idx].connected_from); + + following_edge_idx_offset[original_idx] = + self.points[new_idx].forward_edges.len(); + + self.points[new_idx] + .forward_edges + .extend(forward_edges.into_iter()); + self.points[new_idx] + .connected_from + .extend(connected_from.into_iter()); } } @@ -617,10 +706,10 @@ impl GraphPath { let new_end_idx = remapped_points[edge.end_idx].0; if new_end_idx != edge.end_idx { - let following_edge_idx_offset = following_edge_idx_offset[edge.end_idx]; + let following_edge_idx_offset = following_edge_idx_offset[edge.end_idx]; - edge.end_idx = new_end_idx; - edge.following_edge_idx += following_edge_idx_offset; + edge.end_idx = new_end_idx; + edge.following_edge_idx += following_edge_idx_offset; } } @@ -631,7 +720,7 @@ impl GraphPath { if new_connected_from_idx != *connected_from_idx { *connected_from_idx = new_connected_from_idx; - remapped = true; + remapped = true; } } @@ -653,7 +742,7 @@ impl GraphPath { /// Checks that the following edges are consistent /// #[cfg(any(test, extra_checks))] - pub (crate) fn check_following_edge_consistency(&self) { + pub(crate) fn check_following_edge_consistency(&self) { let mut used_edges = vec![vec![]; self.points.len()]; for point_idx in 0..(self.points.len()) { @@ -663,7 +752,9 @@ impl GraphPath { let edge = &point.forward_edges[edge_idx]; test_assert!(edge.end_idx < self.points.len()); - test_assert!(edge.following_edge_idx < self.points[edge.end_idx].forward_edges.len()); + test_assert!( + edge.following_edge_idx < self.points[edge.end_idx].forward_edges.len() + ); test_assert!(!used_edges[edge.end_idx].contains(&edge.following_edge_idx)); used_edges[edge.end_idx].push(edge.following_edge_idx); @@ -672,38 +763,49 @@ impl GraphPath { } #[cfg(not(any(test, extra_checks)))] - pub (crate) fn check_following_edge_consistency(&self) { - - } + pub(crate) fn check_following_edge_consistency(&self) {} } /// -/// Removes any pairs of collisions that are closer than `CLOSE_DISTANCE` apart, and also rounds the +/// Removes any pairs of collisions that are closer than `CLOSE_DISTANCE` apart, and also rounds the /// first and last collisions to 0.0 and 1.0 -/// -/// When colliding two bezier curves we want to avoid subdividing excessively to produce very small +/// +/// When colliding two bezier curves we want to avoid subdividing excessively to produce very small /// sections as they have a tendency to produce extra collisions due to floating point or root finding /// errors. /// -fn remove_and_round_close_collisions(collisions: &mut SmallVec<[(f64, f64); 8]>, src: &C, tgt: &C) -where C::Point: Coordinate+Coordinate2D { +fn remove_and_round_close_collisions( + collisions: &mut SmallVec<[(f64, f64); 8]>, + src: &C, + tgt: &C, +) where + C::Point: Coordinate + Coordinate2D, +{ // Nothing to do if there are no collisions if collisions.len() == 0 { return; } // Work out the positions of each point - let mut positions = collisions.iter().map(|(t1, _t2)| src.point_at_pos(*t1)).collect::>(); + let mut positions = collisions + .iter() + .map(|(t1, _t2)| src.point_at_pos(*t1)) + .collect::>(); // Find any pairs of points that are too close together let mut collision_idx = 0; - while collision_idx+1 < collisions.len() { + while collision_idx + 1 < collisions.len() { // Just remove both of these if they are too close together (as each collision crosses the curve once, removing collisions in pairs means that there'll still be at least one collision left if the curves actually end up crossing over) - if positions[collision_idx].is_near_to(&positions[collision_idx+1], CLOSE_DISTANCE) { - if (collisions[collision_idx].0 - collisions[collision_idx+1].0).abs() < SMALL_T_DISTANCE - && (collisions[collision_idx].1 - collisions[collision_idx+1].1).abs() < SMALL_T_DISTANCE { - collisions.remove(collision_idx); positions.remove(collision_idx); - collisions.remove(collision_idx); positions.remove(collision_idx); + if positions[collision_idx].is_near_to(&positions[collision_idx + 1], CLOSE_DISTANCE) { + if (collisions[collision_idx].0 - collisions[collision_idx + 1].0).abs() + < SMALL_T_DISTANCE + && (collisions[collision_idx].1 - collisions[collision_idx + 1].1).abs() + < SMALL_T_DISTANCE + { + collisions.remove(collision_idx); + positions.remove(collision_idx); + collisions.remove(collision_idx); + positions.remove(collision_idx); } else { collision_idx += 1; } @@ -711,35 +813,44 @@ where C::Point: Coordinate+Coordinate2D { collision_idx += 1; } } - + // If the first point or the last point is close to the end of the source or target curve, clip to 0 or 1 if collisions.len() > 0 { // Get the start/end points of the source and target - let src_start = src.start_point(); - let src_end = src.end_point(); - let tgt_start = tgt.start_point(); - let tgt_end = tgt.end_point(); + let src_start = src.start_point(); + let src_end = src.end_point(); + let tgt_start = tgt.start_point(); + let tgt_end = tgt.end_point(); // Snap collisions to 0.0 or 1.0 if they're very close to the start or end of either curve for collision_idx in 0..collisions.len() { // Snap the source side if collisions[collision_idx].0 > 0.0 && collisions[collision_idx].0 < 1.0 { - if src_start.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) && collisions[collision_idx].0 < SMALL_T_DISTANCE { + if src_start.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) + && collisions[collision_idx].0 < SMALL_T_DISTANCE + { collisions[collision_idx].0 = 0.0; } - if src_end.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) && collisions[collision_idx].0 > 1.0-SMALL_T_DISTANCE { + if src_end.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) + && collisions[collision_idx].0 > 1.0 - SMALL_T_DISTANCE + { collisions[collision_idx].0 = 1.0; } } // Snap the target side - if collisions[collision_idx].1 > 0.0 && collisions[collision_idx].1 < 1.0 && collisions[collision_idx].1 < SMALL_T_DISTANCE { + if collisions[collision_idx].1 > 0.0 + && collisions[collision_idx].1 < 1.0 + && collisions[collision_idx].1 < SMALL_T_DISTANCE + { if tgt_start.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) { collisions[collision_idx].1 = 0.0; } - if tgt_end.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) && collisions[collision_idx].1 > 1.0-SMALL_T_DISTANCE { + if tgt_end.is_near_to(&positions[collision_idx], CLOSE_DISTANCE) + && collisions[collision_idx].1 > 1.0 - SMALL_T_DISTANCE + { collisions[collision_idx].1 = 1.0; } } diff --git a/src/bezier/path/graph_path/ray_collision.rs b/src/bezier/path/graph_path/ray_collision.rs index 760012f8..b58260d1 100644 --- a/src/bezier/path/graph_path/ray_collision.rs +++ b/src/bezier/path/graph_path/ray_collision.rs @@ -1,4 +1,4 @@ -use super::{GraphPath,GraphEdge,GraphEdgeRef}; +use super::{GraphEdge, GraphEdgeRef, GraphPath}; use crate::bezier::path::ray::*; use crate::geo::*; use crate::line::*; @@ -14,16 +14,19 @@ pub enum GraphRayCollision { SingleEdge(GraphEdgeRef), /// Collision against an intersection point - Intersection(GraphEdgeRef) + Intersection(GraphEdgeRef), } -impl GraphPath { +impl GraphPath { /// /// Finds all collisions between a ray and this path - /// + /// /// The return value is a tuple of (collision, curve_t, line_t, position) - /// - pub fn ray_collisions>(&self, ray: &L) -> Vec<(GraphRayCollision, f64, f64, Point)> { + /// + pub fn ray_collisions>( + &self, + ray: &L, + ) -> Vec<(GraphRayCollision, f64, f64, Point)> { ray_collisions(&self, ray) } } @@ -35,8 +38,8 @@ impl GraphRayCollision { #[inline] pub fn is_intersection(&self) -> bool { match self { - GraphRayCollision::SingleEdge(_) => false, - GraphRayCollision::Intersection(_edges) => true + GraphRayCollision::SingleEdge(_) => false, + GraphRayCollision::Intersection(_edges) => true, } } @@ -46,59 +49,95 @@ impl GraphRayCollision { #[inline] pub fn edge(&self) -> GraphEdgeRef { match self { - GraphRayCollision::SingleEdge(edge) => *edge, - GraphRayCollision::Intersection(edge) => *edge, + GraphRayCollision::SingleEdge(edge) => *edge, + GraphRayCollision::Intersection(edge) => *edge, } } } -impl<'a, Point, Label> RayPath for &'a GraphPath -where Point: Coordinate+Coordinate2D, - Label: Copy { +impl<'a, Point, Label> RayPath for &'a GraphPath +where + Point: Coordinate + Coordinate2D, + Label: Copy, +{ type Point = Point; type Curve = GraphEdge<'a, Point, Label>; - #[inline] fn num_points(&self) -> usize { self.points.len() } + #[inline] + fn num_points(&self) -> usize { + self.points.len() + } - #[inline] fn num_edges(&self, point_idx: usize) -> usize { self.points[point_idx].forward_edges.len() } + #[inline] + fn num_edges(&self, point_idx: usize) -> usize { + self.points[point_idx].forward_edges.len() + } - #[inline] fn edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { + #[inline] + fn edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { let num_edges = self.points[point_idx].forward_edges.len(); - (0..num_edges).into_iter() - .map(move |edge_idx| GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }) + (0..num_edges) + .into_iter() + .map(move |edge_idx| GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }) .collect() } - #[inline] fn reverse_edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { - self.points[point_idx].connected_from.iter() + #[inline] + fn reverse_edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { + self.points[point_idx] + .connected_from + .iter() .flat_map(|connected_point_idx| { let num_edges = self.points[*connected_point_idx].forward_edges.len(); - (0..num_edges).into_iter() - .filter(move |edge_idx| self.points[*connected_point_idx].forward_edges[*edge_idx].end_idx == point_idx) - .map(move |edge_idx| GraphEdgeRef { start_idx: *connected_point_idx, edge_idx: edge_idx, reverse: true }) + (0..num_edges) + .into_iter() + .filter(move |edge_idx| { + self.points[*connected_point_idx].forward_edges[*edge_idx].end_idx + == point_idx + }) + .map(move |edge_idx| GraphEdgeRef { + start_idx: *connected_point_idx, + edge_idx: edge_idx, + reverse: true, + }) }) .collect() } - #[inline] fn get_edge(&self, edge: GraphEdgeRef) -> Self::Curve { - GraphEdge { graph: *self, edge: edge } + #[inline] + fn get_edge(&self, edge: GraphEdgeRef) -> Self::Curve { + GraphEdge { + graph: *self, + edge: edge, + } } - #[inline] fn get_next_edge(&self, edge: GraphEdgeRef) -> (GraphEdgeRef, Self::Curve) { - let next_point_idx = self.edge_end_point_idx(edge); - let next_edge_idx = self.edge_following_edge_idx(edge); + #[inline] + fn get_next_edge(&self, edge: GraphEdgeRef) -> (GraphEdgeRef, Self::Curve) { + let next_point_idx = self.edge_end_point_idx(edge); + let next_edge_idx = self.edge_following_edge_idx(edge); - let next_edge_ref = GraphEdgeRef { start_idx: next_point_idx, edge_idx: next_edge_idx, reverse: edge.reverse }; + let next_edge_ref = GraphEdgeRef { + start_idx: next_point_idx, + edge_idx: next_edge_idx, + reverse: edge.reverse, + }; (next_edge_ref, self.get_edge(next_edge_ref)) } - #[inline] fn point_position(&self, point: usize) -> Self::Point { + #[inline] + fn point_position(&self, point: usize) -> Self::Point { self.points[point].position } - #[inline] fn edge_start_point_idx(&self, edge: GraphEdgeRef) -> usize { + #[inline] + fn edge_start_point_idx(&self, edge: GraphEdgeRef) -> usize { if edge.reverse { self.points[edge.start_idx].forward_edges[edge.edge_idx].end_idx } else { @@ -106,7 +145,8 @@ where Point: Coordinate+Coordinate2D, } } - #[inline] fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize { + #[inline] + fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize { if edge.reverse { edge.start_idx } else { @@ -114,9 +154,12 @@ where Point: Coordinate+Coordinate2D, } } - #[inline] fn edge_following_edge_idx(&self, edge: GraphEdgeRef) -> usize { + #[inline] + fn edge_following_edge_idx(&self, edge: GraphEdgeRef) -> usize { if edge.reverse { - unimplemented!("Finding the following edge for a reversed reference not implemented yet") + unimplemented!( + "Finding the following edge for a reversed reference not implemented yet" + ) } else { self.points[edge.start_idx].forward_edges[edge.edge_idx].following_edge_idx } diff --git a/src/bezier/path/graph_path/test.rs b/src/bezier/path/graph_path/test.rs index d7e19ac9..39534889 100644 --- a/src/bezier/path/graph_path/test.rs +++ b/src/bezier/path/graph_path/test.rs @@ -1,70 +1,208 @@ use super::*; -use crate::bezier::path::*; -use crate::bezier::normal::*; use crate::arc::*; +use crate::bezier::normal::*; +use crate::bezier::path::*; -pub (crate) fn donut() -> GraphPath { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); +pub(crate) fn donut() -> GraphPath { + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); - let mut circle1 = GraphPath::from_path(&circle1, ()); - circle1 = circle1.merge(GraphPath::from_path(&inner_circle1, ())); - let mut circle2 = GraphPath::from_path(&circle2, ()); - circle2 = circle2.merge(GraphPath::from_path(&inner_circle2, ())); + let mut circle1 = GraphPath::from_path(&circle1, ()); + circle1 = circle1.merge(GraphPath::from_path(&inner_circle1, ())); + let mut circle2 = GraphPath::from_path(&circle2, ()); + circle2 = circle2.merge(GraphPath::from_path(&inner_circle2, ())); circle1.collide(circle2, 0.1) } pub fn tricky_path1() -> SimpleBezierPath { BezierPathBuilder::::start(Coord2(266.4305, 634.9583)) - .curve_to((Coord2(267.89352, 634.96545), Coord2(276.2691, 647.3115)), Coord2(283.95255, 660.0379)) - .curve_to((Coord2(287.94046, 666.35474), Coord2(291.91766, 672.60645)), Coord2(295.15033, 677.43414)) - .curve_to((Coord2(296.7672, 679.91516), Coord2(298.1211, 681.9124)), Coord2(299.32123, 683.47577)) - .curve_to((Coord2(299.95978, 684.32623), Coord2(300.40076, 684.9176)), Coord2(300.98044, 685.51074)) - .curve_to((Coord2(301.33307, 685.8545), Coord2(301.51462, 686.0718)), Coord2(301.92783, 686.3648)) - .curve_to((Coord2(302.63144, 686.6535), Coord2(302.6845, 686.9835)), Coord2(303.79065, 687.13)) - .curve_to((Coord2(308.23322, 698.75146), Coord2(314.235, 706.79364)), Coord2(320.5527, 711.571)) - .curve_to((Coord2(323.84628, 713.9084), Coord2(326.7522, 715.38696)), Coord2(329.93036, 715.9504)) - .curve_to((Coord2(333.10065, 716.4182), Coord2(336.06982, 716.2095)), Coord2(338.80997, 715.17615)) - .curve_to((Coord2(344.1068, 713.1569), Coord2(348.558, 708.8886)), Coord2(352.09903, 704.2416)) - .curve_to((Coord2(355.6339, 699.64606), Coord2(358.63943, 694.3838)), Coord2(361.0284, 690.2511)) - .curve_to((Coord2(352.29608, 691.48425), Coord2(348.7531, 697.58563)), Coord2(344.9467, 702.02875)) - .curve_to((Coord2(343.1644, 704.2118), Coord2(340.9616, 706.1748)), Coord2(338.98895, 707.4077)) - .curve_to((Coord2(337.17404, 708.7338), Coord2(334.93362, 709.2896)), Coord2(332.94815, 709.3193)) - .curve_to((Coord2(338.20477, 716.0944), Coord2(342.99326, 713.658)), Coord2(346.69864, 710.2048)) - .curve_to((Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)) - .curve_to((Coord2(358.8071, 690.86554), Coord2(368.403, 680.78076)), Coord2(364.57346, 683.4333)) - .curve_to((Coord2(370.74402, 683.10126), Coord2(380.93408, 677.46747)), Coord2(391.3346, 669.7194)) - .curve_to((Coord2(401.82745, 661.6356), Coord2(411.92975, 652.304)), Coord2(416.44824, 642.7813)) - .curve_to((Coord2(421.56387, 630.7548), Coord2(419.29, 605.44073)), Coord2(418.97845, 598.63885)) - .curve_to((Coord2(416.0324, 600.9351), Coord2(416.06793, 605.21173)), Coord2(415.80798, 610.2456)) - .curve_to((Coord2(418.3617, 603.8127), Coord2(419.7235, 595.5345)), Coord2(417.99966, 597.9464)) - .curve_to((Coord2(417.83536, 597.29565), Coord2(417.6163, 596.428)), Coord2(417.452, 595.7772)) - .curve_to((Coord2(415.13226, 598.33954), Coord2(417.1024, 601.5625)), Coord2(415.80798, 610.2456)) - .curve_to((Coord2(419.39615, 605.133), Coord2(419.15756, 600.892)), Coord2(418.97845, 598.63885)) - .curve_to((Coord2(415.9, 605.6454), Coord2(416.15115, 630.697)), Coord2(410.98987, 640.1752)) - .curve_to((Coord2(407.398, 647.65436), Coord2(397.31293, 657.55756)), Coord2(387.45657, 664.45013)) - .curve_to((Coord2(377.50784, 671.67847), Coord2(367.18683, 676.76263)), Coord2(364.60056, 676.3969)) - .curve_to((Coord2(356.0477, 679.03125), Coord2(358.2825, 685.37573)), Coord2(350.3949, 694.47205)) - .curve_to((Coord2(347.86517, 698.46545), Coord2(345.09418, 702.3025)), Coord2(342.02982, 705.0691)) - .curve_to((Coord2(338.955, 707.7797), Coord2(336.14987, 709.45294)), Coord2(332.94815, 709.3193)) - .curve_to((Coord2(336.5865, 716.2577), Coord2(339.58755, 714.99677)), Coord2(342.64694, 713.29364)) - .curve_to((Coord2(345.54865, 711.4972), Coord2(347.85297, 709.2183)), Coord2(350.22574, 706.551)) - .curve_to((Coord2(354.72943, 701.2933), Coord2(358.0882, 695.26)), Coord2(361.0284, 690.2511)) - .curve_to((Coord2(352.55414, 690.95703), Coord2(349.8117, 695.7842)), Coord2(346.5798, 700.0057)) - .curve_to((Coord2(343.354, 704.1756), Coord2(340.01953, 707.4518)), Coord2(336.43625, 708.6749)) - .curve_to((Coord2(334.73633, 709.2627), Coord2(332.9918, 709.5996)), Coord2(331.1653, 709.1589)) - .curve_to((Coord2(329.34668, 708.8136), Coord2(326.97275, 707.9294)), Coord2(324.69394, 706.071)) - .curve_to((Coord2(319.86685, 702.45667), Coord2(313.55374, 694.77545)), Coord2(307.1513, 682.14154)) - .curve_to((Coord2(305.31448, 680.437), Coord2(305.08902, 680.6507)), Coord2(305.46603, 680.73413)) - .curve_to((Coord2(305.55258, 680.8219), Coord2(305.35938, 680.745)), Coord2(305.29236, 680.7117)) - .curve_to((Coord2(305.03268, 680.5507), Coord2(304.45453, 680.05615)), Coord2(303.91962, 679.53674)) - .curve_to((Coord2(302.7728, 678.36035), Coord2(301.16226, 676.48175)), Coord2(299.40033, 674.3327)) - .curve_to((Coord2(295.8753, 669.90015), Coord2(291.43716, 663.8746)), Coord2(286.9764, 657.9508)) - .curve_to((Coord2(277.76248, 646.196), Coord2(269.10742, 634.2079)), Coord2(266.40128, 634.45917)) - .curve_to((Coord2(266.42087, 634.7936), Coord2(266.41122, 634.6289)), Coord2(266.4305, 634.9583)) + .curve_to( + (Coord2(267.89352, 634.96545), Coord2(276.2691, 647.3115)), + Coord2(283.95255, 660.0379), + ) + .curve_to( + (Coord2(287.94046, 666.35474), Coord2(291.91766, 672.60645)), + Coord2(295.15033, 677.43414), + ) + .curve_to( + (Coord2(296.7672, 679.91516), Coord2(298.1211, 681.9124)), + Coord2(299.32123, 683.47577), + ) + .curve_to( + (Coord2(299.95978, 684.32623), Coord2(300.40076, 684.9176)), + Coord2(300.98044, 685.51074), + ) + .curve_to( + (Coord2(301.33307, 685.8545), Coord2(301.51462, 686.0718)), + Coord2(301.92783, 686.3648), + ) + .curve_to( + (Coord2(302.63144, 686.6535), Coord2(302.6845, 686.9835)), + Coord2(303.79065, 687.13), + ) + .curve_to( + (Coord2(308.23322, 698.75146), Coord2(314.235, 706.79364)), + Coord2(320.5527, 711.571), + ) + .curve_to( + (Coord2(323.84628, 713.9084), Coord2(326.7522, 715.38696)), + Coord2(329.93036, 715.9504), + ) + .curve_to( + (Coord2(333.10065, 716.4182), Coord2(336.06982, 716.2095)), + Coord2(338.80997, 715.17615), + ) + .curve_to( + (Coord2(344.1068, 713.1569), Coord2(348.558, 708.8886)), + Coord2(352.09903, 704.2416), + ) + .curve_to( + (Coord2(355.6339, 699.64606), Coord2(358.63943, 694.3838)), + Coord2(361.0284, 690.2511), + ) + .curve_to( + (Coord2(352.29608, 691.48425), Coord2(348.7531, 697.58563)), + Coord2(344.9467, 702.02875), + ) + .curve_to( + (Coord2(343.1644, 704.2118), Coord2(340.9616, 706.1748)), + Coord2(338.98895, 707.4077), + ) + .curve_to( + (Coord2(337.17404, 708.7338), Coord2(334.93362, 709.2896)), + Coord2(332.94815, 709.3193), + ) + .curve_to( + (Coord2(338.20477, 716.0944), Coord2(342.99326, 713.658)), + Coord2(346.69864, 710.2048), + ) + .curve_to( + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ) + .curve_to( + (Coord2(358.8071, 690.86554), Coord2(368.403, 680.78076)), + Coord2(364.57346, 683.4333), + ) + .curve_to( + (Coord2(370.74402, 683.10126), Coord2(380.93408, 677.46747)), + Coord2(391.3346, 669.7194), + ) + .curve_to( + (Coord2(401.82745, 661.6356), Coord2(411.92975, 652.304)), + Coord2(416.44824, 642.7813), + ) + .curve_to( + (Coord2(421.56387, 630.7548), Coord2(419.29, 605.44073)), + Coord2(418.97845, 598.63885), + ) + .curve_to( + (Coord2(416.0324, 600.9351), Coord2(416.06793, 605.21173)), + Coord2(415.80798, 610.2456), + ) + .curve_to( + (Coord2(418.3617, 603.8127), Coord2(419.7235, 595.5345)), + Coord2(417.99966, 597.9464), + ) + .curve_to( + (Coord2(417.83536, 597.29565), Coord2(417.6163, 596.428)), + Coord2(417.452, 595.7772), + ) + .curve_to( + (Coord2(415.13226, 598.33954), Coord2(417.1024, 601.5625)), + Coord2(415.80798, 610.2456), + ) + .curve_to( + (Coord2(419.39615, 605.133), Coord2(419.15756, 600.892)), + Coord2(418.97845, 598.63885), + ) + .curve_to( + (Coord2(415.9, 605.6454), Coord2(416.15115, 630.697)), + Coord2(410.98987, 640.1752), + ) + .curve_to( + (Coord2(407.398, 647.65436), Coord2(397.31293, 657.55756)), + Coord2(387.45657, 664.45013), + ) + .curve_to( + (Coord2(377.50784, 671.67847), Coord2(367.18683, 676.76263)), + Coord2(364.60056, 676.3969), + ) + .curve_to( + (Coord2(356.0477, 679.03125), Coord2(358.2825, 685.37573)), + Coord2(350.3949, 694.47205), + ) + .curve_to( + (Coord2(347.86517, 698.46545), Coord2(345.09418, 702.3025)), + Coord2(342.02982, 705.0691), + ) + .curve_to( + (Coord2(338.955, 707.7797), Coord2(336.14987, 709.45294)), + Coord2(332.94815, 709.3193), + ) + .curve_to( + (Coord2(336.5865, 716.2577), Coord2(339.58755, 714.99677)), + Coord2(342.64694, 713.29364), + ) + .curve_to( + (Coord2(345.54865, 711.4972), Coord2(347.85297, 709.2183)), + Coord2(350.22574, 706.551), + ) + .curve_to( + (Coord2(354.72943, 701.2933), Coord2(358.0882, 695.26)), + Coord2(361.0284, 690.2511), + ) + .curve_to( + (Coord2(352.55414, 690.95703), Coord2(349.8117, 695.7842)), + Coord2(346.5798, 700.0057), + ) + .curve_to( + (Coord2(343.354, 704.1756), Coord2(340.01953, 707.4518)), + Coord2(336.43625, 708.6749), + ) + .curve_to( + (Coord2(334.73633, 709.2627), Coord2(332.9918, 709.5996)), + Coord2(331.1653, 709.1589), + ) + .curve_to( + (Coord2(329.34668, 708.8136), Coord2(326.97275, 707.9294)), + Coord2(324.69394, 706.071), + ) + .curve_to( + (Coord2(319.86685, 702.45667), Coord2(313.55374, 694.77545)), + Coord2(307.1513, 682.14154), + ) + .curve_to( + (Coord2(305.31448, 680.437), Coord2(305.08902, 680.6507)), + Coord2(305.46603, 680.73413), + ) + .curve_to( + (Coord2(305.55258, 680.8219), Coord2(305.35938, 680.745)), + Coord2(305.29236, 680.7117), + ) + .curve_to( + (Coord2(305.03268, 680.5507), Coord2(304.45453, 680.05615)), + Coord2(303.91962, 679.53674), + ) + .curve_to( + (Coord2(302.7728, 678.36035), Coord2(301.16226, 676.48175)), + Coord2(299.40033, 674.3327), + ) + .curve_to( + (Coord2(295.8753, 669.90015), Coord2(291.43716, 663.8746)), + Coord2(286.9764, 657.9508), + ) + .curve_to( + (Coord2(277.76248, 646.196), Coord2(269.10742, 634.2079)), + Coord2(266.40128, 634.45917), + ) + .curve_to( + (Coord2(266.42087, 634.7936), Coord2(266.41122, 634.6289)), + Coord2(266.4305, 634.9583), + ) .build() } @@ -85,24 +223,20 @@ fn overlapping_rectangle() -> SimpleBezierPath { fn looped_rectangle() -> SimpleBezierPath { BezierPathBuilder::::start(Coord2(1.0, 1.0)) - //.line_to(Coord2(2.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 5.0)) .line_to(Coord2(2.0, 5.0)) .line_to(Coord2(2.0, 1.0)) .line_to(Coord2(3.0, 1.0)) - .line_to(Coord2(5.0, 1.0)) .line_to(Coord2(5.0, 5.0)) - //.line_to(Coord2(3.0, 5.0)) .line_to(Coord2(2.0, 5.0)) .line_to(Coord2(2.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 5.0)) .line_to(Coord2(2.0, 5.0)) - .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(1.0, 1.0)) .build() @@ -110,97 +244,104 @@ fn looped_rectangle() -> SimpleBezierPath { #[test] fn ray_cast_with_tricky_path_after_self_collide() { - let tricky = tricky_path1(); - let mut tricky = GraphPath::from_path(&tricky, ()); + let tricky = tricky_path1(); + let mut tricky = GraphPath::from_path(&tricky, ()); tricky.self_collide(0.01); for edge in tricky.all_edges() { - let target = edge.point_at_pos(0.5); - let normal = edge.normal_at_pos(0.5); - let ray = (target, target+normal); + let target = edge.point_at_pos(0.5); + let normal = edge.normal_at_pos(0.5); + let ray = (target, target + normal); let collisions = tricky.ray_collisions(&ray); // Should be an even number of collisions - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); } } #[test] fn single_difficult_ray_cast_with_tricky_path_before_self_collide() { - let tricky = tricky_path1(); - let tricky = GraphPath::from_path(&tricky, ()); + let tricky = tricky_path1(); + let tricky = GraphPath::from_path(&tricky, ()); - let ray = (Coord2(344.7127586558301, 702.311674360346), Coord2(344.6914625870749, 702.2935114955856)); - let collisions = tricky.ray_collisions(&ray); + let ray = ( + Coord2(344.7127586558301, 702.311674360346), + Coord2(344.6914625870749, 702.2935114955856), + ); + let collisions = tricky.ray_collisions(&ray); println!("{:?}", tricky); println!("{:?}", collisions); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); } #[test] fn single_difficult_ray_cast_with_tricky_path_after_self_collide() { - let tricky = tricky_path1(); - let mut tricky = GraphPath::from_path(&tricky, ()); + let tricky = tricky_path1(); + let mut tricky = GraphPath::from_path(&tricky, ()); tricky.self_collide(0.01); - let ray = (Coord2(344.7127586558301, 702.311674360346), Coord2(344.6914625870749, 702.2935114955856)); - let collisions = tricky.ray_collisions(&ray); + let ray = ( + Coord2(344.7127586558301, 702.311674360346), + Coord2(344.6914625870749, 702.2935114955856), + ); + let collisions = tricky.ray_collisions(&ray); println!("{:?}", tricky); println!("{:?}", collisions); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); } #[test] fn overlapping_rectangle_ray_cast_after_self_collide() { - let overlapping = overlapping_rectangle(); + let overlapping = overlapping_rectangle(); let mut overlapping = GraphPath::from_path(&overlapping, ()); overlapping.self_collide(0.01); - let ray = (Coord2(3.0, 0.0), Coord2(3.0, 5.0)); - let collisions = overlapping.ray_collisions(&ray); + let ray = (Coord2(3.0, 0.0), Coord2(3.0, 5.0)); + let collisions = overlapping.ray_collisions(&ray); println!("{:?}", overlapping); println!("{:?}", collisions); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); } #[test] fn looped_rectangle_ray_cast_after_self_collide() { - let looped = looped_rectangle(); + let looped = looped_rectangle(); let mut looped = GraphPath::from_path(&looped, ()); looped.self_collide(0.01); println!("{:?}", looped); for edge in looped.all_edges() { - let target = edge.point_at_pos(0.5); - let normal = edge.normal_at_pos(0.5); - let ray = (target, target+normal); + let target = edge.point_at_pos(0.5); + let normal = edge.normal_at_pos(0.5); + let ray = (target, target + normal); let collisions = looped.ray_collisions(&ray); // Should be an even number of collisions - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); } } #[test] fn find_gaps() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(5.0, 5.0)) .line_to(Coord2(5.0, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); - let edges = (0..4).into_iter() + let mut graph_path = GraphPath::from_path(&path, ()); + let edges = (0..4) + .into_iter() .map(|point_idx| graph_path.edges_for_point(point_idx).nth(0).unwrap().into()) .collect::>(); @@ -209,11 +350,23 @@ fn find_gaps() { graph_path.set_edge_kind(edges[3], GraphPathEdgeKind::Exterior); // Edge 0,0 is followed by a gap - assert!(graph_path.edge_has_gap(GraphEdgeRef { start_idx: 0, edge_idx: 0, reverse: false })); + assert!(graph_path.edge_has_gap(GraphEdgeRef { + start_idx: 0, + edge_idx: 0, + reverse: false + })); // Edge 1,0 is the gap - assert!(!graph_path.edge_has_gap(GraphEdgeRef { start_idx: 1, edge_idx: 0, reverse: false })); + assert!(!graph_path.edge_has_gap(GraphEdgeRef { + start_idx: 1, + edge_idx: 0, + reverse: false + })); // Edge 2,0 is preceded by the gap - assert!(graph_path.edge_has_gap(GraphEdgeRef { start_idx: 2, edge_idx: 0, reverse: true })); + assert!(graph_path.edge_has_gap(GraphEdgeRef { + start_idx: 2, + edge_idx: 0, + reverse: true + })); } diff --git a/src/bezier/path/intersection.rs b/src/bezier/path/intersection.rs index 529e82a8..e23d15a8 100644 --- a/src/bezier/path/intersection.rs +++ b/src/bezier/path/intersection.rs @@ -1,55 +1,79 @@ -use super::path::*; -use super::to_curves::*; -use super::super::curve::*; -use super::super::intersection::*; use super::super::super::geo::*; use super::super::super::line::*; +use super::super::curve::*; +use super::super::intersection::*; +use super::path::*; +use super::to_curves::*; /// /// Determines the intersections of a path and a line -/// +/// /// Intersections are returned as the path section index, the 't' parameter along that curve and the 't' value along the line: -/// ie: `(path_point_idx, curve_t, line_t)`. -/// -pub fn path_intersects_line<'a, Path: BezierPath, L: Line>(path: &'a Path, line: &'a L) -> impl 'a+Iterator -where Path::Point: 'a+Coordinate2D { +/// ie: `(path_point_idx, curve_t, line_t)`. +/// +pub fn path_intersects_line<'a, Path: BezierPath, L: Line>( + path: &'a Path, + line: &'a L, +) -> impl 'a + Iterator +where + Path::Point: 'a + Coordinate2D, +{ path_to_curves::<_, Curve<_>>(path) .enumerate() - .flat_map(move |(section_id, curve)| curve_intersects_line(&curve, line).into_iter().map(move |(t, s, _pos)| (section_id, t, s))) + .flat_map(move |(section_id, curve)| { + curve_intersects_line(&curve, line) + .into_iter() + .map(move |(t, s, _pos)| (section_id, t, s)) + }) } /// /// Determines the intersections of a path and a ray. -/// +/// /// Return value is `(path_point_idx, curve_t, line_t)`. Ray intersections differ from line intersections /// in that there's no requirement for the result to be within the bounds of the supplied line (so any match in the direction of the line is /// returned). -/// +/// /// It's possible to filter for matches that occur after the start of the line by looking for results with an `s` value >= 0 -/// -pub fn path_intersects_ray<'a, Path: BezierPath, L: Line>(path: &'a Path, line: &'a L) -> impl 'a+Iterator -where Path::Point: 'a+Coordinate2D { +/// +pub fn path_intersects_ray<'a, Path: BezierPath, L: Line>( + path: &'a Path, + line: &'a L, +) -> impl 'a + Iterator +where + Path::Point: 'a + Coordinate2D, +{ path_to_curves::<_, Curve<_>>(path) .enumerate() - .flat_map(move |(section_id, curve)| curve_intersects_line(&curve, line).into_iter().map(move |(t, s, _pos)| (section_id, t, s))) + .flat_map(move |(section_id, curve)| { + curve_intersects_line(&curve, line) + .into_iter() + .map(move |(t, s, _pos)| (section_id, t, s)) + }) } /// /// Finds the points where a path intersects another path -/// +/// /// Intersections are returned as (segment index, t-value), in pairs indicating the position on the first path /// and the position on the second path. Intersections are unordered by default. -/// +/// /// The accuracy value indicates the maximum errors that's permitted for an intersection: the bezier curve /// intersection algorithm is approximate. -/// -pub fn path_intersects_path<'a, Path: BezierPath>(path1: &'a Path, path2: &'a Path, accuracy: f64) -> Vec<((usize, f64), (usize, f64))> -where Path::Point: 'a+Coordinate2D { +/// +pub fn path_intersects_path<'a, Path: BezierPath>( + path1: &'a Path, + path2: &'a Path, + accuracy: f64, +) -> Vec<((usize, f64), (usize, f64))> +where + Path::Point: 'a + Coordinate2D, +{ // Convert both paths to sections: also compute the bounding boxes for quick rejection of sections with no intersections let path1_sections = path_to_curves::<_, Curve<_>>(path1) .enumerate() .map(|(section_id, curve)| (section_id, curve, curve.bounding_box::>())); - + let path2_sections = path_to_curves::<_, Curve<_>>(path2) .enumerate() .map(|(section_id, curve)| (section_id, curve, curve.bounding_box::>())) @@ -69,7 +93,11 @@ where Path::Point: 'a+Coordinate2D { let intersections = curve_intersects_curve_clip(&p1_curve, &p2_curve, accuracy); // Combine with the section IDs to generate the results - result.extend(intersections.into_iter().map(|(t1, t2)| ((p1_section_id, t1), (*p2_section_id, t2)) )); + result.extend( + intersections + .into_iter() + .map(|(t1, t2)| ((p1_section_id, t1), (*p2_section_id, t2))), + ); } } } diff --git a/src/bezier/path/is_clockwise.rs b/src/bezier/path/is_clockwise.rs index 9a8b3d9c..f5b1ff96 100644 --- a/src/bezier/path/is_clockwise.rs +++ b/src/bezier/path/is_clockwise.rs @@ -1,23 +1,28 @@ -use super::path::*; use super::super::super::geo::*; +use super::path::*; use itertools::*; /// /// Determines if a set of points are in a clockwise ordering (assuming that a positive y value indicates an upwards direction) /// -pub fn points_are_clockwise>(mut points: PointIter) -> bool { +pub fn points_are_clockwise>( + mut points: PointIter, +) -> bool { // Technique suggested in https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order let mut total = 0.0; // The first point needs to be repeated at the end of the sequence let first_point = points.next(); if let Some(first_point) = first_point { - let points = vec![first_point.clone()].into_iter().chain(points).chain(vec![first_point].into_iter()); + let points = vec![first_point.clone()] + .into_iter() + .chain(points) + .chain(vec![first_point].into_iter()); // Sum over the edges to determine if the points are clockwise for (start, end) in points.tuple_windows() { - total += (end.x()-start.x()) * (end.y()+start.y()); + total += (end.x() - start.x()) * (end.y() + start.y()); } } @@ -34,7 +39,10 @@ pub trait PathWithIsClockwise { fn is_clockwise(&self) -> bool; } -impl PathWithIsClockwise for P where P::Point: Coordinate+Coordinate2D { +impl PathWithIsClockwise for P +where + P::Point: Coordinate + Coordinate2D, +{ #[inline] fn is_clockwise(&self) -> bool { points_are_clockwise(self.points().map(|(_cp1, _cp2, p)| p)) diff --git a/src/bezier/path/mod.rs b/src/bezier/path/mod.rs index d2bcdac7..75e5acbf 100644 --- a/src/bezier/path/mod.rs +++ b/src/bezier/path/mod.rs @@ -1,38 +1,38 @@ //! //! # Manipulates multiple Bezier curves joined into a path -//! -//! The `BezierPath` trait provides a way to represent a bezier path. `flo_curves` considers a path to be a single -//! closed loop, unlike many libraries which allow for open paths and paths with subpaths. Instead, a path with +//! +//! The `BezierPath` trait provides a way to represent a bezier path. `flo_curves` considers a path to be a single +//! closed loop, unlike many libraries which allow for open paths and paths with subpaths. Instead, a path with //! multiple subpaths is represented as a collection - ie `Vec`. This reduces the number of edge cases //! the library has to deal with. -//! +//! //! The `path_add()`, `path_sub()` and `path_intersect()` functions can be used to perform path arithmetic: combining //! multiple paths into a single result. The `GraphPath` type is used to implement these functions: it can represent //! paths where points can have more than one following edge attached to them and provides functions for implementing //! similar operations. -//! +//! //! `BezierPathBuilder` provides a way to quickly build paths from any type implementing the factory trait without //! needing to generate all of the primitives manually. //! -mod path; -mod to_curves; -mod ray; -mod point; +pub mod algorithms; +mod arithmetic; mod bounds; -mod intersection; -mod path_builder; mod graph_path; +mod intersection; mod is_clockwise; -mod arithmetic; -pub mod algorithms; +mod path; +mod path_builder; +mod point; +mod ray; +mod to_curves; -pub use self::path::*; -pub use self::to_curves::*; -pub use self::point::*; +pub use self::arithmetic::*; pub use self::bounds::*; -pub use self::intersection::*; -pub use self::path_builder::*; pub use self::graph_path::*; +pub use self::intersection::*; pub use self::is_clockwise::*; -pub use self::arithmetic::*; +pub use self::path::*; +pub use self::path_builder::*; +pub use self::point::*; +pub use self::to_curves::*; diff --git a/src/bezier/path/path.rs b/src/bezier/path/path.rs index 49db2e02..80623491 100644 --- a/src/bezier/path/path.rs +++ b/src/bezier/path/path.rs @@ -1,62 +1,66 @@ +use super::super::super::geo::*; +use super::super::curve::*; use super::bounds::*; use super::to_curves::*; -use super::super::curve::*; -use super::super::super::geo::*; use itertools::*; -use std::vec; use std::iter; +use std::vec; /// /// Trait representing a path made out of bezier sections -/// -pub trait BezierPath : Geo+Clone+Sized { +/// +pub trait BezierPath: Geo + Clone + Sized { /// Type of an iterator over the points in this curve. This tuple contains the points ordered as a hull: ie, two control points followed by a point on the curve - type PointIter: Iterator; + type PointIter: Iterator; /// /// Retrieves the initial point of this path - /// + /// fn start_point(&self) -> Self::Point; /// /// Retrieves an iterator over the points in this path - /// + /// fn points(&self) -> Self::PointIter; /// /// Finds the bounds of this path - /// + /// #[inline] - fn bounding_box>(&self) -> Bounds { + fn bounding_box>(&self) -> Bounds { path_bounding_box(self) } /// /// Finds a loose bounding box for this path (more quickly than bounding_box) - /// + /// /// This will contain the path but might not be tightly aligned to the curves /// - fn fast_bounding_box>(&self) -> Bounds { + fn fast_bounding_box>(&self) -> Bounds { path_fast_bounding_box(self) } /// /// Changes this path into a set of bezier curves - /// + /// #[inline] - fn to_curves>(&self) -> Vec { + fn to_curves>(&self) -> Vec { path_to_curves(self).collect() } /// /// Creates a reversed version of this path /// - fn reversed>(&self) -> POut { + fn reversed>(&self) -> POut { // Add in the first point (control points don't matter) - let fake_first_point = (Self::Point::origin(), Self::Point::origin(), self.start_point()); - let points = self.points(); - let points = iter::once(fake_first_point).chain(points); + let fake_first_point = ( + Self::Point::origin(), + Self::Point::origin(), + self.start_point(), + ); + let points = self.points(); + let points = iter::once(fake_first_point).chain(points); // Reverse the direction of the path let reversed_points = points @@ -71,50 +75,56 @@ pub trait BezierPath : Geo+Clone+Sized { /// /// Trait implemented by types that can construct new bezier paths /// -pub trait BezierPathFactory : BezierPath { +pub trait BezierPathFactory: BezierPath { /// /// Creates a new instance of this path from a set of points - /// - fn from_points>(start_point: Self::Point, points: FromIter) -> Self; + /// + fn from_points>( + start_point: Self::Point, + points: FromIter, + ) -> Self; /// /// Creates a new instance of this path from the points in another path /// - fn from_path>(path: &FromPath) -> Self { + fn from_path>(path: &FromPath) -> Self { Self::from_points(path.start_point(), path.points()) } } -impl Geo for (Point, Vec<(Point, Point, Point)>) { +impl Geo for (Point, Vec<(Point, Point, Point)>) { type Point = Point; } /// /// The type (start_point, Vec<(Point, Point, Point)>) is the simplest bezier path type -/// -impl BezierPath for (Point, Vec<(Point, Point, Point)>) { - type PointIter = vec::IntoIter<(Point, Point, Point)>; +/// +impl BezierPath for (Point, Vec<(Point, Point, Point)>) { + type PointIter = vec::IntoIter<(Point, Point, Point)>; /// /// Retrieves the initial point of this path - /// + /// fn start_point(&self) -> Self::Point { self.0.clone() } /// /// Retrieves an iterator over the points in this path - /// + /// fn points(&self) -> Self::PointIter { self.1.clone().into_iter() } } -impl BezierPathFactory for (Point, Vec<(Point, Point, Point)>) { +impl BezierPathFactory for (Point, Vec<(Point, Point, Point)>) { /// /// Creates a new instance of this path from a set of points - /// - fn from_points>(start_point: Self::Point, points: FromIter) -> Self { + /// + fn from_points>( + start_point: Self::Point, + points: FromIter, + ) -> Self { (start_point, points.into_iter().collect()) } } diff --git a/src/bezier/path/path_builder.rs b/src/bezier/path/path_builder.rs index 62c0bb59..500952d4 100644 --- a/src/bezier/path/path_builder.rs +++ b/src/bezier/path/path_builder.rs @@ -2,47 +2,47 @@ use super::path::*; /// /// Used to build a bezier path -/// +/// pub struct BezierPathBuilder { /// Where the path starts start_point: P::Point, /// The points in the path - points: Vec<(P::Point, P::Point, P::Point)> + points: Vec<(P::Point, P::Point, P::Point)>, } impl BezierPathBuilder

{ /// /// Creates a new bezier path builder with the specified start point - /// + /// pub fn start(start: P::Point) -> BezierPathBuilder

{ BezierPathBuilder { - start_point: start, - points: vec![] + start_point: start, + points: vec![], } } /// /// Builds the path for this builder - /// + /// pub fn build(self) -> P { P::from_points(self.start_point, self.points) } /// /// Adds a line to the specified point - /// + /// pub fn line_to(mut self, point: P::Point) -> Self { // Get the vector from the last point to the new point let distance = if self.points.len() == 0 { point - self.start_point } else { - point - self.points[self.points.len()-1].2 + point - self.points[self.points.len() - 1].2 }; // A line puts control points at 33% and 66% of the distance - let cp1 = point - (distance*0.6666); - let cp2 = point - (distance*0.3333); + let cp1 = point - (distance * 0.6666); + let cp2 = point - (distance * 0.3333); self.points.push((cp1, cp2, point)); @@ -51,10 +51,10 @@ impl BezierPathBuilder

{ /// /// Adds a curve to a particular point - /// + /// pub fn curve_to(mut self, (cp1, cp2): (P::Point, P::Point), end_point: P::Point) -> Self { self.points.push((cp1, cp2, end_point)); self } -} \ No newline at end of file +} diff --git a/src/bezier/path/point.rs b/src/bezier/path/point.rs index 584b2e69..33522c06 100644 --- a/src/bezier/path/point.rs +++ b/src/bezier/path/point.rs @@ -1,10 +1,10 @@ -use super::ray::*; -use super::path::*; -use super::to_curves::*; -use super::graph_path::*; +use super::super::super::geo::*; use super::super::curve::*; use super::super::normal::*; -use super::super::super::geo::*; +use super::graph_path::*; +use super::path::*; +use super::ray::*; +use super::to_curves::*; use smallvec::*; @@ -12,37 +12,37 @@ use smallvec::*; /// Represents a curve that can be represented either forwards or backwards /// #[derive(Clone)] -pub (crate) enum ReversableCurve { +pub(crate) enum ReversableCurve { Forward(Curve), - Reversed(Curve) + Reversed(Curve), } impl Geo for ReversableCurve { - type Point=Curve::Point; + type Point = Curve::Point; } impl BezierCurve for ReversableCurve { #[inline] - fn start_point(&self) -> Curve::Point { + fn start_point(&self) -> Curve::Point { match self { - ReversableCurve::Forward(curve) => curve.start_point(), - ReversableCurve::Reversed(curve) => curve.end_point() + ReversableCurve::Forward(curve) => curve.start_point(), + ReversableCurve::Reversed(curve) => curve.end_point(), } } #[inline] - fn end_point(&self) -> Curve::Point { + fn end_point(&self) -> Curve::Point { match self { - ReversableCurve::Forward(curve) => curve.end_point(), - ReversableCurve::Reversed(curve) => curve.start_point() + ReversableCurve::Forward(curve) => curve.end_point(), + ReversableCurve::Reversed(curve) => curve.start_point(), } } #[inline] fn control_points(&self) -> (Curve::Point, Curve::Point) { match self { - ReversableCurve::Forward(curve) => curve.control_points(), - ReversableCurve::Reversed(curve) => { + ReversableCurve::Forward(curve) => curve.control_points(), + ReversableCurve::Reversed(curve) => { let (cp1, cp2) = curve.control_points(); (cp2, cp1) } @@ -50,32 +50,51 @@ impl BezierCurve for ReversableCurve { } } -impl RayPath for Vec -where Curve::Point: Coordinate2D { +impl RayPath for Vec +where + Curve::Point: Coordinate2D, +{ type Curve = ReversableCurve; type Point = Curve::Point; - #[inline] fn num_points(&self) -> usize { + #[inline] + fn num_points(&self) -> usize { self.len() } - #[inline] fn num_edges(&self, _point_idx: usize) -> usize { + #[inline] + fn num_edges(&self, _point_idx: usize) -> usize { 1 } - #[inline] fn reverse_edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { + #[inline] + fn reverse_edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { if point_idx == 0 { - smallvec![GraphEdgeRef { start_idx: self.len()-1, edge_idx: 0, reverse: true }] + smallvec![GraphEdgeRef { + start_idx: self.len() - 1, + edge_idx: 0, + reverse: true + }] } else { - smallvec![GraphEdgeRef { start_idx: point_idx-1, edge_idx: 0, reverse: true }] + smallvec![GraphEdgeRef { + start_idx: point_idx - 1, + edge_idx: 0, + reverse: true + }] } } - #[inline] fn edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { - smallvec![GraphEdgeRef { start_idx: point_idx, edge_idx: 0, reverse: false }] + #[inline] + fn edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]> { + smallvec![GraphEdgeRef { + start_idx: point_idx, + edge_idx: 0, + reverse: false + }] } - #[inline] fn get_edge(&self, edge: GraphEdgeRef) -> Self::Curve { + #[inline] + fn get_edge(&self, edge: GraphEdgeRef) -> Self::Curve { if edge.reverse { ReversableCurve::Reversed(self[edge.start_idx].clone()) } else { @@ -83,16 +102,23 @@ where Curve::Point: Coordinate2D { } } - #[inline] fn get_next_edge(&self, edge: GraphEdgeRef) -> (GraphEdgeRef, Self::Curve) { - let next_ref = GraphEdgeRef { start_idx: self.edge_end_point_idx(edge), edge_idx: 0, reverse: edge.reverse }; + #[inline] + fn get_next_edge(&self, edge: GraphEdgeRef) -> (GraphEdgeRef, Self::Curve) { + let next_ref = GraphEdgeRef { + start_idx: self.edge_end_point_idx(edge), + edge_idx: 0, + reverse: edge.reverse, + }; (next_ref, self.get_edge(next_ref)) } - #[inline] fn point_position(&self, point: usize) -> Self::Point { + #[inline] + fn point_position(&self, point: usize) -> Self::Point { self[point].start_point() } - #[inline] fn edge_start_point_idx(&self, edge: GraphEdgeRef) -> usize { + #[inline] + fn edge_start_point_idx(&self, edge: GraphEdgeRef) -> usize { if edge.reverse { unimplemented!() } else { @@ -100,56 +126,69 @@ where Curve::Point: Coordinate2D { } } - #[inline] fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize { + #[inline] + fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize { if edge.reverse { unimplemented!() } else { - if edge.start_idx+1 == self.len() { + if edge.start_idx + 1 == self.len() { 0 } else { - edge.start_idx+1 + edge.start_idx + 1 } } } - #[inline] fn edge_following_edge_idx(&self, _edge: GraphEdgeRef) -> usize { + #[inline] + fn edge_following_edge_idx(&self, _edge: GraphEdgeRef) -> usize { 0 } } /// /// Returns true if a particular point is within a bezier path -/// +/// pub fn path_contains_point(path: &P, point: &P::Point) -> bool -where P::Point: Coordinate2D { +where + P::Point: Coordinate2D, +{ // We want to cast a ray from the outer edge of the bounds to our point let (min_bounds, max_bounds) = path.bounding_box(); - if min_bounds.x() > point.x() || max_bounds.x() < point.x() || min_bounds.y() > point.y() || max_bounds.y() < point.y() { + if min_bounds.x() > point.x() + || max_bounds.x() < point.x() + || min_bounds.y() > point.y() + || max_bounds.y() < point.y() + { // Point is outside the bounds of the path false } else { // Ray is from the top of the bounds to our point - let ray = (max_bounds + P::Point::from_components(&[0.01, 0.01]), *point); - let ray_direction = ray.1 - ray.0; + let ray = ( + max_bounds + P::Point::from_components(&[0.01, 0.01]), + *point, + ); + let ray_direction = ray.1 - ray.0; // Call through to ray_collisions to get the collisions - let curves = path_to_curves::<_, Curve<_>>(path).collect::>(); - let collisions = ray_collisions(&curves, &ray); + let curves = path_to_curves::<_, Curve<_>>(path).collect::>(); + let collisions = ray_collisions(&curves, &ray); // The total of all of the ray directions - let mut total_direction = 0; + let mut total_direction = 0; for (collision, curve_t, line_t, _pos) in collisions { // Stop once the ray reaches the desired point - if line_t > 1.0 { break; } + if line_t > 1.0 { + break; + } // Curve this collision was is just the start index of the edge - let curve_idx = collision.edge().start_idx; + let curve_idx = collision.edge().start_idx; // Use the normal at this point to determine the direction relative to the ray - let normal = curves[curve_idx].normal_at_pos(curve_t); - let direction = ray_direction.dot(&normal).signum() as i32; + let normal = curves[curve_idx].normal_at_pos(curve_t); + let direction = ray_direction.dot(&normal).signum() as i32; // Add to the total direction total_direction += direction; diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index 95598727..3d4fcfa8 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -1,10 +1,10 @@ -use super::graph_path::*; -use super::super::curve::*; -use super::super::normal::*; -use super::super::intersection::*; +use super::super::super::consts::*; use super::super::super::geo::*; use super::super::super::line::*; -use super::super::super::consts::*; +use super::super::curve::*; +use super::super::intersection::*; +use super::super::normal::*; +use super::graph_path::*; use smallvec::*; use std::cmp::Ordering; @@ -12,9 +12,9 @@ use std::cmp::Ordering; /// /// Represents a path that can be accessed by the ray collision algorithm /// -pub (crate) trait RayPath { - type Point: Coordinate+Coordinate2D; - type Curve: BezierCurve; +pub(crate) trait RayPath { + type Point: Coordinate + Coordinate2D; + type Curve: BezierCurve; /// /// Returns the number of points in this RayPath @@ -62,7 +62,7 @@ pub (crate) trait RayPath { fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize; /// - /// Retrieves the index of the edge following the specified edge + /// Retrieves the index of the edge following the specified edge /// (the edge start from the end point index that continues the path the edge is a part of) /// fn edge_following_edge_idx(&self, edge: GraphEdgeRef) -> usize; @@ -73,17 +73,20 @@ pub (crate) trait RayPath { /// #[inline] fn curve_is_collinear(edge: &Edge, (a, b, c): (f64, f64, f64)) -> bool -where Edge::Point: Coordinate+Coordinate2D { +where + Edge::Point: Coordinate + Coordinate2D, +{ // Fetch the points of the curve let start_point = edge.start_point(); - let end_point = edge.end_point(); - let (cp1, cp2) = edge.control_points(); + let end_point = edge.end_point(); + let (cp1, cp2) = edge.control_points(); // The curve is collinear if all of the points lie on the ray - if (start_point.x()*a + start_point.y()*b + c).abs() < SMALL_DISTANCE - && (end_point.x()*a + end_point.y()*b + c).abs() < SMALL_DISTANCE - && (cp1.x()*a + cp1.y()*b + c).abs() < SMALL_DISTANCE - && (cp2.x()*a + cp2.y()*b + c).abs() < SMALL_DISTANCE { + if (start_point.x() * a + start_point.y() * b + c).abs() < SMALL_DISTANCE + && (end_point.x() * a + end_point.y() * b + c).abs() < SMALL_DISTANCE + && (cp1.x() * a + cp1.y() * b + c).abs() < SMALL_DISTANCE + && (cp2.x() * a + cp2.y() * b + c).abs() < SMALL_DISTANCE + { true } else { false @@ -94,29 +97,38 @@ where Edge::Point: Coordinate+Coordinate2D { enum RayCanIntersect { WrongSide, Collinear, - CrossesRay + CrossesRay, } /// /// Given the coefficients of a ray, returns whether or not an edge can intersect it /// fn ray_can_intersect(edge: &Edge, (a, b, c): (f64, f64, f64)) -> RayCanIntersect -where Edge::Point: Coordinate+Coordinate2D { +where + Edge::Point: Coordinate + Coordinate2D, +{ // Fetch the points of the curve let start_point = edge.start_point(); - let end_point = edge.end_point(); - let (cp1, cp2) = edge.control_points(); + let end_point = edge.end_point(); + let (cp1, cp2) = edge.control_points(); // Calculate distances to each of the points - let start_distance = a*start_point.x() + b*start_point.y() + c; - let cp1_distance = a*cp1.x() + b*cp1.y() + c; - let cp2_distance = a*cp2.x() + b*cp2.y() + c; - let end_distance = a*end_point.x()+ b*end_point.y() + c; + let start_distance = a * start_point.x() + b * start_point.y() + c; + let cp1_distance = a * cp1.x() + b * cp1.y() + c; + let cp2_distance = a * cp2.x() + b * cp2.y() + c; + let end_distance = a * end_point.x() + b * end_point.y() + c; // The sign of the distances indicate which side they're on - let side = start_distance.signum() + end_distance.signum() + cp1_distance.signum() + cp2_distance.signum(); - - if start_distance.abs() < SMALL_DISTANCE && end_distance.abs() < SMALL_DISTANCE && cp1_distance.abs() < SMALL_DISTANCE && cp2_distance.abs() < SMALL_DISTANCE { + let side = start_distance.signum() + + end_distance.signum() + + cp1_distance.signum() + + cp2_distance.signum(); + + if start_distance.abs() < SMALL_DISTANCE + && end_distance.abs() < SMALL_DISTANCE + && cp1_distance.abs() < SMALL_DISTANCE + && cp2_distance.abs() < SMALL_DISTANCE + { // If all the distances are small enough, this section is collinear RayCanIntersect::Collinear } else if side < -3.99 || side > 3.99 { @@ -131,14 +143,18 @@ where Edge::Point: Coordinate+Coordinate2D { /// /// Given a list of points, returns the edges that cross the line given by the specified set of coefficients /// -fn crossing_edges(path: &Path, (a, b, c): (f64, f64, f64), points: Vec) -> Vec { +fn crossing_edges( + path: &Path, + (a, b, c): (f64, f64, f64), + points: Vec, +) -> Vec { let mut crossing_edges = vec![]; for point_idx in points.into_iter() { for incoming_ref in path.reverse_edges_for_point(point_idx) { // Get the incoming edge going in the right direction - let incoming_ref = incoming_ref.reversed(); - let incoming = path.get_edge(incoming_ref); + let incoming_ref = incoming_ref.reversed(); + let incoming = path.get_edge(incoming_ref); // Ignore collinear incoming edges if curve_is_collinear(&incoming, (a, b, c)) { @@ -146,16 +162,20 @@ fn crossing_edges(path: &Path, (a, b, c): (f64, f64, f64), points } // Fetch the leaving edge for the incoming edge - let following_ref = path.edge_following_edge_idx(incoming_ref); - let mut leaving_ref = GraphEdgeRef { start_idx: point_idx, edge_idx: following_ref, reverse: false }; - let mut leaving = path.get_edge(leaving_ref); + let following_ref = path.edge_following_edge_idx(incoming_ref); + let mut leaving_ref = GraphEdgeRef { + start_idx: point_idx, + edge_idx: following_ref, + reverse: false, + }; + let mut leaving = path.get_edge(leaving_ref); // Follow the path until we complete a loop or find a leaving edge that's not collinear while curve_is_collinear(&leaving, (a, b, c)) { let (next_ref, next_edge) = path.get_next_edge(leaving_ref); leaving_ref = next_ref; - leaving = next_edge; + leaving = next_edge; if path.edge_start_point_idx(leaving_ref) == point_idx { // Found a loop that was entirely collinear @@ -166,11 +186,11 @@ fn crossing_edges(path: &Path, (a, b, c): (f64, f64, f64), points // If it's not colinear, add to the set of crossing edges if !curve_is_collinear(&leaving, (a, b, c)) { - let incoming_cp2 = incoming.control_points().1; - let leaving_cp1 = leaving.control_points().0; + let incoming_cp2 = incoming.control_points().1; + let leaving_cp1 = leaving.control_points().0; - let incoming_side = a*incoming_cp2.x() + b*incoming_cp2.y() + c; - let leaving_side = a*leaving_cp1.x() + b*leaving_cp1.y() + c; + let incoming_side = a * incoming_cp2.x() + b * incoming_cp2.y() + c; + let leaving_side = a * leaving_cp1.x() + b * leaving_cp1.y() + c; if incoming_side.signum() != leaving_side.signum() { // Control points are on different sides of the line, so this is a crossing edge @@ -185,27 +205,39 @@ fn crossing_edges(path: &Path, (a, b, c): (f64, f64, f64), points /// /// Performs a basic search for collisions, returning them grouped into two sets. -/// +/// /// The first set is crossing collisions. These are places where the ray met and edge at an angle and crossed it. /// The second set is collinear collisions. These occur on straight edges that follow the same path as the ray. /// #[inline(never)] -fn crossing_and_collinear_collisions(path: &Path, ray: &L) -> (SmallVec<[(GraphEdgeRef, f64, f64, Path::Point); 32]>, SmallVec<[(GraphEdgeRef, f64, f64, Path::Point); 8]>) -where Path::Point: Coordinate+Coordinate2D, - L: Line { - let mut raw_collisions = smallvec![]; +fn crossing_and_collinear_collisions( + path: &Path, + ray: &L, +) -> ( + SmallVec<[(GraphEdgeRef, f64, f64, Path::Point); 32]>, + SmallVec<[(GraphEdgeRef, f64, f64, Path::Point); 8]>, +) +where + Path::Point: Coordinate + Coordinate2D, + L: Line, +{ + let mut raw_collisions = smallvec![]; // If there are multiple collinear sections grouped together, these give them each a common identifier - let mut section_with_point: Option>> = None; - let mut collinear_sections: SmallVec<[Vec<_>; 8]> = smallvec![]; + let mut section_with_point: Option>> = None; + let mut collinear_sections: SmallVec<[Vec<_>; 8]> = smallvec![]; // The coefficients are used to determine if a particular edge can collide with the curve and if it's collinear or not let ray_coeffs = ray.coefficients(); for point_idx in 0..(path.num_points()) { for edge_idx in 0..(path.num_edges(point_idx)) { - let edge_ref = GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false }; - let edge = path.get_edge(edge_ref); + let edge_ref = GraphEdgeRef { + start_idx: point_idx, + edge_idx: edge_idx, + reverse: false, + }; + let edge = path.get_edge(edge_ref); let intersection_type = ray_can_intersect(&edge, ray_coeffs); @@ -220,11 +252,12 @@ where Path::Point: Coordinate+Coordinate2D, RayCanIntersect::Collinear => { // There are usually no collinear collisions, so only allocate our array if we find some - let section_with_point = section_with_point.get_or_insert_with(|| vec![None; path.num_points()]); + let section_with_point = + section_with_point.get_or_insert_with(|| vec![None; path.num_points()]); // This edge is collinear with the ray - let start_idx = path.edge_start_point_idx(edge_ref); - let end_idx = path.edge_end_point_idx(edge_ref); + let start_idx = path.edge_start_point_idx(edge_ref); + let end_idx = path.edge_end_point_idx(edge_ref); if let Some(start_section) = section_with_point[start_idx] { if let Some(_end_section) = section_with_point[end_idx] { @@ -240,12 +273,12 @@ where Path::Point: Coordinate+Coordinate2D, // New section let new_section = collinear_sections.len(); collinear_sections.push(vec![start_idx, end_idx]); - section_with_point[start_idx] = Some(new_section); - section_with_point[end_idx] = Some(new_section); + section_with_point[start_idx] = Some(new_section); + section_with_point[end_idx] = Some(new_section); } } - RayCanIntersect::WrongSide => { + RayCanIntersect::WrongSide => { // Ray does not intersect the curve } } @@ -255,15 +288,17 @@ where Path::Point: Coordinate+Coordinate2D, // Collect any collinear collisions into a vec let collinear_collisions = collinear_sections .into_iter() - .flat_map(move |colinear_edge_points| crossing_edges(path, ray_coeffs, colinear_edge_points) + .flat_map(move |colinear_edge_points| { + crossing_edges(path, ray_coeffs, colinear_edge_points) .into_iter() .map(move |crossing_edge| { - let point = path.edge_start_point_idx(crossing_edge); - let point = path.point_position(point); - let line_t = ray.pos_for_point(&point); + let point = path.edge_start_point_idx(crossing_edge); + let point = path.point_position(point); + let line_t = ray.pos_for_point(&point); (crossing_edge, 0.0, line_t, point) - })) + }) + }) .collect(); (raw_collisions, collinear_collisions) @@ -273,29 +308,53 @@ where Path::Point: Coordinate+Coordinate2D, /// Given a list of collisions, removes any that are at the end just before a collinear section /// #[inline] -fn remove_collisions_before_or_after_collinear_section<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator>(path: &'a Path, ray: &L, collisions: Collisions) -> impl 'a+Iterator -where Path::Point: Coordinate+Coordinate2D, - L: Line { +fn remove_collisions_before_or_after_collinear_section< + 'a, + Path: RayPath, + L: Line, + Collisions: 'a + IntoIterator, +>( + path: &'a Path, + ray: &L, + collisions: Collisions, +) -> impl 'a + Iterator +where + Path::Point: Coordinate + Coordinate2D, + L: Line, +{ let ray_coeffs = ray.coefficients(); - collisions.into_iter() + collisions + .into_iter() .filter(move |(collision, curve_t, _line_t, position)| { if *curve_t > 0.9 { - let end_point_idx = path.edge_end_point_idx(*collision); - let end_point = path.point_position(end_point_idx); + let end_point_idx = path.edge_end_point_idx(*collision); + let end_point = path.point_position(end_point_idx); // If any following edge is collinear, remove this collision - if position.is_near_to(&end_point, CLOSE_DISTANCE) && path.edges_for_point(end_point_idx).into_iter().map(|edge| path.get_edge(edge)).any(|next| curve_is_collinear(&next, ray_coeffs)) { + if position.is_near_to(&end_point, CLOSE_DISTANCE) + && path + .edges_for_point(end_point_idx) + .into_iter() + .map(|edge| path.get_edge(edge)) + .any(|next| curve_is_collinear(&next, ray_coeffs)) + { false } else { true } } else if *curve_t < 0.1 { let start_point_idx = path.edge_start_point_idx(*collision); - let start_point = path.point_position(start_point_idx); + let start_point = path.point_position(start_point_idx); // If any preceding edge is collinear, remove this collision - if position.is_near_to(&start_point, CLOSE_DISTANCE) && path.reverse_edges_for_point(start_point_idx).into_iter().map(|edge| path.get_edge(edge)).any(|previous| curve_is_collinear(&previous, ray_coeffs)) { + if position.is_near_to(&start_point, CLOSE_DISTANCE) + && path + .reverse_edges_for_point(start_point_idx) + .into_iter() + .map(|edge| path.get_edge(edge)) + .any(|previous| curve_is_collinear(&previous, ray_coeffs)) + { // Collisions crossing collinear sections are taken care of during the collinear collision phase false } else { @@ -310,28 +369,40 @@ where Path::Point: Coordinate+Coordinate2D, /// /// Given a list of collisions, finds any that are on a collinear line and moves them to the end of the collinear section -/// +/// /// Collinear edges have the property that a ray collides with them on all points. We treat these as a collision at the start /// of the following section, as this is the point where the line could enter or leave a shape. /// #[inline] -fn move_collinear_collisions_to_end<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator>(path: &'a Path, ray: &L, collisions: Collisions) -> impl 'a+Iterator -where Path::Point: Coordinate+Coordinate2D, - L: Line { +fn move_collinear_collisions_to_end< + 'a, + Path: RayPath, + L: Line, + Collisions: 'a + IntoIterator, +>( + path: &'a Path, + ray: &L, + collisions: Collisions, +) -> impl 'a + Iterator +where + Path::Point: Coordinate + Coordinate2D, + L: Line, +{ let ray_coeffs = ray.coefficients(); - collisions.into_iter() + collisions + .into_iter() .map(move |(collision, curve_t, line_t, position)| { let edge = path.get_edge(collision); if curve_is_collinear(&edge, ray_coeffs) { - let mut edge_ref = collision; + let mut edge_ref = collision; let mut edge; // Skip over collinear sections (they have 0 width from the point of view of the ray) loop { let (next_edge_ref, next_edge) = path.get_next_edge(edge_ref); - edge_ref = next_edge_ref; - edge = next_edge; + edge_ref = next_edge_ref; + edge = next_edge; if !curve_is_collinear(&edge, ray_coeffs) { break; } @@ -349,7 +420,12 @@ where Path::Point: Coordinate+Coordinate2D, /// Returns true if the collision is at the start of the specified edge /// #[inline] -fn collision_is_at_start(path: &Path, edge: &GraphEdgeRef, curve_t: f64, position: &Path::Point) -> bool { +fn collision_is_at_start( + path: &Path, + edge: &GraphEdgeRef, + curve_t: f64, + position: &Path::Point, +) -> bool { if curve_t > 0.1 { false } else { @@ -362,35 +438,53 @@ fn collision_is_at_start(path: &Path, edge: &GraphEdgeRef, curve_ /// Returns true if the collision is at the end of the specified edge /// #[inline] -fn collision_is_at_end(path: &Path, edge: &GraphEdgeRef, curve_t: f64, position: &Path::Point) -> bool { +fn collision_is_at_end( + path: &Path, + edge: &GraphEdgeRef, + curve_t: f64, + position: &Path::Point, +) -> bool { if curve_t < 0.9 { false } else { - let next_point_idx = path.edge_end_point_idx(*edge); - let end_point = path.point_position(next_point_idx); + let next_point_idx = path.edge_end_point_idx(*edge); + let end_point = path.point_position(next_point_idx); end_point.is_near_to(position, SMALL_DISTANCE) } } /// -/// Returns true if the +/// Returns true if the /// #[inline] -fn edges_are_glancing(path: &Path, ray: (f64, f64, f64), previous_edge: &GraphEdgeRef, following_edge: &GraphEdgeRef) -> bool { +fn edges_are_glancing( + path: &Path, + ray: (f64, f64, f64), + previous_edge: &GraphEdgeRef, + following_edge: &GraphEdgeRef, +) -> bool { // Fetch the actual edges and take the ray apart - let following_edge = path.get_edge(*following_edge); - let previous_edge = path.get_edge(*previous_edge); - let (a, b, c) = ray; + let following_edge = path.get_edge(*following_edge); + let previous_edge = path.get_edge(*previous_edge); + let (a, b, c) = ray; // A glancing collision has control points on the same side of the ray - let cp_in = previous_edge.control_points().1; - let cp_out = following_edge.control_points().0; + let cp_in = previous_edge.control_points().1; + let cp_out = following_edge.control_points().0; - let side_in = cp_in.x()*a + cp_in.y()*b + c; - let side_out = cp_out.x()*a + cp_out.y()*b + c; + let side_in = cp_in.x() * a + cp_in.y() * b + c; + let side_out = cp_out.x() * a + cp_out.y() * b + c; - let side_in = if side_in.abs() < 0.001 { 0.0 } else { side_in.signum() }; - let side_out = if side_out.abs() < 0.001 { 0.0 } else { side_out.signum() }; + let side_in = if side_in.abs() < 0.001 { + 0.0 + } else { + side_in.signum() + }; + let side_out = if side_out.abs() < 0.001 { + 0.0 + } else { + side_out.signum() + }; // A glancing collision has both edges on the same side of the ray side_in == side_out @@ -398,46 +492,61 @@ fn edges_are_glancing(path: &Path, ray: (f64, f64, f64), previous /// /// Removes extra collisions found near vertices -/// -/// When a ray crosses at a vertex it will generate a collision at the end of one edge and the beginning +/// +/// When a ray crosses at a vertex it will generate a collision at the end of one edge and the beginning /// of another. If this occurs, we should remove one of those collisions. As we use numerical methods to /// solve for the collision point, it's possible to see only the 'end' or the 'start' collision. -/// -/// It's possible for a ray to hit a vertex and not actually enter the shape. These are 'glancing' -/// collisions. A glancing collision is one that generates exactly one collision at a corner without +/// +/// It's possible for a ray to hit a vertex and not actually enter the shape. These are 'glancing' +/// collisions. A glancing collision is one that generates exactly one collision at a corner without /// actually crossing into the shape (or which happens to hit a curve exactly on a tangent). -/// -/// Corner collisions are found by looking for collisions at the start of an edge (we assume that the +/// +/// Corner collisions are found by looking for collisions at the start of an edge (we assume that the /// filtering in `move_collisions_at_end_to_beginning` has been applied) and checking if the following edge /// crosses the array. If it does not, it's probably a glancing collision. -/// +/// /// Tangent collisions are found just by looking at the tangent of the curve at the point of collision: if /// it's collinear with the ray, then the ray presumably does not cross the edge. -/// +/// /// As we use numerical methods to find line/curve collisions, it's possible for errors to result in a ray /// that hits a corner and the other edge, so we finish up by filtering for this condition. -/// +/// /// We need to filter these both in the same place as the choice of filter depends on whether or not a /// particular collision is a glancing collision or a crossing collision. /// -fn filter_collisions_near_vertices<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator>(path: &'a Path, ray: &'a L, collisions: Collisions) -> impl 'a+Iterator -where L: Line { - let (a, b, c) = ray.coefficients(); - let mut visited_start = None; +fn filter_collisions_near_vertices< + 'a, + Path: RayPath, + L: Line, + Collisions: 'a + IntoIterator, +>( + path: &'a Path, + ray: &'a L, + collisions: Collisions, +) -> impl 'a + Iterator +where + L: Line, +{ + let (a, b, c) = ray.coefficients(); + let mut visited_start = None; - collisions.into_iter() + collisions + .into_iter() .filter_map(move |(edge, curve_t, line_t, position)| { // This only applies to collisions at the end or start of an edge let is_at_start = collision_is_at_start(path, &edge, curve_t, &position); - let is_at_end = !is_at_start && collision_is_at_end(path, &edge, curve_t, &position); + let is_at_end = !is_at_start && collision_is_at_end(path, &edge, curve_t, &position); if is_at_start || is_at_end { // Collision might be crossing or glancing: get the two edges on the collision let (preceding_edge, following_edge) = if is_at_start { - let previous_edge = path.reverse_edges_for_point(edge.start_idx) + let previous_edge = path + .reverse_edges_for_point(edge.start_idx) .into_iter() .map(|previous_edge| previous_edge.reversed()) - .filter(|previous_edge| path.edge_following_edge_idx(*previous_edge) == edge.edge_idx) + .filter(|previous_edge| { + path.edge_following_edge_idx(*previous_edge) == edge.edge_idx + }) .nth(0) .expect("Previous edge for a collision at start"); @@ -449,21 +558,24 @@ where L: Line { }; if edges_are_glancing(path, (a, b, c), &preceding_edge, &following_edge) { - // Ray hits close to a vertex between two edges that both face away from it (ie, may be a glancing collision) // There must also be a glancing collision on the 'other' edge (we can afford this expensive check as glancing collisions are rare) let both_glancing = if is_at_start { // Must be a point close to the end of the preceding edge too - let edge = path.get_edge(preceding_edge); - let collisions = curve_intersects_ray(&edge, ray); + let edge = path.get_edge(preceding_edge); + let collisions = curve_intersects_ray(&edge, ray); - collisions.into_iter().any(|(curve_t, _line_t, position)| collision_is_at_end(path, &preceding_edge, curve_t, &position)) + collisions.into_iter().any(|(curve_t, _line_t, position)| { + collision_is_at_end(path, &preceding_edge, curve_t, &position) + }) } else { // Must be a point close to the start of the following edge too - let edge = path.get_edge(following_edge); - let collisions = curve_intersects_ray(&edge, ray); + let edge = path.get_edge(following_edge); + let collisions = curve_intersects_ray(&edge, ray); - collisions.into_iter().any(|(curve_t, _line_t, position)| collision_is_at_start(path, &following_edge, curve_t, &position)) + collisions.into_iter().any(|(curve_t, _line_t, position)| { + collision_is_at_start(path, &following_edge, curve_t, &position) + }) }; if both_glancing { @@ -473,20 +585,23 @@ where L: Line { // Ray only gets close to the end of one of the edges: must be a crossing collision Some((edge, curve_t, line_t, position)) } - } else { - // Ray crosses exactly on the vertex: report it exactly once (as the beginning of the curve) - let visited_start = visited_start.get_or_insert_with(|| vec![None; path.num_points()]); + let visited_start = + visited_start.get_or_insert_with(|| vec![None; path.num_points()]); // At the start of the curve let was_visited = visited_start[following_edge.start_idx] .as_ref() - .map(|collisions: &SmallVec<[_; 2]>| collisions.contains(&following_edge.edge_idx)) + .map(|collisions: &SmallVec<[_; 2]>| { + collisions.contains(&following_edge.edge_idx) + }) .unwrap_or(false); if !was_visited { - visited_start[following_edge.start_idx].get_or_insert_with(|| smallvec![]).push(following_edge.edge_idx); + visited_start[following_edge.start_idx] + .get_or_insert_with(|| smallvec![]) + .push(following_edge.edge_idx); } if !was_visited { @@ -494,7 +609,6 @@ where L: Line { } else { None } - } } else { // In the middle: not glancing or crossing @@ -507,20 +621,32 @@ where L: Line { /// Removes any collision that manages to hit an edge exactly on a tangent /// #[inline] -fn remove_tangent_collisions<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator>(path: &'a Path, ray: &'a L, collisions: Collisions) -> impl 'a+Iterator -where L: Line { - let ray_vector = (ray.point_at_pos(1.0) - ray.point_at_pos(0.0)).to_unit_vector(); +fn remove_tangent_collisions< + 'a, + Path: RayPath, + L: Line, + Collisions: 'a + IntoIterator, +>( + path: &'a Path, + ray: &'a L, + collisions: Collisions, +) -> impl 'a + Iterator +where + L: Line, +{ + let ray_vector = (ray.point_at_pos(1.0) - ray.point_at_pos(0.0)).to_unit_vector(); - collisions.into_iter() + collisions + .into_iter() .filter(move |(edge, curve_t, _line_t, _position)| { // Check if we've hit a tangent - let edge = path.get_edge(*edge); + let edge = path.get_edge(*edge); // Get the curve tangent at this position - let tangent = edge.tangent_at_pos(*curve_t).to_unit_vector(); + let tangent = edge.tangent_at_pos(*curve_t).to_unit_vector(); // Test if it's going the same way as the ray - let dot_product = ray_vector.dot(&tangent); + let dot_product = ray_vector.dot(&tangent); let dot_product_mag = dot_product.abs() - 1.0; // Dot product of two unit vectors will be 1.0 or -1.0 for a tangent collision @@ -536,8 +662,17 @@ where L: Line { /// Finds any collision that occurred too close to an intersection and flags it as such /// #[inline] -fn flag_collisions_at_intersections<'a, Path: RayPath, Collisions: 'a+IntoIterator>(path: &'a Path, collisions: Collisions) -> impl 'a+Iterator -where Path::Point: Coordinate+Coordinate2D { +fn flag_collisions_at_intersections< + 'a, + Path: RayPath, + Collisions: 'a + IntoIterator, +>( + path: &'a Path, + collisions: Collisions, +) -> impl 'a + Iterator +where + Path::Point: Coordinate + Coordinate2D, +{ collisions .into_iter() .map(move |(collision, curve_t, line_t, position)| { @@ -545,32 +680,53 @@ where Path::Point: Coordinate+Coordinate2D { // Might be at an intersection (close to the start of the curve) if path.num_edges(collision.start_idx) > 1 { // Intersection - (GraphRayCollision::Intersection(collision), curve_t, line_t, position) + ( + GraphRayCollision::Intersection(collision), + curve_t, + line_t, + position, + ) } else { // Edge with only a single following point - (GraphRayCollision::SingleEdge(collision), curve_t, line_t, position) + ( + GraphRayCollision::SingleEdge(collision), + curve_t, + line_t, + position, + ) } } else { // Not at an intersection - (GraphRayCollision::SingleEdge(collision), curve_t, line_t, position) + ( + GraphRayCollision::SingleEdge(collision), + curve_t, + line_t, + position, + ) } }) } /// /// Finds all collisions between a ray and this path -/// -pub (crate) fn ray_collisions(path: &Path, ray: &L) -> Vec<(GraphRayCollision, f64, f64, Path::Point)> -where Path::Point: Coordinate+Coordinate2D, - L: Line { - let (p1, p2) = ray.points(); - let ray_direction = p2 - p1; +/// +pub(crate) fn ray_collisions( + path: &Path, + ray: &L, +) -> Vec<(GraphRayCollision, f64, f64, Path::Point)> +where + Path::Point: Coordinate + Coordinate2D, + L: Line, +{ + let (p1, p2) = ray.points(); + let ray_direction = p2 - p1; // Raw collisions let (crossing_collisions, collinear_collisions) = crossing_and_collinear_collisions(path, ray); - let collinear_collisions = collinear_collisions.into_iter(); - let crossing_collisions = crossing_collisions.into_iter(); - let crossing_collisions = remove_collisions_before_or_after_collinear_section(path, ray, crossing_collisions); + let collinear_collisions = collinear_collisions.into_iter(); + let crossing_collisions = crossing_collisions.into_iter(); + let crossing_collisions = + remove_collisions_before_or_after_collinear_section(path, ray, crossing_collisions); // Chain them together let collisions = collinear_collisions.chain(crossing_collisions); @@ -584,65 +740,77 @@ where Path::Point: Coordinate+Coordinate2D, // Convert to a vec and sort by ray position let mut collisions = collisions.collect::>(); - collisions.sort_by(|(edge_a, curve_t_a, line_t_a, pos_a), (edge_b, curve_t_b, line_t_b, pos_b)| { - // If the collision occurs at the same point on the line (within SMALL_DISTANCE), we need to order by edge priority. Otherwise, order by where collisions occur along the ray - let dx = pos_a.x() - pos_b.x(); - let dy = pos_a.y() - pos_b.y(); - - if dx.abs() > SMALL_DISTANCE || dy.abs() > SMALL_DISTANCE { - // Order by position on the ray - line_t_a.partial_cmp(line_t_b).unwrap_or(Ordering::Equal) - } else { - // Position on the line is the same (stabilise ordering by checking the edges) - let edge_a = edge_a.edge(); - let edge_b = edge_b.edge(); - - let result = edge_a.start_idx.cmp(&edge_b.start_idx); - if result != Ordering::Equal { - // Different start points - result - } else { - // Check if these are the same edge or not - let edge_order = edge_a.edge_idx.cmp(&edge_b.edge_idx); - - // Ordering is reversed depending on the direction of the edge relative to the line - // To produce a consistent ordering, we rely on edges from newer paths being added later in the list (having a higher edge_idx) - // The ordering here is used with `set_edge_kinds_by_ray_casting()` to generate consistent results when paths overlap - // TODO: it would be better to use label ordering here to get a more consistent result. This relies on the order that edges are added to the list - let (earlier_edge, edge_t) = match edge_order { - Ordering::Greater => (edge_b, *curve_t_b), - Ordering::Less => (edge_a, *curve_t_a), - Ordering::Equal => { return Ordering::Equal; } - }; - let earlier_edge = path.get_edge(earlier_edge); - let earlier_normal = earlier_edge.normal_at_pos(edge_t); - let earlier_direction = ray_direction.dot(&earlier_normal); + collisions.sort_by( + |(edge_a, curve_t_a, line_t_a, pos_a), (edge_b, curve_t_b, line_t_b, pos_b)| { + // If the collision occurs at the same point on the line (within SMALL_DISTANCE), we need to order by edge priority. Otherwise, order by where collisions occur along the ray + let dx = pos_a.x() - pos_b.x(); + let dy = pos_a.y() - pos_b.y(); - if earlier_direction < 0.0 { - edge_order.reverse() + if dx.abs() > SMALL_DISTANCE || dy.abs() > SMALL_DISTANCE { + // Order by position on the ray + line_t_a.partial_cmp(line_t_b).unwrap_or(Ordering::Equal) + } else { + // Position on the line is the same (stabilise ordering by checking the edges) + let edge_a = edge_a.edge(); + let edge_b = edge_b.edge(); + + let result = edge_a.start_idx.cmp(&edge_b.start_idx); + if result != Ordering::Equal { + // Different start points + result } else { - edge_order + // Check if these are the same edge or not + let edge_order = edge_a.edge_idx.cmp(&edge_b.edge_idx); + + // Ordering is reversed depending on the direction of the edge relative to the line + // To produce a consistent ordering, we rely on edges from newer paths being added later in the list (having a higher edge_idx) + // The ordering here is used with `set_edge_kinds_by_ray_casting()` to generate consistent results when paths overlap + // TODO: it would be better to use label ordering here to get a more consistent result. This relies on the order that edges are added to the list + let (earlier_edge, edge_t) = match edge_order { + Ordering::Greater => (edge_b, *curve_t_b), + Ordering::Less => (edge_a, *curve_t_a), + Ordering::Equal => { + return Ordering::Equal; + } + }; + let earlier_edge = path.get_edge(earlier_edge); + let earlier_normal = earlier_edge.normal_at_pos(edge_t); + let earlier_direction = ray_direction.dot(&earlier_normal); + + if earlier_direction < 0.0 { + edge_order.reverse() + } else { + edge_order + } } } - } - }); + }, + ); collisions } #[cfg(test)] mod test { - use super::*; + use super::super::graph_path::test::*; use super::super::path::*; use super::super::path_builder::*; - use super::super::graph_path::test::*; + use super::*; #[test] fn raw_donut_collisions() { let donut = donut(); let donut = &donut; - let raw_collisions = crossing_and_collinear_collisions(&donut, &(Coord2(7.000584357101389, 8.342524209216537), Coord2(6.941479643691172, 8.441210096108172))).0.into_iter(); + let raw_collisions = crossing_and_collinear_collisions( + &donut, + &( + Coord2(7.000584357101389, 8.342524209216537), + Coord2(6.941479643691172, 8.441210096108172), + ), + ) + .0 + .into_iter(); println!("{:?}", raw_collisions.collect::>()); // assert!(false); @@ -662,7 +830,8 @@ mod test { let gp = GraphPath::from_path(&rectangle1, ()); let gp = &gp; - let collisions = crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))).1; + let collisions = + crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))).1; assert!(collisions.len() == 0); } @@ -680,8 +849,15 @@ mod test { let gp = GraphPath::from_path(&rectangle1, ()); let gp = &gp; - let collisions = crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))).0.into_iter(); - let collisions = remove_collisions_before_or_after_collinear_section(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0)), collisions); + let collisions = + crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))) + .0 + .into_iter(); + let collisions = remove_collisions_before_or_after_collinear_section( + &gp, + &(Coord2(5.0, 0.0), Coord2(5.0, 5.0)), + collisions, + ); let collisions = collisions.collect::>(); assert!(collisions.len() == 0); @@ -699,8 +875,8 @@ mod test { .build(); // Collide along the vertical seam of this graph - let gp = GraphPath::from_path(&concave_shape, ()); - let gp = &gp; + let gp = GraphPath::from_path(&concave_shape, ()); + let gp = &gp; let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0)); let collisions = crossing_and_collinear_collisions(&gp, &ray).1; @@ -720,12 +896,16 @@ mod test { .build(); // Collide along the vertical seam of this graph - let gp = GraphPath::from_path(&concave_shape, ()); - let gp = &gp; + let gp = GraphPath::from_path(&concave_shape, ()); + let gp = &gp; let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0)); let collisions = crossing_and_collinear_collisions(&gp, &ray).0.into_iter(); - let collisions = remove_collisions_before_or_after_collinear_section(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0)), collisions); + let collisions = remove_collisions_before_or_after_collinear_section( + &gp, + &(Coord2(5.0, 0.0), Coord2(5.0, 5.0)), + collisions, + ); let collisions = collisions.collect::>(); assert!(collisions.len() == 1); @@ -743,23 +923,30 @@ mod test { .build(); // Collide along the vertical seam of this graph - let gp = GraphPath::from_path(&concave_shape, ()); - let gp = &gp; + let gp = GraphPath::from_path(&concave_shape, ()); + let gp = &gp; let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0)); // Raw collisions - let (normal_collisions, collinear_collisions) = crossing_and_collinear_collisions(&gp, &ray); - let normal_collisions = remove_collisions_before_or_after_collinear_section(&gp, &ray, normal_collisions).collect::>(); + let (normal_collisions, collinear_collisions) = + crossing_and_collinear_collisions(&gp, &ray); + let normal_collisions = + remove_collisions_before_or_after_collinear_section(&gp, &ray, normal_collisions) + .collect::>(); assert!(collinear_collisions.len() == 1); assert!(normal_collisions.len() == 1); // Chain them together - let collisions = collinear_collisions.into_iter().chain(normal_collisions.into_iter()).collect::>(); + let collisions = collinear_collisions + .into_iter() + .chain(normal_collisions.into_iter()) + .collect::>(); assert!(collisions.len() == 2); // Filter for accuracy - let collisions = move_collinear_collisions_to_end(&gp, &ray, collisions).collect::>(); + let collisions = + move_collinear_collisions_to_end(&gp, &ray, collisions).collect::>(); assert!(collisions.len() == 2); let collisions = filter_collisions_near_vertices(&gp, &ray, collisions).collect::>(); assert!(collisions.len() == 2); @@ -780,10 +967,10 @@ mod test { let mut with_interior_point = GraphPath::from_path(&with_interior_point, ()); with_interior_point.self_collide(0.01); - let with_interior_point = &with_interior_point; + let with_interior_point = &with_interior_point; - let ray = (Coord2(0.0, 3.0), Coord2(1.0, 3.0)); - let collisions = crossing_and_collinear_collisions(&with_interior_point, &ray).0; + let ray = (Coord2(0.0, 3.0), Coord2(1.0, 3.0)); + let collisions = crossing_and_collinear_collisions(&with_interior_point, &ray).0; println!("{:?}", with_interior_point); println!("{:?}", collisions); @@ -791,12 +978,15 @@ mod test { assert!(collisions.len() == 4); // Filter for accuracy - let collisions = move_collinear_collisions_to_end(&with_interior_point, &ray, collisions).collect::>(); + let collisions = move_collinear_collisions_to_end(&with_interior_point, &ray, collisions) + .collect::>(); assert!(collisions.len() == 4); - let collisions = filter_collisions_near_vertices(&with_interior_point, &ray, collisions).collect::>(); + let collisions = filter_collisions_near_vertices(&with_interior_point, &ray, collisions) + .collect::>(); println!("{:?}", collisions); assert!(collisions.len() == 4); - let collisions = flag_collisions_at_intersections(&with_interior_point, collisions).collect::>(); + let collisions = + flag_collisions_at_intersections(&with_interior_point, collisions).collect::>(); assert!(collisions.len() == 4); } } diff --git a/src/bezier/path/to_curves.rs b/src/bezier/path/to_curves.rs index e10fabbe..f9ffcfde 100644 --- a/src/bezier/path/to_curves.rs +++ b/src/bezier/path/to_curves.rs @@ -1,18 +1,21 @@ -use super::path::*; use super::super::curve::*; +use super::path::*; use itertools::*; /// /// Converts a path to a series of bezier curves -/// -pub fn path_to_curves>(path: &Path) -> impl Iterator { - let just_start_point = vec![(path.start_point(), path.start_point(), path.start_point())].into_iter(); - let points = path.points(); +/// +pub fn path_to_curves>( + path: &Path, +) -> impl Iterator { + let just_start_point = + vec![(path.start_point(), path.start_point(), path.start_point())].into_iter(); + let points = path.points(); - just_start_point.chain(points) - .tuple_windows() - .map(|((_, _, start_point), (cp1, cp2, end_point))| { + just_start_point.chain(points).tuple_windows().map( + |((_, _, start_point), (cp1, cp2, end_point))| { Curve::from_points(start_point, (cp1, cp2), end_point) - }) + }, + ) } diff --git a/src/bezier/search.rs b/src/bezier/search.rs index 2fa7f25d..96a37ab4 100644 --- a/src/bezier/search.rs +++ b/src/bezier/search.rs @@ -1,32 +1,41 @@ +use super::super::geo::*; use super::bounds::*; use super::subdivide::*; -use super::super::geo::*; /// /// Performs a subdivision search on a curve for a point matching a function -/// +/// /// This searches for a point using a matching function that determines whether or not the point /// is within a particular bounding box. The return value is a list of t values for the curve /// described by the w values where the bounding box was shrunk to the size specified by min_size. -/// +/// /// A limitation of this algorithm is that if the target point lies very close to a subdivision point, /// it may produce multiple matches (as it will find a nearby point on either side of the subdivision) -/// -pub fn search_bounds4(min_size: f64, w1: Point, w2: Point, w3: Point, w4: Point, match_fn: MatchFn) -> Vec -where Point: Coordinate, - MatchFn: Fn(Point, Point) -> bool { +/// +pub fn search_bounds4( + min_size: f64, + w1: Point, + w2: Point, + w3: Point, + w4: Point, + match_fn: MatchFn, +) -> Vec +where + Point: Coordinate, + MatchFn: Fn(Point, Point) -> bool, +{ // Helper function to determine if a bounding box is below the minimum size - let min_size_squared = min_size * min_size; - let is_valid_match = |p1: Point, p2: Point| { - let diff = p1-p2; - let size_squared = diff.dot(&diff); + let min_size_squared = min_size * min_size; + let is_valid_match = |p1: Point, p2: Point| { + let diff = p1 - p2; + let size_squared = diff.dot(&diff); size_squared <= min_size_squared }; // Push the initial curve as one to check let mut pending = vec![]; - let mut result = vec![]; + let mut result = vec![]; // Each point is the list of w values and the min/max t values remaining to search pending.push((w1, w2, w3, w4, 0.0, 1.0)); @@ -34,9 +43,9 @@ where Point: Coordinate, // Iterate while there are still curve sections to search while let Some((w1, w2, w3, w4, min_t, max_t)) = pending.pop() { // Subdivide at the midpoint - let midpoint = (min_t + max_t)/2.0; + let midpoint = (min_t + max_t) / 2.0; let ((aw1, aw2, aw3, aw4), (bw1, bw2, bw3, bw4)) = subdivide4(0.5, w1, w2, w3, w4); - + // Compute the bounds of either side let (amin, amax) = bounding_box4(aw1, aw2, aw3, aw4); let (bmin, bmax) = bounding_box4(bw1, bw2, bw3, bw4); @@ -45,7 +54,7 @@ where Point: Coordinate, if match_fn(amin, amax) { if is_valid_match(amin, amax) { // Bounds are small enough this counts as a match: push the midpoint - result.push((min_t+midpoint)/2.0); + result.push((min_t + midpoint) / 2.0); } else { // Continue processing this half of the curve pending.push((aw1, aw2, aw3, aw4, min_t, midpoint)); @@ -56,7 +65,7 @@ where Point: Coordinate, if match_fn(bmin, bmax) { if is_valid_match(bmin, bmax) { // Bounds are small enough this counts as a match: push the midpoint - result.push((midpoint+max_t)/2.0); + result.push((midpoint + max_t) / 2.0); } else { // Continue processing this half of the curve pending.push((bw1, bw2, bw3, bw4, midpoint, max_t)); diff --git a/src/bezier/section.rs b/src/bezier/section.rs index e91c78d0..2007cdb0 100644 --- a/src/bezier/section.rs +++ b/src/bezier/section.rs @@ -1,15 +1,15 @@ -use super::curve::*; -use super::basis::*; -use super::super::geo::*; use super::super::consts::*; +use super::super::geo::*; +use super::basis::*; +use super::curve::*; use std::cell::*; /// /// Represents a subsection of a bezier curve -/// +/// #[derive(Clone)] -pub struct CurveSection<'a, C: 'a+BezierCurve> { +pub struct CurveSection<'a, C: 'a + BezierCurve> { /// Full curve curve: &'a C, @@ -20,31 +20,31 @@ pub struct CurveSection<'a, C: 'a+BezierCurve> { t_m: f64, /// Cached version of the control points for this curve section - cached_control_points: RefCell> + cached_control_points: RefCell>, } -impl<'a, C: 'a+BezierCurve> CurveSection<'a, C> { +impl<'a, C: 'a + BezierCurve> CurveSection<'a, C> { /// /// Creates a new curve section from a region of another bezier curve - /// + /// pub fn new(curve: &'a C, t_min: f64, t_max: f64) -> CurveSection<'a, C> { let t_c = t_min; let t_m = t_max - t_c; CurveSection { - curve: curve, - t_m: t_m, - t_c: t_c, - cached_control_points: RefCell::new(None) + curve: curve, + t_m: t_m, + t_c: t_c, + cached_control_points: RefCell::new(None), } } /// /// Returns the t value on the full curve for a t value on the section - /// + /// #[inline] pub fn t_for_t(&self, t: f64) -> f64 { - t*self.t_m + self.t_c + t * self.t_m + self.t_c } /// @@ -55,46 +55,46 @@ impl<'a, C: 'a+BezierCurve> CurveSection<'a, C> { let t_min = self.t_c; let t_max = self.t_for_t(1.0); - (t_max-t_min).abs() < SMALL_DISTANCE + (t_max - t_min).abs() < SMALL_DISTANCE } /// /// Creates a sub-section from this curve section (dividing it further) - /// + /// /// Unlike calling `section`, this keeps the same type and avoids the need /// for recursive recalculation for things like the control points. This means /// that `original_curve_t_values` will return the coordinates for the same /// original curve as the curve that this subsection was created from. - /// + /// pub fn subsection(&self, t_min: f64, t_max: f64) -> CurveSection<'a, C> { CurveSection::new(self.curve, self.t_for_t(t_min), self.t_for_t(t_max)) } /// /// Returns the original t values (t_min, t_max) that this section was created from - /// + /// #[inline] pub fn original_curve_t_values(&self) -> (f64, f64) { - (self.t_c, self.t_m+self.t_c) + (self.t_c, self.t_m + self.t_c) } /// /// Given a 't' value on the original curve, returns the equivalent value on this section - /// + /// #[inline] pub fn section_t_for_original_t(&self, t: f64) -> f64 { - (t-self.t_c)/self.t_m + (t - self.t_c) / self.t_m } } -impl<'a, C: 'a+BezierCurve> Geo for CurveSection<'a, C> { +impl<'a, C: 'a + BezierCurve> Geo for CurveSection<'a, C> { type Point = C::Point; } -impl<'a, C: 'a+BezierCurve> BezierCurve for CurveSection<'a, C> { +impl<'a, C: 'a + BezierCurve> BezierCurve for CurveSection<'a, C> { /// /// The start point of this curve - /// + /// #[inline] fn start_point(&self) -> Self::Point { self.curve.point_at_pos(self.t_for_t(0.0)) @@ -102,7 +102,7 @@ impl<'a, C: 'a+BezierCurve> BezierCurve for CurveSection<'a, C> { /// /// The end point of this curve - /// + /// #[inline] fn end_point(&self) -> Self::Point { self.curve.point_at_pos(self.t_for_t(1.0)) @@ -110,42 +110,43 @@ impl<'a, C: 'a+BezierCurve> BezierCurve for CurveSection<'a, C> { /// /// The control points in this curve - /// + /// fn control_points(&self) -> (Self::Point, Self::Point) { - self.cached_control_points.borrow_mut() + self.cached_control_points + .borrow_mut() .get_or_insert_with(move || { // This is the de-casteljau subdivision algorithm (ran twice to cut out a section of the curve) let t_min = self.t_c; // Get the weights from the curve - let w1 = self.curve.start_point(); - let (w2, w3) = self.curve.control_points(); - let w4 = self.curve.end_point(); + let w1 = self.curve.start_point(); + let (w2, w3) = self.curve.control_points(); + let w4 = self.curve.end_point(); // Weights (from de casteljau) - let wn1 = w1*(1.0-t_min) + w2*t_min; - let wn2 = w2*(1.0-t_min) + w3*t_min; - let wn3 = w3*(1.0-t_min) + w4*t_min; + let wn1 = w1 * (1.0 - t_min) + w2 * t_min; + let wn2 = w2 * (1.0 - t_min) + w3 * t_min; + let wn3 = w3 * (1.0 - t_min) + w4 * t_min; // Further refine the weights - let wnn1 = wn1*(1.0-t_min) + wn2*t_min; - let wnn2 = wn2*(1.0-t_min) + wn3*t_min; + let wnn1 = wn1 * (1.0 - t_min) + wn2 * t_min; + let wnn2 = wn2 * (1.0 - t_min) + wn3 * t_min; // Get the point at which the two curves join let p = de_casteljau2(t_min, wnn1, wnn2); - + // Curve from t_min to 1 is in (p, wnn2, wn3, w4), we need to subdivide again // Ie, we've removed the section of the curve from 0-t_min here and now need to remove t_max to 1. We're subdividing the curve t_min to 1, so the t_max value is relative to that curve rather than the source. - let (w1, w2, w3) = (p, wnn2, wn3); - let t_max = self.t_m/(1.0-self.t_c); + let (w1, w2, w3) = (p, wnn2, wn3); + let t_max = self.t_m / (1.0 - self.t_c); // Weights (from de casteljau) - let wn1 = w1*(1.0-t_max) + w2*t_max; - let wn2 = w2*(1.0-t_max) + w3*t_max; + let wn1 = w1 * (1.0 - t_max) + w2 * t_max; + let wn2 = w2 * (1.0 - t_max) + w3 * t_max; // let wn3 = w3*(1.0-t_max) + w4*t_max; // Further refine the weights - let wnn1 = wn1*(1.0-t_max) + wn2*t_max; + let wnn1 = wn1 * (1.0 - t_max) + wn2 * t_max; // let wnn2 = wn2*(1.0-t_max) + wn3*t_max; // let p = de_casteljau2(t_min, wnn1, wnn2); @@ -157,7 +158,7 @@ impl<'a, C: 'a+BezierCurve> BezierCurve for CurveSection<'a, C> { /// /// Given a value t from 0 to 1, returns a point on this curve - /// + /// #[inline] fn point_at_pos(&self, t: f64) -> Self::Point { self.curve.point_at_pos(self.t_for_t(t)) diff --git a/src/bezier/solve.rs b/src/bezier/solve.rs index 1b0d63d1..08238dcf 100644 --- a/src/bezier/solve.rs +++ b/src/bezier/solve.rs @@ -1,37 +1,45 @@ -use super::curve::*; -use super::super::geo::*; use super::super::consts::*; +use super::super::geo::*; +use super::curve::*; -use roots::{find_roots_quadratic, find_roots_cubic, Roots}; +use roots::{find_roots_cubic, find_roots_quadratic, Roots}; use smallvec::*; -pub (crate) const CLOSE_ENOUGH: f64 = SMALL_DISTANCE * 50.0; +pub(crate) const CLOSE_ENOUGH: f64 = SMALL_DISTANCE * 50.0; /// /// Solves for t in a single dimension for a bezier curve (finds the point(s) where the basis /// function evaluates to p) -/// +/// pub fn solve_basis_for_t(w1: f64, w2: f64, w3: f64, w4: f64, p: f64) -> SmallVec<[f64; 4]> { // Compute the coefficients for the cubic bezier function - let d = w1-p; - let c = 3.0*(w2-w1); - let b = 3.0*(w3-w2)-c; - let a = w4-w1-c-b; + let d = w1 - p; + let c = 3.0 * (w2 - w1); + let b = 3.0 * (w3 - w2) - c; + let a = w4 - w1 - c - b; // Solve for p - let roots = if a.abs() < 0.00000001 { find_roots_quadratic(b, c, d) } else { find_roots_cubic(a, b, c, d) }; + let roots = if a.abs() < 0.00000001 { + find_roots_quadratic(b, c, d) + } else { + find_roots_cubic(a, b, c, d) + }; let mut roots = match roots { - Roots::No(_) => smallvec![], - Roots::One([a]) => smallvec![a], - Roots::Two([a, b]) => smallvec![a, b], - Roots::Three([a, b, c]) => smallvec![a, b, c], - Roots::Four([a, b, c, d]) => smallvec![a, b, c, d] + Roots::No(_) => smallvec![], + Roots::One([a]) => smallvec![a], + Roots::Two([a, b]) => smallvec![a, b], + Roots::Three([a, b, c]) => smallvec![a, b, c], + Roots::Four([a, b, c, d]) => smallvec![a, b, c, d], }; // Clip to 0/1 for small ranges outside for root in roots.iter_mut() { - if *root < 0.0 && *root > -0.001 { *root = 0.0 } - if *root > 1.0 && *root < 1.001 { *root = 1.0 } + if *root < 0.0 && *root > -0.001 { + *root = 0.0 + } + if *root > 1.0 && *root < 1.001 { + *root = 1.0 + } } // Remove any roots outside the range of the function @@ -58,22 +66,31 @@ pub fn solve_curve_for_t(curve: &C, point: &C::Point) -> Option< /// is best used with accuracy values that are small compared to the length of the curve and with points far away from /// inflection points or cusps. /// -/// This is often 'good enough' to find a point close to where a user has clicked along a curve, for example, but as it -/// effectively ray-casts along the x and y axes to do so is not suitable as a general-purpose way of finding the closest point +/// This is often 'good enough' to find a point close to where a user has clicked along a curve, for example, but as it +/// effectively ray-casts along the x and y axes to do so is not suitable as a general-purpose way of finding the closest point /// on a curve to another point. /// /// Note that `curve_intersects_ray()` can be used to find points on a curve along any direction rather than solely along the axis. /// -pub fn solve_curve_for_t_along_axis(curve: &C, point: &C::Point, accuracy: f64) -> Option { - let p1 = curve.start_point(); - let (p2, p3) = curve.control_points(); - let p4 = curve.end_point(); +pub fn solve_curve_for_t_along_axis( + curve: &C, + point: &C::Point, + accuracy: f64, +) -> Option { + let p1 = curve.start_point(); + let (p2, p3) = curve.control_points(); + let p4 = curve.end_point(); // Solve the basis function for each of the point's dimensions and pick the first that appears close enough (and within the range 0-1) for dimension in 0..(C::Point::len()) { // Solve for this dimension - let (w1, w2, w3, w4) = (p1.get(dimension), p2.get(dimension), p3.get(dimension), p4.get(dimension)); - let possible_t_values = solve_basis_for_t(w1, w2, w3, w4, point.get(dimension)); + let (w1, w2, w3, w4) = ( + p1.get(dimension), + p2.get(dimension), + p3.get(dimension), + p4.get(dimension), + ); + let possible_t_values = solve_basis_for_t(w1, w2, w3, w4, point.get(dimension)); for possible_t in possible_t_values { // Ignore values outside the range of the curve @@ -82,13 +99,13 @@ pub fn solve_curve_for_t_along_axis(curve: &C, point: &C::Point, } // If this is an accurate enough solution, return this as the t value - let point_at_t = curve.point_at_pos(possible_t); + let point_at_t = curve.point_at_pos(possible_t); if point_at_t.is_near_to(point, accuracy) { return Some(possible_t); } } } - + // No solution: result is None None } diff --git a/src/bezier/subdivide.rs b/src/bezier/subdivide.rs index c4f760e2..370ce241 100644 --- a/src/bezier/subdivide.rs +++ b/src/bezier/subdivide.rs @@ -1,20 +1,25 @@ -use super::basis::*; use super::super::geo::*; +use super::basis::*; /// /// Subdivides a cubic bezier curve at a particular point, returning the weights of /// the two component curves -/// -pub fn subdivide4(t: f64, w1: Point, w2: Point, w3: Point, w4: Point) -> - ((Point, Point, Point, Point), (Point, Point, Point, Point)) { +/// +pub fn subdivide4( + t: f64, + w1: Point, + w2: Point, + w3: Point, + w4: Point, +) -> ((Point, Point, Point, Point), (Point, Point, Point, Point)) { // Weights (from de casteljau) - let wn1 = w1*(1.0-t) + w2*t; - let wn2 = w2*(1.0-t) + w3*t; - let wn3 = w3*(1.0-t) + w4*t; + let wn1 = w1 * (1.0 - t) + w2 * t; + let wn2 = w2 * (1.0 - t) + w3 * t; + let wn3 = w3 * (1.0 - t) + w4 * t; // Further refine the weights - let wnn1 = wn1*(1.0-t) + wn2*t; - let wnn2 = wn2*(1.0-t) + wn3*t; + let wnn1 = wn1 * (1.0 - t) + wn2 * t; + let wnn2 = wn2 * (1.0 - t) + wn3 * t; // Get the point at which the two curves join let p = de_casteljau2(t, wnn1, wnn2); diff --git a/src/bezier/tangent.rs b/src/bezier/tangent.rs index 38b56dbf..7f72c628 100644 --- a/src/bezier/tangent.rs +++ b/src/bezier/tangent.rs @@ -1,24 +1,29 @@ -use super::curve::*; use super::basis::*; +use super::curve::*; use super::derivative::*; /// /// A structure that can be used to compute the tangent of a bezier curve -/// +/// pub struct Tangent { /// The derivative of the curve - derivative: (Curve::Point, Curve::Point, Curve::Point) + derivative: (Curve::Point, Curve::Point, Curve::Point), } impl<'a, Curve: BezierCurve> From<&'a Curve> for Tangent { /// /// Creates a structure that can computes the tangents for a bezier curve - /// + /// fn from(curve: &'a Curve) -> Tangent { let control_points = curve.control_points(); Tangent { - derivative: derivative4(curve.start_point(), control_points.0, control_points.1, curve.end_point()) + derivative: derivative4( + curve.start_point(), + control_points.0, + control_points.1, + curve.end_point(), + ), } } } @@ -26,7 +31,7 @@ impl<'a, Curve: BezierCurve> From<&'a Curve> for Tangent { impl Tangent { /// /// Calculates the tangent at a particular point - /// + /// pub fn tangent(&self, t: f64) -> Curve::Point { de_casteljau3(t, self.derivative.0, self.derivative.1, self.derivative.2) } diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index 9658cf5d..f6fcadd4 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -1,7 +1,7 @@ use super::basis::*; use super::curve::*; -use super::section::*; use super::derivative::*; +use super::section::*; use crate::geo::*; @@ -9,23 +9,26 @@ use crate::geo::*; /// Walks a bezier curve by dividing it into a number of sections /// /// These sections are uneven in length: they all advance equally by 't' value but the points will -/// be spaced according to the shape of the curve (will have an uneven distance between them) +/// be spaced according to the shape of the curve (will have an uneven distance between them) /// #[inline] -pub fn walk_curve_unevenly<'a, Curve: BezierCurve>(curve: &'a Curve, num_subdivisions: usize) -> impl 'a+Iterator> { +pub fn walk_curve_unevenly<'a, Curve: BezierCurve>( + curve: &'a Curve, + num_subdivisions: usize, +) -> impl 'a + Iterator> { if num_subdivisions > 0 { UnevenWalkIterator { - curve: curve, - step: (1.0)/(num_subdivisions as f64), - num_subdivisions: num_subdivisions, - last_subdivision: 0 + curve: curve, + step: (1.0) / (num_subdivisions as f64), + num_subdivisions: num_subdivisions, + last_subdivision: 0, } } else { UnevenWalkIterator { - curve: curve, - step: 0.0, - num_subdivisions: 0, - last_subdivision: 0 + curve: curve, + step: 0.0, + num_subdivisions: 0, + last_subdivision: 0, } } } @@ -36,20 +39,28 @@ pub fn walk_curve_unevenly<'a, Curve: BezierCurve>(curve: &'a Curve, num_subdivi /// This walks evenly using the curve's chord length rather than the arc length: each section returned will have a `chord_length()` /// of `distance`. The call `vary_by()` can be used on the result to vary the step size at each point. /// -pub fn walk_curve_evenly<'a, Curve: BezierCurve>(curve: &'a Curve, distance: f64, max_error: f64) -> EvenWalkIterator<'a, Curve> { +pub fn walk_curve_evenly<'a, Curve: BezierCurve>( + curve: &'a Curve, + distance: f64, + max_error: f64, +) -> EvenWalkIterator<'a, Curve> { const INITIAL_INCREMENT: f64 = 0.01; // Too small or negative values might produce bad effects due to floating point inprecision - let max_error = if max_error < 1e-10 { 1e-10 } else { max_error }; - let distance = if distance < 1e-10 { 1e-10 } else { distance }; + let max_error = if max_error < 1e-10 { 1e-10 } else { max_error }; + let distance = if distance < 1e-10 { 1e-10 } else { distance }; // Compute the derivative of the curve - let (cp1, cp2) = curve.control_points(); + let (cp1, cp2) = curve.control_points(); let (wn1, wn2, wn3) = derivative4(curve.start_point(), cp1, cp2, curve.end_point()); // We can calculate the initial speed from close to the first point of the curve - let initial_speed = de_casteljau3(0.001, wn1, wn2, wn3).magnitude(); - let initial_speed = if distance/(initial_speed.abs()) > 0.25 { de_casteljau3(0.01, wn1, wn2, wn3).magnitude() } else { initial_speed }; + let initial_speed = de_casteljau3(0.001, wn1, wn2, wn3).magnitude(); + let initial_speed = if distance / (initial_speed.abs()) > 0.25 { + de_casteljau3(0.01, wn1, wn2, wn3).magnitude() + } else { + initial_speed + }; let initial_increment = if initial_speed.abs() < 0.00000001 { INITIAL_INCREMENT @@ -57,16 +68,20 @@ pub fn walk_curve_evenly<'a, Curve: BezierCurve>(curve: &'a Curve, distance: f64 distance / initial_speed }; - let initial_increment = if initial_increment > 0.25 { INITIAL_INCREMENT } else { initial_increment }; + let initial_increment = if initial_increment > 0.25 { + INITIAL_INCREMENT + } else { + initial_increment + }; EvenWalkIterator { - curve: curve, - derivative: (wn1, wn2, wn3), - last_t: 0.0, - last_point: curve.start_point(), + curve: curve, + derivative: (wn1, wn2, wn3), + last_t: 0.0, + last_point: curve.start_point(), last_increment: initial_increment, - distance: distance, - max_error: max_error + distance: distance, + max_error: max_error, } } @@ -75,16 +90,16 @@ pub fn walk_curve_evenly<'a, Curve: BezierCurve>(curve: &'a Curve, distance: f64 /// struct UnevenWalkIterator<'a, Curve: BezierCurve> { /// The curve that this is iterating over - curve: &'a Curve, + curve: &'a Curve, /// The distance between t-values - step: f64, + step: f64, /// The total number of subdivisions to return - num_subdivisions: usize, + num_subdivisions: usize, /// The number of the most recently returned subdivision - last_subdivision: usize + last_subdivision: usize, } impl<'a, Curve: BezierCurve> Iterator for UnevenWalkIterator<'a, Curve> { @@ -112,37 +127,37 @@ impl<'a, Curve: BezierCurve> Iterator for UnevenWalkIterator<'a, Curve> { /// pub struct EvenWalkIterator<'a, Curve: BezierCurve> { /// The curve that is being walked - curve: &'a Curve, + curve: &'a Curve, /// The wn1, wn2, wn3 of the derivative of the curve - derivative: (Curve::Point, Curve::Point, Curve::Point), + derivative: (Curve::Point, Curve::Point, Curve::Point), /// The last 't' value where a coordinate was generated - last_t: f64, + last_t: f64, /// The point generated at the last 't' value - last_point: Curve::Point, + last_point: Curve::Point, /// The last increment last_increment: f64, /// The target distance between points (as the chord length) - distance: f64, + distance: f64, /// The maximum error in distance for the points that are generated by this iterator - max_error: f64 + max_error: f64, } /// /// Iterator that modifies the behaviour of EvenWalkIterator so that it varies the distance between /// each step /// -struct VaryingWalkIterator<'a, Curve: BezierCurve, DistanceIter: 'a+Iterator> { +struct VaryingWalkIterator<'a, Curve: BezierCurve, DistanceIter: 'a + Iterator> { /// The even walk iterator even_iterator: EvenWalkIterator<'a, Curve>, /// Iterator that returns the distance for each step (or None if the distance is fixed for the remaining distance) - distance_iterator: Option + distance_iterator: Option, } impl<'a, Curve: BezierCurve> EvenWalkIterator<'a, Curve> { @@ -153,10 +168,13 @@ impl<'a, Curve: BezierCurve> EvenWalkIterator<'a, Curve> { /// would generate a cycle of distances (say, by calling `cycle()`), but if it does end, the last distance will be used /// until the iteration over the curve is completed /// - pub fn vary_by>(self, distance: DistanceIter) -> impl 'a+Iterator> { + pub fn vary_by>( + self, + distance: DistanceIter, + ) -> impl 'a + Iterator> { VaryingWalkIterator { - even_iterator: self, - distance_iterator: Some(distance) + even_iterator: self, + distance_iterator: Some(distance), } } } @@ -168,14 +186,14 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { const MAX_ITERATIONS: usize = 32; // Gather values - let curve = self.curve; + let curve = self.curve; let (wn1, wn2, wn3) = self.derivative; - let distance = self.distance; - let max_error = self.max_error; + let distance = self.distance; + let max_error = self.max_error; let mut t_increment = self.last_increment; - let last_t = self.last_t; - let mut next_t = last_t + t_increment; - let last_point = self.last_point.clone(); + let last_t = self.last_t; + let mut next_t = last_t + t_increment; + let last_point = self.last_point.clone(); let mut next_point; // If the next point appears to be after the end of the curve, and the end of the curve is further away than the closest distance, return None @@ -197,11 +215,11 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { debug_assert!(!t_increment.is_nan()); // next_point contains the initial estimate of the position of the point at distance 't' from the current point - next_point = curve.point_at_pos(next_t); + next_point = curve.point_at_pos(next_t); // Compute the distance to the guess and the error - let next_distance = last_point.distance_to(&next_point); - let error = distance - next_distance; + let next_distance = last_point.distance_to(&next_point); + let error = distance - next_distance; // We've found the next point if the error drops low enough if error.abs() < max_error { @@ -209,25 +227,25 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { } // Use the slope of the curve at this position to work out the next point to try - let tangent = de_casteljau3(next_t, wn1, wn2, wn3); - let speed = tangent.magnitude(); + let tangent = de_casteljau3(next_t, wn1, wn2, wn3); + let speed = tangent.magnitude(); if speed.abs() < 0.00000001 { // Very rarely, the speed can be 0 (at t=0 or t=1 when the control points overlap, for the easiest example to construct) // Use the error to adjust the t position we're testing if it's larger than max_error - let error_ratio = distance / next_distance; - t_increment = if error_ratio < 0.5 { + let error_ratio = distance / next_distance; + t_increment = if error_ratio < 0.5 { t_increment * 0.5 - } else if error_ratio > 1.5 { + } else if error_ratio > 1.5 { t_increment * 1.5 } else { t_increment * error_ratio }; } else { // Use the current speed to work out the adjustment for t_increment - let error = next_distance - distance; - let adjustment = error / speed; + let error = next_distance - distance; + let adjustment = error / speed; if adjustment >= t_increment { t_increment *= 0.3333333; @@ -236,12 +254,12 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { } } - next_t = last_t + t_increment; + next_t = last_t + t_increment; // Sharp changes in direction can sometimes cause the distance to fail to converge: we limit the maximum number of iterations to avoid this - // (It's possible for there to be multiple points 'distance' away or two equidistant points around the target point, + // (It's possible for there to be multiple points 'distance' away or two equidistant points around the target point, // and for this algorithm to fail to converge as a result) - count += 1; + count += 1; if count >= MAX_ITERATIONS { break; } @@ -255,16 +273,18 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { } // Update the coordinates - self.last_point = next_point; + self.last_point = next_point; self.last_increment = t_increment; - self.last_t = next_t; + self.last_t = next_t; // Return the section that we found Some(self.curve.section(last_t, next_t)) } } -impl<'a, Curve: BezierCurve, DistanceIter: 'a+Iterator> Iterator for VaryingWalkIterator<'a, Curve, DistanceIter> { +impl<'a, Curve: BezierCurve, DistanceIter: 'a + Iterator> Iterator + for VaryingWalkIterator<'a, Curve, DistanceIter> +{ type Item = CurveSection<'a, Curve>; fn next(&mut self) -> Option { @@ -272,9 +292,9 @@ impl<'a, Curve: BezierCurve, DistanceIter: 'a+Iterator> Iterator for V if let Some(distance_iterator) = &mut self.distance_iterator { if let Some(distance) = distance_iterator.next() { // Update the distance in the 'even' iterator - let ratio = distance / self.even_iterator.distance; - self.even_iterator.distance = distance; - self.even_iterator.last_increment *= ratio; + let ratio = distance / self.even_iterator.distance; + self.even_iterator.distance = distance; + self.even_iterator.last_increment *= ratio; } else { // No more distance changes self.distance_iterator = None; diff --git a/src/debug/graph_path_debug.rs b/src/debug/graph_path_debug.rs index 3570f99c..cb16867d 100644 --- a/src/debug/graph_path_debug.rs +++ b/src/debug/graph_path_debug.rs @@ -1,47 +1,59 @@ -use super::super::bezier::*; use super::super::bezier::path::*; +use super::super::bezier::*; use std::fmt::Write; /// /// Writes out the graph path as an SVG string /// -pub fn graph_path_svg_string(path: &GraphPath, rays: Vec<(P, P)>) -> String { +pub fn graph_path_svg_string( + path: &GraphPath, + rays: Vec<(P, P)>, +) -> String { let mut result = String::new(); - let bounds = path.all_edges().fold(Bounds::empty(), |a, b| a.union_bounds(b.bounding_box())); - let offset = bounds.min(); - let scale = 1000.0/(bounds.max() - bounds.min()).x(); - - let mut index = 0; - - for kinds in vec![ vec![ GraphPathEdgeKind::Uncategorised, GraphPathEdgeKind::Visited, GraphPathEdgeKind::Interior ], vec![ GraphPathEdgeKind::Exterior ] ] { + let bounds = path + .all_edges() + .fold(Bounds::empty(), |a, b| a.union_bounds(b.bounding_box())); + let offset = bounds.min(); + let scale = 1000.0 / (bounds.max() - bounds.min()).x(); + + let mut index = 0; + + for kinds in vec![ + vec![ + GraphPathEdgeKind::Uncategorised, + GraphPathEdgeKind::Visited, + GraphPathEdgeKind::Interior, + ], + vec![GraphPathEdgeKind::Exterior], + ] { for edge in path.all_edges() { if !kinds.contains(&edge.kind()) { continue; } let start_point = edge.start_point(); - let end_point = edge.end_point(); - let (cp1, cp2) = edge.control_points(); + let end_point = edge.end_point(); + let (cp1, cp2) = edge.control_points(); write!(result, "\n", - index, + index, start_point.x(), start_point.y(), cp1.x(), cp1.y(), cp2.x(), cp2.y(), end_point.x(), end_point.y()).unwrap(); - let start_point = (start_point - offset)*scale; - let end_point = (end_point - offset)*scale; - let cp1 = (cp1 - offset)*scale; - let cp2 = (cp2 - offset)*scale; + let start_point = (start_point - offset) * scale; + let end_point = (end_point - offset) * scale; + let cp1 = (cp1 - offset) * scale; + let cp2 = (cp2 - offset) * scale; - let kind = match edge.kind() { - GraphPathEdgeKind::Uncategorised => "yellow", - GraphPathEdgeKind::Visited => "red", - GraphPathEdgeKind::Exterior => "blue", - GraphPathEdgeKind::Interior => "green" + let kind = match edge.kind() { + GraphPathEdgeKind::Uncategorised => "yellow", + GraphPathEdgeKind::Visited => "red", + GraphPathEdgeKind::Exterior => "blue", + GraphPathEdgeKind::Interior => "green", }; write!(result, "\n", @@ -52,21 +64,38 @@ pub fn graph_path_svg_string(path: &GraphPath\n", end_point.x(), end_point.y()).unwrap(); - write!(result, "{} <- {} - {}\n", end_point.x()+4.0, end_point.y()+8.0, edge.end_point_index(), edge.start_point_index(), index).unwrap(); + write!( + result, + "{} <- {} - {}\n", + end_point.x() + 4.0, + end_point.y() + 8.0, + edge.end_point_index(), + edge.start_point_index(), + index + ) + .unwrap(); index += 1; } } for (p1, p2) in rays { - write!(result, "\n", p1.x(), p1.y(), p2.x(), p2.y()).unwrap(); + write!( + result, + "\n", + p1.x(), + p1.y(), + p2.x(), + p2.y() + ) + .unwrap(); let collisions = path.ray_collisions(&(p1, p2)); write!(result, "", collisions.len()).unwrap(); let p1 = (p1 - offset) * scale; let p2 = (p2 - offset) * scale; - let point_offset = p2-p1; + let point_offset = p2 - p1; let p1 = p1 - (point_offset * 1000.0); let p2 = p2 + (point_offset * 1000.0); @@ -74,32 +103,32 @@ pub fn graph_path_svg_string(path: &GraphPath { side }, - PathDirection::Anticlockwise => { -side } + let edge = collision.edge(); + let PathLabel(path_number, direction) = path.edge_label(edge); + let normal = path.get_edge(edge).normal_at_pos(curve_t); + + let side = ray_direction.dot(&normal).signum() as i32; + let side = match direction { + PathDirection::Clockwise => side, + PathDirection::Anticlockwise => -side, }; // Update the collision count collision_count += side; - collision_num += 1; + collision_num += 1; - let pos = (pos - offset)*scale; + let pos = (pos - offset) * scale; - let edge = path.get_edge(edge); + let edge = path.get_edge(edge); let start_point = edge.start_point(); - let end_point = edge.end_point(); - let (cp1, cp2) = edge.control_points(); + let end_point = edge.end_point(); + let (cp1, cp2) = edge.control_points(); write!(result, "\n", collision_num, path_number, @@ -108,10 +137,10 @@ pub fn graph_path_svg_string(path: &GraphPath\n", start_point.x(), start_point.y(), @@ -120,10 +149,25 @@ pub fn graph_path_svg_string(path: &GraphPath\n", pos.x(), pos.y()).unwrap(); - write!(result, "{}: C{} ({})\n", pos.x() + 2.0, pos.y()+3.0, collision_num, collision_count, side).unwrap(); + write!( + result, + "\n", + pos.x(), + pos.y() + ) + .unwrap(); + write!( + result, + "{}: C{} ({})\n", + pos.x() + 2.0, + pos.y() + 3.0, + collision_num, + collision_count, + side + ) + .unwrap(); } } result -} \ No newline at end of file +} diff --git a/src/debug/mod.rs b/src/debug/mod.rs index 564cbe2b..9a6386e0 100644 --- a/src/debug/mod.rs +++ b/src/debug/mod.rs @@ -1,5 +1,5 @@ -mod path_to_string; mod graph_path_debug; +mod path_to_string; -pub use self::path_to_string::*; pub use self::graph_path_debug::*; +pub use self::path_to_string::*; diff --git a/src/debug/path_to_string.rs b/src/debug/path_to_string.rs index 1dcb92cb..de669958 100644 --- a/src/debug/path_to_string.rs +++ b/src/debug/path_to_string.rs @@ -1,21 +1,39 @@ -use super::super::geo::*; use super::super::bezier::path::*; +use super::super::geo::*; use std::fmt::*; /// /// Writes out a path as a Rust simple bezier path definition -/// +/// /// This can be used to generate code for a test when a path definition fails to perform as expected /// -pub fn bezier_path_to_rust_definition>(path: &P) -> String { +pub fn bezier_path_to_rust_definition>( + path: &P, +) -> String { let mut rust_code = String::new(); let start = path.start_point(); - write!(&mut rust_code, "BezierPathBuilder::::start(Coord2({}, {}))", start.x(), start.y()).unwrap(); + write!( + &mut rust_code, + "BezierPathBuilder::::start(Coord2({}, {}))", + start.x(), + start.y() + ) + .unwrap(); for (cp1, cp2, endpoint) in path.points() { - write!(&mut rust_code, "\n .curve_to((Coord2({}, {}), Coord2({}, {})), Coord2({}, {}))", cp1.x(), cp1.y(), cp2.x(), cp2.y(), endpoint.x(), endpoint.y()).unwrap(); + write!( + &mut rust_code, + "\n .curve_to((Coord2({}, {}), Coord2({}, {})), Coord2({}, {}))", + cp1.x(), + cp1.y(), + cp2.x(), + cp2.y(), + endpoint.x(), + endpoint.y() + ) + .unwrap(); } write!(&mut rust_code, "\n .build()").unwrap(); diff --git a/src/geo/bounding_box.rs b/src/geo/bounding_box.rs index 316ab896..ef4adc7c 100644 --- a/src/geo/bounding_box.rs +++ b/src/geo/bounding_box.rs @@ -1,20 +1,20 @@ +use super::coordinate::*; use super::geo::*; use super::has_bounds::*; -use super::coordinate::*; /// /// Trait implemented by things representing axis-aligned bounding boxes -/// -pub trait BoundingBox : Geo+Sized { +/// +pub trait BoundingBox: Geo + Sized { /// /// Returns a bounding box with the specified minimum and maximum coordinates - /// + /// fn from_min_max(min: Self::Point, max: Self::Point) -> Self; /// /// Returns a bounding box containing the specified points - /// - fn bounds_for_points>(points: PointIter) -> Self { + /// + fn bounds_for_points>(points: PointIter) -> Self { let mut points = points.into_iter(); // Initialise the bounding box with the first point @@ -43,19 +43,19 @@ pub trait BoundingBox : Geo+Sized { /// /// Returns the maximum point of this bounding box - /// + /// fn max(&self) -> Self::Point; /// /// Returns an empty bounding box - /// + /// fn empty() -> Self { Self::from_min_max(Self::Point::origin(), Self::Point::origin()) } /// /// True if this bounding box is empty - /// + /// #[inline] fn is_empty(&self) -> bool { self.min() == self.max() @@ -63,27 +63,34 @@ pub trait BoundingBox : Geo+Sized { /// /// Creates the union of this and another bounding box - /// + /// fn union_bounds(self, target: Self) -> Self { if self.is_empty() { target } else if target.is_empty() { self } else { - Self::from_min_max(Self::Point::from_smallest_components(self.min(), target.min()), Self::Point::from_biggest_components(self.max(), target.max())) + Self::from_min_max( + Self::Point::from_smallest_components(self.min(), target.min()), + Self::Point::from_biggest_components(self.max(), target.max()), + ) } } /// /// Returns true if this bounding box overlaps another - /// + /// fn overlaps(&self, target: &Self) -> bool { let (min1, max1) = (self.min(), self.max()); let (min2, max2) = (target.min(), target.max()); for p_index in 0..Self::Point::len() { - if min1.get(p_index) > max2.get(p_index) { return false; } - if min2.get(p_index) > max1.get(p_index) { return false; } + if min1.get(p_index) > max2.get(p_index) { + return false; + } + if min2.get(p_index) > max1.get(p_index) { + return false; + } } true @@ -92,9 +99,9 @@ pub trait BoundingBox : Geo+Sized { /// /// Type representing a bounding box -/// +/// /// (Unlike a normal point tuple this always represents its bounds in minimum/maximum order) -/// +/// #[derive(Debug, Clone, Copy, PartialEq)] pub struct Bounds(Point, Point); @@ -116,13 +123,13 @@ impl BoundingBox for (Point, Point) { } impl HasBoundingBox for Bounds { - fn get_bounding_box>(&self) -> Bounds { + fn get_bounding_box>(&self) -> Bounds { Bounds::from_min_max(self.min(), self.max()) } } impl Geo for Bounds { - type Point=Point; + type Point = Point; } impl BoundingBox for Bounds { @@ -140,4 +147,4 @@ impl BoundingBox for Bounds { fn max(&self) -> Self::Point { self.1 } -} \ No newline at end of file +} diff --git a/src/geo/coordinate.rs b/src/geo/coordinate.rs index 4af42b66..e212913e 100644 --- a/src/geo/coordinate.rs +++ b/src/geo/coordinate.rs @@ -3,7 +3,7 @@ //! //! The `Coordinate` trait provides a way to represent coordinates in arbitary numbers of dimensions. Most of the //! types in `flo_curves` support arbitrary coordinate types through this trait. -//! +//! //! `Coordinate2D` coordinates are a special case of coordinates with only two dimensions. Some operations are //! only defined for two dimensions: for example, taking the normal of a Bezier curve. The `Coord2` type is //! supplied as a generic implementation of a 2-dimensional coordinate, though these operations will work on @@ -16,52 +16,59 @@ use std::ops::*; /// /// Represents a value that can be used as a coordinate in a bezier curve -/// -pub trait Coordinate : Sized+Copy+Add+Mul+Sub+PartialEq { +/// +pub trait Coordinate: + Sized + + Copy + + Add + + Mul + + Sub + + PartialEq +{ /// /// Creates a new coordinate from the specified set of components - /// + /// fn from_components(components: &[f64]) -> Self; /// /// Returns the origin coordinate - /// + /// fn origin() -> Self; /// /// The number of components in this coordinate - /// + /// fn len() -> usize; /// /// Retrieves the component at the specified index - /// + /// fn get(&self, index: usize) -> f64; /// /// Returns a point made up of the biggest components of the two points - /// + /// fn from_biggest_components(p1: Self, p2: Self) -> Self; /// /// Returns a point made up of the smallest components of the two points - /// + /// fn from_smallest_components(p1: Self, p2: Self) -> Self; /// /// Computes the distance between this coordinate and another of the same type - /// + /// #[inline] fn distance_to(&self, target: &Self) -> f64 { - let offset = *self - *target; - let squared_distance = offset.dot(&offset); + let offset = *self - *target; + let squared_distance = offset.dot(&offset); f64::sqrt(squared_distance) } /// /// Computes the dot product for this vector along with another vector - /// + /// #[inline] fn dot(&self, target: &Self) -> f64 { let mut dot_product = 0.0; @@ -75,7 +82,7 @@ pub trait Coordinate : Sized+Copy+Add+Mul+S /// /// Computes the magnitude of this vector - /// + /// #[inline] fn magnitude(&self) -> f64 { f64::sqrt(self.dot(self)) @@ -83,14 +90,14 @@ pub trait Coordinate : Sized+Copy+Add+Mul+S /// /// Treating this as a vector, returns a unit vector in the same direction - /// + /// #[inline] fn to_unit_vector(&self) -> Self { let magnitude = self.magnitude(); if magnitude == 0.0 { Self::origin() } else { - *self * (1.0/magnitude) + *self * (1.0 / magnitude) } } @@ -117,7 +124,7 @@ pub trait Coordinate : Sized+Copy+Add+Mul+S for component in 0..Self::len() { let unrounded_value = self.get(component); - let rounded_value = (unrounded_value/accuracy).round() * accuracy; + let rounded_value = (unrounded_value / accuracy).round() * accuracy; new_components.push(rounded_value); } @@ -127,41 +134,41 @@ pub trait Coordinate : Sized+Copy+Add+Mul+S /// /// True if this point is within max_distance of another point - /// + /// #[inline] fn is_near_to(&self, other: &Self, max_distance: f64) -> bool { - let offset = *self - *other; - let squared_distance = offset.dot(&offset); + let offset = *self - *other; + let squared_distance = offset.dot(&offset); - squared_distance <= (max_distance*max_distance) + squared_distance <= (max_distance * max_distance) } /// /// Generates a smoothed version of a set of coordinates, using the specified weights /// (weights should add up to 1.0). - /// + /// /// A suggested set of weights might be '[0.25, 0.5, 0.25]', which will slightly /// adjust each point according to its neighbours (the central weight is what's /// applied to the 'current' point) - /// + /// fn smooth(points: &[Self], weights: &[f64]) -> Vec { - let mut smoothed = vec![]; - let points_len = points.len() as i32; - let weight_len = weights.len() as i32; - let weight_offset = weight_len/2; - + let mut smoothed = vec![]; + let points_len = points.len() as i32; + let weight_len = weights.len() as i32; + let weight_offset = weight_len / 2; + for index in 0..points_len { - let mut res = Self::origin(); + let mut res = Self::origin(); let initial_pos = index - weight_offset; for weight_pos in 0..weight_len { - let weight = weights[weight_pos as usize]; - let source_pos = initial_pos + weight_pos; + let weight = weights[weight_pos as usize]; + let source_pos = initial_pos + weight_pos; - let source_val = if source_pos < 0 { + let source_val = if source_pos < 0 { &points[0] } else if source_pos >= points_len { - &points[(points_len-1) as usize] + &points[(points_len - 1) as usize] } else { &points[source_pos as usize] }; @@ -178,18 +185,20 @@ pub trait Coordinate : Sized+Copy+Add+Mul+S /// /// Represents a coordinate with a 2D position -/// +/// pub trait Coordinate2D { fn x(&self) -> f64; fn y(&self) -> f64; #[inline] - fn coords(&self) -> (f64, f64) { (self.x(), self.y()) } + fn coords(&self) -> (f64, f64) { + (self.x(), self.y()) + } } /// /// Represents a coordinate with a 3D position -/// +/// pub trait Coordinate3D { fn x(&self) -> f64; fn y(&self) -> f64; @@ -201,9 +210,18 @@ impl Coordinate for f64 { components[0] } - #[inline] fn origin() -> f64 { 0.0 } - #[inline] fn len() -> usize { 1 } - #[inline] fn get(&self, _index: usize) -> f64 { *self } + #[inline] + fn origin() -> f64 { + 0.0 + } + #[inline] + fn len() -> usize { + 1 + } + #[inline] + fn get(&self, _index: usize) -> f64 { + *self + } #[inline] fn from_biggest_components(p1: f64, p2: f64) -> f64 { @@ -225,7 +243,7 @@ impl Coordinate for f64 { #[inline] fn distance_to(&self, target: &f64) -> f64 { - f64::abs(self-target) + f64::abs(self - target) } fn dot(&self, target: &f64) -> f64 { @@ -240,7 +258,7 @@ pub struct Coord2(pub f64, pub f64); impl Coordinate2D for Coord2 { /// /// X component of this coordinate - /// + /// #[inline] fn x(&self) -> f64 { self.0 @@ -248,7 +266,7 @@ impl Coordinate2D for Coord2 { /// /// Y component of this coordinate - /// + /// #[inline] fn y(&self) -> f64 { self.1 @@ -256,7 +274,7 @@ impl Coordinate2D for Coord2 { } impl Add for Coord2 { - type Output=Coord2; + type Output = Coord2; #[inline] fn add(self, rhs: Coord2) -> Coord2 { @@ -265,7 +283,7 @@ impl Add for Coord2 { } impl Sub for Coord2 { - type Output=Coord2; + type Output = Coord2; #[inline] fn sub(self, rhs: Coord2) -> Coord2 { @@ -274,7 +292,7 @@ impl Sub for Coord2 { } impl Mul for Coord2 { - type Output=Coord2; + type Output = Coord2; #[inline] fn mul(self, rhs: f64) -> Coord2 { @@ -318,35 +336,43 @@ impl Coordinate for Coord2 { } #[inline] - fn len() -> usize { 2 } + fn len() -> usize { + 2 + } #[inline] - fn get(&self, index: usize) -> f64 { + fn get(&self, index: usize) -> f64 { match index { 0 => self.0, 1 => self.1, - _ => panic!("Coord2 only has two components") + _ => panic!("Coord2 only has two components"), } } fn from_biggest_components(p1: Coord2, p2: Coord2) -> Coord2 { - Coord2(f64::from_biggest_components(p1.0, p2.0), f64::from_biggest_components(p1.1, p2.1)) + Coord2( + f64::from_biggest_components(p1.0, p2.0), + f64::from_biggest_components(p1.1, p2.1), + ) } fn from_smallest_components(p1: Coord2, p2: Coord2) -> Coord2 { - Coord2(f64::from_smallest_components(p1.0, p2.0), f64::from_smallest_components(p1.1, p2.1)) + Coord2( + f64::from_smallest_components(p1.0, p2.0), + f64::from_smallest_components(p1.1, p2.1), + ) } #[inline] fn distance_to(&self, target: &Coord2) -> f64 { - let dist_x = target.0-self.0; - let dist_y = target.1-self.1; + let dist_x = target.0 - self.0; + let dist_y = target.1 - self.1; - f64::sqrt(dist_x*dist_x + dist_y*dist_y) + f64::sqrt(dist_x * dist_x + dist_y * dist_y) } #[inline] fn dot(&self, target: &Self) -> f64 { - self.0*target.0 + self.1*target.1 + self.0 * target.0 + self.1 * target.1 } } diff --git a/src/geo/coordinate_ext.rs b/src/geo/coordinate_ext.rs index 63b7b24e..24168624 100644 --- a/src/geo/coordinate_ext.rs +++ b/src/geo/coordinate_ext.rs @@ -20,18 +20,22 @@ pub trait Coordinate2DExt { fn unit_vector_at_angle(radians: impl Into) -> Self; } -impl CoordinateExt for T -where T: Coordinate { +impl CoordinateExt for T +where + T: Coordinate, +{ fn unit_vector() -> Self { - let mut components = vec![0.0; Self::len()]; - components[0] = 1.0; + let mut components = vec![0.0; Self::len()]; + components[0] = 1.0; Self::from_components(&components) } } impl Coordinate2DExt for T -where T: Coordinate+Coordinate2D { +where + T: Coordinate + Coordinate2D, +{ fn unit_vector_at_angle(radians: impl Into) -> Self { let radians = radians.into(); diff --git a/src/geo/geo.rs b/src/geo/geo.rs index 941656eb..324643a7 100644 --- a/src/geo/geo.rs +++ b/src/geo/geo.rs @@ -2,7 +2,7 @@ use super::coordinate::*; /// /// Simple base trait implemented by things representing geometry -/// +/// pub trait Geo { /// The type of a point in this geometry type Point: Coordinate; diff --git a/src/geo/has_bounds.rs b/src/geo/has_bounds.rs index a0aae708..7fdd3533 100644 --- a/src/geo/has_bounds.rs +++ b/src/geo/has_bounds.rs @@ -1,12 +1,12 @@ -use super::geo::*; use super::bounding_box::*; +use super::geo::*; /// /// Trait implemented by types that have a bounding box associated with them /// -pub trait HasBoundingBox : Geo { +pub trait HasBoundingBox: Geo { /// /// Returns the bounding box that encloses this item /// - fn get_bounding_box>(&self) -> Bounds; + fn get_bounding_box>(&self) -> Bounds; } diff --git a/src/geo/mod.rs b/src/geo/mod.rs index 0e8ff129..c7737fbc 100644 --- a/src/geo/mod.rs +++ b/src/geo/mod.rs @@ -1,26 +1,25 @@ //! //! # Traits for basic geometric definitions -//! +//! //! This provides some basic geometric definitions. The `Geo` trait can be implemented by any type that has //! a particular type of coordinate - for example, implementations of `BezierCurve` need to implement `Geo` //! in order to describe what type they use for coordinates. -//! +//! //! `BoundingBox` provides a way to describe axis-aligned bounding boxes. It too is a trait, making it //! possible to request bounding boxes in types other than the default `Bounds` type supplied by the //! library. //! -mod geo; -mod sweep; -mod has_bounds; +mod bounding_box; mod coordinate; mod coordinate_ext; -mod bounding_box; +mod geo; +mod has_bounds; +mod sweep; -pub use self::geo::*; -pub use self::sweep::*; -pub use self::has_bounds::*; -pub use self::coordinate::*; pub use self::bounding_box::*; +pub use self::coordinate::*; pub use self::coordinate_ext::*; - +pub use self::geo::*; +pub use self::has_bounds::*; +pub use self::sweep::*; diff --git a/src/geo/sweep.rs b/src/geo/sweep.rs index 845856ff..e012bdeb 100644 --- a/src/geo/sweep.rs +++ b/src/geo/sweep.rs @@ -2,22 +2,25 @@ use crate::geo::*; use smallvec::*; -use std::cmp::{Ordering}; +use std::cmp::Ordering; /// /// Sweeps a set of objects with bounding boxes to find the potential collisions between them /// /// The objects must be sorted into order by their min-x position, with the lowest first /// -pub fn sweep_self<'a, TItem, BoundsIter>(ordered_items: BoundsIter) -> impl 'a+Iterator +pub fn sweep_self<'a, TItem, BoundsIter>( + ordered_items: BoundsIter, +) -> impl 'a + Iterator where -BoundsIter: 'a+Iterator, -TItem: 'a+HasBoundingBox, -TItem::Point: Coordinate2D { + BoundsIter: 'a + Iterator, + TItem: 'a + HasBoundingBox, + TItem::Point: Coordinate2D, +{ SweepSelfIterator { - bounds_iterator: ordered_items, - pending: smallvec![], - by_max_x: Vec::new() + bounds_iterator: ordered_items, + pending: smallvec![], + by_max_x: Vec::new(), } } @@ -27,18 +30,22 @@ TItem::Point: Coordinate2D { /// This will only collide between objects in src and objects in tgt. Both must be sorted into order by /// their min-x position, with the lowest first /// -pub fn sweep_against<'a, TItem, SrcBoundsIter, TgtBoundsIter>(src: SrcBoundsIter, tgt: TgtBoundsIter) -> impl 'a+Iterator +pub fn sweep_against<'a, TItem, SrcBoundsIter, TgtBoundsIter>( + src: SrcBoundsIter, + tgt: TgtBoundsIter, +) -> impl 'a + Iterator where -SrcBoundsIter: 'a+Iterator, -TgtBoundsIter: 'a+Iterator, -TItem: 'a+HasBoundingBox, -TItem::Point: Coordinate2D { + SrcBoundsIter: 'a + Iterator, + TgtBoundsIter: 'a + Iterator, + TItem: 'a + HasBoundingBox, + TItem::Point: Coordinate2D, +{ SweepAgainstIterator { - src_iterator: Some(src), - tgt_iterator: tgt, - pending: smallvec![], - src_by_max_x: Vec::new(), - src_last_min_x: f64::MIN + src_iterator: Some(src), + tgt_iterator: tgt, + pending: smallvec![], + src_by_max_x: Vec::new(), + src_last_min_x: f64::MIN, } } @@ -47,9 +54,10 @@ TItem::Point: Coordinate2D { /// struct SweepSelfIterator<'a, TItem, BoundsIter> where -BoundsIter: 'a+Iterator, -TItem: 'a+HasBoundingBox, -TItem::Point: Coordinate2D { + BoundsIter: 'a + Iterator, + TItem: 'a + HasBoundingBox, + TItem::Point: Coordinate2D, +{ /// Iterator, ordered by minimum X position, that returns the items to be checked for overlaps bounds_iterator: BoundsIter, @@ -58,14 +66,15 @@ TItem::Point: Coordinate2D { /// Items currently under consideration for collisions, reverse ordered by their maximum X coordinate /// (reverse ordered so we can remove the earliest items by popping them) - by_max_x: Vec<(Bounds, &'a TItem)> + by_max_x: Vec<(Bounds, &'a TItem)>, } impl<'a, TItem, BoundsIter> Iterator for SweepSelfIterator<'a, TItem, BoundsIter> where -BoundsIter: 'a+Iterator, -TItem: 'a+HasBoundingBox, -TItem::Point: Coordinate2D { + BoundsIter: 'a + Iterator, + TItem: 'a + HasBoundingBox, + TItem::Point: Coordinate2D, +{ type Item = (&'a TItem, &'a TItem); fn next(&mut self) -> Option { @@ -77,7 +86,7 @@ TItem::Point: Coordinate2D { // Attempt to fill the pending queue by reading from the bounds iterator loop { // Read the next item and retrieve its bounding box - let next_item = if let Some(next_item) = self.bounds_iterator.next() { + let next_item = if let Some(next_item) = self.bounds_iterator.next() { next_item } else { // No more items to read, and the pending queue is empty @@ -89,7 +98,7 @@ TItem::Point: Coordinate2D { // Remove elements from the front of the by_max_x list until the closest ends after where this item begins // As the bounds_iterator is ordered by the min_x, we'll never see anything that's before this point again here - let min_x = next_bounds.min().x(); + let min_x = next_bounds.min().x(); while let Some((earliest_x, _item)) = self.by_max_x.last() { if earliest_x.max().x() >= min_x { break; @@ -108,18 +117,21 @@ TItem::Point: Coordinate2D { // Insert the new item into the 'by_max_x' list // TODO: possible that something like a btree is much more efficient here when there are a lot of items to process - let max_x = next_bounds.max().x(); - let index = self.by_max_x.binary_search_by(|(bounds, _item)| { - let item_max_x = bounds.max().x(); - - if item_max_x > max_x { - Ordering::Less - } else if item_max_x == max_x { - Ordering::Equal - } else { - Ordering::Greater - } - }).unwrap_or_else(|idx| idx); + let max_x = next_bounds.max().x(); + let index = self + .by_max_x + .binary_search_by(|(bounds, _item)| { + let item_max_x = bounds.max().x(); + + if item_max_x > max_x { + Ordering::Less + } else if item_max_x == max_x { + Ordering::Equal + } else { + Ordering::Greater + } + }) + .unwrap_or_else(|idx| idx); self.by_max_x.insert(index, (next_bounds, next_item)); @@ -136,10 +148,11 @@ TItem::Point: Coordinate2D { /// struct SweepAgainstIterator<'a, TItem, SrcIterator, TgtIterator> where -SrcIterator: 'a+Iterator, -TgtIterator: 'a+Iterator, -TItem: 'a+HasBoundingBox, -TItem::Point: Coordinate2D { + SrcIterator: 'a + Iterator, + TgtIterator: 'a + Iterator, + TItem: 'a + HasBoundingBox, + TItem::Point: Coordinate2D, +{ /// Iterator, ordered by minimum X position src_iterator: Option, @@ -156,12 +169,14 @@ TItem::Point: Coordinate2D { src_by_max_x: Vec<(Bounds, &'a TItem)>, } -impl<'a, TItem, SrcIterator, TgtIterator> Iterator for SweepAgainstIterator<'a, TItem, SrcIterator, TgtIterator> +impl<'a, TItem, SrcIterator, TgtIterator> Iterator + for SweepAgainstIterator<'a, TItem, SrcIterator, TgtIterator> where -SrcIterator: 'a+Iterator, -TgtIterator: 'a+Iterator, -TItem: 'a+HasBoundingBox, -TItem::Point: Coordinate2D { + SrcIterator: 'a + Iterator, + TgtIterator: 'a + Iterator, + TItem: 'a + HasBoundingBox, + TItem::Point: Coordinate2D, +{ type Item = (&'a TItem, &'a TItem); fn next(&mut self) -> Option { @@ -172,14 +187,18 @@ TItem::Point: Coordinate2D { } // Read a new target item. Target items determine the sweep position (we read things in order such that there'll be no collisions before this point) - let next_tgt = self.tgt_iterator.next(); - let next_tgt = if let Some(next_tgt) = next_tgt { next_tgt } else { return None }; + let next_tgt = self.tgt_iterator.next(); + let next_tgt = if let Some(next_tgt) = next_tgt { + next_tgt + } else { + return None; + }; let next_tgt_bounds = next_tgt.get_bounding_box::>(); // Sweep the source and target items - let tgt_min_x = next_tgt_bounds.min().x(); - let tgt_max_x = next_tgt_bounds.max().x(); + let tgt_min_x = next_tgt_bounds.min().x(); + let tgt_max_x = next_tgt_bounds.max().x(); while let Some((earliest_x, _item)) = self.src_by_max_x.last() { if earliest_x.max().x() >= tgt_min_x { @@ -192,32 +211,39 @@ TItem::Point: Coordinate2D { // Read source items and add them to the src list until we find one after the existing target loop { // Stop reading if we get a source item that can't overlap the current target item - if self.src_last_min_x > tgt_max_x { break; } + if self.src_last_min_x > tgt_max_x { + break; + } // Try to read the next source item - let next_src = if let Some(next_src) = self.src_iterator.as_mut().and_then(|iter| iter.next()) { + let next_src = if let Some(next_src) = + self.src_iterator.as_mut().and_then(|iter| iter.next()) + { next_src } else { self.src_iterator = None; - break; + break; }; // Add to the list of source items - let src_bounds = next_src.get_bounding_box::>(); - let src_min_x = src_bounds.min().x(); - let src_max_x = src_bounds.max().x(); - - let index = self.src_by_max_x.binary_search_by(|(bounds, _item)| { - let item_max_x = bounds.max().x(); - - if item_max_x > src_max_x { - Ordering::Less - } else if item_max_x == src_max_x { - Ordering::Equal - } else { - Ordering::Greater - } - }).unwrap_or_else(|idx| idx); + let src_bounds = next_src.get_bounding_box::>(); + let src_min_x = src_bounds.min().x(); + let src_max_x = src_bounds.max().x(); + + let index = self + .src_by_max_x + .binary_search_by(|(bounds, _item)| { + let item_max_x = bounds.max().x(); + + if item_max_x > src_max_x { + Ordering::Less + } else if item_max_x == src_max_x { + Ordering::Equal + } else { + Ordering::Greater + } + }) + .unwrap_or_else(|idx| idx); self.src_by_max_x.insert(index, (src_bounds, next_src)); diff --git a/src/lib.rs b/src/lib.rs index 45c73fe7..881babea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,33 +1,34 @@ -//! +//! //! flo_curves //! ========== //! -//! `flo_curves` is a library of routines for inspecting and manipulating curves, with a focus on cubic Bézier curves. In -//! this library, you'll find routines for computing points on curves, performing collision detection between curves and +//! `flo_curves` is a library of routines for inspecting and manipulating curves, with a focus on cubic Bézier curves. In +//! this library, you'll find routines for computing points on curves, performing collision detection between curves and //! lines or other curves, all the way up to routines for combining paths made up of multiple curves. -//! +//! //! Anyone doing any work with Bézier curves will likely find something in this library that is of use, but its range of //! functions makes it particularly useful for collision detection or performing path arithmetic. -//! +//! //! A set of curve and coordinate types are provided by the library, as well as a set of traits that can be implemented //! on any types with suitable properties. Implementing these traits makes it possible to add the extra features of this //! library to any existing code that has its own way of representing coordinates, curves or paths. -//! +//! //! `flo_curves` was built as a support library for `flowbetween`, an animation tool I'm working on. //! #![warn(bare_trait_objects)] -#[macro_use] mod test_assert; -mod consts; -pub mod bezier; -pub mod line; +#[macro_use] +mod test_assert; pub mod arc; +pub mod bezier; +mod consts; pub mod debug; +pub mod line; pub mod geo; pub use self::geo::*; -pub use self::bezier::BezierCurveFactory; pub use self::bezier::BezierCurve; +pub use self::bezier::BezierCurveFactory; pub use self::line::Line; diff --git a/src/line/coefficients.rs b/src/line/coefficients.rs index dd366526..9098fdc6 100644 --- a/src/line/coefficients.rs +++ b/src/line/coefficients.rs @@ -1,27 +1,29 @@ -use super::line::*; use super::super::geo::*; +use super::line::*; /// /// For a two-dimensional line, computes the coefficients of the line equation ax+by+c=0 -/// These coefficients are not normalized, which is slightly more efficient than computing the normalized form. -/// +/// These coefficients are not normalized, which is slightly more efficient than computing the normalized form. +/// /// This will return (0,0,0) for a line where the start and end point are the same. -/// -pub fn line_coefficients_2d_unnormalized(line: &L) -> (f64, f64, f64) -where L::Point: Coordinate+Coordinate2D { - // Compute the offset - let (from, to) = line.points(); - let offset = to - from; +/// +pub fn line_coefficients_2d_unnormalized(line: &L) -> (f64, f64, f64) +where + L::Point: Coordinate + Coordinate2D, +{ + // Compute the offset + let (from, to) = line.points(); + let offset = to - from; // Compute values for a, b, c - let (a, b, c) = if offset.x() == 0.0 && offset.y() == 0.0 { + let (a, b, c) = if offset.x() == 0.0 && offset.y() == 0.0 { // This is a point rather than a line return (0.0, 0.0, 0.0); } else if offset.x().abs() > offset.y().abs() { // Derive a, b, c from y = ax+c let a = offset.y() / offset.x(); let b = -1.0; - let c = -(a*from.x() + b*from.y()); + let c = -(a * from.x() + b * from.y()); if offset.x() > 0.0 { (-a, -b, -c) @@ -32,7 +34,7 @@ where L::Point: Coordinate+Coordinate2D { // Derive a, b, c from x = by+c let a = -1.0; let b = offset.x() / offset.y(); - let c = -(a*from.x() + b*from.y()); + let c = -(a * from.x() + b * from.y()); if offset.y() > 0.0 { (-a, -b, -c) @@ -45,19 +47,21 @@ where L::Point: Coordinate+Coordinate2D { } /// -/// For a two-dimensional line, computes the coefficients of the line equation ax+by+c=0, such that +/// For a two-dimensional line, computes the coefficients of the line equation ax+by+c=0, such that /// a^2+b^2 = 1. This normalized form means that `a*x + b*y + c` will return the distance that the /// point `x`, `y` is from the line. -/// +/// /// This will return (0,0,0) for a line where the start and end point are the same. -/// -pub fn line_coefficients_2d(line: &L) -> (f64, f64, f64) -where L::Point: Coordinate+Coordinate2D { +/// +pub fn line_coefficients_2d(line: &L) -> (f64, f64, f64) +where + L::Point: Coordinate + Coordinate2D, +{ let (a, b, c) = line_coefficients_2d_unnormalized(line); // Normalise so that a^2+b^2 = 1 - let factor = (a*a + b*b).sqrt(); - let (a, b, c) = (a/factor, b/factor, c/factor); + let factor = (a * a + b * b).sqrt(); + let (a, b, c) = (a / factor, b / factor, c / factor); (a, b, c) } diff --git a/src/line/intersection.rs b/src/line/intersection.rs index ef2c9d7c..464c54f3 100644 --- a/src/line/intersection.rs +++ b/src/line/intersection.rs @@ -1,5 +1,5 @@ -use super::line::*; use super::super::geo::*; +use super::line::*; /// Smallest divisor magnitude to use in ray_intersects_ray (the closer the divisor is to 0, the more close to parallel the lines are), so this /// determines the shallowest angle allowed between two lines before we consider them to be parallel. @@ -8,25 +8,29 @@ const RAY_DIVISOR_SMALLEST_VALUE: f64 = 2e-12; /// /// Returns the point at which two lines intersect (if they intersect) -/// +/// /// Only the 2-dimensional form is supported at the moment (lines are much less likely to intersect /// in higher dimensions) -/// -pub fn line_intersects_line(line1: &L, line2: &L) -> Option -where L::Point: Coordinate2D { +/// +pub fn line_intersects_line(line1: &L, line2: &L) -> Option +where + L::Point: Coordinate2D, +{ let line1_points = line1.points(); let line2_points = line2.points(); let ((x1, y1), (x2, y2)) = (line1_points.0.coords(), line1_points.1.coords()); let ((x3, y3), (x4, y4)) = (line2_points.0.coords(), line2_points.1.coords()); - let ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); - let ub = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) + / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) + / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); if ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0 { Some(L::Point::from_components(&[ - x1+(ua*(x2-x1)), - y1+(ua*(y2-y1)) + x1 + (ua * (x2 - x1)), + y1 + (ua * (y2 - y1)), ])) } else { None @@ -36,24 +40,27 @@ where L::Point: Coordinate2D { /// /// Returns the point at which a line and a ray intersect (if they intersect). The ray is assumed to be /// infinitely long, but the line is not. -/// +/// /// Only the 2-dimensional form is supported at the moment (lines are much less likely to intersect /// in higher dimensions) -/// -pub fn line_intersects_ray(line: &L, ray: &L) -> Option -where L::Point: Coordinate2D { +/// +pub fn line_intersects_ray(line: &L, ray: &L) -> Option +where + L::Point: Coordinate2D, +{ let line_points = line.points(); - let ray_points = ray.points(); + let ray_points = ray.points(); let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords()); let ((x3, y3), (x4, y4)) = (ray_points.0.coords(), ray_points.1.coords()); - let ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) + / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); if ua >= 0.0 && ua <= 1.0 { Some(L::Point::from_components(&[ - x1+(ua*(x2-x1)), - y1+(ua*(y2-y1)) + x1 + (ua * (x2 - x1)), + y1 + (ua * (y2 - y1)), ])) } else { None @@ -62,26 +69,28 @@ where L::Point: Coordinate2D { /// /// Returns the point at which two rays intersect (if they intersect). Rays are infinitely long. -/// +/// /// Only the 2-dimensional form is supported at the moment (lines are much less likely to intersect /// in higher dimensions) -/// -pub fn ray_intersects_ray(line: &L, ray: &L) -> Option -where L::Point: Coordinate2D { +/// +pub fn ray_intersects_ray(line: &L, ray: &L) -> Option +where + L::Point: Coordinate2D, +{ let line_points = line.points(); - let ray_points = ray.points(); + let ray_points = ray.points(); - let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords()); - let ((x3, y3), (x4, y4)) = (ray_points.0.coords(), ray_points.1.coords()); + let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords()); + let ((x3, y3), (x4, y4)) = (ray_points.0.coords(), ray_points.1.coords()); - let divisor = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1); + let divisor = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); if divisor.abs() > RAY_DIVISOR_SMALLEST_VALUE { - let ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / divisor; + let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / divisor; Some(L::Point::from_components(&[ - x1+(ua*(x2-x1)), - y1+(ua*(y2-y1)) + x1 + (ua * (x2 - x1)), + y1 + (ua * (y2 - y1)), ])) } else { None @@ -90,26 +99,34 @@ where L::Point: Coordinate2D { /// /// Determines if a 2D line has intersected a bounding box (and returns the intersection if it exists) -/// +/// pub fn line_clip_to_bounds(line: &L, bounds: &(L::Point, L::Point)) -> Option -where L::Point: Coordinate2D { +where + L::Point: Coordinate2D, +{ // Fetch the points for the line - let line_points = line.points(); - let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords()); - let (dx, dy) = (x2-x1, y2-y1); + let line_points = line.points(); + let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords()); + let (dx, dy) = (x2 - x1, y2 - y1); // ... and the points for the bounding rectangle - let (xmin, ymin) = (bounds.0.x().min(bounds.1.x()), bounds.0.y().min(bounds.1.y())); - let (xmax, ymax) = (bounds.0.x().max(bounds.1.x()), bounds.0.y().max(bounds.1.y())); + let (xmin, ymin) = ( + bounds.0.x().min(bounds.1.x()), + bounds.0.y().min(bounds.1.y()), + ); + let (xmax, ymax) = ( + bounds.0.x().max(bounds.1.x()), + bounds.0.y().max(bounds.1.y()), + ); // Our line can be described as '(x1+t*dx, y1+t*dy)' where 0 <= t <= 1 // We want to solve for the edges, eg (xmin=x1+tmin*dx => txmin=(xmin-x1)/dx) - let delta = [-dx, dx, -dy, dy]; - let edge = [x1-xmin, xmax-x1, y1-ymin, ymax-y1]; + let delta = [-dx, dx, -dy, dy]; + let edge = [x1 - xmin, xmax - x1, y1 - ymin, ymax - y1]; // t1 and t2 are the points on the line. Initially they describe the entire line (t=0 -> t=1) - let mut t1 = 0.0; - let mut t2 = 1.0; + let mut t1 = 0.0; + let mut t2 = 1.0; // Clip against each of the 4 edges in turn for (delta, edge) in delta.iter().zip(edge.iter()) { @@ -121,7 +138,7 @@ where L::Point: Coordinate2D { } } else { // Compute the 't' value where the line intersects this edge - let t = edge/delta; + let t = edge / delta; if delta < &0.0 && t1 < t { // Start of the line is clipped against this edge (if the delta value is <0 the start is closer to this edge) @@ -138,8 +155,8 @@ where L::Point: Coordinate2D { None } else { // Line intersects the rectangle. t1 and t2 indicate where - let p1 = L::Point::from_components(&[x1 + t1*dx, y1 + t1*dy]); - let p2 = L::Point::from_components(&[x1 + t2*dx, y1 + t2*dy]); + let p1 = L::Point::from_components(&[x1 + t1 * dx, y1 + t1 * dy]); + let p2 = L::Point::from_components(&[x1 + t2 * dx, y1 + t2 * dy]); Some(L::from_points(p1, p2)) } diff --git a/src/line/line.rs b/src/line/line.rs index d7eb0b52..7b7a3940 100644 --- a/src/line/line.rs +++ b/src/line/line.rs @@ -1,47 +1,47 @@ -use super::coefficients::*; use super::super::geo::*; +use super::coefficients::*; /// /// Represents a straight line -/// -pub trait Line : Geo { +/// +pub trait Line: Geo { /// /// Creates a new line from points - /// + /// fn from_points(p1: Self::Point, p2: Self::Point) -> Self; /// /// Returns the two points that mark the start and end of this line - /// + /// fn points(&self) -> (Self::Point, Self::Point); /// /// Given a value 't' from 0 to 1, returns the point at that position along the line - /// + /// fn point_at_pos(&self, t: f64) -> Self::Point { - let (p1, p2) = self.points(); - let delta = p2-p1; + let (p1, p2) = self.points(); + let delta = p2 - p1; - p1 + delta*t + p1 + delta * t } /// /// Given a point (assumed to be on the line), returns the 't' value on the line - /// + /// /// If the point is not on the line, this will return a t value where at least one of the components of the point matches with /// the point on the line. /// fn pos_for_point(&self, point: &Self::Point) -> f64 { - let (p1, p2) = self.points(); - let delta_line = p2-p1; - let delta_point = *point-p1; + let (p1, p2) = self.points(); + let delta_line = p2 - p1; + let delta_point = *point - p1; for component_idx in 0..Self::Point::len() { - let line_component = delta_line.get(component_idx); + let line_component = delta_line.get(component_idx); let point_component = delta_point.get(component_idx); if line_component.abs() > 0.000001 && point_component.abs() > 0.000001 { - return point_component/line_component; + return point_component / line_component; } } @@ -52,14 +52,14 @@ pub trait Line : Geo { /// /// Trait implemented by a 2D line -/// +/// pub trait Line2D { - type Point: Coordinate+Coordinate2D; + type Point: Coordinate + Coordinate2D; /// /// Returns the coefficients (a, b, c) for this line, such that ax+by+c = 0 for /// any point on the line and also such that a^2 + b^2 = 1 - /// + /// fn coefficients(&self) -> (f64, f64, f64); /// @@ -68,7 +68,7 @@ pub trait Line2D { /// Note that this will project the line to infinity so this can return a distance to a point outside of the start or end point /// of the line. To determine if this has occurred, the `pos_for_point()` call can be used to determine the `t` value for the /// closest point: it will return a value in the range `0.0..1.0` if the closest point is within the line. - /// + /// fn distance_to(&self, p: &Self::Point) -> f64; /// @@ -77,14 +77,14 @@ pub trait Line2D { fn which_side(&self, p: &Self::Point) -> i8; } -impl Geo for (Point, Point) { +impl Geo for (Point, Point) { type Point = Point; } /// /// Simplest line is just a tuple of two points -/// -impl Line for (Point, Point) { +/// +impl Line for (Point, Point) { /// /// Creates a new line from points /// @@ -95,20 +95,20 @@ impl Line for (Point, Point) { /// /// Returns the two points that mark the start and end of this line - /// + /// #[inline] fn points(&self) -> (Self::Point, Self::Point) { self.clone() } } -impl> Line2D for L { - type Point=Point; +impl> Line2D for L { + type Point = Point; /// /// Returns the coefficients (a, b, c) for this line, such that ax+by+c = 0 for /// any point on the line and also such that a^2 + b^2 = 1 - /// + /// #[inline] fn coefficients(&self) -> (f64, f64, f64) { line_coefficients_2d(self) @@ -116,12 +116,12 @@ impl> Line2D for L { /// /// Returns the distance from a point to this line - /// + /// #[inline] fn distance_to(&self, p: &Self::Point) -> f64 { let (a, b, c) = self.coefficients(); - a*p.x() + b*p.y() + c + a * p.x() + b * p.y() + c } /// @@ -131,7 +131,9 @@ impl> Line2D for L { fn which_side(&self, p: &Self::Point) -> i8 { let (start, end) = self.points(); - let side = ((p.x()-start.x())*(end.y()-start.y()) - (p.y()-start.y())*(end.x()-start.x())).signum(); + let side = ((p.x() - start.x()) * (end.y() - start.y()) + - (p.y() - start.y()) * (end.x() - start.x())) + .signum(); if side < 0.0 { -1 diff --git a/src/line/mod.rs b/src/line/mod.rs index 7b276444..9863f07f 100644 --- a/src/line/mod.rs +++ b/src/line/mod.rs @@ -1,22 +1,22 @@ //! //! # Manipulating and describing lines -//! +//! //! While `flo_curves` deals mostly with curves, it also supplies a small library of functions for manipulating //! lines. The `Line` trait can be implemented on other types that define lines, enabling them to be used anywhere //! the library needs to perform an operation on a line. -//! +//! //! The basic line type is simply a tuple of two points (that is, any tuple of two values of the same type that //! implements `Coordinate`). //! +mod coefficients; +mod intersection; mod line; mod to_curve; -mod intersection; -mod coefficients; -pub use self::line::*; -pub use self::to_curve::*; pub use self::coefficients::*; pub use self::intersection::*; +pub use self::line::*; +pub use self::to_curve::*; pub use super::geo::*; diff --git a/src/line/to_curve.rs b/src/line/to_curve.rs index 92b01bbd..07200afe 100644 --- a/src/line/to_curve.rs +++ b/src/line/to_curve.rs @@ -1,13 +1,16 @@ -use super::line::*; use super::super::bezier::*; +use super::line::*; /// /// Changes a line to a bezier curve -/// -pub fn line_to_bezier>(line: &L) -> Curve { - let points = line.points(); - let point_distance = points.1 - points.0; - let (cp1, cp2) = (points.0 + point_distance*0.3333, points.0 + point_distance*0.6666); +/// +pub fn line_to_bezier>(line: &L) -> Curve { + let points = line.points(); + let point_distance = points.1 - points.0; + let (cp1, cp2) = ( + points.0 + point_distance * 0.3333, + points.0 + point_distance * 0.6666, + ); Curve::from_points(points.0, (cp1, cp2), points.1) } diff --git a/src/test_assert.rs b/src/test_assert.rs index 5346ba99..d7198866 100644 --- a/src/test_assert.rs +++ b/src/test_assert.rs @@ -1,9 +1,8 @@ - #[cfg(not(any(test, extra_checks)))] macro_rules! test_assert { - ($cond:expr) => ({ }); - ($cond:expr,) => ({ }); - ($cond:expr, $($arg:tt)+) => ({ }); + ($cond:expr) => {{}}; + ($cond:expr,) => {{}}; + ($cond:expr, $($arg:tt)+) => {{}}; } #[cfg(any(test, extra_checks))] diff --git a/tests/bezier/algorithms/fill_concave.rs b/tests/bezier/algorithms/fill_concave.rs index 40952dc1..f93511a2 100644 --- a/tests/bezier/algorithms/fill_concave.rs +++ b/tests/bezier/algorithms/fill_concave.rs @@ -1,114 +1,124 @@ -use flo_curves::*; -use flo_curves::bezier::*; -use flo_curves::bezier::path::*; use flo_curves::bezier::path::algorithms::*; +use flo_curves::bezier::path::*; +use flo_curves::bezier::*; +use flo_curves::*; -fn circle_ray_cast(circle_center: Coord2, radius: f64) -> impl Fn(Coord2, Coord2) -> Vec> { +fn circle_ray_cast( + circle_center: Coord2, + radius: f64, +) -> impl Fn(Coord2, Coord2) -> Vec> { move |from: Coord2, to: Coord2| { - let from = from - circle_center; - let to = to - circle_center; + let from = from - circle_center; + let to = to - circle_center; - let x1 = from.x(); - let y1 = from.y(); - let x2 = to.x(); - let y2 = to.y(); + let x1 = from.x(); + let y1 = from.y(); + let x2 = to.x(); + let y2 = to.y(); - let dx = x2-x1; - let dy = y2-y1; - let dr = (dx*dx + dy*dy).sqrt(); + let dx = x2 - x1; + let dy = y2 - y1; + let dr = (dx * dx + dy * dy).sqrt(); - let d = x1*y2 - x2*y1; + let d = x1 * y2 - x2 * y1; - let xc1 = (d*dy + (dy.signum()*dx*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); - let xc2 = (d*dy - (dy.signum()*dx*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); - let yc1 = (-d*dx + (dy.abs()*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); - let yc2 = (-d*dx - (dy.abs()*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); + let xc1 = (d * dy + (dy.signum() * dx * ((radius * radius * dr * dr - d * d).sqrt()))) + / (dr * dr); + let xc2 = (d * dy - (dy.signum() * dx * ((radius * radius * dr * dr - d * d).sqrt()))) + / (dr * dr); + let yc1 = (-d * dx + (dy.abs() * ((radius * radius * dr * dr - d * d).sqrt()))) / (dr * dr); + let yc2 = (-d * dx - (dy.abs() * ((radius * radius * dr * dr - d * d).sqrt()))) / (dr * dr); vec![ - RayCollision::new(Coord2(xc1, yc1)+circle_center, ()), RayCollision::new(Coord2(xc2, yc2)+circle_center, ()) + RayCollision::new(Coord2(xc1, yc1) + circle_center, ()), + RayCollision::new(Coord2(xc2, yc2) + circle_center, ()), ] } } #[test] fn ray_cast_to_circle_at_origin() { - let ray_cast = circle_ray_cast(Coord2(0.0, 0.0), 5.0); + let ray_cast = circle_ray_cast(Coord2(0.0, 0.0), 5.0); let from_center = ray_cast(Coord2(0.0, 0.0), Coord2(1.0, 1.0)); assert!(from_center.len() == 2); - assert!((from_center[0].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); - assert!((from_center[1].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); + assert!((from_center[0].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); + assert!((from_center[1].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); assert!(from_center[0].position.distance_to(&Coord2(3.54, 3.54)) < 0.1); assert!(from_center[1].position.distance_to(&Coord2(-3.54, -3.54)) < 0.1); let offset = ray_cast(Coord2(1.0, 1.0), Coord2(2.0, 2.0)); assert!(offset.len() == 2); - assert!((offset[0].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); - assert!((offset[1].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); + assert!((offset[0].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); + assert!((offset[1].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); assert!(offset[0].position.distance_to(&Coord2(3.54, 3.54)) < 0.1); assert!(offset[1].position.distance_to(&Coord2(-3.54, -3.54)) < 0.1); let offset2 = ray_cast(Coord2(1.0, 1.0), Coord2(1.0, 2.0)); assert!(offset2.len() == 2); - assert!((offset2[0].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); - assert!((offset2[1].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); + assert!((offset2[0].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); + assert!((offset2[1].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); let offset3 = ray_cast(Coord2(1.0, 1.0), Coord2(2.0, 1.0)); assert!(offset3.len() == 2); - assert!((offset3[0].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); - assert!((offset3[1].position.distance_to(&Coord2(0.0, 0.0))-5.0).abs() < 0.1); + assert!((offset3[0].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); + assert!((offset3[1].position.distance_to(&Coord2(0.0, 0.0)) - 5.0).abs() < 0.1); } #[test] fn ray_cast_to_circle() { - let ray_cast = circle_ray_cast(Coord2(10.0, 10.0), 5.0); + let ray_cast = circle_ray_cast(Coord2(10.0, 10.0), 5.0); let from_center = ray_cast(Coord2(10.0, 10.0), Coord2(11.0, 11.0)); assert!(from_center.len() == 2); - assert!((from_center[0].position.distance_to(&Coord2(10.0, 10.0))-5.0).abs() < 0.1); - assert!((from_center[1].position.distance_to(&Coord2(10.0, 10.0))-5.0).abs() < 0.1); + assert!((from_center[0].position.distance_to(&Coord2(10.0, 10.0)) - 5.0).abs() < 0.1); + assert!((from_center[1].position.distance_to(&Coord2(10.0, 10.0)) - 5.0).abs() < 0.1); assert!(from_center[0].position.distance_to(&Coord2(13.54, 13.54)) < 0.1); assert!(from_center[1].position.distance_to(&Coord2(6.46, 6.46)) < 0.1); let offset = ray_cast(Coord2(11.0, 11.0), Coord2(12.0, 12.0)); assert!(offset.len() == 2); - assert!((offset[0].position.distance_to(&Coord2(10.0, 10.0))-5.0).abs() < 0.1); - assert!((offset[1].position.distance_to(&Coord2(10.0, 10.0))-5.0).abs() < 0.1); + assert!((offset[0].position.distance_to(&Coord2(10.0, 10.0)) - 5.0).abs() < 0.1); + assert!((offset[1].position.distance_to(&Coord2(10.0, 10.0)) - 5.0).abs() < 0.1); assert!(offset[0].position.distance_to(&Coord2(13.54, 13.54)) < 0.1); assert!(offset[1].position.distance_to(&Coord2(6.46, 6.46)) < 0.1); let offset2 = ray_cast(Coord2(11.0, 11.0), Coord2(12.0, 11.0)); assert!(offset2.len() == 2); - assert!((offset2[0].position.distance_to(&Coord2(10.0, 10.0))-5.0).abs() < 0.1); - assert!((offset2[1].position.distance_to(&Coord2(10.0, 10.0))-5.0).abs() < 0.1); + assert!((offset2[0].position.distance_to(&Coord2(10.0, 10.0)) - 5.0).abs() < 0.1); + assert!((offset2[1].position.distance_to(&Coord2(10.0, 10.0)) - 5.0).abs() < 0.1); } #[test] fn fill_concave_circle() { // Simple circle ray-casting algorithm - let circle_center = Coord2(10.0, 10.0); - let radius = 50.0; + let circle_center = Coord2(10.0, 10.0); + let radius = 50.0; let circle_ray_cast = circle_ray_cast(circle_center, radius); // Flood-fill this curve - let path = flood_fill_concave::(circle_center, &FillSettings::default(), circle_ray_cast); + let path = flood_fill_concave::( + circle_center, + &FillSettings::default(), + circle_ray_cast, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() == 1); for curve in path.unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-radius).abs() < 1.0); + assert!((distance - radius).abs() < 1.0); } } } @@ -116,22 +126,26 @@ fn fill_concave_circle() { #[test] fn fill_concave_circle_offset() { // Simple circle ray-casting algorithm - let circle_center = Coord2(10.0, 10.0); - let radius = 50.0; + let circle_center = Coord2(10.0, 10.0); + let radius = 50.0; let circle_ray_cast = circle_ray_cast(circle_center, radius); // Flood-fill this curve - let path = flood_fill_concave::(circle_center + Coord2(1.0, 0.0), &FillSettings::default(), circle_ray_cast); + let path = flood_fill_concave::( + circle_center + Coord2(1.0, 0.0), + &FillSettings::default(), + circle_ray_cast, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() == 1); for curve in path.unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-radius).abs() < 1.0); + assert!((distance - radius).abs() < 1.0); } } } @@ -139,19 +153,24 @@ fn fill_concave_circle_offset() { #[test] fn fill_concave_doughnut() { // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled - let circle_center = Coord2(10.0, 10.0); - let outer_radius = 100.0; - let inner_radius = 50.0; - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let inner_circle = circle_ray_cast(circle_center, inner_radius); - let doughnut = |from: Coord2, to: Coord2| { - outer_circle(from.clone(), to.clone()).into_iter() + let circle_center = Coord2(10.0, 10.0); + let outer_radius = 100.0; + let inner_radius = 50.0; + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let inner_circle = circle_ray_cast(circle_center, inner_radius); + let doughnut = |from: Coord2, to: Coord2| { + outer_circle(from.clone(), to.clone()) + .into_iter() .chain(inner_circle(from, to)) }; // Flood-fill this curve - let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); - let path = flood_fill_concave::(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); + let path = flood_fill_concave::( + start_point, + &FillSettings::default(), + doughnut, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() != 0); @@ -160,50 +179,52 @@ fn fill_concave_doughnut() { for curve in path.as_ref().unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-outer_radius).abs() < 2.0); + assert!((distance - outer_radius).abs() < 2.0); } } for curve in path.unwrap()[1].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-inner_radius).abs() < 2.0); + assert!((distance - inner_radius).abs() < 2.0); } } } #[test] fn fill_doughnut_with_extra_holes() { - // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled - let circle_center = Coord2(10.0, 10.0); - let outer_radius = 100.0; - let inner_radius = 50.0; - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let inner_circle = circle_ray_cast(circle_center, inner_radius); - let doughnut = |from: Coord2, to: Coord2| { + // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled + let circle_center = Coord2(10.0, 10.0); + let outer_radius = 100.0; + let inner_radius = 50.0; + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let inner_circle = circle_ray_cast(circle_center, inner_radius); + let doughnut = |from: Coord2, to: Coord2| { let inner_collisions = inner_circle(from.clone(), to.clone()); let outer_collisions = outer_circle(from.clone(), to.clone()); - let ray = to-from; - if (ray.x()/ray.y()).abs() < 0.1 || (ray.y()/ray.x()) < 0.1 { + let ray = to - from; + if (ray.x() / ray.y()).abs() < 0.1 || (ray.y() / ray.x()) < 0.1 { // Just the inner collisions (leave holes in the collision list) - inner_collisions.into_iter() - .chain(vec![]) + inner_collisions.into_iter().chain(vec![]) } else { // All the collisions - inner_collisions.into_iter() - .chain(outer_collisions) + inner_collisions.into_iter().chain(outer_collisions) } }; // Flood-fill this curve - let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); - let path = flood_fill_concave::(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); + let path = flood_fill_concave::( + start_point, + &FillSettings::default(), + doughnut, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() != 0); @@ -212,50 +233,52 @@ fn fill_doughnut_with_extra_holes() { for curve in path.as_ref().unwrap()[1].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-outer_radius).abs() < 5.0); + assert!((distance - outer_radius).abs() < 5.0); } } for curve in path.unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-inner_radius).abs() < 2.0); + assert!((distance - inner_radius).abs() < 2.0); } } } #[test] fn fill_circle_without_escaping_gaps() { - // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled - let circle_center = Coord2(10.0, 10.0); - let enclosing_radius = 200.0; - let outer_radius = 100.0; - let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let doughnut = |from: Coord2, to: Coord2| { - let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); - - let ray = to-from; - if (ray.x()/ray.y()).abs() < 0.01 { + // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled + let circle_center = Coord2(10.0, 10.0); + let enclosing_radius = 200.0; + let outer_radius = 100.0; + let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let doughnut = |from: Coord2, to: Coord2| { + let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); + let outer_collisions = outer_circle(from.clone(), to.clone()); + + let ray = to - from; + if (ray.x() / ray.y()).abs() < 0.01 { // Just the inner collisions (leave holes in the collision list) - enclosing_collisions.into_iter() - .chain(vec![]) + enclosing_collisions.into_iter().chain(vec![]) } else { // All the collisions - enclosing_collisions.into_iter() - .chain(outer_collisions) + enclosing_collisions.into_iter().chain(outer_collisions) } }; // Flood-fill this curve - let start_point = circle_center; - let path = flood_fill_concave::(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center; + let path = flood_fill_concave::( + start_point, + &FillSettings::default(), + doughnut, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() != 0); @@ -263,42 +286,43 @@ fn fill_circle_without_escaping_gaps() { for curve in path.as_ref().unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-outer_radius).abs() < 5.0); + assert!((distance - outer_radius).abs() < 5.0); } } } - #[test] fn fill_circle_without_escaping_gaps_offset() { - // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled - let circle_center = Coord2(10.0, 10.0); - let enclosing_radius = 200.0; - let outer_radius = 100.0; - let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let doughnut = |from: Coord2, to: Coord2| { - let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); - - let ray = to-from; - if (ray.x()/ray.y()).abs() < 0.01 { + // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled + let circle_center = Coord2(10.0, 10.0); + let enclosing_radius = 200.0; + let outer_radius = 100.0; + let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let doughnut = |from: Coord2, to: Coord2| { + let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); + let outer_collisions = outer_circle(from.clone(), to.clone()); + + let ray = to - from; + if (ray.x() / ray.y()).abs() < 0.01 { // Just the inner collisions (leave holes in the collision list) - enclosing_collisions.into_iter() - .chain(vec![]) + enclosing_collisions.into_iter().chain(vec![]) } else { // All the collisions - enclosing_collisions.into_iter() - .chain(outer_collisions) + enclosing_collisions.into_iter().chain(outer_collisions) } }; // Flood-fill this curve - let start_point = circle_center + Coord2(50.0, 70.0); - let path = flood_fill_concave::(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center + Coord2(50.0, 70.0); + let path = flood_fill_concave::( + start_point, + &FillSettings::default(), + doughnut, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() != 0); @@ -306,46 +330,52 @@ fn fill_circle_without_escaping_gaps_offset() { for curve in path.as_ref().unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-outer_radius).abs() < 5.0); + assert!((distance - outer_radius).abs() < 5.0); } } } #[test] fn fill_doughnut_without_escaping_gaps() { - // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled - let circle_center = Coord2(10.0, 10.0); - let enclosing_radius = 200.0; - let outer_radius = 100.0; - let inner_radius = 50.0; - let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let inner_circle = circle_ray_cast(circle_center, inner_radius); - let doughnut = |from: Coord2, to: Coord2| { - let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); - let inner_collisions = inner_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); - - let ray = to-from; - if (ray.x()/ray.y()).abs() < 0.01 { + // A 'doughnut' shape is one of the harder shapes to fill in this manner as eventually we'll have to raycast over areas we've already filled + let circle_center = Coord2(10.0, 10.0); + let enclosing_radius = 200.0; + let outer_radius = 100.0; + let inner_radius = 50.0; + let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let inner_circle = circle_ray_cast(circle_center, inner_radius); + let doughnut = |from: Coord2, to: Coord2| { + let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); + let inner_collisions = inner_circle(from.clone(), to.clone()); + let outer_collisions = outer_circle(from.clone(), to.clone()); + + let ray = to - from; + if (ray.x() / ray.y()).abs() < 0.01 { // Just the inner collisions (leave holes in the collision list) - inner_collisions.into_iter() + inner_collisions + .into_iter() .chain(enclosing_collisions) .chain(vec![]) } else { // All the collisions - inner_collisions.into_iter() + inner_collisions + .into_iter() .chain(enclosing_collisions) .chain(outer_collisions) } }; // Flood-fill this curve - let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); - let path = flood_fill_concave::(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); + let path = flood_fill_concave::( + start_point, + &FillSettings::default(), + doughnut, + ); assert!(path.is_some()); assert!(path.as_ref().unwrap().len() != 0); @@ -354,19 +384,19 @@ fn fill_doughnut_without_escaping_gaps() { for curve in path.as_ref().unwrap()[0].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-outer_radius).abs() < 5.0); + assert!((distance - outer_radius).abs() < 5.0); } } for curve in path.unwrap()[1].to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-inner_radius).abs() < 2.0); + assert!((distance - inner_radius).abs() < 2.0); } } } diff --git a/tests/bezier/algorithms/fill_convex.rs b/tests/bezier/algorithms/fill_convex.rs index 190d002c..2eb83132 100644 --- a/tests/bezier/algorithms/fill_convex.rs +++ b/tests/bezier/algorithms/fill_convex.rs @@ -1,31 +1,37 @@ -use flo_curves::*; -use flo_curves::bezier::*; -use flo_curves::bezier::path::*; use flo_curves::bezier::path::algorithms::*; +use flo_curves::bezier::path::*; +use flo_curves::bezier::*; +use flo_curves::*; -fn circle_ray_cast(circle_center: Coord2, radius: f64) -> impl Fn(Coord2, Coord2) -> Vec> { +fn circle_ray_cast( + circle_center: Coord2, + radius: f64, +) -> impl Fn(Coord2, Coord2) -> Vec> { move |from: Coord2, to: Coord2| { - let from = from - circle_center; - let to = to - circle_center; + let from = from - circle_center; + let to = to - circle_center; - let x1 = from.x(); - let y1 = from.y(); - let x2 = to.x(); - let y2 = to.y(); + let x1 = from.x(); + let y1 = from.y(); + let x2 = to.x(); + let y2 = to.y(); - let dx = x2-x1; - let dy = y2-y1; - let dr = (dx*dx + dy*dy).sqrt(); + let dx = x2 - x1; + let dy = y2 - y1; + let dr = (dx * dx + dy * dy).sqrt(); - let d = x1*y2 - x2*y1; + let d = x1 * y2 - x2 * y1; - let xc1 = (d*dy + (dy.signum()*dx*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); - let xc2 = (d*dy - (dy.signum()*dx*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); - let yc1 = (-d*dx + (dy.abs()*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); - let yc2 = (-d*dx - (dy.abs()*((radius*radius*dr*dr - d*d).sqrt())))/(dr*dr); + let xc1 = (d * dy + (dy.signum() * dx * ((radius * radius * dr * dr - d * d).sqrt()))) + / (dr * dr); + let xc2 = (d * dy - (dy.signum() * dx * ((radius * radius * dr * dr - d * d).sqrt()))) + / (dr * dr); + let yc1 = (-d * dx + (dy.abs() * ((radius * radius * dr * dr - d * d).sqrt()))) / (dr * dr); + let yc2 = (-d * dx - (dy.abs() * ((radius * radius * dr * dr - d * d).sqrt()))) / (dr * dr); vec![ - RayCollision::new(Coord2(xc1, yc1)+circle_center, ()), RayCollision::new(Coord2(xc2, yc2)+circle_center, ()) + RayCollision::new(Coord2(xc1, yc1) + circle_center, ()), + RayCollision::new(Coord2(xc2, yc2) + circle_center, ()), ] } } @@ -33,8 +39,8 @@ fn circle_ray_cast(circle_center: Coord2, radius: f64) -> impl Fn(Coord2, Coord2 #[test] fn trace_convex_circle() { // Simple circle ray-casting algorithm - let circle_center = Coord2(10.0, 10.0); - let radius = 100.0; + let circle_center = Coord2(10.0, 10.0); + let radius = 100.0; let circle_ray_cast = circle_ray_cast(circle_center, radius); // Trace the outline @@ -45,11 +51,15 @@ fn trace_convex_circle() { // Points should be no more that 4.0 pixels apart and should be the correct distance from the circle for point_idx in 0..outline.len() { - let next_point_idx = if point_idx+1 >= outline.len() { 0 } else { point_idx+1 }; - let point = &outline[point_idx]; - let next_point = &outline[next_point_idx]; - - assert!((point.position.distance_to(&circle_center)-radius).abs() < 1.0); + let next_point_idx = if point_idx + 1 >= outline.len() { + 0 + } else { + point_idx + 1 + }; + let point = &outline[point_idx]; + let next_point = &outline[next_point_idx]; + + assert!((point.position.distance_to(&circle_center) - radius).abs() < 1.0); assert!(point.position.distance_to(&next_point.position) <= 4.0); } @@ -59,21 +69,22 @@ fn trace_convex_circle() { #[test] fn fill_convex_circle() { // Simple circle ray-casting algorithm - let circle_center = Coord2(10.0, 10.0); - let radius = 100.0; + let circle_center = Coord2(10.0, 10.0); + let radius = 100.0; let circle_ray_cast = circle_ray_cast(circle_center, radius); // Flood-fill this curve - let path: Option = flood_fill_convex(circle_center, &FillSettings::default(), circle_ray_cast); + let path: Option = + flood_fill_convex(circle_center, &FillSettings::default(), circle_ray_cast); assert!(path.is_some()); for curve in path.unwrap().to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); - assert!((distance-radius).abs() < 1.0); + assert!((distance - radius).abs() < 1.0); } } } @@ -81,28 +92,31 @@ fn fill_convex_circle() { #[test] fn trace_convex_doughnut() { // With a convex fill, a 'doughnut' shape will only fill those points that are immediately reachable from the origin point - let circle_center = Coord2(10.0, 10.0); - let outer_radius = 100.0; - let inner_radius = 50.0; - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let inner_circle = circle_ray_cast(circle_center, inner_radius); - let doughnut = |from: Coord2, to: Coord2| { - outer_circle(from.clone(), to.clone()).into_iter() + let circle_center = Coord2(10.0, 10.0); + let outer_radius = 100.0; + let inner_radius = 50.0; + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let inner_circle = circle_ray_cast(circle_center, inner_radius); + let doughnut = |from: Coord2, to: Coord2| { + outer_circle(from.clone(), to.clone()) + .into_iter() .chain(inner_circle(from, to)) }; // Trace the outline - let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); - let outline = trace_outline_convex(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); + let outline = trace_outline_convex(start_point, &FillSettings::default(), doughnut); // Should be at least one point assert!(outline.len() > 0); for point_idx in 0..outline.len() { - let point = &outline[point_idx]; + let point = &outline[point_idx]; - assert!((point.position.distance_to(&circle_center)-outer_radius).abs() < 1.0 - || (point.position.distance_to(&circle_center)-inner_radius).abs() < 1.0); + assert!( + (point.position.distance_to(&circle_center) - outer_radius).abs() < 1.0 + || (point.position.distance_to(&circle_center) - inner_radius).abs() < 1.0 + ); } assert!(outline.len() > 8); @@ -111,26 +125,31 @@ fn trace_convex_doughnut() { #[test] fn fill_convex_doughnut() { // With a convex fill, a 'doughnut' shape will only fill those points that are immediately reachable from the origin point - let circle_center = Coord2(10.0, 10.0); - let outer_radius = 100.0; - let inner_radius = 50.0; - let outer_circle = circle_ray_cast(circle_center, outer_radius); - let inner_circle = circle_ray_cast(circle_center, inner_radius); - let doughnut = |from: Coord2, to: Coord2| { - outer_circle(from.clone(), to.clone()).into_iter() + let circle_center = Coord2(10.0, 10.0); + let outer_radius = 100.0; + let inner_radius = 50.0; + let outer_circle = circle_ray_cast(circle_center, outer_radius); + let inner_circle = circle_ray_cast(circle_center, inner_radius); + let doughnut = |from: Coord2, to: Coord2| { + outer_circle(from.clone(), to.clone()) + .into_iter() .chain(inner_circle(from, to)) }; // Flood-fill this curve - let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); - let path = flood_fill_convex::(start_point, &FillSettings::default(), doughnut); + let start_point = circle_center + Coord2(inner_radius + 10.0, 0.0); + let path = flood_fill_convex::( + start_point, + &FillSettings::default(), + doughnut, + ); assert!(path.is_some()); for curve in path.as_ref().unwrap().to_curves::>() { for t in 0..100 { - let t = (t as f64)/100.0; - let distance = circle_center.distance_to(&curve.point_at_pos(t)); + let t = (t as f64) / 100.0; + let distance = circle_center.distance_to(&curve.point_at_pos(t)); assert!(distance >= inner_radius - 2.0 && distance <= outer_radius + 2.0) } diff --git a/tests/bezier/algorithms/mod.rs b/tests/bezier/algorithms/mod.rs index 00e29f4e..a9a1a994 100644 --- a/tests/bezier/algorithms/mod.rs +++ b/tests/bezier/algorithms/mod.rs @@ -1,2 +1,2 @@ -mod fill_convex; mod fill_concave; +mod fill_convex; diff --git a/tests/bezier/basis.rs b/tests/bezier/basis.rs index a6fc4dad..d5f63f69 100644 --- a/tests/bezier/basis.rs +++ b/tests/bezier/basis.rs @@ -14,10 +14,10 @@ fn basis_at_t1_is_w4() { #[test] fn basis_agrees_with_de_casteljau() { for x in 0..100 { - let t = (x as f64)/100.0; + let t = (x as f64) / 100.0; - let basis = bezier::basis(t, 2.0, 3.0, 4.0, 5.0); - let de_casteljau = bezier::de_casteljau4(t, 2.0, 3.0, 4.0, 5.0); + let basis = bezier::basis(t, 2.0, 3.0, 4.0, 5.0); + let de_casteljau = bezier::de_casteljau4(t, 2.0, 3.0, 4.0, 5.0); assert!(approx_equal(basis, de_casteljau)); } diff --git a/tests/bezier/bounds.rs b/tests/bezier/bounds.rs index b0fe91cd..53639f7b 100644 --- a/tests/bezier/bounds.rs +++ b/tests/bezier/bounds.rs @@ -1,11 +1,15 @@ -use flo_curves::bezier::{BezierCurve, BezierCurveFactory}; use flo_curves::bezier; +use flo_curves::bezier::{BezierCurve, BezierCurveFactory}; use flo_curves::geo::Coord2; use flo_curves::geo::Coordinate; #[test] fn get_straight_line_bounds() { - let straight_line = bezier::Curve::from_points(Coord2(0.0, 1.0), (Coord2(0.5, 1.5), Coord2(1.5, 2.5)), Coord2(2.0, 3.0)); + let straight_line = bezier::Curve::from_points( + Coord2(0.0, 1.0), + (Coord2(0.5, 1.5), Coord2(1.5, 2.5)), + Coord2(2.0, 3.0), + ); let bounds: (Coord2, Coord2) = straight_line.bounding_box(); @@ -14,7 +18,11 @@ fn get_straight_line_bounds() { #[test] fn get_curved_line_bounds() { - let curved_line = bezier::Curve::from_points(Coord2(0.0, 1.0), (Coord2(-1.1875291, 1.5), Coord2(1.5, 2.5)), Coord2(2.0, 3.0)); + let curved_line = bezier::Curve::from_points( + Coord2(0.0, 1.0), + (Coord2(-1.1875291, 1.5), Coord2(1.5, 2.5)), + Coord2(2.0, 3.0), + ); let bounds: (Coord2, Coord2) = curved_line.bounding_box(); diff --git a/tests/bezier/characteristics.rs b/tests/bezier/characteristics.rs index afea983c..5f762a17 100644 --- a/tests/bezier/characteristics.rs +++ b/tests/bezier/characteristics.rs @@ -1,27 +1,45 @@ -use flo_curves::*; use flo_curves::bezier::*; +use flo_curves::*; #[test] fn detect_loop_1() { - let curve = Curve::from_points(Coord2(110.0, 150.0), (Coord2(287.0, 227.0), Coord2(70.0, 205.0)), Coord2(205.0, 159.0)); + let curve = Curve::from_points( + Coord2(110.0, 150.0), + (Coord2(287.0, 227.0), Coord2(70.0, 205.0)), + Coord2(205.0, 159.0), + ); assert!(curve.characteristics() == bezier::CurveCategory::Loop); } #[test] fn detect_loop_2() { - let curve = Curve::from_points(Coord2(549.2899780273438, 889.4202270507813), (Coord2(553.4288330078125, 893.8638305664063), Coord2(542.5203247070313, 889.04931640625)), Coord2(548.051025390625, 891.1853637695313)); + let curve = Curve::from_points( + Coord2(549.2899780273438, 889.4202270507813), + ( + Coord2(553.4288330078125, 893.8638305664063), + Coord2(542.5203247070313, 889.04931640625), + ), + Coord2(548.051025390625, 891.1853637695313), + ); assert!(characterize_curve(&curve) == bezier::CurveCategory::Loop); } #[test] fn detect_loop_2_features() { - let curve = Curve::from_points(Coord2(549.2899780273438, 889.4202270507813), (Coord2(553.4288330078125, 893.8638305664063), Coord2(542.5203247070313, 889.04931640625)), Coord2(548.051025390625, 891.1853637695313)); + let curve = Curve::from_points( + Coord2(549.2899780273438, 889.4202270507813), + ( + Coord2(553.4288330078125, 893.8638305664063), + Coord2(542.5203247070313, 889.04931640625), + ), + Coord2(548.051025390625, 891.1853637695313), + ); match features_for_curve(&curve, 0.01) { - CurveFeatures::Loop(t1, t2) => { + CurveFeatures::Loop(t1, t2) => { let (p1, p2) = (curve.point_at_pos(t1), curve.point_at_pos(t2)); assert!(p1.is_near_to(&p2, 0.01)); - }, - _ => assert!(false) + } + _ => assert!(false), } } diff --git a/tests/bezier/curve_intersection_clip.rs b/tests/bezier/curve_intersection_clip.rs index c3511333..2fd607b8 100644 --- a/tests/bezier/curve_intersection_clip.rs +++ b/tests/bezier/curve_intersection_clip.rs @@ -1,15 +1,24 @@ -use flo_curves::*; -use flo_curves::line; use flo_curves::bezier; +use flo_curves::line; +use flo_curves::*; #[test] fn find_intersection_on_straight_line_not_middle() { // Cross that intersects at (5.0, 5.0) - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 0.0), Coord2(13.0, 13.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(9.0, 1.0), Coord2(0.0, 10.0))); - - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - println!("{:?} {:?}", intersections, intersections.iter().map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))).collect::>()); + let curve1 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 0.0), Coord2(13.0, 13.0))); + let curve2 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(9.0, 1.0), Coord2(0.0, 10.0))); + + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + println!( + "{:?} {:?}", + intersections, + intersections + .iter() + .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) + .collect::>() + ); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -24,11 +33,20 @@ fn find_intersection_on_straight_line_not_middle() { #[test] fn find_intersection_on_straight_line_middle() { // Cross that intersects at (5.0, 5.0) - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 0.0), Coord2(10.0, 10.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); - - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - println!("{:?} {:?}", intersections, intersections.iter().map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))).collect::>()); + let curve1 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 0.0), Coord2(10.0, 10.0))); + let curve2 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); + + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + println!( + "{:?} {:?}", + intersections, + intersections + .iter() + .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) + .collect::>() + ); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -43,10 +61,12 @@ fn find_intersection_on_straight_line_middle() { #[test] fn find_intersection_on_straight_line_start() { // Intersection at the start of two curves - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(10.0, 10.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(0.0, 10.0))); + let curve1 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(10.0, 10.0))); + let curve2 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -63,10 +83,12 @@ fn find_intersection_on_straight_line_start() { #[test] fn find_intersection_on_straight_line_end_1() { // Intersection at the start of two curves - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 10.0), Coord2(5.0, 5.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 10.0), Coord2(5.0, 5.0))); + let curve1 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 10.0), Coord2(5.0, 5.0))); + let curve2 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 10.0), Coord2(5.0, 5.0))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -83,10 +105,12 @@ fn find_intersection_on_straight_line_end_1() { #[test] fn find_intersection_on_straight_line_end_to_start_1() { // Intersection at the start of two curves - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 10.0), Coord2(5.0, 5.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(0.0, 10.0))); + let curve1 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 10.0), Coord2(5.0, 5.0))); + let curve2 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -103,10 +127,10 @@ fn find_intersection_on_straight_line_end_to_start_1() { #[test] fn find_intersection_on_line_end_to_end_2() { // Intersection that should be found in self_collide_removes_shared_point_2 in the graph_path tests - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(3.0, 3.0))); + let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); + let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(3.0, 3.0))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -123,10 +147,10 @@ fn find_intersection_on_line_end_to_end_3() { // TODO: this fails at the moment (the issue is that as the ray is collinear we get the wrong t values for the intersection point) // Intersection that should be found in self_collide_removes_shared_point_1 in the graph_path tests - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 1.0), Coord2(3.0, 3.0))); + let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); + let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 1.0), Coord2(3.0, 3.0))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -140,7 +164,7 @@ fn find_intersection_on_line_end_to_end_3() { #[test] fn solve_for_end_1() { - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); + let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); let end_pos = bezier::solve_curve_for_t(&curve1, &Coord2(3.0, 3.0)); assert!(end_pos.is_some()); @@ -149,7 +173,7 @@ fn solve_for_end_1() { #[test] fn solve_for_end_2() { - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 1.0), Coord2(3.0, 3.0))); + let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 1.0), Coord2(3.0, 3.0))); let end_pos = bezier::solve_curve_for_t(&curve1, &Coord2(3.0, 3.0)); assert!(end_pos.is_some()); @@ -159,10 +183,10 @@ fn solve_for_end_2() { #[test] fn find_intersection_on_line_end_to_start_2() { // Reverse of the intersection that should be found in self_collide_removes_shared_point_2 in the graph_path tests - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(3.0, 3.0), Coord2(5.0, 5.0))); + let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(1.0, 5.0), Coord2(3.0, 3.0))); + let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(3.0, 3.0), Coord2(5.0, 5.0))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); assert!(intersections.len() != 0); let intersect_point = curve1.point_at_pos(intersections[0].0); @@ -177,11 +201,20 @@ fn find_intersection_on_line_end_to_start_2() { #[test] fn find_intersection_on_straight_line_near_end() { // Intersection at the start of two curves - let curve1 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 10.0), Coord2(4.9, 5.1))); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 10.0), Coord2(5.1, 4.9))); + let curve1 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 10.0), Coord2(4.9, 5.1))); + let curve2 = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 10.0), Coord2(5.1, 4.9))); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); - println!("{:?} {:?}", intersections, intersections.iter().map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))).collect::>()); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + println!( + "{:?} {:?}", + intersections, + intersections + .iter() + .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) + .collect::>() + ); assert!(intersections.len() != 0); assert!(intersections.len() == 1); @@ -198,11 +231,26 @@ fn find_intersections_on_curve() { // Coord2(133.16, 167.13) // Coord2(179.87, 199.67) // - let curve1 = bezier::Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); - let curve2 = bezier::Curve::from_points(Coord2(5.0, 150.0), (Coord2(180.0, 20.0), Coord2(80.0, 250.0)), Coord2(210.0, 190.0)); - - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - println!("{:?} {:?}", intersections, intersections.iter().map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))).collect::>()); + let curve1 = bezier::Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); + let curve2 = bezier::Curve::from_points( + Coord2(5.0, 150.0), + (Coord2(180.0, 20.0), Coord2(80.0, 250.0)), + Coord2(210.0, 190.0), + ); + + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + println!( + "{:?} {:?}", + intersections, + intersections + .iter() + .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) + .collect::>() + ); // All intersections should be approximately the same location for intersect in intersections.iter() { @@ -219,10 +267,18 @@ fn find_intersections_on_curve() { #[test] fn intersections_with_overlapping_curves_1() { - let curve1 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); - let curve2 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); + let curve1 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); + let curve2 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); @@ -231,11 +287,19 @@ fn intersections_with_overlapping_curves_1() { #[test] fn intersections_with_overlapping_curves_2() { - let curve1 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); - let curve2 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); + let curve1 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); + let curve2 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); let curve2 = bezier::Curve::from_curve(&curve2.section(0.2, 0.6)); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); @@ -244,11 +308,19 @@ fn intersections_with_overlapping_curves_2() { #[test] fn intersections_with_overlapping_curves_3() { - let curve1 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); - let curve2 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); + let curve1 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); + let curve2 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); let curve1 = bezier::Curve::from_curve(&curve1.section(0.2, 0.6)); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); @@ -257,10 +329,18 @@ fn intersections_with_overlapping_curves_3() { #[test] fn intersections_with_nearby_curves_1() { - let curve1 = bezier::Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); - let curve2 = bezier::Curve::from_points(Coord2(350.22574, 706.551), (Coord2(354.72943, 701.2933), Coord2(358.0882, 695.26)), Coord2(361.0284, 690.2511)); + let curve1 = bezier::Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); + let curve2 = bezier::Curve::from_points( + Coord2(350.22574, 706.551), + (Coord2(354.72943, 701.2933), Coord2(358.0882, 695.26)), + Coord2(361.0284, 690.2511), + ); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); @@ -269,10 +349,24 @@ fn intersections_with_nearby_curves_1() { #[test] fn intersections_with_nearby_curves_2() { - let curve1 = bezier::Curve::from_points(Coord2(305.86907958984375, 882.2529296875), (Coord2(305.41015625, 880.7345581054688), Coord2(303.0707092285156, 879.744140625)), Coord2(298.0640869140625, 875.537353515625)); - let curve2 = bezier::Curve::from_points(Coord2(302.7962341308594, 879.1681518554688), (Coord2(299.5769348144531, 876.8582763671875), Coord2(297.1976318359375, 874.7939453125)), Coord2(301.4282531738281, 878.26220703125)); + let curve1 = bezier::Curve::from_points( + Coord2(305.86907958984375, 882.2529296875), + ( + Coord2(305.41015625, 880.7345581054688), + Coord2(303.0707092285156, 879.744140625), + ), + Coord2(298.0640869140625, 875.537353515625), + ); + let curve2 = bezier::Curve::from_points( + Coord2(302.7962341308594, 879.1681518554688), + ( + Coord2(299.5769348144531, 876.8582763671875), + Coord2(297.1976318359375, 874.7939453125), + ), + Coord2(301.4282531738281, 878.26220703125), + ); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); assert!(intersections.len() <= 9); @@ -280,10 +374,24 @@ fn intersections_with_nearby_curves_2() { #[test] fn intersections_with_nearby_curves_3() { - let curve1 = bezier::Curve::from_points(Coord2(304.6919250488281, 880.6288452148438), (Coord2(304.2330017089844, 879.1104736328125), Coord2(301.8935546875, 878.1200561523438)), Coord2(296.8869323730469, 873.9132690429688)); - let curve2 = bezier::Curve::from_points(Coord2(301.61907958984375, 877.5440673828125), (Coord2(300.2510986328125, 876.6381225585938), Coord2(298.3997802734375, 875.2341918945313)), Coord2(296.0204772949219, 873.1698608398438)); + let curve1 = bezier::Curve::from_points( + Coord2(304.6919250488281, 880.6288452148438), + ( + Coord2(304.2330017089844, 879.1104736328125), + Coord2(301.8935546875, 878.1200561523438), + ), + Coord2(296.8869323730469, 873.9132690429688), + ); + let curve2 = bezier::Curve::from_points( + Coord2(301.61907958984375, 877.5440673828125), + ( + Coord2(300.2510986328125, 876.6381225585938), + Coord2(298.3997802734375, 875.2341918945313), + ), + Coord2(296.0204772949219, 873.1698608398438), + ); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); // assert!(intersections.len() <= 9); @@ -291,10 +399,24 @@ fn intersections_with_nearby_curves_3() { #[test] fn intersections_with_nearby_curves_4() { - let curve1 = bezier::Curve::from_points(Coord2(436.15716552734375, 869.3236083984375), (Coord2(444.5263671875, 869.2921752929688), Coord2(480.9628601074219, 854.3709106445313)), Coord2(490.6786804199219, 849.5614624023438)); - let curve2 = bezier::Curve::from_points(Coord2(462.5539855957031, 861.322021484375), (Coord2(462.4580078125, 861.4293823242188), Coord2(462.3710021972656, 861.5908813476563)), Coord2(462.3448486328125, 861.8137817382813)); + let curve1 = bezier::Curve::from_points( + Coord2(436.15716552734375, 869.3236083984375), + ( + Coord2(444.5263671875, 869.2921752929688), + Coord2(480.9628601074219, 854.3709106445313), + ), + Coord2(490.6786804199219, 849.5614624023438), + ); + let curve2 = bezier::Curve::from_points( + Coord2(462.5539855957031, 861.322021484375), + ( + Coord2(462.4580078125, 861.4293823242188), + Coord2(462.3710021972656, 861.5908813476563), + ), + Coord2(462.3448486328125, 861.8137817382813), + ); - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); assert!(intersections.len() <= 9); @@ -302,8 +424,22 @@ fn intersections_with_nearby_curves_4() { #[test] fn intersection_curve_1() { - let curve1 = bezier::Curve::from_points(Coord2(252.08901977539063, 676.4180908203125), (Coord2(244.0195770263672, 679.6658935546875), Coord2(244.11508178710938, 682.8816528320313)), Coord2(244.31190490722656, 686.1041259765625)); - let curve2 = bezier::Curve::from_points(Coord2(244.31190490722656, 686.1041259765625), (Coord2(250.65411376953125, 661.4817504882813), Coord2(255.51109313964844, 635.5418701171875)), Coord2(265.2398376464844, 618.4223022460938)); + let curve1 = bezier::Curve::from_points( + Coord2(252.08901977539063, 676.4180908203125), + ( + Coord2(244.0195770263672, 679.6658935546875), + Coord2(244.11508178710938, 682.8816528320313), + ), + Coord2(244.31190490722656, 686.1041259765625), + ); + let curve2 = bezier::Curve::from_points( + Coord2(244.31190490722656, 686.1041259765625), + ( + Coord2(250.65411376953125, 661.4817504882813), + Coord2(255.51109313964844, 635.5418701171875), + ), + Coord2(265.2398376464844, 618.4223022460938), + ); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); @@ -311,22 +447,56 @@ fn intersection_curve_1() { assert!(intersections.len() != 1); assert!(intersections.len() == 2); - assert!(curve1.point_at_pos(intersections[0].0).distance_to(&curve2.point_at_pos(intersections[0].1)) < 0.01); - assert!(curve1.point_at_pos(intersections[1].0).distance_to(&curve2.point_at_pos(intersections[1].1)) < 0.01); + assert!( + curve1 + .point_at_pos(intersections[0].0) + .distance_to(&curve2.point_at_pos(intersections[0].1)) + < 0.01 + ); + assert!( + curve1 + .point_at_pos(intersections[1].0) + .distance_to(&curve2.point_at_pos(intersections[1].1)) + < 0.01 + ); let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 2); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&curve1.point_at_pos(intersections[0].1)) < 0.01); - assert!(curve2.point_at_pos(intersections[1].0).distance_to(&curve1.point_at_pos(intersections[1].1)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&curve1.point_at_pos(intersections[0].1)) + < 0.01 + ); + assert!( + curve2 + .point_at_pos(intersections[1].0) + .distance_to(&curve1.point_at_pos(intersections[1].1)) + < 0.01 + ); } #[test] fn intersection_curve_2() { - let curve1 = bezier::Curve::from_points(Coord2(248.42221069335938, 678.5138549804688), (Coord2(240.33773803710938, 703.49462890625), Coord2(246.20928955078125, 728.5226440429688)), Coord2(258.2634582519531, 745.7745361328125)); - let curve2 = bezier::Curve::from_points(Coord2(240.6450958251953, 688.1998901367188), (Coord2(248.51101684570313, 684.6644897460938), Coord2(248.41787719726563, 681.5728759765625)), Coord2(248.42221069335938, 678.5138549804688)); + let curve1 = bezier::Curve::from_points( + Coord2(248.42221069335938, 678.5138549804688), + ( + Coord2(240.33773803710938, 703.49462890625), + Coord2(246.20928955078125, 728.5226440429688), + ), + Coord2(258.2634582519531, 745.7745361328125), + ); + let curve2 = bezier::Curve::from_points( + Coord2(240.6450958251953, 688.1998901367188), + ( + Coord2(248.51101684570313, 684.6644897460938), + Coord2(248.41787719726563, 681.5728759765625), + ), + Coord2(248.42221069335938, 678.5138549804688), + ); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); @@ -334,88 +504,194 @@ fn intersection_curve_2() { assert!(intersections.len() != 1); assert!(intersections.len() == 2); - assert!(curve1.point_at_pos(intersections[0].0).distance_to(&curve2.point_at_pos(intersections[0].1)) < 0.01); - assert!(curve1.point_at_pos(intersections[1].0).distance_to(&curve2.point_at_pos(intersections[1].1)) < 0.01); + assert!( + curve1 + .point_at_pos(intersections[0].0) + .distance_to(&curve2.point_at_pos(intersections[0].1)) + < 0.01 + ); + assert!( + curve1 + .point_at_pos(intersections[1].0) + .distance_to(&curve2.point_at_pos(intersections[1].1)) + < 0.01 + ); let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 2); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&curve1.point_at_pos(intersections[0].1)) < 0.01); - assert!(curve2.point_at_pos(intersections[1].0).distance_to(&curve1.point_at_pos(intersections[1].1)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&curve1.point_at_pos(intersections[0].1)) + < 0.01 + ); + assert!( + curve2 + .point_at_pos(intersections[1].0) + .distance_to(&curve1.point_at_pos(intersections[1].1)) + < 0.01 + ); } #[test] fn intersection_curve_3() { - let curve1 = bezier::Curve::from_points(Coord2(377.8294677734375, 495.076904296875), (Coord2(380.0453796386719, 492.69927978515625), Coord2(381.98138427734375, 489.805419921875)), Coord2(383.61865234375, 486.40106201171875)); - let curve2 = bezier::Curve::from_points(Coord2(379.064697265625, 493.7556457519531), (Coord2(371.90069580078125, 491.9415588378906), Coord2(368.96783447265625, 493.451171875)), Coord2(366.3587951660156, 494.5915832519531)); + let curve1 = bezier::Curve::from_points( + Coord2(377.8294677734375, 495.076904296875), + ( + Coord2(380.0453796386719, 492.69927978515625), + Coord2(381.98138427734375, 489.805419921875), + ), + Coord2(383.61865234375, 486.40106201171875), + ); + let curve2 = bezier::Curve::from_points( + Coord2(379.064697265625, 493.7556457519531), + ( + Coord2(371.90069580078125, 491.9415588378906), + Coord2(368.96783447265625, 493.451171875), + ), + Coord2(366.3587951660156, 494.5915832519531), + ); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - assert!(curve1.point_at_pos(intersections[0].0).distance_to(&curve2.point_at_pos(intersections[0].1)) < 0.01); + assert!( + curve1 + .point_at_pos(intersections[0].0) + .distance_to(&curve2.point_at_pos(intersections[0].1)) + < 0.01 + ); let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&curve1.point_at_pos(intersections[0].1)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&curve1.point_at_pos(intersections[0].1)) + < 0.01 + ); } #[test] fn intersection_curve_4() { - let curve1 = bezier::Curve::from_points(Coord2(377.8294677734375, 495.076904296875), (Coord2(380.0453796386719, 492.69927978515625), Coord2(381.98138427734375, 489.805419921875)), Coord2(383.61865234375, 486.40106201171875)); - let curve2 = bezier::Curve::from_points(Coord2(379.064697265625, 493.7556457519531), (Coord2(371.3619079589844, 493.8326110839844), Coord2(366.50872802734375, 495.2229919433594)), Coord2(362.0657958984375, 496.14581298828125)); + let curve1 = bezier::Curve::from_points( + Coord2(377.8294677734375, 495.076904296875), + ( + Coord2(380.0453796386719, 492.69927978515625), + Coord2(381.98138427734375, 489.805419921875), + ), + Coord2(383.61865234375, 486.40106201171875), + ); + let curve2 = bezier::Curve::from_points( + Coord2(379.064697265625, 493.7556457519531), + ( + Coord2(371.3619079589844, 493.8326110839844), + Coord2(366.50872802734375, 495.2229919433594), + ), + Coord2(362.0657958984375, 496.14581298828125), + ); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - assert!(curve1.point_at_pos(intersections[0].0).distance_to(&curve2.point_at_pos(intersections[0].1)) < 0.01); + assert!( + curve1 + .point_at_pos(intersections[0].0) + .distance_to(&curve2.point_at_pos(intersections[0].1)) + < 0.01 + ); let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&curve1.point_at_pos(intersections[0].1)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&curve1.point_at_pos(intersections[0].1)) + < 0.01 + ); } #[test] fn intersection_curve_5() { - let curve1 = bezier::Curve::from_points(Coord2(379.064697265625, 493.7556457519531), (Coord2(371.90069580078125, 491.9415588378906), Coord2(368.96783447265625, 493.451171875)), Coord2(366.3587951660156, 494.5915832519531)); - let curve2 = bezier::Curve::from_points(Coord2(379.064697265625, 493.7556457519531), (Coord2(371.3619079589844, 493.8326110839844), Coord2(366.50872802734375, 495.2229919433594)), Coord2(362.0657958984375, 496.14581298828125)); - + let curve1 = bezier::Curve::from_points( + Coord2(379.064697265625, 493.7556457519531), + ( + Coord2(371.90069580078125, 491.9415588378906), + Coord2(368.96783447265625, 493.451171875), + ), + Coord2(366.3587951660156, 494.5915832519531), + ); + let curve2 = bezier::Curve::from_points( + Coord2(379.064697265625, 493.7556457519531), + ( + Coord2(371.3619079589844, 493.8326110839844), + Coord2(366.50872802734375, 495.2229919433594), + ), + Coord2(362.0657958984375, 496.14581298828125), + ); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - assert!(curve1.point_at_pos(intersections[0].0).distance_to(&curve2.point_at_pos(intersections[0].1)) < 0.01); + assert!( + curve1 + .point_at_pos(intersections[0].0) + .distance_to(&curve2.point_at_pos(intersections[0].1)) + < 0.01 + ); let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&curve1.point_at_pos(intersections[0].1)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&curve1.point_at_pos(intersections[0].1)) + < 0.01 + ); } #[test] fn intersection_curve_6() { - let curve1 = bezier::Curve::from_points(Coord2(608.7642211914063, 855.5934448242188), (Coord2(608.6810302734375, 855.288330078125), Coord2(608.5828857421875, 855.0850830078125)), Coord2(608.47265625, 855.011962890625)); - let curve2 = bezier::Curve::from_points(Coord2(608.81689453125, 855.5904541015625), (Coord2(608.7009887695313, 855.386474609375), Coord2(608.5858154296875, 855.193115234375)), Coord2(608.47265625, 855.011962890625)); - + let curve1 = bezier::Curve::from_points( + Coord2(608.7642211914063, 855.5934448242188), + ( + Coord2(608.6810302734375, 855.288330078125), + Coord2(608.5828857421875, 855.0850830078125), + ), + Coord2(608.47265625, 855.011962890625), + ); + let curve2 = bezier::Curve::from_points( + Coord2(608.81689453125, 855.5904541015625), + ( + Coord2(608.7009887695313, 855.386474609375), + Coord2(608.5858154296875, 855.193115234375), + ), + Coord2(608.47265625, 855.011962890625), + ); + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); assert!(intersections.len() == 1); - + let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); assert!(intersections.len() != 0); @@ -426,15 +702,39 @@ fn intersection_curve_6() { fn intersection_curve_7() { // Three curves that should intersect in two places, seems to be generating a missed collision when doing a self-collide // See `remove_interior_points_complex_1`: one of these curves seems to be producing only one collision, at least for the case that prompted that test - let curve1 = bezier::Curve::from_points(Coord2(602.1428833007813, 859.0895385742188), (Coord2(607.4638061523438, 858.4710693359375), Coord2(614.4444580078125, 855.14404296875)), Coord2(608.3931884765625, 855.6187133789063)); - let curve2 = bezier::Curve::from_points(Coord2(608.3871459960938, 864.779541015625), (Coord2(609.6311645507813, 860.09716796875), Coord2(608.9767456054688, 852.512939453125)), Coord2(607.8642578125, 855.7709350585938)); - let curve3 = bezier::Curve::from_points(Coord2(611.2799682617188, 862.292236328125), (Coord2(610.607666015625, 857.74365234375), Coord2(606.7666625976563, 851.5074462890625)), Coord2(606.7361450195313, 853.9833984375)); + let curve1 = bezier::Curve::from_points( + Coord2(602.1428833007813, 859.0895385742188), + ( + Coord2(607.4638061523438, 858.4710693359375), + Coord2(614.4444580078125, 855.14404296875), + ), + Coord2(608.3931884765625, 855.6187133789063), + ); + let curve2 = bezier::Curve::from_points( + Coord2(608.3871459960938, 864.779541015625), + ( + Coord2(609.6311645507813, 860.09716796875), + Coord2(608.9767456054688, 852.512939453125), + ), + Coord2(607.8642578125, 855.7709350585938), + ); + let curve3 = bezier::Curve::from_points( + Coord2(611.2799682617188, 862.292236328125), + ( + Coord2(610.607666015625, 857.74365234375), + Coord2(606.7666625976563, 851.5074462890625), + ), + Coord2(606.7361450195313, 853.9833984375), + ); let intersections1 = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); let intersections2 = bezier::curve_intersects_curve_clip(&curve2, &curve3, 0.01); let intersections3 = bezier::curve_intersects_curve_clip(&curve1, &curve3, 0.01); - println!("{:?}\n{:?}\n{:?}\n", intersections1, intersections2, intersections3); + println!( + "{:?}\n{:?}\n{:?}\n", + intersections1, intersections2, intersections3 + ); assert!(intersections1.len() == 2); assert!(intersections2.len() == 2); @@ -444,7 +744,10 @@ fn intersection_curve_7() { let intersections2 = bezier::curve_intersects_curve_clip(&curve3, &curve2, 0.01); let intersections3 = bezier::curve_intersects_curve_clip(&curve3, &curve1, 0.01); - println!("{:?}\n{:?}\n{:?}\n", intersections1, intersections2, intersections3); + println!( + "{:?}\n{:?}\n{:?}\n", + intersections1, intersections2, intersections3 + ); assert!(intersections1.len() == 2); assert!(intersections2.len() == 2); @@ -454,8 +757,22 @@ fn intersection_curve_7() { #[test] fn intersection_curve_8() { // This curve starts and ends at the same position - let loop_curve = bezier::Curve::from_points(Coord2(534.170654296875, 832.8574829101563), (Coord2(534.3781127929688, 832.078369140625), Coord2(534.73828125, 832.8485107421875)), Coord2(534.170654296875, 832.8574829101563)); - let curve2 = bezier::Curve::from_points(Coord2(534.3034057617188, 832.695068359375), (Coord2(534.2012536621094, 832.3760168457031), Coord2(534.2515673828125, 832.5331616210938)), Coord2(534.1509399414063, 832.2188720703125)); + let loop_curve = bezier::Curve::from_points( + Coord2(534.170654296875, 832.8574829101563), + ( + Coord2(534.3781127929688, 832.078369140625), + Coord2(534.73828125, 832.8485107421875), + ), + Coord2(534.170654296875, 832.8574829101563), + ); + let curve2 = bezier::Curve::from_points( + Coord2(534.3034057617188, 832.695068359375), + ( + Coord2(534.2012536621094, 832.3760168457031), + Coord2(534.2515673828125, 832.5331616210938), + ), + Coord2(534.1509399414063, 832.2188720703125), + ); let intersections1 = bezier::curve_intersects_curve_clip(&loop_curve, &curve2, 0.01); let intersections2 = bezier::curve_intersects_curve_clip(&curve2, &loop_curve, 0.01); @@ -475,9 +792,23 @@ fn intersection_curve_8() { #[test] fn intersection_curve_9() { - let curve1 = bezier::Curve::from_points(Coord2(576.0272827148438, 854.4729614257813), (Coord2(575.8976440429688, 855.9894409179688), Coord2(576.928466796875, 870.5877685546875)), Coord2(577.4629516601563, 873.9253540039063)); - let curve2 = bezier::Curve::from_points(Coord2(576.0455932617188, 854.9674072265625), (Coord2(574.2247924804688, 855.3988037109375), Coord2(577.3884887695313, 863.1025390625)), Coord2(580.003662109375, 863.5904541015625)); - + let curve1 = bezier::Curve::from_points( + Coord2(576.0272827148438, 854.4729614257813), + ( + Coord2(575.8976440429688, 855.9894409179688), + Coord2(576.928466796875, 870.5877685546875), + ), + Coord2(577.4629516601563, 873.9253540039063), + ); + let curve2 = bezier::Curve::from_points( + Coord2(576.0455932617188, 854.9674072265625), + ( + Coord2(574.2247924804688, 855.3988037109375), + Coord2(577.3884887695313, 863.1025390625), + ), + Coord2(580.003662109375, 863.5904541015625), + ); + let intersections1 = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); let intersections2 = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); @@ -493,15 +824,21 @@ fn intersection_curve_9() { #[test] fn intersection_curve_10() { - let curve1 = bezier::Curve { - start_point: Coord2(284.86767013759504, 712.1320343559642), - end_point: Coord2(709.1317388495236, 712.1320343559643), - control_points: (Coord2(402.02495766297596, 829.2893218813454), Coord2(591.9744513241425, 829.2893218813454)) + let curve1 = bezier::Curve { + start_point: Coord2(284.86767013759504, 712.1320343559642), + end_point: Coord2(709.1317388495236, 712.1320343559643), + control_points: ( + Coord2(402.02495766297596, 829.2893218813454), + Coord2(591.9744513241425, 829.2893218813454), + ), }; - let curve2 = bezier::Curve { - start_point: Coord2(290.8682611504764, 712.1320343559642), - end_point: Coord2(715.132329862405, 712.1320343559643), - control_points: (Coord2(408.02554867585735, 829.2893218813454), Coord2(597.9750423370239, 829.2893218813454)) + let curve2 = bezier::Curve { + start_point: Coord2(290.8682611504764, 712.1320343559642), + end_point: Coord2(715.132329862405, 712.1320343559643), + control_points: ( + Coord2(408.02554867585735, 829.2893218813454), + Coord2(597.9750423370239, 829.2893218813454), + ), }; let intersections1 = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); @@ -527,15 +864,21 @@ fn intersection_curve_10() { fn intersection_curve_11() { // Tries to eliminate the subdivisions from intersection_curve_10 so will more reliably fail if other changes are made to // the intersection algorithm - let curve1 = bezier::Curve { - start_point: Coord2(284.86767013759504, 712.1320343559642), - end_point: Coord2(709.1317388495236, 712.1320343559643), - control_points: (Coord2(402.02495766297596, 829.2893218813454), Coord2(591.9744513241425, 829.2893218813454)) + let curve1 = bezier::Curve { + start_point: Coord2(284.86767013759504, 712.1320343559642), + end_point: Coord2(709.1317388495236, 712.1320343559643), + control_points: ( + Coord2(402.02495766297596, 829.2893218813454), + Coord2(591.9744513241425, 829.2893218813454), + ), }; - let curve2 = bezier::Curve { - start_point: Coord2(290.8682611504764, 712.1320343559642), - end_point: Coord2(715.132329862405, 712.1320343559643), - control_points: (Coord2(408.02554867585735, 829.2893218813454), Coord2(597.9750423370239, 829.2893218813454)) + let curve2 = bezier::Curve { + start_point: Coord2(290.8682611504764, 712.1320343559642), + end_point: Coord2(715.132329862405, 712.1320343559643), + control_points: ( + Coord2(408.02554867585735, 829.2893218813454), + Coord2(597.9750423370239, 829.2893218813454), + ), }; let curve1 = curve1.section(0.49236699497857783, 0.5065132298924669); @@ -578,8 +921,22 @@ fn intersection_curve_11() { fn intersection_very_close_to_start_1() { // One section here is a line, so we can find that the start point of the 'remaining' section is very close to that line (enough that it should produce an intersection) // Source of this is a case where the 'remaining' section is slightly rounded due to a conversion to f32 and back to f64 - let fragment = bezier::Curve { start_point: Coord2(503.12144515225805, 515.6925644864517), end_point: Coord2(558.5270966048384, 794.2355841209692), control_points: (Coord2(521.5881487814031, 608.5309529306364), Coord2(540.0548524105482, 701.369341374821)) }; - let remaining = bezier::Curve { start_point: Coord2(522.6328735351563, 613.7830200195313), end_point: Coord2(582.0244140625, 582.0244140625), control_points: (Coord2(544.3945922851563, 609.47509765625), Coord2(565.159912109375, 598.8888549804688)) }; + let fragment = bezier::Curve { + start_point: Coord2(503.12144515225805, 515.6925644864517), + end_point: Coord2(558.5270966048384, 794.2355841209692), + control_points: ( + Coord2(521.5881487814031, 608.5309529306364), + Coord2(540.0548524105482, 701.369341374821), + ), + }; + let remaining = bezier::Curve { + start_point: Coord2(522.6328735351563, 613.7830200195313), + end_point: Coord2(582.0244140625, 582.0244140625), + control_points: ( + Coord2(544.3945922851563, 609.47509765625), + Coord2(565.159912109375, 598.8888549804688), + ), + }; let intersections1 = bezier::curve_intersects_curve_clip(&fragment, &remaining, 0.01); let intersections2 = bezier::curve_intersects_curve_clip(&remaining, &fragment, 0.01); @@ -615,8 +972,22 @@ fn solve_t_close_to_start() { use flo_curves::bezier::*; // Same curve as above, but we try to solve the closest point for one curve against another - let fragment = bezier::Curve { start_point: Coord2(503.12144515225805, 515.6925644864517), end_point: Coord2(558.5270966048384, 794.2355841209692), control_points: (Coord2(521.5881487814031, 608.5309529306364), Coord2(540.0548524105482, 701.369341374821)) }; - let remaining = bezier::Curve { start_point: Coord2(522.6328735351563, 613.7830200195313), end_point: Coord2(582.0244140625, 582.0244140625), control_points: (Coord2(544.3945922851563, 609.47509765625), Coord2(565.159912109375, 598.8888549804688)) }; + let fragment = bezier::Curve { + start_point: Coord2(503.12144515225805, 515.6925644864517), + end_point: Coord2(558.5270966048384, 794.2355841209692), + control_points: ( + Coord2(521.5881487814031, 608.5309529306364), + Coord2(540.0548524105482, 701.369341374821), + ), + }; + let remaining = bezier::Curve { + start_point: Coord2(522.6328735351563, 613.7830200195313), + end_point: Coord2(582.0244140625, 582.0244140625), + control_points: ( + Coord2(544.3945922851563, 609.47509765625), + Coord2(565.159912109375, 598.8888549804688), + ), + }; // In this case, the fragment is linear (however, as we're solving for a point close to a curve, this shouldn't be necessary always) assert!(fragment.characteristics() == CurveCategory::Linear); @@ -625,9 +996,11 @@ fn solve_t_close_to_start() { let t_remaining = 0.0; // Should be able to solve for this point on the remaining curve - let t_fragment = fragment.t_for_point(&remaining.start_point).expect("t value"); - let t_point = fragment.point_at_pos(t_fragment); - let t_distance = t_point.distance_to(&remaining.point_at_pos(t_remaining)); + let t_fragment = fragment + .t_for_point(&remaining.start_point) + .expect("t value"); + let t_point = fragment.point_at_pos(t_fragment); + let t_distance = t_point.distance_to(&remaining.point_at_pos(t_remaining)); // The above test should be able to solve this value to at least this precision level (t_remaining = 0.0, t_fragment = as above) assert!(t_distance < 0.02); @@ -636,13 +1009,24 @@ fn solve_t_close_to_start() { #[test] fn intersections_on_curve_sections() { // Two sections from the same larger curve, no shared points but overlapping in the middle - let larger_curve = bezier::Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); - - let curve1 = larger_curve.section(0.1, 0.5); - let curve2 = larger_curve.section(0.4, 0.8); - - let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - println!("{:?} {:?}", intersections, intersections.iter().map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))).collect::>()); + let larger_curve = bezier::Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); + + let curve1 = larger_curve.section(0.1, 0.5); + let curve2 = larger_curve.section(0.4, 0.8); + + let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); + println!( + "{:?} {:?}", + intersections, + intersections + .iter() + .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) + .collect::>() + ); // All intersections should be approximately the same location for intersect in intersections.iter() { diff --git a/tests/bezier/deform.rs b/tests/bezier/deform.rs index 4e42368d..f743363b 100644 --- a/tests/bezier/deform.rs +++ b/tests/bezier/deform.rs @@ -1,77 +1,133 @@ -use flo_curves::*; use flo_curves::bezier; +use flo_curves::*; #[test] fn deform_line_upwards() { - let curve = bezier::Curve::from_points(Coord2(0.0, 0.0), (Coord2(2.5, 0.0), Coord2(7.5, 0.0)), Coord2(10.0, 0.0)); - let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, 0.5, &Coord2(0.0, 4.0)); - - let original_point = curve.point_at_pos(0.5); - let new_point = deformed.point_at_pos(0.5); - - let offset = new_point - original_point; - - assert!((offset.x()-0.0).abs() < 0.01); - assert!((offset.y()-4.0).abs() < 0.01); - - assert!(curve.point_at_pos(0.0).distance_to(&deformed.point_at_pos(0.0)) < 0.01); - assert!(curve.point_at_pos(1.0).distance_to(&deformed.point_at_pos(1.0)) < 0.01); + let curve = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(2.5, 0.0), Coord2(7.5, 0.0)), + Coord2(10.0, 0.0), + ); + let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, 0.5, &Coord2(0.0, 4.0)); + + let original_point = curve.point_at_pos(0.5); + let new_point = deformed.point_at_pos(0.5); + + let offset = new_point - original_point; + + assert!((offset.x() - 0.0).abs() < 0.01); + assert!((offset.y() - 4.0).abs() < 0.01); + + assert!( + curve + .point_at_pos(0.0) + .distance_to(&deformed.point_at_pos(0.0)) + < 0.01 + ); + assert!( + curve + .point_at_pos(1.0) + .distance_to(&deformed.point_at_pos(1.0)) + < 0.01 + ); } #[test] fn deform_curve_at_halfway_point() { - let curve = bezier::Curve::from_points(Coord2(10.0, 20.0), (Coord2(0.0, 15.0), Coord2(16.0, 30.0)), Coord2(20.0, 15.0)); - let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, 0.5, &Coord2(3.0, 4.0)); - - let original_point = curve.point_at_pos(0.5); - let new_point = deformed.point_at_pos(0.5); - - let offset = new_point - original_point; - - assert!((offset.x()-3.0).abs() < 0.01); - assert!((offset.y()-4.0).abs() < 0.01); - - assert!(curve.point_at_pos(0.0).distance_to(&deformed.point_at_pos(0.0)) < 0.01); - assert!(curve.point_at_pos(1.0).distance_to(&deformed.point_at_pos(1.0)) < 0.01); + let curve = bezier::Curve::from_points( + Coord2(10.0, 20.0), + (Coord2(0.0, 15.0), Coord2(16.0, 30.0)), + Coord2(20.0, 15.0), + ); + let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, 0.5, &Coord2(3.0, 4.0)); + + let original_point = curve.point_at_pos(0.5); + let new_point = deformed.point_at_pos(0.5); + + let offset = new_point - original_point; + + assert!((offset.x() - 3.0).abs() < 0.01); + assert!((offset.y() - 4.0).abs() < 0.01); + + assert!( + curve + .point_at_pos(0.0) + .distance_to(&deformed.point_at_pos(0.0)) + < 0.01 + ); + assert!( + curve + .point_at_pos(1.0) + .distance_to(&deformed.point_at_pos(1.0)) + < 0.01 + ); } #[test] fn deform_curve_at_other_point() { - let t = 0.32; - let curve = bezier::Curve::from_points(Coord2(10.0, 20.0), (Coord2(0.0, 15.0), Coord2(16.0, 30.0)), Coord2(20.0, 15.0)); - let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, t, &Coord2(3.0, 4.0)); - - let original_point = curve.point_at_pos(t); - let new_point = deformed.point_at_pos(t); - - let offset = new_point - original_point; - - assert!((offset.x()-3.0).abs() < 0.01); - assert!((offset.y()-4.0).abs() < 0.01); - - assert!(curve.point_at_pos(0.0).distance_to(&deformed.point_at_pos(0.0)) < 0.01); - assert!(curve.point_at_pos(1.0).distance_to(&deformed.point_at_pos(1.0)) < 0.01); + let t = 0.32; + let curve = bezier::Curve::from_points( + Coord2(10.0, 20.0), + (Coord2(0.0, 15.0), Coord2(16.0, 30.0)), + Coord2(20.0, 15.0), + ); + let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, t, &Coord2(3.0, 4.0)); + + let original_point = curve.point_at_pos(t); + let new_point = deformed.point_at_pos(t); + + let offset = new_point - original_point; + + assert!((offset.x() - 3.0).abs() < 0.01); + assert!((offset.y() - 4.0).abs() < 0.01); + + assert!( + curve + .point_at_pos(0.0) + .distance_to(&deformed.point_at_pos(0.0)) + < 0.01 + ); + assert!( + curve + .point_at_pos(1.0) + .distance_to(&deformed.point_at_pos(1.0)) + < 0.01 + ); } #[test] fn deform_curve_at_many_other_points() { for t in 0..100 { // Won't work at 0 or 1 as these are the start and end points and don't move - let t = (t as f64)/100.0; - let t = (0.9*t)+0.05; - - let curve = bezier::Curve::from_points(Coord2(5.0, 23.0), (Coord2(-10.0, 15.0), Coord2(26.0, 30.0)), Coord2(22.0, 17.0)); - let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, t, &Coord2(6.0, -4.0)); - - let original_point = curve.point_at_pos(t); - let new_point = deformed.point_at_pos(t); - - let offset = new_point - original_point; - - assert!((offset.x()-6.0).abs() < 0.01); - assert!((offset.y()- -4.0).abs() < 0.01); - - assert!(curve.point_at_pos(0.0).distance_to(&deformed.point_at_pos(0.0)) < 0.01); - assert!(curve.point_at_pos(1.0).distance_to(&deformed.point_at_pos(1.0)) < 0.01); + let t = (t as f64) / 100.0; + let t = (0.9 * t) + 0.05; + + let curve = bezier::Curve::from_points( + Coord2(5.0, 23.0), + (Coord2(-10.0, 15.0), Coord2(26.0, 30.0)), + Coord2(22.0, 17.0), + ); + let deformed = bezier::move_point::<_, _, bezier::Curve<_>>(&curve, t, &Coord2(6.0, -4.0)); + + let original_point = curve.point_at_pos(t); + let new_point = deformed.point_at_pos(t); + + let offset = new_point - original_point; + + assert!((offset.x() - 6.0).abs() < 0.01); + assert!((offset.y() - -4.0).abs() < 0.01); + + assert!( + curve + .point_at_pos(0.0) + .distance_to(&deformed.point_at_pos(0.0)) + < 0.01 + ); + assert!( + curve + .point_at_pos(1.0) + .distance_to(&deformed.point_at_pos(1.0)) + < 0.01 + ); } } diff --git a/tests/bezier/distort.rs b/tests/bezier/distort.rs index 11633540..00a81921 100644 --- a/tests/bezier/distort.rs +++ b/tests/bezier/distort.rs @@ -1,25 +1,41 @@ -use flo_curves::geo::*; use flo_curves::bezier::*; +use flo_curves::geo::*; #[test] fn line_to_sine_wave() { - let line = Curve::from_points(Coord2(100.0, 100.0), (Coord2(100.0, 100.0), Coord2(400.0, 100.0)), Coord2(400.0, 100.0)); - let distorted = distort_curve::<_, _, Curve<_>>(&line, |pos, _t| Coord2(pos.x(), pos.y() + (pos.x()*20.0).sin()), 1.0, 1.0).expect("Fit curve"); + let line = Curve::from_points( + Coord2(100.0, 100.0), + (Coord2(100.0, 100.0), Coord2(400.0, 100.0)), + Coord2(400.0, 100.0), + ); + let distorted = distort_curve::<_, _, Curve<_>>( + &line, + |pos, _t| Coord2(pos.x(), pos.y() + (pos.x() * 20.0).sin()), + 1.0, + 1.0, + ) + .expect("Fit curve"); for curve in distorted.into_iter() { println!("{:?}", curve); for section in walk_curve_evenly(&curve, 1.0, 0.1) { - let (t_min, t_max) = section.original_curve_t_values(); - let t_mid = (t_min+t_max)/2.0; - let pos = section.point_at_pos(t_mid); + let (t_min, t_max) = section.original_curve_t_values(); + let t_mid = (t_min + t_max) / 2.0; + let pos = section.point_at_pos(t_mid); - let expected_y = 100.0 + (pos.x()*20.0).sin(); - let actual_y = pos.y(); + let expected_y = 100.0 + (pos.x() * 20.0).sin(); + let actual_y = pos.y(); - println!(" {:?} {:?} {:?} {:?}", t_mid, expected_y, actual_y, (expected_y-actual_y).abs()); + println!( + " {:?} {:?} {:?} {:?}", + t_mid, + expected_y, + actual_y, + (expected_y - actual_y).abs() + ); - assert!((expected_y-actual_y).abs() < 4.0); + assert!((expected_y - actual_y).abs() < 4.0); } } } diff --git a/tests/bezier/intersection.rs b/tests/bezier/intersection.rs index c4832d24..8134067a 100644 --- a/tests/bezier/intersection.rs +++ b/tests/bezier/intersection.rs @@ -1,14 +1,15 @@ -use flo_curves::*; use flo_curves::bezier; use flo_curves::line; +use flo_curves::*; #[test] fn find_intersection_on_straight_line() { // Cross that intersects at (5.0, 5.0) - let line = (Coord2(0.0, 0.0), Coord2(10.0, 10.0)); - let curve = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); + let line = (Coord2(0.0, 0.0), Coord2(10.0, 10.0)); + let curve = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); assert!(intersections.len() == 1); let intersect_point = curve.point_at_pos(intersections[0].0); @@ -18,10 +19,11 @@ fn find_intersection_on_straight_line() { #[test] fn find_intersection_with_vertical_ray() { // Cross that intersects at (5.0, 5.0) - let line = (Coord2(5.0, 0.0), Coord2(5.0, 10.0)); - let curve = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); + let line = (Coord2(5.0, 0.0), Coord2(5.0, 10.0)); + let curve = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); assert!(intersections.len() == 1); let intersect_point = curve.point_at_pos(intersections[0].0); @@ -31,10 +33,11 @@ fn find_intersection_with_vertical_ray() { #[test] fn find_intersection_with_horizontal_ray() { // Cross that intersects at (5.0, 5.0) - let line = (Coord2(0.0, 5.0), Coord2(10.0, 5.0)); - let curve = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); + let line = (Coord2(0.0, 5.0), Coord2(10.0, 5.0)); + let curve = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); assert!(intersections.len() == 1); let intersect_point = curve.point_at_pos(intersections[0].0); @@ -44,20 +47,22 @@ fn find_intersection_with_horizontal_ray() { #[test] fn no_intersection_if_line_does_not_cross_curve() { // Line moves away from the curve - let line = (Coord2(0.0, 0.0), Coord2(-10.0, -10.0)); - let curve = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); + let line = (Coord2(0.0, 0.0), Coord2(-10.0, -10.0)); + let curve = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); assert!(intersections.len() == 0); } #[test] fn find_intersection_on_straight_line_against_ray() { // Line moves away from the curve so it doesn't intersect. When we use intersects_ray(), however, we find intersections anywhere along the line - let line = (Coord2(0.0, 0.0), Coord2(-10.0, -10.0)); - let curve = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); + let line = (Coord2(0.0, 0.0), Coord2(-10.0, -10.0)); + let curve = + line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); - let intersections = bezier::curve_intersects_ray(&curve, &line); + let intersections = bezier::curve_intersects_ray(&curve, &line); assert!(intersections.len() == 1); let intersect_point = curve.point_at_pos(intersections[0].0); @@ -66,58 +71,83 @@ fn find_intersection_on_straight_line_against_ray() { #[test] fn find_intersection_on_curve() { - let line = (Coord2(0.0, 6.0), Coord2(10.0, 4.0)); - let curve = bezier::Curve { - start_point: Coord2(0.0, 2.0), - end_point: Coord2(10.0, 8.0), - control_points: (Coord2(0.0, 20.0), Coord2(10.0, -10.0)) + let line = (Coord2(0.0, 6.0), Coord2(10.0, 4.0)); + let curve = bezier::Curve { + start_point: Coord2(0.0, 2.0), + end_point: Coord2(10.0, 8.0), + control_points: (Coord2(0.0, 20.0), Coord2(10.0, -10.0)), }; // Find the intersections - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); // Should be 3 intersections assert!(intersections.len() == 3); // Curve is symmetrical so the mid-point should be at 5,5 - assert!(curve.point_at_pos(intersections[1].0).distance_to(&Coord2(5.0, 5.0)) < 0.01); + assert!( + curve + .point_at_pos(intersections[1].0) + .distance_to(&Coord2(5.0, 5.0)) + < 0.01 + ); // Other points are a bit less precise - assert!(curve.point_at_pos(intersections[0].0).distance_to(&Coord2(0.260, 5.948)) < 0.01); - assert!(curve.point_at_pos(intersections[2].0).distance_to(&Coord2(9.740, 4.052)) < 0.01); + assert!( + curve + .point_at_pos(intersections[0].0) + .distance_to(&Coord2(0.260, 5.948)) + < 0.01 + ); + assert!( + curve + .point_at_pos(intersections[2].0) + .distance_to(&Coord2(9.740, 4.052)) + < 0.01 + ); } #[test] fn find_intersection_on_curve_short_line() { - let line = (Coord2(0.0, 6.0), Coord2(8.0, 4.4)); - let curve = bezier::Curve { - start_point: Coord2(0.0, 2.0), - end_point: Coord2(10.0, 8.0), - control_points: (Coord2(0.0, 20.0), Coord2(10.0, -10.0)) + let line = (Coord2(0.0, 6.0), Coord2(8.0, 4.4)); + let curve = bezier::Curve { + start_point: Coord2(0.0, 2.0), + end_point: Coord2(10.0, 8.0), + control_points: (Coord2(0.0, 20.0), Coord2(10.0, -10.0)), }; // Find the intersections - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); // Should be 2 intersections assert!(intersections.len() == 2); - assert!(curve.point_at_pos(intersections[1].0).distance_to(&Coord2(5.0, 5.0)) < 0.01); - assert!(curve.point_at_pos(intersections[0].0).distance_to(&Coord2(0.260, 5.948)) < 0.01); + assert!( + curve + .point_at_pos(intersections[1].0) + .distance_to(&Coord2(5.0, 5.0)) + < 0.01 + ); + assert!( + curve + .point_at_pos(intersections[0].0) + .distance_to(&Coord2(0.260, 5.948)) + < 0.01 + ); } #[test] fn dot_intersects_nothing() { // Line with 0 length - let line = (Coord2(4.0, 4.0), Coord2(4.0, 4.0)); - let curve = bezier::Curve { - start_point: Coord2(0.0, 2.0), - end_point: Coord2(10.0, 8.0), - control_points: (Coord2(0.0, 20.0), Coord2(10.0, -10.0)) + let line = (Coord2(4.0, 4.0), Coord2(4.0, 4.0)); + let curve = bezier::Curve { + start_point: Coord2(0.0, 2.0), + end_point: Coord2(10.0, 8.0), + control_points: (Coord2(0.0, 20.0), Coord2(10.0, -10.0)), }; // Find the intersections - let intersections = bezier::curve_intersects_line(&curve, &line); + let intersections = bezier::curve_intersects_line(&curve, &line); // Should be no intersections assert!(intersections.len() == 0); @@ -125,93 +155,122 @@ fn dot_intersects_nothing() { #[test] fn lines_intersect_at_start() { - let line1 = (Coord2(4.0, 4.0), Coord2(5.0, 8.0)); - let line2 = (Coord2(4.0, 4.0), Coord2(8.0, 5.0)); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&line2); + let line1 = (Coord2(4.0, 4.0), Coord2(5.0, 8.0)); + let line2 = (Coord2(4.0, 4.0), Coord2(8.0, 5.0)); + let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&line2); let intersections = bezier::curve_intersects_line(&curve2, &line1); assert!(intersections.len() == 1); assert!(intersections[0].0 < 0.01); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&Coord2(4.0, 4.0)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&Coord2(4.0, 4.0)) + < 0.01 + ); } #[test] fn lines_intersect_at_end() { - let line1 = (Coord2(5.0, 8.0), Coord2(4.0, 4.0)); - let line2 = (Coord2(8.0, 5.0), Coord2(4.0, 4.0)); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&line2); + let line1 = (Coord2(5.0, 8.0), Coord2(4.0, 4.0)); + let line2 = (Coord2(8.0, 5.0), Coord2(4.0, 4.0)); + let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&line2); let intersections = bezier::curve_intersects_line(&curve2, &line1); assert!(intersections.len() == 1); assert!(intersections[0].0 > 0.99); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&Coord2(4.0, 4.0)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&Coord2(4.0, 4.0)) + < 0.01 + ); } #[test] fn lines_intersect_start_to_end() { - let line1 = (Coord2(4.0, 4.0), Coord2(5.0, 8.0)); - let line2 = (Coord2(8.0, 5.0), Coord2(4.0, 4.0)); - let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&line2); + let line1 = (Coord2(4.0, 4.0), Coord2(5.0, 8.0)); + let line2 = (Coord2(8.0, 5.0), Coord2(4.0, 4.0)); + let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&line2); let intersections = bezier::curve_intersects_line(&curve2, &line1); assert!(intersections.len() == 1); assert!(intersections[0].0 > 0.99); - assert!(curve2.point_at_pos(intersections[0].0).distance_to(&Coord2(4.0, 4.0)) < 0.01); + assert!( + curve2 + .point_at_pos(intersections[0].0) + .distance_to(&Coord2(4.0, 4.0)) + < 0.01 + ); } #[test] fn ray_intersects_collinear_line_1() { // Ray intersecting a collinear line edge-on - let ray = (Coord2(0.0, 0.0), Coord2(2.0, 1.0)); - let line = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(4.0, 2.0), Coord2(8.0, 4.0))); + let ray = (Coord2(0.0, 0.0), Coord2(2.0, 1.0)); + let line = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(4.0, 2.0), Coord2(8.0, 4.0))); let intersections = bezier::curve_intersects_ray(&line, &ray); assert!(intersections.len() == 2); assert!(intersections[0].0 < 0.001); assert!(intersections[0].2.distance_to(&Coord2(4.0, 2.0)) < 0.01); - assert!((intersections[1].0-1.0).abs() < 0.001); + assert!((intersections[1].0 - 1.0).abs() < 0.001); assert!(intersections[1].2.distance_to(&Coord2(8.0, 4.0)) < 0.01); } #[test] fn ray_intersects_collinear_line_2() { // Intersecting a collinear line which has a point closer to the start of the ray than the start of the line - let ray = (Coord2(0.0, 0.0), Coord2(2.0, 1.0)); - let line = bezier::Curve::from_points(Coord2(4.0, 2.0), (Coord2(2.0, 1.0), Coord2(10.0, 5.0)), Coord2(8.0, 4.0)); // line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(4.0, 2.0), Coord2(8.0, 4.0))); + let ray = (Coord2(0.0, 0.0), Coord2(2.0, 1.0)); + let line = bezier::Curve::from_points( + Coord2(4.0, 2.0), + (Coord2(2.0, 1.0), Coord2(10.0, 5.0)), + Coord2(8.0, 4.0), + ); // line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(4.0, 2.0), Coord2(8.0, 4.0))); let intersections = bezier::curve_intersects_ray(&line, &ray); assert!(intersections.len() == 2); assert!(intersections[0].0 < 0.001); assert!(intersections[0].2.distance_to(&Coord2(4.0, 2.0)) < 0.01); - assert!((intersections[1].0-1.0).abs() < 0.001); + assert!((intersections[1].0 - 1.0).abs() < 0.001); assert!(intersections[1].2.distance_to(&Coord2(8.0, 4.0)) < 0.01); } #[test] fn ray_intersects_collinear_line_3() { // Line moving towards the start of the ray instead of away from it - let ray = (Coord2(0.0, 0.0), Coord2(2.0, 1.0)); - let line = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(8.0, 4.0), Coord2(4.0, 2.0))); + let ray = (Coord2(0.0, 0.0), Coord2(2.0, 1.0)); + let line = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(8.0, 4.0), Coord2(4.0, 2.0))); let intersections = bezier::curve_intersects_ray(&line, &ray); assert!(intersections.len() == 2); assert!(intersections[0].0.abs() < 0.001); assert!(intersections[0].2.distance_to(&Coord2(8.0, 4.0)) < 0.01); - assert!((intersections[1].0-1.0).abs() < 0.001); + assert!((intersections[1].0 - 1.0).abs() < 0.001); assert!(intersections[1].2.distance_to(&Coord2(4.0, 2.0)) < 0.01); } #[test] fn ray_intersects_curve_1() { // Failed intersection in ring_with_offset_crossbar_ray_casting_issue - let curve = bezier::Curve::from_points(Coord2(0.5857864376269051, 0.5857864376269049), (Coord2(0.488017920077567, 0.683554955176243), Coord2(0.40248767198507907, 0.7889273585090868)), Coord2(0.3291956933494412, 0.899999999999999)); - let ray = (Coord2(0.3853378796624052, 0.7560017173290998), Coord2(0.385337879662404, 1.0999999999999999)); + let curve = bezier::Curve::from_points( + Coord2(0.5857864376269051, 0.5857864376269049), + ( + Coord2(0.488017920077567, 0.683554955176243), + Coord2(0.40248767198507907, 0.7889273585090868), + ), + Coord2(0.3291956933494412, 0.899999999999999), + ); + let ray = ( + Coord2(0.3853378796624052, 0.7560017173290998), + Coord2(0.385337879662404, 1.0999999999999999), + ); let intersections = bezier::curve_intersects_ray(&curve, &ray); @@ -222,8 +281,18 @@ fn ray_intersects_curve_1() { #[test] fn ray_intersects_curve_1a() { // As above but the ray is less vertical - let curve = bezier::Curve::from_points(Coord2(0.5857864376269051, 0.5857864376269049), (Coord2(0.488017920077567, 0.683554955176243), Coord2(0.40248767198507907, 0.7889273585090868)), Coord2(0.3291956933494412, 0.899999999999999)); - let ray = (Coord2(0.3854378796624052, 0.7560017173290998), Coord2(0.385337879662404, 1.0999999999999999)); + let curve = bezier::Curve::from_points( + Coord2(0.5857864376269051, 0.5857864376269049), + ( + Coord2(0.488017920077567, 0.683554955176243), + Coord2(0.40248767198507907, 0.7889273585090868), + ), + Coord2(0.3291956933494412, 0.899999999999999), + ); + let ray = ( + Coord2(0.3854378796624052, 0.7560017173290998), + Coord2(0.385337879662404, 1.0999999999999999), + ); let intersections = bezier::curve_intersects_ray(&curve, &ray); @@ -234,8 +303,18 @@ fn ray_intersects_curve_1a() { #[test] fn ray_intersects_curve_1b() { // Failed intersection in ring_with_offset_crossbar_ray_casting_issue (different ray: all vertical rays seem to be an issue) - let curve = bezier::Curve::from_points(Coord2(0.5857864376269051, 0.5857864376269049), (Coord2(0.488017920077567, 0.683554955176243), Coord2(0.40248767198507907, 0.7889273585090868)), Coord2(0.3291956933494412, 0.899999999999999)); - let ray = (Coord2(0.395337879662404, 0.7560017173290998), Coord2(0.395337879662404, 1.0999999999999999)); + let curve = bezier::Curve::from_points( + Coord2(0.5857864376269051, 0.5857864376269049), + ( + Coord2(0.488017920077567, 0.683554955176243), + Coord2(0.40248767198507907, 0.7889273585090868), + ), + Coord2(0.3291956933494412, 0.899999999999999), + ); + let ray = ( + Coord2(0.395337879662404, 0.7560017173290998), + Coord2(0.395337879662404, 1.0999999999999999), + ); let intersections = bezier::curve_intersects_ray(&curve, &ray); @@ -246,8 +325,18 @@ fn ray_intersects_curve_1b() { #[test] fn ray_intersects_curve_1c() { // Horizontal ray to the collision point in 1a - let curve = bezier::Curve::from_points(Coord2(0.5857864376269051, 0.5857864376269049), (Coord2(0.488017920077567, 0.683554955176243), Coord2(0.40248767198507907, 0.7889273585090868)), Coord2(0.3291956933494412, 0.899999999999999)); - let ray = (Coord2(0.7560017173290998, 0.8192109187049827), Coord2(1.0999999999999999, 0.8192109187049827)); + let curve = bezier::Curve::from_points( + Coord2(0.5857864376269051, 0.5857864376269049), + ( + Coord2(0.488017920077567, 0.683554955176243), + Coord2(0.40248767198507907, 0.7889273585090868), + ), + Coord2(0.3291956933494412, 0.899999999999999), + ); + let ray = ( + Coord2(0.7560017173290998, 0.8192109187049827), + Coord2(1.0999999999999999, 0.8192109187049827), + ); let intersections = bezier::curve_intersects_ray(&curve, &ray); @@ -258,8 +347,18 @@ fn ray_intersects_curve_1c() { #[test] fn ray_intersects_curve_1d() { // Failed intersection in ring_with_offset_crossbar_ray_casting_issue (vertical ray to the collision point from 1a) - let curve = bezier::Curve::from_points(Coord2(0.5857864376269051, 0.5857864376269049), (Coord2(0.488017920077567, 0.683554955176243), Coord2(0.40248767198507907, 0.7889273585090868)), Coord2(0.3291956933494412, 0.899999999999999)); - let ray = (Coord2(0.38541950989400653, 0.7560017173290998), Coord2(0.38541950989400653, 1.0999999999999999)); + let curve = bezier::Curve::from_points( + Coord2(0.5857864376269051, 0.5857864376269049), + ( + Coord2(0.488017920077567, 0.683554955176243), + Coord2(0.40248767198507907, 0.7889273585090868), + ), + Coord2(0.3291956933494412, 0.899999999999999), + ); + let ray = ( + Coord2(0.38541950989400653, 0.7560017173290998), + Coord2(0.38541950989400653, 1.0999999999999999), + ); let intersections = bezier::curve_intersects_ray(&curve, &ray); @@ -270,9 +369,19 @@ fn ray_intersects_curve_1d() { #[test] fn ray_intersects_curve_1e() { // Same intersection but using the clipping algorithm (fails the same way as we eventually try to find via the root finder) - let curve = bezier::Curve::from_points(Coord2(0.5857864376269051, 0.5857864376269049), (Coord2(0.488017920077567, 0.683554955176243), Coord2(0.40248767198507907, 0.7889273585090868)), Coord2(0.3291956933494412, 0.899999999999999)); - let ray = (Coord2(0.3853378796624052, 0.0), Coord2(0.385337879662404, 10.0)); - let ray = line::line_to_bezier(&ray); + let curve = bezier::Curve::from_points( + Coord2(0.5857864376269051, 0.5857864376269049), + ( + Coord2(0.488017920077567, 0.683554955176243), + Coord2(0.40248767198507907, 0.7889273585090868), + ), + Coord2(0.3291956933494412, 0.899999999999999), + ); + let ray = ( + Coord2(0.3853378796624052, 0.0), + Coord2(0.385337879662404, 10.0), + ); + let ray = line::line_to_bezier(&ray); let intersections = bezier::curve_intersects_curve_clip(&curve, &ray, 0.01); @@ -293,21 +402,21 @@ fn roots_library_does_not_have_missing_root_bug() { let x = 0.754710877053; // Demonstrate that this is a root - assert!((a*x*x*x + b*x*x + c*x + d).abs() < 0.001); + assert!((a * x * x * x + b * x * x + c * x + d).abs() < 0.001); // Try to find this root let roots = find_roots_cubic(a, b, c, d); let roots = match roots { - Roots::No(_) => vec![], - Roots::One(r) => r.to_vec(), - Roots::Two(r) => r.to_vec(), + Roots::No(_) => vec![], + Roots::One(r) => r.to_vec(), + Roots::Two(r) => r.to_vec(), Roots::Three(r) => r.to_vec(), - Roots::Four(r) => r.to_vec() + Roots::Four(r) => r.to_vec(), }; // Should exist a root that's close to the value above println!("{:?}", roots); - assert!(roots.into_iter().any(|r| (r-x).abs() < 0.01)); + assert!(roots.into_iter().any(|r| (r - x).abs() < 0.01)); } #[test] @@ -323,21 +432,21 @@ fn ray_missing_root_2() { let x = 0.754710877053; // Demonstrate that this is a root - assert!((a*x*x*x + b*x*x + c*x + d).abs() < 0.001); + assert!((a * x * x * x + b * x * x + c * x + d).abs() < 0.001); // Try to find this root let roots = find_roots_cubic(a, b, c, d); let roots = match roots { - Roots::No(_) => vec![], - Roots::One(r) => r.to_vec(), - Roots::Two(r) => r.to_vec(), + Roots::No(_) => vec![], + Roots::One(r) => r.to_vec(), + Roots::Two(r) => r.to_vec(), Roots::Three(r) => r.to_vec(), - Roots::Four(r) => r.to_vec() + Roots::Four(r) => r.to_vec(), }; // Should exist a root that's close to the value above println!("{:?}", roots); - assert!(roots.into_iter().any(|r| (r-x).abs() < 0.01)); + assert!(roots.into_iter().any(|r| (r - x).abs() < 0.01)); } #[test] @@ -353,19 +462,19 @@ fn ray_missing_root_3() { let x = 0.754710877053; // Demonstrate that this is a root - assert!((a*x*x*x + b*x*x + c*x + d).abs() < 0.001); + assert!((a * x * x * x + b * x * x + c * x + d).abs() < 0.001); // Try to find this root let roots = find_roots_cubic(a, b, c, d); let roots = match roots { - Roots::No(_) => vec![], - Roots::One(r) => r.to_vec(), - Roots::Two(r) => r.to_vec(), + Roots::No(_) => vec![], + Roots::One(r) => r.to_vec(), + Roots::Two(r) => r.to_vec(), Roots::Three(r) => r.to_vec(), - Roots::Four(r) => r.to_vec() + Roots::Four(r) => r.to_vec(), }; // Should exist a root that's close to the value above println!("{:?}", roots); - assert!(roots.into_iter().any(|r| (r-x).abs() < 0.01)); + assert!(roots.into_iter().any(|r| (r - x).abs() < 0.01)); } diff --git a/tests/bezier/length.rs b/tests/bezier/length.rs index 5a33c496..01efc097 100644 --- a/tests/bezier/length.rs +++ b/tests/bezier/length.rs @@ -7,7 +7,7 @@ fn subdivide_length(curve: &Curve) -> f64 { let mut length = 0.0; for subsection in walk_curve_unevenly(curve, 1000) { - length += chord_length(&subsection); + length += chord_length(&subsection); } length @@ -15,9 +15,13 @@ fn subdivide_length(curve: &Curve) -> f64 { #[test] fn measure_point_length() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(412.0, 500.0)), Coord2(412.0, 500.0)); - let by_subdivision = subdivide_length(&c); - let by_measuring = curve_length(&c, 0.5); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(412.0, 500.0)), + Coord2(412.0, 500.0), + ); + let by_subdivision = subdivide_length(&c); + let by_measuring = curve_length(&c, 0.5); assert!((by_measuring - by_subdivision).abs() < 1.0); assert!(by_measuring.abs() < 0.1); @@ -25,36 +29,52 @@ fn measure_point_length() { #[test] fn measure_length_1() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let by_subdivision = subdivide_length(&c); - let by_measuring = curve_length(&c, 0.5); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let by_subdivision = subdivide_length(&c); + let by_measuring = curve_length(&c, 0.5); assert!((by_measuring - by_subdivision).abs() < 1.0); } #[test] fn measure_length_2() { - let c = Curve::from_points(Coord2(987.7637, 993.9645), (Coord2(991.1699, 994.0231), Coord2(1043.5605, 853.44885)), Coord2(1064.9473, 994.277)); - let by_subdivision = subdivide_length(&c); - let by_measuring = curve_length(&c, 0.5); + let c = Curve::from_points( + Coord2(987.7637, 993.9645), + (Coord2(991.1699, 994.0231), Coord2(1043.5605, 853.44885)), + Coord2(1064.9473, 994.277), + ); + let by_subdivision = subdivide_length(&c); + let by_measuring = curve_length(&c, 0.5); assert!((by_measuring - by_subdivision).abs() < 1.0); } #[test] fn measure_length_3() { - let c = Curve::from_points(Coord2(170.83203, 534.28906), (Coord2(140.99219, 492.1289), Coord2(0.52734375, 478.67188)), Coord2(262.95313, 533.2656)); - let by_subdivision = subdivide_length(&c); - let by_measuring = curve_length(&c, 0.5); + let c = Curve::from_points( + Coord2(170.83203, 534.28906), + (Coord2(140.99219, 492.1289), Coord2(0.52734375, 478.67188)), + Coord2(262.95313, 533.2656), + ); + let by_subdivision = subdivide_length(&c); + let by_measuring = curve_length(&c, 0.5); assert!((by_measuring - by_subdivision).abs() < 1.0); } #[test] fn measure_length_4() { - let c = Curve::from_points(Coord2(170.83203, 534.28906), (Coord2(35.15625, 502.65625), Coord2(0.52734375, 478.67188)), Coord2(262.95313, 533.2656)); - let by_subdivision = subdivide_length(&c); - let by_measuring = curve_length(&c, 0.5); + let c = Curve::from_points( + Coord2(170.83203, 534.28906), + (Coord2(35.15625, 502.65625), Coord2(0.52734375, 478.67188)), + Coord2(262.95313, 533.2656), + ); + let by_subdivision = subdivide_length(&c); + let by_measuring = curve_length(&c, 0.5); assert!((by_measuring - by_subdivision).abs() < 1.0); } diff --git a/tests/bezier/mod.rs b/tests/bezier/mod.rs index f398a8b6..b5050841 100644 --- a/tests/bezier/mod.rs +++ b/tests/bezier/mod.rs @@ -1,36 +1,40 @@ -use flo_curves::*; use flo_curves::bezier; +use flo_curves::*; -mod path; mod algorithms; +mod path; mod basis; -mod section; -mod subdivide; -mod derivative; -mod tangent; -mod normal; mod bounds; +mod characteristics; +mod curve_intersection_clip; mod deform; -mod search; -mod solve; +mod derivative; +mod distort; +mod intersection; +mod length; +mod normal; mod offset; mod overlaps; -mod intersection; -mod characteristics; +mod search; +mod section; mod self_intersection; -mod curve_intersection_clip; -mod length; +mod solve; +mod subdivide; +mod tangent; mod walk; -mod distort; pub fn approx_equal(a: f64, b: f64) -> bool { - f64::floor(f64::abs(a-b)*10000.0) == 0.0 + f64::floor(f64::abs(a - b) * 10000.0) == 0.0 } #[test] fn read_curve_control_points() { - let curve = bezier::Curve::from_points(Coord2(1.0, 1.0), (Coord2(3.0, 3.0), Coord2(4.0, 4.0)), Coord2(2.0, 2.0)); + let curve = bezier::Curve::from_points( + Coord2(1.0, 1.0), + (Coord2(3.0, 3.0), Coord2(4.0, 4.0)), + Coord2(2.0, 2.0), + ); assert!(curve.start_point() == Coord2(1.0, 1.0)); assert!(curve.end_point() == Coord2(2.0, 2.0)); @@ -39,13 +43,23 @@ fn read_curve_control_points() { #[test] fn read_curve_points() { - let curve = bezier::Curve::from_points(Coord2(1.0, 1.0), (Coord2(3.0, 3.0), Coord2(4.0, 4.0)), Coord2(2.0, 2.0)); + let curve = bezier::Curve::from_points( + Coord2(1.0, 1.0), + (Coord2(3.0, 3.0), Coord2(4.0, 4.0)), + Coord2(2.0, 2.0), + ); for x in 0..100 { - let t = (x as f64)/100.0; + let t = (x as f64) / 100.0; - let point = curve.point_at_pos(t); - let another_point = bezier::de_casteljau4(t, Coord2(1.0, 1.0), Coord2(3.0, 3.0), Coord2(4.0, 4.0), Coord2(2.0, 2.0)); + let point = curve.point_at_pos(t); + let another_point = bezier::de_casteljau4( + t, + Coord2(1.0, 1.0), + Coord2(3.0, 3.0), + Coord2(4.0, 4.0), + Coord2(2.0, 2.0), + ); assert!(point.distance_to(&another_point) < 0.001); } diff --git a/tests/bezier/normal.rs b/tests/bezier/normal.rs index 358526a6..541e8aae 100644 --- a/tests/bezier/normal.rs +++ b/tests/bezier/normal.rs @@ -1,11 +1,15 @@ -use flo_curves::*; use flo_curves::bezier; -use flo_curves::bezier::{NormalCurve}; +use flo_curves::bezier::NormalCurve; +use flo_curves::*; #[test] fn normal_for_line_is_straight_up() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(3.0, 0.0), Coord2(7.0, 0.0)), Coord2(10.0, 0.0)); - let normal = line.normal_at_pos(0.5); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(3.0, 0.0), Coord2(7.0, 0.0)), + Coord2(10.0, 0.0), + ); + let normal = line.normal_at_pos(0.5); // Normal should be a line facing up assert!(normal.x().abs() < 0.01); @@ -14,11 +18,15 @@ fn normal_for_line_is_straight_up() { #[test] fn normal_for_short_line_is_straight_up_1() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0000000003, 0.0), Coord2(0.0000000007, 0.0)), Coord2(0.0000000010, 0.0)); - let normal = line.normal_at_pos(0.5); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0000000003, 0.0), Coord2(0.0000000007, 0.0)), + Coord2(0.0000000010, 0.0), + ); + let normal = line.normal_at_pos(0.5); // Normals usually aren't unit vectors, but will produce very small values for very short lines - let normal = normal.to_unit_vector(); + let normal = normal.to_unit_vector(); // Normal should be a line facing up assert!(normal.x().abs() < 0.01); @@ -29,8 +37,12 @@ fn normal_for_short_line_is_straight_up_1() { fn normal_for_short_line_is_straight_up_2() { // IEEE floating point has extra precision for numbers very close to 0, so we also try with a short line 'far away' from 0 // (Will break down eventually when the line is far enough away as it will get represented as a point due to how floating point works) - let line = bezier::Curve::from_points(Coord2(10.0, 10.0), (Coord2(10.0000000003, 10.0), Coord2(10.0000000007, 10.0)), Coord2(10.0000000010, 10.0)); - let normal = line.normal_at_pos(0.5); + let line = bezier::Curve::from_points( + Coord2(10.0, 10.0), + (Coord2(10.0000000003, 10.0), Coord2(10.0000000007, 10.0)), + Coord2(10.0000000010, 10.0), + ); + let normal = line.normal_at_pos(0.5); // Normals usually aren't unit vectors, but will produce very small values for very short lines let normal = normal.to_unit_vector(); @@ -44,8 +56,12 @@ fn normal_for_short_line_is_straight_up_2() { fn normal_for_short_line_is_straight_up_2_t_0() { // IEEE floating point has extra precision for numbers very close to 0, so we also try with a short line 'far away' from 0 // (Will break down eventually when the line is far enough away as it will get represented as a point due to how floating point works) - let line = bezier::Curve::from_points(Coord2(10.0, 10.0), (Coord2(10.0000000003, 10.0), Coord2(10.0000000007, 10.0)), Coord2(10.0000000010, 10.0)); - let normal = line.normal_at_pos(0.0); + let line = bezier::Curve::from_points( + Coord2(10.0, 10.0), + (Coord2(10.0000000003, 10.0), Coord2(10.0000000007, 10.0)), + Coord2(10.0000000010, 10.0), + ); + let normal = line.normal_at_pos(0.0); // Normals usually aren't unit vectors, but will produce very small values for very short lines let normal = normal.to_unit_vector(); @@ -57,14 +73,18 @@ fn normal_for_short_line_is_straight_up_2_t_0() { #[test] fn normal_for_short_line_is_straight_up_overlapping_control_points_inside_line() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0000000000, 0.0), Coord2(0.0000000010, 0.0)), Coord2(0.0000000010, 0.0)); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0000000000, 0.0), Coord2(0.0000000010, 0.0)), + Coord2(0.0000000010, 0.0), + ); for t in 1..100 { - let t = (t as f64) / 100.0; - let normal = line.normal_at_pos(t); + let t = (t as f64) / 100.0; + let normal = line.normal_at_pos(t); // Normals usually aren't unit vectors, but will produce very small values for very short lines - let normal = normal.to_unit_vector(); + let normal = normal.to_unit_vector(); // Normal should be a line facing up assert!(normal.x().abs() < 0.01); @@ -74,13 +94,17 @@ fn normal_for_short_line_is_straight_up_overlapping_control_points_inside_line() #[test] fn normal_for_short_line_is_straight_up_overlapping_control_points_t_0() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0000000000, 0.0), Coord2(0.0000000010, 0.0)), Coord2(0.0000000010, 0.0)); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0000000000, 0.0), Coord2(0.0000000010, 0.0)), + Coord2(0.0000000010, 0.0), + ); - let t = 0.0; - let normal = line.normal_at_pos(t); + let t = 0.0; + let normal = line.normal_at_pos(t); // Normals usually aren't unit vectors, but will produce very small values for very short lines - let normal = normal.to_unit_vector(); + let normal = normal.to_unit_vector(); // Normal should be a line facing up assert!(normal.x().abs() < 0.01); @@ -89,13 +113,17 @@ fn normal_for_short_line_is_straight_up_overlapping_control_points_t_0() { #[test] fn normal_for_short_line_is_straight_up_overlapping_control_points_t_1() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0000000000, 0.0), Coord2(0.0000000010, 0.0)), Coord2(0.0000000010, 0.0)); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0000000000, 0.0), Coord2(0.0000000010, 0.0)), + Coord2(0.0000000010, 0.0), + ); - let t = 1.0; - let normal = line.normal_at_pos(t); + let t = 1.0; + let normal = line.normal_at_pos(t); // Normals usually aren't unit vectors, but will produce very small values for very short lines - let normal = normal.to_unit_vector(); + let normal = normal.to_unit_vector(); // Normal should be a line facing up assert!(normal.x().abs() < 0.01); @@ -104,10 +132,17 @@ fn normal_for_short_line_is_straight_up_overlapping_control_points_t_1() { #[test] fn normal_for_short_diagonal_line_is_diagonal() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0000000003, 0.0000000003), Coord2(0.0000000007, 0.0000000007)), Coord2(0.0000000010, 0.0000000010)); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + ( + Coord2(0.0000000003, 0.0000000003), + Coord2(0.0000000007, 0.0000000007), + ), + Coord2(0.0000000010, 0.0000000010), + ); for t in 0..101 { - let t = (t as f64) / 100.0; - let normal = line.normal_at_pos(t); + let t = (t as f64) / 100.0; + let normal = line.normal_at_pos(t); // Normals usually aren't unit vectors, but will produce very small values for very short lines let normal = normal.to_unit_vector(); @@ -115,16 +150,23 @@ fn normal_for_short_diagonal_line_is_diagonal() { // Normal should be a 45 degree diagonal line assert!(normal.x() < -0.01); assert!(normal.y() > 0.01); - assert!((-normal.x()-normal.y()).abs() < 0.01); + assert!((-normal.x() - normal.y()).abs() < 0.01); } } #[test] fn normal_for_short_diagonal_line_is_diagonal_overlapping_points() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0000000000, 0.0000000000), Coord2(0.0000000010, 0.0000000010)), Coord2(0.0000000010, 0.0000000010)); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + ( + Coord2(0.0000000000, 0.0000000000), + Coord2(0.0000000010, 0.0000000010), + ), + Coord2(0.0000000010, 0.0000000010), + ); for t in 0..101 { - let t = (t as f64) / 100.0; - let normal = line.normal_at_pos(t); + let t = (t as f64) / 100.0; + let normal = line.normal_at_pos(t); // Normals usually aren't unit vectors, but will produce very small values for very short lines let normal = normal.to_unit_vector(); @@ -132,14 +174,18 @@ fn normal_for_short_diagonal_line_is_diagonal_overlapping_points() { // Normal should be a 45 degree diagonal line assert!(normal.x() < -0.01); assert!(normal.y() > 0.01); - assert!((-normal.x()-normal.y()).abs() < 0.01); + assert!((-normal.x() - normal.y()).abs() < 0.01); } } #[test] fn normal_for_point() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0, 0.0), Coord2(0.0, 0.0)), Coord2(0.0, 0.0)); - let normal = line.normal_at_pos(0.5); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 0.0), Coord2(0.0, 0.0)), + Coord2(0.0, 0.0), + ); + let normal = line.normal_at_pos(0.5); // Normal should be the (0,0) vector (points don't have normals) assert!(normal.x().abs() < 0.0001); @@ -148,8 +194,12 @@ fn normal_for_point() { #[test] fn normal_at_start_of_curve_matches_control_points() { - let line = bezier::Curve::from_points(Coord2(0.0,0.0), (Coord2(0.0, 1.0), Coord2(7.0, 0.0)), Coord2(10.0, 0.0)); - let normal = line.normal_at_pos(0.0); + let line = bezier::Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.0, 1.0), Coord2(7.0, 0.0)), + Coord2(10.0, 0.0), + ); + let normal = line.normal_at_pos(0.0); // Normal should be a facing left assert!(normal.x() < 0.0); diff --git a/tests/bezier/offset.rs b/tests/bezier/offset.rs index bd5e7a80..2ca5fdf8 100644 --- a/tests/bezier/offset.rs +++ b/tests/bezier/offset.rs @@ -1,8 +1,8 @@ -use flo_curves::*; -use flo_curves::line; -use flo_curves::line::{Line2D}; -use flo_curves::bezier::*; use flo_curves::bezier::NormalCurve; +use flo_curves::bezier::*; +use flo_curves::line; +use flo_curves::line::Line2D; +use flo_curves::*; use std::f64; @@ -13,35 +13,42 @@ use std::f64; /// (When the offsets are different, there are a few choices for distance: we use the 't' value but it would be more /// correct to use curve length) /// -fn max_error(src_curve: &Curve, offset_curve: &Vec, initial_offset: f64, final_offset: f64) -> f64 -where Curve::Point: Coordinate2D+Normalize, -Curve: BezierCurve+NormalCurve { - let mut error = 0.0f64; - let mut last_closest: Option<(f64, Curve::Point)> = None; +fn max_error( + src_curve: &Curve, + offset_curve: &Vec, + initial_offset: f64, + final_offset: f64, +) -> f64 +where + Curve::Point: Coordinate2D + Normalize, + Curve: BezierCurve + NormalCurve, +{ + let mut error = 0.0f64; + let mut last_closest: Option<(f64, Curve::Point)> = None; for offset in offset_curve.iter() { for t in 1..=99 { - let t = (t as f64)/100.0; + let t = (t as f64) / 100.0; - let pos = offset.point_at_pos(t); - let normal = offset.normal_at_pos(t); - let intersect = curve_intersects_ray(src_curve, &(pos, pos+normal)); + let pos = offset.point_at_pos(t); + let normal = offset.normal_at_pos(t); + let intersect = curve_intersects_ray(src_curve, &(pos, pos + normal)); - let mut min_error = f64::MAX; + let mut min_error = f64::MAX; if let Some((last_expected_offset, last_point)) = last_closest { - let distance = last_point.distance_to(&pos); - min_error = min_error.min((distance-last_expected_offset).abs()); + let distance = last_point.distance_to(&pos); + min_error = min_error.min((distance - last_expected_offset).abs()); } for (curve_t, _, intersect_point) in intersect { - let expected_offset = (final_offset-initial_offset) * curve_t + initial_offset; + let expected_offset = (final_offset - initial_offset) * curve_t + initial_offset; - let distance = intersect_point.distance_to(&pos); - let error = (distance-expected_offset).abs(); + let distance = intersect_point.distance_to(&pos); + let error = (distance - expected_offset).abs(); if error < min_error { - min_error = error; - last_closest = Some((expected_offset, intersect_point)); + min_error = error; + last_closest = Some((expected_offset, intersect_point)); } } @@ -61,36 +68,52 @@ Curve: BezierCurve+NormalCurve { #[test] fn offset_overlap_start_point() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(error <= 3.5); } #[test] fn offset_overlap_end_point() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(163.0, 589.0), Coord2(308.0, 665.0)), Coord2(308.0, 665.0)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(163.0, 589.0), Coord2(308.0, 665.0)), + Coord2(308.0, 665.0), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(error <= 10.0); } #[test] fn simple_offset_1() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(163.0, 589.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(163.0, 589.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(error <= 2.0); } #[test] fn simple_offset_2() { - let c = Curve::from_points(Coord2(110.0, 110.0), (Coord2(110.0,300.0), Coord2(500.0,300.0)), Coord2(500.0,110.0)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(110.0, 110.0), + (Coord2(110.0, 300.0), Coord2(500.0, 300.0)), + Coord2(500.0, 110.0), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(error <= 2.0); } @@ -98,9 +121,16 @@ fn simple_offset_2() { #[test] fn simple_offset_3() { // This curve doesn't produce a very satisfying result, so it's interesting it has a low error value - let c = Curve::from_points(Coord2(516.170654296875, 893.27001953125), (Coord2(445.1522921545783, 856.2028149461783), Coord2(447.7831664737134, 878.3276285260063)), Coord2(450.51018453430754, 901.260980294519)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(516.170654296875, 893.27001953125), + ( + Coord2(445.1522921545783, 856.2028149461783), + Coord2(447.7831664737134, 878.3276285260063), + ), + Coord2(450.51018453430754, 901.260980294519), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(error <= 2.0); } @@ -108,9 +138,13 @@ fn simple_offset_3() { #[test] fn simple_offset_4() { // This curve seems to produce a huge spike - let c = Curve::from_points(Coord2(987.7637, 993.9645), (Coord2(991.1699, 994.0231), Coord2(1043.5605, 853.44885)), Coord2(1064.9473, 994.277)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(987.7637, 993.9645), + (Coord2(991.1699, 994.0231), Coord2(1043.5605, 853.44885)), + Coord2(1064.9473, 994.277), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(error <= 10.0); } @@ -120,55 +154,78 @@ fn simple_offset_5() { // This curve has a point approaching a cusp, so it produces 'strange' values // We bulge out slightly around the cusp so there's a large error - let c = Curve::from_points(Coord2(170.83203, 534.28906), (Coord2(140.99219, 492.1289), Coord2(0.52734375, 478.67188)), Coord2(262.95313, 533.2656)); - let offset_1 = offset(&c, 10.0, 10.0); - let error_1 = max_error(&c, &offset_1, 10.0, 10.0); + let c = Curve::from_points( + Coord2(170.83203, 534.28906), + (Coord2(140.99219, 492.1289), Coord2(0.52734375, 478.67188)), + Coord2(262.95313, 533.2656), + ); + let offset_1 = offset(&c, 10.0, 10.0); + let error_1 = max_error(&c, &offset_1, 10.0, 10.0); assert!(error_1 <= 12.0); // Offsetting too much 'inside' the curve starts to produce chaotic behaviour around the cusp with this algorithm - let offset_2 = offset(&c, -2.0, -2.0); - let error_2 = max_error(&c, &offset_2, 2.0, 2.0); + let offset_2 = offset(&c, -2.0, -2.0); + let error_2 = max_error(&c, &offset_2, 2.0, 2.0); assert!(error_2 <= 4.0); } #[test] fn simple_offset_6() { - let c = Curve::from_points(Coord2(170.83203, 534.28906), (Coord2(35.15625, 502.65625), Coord2(0.52734375, 478.67188)), Coord2(262.95313, 533.2656)); + let c = Curve::from_points( + Coord2(170.83203, 534.28906), + (Coord2(35.15625, 502.65625), Coord2(0.52734375, 478.67188)), + Coord2(262.95313, 533.2656), + ); // This is a very tight curve, so there's no good solution in this direction for large offsets (the scaling algorithm produces a very chaotic curve) - let offset_1 = offset(&c, 2.0, 2.0); - let error_1 = max_error(&c, &offset_1, 2.0, 2.0); + let offset_1 = offset(&c, 2.0, 2.0); + let error_1 = max_error(&c, &offset_1, 2.0, 2.0); assert!(error_1 <= 2.0); - let offset_2 = offset(&c, -10.0, -10.0); - let error_2 = max_error(&c, &offset_2, 10.0, 10.0); + let offset_2 = offset(&c, -10.0, -10.0); + let error_2 = max_error(&c, &offset_2, 10.0, 10.0); assert!(error_2 <= 1.0); } #[test] fn resizing_offset_1() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(163.0, 589.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let offset = offset(&c, 10.0, 40.0); - let error = max_error(&c, &offset, 10.0, 40.0); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(163.0, 589.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let offset = offset(&c, 10.0, 40.0); + let error = max_error(&c, &offset, 10.0, 40.0); assert!(error <= 2.0); } #[test] fn resizing_offset_2() { - let c = Curve::from_points(Coord2(110.0, 110.0), (Coord2(110.0,300.0), Coord2(500.0,300.0)), Coord2(500.0,110.0)); - let offset = offset(&c, 10.0, 40.0); - let error = max_error(&c, &offset, 10.0, 40.0); + let c = Curve::from_points( + Coord2(110.0, 110.0), + (Coord2(110.0, 300.0), Coord2(500.0, 300.0)), + Coord2(500.0, 110.0), + ); + let offset = offset(&c, 10.0, 40.0); + let error = max_error(&c, &offset, 10.0, 40.0); assert!(error <= 6.0); } #[test] fn resize_offset_3() { - let c = Curve::from_points(Coord2(516.170654296875, 893.27001953125), (Coord2(445.1522921545783, 856.2028149461783), Coord2(447.7831664737134, 878.3276285260063)), Coord2(450.51018453430754, 901.260980294519)); - let offset = offset(&c, 10.0, 40.0); - let error = max_error(&c, &offset, 10.0, 40.0); + let c = Curve::from_points( + Coord2(516.170654296875, 893.27001953125), + ( + Coord2(445.1522921545783, 856.2028149461783), + Coord2(447.7831664737134, 878.3276285260063), + ), + Coord2(450.51018453430754, 901.260980294519), + ); + let offset = offset(&c, 10.0, 40.0); + let error = max_error(&c, &offset, 10.0, 40.0); // The error seems to get so high because we're using the 't' value as a ratio for determining width rather than curve length // This also results in this offset curve not being particularly smooth @@ -177,15 +234,19 @@ fn resize_offset_3() { #[test] fn move_offset_1() { - let c = Curve::from_points(Coord2(163.0, 579.0), (Coord2(163.0, 579.0), Coord2(405.0, 684.0)), Coord2(405.0, 684.0)); - let offset = offset(&c, 10.0, 10.0); - let error = max_error(&c, &offset, 10.0, 10.0); + let c = Curve::from_points( + Coord2(163.0, 579.0), + (Coord2(163.0, 579.0), Coord2(405.0, 684.0)), + Coord2(405.0, 684.0), + ); + let offset = offset(&c, 10.0, 10.0); + let error = max_error(&c, &offset, 10.0, 10.0); assert!(offset.len() == 1); - let w1 = offset[0].start_point(); - let (w2, w3) = offset[0].control_points(); - let w4 = offset[0].end_point(); + let w1 = offset[0].start_point(); + let (w2, w3) = offset[0].control_points(); + let w4 = offset[0].end_point(); assert!((w2, w3).distance_to(&w1) < 0.01); assert!((w2, w3).distance_to(&w4) < 0.01); @@ -195,17 +256,22 @@ fn move_offset_1() { #[test] fn normals_for_line_do_not_meet_at_intersection() { // Overlapping control points mean that this curve defines a line - let c = Curve::from_points(Coord2(163.0, 579.0), (Coord2(163.0, 579.0), Coord2(405.0, 684.0)), Coord2(405.0, 684.0)); + let c = Curve::from_points( + Coord2(163.0, 579.0), + (Coord2(163.0, 579.0), Coord2(405.0, 684.0)), + Coord2(405.0, 684.0), + ); // Compute the normal at the start and the end of the line - let start = c.start_point(); - let end = c.end_point(); - let start_normal = c.normal_at_pos(0.0).to_unit_vector(); - let end_normal = c.normal_at_pos(1.0).to_unit_vector(); + let start = c.start_point(); + let end = c.end_point(); + let start_normal = c.normal_at_pos(0.0).to_unit_vector(); + let end_normal = c.normal_at_pos(1.0).to_unit_vector(); // The rays starting from the start and end of this line should not intersect // (This generates a ray divisor of 0.00000000000002603472992745992, because we lose enough precision that the lines appear to be not quite parallel) - let intersection = line::ray_intersects_ray(&(start, start+start_normal), &(end, end+end_normal)); + let intersection = + line::ray_intersects_ray(&(start, start + start_normal), &(end, end + end_normal)); assert!(intersection.is_none()); } @@ -214,15 +280,16 @@ fn offset_lms_sampling_arc_start_tangent() { use flo_curves::arc::*; // 90 degree circle arc - let circle = Circle::new(Coord2(0.0, 0.0), 100.0); - let arc = circle.arc(0.0, f64::consts::PI / 2.0); - let arc_curve = arc.to_bezier_curve::>(); + let circle = Circle::new(Coord2(0.0, 0.0), 100.0); + let arc = circle.arc(0.0, f64::consts::PI / 2.0); + let arc_curve = arc.to_bezier_curve::>(); // Offset by 10 - let offset_arc = offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 0.01).expect("Offset curve"); + let offset_arc = + offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 0.01).expect("Offset curve"); - let start_tangent_original = arc_curve.tangent_at_pos(0.0).to_unit_vector(); - let start_tangent_new = offset_arc[0].tangent_at_pos(0.0).to_unit_vector(); + let start_tangent_original = arc_curve.tangent_at_pos(0.0).to_unit_vector(); + let start_tangent_new = offset_arc[0].tangent_at_pos(0.0).to_unit_vector(); println!("{:?} {:?}", start_tangent_original, start_tangent_new); @@ -234,15 +301,18 @@ fn offset_lms_sampling_arc_end_tangent() { use flo_curves::arc::*; // 90 degree circle arc - let circle = Circle::new(Coord2(0.0, 0.0), 100.0); - let arc = circle.arc(0.0, f64::consts::PI / 2.0); - let arc_curve = arc.to_bezier_curve::>(); + let circle = Circle::new(Coord2(0.0, 0.0), 100.0); + let arc = circle.arc(0.0, f64::consts::PI / 2.0); + let arc_curve = arc.to_bezier_curve::>(); // Offset by 10 - let offset_arc = offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 0.01).expect("Offset curve"); + let offset_arc = + offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 0.01).expect("Offset curve"); - let end_tangent_original = arc_curve.tangent_at_pos(1.0).to_unit_vector(); - let end_tangent_new = offset_arc[offset_arc.len()-1].tangent_at_pos(1.0).to_unit_vector(); + let end_tangent_original = arc_curve.tangent_at_pos(1.0).to_unit_vector(); + let end_tangent_new = offset_arc[offset_arc.len() - 1] + .tangent_at_pos(1.0) + .to_unit_vector(); println!("{:?} {:?}", end_tangent_original, end_tangent_new); @@ -254,20 +324,21 @@ fn offset_lms_sampling_arc_end_point() { use flo_curves::arc::*; // 90 degree circle arc - let circle = Circle::new(Coord2(0.0, 0.0), 100.0); - let arc = circle.arc(0.0, f64::consts::PI / 2.0); - let arc_curve = arc.to_bezier_curve::>(); + let circle = Circle::new(Coord2(0.0, 0.0), 100.0); + let arc = circle.arc(0.0, f64::consts::PI / 2.0); + let arc_curve = arc.to_bezier_curve::>(); // Offset by 10 - let offset_arc = offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 0.01).expect("Offset curve"); + let offset_arc = + offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 0.01).expect("Offset curve"); - let end_point_original = arc_curve.point_at_pos(1.0); - let end_point_new = offset_arc[offset_arc.len()-1].point_at_pos(1.0); - let end_point_expected = Coord2(end_point_original.x() + 10.0, end_point_original.y()); + let end_point_original = arc_curve.point_at_pos(1.0); + let end_point_new = offset_arc[offset_arc.len() - 1].point_at_pos(1.0); + let end_point_expected = Coord2(end_point_original.x() + 10.0, end_point_original.y()); println!("{:?} {:?}", end_point_original, end_point_new); - assert!((end_point_original.distance_to(&end_point_new)-10.0).abs() < 0.01); + assert!((end_point_original.distance_to(&end_point_new) - 10.0).abs() < 0.01); assert!(end_point_expected.distance_to(&end_point_new) < 0.01); } @@ -276,12 +347,13 @@ fn offset_lms_sampling_arc_fit_single_curve() { use flo_curves::arc::*; // 90 degree circle arc - let circle = Circle::new(Coord2(0.0, 0.0), 100.0); - let arc = circle.arc(0.0, f64::consts::PI / 2.0); - let arc_curve = arc.to_bezier_curve::>(); + let circle = Circle::new(Coord2(0.0, 0.0), 100.0); + let arc = circle.arc(0.0, f64::consts::PI / 2.0); + let arc_curve = arc.to_bezier_curve::>(); // Offset by 10 - let offset_arc = offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 1.0).expect("Offset curve"); + let offset_arc = + offset_lms_sampling(&arc_curve, |_t| 10.0, |_t| 0.0, 20, 1.0).expect("Offset curve"); // We should be able to find a single bezier curve that fits these points assert!(offset_arc.len() == 1); diff --git a/tests/bezier/overlaps.rs b/tests/bezier/overlaps.rs index c6f88adb..57daa723 100644 --- a/tests/bezier/overlaps.rs +++ b/tests/bezier/overlaps.rs @@ -2,7 +2,11 @@ use flo_curves::bezier::*; #[test] fn simple_overlapping_curves() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); let section = curve1.section(0.3333, 0.6666); let overlaps = overlapping_region(&curve1, §ion).unwrap(); @@ -13,7 +17,11 @@ fn simple_overlapping_curves() { #[test] fn simple_overlapping_curves_curve2_larger() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); let section = curve1.section(0.3333, 0.6666); let overlaps = overlapping_region(§ion, &curve1).unwrap(); @@ -27,8 +35,16 @@ fn simple_overlapping_curves_curve2_larger() { #[test] fn simple_overlapping_curves_same() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); - let section = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); + let section = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); let overlaps = overlapping_region(&curve1, §ion).unwrap(); @@ -38,8 +54,16 @@ fn simple_overlapping_curves_same() { #[test] fn simple_overlapping_curves_reversed() { - let curve1 = Curve::from_points(Coord2(220.0, 220.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(10.0, 100.0)); - let section = Curve::from_points(Coord2(10.0, 100.0), (Coord2(40.0, 140.0), Coord2(90.0, 30.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(220.0, 220.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(10.0, 100.0), + ); + let section = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(40.0, 140.0), Coord2(90.0, 30.0)), + Coord2(220.0, 220.0), + ); let overlaps = overlapping_region(&curve1, §ion).unwrap(); @@ -49,7 +73,11 @@ fn simple_overlapping_curves_reversed() { #[test] fn simple_overlapping_lines() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), Coord2(220.0, 100.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), + Coord2(220.0, 100.0), + ); let section = curve1.section(0.3333, 0.6666); let overlaps = overlapping_region(&curve1, §ion).unwrap(); @@ -60,8 +88,16 @@ fn simple_overlapping_lines() { #[test] fn overlapping_lines_same() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), Coord2(220.0, 100.0)); - let section = Curve::from_points(Coord2(10.0, 100.0), (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), Coord2(220.0, 100.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), + Coord2(220.0, 100.0), + ); + let section = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), + Coord2(220.0, 100.0), + ); let overlaps = overlapping_region(&curve1, §ion).unwrap(); @@ -71,8 +107,16 @@ fn overlapping_lines_same() { #[test] fn overlapping_lines_with_different_t_values() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), Coord2(220.0, 100.0)); - let section = Curve::from_points(Coord2(10.0, 100.0), (Coord2(50.0, 100.0), Coord2(180.0, 100.0)), Coord2(220.0, 100.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(30.0, 100.0), Coord2(200.0, 100.0)), + Coord2(220.0, 100.0), + ); + let section = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(50.0, 100.0), Coord2(180.0, 100.0)), + Coord2(220.0, 100.0), + ); let overlaps = overlapping_region(&curve1, §ion).unwrap(); @@ -83,12 +127,26 @@ fn overlapping_lines_with_different_t_values() { #[test] fn overlaps_with_known_curve_1() { // These curves should overlap - let curve1 = Curve::from_points(Coord2(346.69864, 710.2048), (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), Coord2(356.28525, 698.20306)); - let curve2 = Curve::from_points(Coord2(350.22574, 706.551), (Coord2(354.72943, 701.2933), Coord2(358.0882, 695.26)), Coord2(361.0284, 690.2511)); + let curve1 = Curve::from_points( + Coord2(346.69864, 710.2048), + (Coord2(350.41446, 706.8076), Coord2(353.61026, 702.4266)), + Coord2(356.28525, 698.20306), + ); + let curve2 = Curve::from_points( + Coord2(350.22574, 706.551), + (Coord2(354.72943, 701.2933), Coord2(358.0882, 695.26)), + Coord2(361.0284, 690.2511), + ); // They currently don't - assert!(curve1.t_for_point(&curve2.start_point()).is_some() || curve2.t_for_point(&curve1.start_point()).is_some()); - assert!(curve1.t_for_point(&curve2.end_point()).is_some() || curve2.t_for_point(&curve1.end_point()).is_some()); + assert!( + curve1.t_for_point(&curve2.start_point()).is_some() + || curve2.t_for_point(&curve1.start_point()).is_some() + ); + assert!( + curve1.t_for_point(&curve2.end_point()).is_some() + || curve2.t_for_point(&curve1.end_point()).is_some() + ); assert!(!overlapping_region(&curve1, &curve2).is_some()); } @@ -96,12 +154,32 @@ fn overlaps_with_known_curve_1() { #[test] fn overlaps_with_known_curve_2() { // These curves should overlap - let curve1 = Curve::from_points(Coord2(305.86907958984375, 882.2529296875), (Coord2(305.41015625, 880.7345581054688), Coord2(303.0707092285156, 879.744140625)), Coord2(298.0640869140625, 875.537353515625)); - let curve2 = Curve::from_points(Coord2(302.7962341308594, 879.1681518554688), (Coord2(299.5769348144531, 876.8582763671875), Coord2(297.1976318359375, 874.7939453125)), Coord2(301.4282531738281, 878.26220703125)); + let curve1 = Curve::from_points( + Coord2(305.86907958984375, 882.2529296875), + ( + Coord2(305.41015625, 880.7345581054688), + Coord2(303.0707092285156, 879.744140625), + ), + Coord2(298.0640869140625, 875.537353515625), + ); + let curve2 = Curve::from_points( + Coord2(302.7962341308594, 879.1681518554688), + ( + Coord2(299.5769348144531, 876.8582763671875), + Coord2(297.1976318359375, 874.7939453125), + ), + Coord2(301.4282531738281, 878.26220703125), + ); // They currently don't - assert!(!curve1.t_for_point(&curve2.start_point()).is_some() || curve2.t_for_point(&curve1.start_point()).is_some()); - assert!(curve1.t_for_point(&curve2.end_point()).is_some() || curve2.t_for_point(&curve1.end_point()).is_some()); + assert!( + !curve1.t_for_point(&curve2.start_point()).is_some() + || curve2.t_for_point(&curve1.start_point()).is_some() + ); + assert!( + curve1.t_for_point(&curve2.end_point()).is_some() + || curve2.t_for_point(&curve1.end_point()).is_some() + ); assert!(!overlapping_region(&curve1, &curve2).is_some()); } @@ -109,8 +187,22 @@ fn overlaps_with_known_curve_2() { #[test] fn overlaps_with_known_curve_3() { // These curves should overlap - let curve1 = Curve::from_points(Coord2(510.6888427734375, 684.9293212890625), (Coord2(511.68206787109375, 683.7874145507813), Coord2(512.7827758789063, 682.6954345703125)), Coord2(513.9757080078125, 681.668212890625)); - let curve2 = Curve::from_points(Coord2(510.6888427734375, 684.9293212890625), (Coord2(511.66473388671875, 683.8077392578125), Coord2(512.7447509765625, 682.73388671875)), Coord2(513.9143676757813, 681.7202758789063)); + let curve1 = Curve::from_points( + Coord2(510.6888427734375, 684.9293212890625), + ( + Coord2(511.68206787109375, 683.7874145507813), + Coord2(512.7827758789063, 682.6954345703125), + ), + Coord2(513.9757080078125, 681.668212890625), + ); + let curve2 = Curve::from_points( + Coord2(510.6888427734375, 684.9293212890625), + ( + Coord2(511.66473388671875, 683.8077392578125), + Coord2(512.7447509765625, 682.73388671875), + ), + Coord2(513.9143676757813, 681.7202758789063), + ); assert!(overlapping_region(&curve1, &curve2).is_some()); assert!(overlapping_region(&curve2, &curve1).is_some()); diff --git a/tests/bezier/path/arithmetic_add.rs b/tests/bezier/path/arithmetic_add.rs index 03af4d84..5fa8c8e8 100644 --- a/tests/bezier/path/arithmetic_add.rs +++ b/tests/bezier/path/arithmetic_add.rs @@ -1,7 +1,7 @@ -use flo_curves::*; use flo_curves::arc::*; use flo_curves::bezier::path::*; use flo_curves::debug::*; +use flo_curves::*; use super::svg::*; @@ -17,27 +17,41 @@ fn add_two_overlapping_circles() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(7.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 2); assert!(num_points_on_circle2 == 2); @@ -56,13 +70,16 @@ fn add_two_identical_circles() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point).collect::>(); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point) + .collect::>(); for point in points.iter() { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01); + assert!((distance_to_circle1 - 4.0).abs() < 0.01); println!("{:?} {:?}", point, distance_to_circle1); } @@ -85,13 +102,15 @@ fn add_two_very_close_circles() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.1); + assert!((distance_to_circle1 - 4.0).abs() < 0.1); println!("{:?} {:?}", point, distance_to_circle1); } @@ -111,13 +130,15 @@ fn add_two_close_circles() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); for point in points { let distance_to_circle1 = Coord2(500.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-300.0).abs() < 10.0); + assert!((distance_to_circle1 - 300.0).abs() < 10.0); println!("{:?} {:?}", point, distance_to_circle1); } @@ -130,32 +151,52 @@ fn add_two_overlapping_circles_via_combination_chain() { let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_combine::(PathCombine::Add(vec![PathCombine::Path(vec![circle1]), PathCombine::Path(vec![circle2])]), 0.01); + let combined_circles = path_combine::( + PathCombine::Add(vec![ + PathCombine::Path(vec![circle1]), + PathCombine::Path(vec![circle2]), + ]), + 0.01, + ); assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(7.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 2); assert!(num_points_on_circle2 == 2); @@ -165,11 +206,14 @@ fn add_two_overlapping_circles_via_combination_chain() { #[test] fn add_series_of_circles_via_combination_chain() { // Two overlapping circles - let circles = (0..4).into_iter() - .map(|idx| Circle::new(Coord2(5.0 + (idx as f64)*2.0, 4.0), 4.0).to_path::()) + let circles = (0..4) + .into_iter() + .map(|idx| { + Circle::new(Coord2(5.0 + (idx as f64) * 2.0, 4.0), 4.0).to_path::() + }) .map(|circle| PathCombine::Path(vec![circle])); - let combine = PathCombine::Add(circles.collect()); - let combined_circles = path_combine::(combine, 0.01); + let combine = PathCombine::Add(circles.collect()); + let combined_circles = path_combine::(combine, 0.01); assert!(combined_circles.len() == 1); } @@ -186,16 +230,20 @@ fn add_circle_inside_circle() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; + let mut num_points_on_circle1 = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); // Must be on the circle - assert!((distance_to_circle1-4.0).abs() < 0.01); - if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } + assert!((distance_to_circle1 - 4.0).abs() < 0.01); + if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } } assert!(num_points_on_circle1 == 4); @@ -213,27 +261,41 @@ fn add_two_overlapping_circles_further_apart() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(12.9, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 4); assert!(num_points_on_circle2 == 4); @@ -254,27 +316,41 @@ fn add_two_overlapping_circles_with_one_reversed() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(7.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 2); assert!(num_points_on_circle2 == 2); @@ -297,10 +373,10 @@ fn add_two_non_overlapping_circles() { #[test] fn add_two_doughnuts() { // Two overlapping circles - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); println!("{}", svg_path_string(&circle1)); println!("{}", svg_path_string(&inner_circle1)); @@ -308,18 +384,28 @@ fn add_two_doughnuts() { println!("{}", svg_path_string(&inner_circle2)); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1, inner_circle1], &vec![circle2, inner_circle2], 0.09); + let combined_circles = path_add::<_, _, SimpleBezierPath>( + &vec![circle1, inner_circle1], + &vec![circle2, inner_circle2], + 0.09, + ); println!("{:?}", combined_circles.len()); println!("{:?}", combined_circles); - println!("{:?}", combined_circles.iter().map(|path| svg_path_string(path)).collect::>()); + println!( + "{:?}", + combined_circles + .iter() + .map(|path| svg_path_string(path)) + .collect::>() + ); assert!(combined_circles.len() == 4); } #[test] fn remove_interior_from_circle_removes_nothing() { - let circle = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![circle.clone()], 0.1); + let circle = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![circle.clone()], 0.1); assert!(removed.len() == 1); assert!(removed[0].1.len() == circle.1.len()); @@ -333,10 +419,13 @@ fn remove_interior_from_circle_removes_nothing() { #[test] fn remove_interior_for_ring_removes_center() { - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone()], 0.01); + let removed = path_remove_interior_points::<_, SimpleBezierPath>( + &vec![ring1.clone(), ring2.clone()], + 0.01, + ); assert!(removed.len() == 1); @@ -349,16 +438,19 @@ fn remove_interior_for_ring_removes_center() { #[test] fn remove_interior_for_ring_with_crossbar_removes_center() { - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 1.9)) + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); + let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 1.9)) .line_to(Coord2(0.2, 2.1)) .line_to(Coord2(3.8, 2.1)) .line_to(Coord2(3.8, 1.9)) .line_to(Coord2(0.2, 1.9)) .build(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone(), crossbar1.clone()], 0.01); + let removed = path_remove_interior_points::<_, SimpleBezierPath>( + &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + 0.01, + ); assert!(removed.len() == 1); @@ -371,9 +463,9 @@ fn remove_interior_for_ring_with_crossbar_removes_center() { #[test] fn remove_interior_for_ring_with_offset_crossbar_removes_center() { - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.7).to_path::(); - let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 0.9)) + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.7).to_path::(); + let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 0.9)) .line_to(Coord2(0.2, 1.1)) .line_to(Coord2(3.8, 1.1)) .line_to(Coord2(3.8, 0.9)) @@ -383,7 +475,10 @@ fn remove_interior_for_ring_with_offset_crossbar_removes_center() { // Create the graph path from the source side let path = vec![ring1.clone(), ring2.clone(), crossbar1.clone()]; let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path.iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path.iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide the path with itself to find the intersections merged_path.self_collide(0.01); @@ -394,7 +489,10 @@ fn remove_interior_for_ring_with_offset_crossbar_removes_center() { println!("{}", graph_path_svg_string(&merged_path, vec![])); // Try the actual removing operation - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone(), crossbar1.clone()], 0.01); + let removed = path_remove_interior_points::<_, SimpleBezierPath>( + &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + 0.01, + ); assert!(removed.len() == 1); } @@ -402,9 +500,9 @@ fn remove_interior_for_ring_with_offset_crossbar_removes_center() { #[test] fn ring_with_offset_crossbar_ray_casting_issue() { // Hit a bug where the ray (Coord2(0.3853378796624052, 0.7560017173290998), Coord2(0.385337879662404, 1.0999999999999999)) seems to be missing intersections - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.7).to_path::(); - let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 0.9)) + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.7).to_path::(); + let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 0.9)) .line_to(Coord2(0.2, 1.1)) .line_to(Coord2(3.8, 1.1)) .line_to(Coord2(3.8, 0.9)) @@ -413,32 +511,46 @@ fn ring_with_offset_crossbar_ray_casting_issue() { let path = vec![ring1.clone(), ring2.clone(), crossbar1.clone()]; let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(path.iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + path.iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); merged_path.self_collide(0.01); merged_path.round(0.01); - let ray_cast = merged_path.ray_collisions(&(Coord2(0.3853378796624052, 0.7560017173290998), Coord2(0.385337879662404, 1.0999999999999999))); + let ray_cast = merged_path.ray_collisions(&( + Coord2(0.3853378796624052, 0.7560017173290998), + Coord2(0.385337879662404, 1.0999999999999999), + )); println!("{}: {:?}", ray_cast.len(), ray_cast); assert!(ray_cast.len() == 6); } #[test] fn remove_interior_for_ring_with_cross_removes_center() { - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 1.9)) + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); + let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 1.9)) .line_to(Coord2(0.2, 2.1)) .line_to(Coord2(3.8, 2.1)) .line_to(Coord2(3.8, 1.9)) .build(); - let crossbar2 = BezierPathBuilder::::start(Coord2(1.9, 0.2)) + let crossbar2 = BezierPathBuilder::::start(Coord2(1.9, 0.2)) .line_to(Coord2(2.1, 0.2)) .line_to(Coord2(2.1, 3.8)) .line_to(Coord2(1.9, 3.8)) .build(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone(), crossbar1.clone(), crossbar2.clone()], 0.01); + let removed = path_remove_interior_points::<_, SimpleBezierPath>( + &vec![ + ring1.clone(), + ring2.clone(), + crossbar1.clone(), + crossbar2.clone(), + ], + 0.01, + ); // Check the removed result assert!(removed.len() == 1); @@ -452,10 +564,13 @@ fn remove_interior_for_ring_with_cross_removes_center() { #[test] fn remove_overlapped_for_ring_does_not_remove_center() { - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let removed = path_remove_overlapped_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone()], 0.01); + let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( + &vec![ring1.clone(), ring2.clone()], + 0.01, + ); assert!(removed.len() == 2); @@ -475,16 +590,19 @@ fn remove_overlapped_for_ring_does_not_remove_center() { #[test] fn remove_overlapped_for_ring_with_overlapping_crossbar() { // Crossbar overlaps both the main ring and the center - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 1.9)) + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); + let crossbar1 = BezierPathBuilder::::start(Coord2(0.2, 1.9)) .line_to(Coord2(0.2, 2.1)) .line_to(Coord2(3.8, 2.1)) .line_to(Coord2(3.8, 1.9)) .line_to(Coord2(0.2, 1.9)) .build(); - let removed = path_remove_overlapped_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone(), crossbar1.clone()], 0.01); + let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( + &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + 0.01, + ); assert!(removed.len() == 5); } @@ -492,15 +610,18 @@ fn remove_overlapped_for_ring_with_overlapping_crossbar() { #[test] fn remove_overlapped_for_ring_with_crossbar_in_space() { // Crossbar is floating in space here - let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let crossbar1 = BezierPathBuilder::::start(Coord2(1.6, 0.9)) + let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); + let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); + let crossbar1 = BezierPathBuilder::::start(Coord2(1.6, 0.9)) .line_to(Coord2(1.6, 1.1)) .line_to(Coord2(2.4, 1.1)) .line_to(Coord2(2.4, 0.9)) .build(); - let removed = path_remove_overlapped_points::<_, SimpleBezierPath>(&vec![ring1.clone(), ring2.clone(), crossbar1.clone()], 0.01); + let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( + &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + 0.01, + ); assert!(removed.len() == 3); } @@ -516,7 +637,8 @@ fn remove_interior_points_basic() { .line_to(Coord2(1.0, 1.0)) .build(); - let with_points_removed: Vec = path_remove_interior_points(&vec![with_interior_point], 0.1); + let with_points_removed: Vec = + path_remove_interior_points(&vec![with_interior_point], 0.1); // Should be 5 points in the path with points removed assert!(with_points_removed.len() == 1); @@ -528,12 +650,16 @@ fn remove_interior_points_basic() { Coord2(1.0, 5.0), Coord2(5.0, 5.0), Coord2(5.0, 1.0), - Coord2(3.0, 3.0) + Coord2(3.0, 3.0), ]; - assert!(expected_points.iter().any(|expected| with_points_removed[0].start_point().distance_to(expected) < 0.1)); + assert!(expected_points + .iter() + .any(|expected| with_points_removed[0].start_point().distance_to(expected) < 0.1)); for (_cp1, _cp2, point) in with_points_removed[0].points() { - assert!(expected_points.iter().any(|expected| point.distance_to(expected) < 0.1)); + assert!(expected_points + .iter() + .any(|expected| point.distance_to(expected) < 0.1)); } } @@ -548,11 +674,11 @@ fn self_collide_is_stable() { .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&with_interior_point, ()); + let mut graph_path = GraphPath::from_path(&with_interior_point, ()); graph_path.self_collide(0.1); - let initial_num_points = graph_path.num_points(); - let initial_num_edges = graph_path.all_edges().count(); + let initial_num_points = graph_path.num_points(); + let initial_num_edges = graph_path.all_edges().count(); graph_path.self_collide(0.1); assert!(graph_path.all_edges().count() == initial_num_edges); @@ -598,12 +724,13 @@ fn rectangle_add() { .build(); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -635,12 +762,13 @@ fn rectangle_add_with_shared_point() { .build(); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -672,12 +800,13 @@ fn rectangle_add_with_shared_point_2() { .build(); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -711,12 +840,13 @@ fn rectangle_add_with_shared_point_3() { .build(); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -751,17 +881,21 @@ fn rectangle_add_with_shared_point_4() { .reversed::(); // Print out the graph path generated by adding these two points - let mut gp = GraphPath::from_path(&rectangle1, PathLabel(0, PathDirection::Clockwise)).collide(GraphPath::from_path(&rectangle2, PathLabel(1, PathDirection::Clockwise)), 0.01); + let mut gp = GraphPath::from_path(&rectangle1, PathLabel(0, PathDirection::Clockwise)).collide( + GraphPath::from_path(&rectangle2, PathLabel(1, PathDirection::Clockwise)), + 0.01, + ); gp.set_exterior_by_adding(); println!("{:?}", gp); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -796,12 +930,13 @@ fn rectangle_add_with_shared_point_5() { .build(); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(1.0, 5.0)) < 0.1); @@ -834,12 +969,13 @@ fn rectangle_add_with_shared_point_6() { .build(); // Add them - let shared_point = path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let shared_point = + path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(1.0, 5.0)) < 0.1); diff --git a/tests/bezier/path/arithmetic_chain_add.rs b/tests/bezier/path/arithmetic_chain_add.rs index eeeaaac9..b723f430 100644 --- a/tests/bezier/path/arithmetic_chain_add.rs +++ b/tests/bezier/path/arithmetic_chain_add.rs @@ -1,6 +1,6 @@ -use flo_curves::*; use flo_curves::arc::*; use flo_curves::bezier::path::*; +use flo_curves::*; use super::svg::*; @@ -11,32 +11,47 @@ fn add_two_overlapping_circles() { let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); + let combined_circles = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(7.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 2); assert!(num_points_on_circle2 == 2); @@ -50,21 +65,26 @@ fn add_circle_inside_circle() { let circle2 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); // Combine them - let combined_circles = path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); + let combined_circles = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; + let mut num_points_on_circle1 = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); // Must be on the circle - assert!((distance_to_circle1-4.0).abs() < 0.01); - if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } + assert!((distance_to_circle1 - 4.0).abs() < 0.01); + if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } } assert!(num_points_on_circle1 == 4); @@ -77,32 +97,47 @@ fn add_two_overlapping_circles_further_apart() { let circle2 = Circle::new(Coord2(12.9, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); + let combined_circles = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(12.9, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 4); assert!(num_points_on_circle2 == 4); @@ -117,33 +152,48 @@ fn add_two_overlapping_circles_with_one_reversed() { let circle2 = circle2.reversed::(); // Combine them - let combined_circles = path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); + let combined_circles = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.01); println!("{:?}", combined_circles); assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(7.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 2); assert!(num_points_on_circle2 == 2); @@ -157,7 +207,8 @@ fn add_two_non_overlapping_circles() { let circle2 = Circle::new(Coord2(20.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.1); + let combined_circles = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1], vec![circle2]], 0.1); println!("{:?}", combined_circles); assert!(combined_circles.len() == 2); @@ -166,10 +217,10 @@ fn add_two_non_overlapping_circles() { #[test] fn add_two_doughnuts() { // Two overlapping circles - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); println!("{}", svg_path_string(&circle1)); println!("{}", svg_path_string(&inner_circle1)); @@ -177,11 +228,20 @@ fn add_two_doughnuts() { println!("{}", svg_path_string(&inner_circle2)); // Combine them - let combined_circles = path_add_chain::<_, SimpleBezierPath>(&vec![vec![circle1, inner_circle1], vec![circle2, inner_circle2]], 0.09); + let combined_circles = path_add_chain::<_, SimpleBezierPath>( + &vec![vec![circle1, inner_circle1], vec![circle2, inner_circle2]], + 0.09, + ); println!("{:?}", combined_circles.len()); println!("{:?}", combined_circles); - println!("{:?}", combined_circles.iter().map(|path| svg_path_string(path)).collect::>()); + println!( + "{:?}", + combined_circles + .iter() + .map(|path| svg_path_string(path)) + .collect::>() + ); assert!(combined_circles.len() == 4); } @@ -196,7 +256,8 @@ fn remove_interior_points_basic() { .line_to(Coord2(1.0, 1.0)) .build(); - let with_points_removed: Vec = path_remove_interior_points(&vec![with_interior_point], 0.1); + let with_points_removed: Vec = + path_remove_interior_points(&vec![with_interior_point], 0.1); // Should be 5 points in the path with points removed assert!(with_points_removed.len() == 1); @@ -208,12 +269,16 @@ fn remove_interior_points_basic() { Coord2(1.0, 5.0), Coord2(5.0, 5.0), Coord2(5.0, 1.0), - Coord2(3.0, 3.0) + Coord2(3.0, 3.0), ]; - assert!(expected_points.iter().any(|expected| with_points_removed[0].start_point().distance_to(expected) < 0.1)); + assert!(expected_points + .iter() + .any(|expected| with_points_removed[0].start_point().distance_to(expected) < 0.1)); for (_cp1, _cp2, point) in with_points_removed[0].points() { - assert!(expected_points.iter().any(|expected| point.distance_to(expected) < 0.1)); + assert!(expected_points + .iter() + .any(|expected| point.distance_to(expected) < 0.1)); } } @@ -256,12 +321,13 @@ fn rectangle_add() { .build(); // Add them - let shared_point = path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); + let shared_point = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -293,12 +359,13 @@ fn rectangle_add_with_shared_point() { .build(); // Add them - let shared_point = path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); + let shared_point = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -330,12 +397,13 @@ fn rectangle_add_with_shared_point_2() { .build(); // Add them - let shared_point = path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); + let shared_point = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -369,12 +437,13 @@ fn rectangle_add_with_shared_point_3() { .build(); // Add them - let shared_point = path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); + let shared_point = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -409,12 +478,13 @@ fn rectangle_add_with_shared_point_5() { .build(); // Add them - let shared_point = path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); + let shared_point = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(1.0, 5.0)) < 0.1); @@ -447,12 +517,13 @@ fn rectangle_add_with_shared_point_6() { .build(); // Add them - let shared_point = path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); + let shared_point = + path_add_chain::<_, SimpleBezierPath>(&vec![vec![rectangle1], vec![rectangle2]], 0.01); assert!(shared_point.len() == 1); - let shared_point = &shared_point[0]; - let points = shared_point.points().collect::>(); + let shared_point = &shared_point[0]; + let points = shared_point.points().collect::>(); assert!(shared_point.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(1.0, 5.0)) < 0.1); diff --git a/tests/bezier/path/arithmetic_complicated_paths.rs b/tests/bezier/path/arithmetic_complicated_paths.rs index daf72b95..396950c0 100644 --- a/tests/bezier/path/arithmetic_complicated_paths.rs +++ b/tests/bezier/path/arithmetic_complicated_paths.rs @@ -1,76 +1,417 @@ -use flo_curves::*; -use flo_curves::debug::*; use flo_curves::bezier::path::*; +use flo_curves::debug::*; +use flo_curves::*; use super::svg::*; #[test] fn remove_interior_points_1() { // Complicated curve found in FlowBetween that produces 0 points when interior points are removed - // It appears this has three curves that converge on a single point, which generates two points in the output, + // It appears this has three curves that converge on a single point, which generates two points in the output, // which in turn produces a spurious edge, which prevents us from being able to follow the path all the way around. - let curve = BezierPathBuilder::::start(Coord2(562.0692138671875, 669.944580078125)) - .curve_to((Coord2(562.0692138671875, 669.944580078125), Coord2(562.0692138671875, 669.944580078125)), Coord2(562.0692138671875, 669.944580078125)) - .curve_to((Coord2(562.4200439453125, 669.9562377929688), Coord2(562.6718139648438, 670.0160522460938)), Coord2(562.8291015625, 670.0160522460938)) - .curve_to((Coord2(562.7747802734375, 670.2525634765625), Coord2(563.1968383789063, 669.9431762695313)), Coord2(563.401611328125, 669.6962890625)) - .curve_to((Coord2(563.218505859375, 669.447021484375), Coord2(562.7468872070313, 668.9757690429688)), Coord2(562.6525268554688, 669.1633911132813)) - .curve_to((Coord2(562.3690185546875, 669.1181640625), Coord2(562.02490234375, 668.9761352539063)), Coord2(561.6610107421875, 668.9097290039063)) - .curve_to((Coord2(560.6327514648438, 668.3336181640625), Coord2(560.5078125, 668.680419921875)), Coord2(560.7962036132813, 668.913818359375)) - .curve_to((Coord2(560.7932739257813, 669.053955078125), Coord2(560.795166015625, 668.9855346679688)), Coord2(560.7711791992188, 669.1161499023438)) - .curve_to((Coord2(560.0169067382813, 670.2783813476563), Coord2(561.4442749023438, 669.9208984375)), Coord2(561.7700805664063, 668.3951416015625)) - .curve_to((Coord2(560.5978393554688, 668.2579956054688), Coord2(555.1843872070313, 665.8880615234375)), Coord2(552.6854248046875, 664.9908447265625)) - .curve_to((Coord2(552.62158203125, 664.9489135742188), Coord2(552.4188842773438, 664.8121948242188)), Coord2(552.1951293945313, 664.701171875)) - .curve_to((Coord2(552.0418090820313, 669.23193359375), Coord2(555.0795288085938, 667.7922973632813)), Coord2(555.9325561523438, 664.439697265625)) - .curve_to((Coord2(555.8035278320313, 663.3936767578125), Coord2(543.4547729492188, 664.566162109375)), Coord2(541.8832397460938, 667.4561767578125)) - .curve_to((Coord2(542.26611328125, 672.0006103515625), Coord2(548.4946899414063, 670.5872192382813)), Coord2(547.83984375, 666.5941772460938)) - .curve_to((Coord2(546.2003784179688, 665.4840087890625), Coord2(543.0369262695313, 665.3294677734375)), Coord2(543.1106567382813, 665.9275512695313)) - .curve_to((Coord2(536.3306274414063, 669.7837524414063), Coord2(541.8121337890625, 670.9800415039063)), Coord2(539.4649658203125, 666.6785888671875)) - .curve_to((Coord2(536.6891479492188, 665.93017578125), Coord2(534.8207397460938, 663.9938354492188)), Coord2(533.337890625, 661.9244995117188)) - .curve_to((Coord2(532.1223754882813, 662.1298828125), Coord2(530.9287109375, 662.1915893554688)), Coord2(534.033203125, 663.5484619140625)) - .curve_to((Coord2(539.8789672851563, 669.0048828125), Coord2(535.4338989257813, 664.3715209960938)), Coord2(530.1646118164063, 657.32666015625)) - .curve_to((Coord2(525.6614379882813, 654.2191162109375), Coord2(526.3388671875, 656.8445434570313)), Coord2(530.332275390625, 658.5115356445313)) - .curve_to((Coord2(530.9607543945313, 663.3235473632813), Coord2(535.1883544921875, 667.216552734375)), Coord2(533.1292724609375, 661.65673828125)) - .curve_to((Coord2(526.8078002929688, 654.7847290039063), Coord2(527.2481689453125, 655.82421875)), Coord2(528.5620727539063, 658.5321044921875)) - .curve_to((Coord2(529.048828125, 663.075927734375), Coord2(530.8765869140625, 662.1258544921875)), Coord2(531.5584106445313, 659.6661987304688)) - .curve_to((Coord2(530.1249389648438, 657.940185546875), Coord2(529.1561889648438, 657.2536010742188)), Coord2(528.7389526367188, 655.5059814453125)) - .curve_to((Coord2(527.8021240234375, 654.7122192382813), Coord2(529.899658203125, 656.5814819335938)), Coord2(531.8333740234375, 654.7963256835938)) - .curve_to((Coord2(538.0204467773438, 653.547119140625), Coord2(542.1532592773438, 652.2764892578125)), Coord2(544.957275390625, 652.1034545898438)) - .curve_to((Coord2(545.7479858398438, 652.0574340820313), Coord2(546.3248291015625, 651.8165283203125)), Coord2(546.8508911132813, 651.8157958984375)) - .curve_to((Coord2(548.2747802734375, 652.2127685546875), Coord2(548.1990356445313, 651.2047119140625)), Coord2(547.912109375, 650.8655395507813)) - .curve_to((Coord2(547.7791748046875, 650.193359375), Coord2(549.1414184570313, 650.476806640625)), Coord2(548.0958251953125, 650.5689086914063)) - .curve_to((Coord2(548.0958251953125, 650.7786865234375), Coord2(548.0958251953125, 651.0584716796875)), Coord2(548.0958251953125, 651.2682495117188)) - .curve_to((Coord2(548.9656982421875, 651.643798828125), Coord2(547.8914184570313, 651.3145141601563)), Coord2(549.1207275390625, 650.8655395507813)) - .curve_to((Coord2(548.8338012695313, 650.4108276367188), Coord2(547.700927734375, 649.2344360351563)), Coord2(546.8508911132813, 649.63134765625)) - .curve_to((Coord2(546.3272705078125, 649.630615234375), Coord2(545.5951538085938, 649.4255981445313)), Coord2(544.8019409179688, 649.4730834960938)) - .curve_to((Coord2(542.03857421875, 649.6287841796875), Coord2(537.2066040039063, 649.3989868164063)), Coord2(530.6641845703125, 650.7567138671875)) - .curve_to((Coord2(529.2568359375, 650.2000122070313), Coord2(525.3572998046875, 653.1232299804688)), Coord2(525.4530639648438, 656.3499145507813)) - .curve_to((Coord2(526.0859985351563, 658.6912231445313), Coord2(527.9020385742188, 660.7957763671875)), Coord2(529.1198120117188, 661.9281616210938)) - .curve_to((Coord2(532.0623779296875, 661.90576171875), Coord2(533.4664306640625, 660.0416259765625)), Coord2(529.3554077148438, 657.97998046875)) - .curve_to((Coord2(526.156005859375, 654.2037353515625), Coord2(522.1826782226563, 656.4036254882813)), Coord2(530.3896484375, 664.3438720703125)) - .curve_to((Coord2(536.754150390625, 667.3721923828125), Coord2(535.8456420898438, 660.3375854492188)), Coord2(530.91162109375, 658.0571899414063)) - .curve_to((Coord2(529.3756103515625, 652.6741333007813), Coord2(525.1596069335938, 652.45458984375)), Coord2(527.2052612304688, 659.278076171875)) - .curve_to((Coord2(532.2788696289063, 667.9177856445313), Coord2(540.3832397460938, 669.9564208984375)), Coord2(534.7332763671875, 662.905029296875)) - .curve_to((Coord2(532.432373046875, 658.3805541992188), Coord2(530.3565063476563, 660.47900390625)), Coord2(530.7684326171875, 663.4412841796875)) - .curve_to((Coord2(531.7405395507813, 665.5307006835938), Coord2(535.3882446289063, 669.1942138671875)), Coord2(538.6748046875, 669.9224853515625)) - .curve_to((Coord2(545.757080078125, 667.9179077148438), Coord2(541.6903686523438, 667.3967895507813)), Coord2(543.7351684570313, 669.8466186523438)) - .curve_to((Coord2(545.7384643554688, 670.13720703125), Coord2(545.3059692382813, 669.0546875)), Coord2(544.8681640625, 669.27587890625)) - .curve_to((Coord2(544.5274047851563, 665.6309204101563), Coord2(545.35498046875, 665.0091552734375)), Coord2(545.9674682617188, 668.1023559570313)) - .curve_to((Coord2(544.9426879882813, 667.5361328125), Coord2(553.4862670898438, 669.1529541015625)), Coord2(556.2109375, 667.8751831054688)) - .curve_to((Coord2(557.3668823242188, 664.4981079101563), Coord2(550.9618530273438, 662.6256103515625)), Coord2(549.96337890625, 666.1478271484375)) - .curve_to((Coord2(551.0449829101563, 667.5820922851563), Coord2(551.2767333984375, 667.6608276367188)), Coord2(551.4939575195313, 667.7685546875)) - .curve_to((Coord2(554.0316772460938, 668.719970703125), Coord2(560.2760620117188, 670.0292358398438)), Coord2(561.5628051757813, 670.177978515625)) - .curve_to((Coord2(562.9513549804688, 668.7757568359375), Coord2(560.3701782226563, 666.8861694335938)), Coord2(559.4100952148438, 668.6519775390625)) - .curve_to((Coord2(559.3812255859375, 668.7340698242188), Coord2(559.3759765625, 668.8223876953125)), Coord2(559.3749389648438, 668.913818359375)) - .curve_to((Coord2(559.663330078125, 669.5628662109375), Coord2(561.01806640625, 670.2659912109375)), Coord2(561.4695434570313, 669.9596557617188)) - .curve_to((Coord2(561.845458984375, 670.0281982421875), Coord2(562.2411499023438, 669.9994506835938)), Coord2(562.5125732421875, 670.0426025390625)) - .curve_to((Coord2(562.97314453125, 670.3184814453125), Coord2(562.8713989257813, 669.9105834960938)), Coord2(562.6882934570313, 669.6962890625)) - .curve_to((Coord2(562.89306640625, 669.4701538085938), Coord2(563.1842041015625, 669.1715698242188)), Coord2(562.8291015625, 669.4080810546875)) - .curve_to((Coord2(562.6779174804688, 669.4080810546875), Coord2(562.442626953125, 669.45654296875)), Coord2(562.085693359375, 669.44482421875)) - .build(); + let curve = + BezierPathBuilder::::start(Coord2(562.0692138671875, 669.944580078125)) + .curve_to( + ( + Coord2(562.0692138671875, 669.944580078125), + Coord2(562.0692138671875, 669.944580078125), + ), + Coord2(562.0692138671875, 669.944580078125), + ) + .curve_to( + ( + Coord2(562.4200439453125, 669.9562377929688), + Coord2(562.6718139648438, 670.0160522460938), + ), + Coord2(562.8291015625, 670.0160522460938), + ) + .curve_to( + ( + Coord2(562.7747802734375, 670.2525634765625), + Coord2(563.1968383789063, 669.9431762695313), + ), + Coord2(563.401611328125, 669.6962890625), + ) + .curve_to( + ( + Coord2(563.218505859375, 669.447021484375), + Coord2(562.7468872070313, 668.9757690429688), + ), + Coord2(562.6525268554688, 669.1633911132813), + ) + .curve_to( + ( + Coord2(562.3690185546875, 669.1181640625), + Coord2(562.02490234375, 668.9761352539063), + ), + Coord2(561.6610107421875, 668.9097290039063), + ) + .curve_to( + ( + Coord2(560.6327514648438, 668.3336181640625), + Coord2(560.5078125, 668.680419921875), + ), + Coord2(560.7962036132813, 668.913818359375), + ) + .curve_to( + ( + Coord2(560.7932739257813, 669.053955078125), + Coord2(560.795166015625, 668.9855346679688), + ), + Coord2(560.7711791992188, 669.1161499023438), + ) + .curve_to( + ( + Coord2(560.0169067382813, 670.2783813476563), + Coord2(561.4442749023438, 669.9208984375), + ), + Coord2(561.7700805664063, 668.3951416015625), + ) + .curve_to( + ( + Coord2(560.5978393554688, 668.2579956054688), + Coord2(555.1843872070313, 665.8880615234375), + ), + Coord2(552.6854248046875, 664.9908447265625), + ) + .curve_to( + ( + Coord2(552.62158203125, 664.9489135742188), + Coord2(552.4188842773438, 664.8121948242188), + ), + Coord2(552.1951293945313, 664.701171875), + ) + .curve_to( + ( + Coord2(552.0418090820313, 669.23193359375), + Coord2(555.0795288085938, 667.7922973632813), + ), + Coord2(555.9325561523438, 664.439697265625), + ) + .curve_to( + ( + Coord2(555.8035278320313, 663.3936767578125), + Coord2(543.4547729492188, 664.566162109375), + ), + Coord2(541.8832397460938, 667.4561767578125), + ) + .curve_to( + ( + Coord2(542.26611328125, 672.0006103515625), + Coord2(548.4946899414063, 670.5872192382813), + ), + Coord2(547.83984375, 666.5941772460938), + ) + .curve_to( + ( + Coord2(546.2003784179688, 665.4840087890625), + Coord2(543.0369262695313, 665.3294677734375), + ), + Coord2(543.1106567382813, 665.9275512695313), + ) + .curve_to( + ( + Coord2(536.3306274414063, 669.7837524414063), + Coord2(541.8121337890625, 670.9800415039063), + ), + Coord2(539.4649658203125, 666.6785888671875), + ) + .curve_to( + ( + Coord2(536.6891479492188, 665.93017578125), + Coord2(534.8207397460938, 663.9938354492188), + ), + Coord2(533.337890625, 661.9244995117188), + ) + .curve_to( + ( + Coord2(532.1223754882813, 662.1298828125), + Coord2(530.9287109375, 662.1915893554688), + ), + Coord2(534.033203125, 663.5484619140625), + ) + .curve_to( + ( + Coord2(539.8789672851563, 669.0048828125), + Coord2(535.4338989257813, 664.3715209960938), + ), + Coord2(530.1646118164063, 657.32666015625), + ) + .curve_to( + ( + Coord2(525.6614379882813, 654.2191162109375), + Coord2(526.3388671875, 656.8445434570313), + ), + Coord2(530.332275390625, 658.5115356445313), + ) + .curve_to( + ( + Coord2(530.9607543945313, 663.3235473632813), + Coord2(535.1883544921875, 667.216552734375), + ), + Coord2(533.1292724609375, 661.65673828125), + ) + .curve_to( + ( + Coord2(526.8078002929688, 654.7847290039063), + Coord2(527.2481689453125, 655.82421875), + ), + Coord2(528.5620727539063, 658.5321044921875), + ) + .curve_to( + ( + Coord2(529.048828125, 663.075927734375), + Coord2(530.8765869140625, 662.1258544921875), + ), + Coord2(531.5584106445313, 659.6661987304688), + ) + .curve_to( + ( + Coord2(530.1249389648438, 657.940185546875), + Coord2(529.1561889648438, 657.2536010742188), + ), + Coord2(528.7389526367188, 655.5059814453125), + ) + .curve_to( + ( + Coord2(527.8021240234375, 654.7122192382813), + Coord2(529.899658203125, 656.5814819335938), + ), + Coord2(531.8333740234375, 654.7963256835938), + ) + .curve_to( + ( + Coord2(538.0204467773438, 653.547119140625), + Coord2(542.1532592773438, 652.2764892578125), + ), + Coord2(544.957275390625, 652.1034545898438), + ) + .curve_to( + ( + Coord2(545.7479858398438, 652.0574340820313), + Coord2(546.3248291015625, 651.8165283203125), + ), + Coord2(546.8508911132813, 651.8157958984375), + ) + .curve_to( + ( + Coord2(548.2747802734375, 652.2127685546875), + Coord2(548.1990356445313, 651.2047119140625), + ), + Coord2(547.912109375, 650.8655395507813), + ) + .curve_to( + ( + Coord2(547.7791748046875, 650.193359375), + Coord2(549.1414184570313, 650.476806640625), + ), + Coord2(548.0958251953125, 650.5689086914063), + ) + .curve_to( + ( + Coord2(548.0958251953125, 650.7786865234375), + Coord2(548.0958251953125, 651.0584716796875), + ), + Coord2(548.0958251953125, 651.2682495117188), + ) + .curve_to( + ( + Coord2(548.9656982421875, 651.643798828125), + Coord2(547.8914184570313, 651.3145141601563), + ), + Coord2(549.1207275390625, 650.8655395507813), + ) + .curve_to( + ( + Coord2(548.8338012695313, 650.4108276367188), + Coord2(547.700927734375, 649.2344360351563), + ), + Coord2(546.8508911132813, 649.63134765625), + ) + .curve_to( + ( + Coord2(546.3272705078125, 649.630615234375), + Coord2(545.5951538085938, 649.4255981445313), + ), + Coord2(544.8019409179688, 649.4730834960938), + ) + .curve_to( + ( + Coord2(542.03857421875, 649.6287841796875), + Coord2(537.2066040039063, 649.3989868164063), + ), + Coord2(530.6641845703125, 650.7567138671875), + ) + .curve_to( + ( + Coord2(529.2568359375, 650.2000122070313), + Coord2(525.3572998046875, 653.1232299804688), + ), + Coord2(525.4530639648438, 656.3499145507813), + ) + .curve_to( + ( + Coord2(526.0859985351563, 658.6912231445313), + Coord2(527.9020385742188, 660.7957763671875), + ), + Coord2(529.1198120117188, 661.9281616210938), + ) + .curve_to( + ( + Coord2(532.0623779296875, 661.90576171875), + Coord2(533.4664306640625, 660.0416259765625), + ), + Coord2(529.3554077148438, 657.97998046875), + ) + .curve_to( + ( + Coord2(526.156005859375, 654.2037353515625), + Coord2(522.1826782226563, 656.4036254882813), + ), + Coord2(530.3896484375, 664.3438720703125), + ) + .curve_to( + ( + Coord2(536.754150390625, 667.3721923828125), + Coord2(535.8456420898438, 660.3375854492188), + ), + Coord2(530.91162109375, 658.0571899414063), + ) + .curve_to( + ( + Coord2(529.3756103515625, 652.6741333007813), + Coord2(525.1596069335938, 652.45458984375), + ), + Coord2(527.2052612304688, 659.278076171875), + ) + .curve_to( + ( + Coord2(532.2788696289063, 667.9177856445313), + Coord2(540.3832397460938, 669.9564208984375), + ), + Coord2(534.7332763671875, 662.905029296875), + ) + .curve_to( + ( + Coord2(532.432373046875, 658.3805541992188), + Coord2(530.3565063476563, 660.47900390625), + ), + Coord2(530.7684326171875, 663.4412841796875), + ) + .curve_to( + ( + Coord2(531.7405395507813, 665.5307006835938), + Coord2(535.3882446289063, 669.1942138671875), + ), + Coord2(538.6748046875, 669.9224853515625), + ) + .curve_to( + ( + Coord2(545.757080078125, 667.9179077148438), + Coord2(541.6903686523438, 667.3967895507813), + ), + Coord2(543.7351684570313, 669.8466186523438), + ) + .curve_to( + ( + Coord2(545.7384643554688, 670.13720703125), + Coord2(545.3059692382813, 669.0546875), + ), + Coord2(544.8681640625, 669.27587890625), + ) + .curve_to( + ( + Coord2(544.5274047851563, 665.6309204101563), + Coord2(545.35498046875, 665.0091552734375), + ), + Coord2(545.9674682617188, 668.1023559570313), + ) + .curve_to( + ( + Coord2(544.9426879882813, 667.5361328125), + Coord2(553.4862670898438, 669.1529541015625), + ), + Coord2(556.2109375, 667.8751831054688), + ) + .curve_to( + ( + Coord2(557.3668823242188, 664.4981079101563), + Coord2(550.9618530273438, 662.6256103515625), + ), + Coord2(549.96337890625, 666.1478271484375), + ) + .curve_to( + ( + Coord2(551.0449829101563, 667.5820922851563), + Coord2(551.2767333984375, 667.6608276367188), + ), + Coord2(551.4939575195313, 667.7685546875), + ) + .curve_to( + ( + Coord2(554.0316772460938, 668.719970703125), + Coord2(560.2760620117188, 670.0292358398438), + ), + Coord2(561.5628051757813, 670.177978515625), + ) + .curve_to( + ( + Coord2(562.9513549804688, 668.7757568359375), + Coord2(560.3701782226563, 666.8861694335938), + ), + Coord2(559.4100952148438, 668.6519775390625), + ) + .curve_to( + ( + Coord2(559.3812255859375, 668.7340698242188), + Coord2(559.3759765625, 668.8223876953125), + ), + Coord2(559.3749389648438, 668.913818359375), + ) + .curve_to( + ( + Coord2(559.663330078125, 669.5628662109375), + Coord2(561.01806640625, 670.2659912109375), + ), + Coord2(561.4695434570313, 669.9596557617188), + ) + .curve_to( + ( + Coord2(561.845458984375, 670.0281982421875), + Coord2(562.2411499023438, 669.9994506835938), + ), + Coord2(562.5125732421875, 670.0426025390625), + ) + .curve_to( + ( + Coord2(562.97314453125, 670.3184814453125), + Coord2(562.8713989257813, 669.9105834960938), + ), + Coord2(562.6882934570313, 669.6962890625), + ) + .curve_to( + ( + Coord2(562.89306640625, 669.4701538085938), + Coord2(563.1842041015625, 669.1715698242188), + ), + Coord2(562.8291015625, 669.4080810546875), + ) + .curve_to( + ( + Coord2(562.6779174804688, 669.4080810546875), + Coord2(562.442626953125, 669.45654296875), + ), + Coord2(562.085693359375, 669.44482421875), + ) + .build(); // Create the graph path from the source side let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(vec![&curve].into_iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + vec![&curve] + .into_iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); // Collide the path with itself to find the intersections merged_path.self_collide(0.01); @@ -79,11 +420,16 @@ fn remove_interior_points_1() { println!("{}", graph_path_svg_string(&merged_path, vec![])); println!("{:?}", svg_path_string(&curve)); - let with_points_removed: Vec = path_remove_interior_points(&vec![curve], 0.01); + let with_points_removed: Vec = + path_remove_interior_points(&vec![curve], 0.01); - println!("{:?}", with_points_removed.iter() - .map(|path| svg_path_string(path)) - .collect::>()); + println!( + "{:?}", + with_points_removed + .iter() + .map(|path| svg_path_string(path)) + .collect::>() + ); assert!(with_points_removed.len() > 0); } @@ -151,132 +497,799 @@ fn remove_interior_points_1_without_failing_section() { .build(); println!("{:?}", svg_path_string(&curve)); - let with_points_removed: Vec = path_remove_interior_points(&vec![curve], 0.01); + let with_points_removed: Vec = + path_remove_interior_points(&vec![curve], 0.01); - println!("{:?}", with_points_removed.iter() - .map(|path| svg_path_string(path)) - .collect::>()); + println!( + "{:?}", + with_points_removed + .iter() + .map(|path| svg_path_string(path)) + .collect::>() + ); assert!(with_points_removed.len() > 0); } #[test] fn remove_interior_points_complex_2() { - let path = BezierPathBuilder::::start(Coord2(589.8298950195313, 841.699951171875)) - .curve_to((Coord2(589.8298950195313, 841.699951171875), Coord2(589.8298950195313, 841.699951171875)), Coord2(589.8298950195313, 841.699951171875)) - .curve_to((Coord2(585.0781860351563, 841.545166015625), Coord2(588.116943359375, 846.1569213867188)), Coord2(589.9508056640625, 846.92041015625)) - .curve_to((Coord2(593.9074096679688, 850.3338623046875), Coord2(596.3680419921875, 855.8639526367188)), Coord2(600.2550048828125, 860.024169921875)) - .curve_to((Coord2(602.3019409179688, 864.72900390625), Coord2(603.487060546875, 861.721435546875)), Coord2(602.1428833007813, 859.0895385742188)) - .curve_to((Coord2(607.4638061523438, 858.4710693359375), Coord2(614.4444580078125, 855.14404296875)), Coord2(608.3931884765625, 855.6187133789063)) - .curve_to((Coord2(604.7843627929688, 851.9526977539063), Coord2(601.4735107421875, 847.9655151367188)), Coord2(597.78515625, 843.8760986328125)) - .curve_to((Coord2(601.0536499023438, 837.7391357421875), Coord2(590.90966796875, 841.439453125)), Coord2(587.8450927734375, 847.3414916992188)) - .curve_to((Coord2(592.2240600585938, 850.6311645507813), Coord2(595.8001098632813, 856.1324462890625)), Coord2(599.6971435546875, 861.4691772460938)) - .curve_to((Coord2(599.6600952148438, 866.1685546875), Coord2(601.5029907226563, 861.010498046875)), Coord2(601.408447265625, 857.6356811523438)) - .curve_to((Coord2(605.051025390625, 858.197509765625), Coord2(608.0866088867188, 854.1636352539063)), Coord2(597.3378295898438, 846.8604125976563)) - .curve_to((Coord2(597.2238159179688, 836.9576416015625), Coord2(590.7571411132813, 843.5430297851563)), Coord2(587.1199340820313, 848.599365234375)) - .curve_to((Coord2(588.7532348632813, 853.0540161132813), Coord2(591.633544921875, 856.119873046875)), Coord2(594.626708984375, 853.6188354492188)) - .curve_to((Coord2(596.7156982421875, 852.8362426757813), Coord2(595.0059814453125, 845.878662109375)), Coord2(591.52490234375, 845.5113525390625)) - .curve_to((Coord2(585.76171875, 847.6647338867188), Coord2(580.7750244140625, 855.853759765625)), Coord2(586.7627563476563, 853.3876342773438)) - .curve_to((Coord2(588.5208129882813, 859.3195190429688), Coord2(594.2566528320313, 860.6160278320313)), Coord2(592.3621826171875, 860.9254760742188)) - .curve_to((Coord2(594.9733276367188, 864.4375), Coord2(593.3421020507813, 848.7232055664063)), Coord2(586.76220703125, 847.8418579101563)) - .curve_to((Coord2(589.7845458984375, 841.6835327148438), Coord2(583.6079711914063, 848.498046875)), Coord2(580.9037475585938, 853.9146118164063)) - .curve_to((Coord2(580.701904296875, 853.186767578125), Coord2(578.50439453125, 857.2315063476563)), Coord2(581.5901489257813, 860.4940795898438)) - .curve_to((Coord2(585.6346435546875, 863.285400390625), Coord2(589.900146484375, 854.3807373046875)), Coord2(584.1525268554688, 856.2511596679688)) - .curve_to((Coord2(590.3831787109375, 852.05712890625), Coord2(578.9157104492188, 850.2012329101563)), Coord2(574.5430297851563, 856.5203247070313)) - .curve_to((Coord2(573.6943969726563, 863.1355590820313), Coord2(580.0052490234375, 871.26220703125)), Coord2(575.3004760742188, 871.1060791015625)) - .curve_to((Coord2(576.81103515625, 870.624267578125), Coord2(572.30712890625, 859.2913818359375)), Coord2(570.9198608398438, 861.718994140625)) - .curve_to((Coord2(572.5287475585938, 864.7382202148438), Coord2(581.41259765625, 882.9050903320313)), Coord2(580.4722900390625, 881.7498779296875)) - .curve_to((Coord2(580.0606689453125, 880.2344970703125), Coord2(575.6553955078125, 869.0311889648438)), Coord2(573.716552734375, 868.6065673828125)) - .curve_to((Coord2(570.4192504882813, 866.5391845703125), Coord2(572.1432495117188, 889.7837524414063)), Coord2(575.9349365234375, 889.2540893554688)) - .curve_to((Coord2(579.9112548828125, 889.1182250976563), Coord2(573.3362426757813, 870.1537475585938)), Coord2(570.325439453125, 872.933349609375)) - .curve_to((Coord2(566.7039184570313, 872.4866333007813), Coord2(575.889892578125, 896.3516845703125)), Coord2(580.193359375, 885.1004028320313)) - .curve_to((Coord2(578.9361572265625, 882.8379516601563), Coord2(578.29638671875, 880.9623413085938)), Coord2(577.2049560546875, 878.0570678710938)) - .curve_to((Coord2(576.3244018554688, 875.5227661132813), Coord2(575.8396606445313, 874.0106811523438)), Coord2(575.3523559570313, 871.5857543945313)) - .curve_to((Coord2(567.6146240234375, 879.8153076171875), Coord2(569.26904296875, 890.168212890625)), Coord2(572.8831176757813, 890.166259765625)) - .curve_to((Coord2(580.7759399414063, 887.835693359375), Coord2(580.0247802734375, 885.56103515625)), Coord2(572.6173095703125, 889.1515502929688)) - .curve_to((Coord2(572.6390991210938, 889.1546020507813), Coord2(572.2571411132813, 889.167724609375)), Coord2(572.2820434570313, 889.1630249023438)) - .curve_to((Coord2(570.7896728515625, 887.8728637695313), Coord2(567.4065551757813, 888.0462036132813)), Coord2(572.1813354492188, 892.5457763671875)) - .curve_to((Coord2(570.9942016601563, 894.4725341796875), Coord2(577.9598999023438, 900.7188720703125)), Coord2(582.4383544921875, 902.0015258789063)) - .curve_to((Coord2(582.8182373046875, 902.308349609375), Coord2(586.3283081054688, 901.2371826171875)), Coord2(586.35205078125, 900.798583984375)) - .curve_to((Coord2(588.947998046875, 898.2053833007813), Coord2(592.195068359375, 891.016845703125)), Coord2(591.5047607421875, 889.0786743164063)) - .curve_to((Coord2(592.836669921875, 884.303955078125), Coord2(592.759033203125, 882.3919677734375)), Coord2(593.544921875, 881.51806640625)) - .curve_to((Coord2(594.1064453125, 880.7155151367188), Coord2(593.8582153320313, 881.4864501953125)), Coord2(596.4064331054688, 879.8722534179688)) - .curve_to((Coord2(597.3624877929688, 879.4691162109375), Coord2(597.849365234375, 879.2901611328125)), Coord2(598.5863037109375, 879.035400390625)) - .curve_to((Coord2(598.9070434570313, 878.928466796875), Coord2(599.0929565429688, 878.8623657226563)), Coord2(599.3098754882813, 878.7935180664063)) - .curve_to((Coord2(596.8707275390625, 882.4271240234375), Coord2(601.0760498046875, 876.7950439453125)), Coord2(603.7096557617188, 873.0940551757813)) - .curve_to((Coord2(603.7099609375, 873.09423828125), Coord2(603.7090454101563, 872.9913940429688)), Coord2(603.708740234375, 872.9912109375)) - .curve_to((Coord2(602.2408447265625, 869.050048828125), Coord2(594.5162353515625, 860.6947021484375)), Coord2(590.5521850585938, 859.4874267578125)) - .curve_to((Coord2(584.2527465820313, 852.785888671875), Coord2(581.061279296875, 852.8796997070313)), Coord2(581.9452514648438, 853.1057739257813)) - .curve_to((Coord2(582.593017578125, 852.9660034179688), Coord2(580.7717895507813, 856.9833984375)), Coord2(580.3909301757813, 856.538818359375)) - .curve_to((Coord2(580.433837890625, 856.8338623046875), Coord2(577.17236328125, 858.1321411132813)), Coord2(578.2269897460938, 857.4826049804688)) - .curve_to((Coord2(579.6019897460938, 857.3278198242188), Coord2(581.242431640625, 858.7636108398438)), Coord2(586.4614868164063, 860.9908447265625)) - .curve_to((Coord2(589.0213012695313, 862.7692260742188), Coord2(595.4768676757813, 865.4718017578125)), Coord2(598.6329345703125, 865.7658081054688)) - .curve_to((Coord2(598.567138671875, 866.820556640625), Coord2(603.420166015625, 864.4375610351563)), Coord2(603.9707641601563, 863.19189453125)) - .curve_to((Coord2(604.5771484375, 862.9888916015625), Coord2(605.8325805664063, 859.209716796875)), Coord2(605.4430541992188, 858.7909545898438)) - .curve_to((Coord2(604.993408203125, 855.49951171875), Coord2(601.7562866210938, 849.7847900390625)), Coord2(600.6087036132813, 850.0838623046875)) - .curve_to((Coord2(598.4024047851563, 846.3101196289063), Coord2(598.4458618164063, 848.7549438476563)), Coord2(599.054931640625, 849.7504272460938)) - .curve_to((Coord2(599.3753051757813, 849.5570678710938), Coord2(598.5122680664063, 852.48095703125)), Coord2(598.3043212890625, 852.2940063476563)) - .curve_to((Coord2(594.026123046875, 853.1077270507813), Coord2(590.7466430664063, 857.636474609375)), Coord2(597.5106811523438, 857.97314453125)) - .curve_to((Coord2(599.0369262695313, 859.3222045898438), Coord2(599.9699096679688, 856.8018798828125)), Coord2(600.7046508789063, 857.8336181640625)) - .curve_to((Coord2(602.2219848632813, 857.0648803710938), Coord2(604.2450561523438, 856.0399780273438)), Coord2(605.7623901367188, 855.271240234375)) - .curve_to((Coord2(606.0952758789063, 855.509765625), Coord2(607.6958618164063, 852.7109985351563)), Coord2(605.1021118164063, 850.0916748046875)) - .curve_to((Coord2(607.8820190429688, 846.5908203125), Coord2(593.875244140625, 843.7130737304688)), Coord2(588.6094360351563, 846.0635986328125)) - .curve_to((Coord2(588.1766357421875, 846.2265625), Coord2(587.21044921875, 849.5330200195313)), Coord2(587.5308227539063, 849.7504272460938)) - .curve_to((Coord2(588.139892578125, 853.0325317382813), Coord2(591.3778076171875, 858.6402587890625)), Coord2(592.365966796875, 858.1364135742188)) - .curve_to((Coord2(594.4129028320313, 861.7054443359375), Coord2(594.3702392578125, 859.3676147460938)), Coord2(593.9205932617188, 858.7909545898438)) - .curve_to((Coord2(593.531005859375, 859.0397338867188), Coord2(594.5933837890625, 855.8880004882813)), Coord2(594.7660522460938, 856.260986328125)) - .curve_to((Coord2(595.0291137695313, 855.397216796875), Coord2(599.3807983398438, 853.1875)), Coord2(598.6329345703125, 854.2422485351563)) - .curve_to((Coord2(598.4532470703125, 854.5361938476563), Coord2(597.2518920898438, 853.0940551757813)), Coord2(591.7156982421875, 850.7276611328125)) - .curve_to((Coord2(588.8387451171875, 848.8101196289063), Coord2(581.9440307617188, 846.1011962890625)), Coord2(578.2269897460938, 845.9464111328125)) - .curve_to((Coord2(577.8850708007813, 845.296875), Coord2(573.4860229492188, 846.9068603515625)), Coord2(572.7304077148438, 847.910888671875)) - .curve_to((Coord2(571.8158569335938, 847.940185546875), Coord2(569.74658203125, 852.5507202148438)), Coord2(570.394287109375, 853.1057739257813)) - .curve_to((Coord2(571.2783203125, 857.921142578125), Coord2(578.909423828125, 867.0386962890625)), Coord2(583.4408569335938, 868.6987915039063)) - .curve_to((Coord2(590.3077392578125, 875.8531494140625), Coord2(593.4223022460938, 875.197265625)), Coord2(591.9874877929688, 873.194091796875)) - .curve_to((Coord2(591.9872436523438, 873.1967163085938), Coord2(591.986328125, 873.0939331054688)), Coord2(591.9866333007813, 873.0940551757813)) - .curve_to((Coord2(594.6202392578125, 869.4052734375), Coord2(598.6082153320313, 863.8417358398438)), Coord2(595.7800903320313, 867.597900390625)) - .curve_to((Coord2(595.6080322265625, 867.6517333984375), Coord2(595.2333374023438, 867.7623901367188)), Coord2(594.86767578125, 867.8843994140625)) - .curve_to((Coord2(594.2318115234375, 868.0874633789063), Coord2(592.8426513671875, 868.5746459960938)), Coord2(591.7855224609375, 869.0294189453125)) - .curve_to((Coord2(590.3074340820313, 869.1311645507813), Coord2(585.4846801757813, 872.3850708007813)), Coord2(583.8530883789063, 874.7000122070313)) - .curve_to((Coord2(582.0348510742188, 877.52783203125), Coord2(580.2197875976563, 883.7698364257813)), Coord2(579.9202880859375, 886.5905151367188)) - .curve_to((Coord2(577.5986328125, 892.2477416992188), Coord2(579.3203735351563, 892.0960083007813)), Coord2(579.736328125, 890.97021484375)) - .curve_to((Coord2(579.3685302734375, 890.795166015625), Coord2(582.47412109375, 889.8472900390625)), Coord2(582.4383544921875, 890.154052734375)) - .curve_to((Coord2(583.9168090820313, 891.4367065429688), Coord2(587.2955322265625, 891.2642211914063)), Coord2(582.5258178710938, 886.7721557617188)) - .curve_to((Coord2(583.718017578125, 884.8529663085938), Coord2(576.7567138671875, 878.6074829101563)), Coord2(572.2820434570313, 877.3173217773438)) - .curve_to((Coord2(572.260009765625, 877.3126220703125), Coord2(571.8314208984375, 877.3272705078125)), Coord2(571.8067016601563, 877.3335571289063)) - .curve_to((Coord2(560.010498046875, 881.22509765625), Coord2(569.1023559570313, 904.441650390625)), Coord2(580.4130249023438, 899.290283203125)) - .curve_to((Coord2(587.358642578125, 896.5389404296875), Coord2(577.4598388671875, 865.2567138671875)), Coord2(569.1909790039063, 866.8143920898438)) - .curve_to((Coord2(564.2433471679688, 876.4852905273438), Coord2(565.2119750976563, 879.7418823242188)), Coord2(566.0805053710938, 882.063720703125)) - .curve_to((Coord2(566.91650390625, 884.5101928710938), Coord2(568.3486328125, 888.1217651367188)), Coord2(569.8612060546875, 890.8428344726563)) - .curve_to((Coord2(587.60546875, 903.775146484375), Coord2(589.3789672851563, 862.3728637695313)), Coord2(570.9526977539063, 861.138671875)) - .curve_to((Coord2(555.2870483398438, 863.2453002929688), Coord2(565.8951416015625, 904.8345336914063)), Coord2(580.59521484375, 900.0840454101563)) - .curve_to((Coord2(594.0852661132813, 895.3810424804688), Coord2(579.9393920898438, 852.4515380859375)), Coord2(565.9549560546875, 859.7515869140625)) - .curve_to((Coord2(557.6361083984375, 864.9191284179688), Coord2(571.5972900390625, 896.5897216796875)), Coord2(582.2666625976563, 893.362060546875)) - .curve_to((Coord2(596.2720947265625, 889.8972778320313), Coord2(586.6115112304688, 848.4343872070313)), Coord2(571.9376220703125, 850.0352172851563)) - .curve_to((Coord2(556.0201416015625, 851.1971435546875), Coord2(561.6185302734375, 885.7030029296875)), Coord2(577.4126586914063, 882.59521484375)) - .curve_to((Coord2(589.552978515625, 879.34228515625), Coord2(591.1780395507813, 853.8557739257813)), Coord2(584.5911254882813, 850.6495971679688)) - .curve_to((Coord2(573.9320678710938, 846.2090454101563), Coord2(564.6778564453125, 858.427490234375)), Coord2(573.91796875, 861.5853881835938)) - .curve_to((Coord2(572.7061767578125, 870.7952880859375), Coord2(589.696533203125, 873.0230712890625)), Coord2(593.0504760742188, 859.9937133789063)) - .curve_to((Coord2(595.9219970703125, 858.3511352539063), Coord2(586.99267578125, 843.552978515625)), Coord2(581.2372436523438, 842.6605834960938)) - .curve_to((Coord2(576.9470825195313, 848.0301513671875), Coord2(573.3963623046875, 858.2985229492188)), Coord2(577.3682861328125, 853.6705322265625)) - .curve_to((Coord2(573.3412475585938, 856.9037475585938), Coord2(593.6327514648438, 872.11083984375)), Coord2(601.6283569335938, 866.2468872070313)) - .curve_to((Coord2(603.8673706054688, 859.3587036132813), Coord2(597.4412231445313, 846.4760131835938)), Coord2(595.3387451171875, 847.1231689453125)) - .curve_to((Coord2(600.8814697265625, 844.0478515625), Coord2(586.74169921875, 842.0896606445313)), Coord2(580.974609375, 845.4783935546875)) - .curve_to((Coord2(577.481201171875, 849.10498046875), Coord2(595.04345703125, 864.8500366210938)), Coord2(599.9298095703125, 862.3732299804688)) - .curve_to((Coord2(603.3001708984375, 859.6436767578125), Coord2(598.7106323242188, 838.4157104492188)), Coord2(590.7146606445313, 839.1911010742188)) - .curve_to((Coord2(586.1773071289063, 843.9035034179688), Coord2(581.3427124023438, 853.1783447265625)), Coord2(589.6311645507813, 853.3121948242188)) - .curve_to((Coord2(591.4857788085938, 861.0636596679688), Coord2(603.1357421875, 865.7894287109375)), Coord2(608.3871459960938, 864.779541015625)) - .curve_to((Coord2(609.6311645507813, 860.09716796875), Coord2(608.9767456054688, 852.512939453125)), Coord2(607.8642578125, 855.7709350585938)) - .curve_to((Coord2(604.8263549804688, 851.1680908203125), Coord2(599.0707397460938, 843.4915161132813)), Coord2(593.83251953125, 839.4678955078125)) - .curve_to((Coord2(588.12841796875, 843.3626708984375), Coord2(584.9580688476563, 854.13720703125)), Coord2(590.4581909179688, 850.477294921875)) - .curve_to((Coord2(593.902099609375, 854.3041381835938), Coord2(597.5442504882813, 858.5637817382813)), Coord2(601.3976440429688, 862.492431640625)) - .curve_to((Coord2(596.497314453125, 864.138427734375), Coord2(605.6493530273438, 863.7943115234375)), Coord2(611.2799682617188, 862.292236328125)) - .curve_to((Coord2(610.607666015625, 857.74365234375), Coord2(606.7666625976563, 851.5074462890625)), Coord2(606.7361450195313, 853.9833984375)) - .curve_to((Coord2(602.8890380859375, 849.8455200195313), Coord2(597.0514526367188, 846.7515869140625)), Coord2(593.0549926757813, 843.3157958984375)) - .curve_to((Coord2(591.688232421875, 841.3230590820313), Coord2(585.3775024414063, 841.3017578125)), Coord2(589.6620483398438, 842.685791015625)) - .build(); + let path = + BezierPathBuilder::::start(Coord2(589.8298950195313, 841.699951171875)) + .curve_to( + ( + Coord2(589.8298950195313, 841.699951171875), + Coord2(589.8298950195313, 841.699951171875), + ), + Coord2(589.8298950195313, 841.699951171875), + ) + .curve_to( + ( + Coord2(585.0781860351563, 841.545166015625), + Coord2(588.116943359375, 846.1569213867188), + ), + Coord2(589.9508056640625, 846.92041015625), + ) + .curve_to( + ( + Coord2(593.9074096679688, 850.3338623046875), + Coord2(596.3680419921875, 855.8639526367188), + ), + Coord2(600.2550048828125, 860.024169921875), + ) + .curve_to( + ( + Coord2(602.3019409179688, 864.72900390625), + Coord2(603.487060546875, 861.721435546875), + ), + Coord2(602.1428833007813, 859.0895385742188), + ) + .curve_to( + ( + Coord2(607.4638061523438, 858.4710693359375), + Coord2(614.4444580078125, 855.14404296875), + ), + Coord2(608.3931884765625, 855.6187133789063), + ) + .curve_to( + ( + Coord2(604.7843627929688, 851.9526977539063), + Coord2(601.4735107421875, 847.9655151367188), + ), + Coord2(597.78515625, 843.8760986328125), + ) + .curve_to( + ( + Coord2(601.0536499023438, 837.7391357421875), + Coord2(590.90966796875, 841.439453125), + ), + Coord2(587.8450927734375, 847.3414916992188), + ) + .curve_to( + ( + Coord2(592.2240600585938, 850.6311645507813), + Coord2(595.8001098632813, 856.1324462890625), + ), + Coord2(599.6971435546875, 861.4691772460938), + ) + .curve_to( + ( + Coord2(599.6600952148438, 866.1685546875), + Coord2(601.5029907226563, 861.010498046875), + ), + Coord2(601.408447265625, 857.6356811523438), + ) + .curve_to( + ( + Coord2(605.051025390625, 858.197509765625), + Coord2(608.0866088867188, 854.1636352539063), + ), + Coord2(597.3378295898438, 846.8604125976563), + ) + .curve_to( + ( + Coord2(597.2238159179688, 836.9576416015625), + Coord2(590.7571411132813, 843.5430297851563), + ), + Coord2(587.1199340820313, 848.599365234375), + ) + .curve_to( + ( + Coord2(588.7532348632813, 853.0540161132813), + Coord2(591.633544921875, 856.119873046875), + ), + Coord2(594.626708984375, 853.6188354492188), + ) + .curve_to( + ( + Coord2(596.7156982421875, 852.8362426757813), + Coord2(595.0059814453125, 845.878662109375), + ), + Coord2(591.52490234375, 845.5113525390625), + ) + .curve_to( + ( + Coord2(585.76171875, 847.6647338867188), + Coord2(580.7750244140625, 855.853759765625), + ), + Coord2(586.7627563476563, 853.3876342773438), + ) + .curve_to( + ( + Coord2(588.5208129882813, 859.3195190429688), + Coord2(594.2566528320313, 860.6160278320313), + ), + Coord2(592.3621826171875, 860.9254760742188), + ) + .curve_to( + ( + Coord2(594.9733276367188, 864.4375), + Coord2(593.3421020507813, 848.7232055664063), + ), + Coord2(586.76220703125, 847.8418579101563), + ) + .curve_to( + ( + Coord2(589.7845458984375, 841.6835327148438), + Coord2(583.6079711914063, 848.498046875), + ), + Coord2(580.9037475585938, 853.9146118164063), + ) + .curve_to( + ( + Coord2(580.701904296875, 853.186767578125), + Coord2(578.50439453125, 857.2315063476563), + ), + Coord2(581.5901489257813, 860.4940795898438), + ) + .curve_to( + ( + Coord2(585.6346435546875, 863.285400390625), + Coord2(589.900146484375, 854.3807373046875), + ), + Coord2(584.1525268554688, 856.2511596679688), + ) + .curve_to( + ( + Coord2(590.3831787109375, 852.05712890625), + Coord2(578.9157104492188, 850.2012329101563), + ), + Coord2(574.5430297851563, 856.5203247070313), + ) + .curve_to( + ( + Coord2(573.6943969726563, 863.1355590820313), + Coord2(580.0052490234375, 871.26220703125), + ), + Coord2(575.3004760742188, 871.1060791015625), + ) + .curve_to( + ( + Coord2(576.81103515625, 870.624267578125), + Coord2(572.30712890625, 859.2913818359375), + ), + Coord2(570.9198608398438, 861.718994140625), + ) + .curve_to( + ( + Coord2(572.5287475585938, 864.7382202148438), + Coord2(581.41259765625, 882.9050903320313), + ), + Coord2(580.4722900390625, 881.7498779296875), + ) + .curve_to( + ( + Coord2(580.0606689453125, 880.2344970703125), + Coord2(575.6553955078125, 869.0311889648438), + ), + Coord2(573.716552734375, 868.6065673828125), + ) + .curve_to( + ( + Coord2(570.4192504882813, 866.5391845703125), + Coord2(572.1432495117188, 889.7837524414063), + ), + Coord2(575.9349365234375, 889.2540893554688), + ) + .curve_to( + ( + Coord2(579.9112548828125, 889.1182250976563), + Coord2(573.3362426757813, 870.1537475585938), + ), + Coord2(570.325439453125, 872.933349609375), + ) + .curve_to( + ( + Coord2(566.7039184570313, 872.4866333007813), + Coord2(575.889892578125, 896.3516845703125), + ), + Coord2(580.193359375, 885.1004028320313), + ) + .curve_to( + ( + Coord2(578.9361572265625, 882.8379516601563), + Coord2(578.29638671875, 880.9623413085938), + ), + Coord2(577.2049560546875, 878.0570678710938), + ) + .curve_to( + ( + Coord2(576.3244018554688, 875.5227661132813), + Coord2(575.8396606445313, 874.0106811523438), + ), + Coord2(575.3523559570313, 871.5857543945313), + ) + .curve_to( + ( + Coord2(567.6146240234375, 879.8153076171875), + Coord2(569.26904296875, 890.168212890625), + ), + Coord2(572.8831176757813, 890.166259765625), + ) + .curve_to( + ( + Coord2(580.7759399414063, 887.835693359375), + Coord2(580.0247802734375, 885.56103515625), + ), + Coord2(572.6173095703125, 889.1515502929688), + ) + .curve_to( + ( + Coord2(572.6390991210938, 889.1546020507813), + Coord2(572.2571411132813, 889.167724609375), + ), + Coord2(572.2820434570313, 889.1630249023438), + ) + .curve_to( + ( + Coord2(570.7896728515625, 887.8728637695313), + Coord2(567.4065551757813, 888.0462036132813), + ), + Coord2(572.1813354492188, 892.5457763671875), + ) + .curve_to( + ( + Coord2(570.9942016601563, 894.4725341796875), + Coord2(577.9598999023438, 900.7188720703125), + ), + Coord2(582.4383544921875, 902.0015258789063), + ) + .curve_to( + ( + Coord2(582.8182373046875, 902.308349609375), + Coord2(586.3283081054688, 901.2371826171875), + ), + Coord2(586.35205078125, 900.798583984375), + ) + .curve_to( + ( + Coord2(588.947998046875, 898.2053833007813), + Coord2(592.195068359375, 891.016845703125), + ), + Coord2(591.5047607421875, 889.0786743164063), + ) + .curve_to( + ( + Coord2(592.836669921875, 884.303955078125), + Coord2(592.759033203125, 882.3919677734375), + ), + Coord2(593.544921875, 881.51806640625), + ) + .curve_to( + ( + Coord2(594.1064453125, 880.7155151367188), + Coord2(593.8582153320313, 881.4864501953125), + ), + Coord2(596.4064331054688, 879.8722534179688), + ) + .curve_to( + ( + Coord2(597.3624877929688, 879.4691162109375), + Coord2(597.849365234375, 879.2901611328125), + ), + Coord2(598.5863037109375, 879.035400390625), + ) + .curve_to( + ( + Coord2(598.9070434570313, 878.928466796875), + Coord2(599.0929565429688, 878.8623657226563), + ), + Coord2(599.3098754882813, 878.7935180664063), + ) + .curve_to( + ( + Coord2(596.8707275390625, 882.4271240234375), + Coord2(601.0760498046875, 876.7950439453125), + ), + Coord2(603.7096557617188, 873.0940551757813), + ) + .curve_to( + ( + Coord2(603.7099609375, 873.09423828125), + Coord2(603.7090454101563, 872.9913940429688), + ), + Coord2(603.708740234375, 872.9912109375), + ) + .curve_to( + ( + Coord2(602.2408447265625, 869.050048828125), + Coord2(594.5162353515625, 860.6947021484375), + ), + Coord2(590.5521850585938, 859.4874267578125), + ) + .curve_to( + ( + Coord2(584.2527465820313, 852.785888671875), + Coord2(581.061279296875, 852.8796997070313), + ), + Coord2(581.9452514648438, 853.1057739257813), + ) + .curve_to( + ( + Coord2(582.593017578125, 852.9660034179688), + Coord2(580.7717895507813, 856.9833984375), + ), + Coord2(580.3909301757813, 856.538818359375), + ) + .curve_to( + ( + Coord2(580.433837890625, 856.8338623046875), + Coord2(577.17236328125, 858.1321411132813), + ), + Coord2(578.2269897460938, 857.4826049804688), + ) + .curve_to( + ( + Coord2(579.6019897460938, 857.3278198242188), + Coord2(581.242431640625, 858.7636108398438), + ), + Coord2(586.4614868164063, 860.9908447265625), + ) + .curve_to( + ( + Coord2(589.0213012695313, 862.7692260742188), + Coord2(595.4768676757813, 865.4718017578125), + ), + Coord2(598.6329345703125, 865.7658081054688), + ) + .curve_to( + ( + Coord2(598.567138671875, 866.820556640625), + Coord2(603.420166015625, 864.4375610351563), + ), + Coord2(603.9707641601563, 863.19189453125), + ) + .curve_to( + ( + Coord2(604.5771484375, 862.9888916015625), + Coord2(605.8325805664063, 859.209716796875), + ), + Coord2(605.4430541992188, 858.7909545898438), + ) + .curve_to( + ( + Coord2(604.993408203125, 855.49951171875), + Coord2(601.7562866210938, 849.7847900390625), + ), + Coord2(600.6087036132813, 850.0838623046875), + ) + .curve_to( + ( + Coord2(598.4024047851563, 846.3101196289063), + Coord2(598.4458618164063, 848.7549438476563), + ), + Coord2(599.054931640625, 849.7504272460938), + ) + .curve_to( + ( + Coord2(599.3753051757813, 849.5570678710938), + Coord2(598.5122680664063, 852.48095703125), + ), + Coord2(598.3043212890625, 852.2940063476563), + ) + .curve_to( + ( + Coord2(594.026123046875, 853.1077270507813), + Coord2(590.7466430664063, 857.636474609375), + ), + Coord2(597.5106811523438, 857.97314453125), + ) + .curve_to( + ( + Coord2(599.0369262695313, 859.3222045898438), + Coord2(599.9699096679688, 856.8018798828125), + ), + Coord2(600.7046508789063, 857.8336181640625), + ) + .curve_to( + ( + Coord2(602.2219848632813, 857.0648803710938), + Coord2(604.2450561523438, 856.0399780273438), + ), + Coord2(605.7623901367188, 855.271240234375), + ) + .curve_to( + ( + Coord2(606.0952758789063, 855.509765625), + Coord2(607.6958618164063, 852.7109985351563), + ), + Coord2(605.1021118164063, 850.0916748046875), + ) + .curve_to( + ( + Coord2(607.8820190429688, 846.5908203125), + Coord2(593.875244140625, 843.7130737304688), + ), + Coord2(588.6094360351563, 846.0635986328125), + ) + .curve_to( + ( + Coord2(588.1766357421875, 846.2265625), + Coord2(587.21044921875, 849.5330200195313), + ), + Coord2(587.5308227539063, 849.7504272460938), + ) + .curve_to( + ( + Coord2(588.139892578125, 853.0325317382813), + Coord2(591.3778076171875, 858.6402587890625), + ), + Coord2(592.365966796875, 858.1364135742188), + ) + .curve_to( + ( + Coord2(594.4129028320313, 861.7054443359375), + Coord2(594.3702392578125, 859.3676147460938), + ), + Coord2(593.9205932617188, 858.7909545898438), + ) + .curve_to( + ( + Coord2(593.531005859375, 859.0397338867188), + Coord2(594.5933837890625, 855.8880004882813), + ), + Coord2(594.7660522460938, 856.260986328125), + ) + .curve_to( + ( + Coord2(595.0291137695313, 855.397216796875), + Coord2(599.3807983398438, 853.1875), + ), + Coord2(598.6329345703125, 854.2422485351563), + ) + .curve_to( + ( + Coord2(598.4532470703125, 854.5361938476563), + Coord2(597.2518920898438, 853.0940551757813), + ), + Coord2(591.7156982421875, 850.7276611328125), + ) + .curve_to( + ( + Coord2(588.8387451171875, 848.8101196289063), + Coord2(581.9440307617188, 846.1011962890625), + ), + Coord2(578.2269897460938, 845.9464111328125), + ) + .curve_to( + ( + Coord2(577.8850708007813, 845.296875), + Coord2(573.4860229492188, 846.9068603515625), + ), + Coord2(572.7304077148438, 847.910888671875), + ) + .curve_to( + ( + Coord2(571.8158569335938, 847.940185546875), + Coord2(569.74658203125, 852.5507202148438), + ), + Coord2(570.394287109375, 853.1057739257813), + ) + .curve_to( + ( + Coord2(571.2783203125, 857.921142578125), + Coord2(578.909423828125, 867.0386962890625), + ), + Coord2(583.4408569335938, 868.6987915039063), + ) + .curve_to( + ( + Coord2(590.3077392578125, 875.8531494140625), + Coord2(593.4223022460938, 875.197265625), + ), + Coord2(591.9874877929688, 873.194091796875), + ) + .curve_to( + ( + Coord2(591.9872436523438, 873.1967163085938), + Coord2(591.986328125, 873.0939331054688), + ), + Coord2(591.9866333007813, 873.0940551757813), + ) + .curve_to( + ( + Coord2(594.6202392578125, 869.4052734375), + Coord2(598.6082153320313, 863.8417358398438), + ), + Coord2(595.7800903320313, 867.597900390625), + ) + .curve_to( + ( + Coord2(595.6080322265625, 867.6517333984375), + Coord2(595.2333374023438, 867.7623901367188), + ), + Coord2(594.86767578125, 867.8843994140625), + ) + .curve_to( + ( + Coord2(594.2318115234375, 868.0874633789063), + Coord2(592.8426513671875, 868.5746459960938), + ), + Coord2(591.7855224609375, 869.0294189453125), + ) + .curve_to( + ( + Coord2(590.3074340820313, 869.1311645507813), + Coord2(585.4846801757813, 872.3850708007813), + ), + Coord2(583.8530883789063, 874.7000122070313), + ) + .curve_to( + ( + Coord2(582.0348510742188, 877.52783203125), + Coord2(580.2197875976563, 883.7698364257813), + ), + Coord2(579.9202880859375, 886.5905151367188), + ) + .curve_to( + ( + Coord2(577.5986328125, 892.2477416992188), + Coord2(579.3203735351563, 892.0960083007813), + ), + Coord2(579.736328125, 890.97021484375), + ) + .curve_to( + ( + Coord2(579.3685302734375, 890.795166015625), + Coord2(582.47412109375, 889.8472900390625), + ), + Coord2(582.4383544921875, 890.154052734375), + ) + .curve_to( + ( + Coord2(583.9168090820313, 891.4367065429688), + Coord2(587.2955322265625, 891.2642211914063), + ), + Coord2(582.5258178710938, 886.7721557617188), + ) + .curve_to( + ( + Coord2(583.718017578125, 884.8529663085938), + Coord2(576.7567138671875, 878.6074829101563), + ), + Coord2(572.2820434570313, 877.3173217773438), + ) + .curve_to( + ( + Coord2(572.260009765625, 877.3126220703125), + Coord2(571.8314208984375, 877.3272705078125), + ), + Coord2(571.8067016601563, 877.3335571289063), + ) + .curve_to( + ( + Coord2(560.010498046875, 881.22509765625), + Coord2(569.1023559570313, 904.441650390625), + ), + Coord2(580.4130249023438, 899.290283203125), + ) + .curve_to( + ( + Coord2(587.358642578125, 896.5389404296875), + Coord2(577.4598388671875, 865.2567138671875), + ), + Coord2(569.1909790039063, 866.8143920898438), + ) + .curve_to( + ( + Coord2(564.2433471679688, 876.4852905273438), + Coord2(565.2119750976563, 879.7418823242188), + ), + Coord2(566.0805053710938, 882.063720703125), + ) + .curve_to( + ( + Coord2(566.91650390625, 884.5101928710938), + Coord2(568.3486328125, 888.1217651367188), + ), + Coord2(569.8612060546875, 890.8428344726563), + ) + .curve_to( + ( + Coord2(587.60546875, 903.775146484375), + Coord2(589.3789672851563, 862.3728637695313), + ), + Coord2(570.9526977539063, 861.138671875), + ) + .curve_to( + ( + Coord2(555.2870483398438, 863.2453002929688), + Coord2(565.8951416015625, 904.8345336914063), + ), + Coord2(580.59521484375, 900.0840454101563), + ) + .curve_to( + ( + Coord2(594.0852661132813, 895.3810424804688), + Coord2(579.9393920898438, 852.4515380859375), + ), + Coord2(565.9549560546875, 859.7515869140625), + ) + .curve_to( + ( + Coord2(557.6361083984375, 864.9191284179688), + Coord2(571.5972900390625, 896.5897216796875), + ), + Coord2(582.2666625976563, 893.362060546875), + ) + .curve_to( + ( + Coord2(596.2720947265625, 889.8972778320313), + Coord2(586.6115112304688, 848.4343872070313), + ), + Coord2(571.9376220703125, 850.0352172851563), + ) + .curve_to( + ( + Coord2(556.0201416015625, 851.1971435546875), + Coord2(561.6185302734375, 885.7030029296875), + ), + Coord2(577.4126586914063, 882.59521484375), + ) + .curve_to( + ( + Coord2(589.552978515625, 879.34228515625), + Coord2(591.1780395507813, 853.8557739257813), + ), + Coord2(584.5911254882813, 850.6495971679688), + ) + .curve_to( + ( + Coord2(573.9320678710938, 846.2090454101563), + Coord2(564.6778564453125, 858.427490234375), + ), + Coord2(573.91796875, 861.5853881835938), + ) + .curve_to( + ( + Coord2(572.7061767578125, 870.7952880859375), + Coord2(589.696533203125, 873.0230712890625), + ), + Coord2(593.0504760742188, 859.9937133789063), + ) + .curve_to( + ( + Coord2(595.9219970703125, 858.3511352539063), + Coord2(586.99267578125, 843.552978515625), + ), + Coord2(581.2372436523438, 842.6605834960938), + ) + .curve_to( + ( + Coord2(576.9470825195313, 848.0301513671875), + Coord2(573.3963623046875, 858.2985229492188), + ), + Coord2(577.3682861328125, 853.6705322265625), + ) + .curve_to( + ( + Coord2(573.3412475585938, 856.9037475585938), + Coord2(593.6327514648438, 872.11083984375), + ), + Coord2(601.6283569335938, 866.2468872070313), + ) + .curve_to( + ( + Coord2(603.8673706054688, 859.3587036132813), + Coord2(597.4412231445313, 846.4760131835938), + ), + Coord2(595.3387451171875, 847.1231689453125), + ) + .curve_to( + ( + Coord2(600.8814697265625, 844.0478515625), + Coord2(586.74169921875, 842.0896606445313), + ), + Coord2(580.974609375, 845.4783935546875), + ) + .curve_to( + ( + Coord2(577.481201171875, 849.10498046875), + Coord2(595.04345703125, 864.8500366210938), + ), + Coord2(599.9298095703125, 862.3732299804688), + ) + .curve_to( + ( + Coord2(603.3001708984375, 859.6436767578125), + Coord2(598.7106323242188, 838.4157104492188), + ), + Coord2(590.7146606445313, 839.1911010742188), + ) + .curve_to( + ( + Coord2(586.1773071289063, 843.9035034179688), + Coord2(581.3427124023438, 853.1783447265625), + ), + Coord2(589.6311645507813, 853.3121948242188), + ) + .curve_to( + ( + Coord2(591.4857788085938, 861.0636596679688), + Coord2(603.1357421875, 865.7894287109375), + ), + Coord2(608.3871459960938, 864.779541015625), + ) + .curve_to( + ( + Coord2(609.6311645507813, 860.09716796875), + Coord2(608.9767456054688, 852.512939453125), + ), + Coord2(607.8642578125, 855.7709350585938), + ) + .curve_to( + ( + Coord2(604.8263549804688, 851.1680908203125), + Coord2(599.0707397460938, 843.4915161132813), + ), + Coord2(593.83251953125, 839.4678955078125), + ) + .curve_to( + ( + Coord2(588.12841796875, 843.3626708984375), + Coord2(584.9580688476563, 854.13720703125), + ), + Coord2(590.4581909179688, 850.477294921875), + ) + .curve_to( + ( + Coord2(593.902099609375, 854.3041381835938), + Coord2(597.5442504882813, 858.5637817382813), + ), + Coord2(601.3976440429688, 862.492431640625), + ) + .curve_to( + ( + Coord2(596.497314453125, 864.138427734375), + Coord2(605.6493530273438, 863.7943115234375), + ), + Coord2(611.2799682617188, 862.292236328125), + ) + .curve_to( + ( + Coord2(610.607666015625, 857.74365234375), + Coord2(606.7666625976563, 851.5074462890625), + ), + Coord2(606.7361450195313, 853.9833984375), + ) + .curve_to( + ( + Coord2(602.8890380859375, 849.8455200195313), + Coord2(597.0514526367188, 846.7515869140625), + ), + Coord2(593.0549926757813, 843.3157958984375), + ) + .curve_to( + ( + Coord2(591.688232421875, 841.3230590820313), + Coord2(585.3775024414063, 841.3017578125), + ), + Coord2(589.6620483398438, 842.685791015625), + ) + .build(); // This path has generated an error that indicates that no result path was generated (unfortunately it seems this version does not produce the error) - let without_interior_points = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); + let without_interior_points = + path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); /* // Bug appears to be that not all collisions are generated (so two self-collides in a row will generate more points) @@ -305,118 +1318,779 @@ fn remove_interior_points_complex_2() { #[test] fn remove_interior_points_complex_2_without_healing() { - let path = BezierPathBuilder::::start(Coord2(589.8298950195313, 841.699951171875)) - .curve_to((Coord2(589.8298950195313, 841.699951171875), Coord2(589.8298950195313, 841.699951171875)), Coord2(589.8298950195313, 841.699951171875)) - .curve_to((Coord2(585.0781860351563, 841.545166015625), Coord2(588.116943359375, 846.1569213867188)), Coord2(589.9508056640625, 846.92041015625)) - .curve_to((Coord2(593.9074096679688, 850.3338623046875), Coord2(596.3680419921875, 855.8639526367188)), Coord2(600.2550048828125, 860.024169921875)) - .curve_to((Coord2(602.3019409179688, 864.72900390625), Coord2(603.487060546875, 861.721435546875)), Coord2(602.1428833007813, 859.0895385742188)) - .curve_to((Coord2(607.4638061523438, 858.4710693359375), Coord2(614.4444580078125, 855.14404296875)), Coord2(608.3931884765625, 855.6187133789063)) - .curve_to((Coord2(604.7843627929688, 851.9526977539063), Coord2(601.4735107421875, 847.9655151367188)), Coord2(597.78515625, 843.8760986328125)) - .curve_to((Coord2(601.0536499023438, 837.7391357421875), Coord2(590.90966796875, 841.439453125)), Coord2(587.8450927734375, 847.3414916992188)) - .curve_to((Coord2(592.2240600585938, 850.6311645507813), Coord2(595.8001098632813, 856.1324462890625)), Coord2(599.6971435546875, 861.4691772460938)) - .curve_to((Coord2(599.6600952148438, 866.1685546875), Coord2(601.5029907226563, 861.010498046875)), Coord2(601.408447265625, 857.6356811523438)) - .curve_to((Coord2(605.051025390625, 858.197509765625), Coord2(608.0866088867188, 854.1636352539063)), Coord2(597.3378295898438, 846.8604125976563)) - .curve_to((Coord2(597.2238159179688, 836.9576416015625), Coord2(590.7571411132813, 843.5430297851563)), Coord2(587.1199340820313, 848.599365234375)) - .curve_to((Coord2(588.7532348632813, 853.0540161132813), Coord2(591.633544921875, 856.119873046875)), Coord2(594.626708984375, 853.6188354492188)) - .curve_to((Coord2(596.7156982421875, 852.8362426757813), Coord2(595.0059814453125, 845.878662109375)), Coord2(591.52490234375, 845.5113525390625)) - .curve_to((Coord2(585.76171875, 847.6647338867188), Coord2(580.7750244140625, 855.853759765625)), Coord2(586.7627563476563, 853.3876342773438)) - .curve_to((Coord2(588.5208129882813, 859.3195190429688), Coord2(594.2566528320313, 860.6160278320313)), Coord2(592.3621826171875, 860.9254760742188)) - .curve_to((Coord2(594.9733276367188, 864.4375), Coord2(593.3421020507813, 848.7232055664063)), Coord2(586.76220703125, 847.8418579101563)) - .curve_to((Coord2(589.7845458984375, 841.6835327148438), Coord2(583.6079711914063, 848.498046875)), Coord2(580.9037475585938, 853.9146118164063)) - .curve_to((Coord2(580.701904296875, 853.186767578125), Coord2(578.50439453125, 857.2315063476563)), Coord2(581.5901489257813, 860.4940795898438)) - .curve_to((Coord2(585.6346435546875, 863.285400390625), Coord2(589.900146484375, 854.3807373046875)), Coord2(584.1525268554688, 856.2511596679688)) - .curve_to((Coord2(590.3831787109375, 852.05712890625), Coord2(578.9157104492188, 850.2012329101563)), Coord2(574.5430297851563, 856.5203247070313)) - .curve_to((Coord2(573.6943969726563, 863.1355590820313), Coord2(580.0052490234375, 871.26220703125)), Coord2(575.3004760742188, 871.1060791015625)) - .curve_to((Coord2(576.81103515625, 870.624267578125), Coord2(572.30712890625, 859.2913818359375)), Coord2(570.9198608398438, 861.718994140625)) - .curve_to((Coord2(572.5287475585938, 864.7382202148438), Coord2(581.41259765625, 882.9050903320313)), Coord2(580.4722900390625, 881.7498779296875)) - .curve_to((Coord2(580.0606689453125, 880.2344970703125), Coord2(575.6553955078125, 869.0311889648438)), Coord2(573.716552734375, 868.6065673828125)) - .curve_to((Coord2(570.4192504882813, 866.5391845703125), Coord2(572.1432495117188, 889.7837524414063)), Coord2(575.9349365234375, 889.2540893554688)) - .curve_to((Coord2(579.9112548828125, 889.1182250976563), Coord2(573.3362426757813, 870.1537475585938)), Coord2(570.325439453125, 872.933349609375)) - .curve_to((Coord2(566.7039184570313, 872.4866333007813), Coord2(575.889892578125, 896.3516845703125)), Coord2(580.193359375, 885.1004028320313)) - .curve_to((Coord2(578.9361572265625, 882.8379516601563), Coord2(578.29638671875, 880.9623413085938)), Coord2(577.2049560546875, 878.0570678710938)) - .curve_to((Coord2(576.3244018554688, 875.5227661132813), Coord2(575.8396606445313, 874.0106811523438)), Coord2(575.3523559570313, 871.5857543945313)) - .curve_to((Coord2(567.6146240234375, 879.8153076171875), Coord2(569.26904296875, 890.168212890625)), Coord2(572.8831176757813, 890.166259765625)) - .curve_to((Coord2(580.7759399414063, 887.835693359375), Coord2(580.0247802734375, 885.56103515625)), Coord2(572.6173095703125, 889.1515502929688)) - .curve_to((Coord2(572.6390991210938, 889.1546020507813), Coord2(572.2571411132813, 889.167724609375)), Coord2(572.2820434570313, 889.1630249023438)) - .curve_to((Coord2(570.7896728515625, 887.8728637695313), Coord2(567.4065551757813, 888.0462036132813)), Coord2(572.1813354492188, 892.5457763671875)) - .curve_to((Coord2(570.9942016601563, 894.4725341796875), Coord2(577.9598999023438, 900.7188720703125)), Coord2(582.4383544921875, 902.0015258789063)) - .curve_to((Coord2(582.8182373046875, 902.308349609375), Coord2(586.3283081054688, 901.2371826171875)), Coord2(586.35205078125, 900.798583984375)) - .curve_to((Coord2(588.947998046875, 898.2053833007813), Coord2(592.195068359375, 891.016845703125)), Coord2(591.5047607421875, 889.0786743164063)) - .curve_to((Coord2(592.836669921875, 884.303955078125), Coord2(592.759033203125, 882.3919677734375)), Coord2(593.544921875, 881.51806640625)) - .curve_to((Coord2(594.1064453125, 880.7155151367188), Coord2(593.8582153320313, 881.4864501953125)), Coord2(596.4064331054688, 879.8722534179688)) - .curve_to((Coord2(597.3624877929688, 879.4691162109375), Coord2(597.849365234375, 879.2901611328125)), Coord2(598.5863037109375, 879.035400390625)) - .curve_to((Coord2(598.9070434570313, 878.928466796875), Coord2(599.0929565429688, 878.8623657226563)), Coord2(599.3098754882813, 878.7935180664063)) - .curve_to((Coord2(596.8707275390625, 882.4271240234375), Coord2(601.0760498046875, 876.7950439453125)), Coord2(603.7096557617188, 873.0940551757813)) - .curve_to((Coord2(603.7099609375, 873.09423828125), Coord2(603.7090454101563, 872.9913940429688)), Coord2(603.708740234375, 872.9912109375)) - .curve_to((Coord2(602.2408447265625, 869.050048828125), Coord2(594.5162353515625, 860.6947021484375)), Coord2(590.5521850585938, 859.4874267578125)) - .curve_to((Coord2(584.2527465820313, 852.785888671875), Coord2(581.061279296875, 852.8796997070313)), Coord2(581.9452514648438, 853.1057739257813)) - .curve_to((Coord2(582.593017578125, 852.9660034179688), Coord2(580.7717895507813, 856.9833984375)), Coord2(580.3909301757813, 856.538818359375)) - .curve_to((Coord2(580.433837890625, 856.8338623046875), Coord2(577.17236328125, 858.1321411132813)), Coord2(578.2269897460938, 857.4826049804688)) - .curve_to((Coord2(579.6019897460938, 857.3278198242188), Coord2(581.242431640625, 858.7636108398438)), Coord2(586.4614868164063, 860.9908447265625)) - .curve_to((Coord2(589.0213012695313, 862.7692260742188), Coord2(595.4768676757813, 865.4718017578125)), Coord2(598.6329345703125, 865.7658081054688)) - .curve_to((Coord2(598.567138671875, 866.820556640625), Coord2(603.420166015625, 864.4375610351563)), Coord2(603.9707641601563, 863.19189453125)) - .curve_to((Coord2(604.5771484375, 862.9888916015625), Coord2(605.8325805664063, 859.209716796875)), Coord2(605.4430541992188, 858.7909545898438)) - .curve_to((Coord2(604.993408203125, 855.49951171875), Coord2(601.7562866210938, 849.7847900390625)), Coord2(600.6087036132813, 850.0838623046875)) - .curve_to((Coord2(598.4024047851563, 846.3101196289063), Coord2(598.4458618164063, 848.7549438476563)), Coord2(599.054931640625, 849.7504272460938)) - .curve_to((Coord2(599.3753051757813, 849.5570678710938), Coord2(598.5122680664063, 852.48095703125)), Coord2(598.3043212890625, 852.2940063476563)) - .curve_to((Coord2(594.026123046875, 853.1077270507813), Coord2(590.7466430664063, 857.636474609375)), Coord2(597.5106811523438, 857.97314453125)) - .curve_to((Coord2(599.0369262695313, 859.3222045898438), Coord2(599.9699096679688, 856.8018798828125)), Coord2(600.7046508789063, 857.8336181640625)) - .curve_to((Coord2(602.2219848632813, 857.0648803710938), Coord2(604.2450561523438, 856.0399780273438)), Coord2(605.7623901367188, 855.271240234375)) - .curve_to((Coord2(606.0952758789063, 855.509765625), Coord2(607.6958618164063, 852.7109985351563)), Coord2(605.1021118164063, 850.0916748046875)) - .curve_to((Coord2(607.8820190429688, 846.5908203125), Coord2(593.875244140625, 843.7130737304688)), Coord2(588.6094360351563, 846.0635986328125)) - .curve_to((Coord2(588.1766357421875, 846.2265625), Coord2(587.21044921875, 849.5330200195313)), Coord2(587.5308227539063, 849.7504272460938)) - .curve_to((Coord2(588.139892578125, 853.0325317382813), Coord2(591.3778076171875, 858.6402587890625)), Coord2(592.365966796875, 858.1364135742188)) - .curve_to((Coord2(594.4129028320313, 861.7054443359375), Coord2(594.3702392578125, 859.3676147460938)), Coord2(593.9205932617188, 858.7909545898438)) - .curve_to((Coord2(593.531005859375, 859.0397338867188), Coord2(594.5933837890625, 855.8880004882813)), Coord2(594.7660522460938, 856.260986328125)) - .curve_to((Coord2(595.0291137695313, 855.397216796875), Coord2(599.3807983398438, 853.1875)), Coord2(598.6329345703125, 854.2422485351563)) - .curve_to((Coord2(598.4532470703125, 854.5361938476563), Coord2(597.2518920898438, 853.0940551757813)), Coord2(591.7156982421875, 850.7276611328125)) - .curve_to((Coord2(588.8387451171875, 848.8101196289063), Coord2(581.9440307617188, 846.1011962890625)), Coord2(578.2269897460938, 845.9464111328125)) - .curve_to((Coord2(577.8850708007813, 845.296875), Coord2(573.4860229492188, 846.9068603515625)), Coord2(572.7304077148438, 847.910888671875)) - .curve_to((Coord2(571.8158569335938, 847.940185546875), Coord2(569.74658203125, 852.5507202148438)), Coord2(570.394287109375, 853.1057739257813)) - .curve_to((Coord2(571.2783203125, 857.921142578125), Coord2(578.909423828125, 867.0386962890625)), Coord2(583.4408569335938, 868.6987915039063)) - .curve_to((Coord2(590.3077392578125, 875.8531494140625), Coord2(593.4223022460938, 875.197265625)), Coord2(591.9874877929688, 873.194091796875)) - .curve_to((Coord2(591.9872436523438, 873.1967163085938), Coord2(591.986328125, 873.0939331054688)), Coord2(591.9866333007813, 873.0940551757813)) - .curve_to((Coord2(594.6202392578125, 869.4052734375), Coord2(598.6082153320313, 863.8417358398438)), Coord2(595.7800903320313, 867.597900390625)) - .curve_to((Coord2(595.6080322265625, 867.6517333984375), Coord2(595.2333374023438, 867.7623901367188)), Coord2(594.86767578125, 867.8843994140625)) - .curve_to((Coord2(594.2318115234375, 868.0874633789063), Coord2(592.8426513671875, 868.5746459960938)), Coord2(591.7855224609375, 869.0294189453125)) - .curve_to((Coord2(590.3074340820313, 869.1311645507813), Coord2(585.4846801757813, 872.3850708007813)), Coord2(583.8530883789063, 874.7000122070313)) - .curve_to((Coord2(582.0348510742188, 877.52783203125), Coord2(580.2197875976563, 883.7698364257813)), Coord2(579.9202880859375, 886.5905151367188)) - .curve_to((Coord2(577.5986328125, 892.2477416992188), Coord2(579.3203735351563, 892.0960083007813)), Coord2(579.736328125, 890.97021484375)) - .curve_to((Coord2(579.3685302734375, 890.795166015625), Coord2(582.47412109375, 889.8472900390625)), Coord2(582.4383544921875, 890.154052734375)) - .curve_to((Coord2(583.9168090820313, 891.4367065429688), Coord2(587.2955322265625, 891.2642211914063)), Coord2(582.5258178710938, 886.7721557617188)) - .curve_to((Coord2(583.718017578125, 884.8529663085938), Coord2(576.7567138671875, 878.6074829101563)), Coord2(572.2820434570313, 877.3173217773438)) - .curve_to((Coord2(572.260009765625, 877.3126220703125), Coord2(571.8314208984375, 877.3272705078125)), Coord2(571.8067016601563, 877.3335571289063)) - .curve_to((Coord2(560.010498046875, 881.22509765625), Coord2(569.1023559570313, 904.441650390625)), Coord2(580.4130249023438, 899.290283203125)) - .curve_to((Coord2(587.358642578125, 896.5389404296875), Coord2(577.4598388671875, 865.2567138671875)), Coord2(569.1909790039063, 866.8143920898438)) - .curve_to((Coord2(564.2433471679688, 876.4852905273438), Coord2(565.2119750976563, 879.7418823242188)), Coord2(566.0805053710938, 882.063720703125)) - .curve_to((Coord2(566.91650390625, 884.5101928710938), Coord2(568.3486328125, 888.1217651367188)), Coord2(569.8612060546875, 890.8428344726563)) - .curve_to((Coord2(587.60546875, 903.775146484375), Coord2(589.3789672851563, 862.3728637695313)), Coord2(570.9526977539063, 861.138671875)) - .curve_to((Coord2(555.2870483398438, 863.2453002929688), Coord2(565.8951416015625, 904.8345336914063)), Coord2(580.59521484375, 900.0840454101563)) - .curve_to((Coord2(594.0852661132813, 895.3810424804688), Coord2(579.9393920898438, 852.4515380859375)), Coord2(565.9549560546875, 859.7515869140625)) - .curve_to((Coord2(557.6361083984375, 864.9191284179688), Coord2(571.5972900390625, 896.5897216796875)), Coord2(582.2666625976563, 893.362060546875)) - .curve_to((Coord2(596.2720947265625, 889.8972778320313), Coord2(586.6115112304688, 848.4343872070313)), Coord2(571.9376220703125, 850.0352172851563)) - .curve_to((Coord2(556.0201416015625, 851.1971435546875), Coord2(561.6185302734375, 885.7030029296875)), Coord2(577.4126586914063, 882.59521484375)) - .curve_to((Coord2(589.552978515625, 879.34228515625), Coord2(591.1780395507813, 853.8557739257813)), Coord2(584.5911254882813, 850.6495971679688)) - .curve_to((Coord2(573.9320678710938, 846.2090454101563), Coord2(564.6778564453125, 858.427490234375)), Coord2(573.91796875, 861.5853881835938)) - .curve_to((Coord2(572.7061767578125, 870.7952880859375), Coord2(589.696533203125, 873.0230712890625)), Coord2(593.0504760742188, 859.9937133789063)) - .curve_to((Coord2(595.9219970703125, 858.3511352539063), Coord2(586.99267578125, 843.552978515625)), Coord2(581.2372436523438, 842.6605834960938)) - .curve_to((Coord2(576.9470825195313, 848.0301513671875), Coord2(573.3963623046875, 858.2985229492188)), Coord2(577.3682861328125, 853.6705322265625)) - .curve_to((Coord2(573.3412475585938, 856.9037475585938), Coord2(593.6327514648438, 872.11083984375)), Coord2(601.6283569335938, 866.2468872070313)) - .curve_to((Coord2(603.8673706054688, 859.3587036132813), Coord2(597.4412231445313, 846.4760131835938)), Coord2(595.3387451171875, 847.1231689453125)) - .curve_to((Coord2(600.8814697265625, 844.0478515625), Coord2(586.74169921875, 842.0896606445313)), Coord2(580.974609375, 845.4783935546875)) - .curve_to((Coord2(577.481201171875, 849.10498046875), Coord2(595.04345703125, 864.8500366210938)), Coord2(599.9298095703125, 862.3732299804688)) - .curve_to((Coord2(603.3001708984375, 859.6436767578125), Coord2(598.7106323242188, 838.4157104492188)), Coord2(590.7146606445313, 839.1911010742188)) - .curve_to((Coord2(586.1773071289063, 843.9035034179688), Coord2(581.3427124023438, 853.1783447265625)), Coord2(589.6311645507813, 853.3121948242188)) - .curve_to((Coord2(591.4857788085938, 861.0636596679688), Coord2(603.1357421875, 865.7894287109375)), Coord2(608.3871459960938, 864.779541015625)) - .curve_to((Coord2(609.6311645507813, 860.09716796875), Coord2(608.9767456054688, 852.512939453125)), Coord2(607.8642578125, 855.7709350585938)) - .curve_to((Coord2(604.8263549804688, 851.1680908203125), Coord2(599.0707397460938, 843.4915161132813)), Coord2(593.83251953125, 839.4678955078125)) - .curve_to((Coord2(588.12841796875, 843.3626708984375), Coord2(584.9580688476563, 854.13720703125)), Coord2(590.4581909179688, 850.477294921875)) - .curve_to((Coord2(593.902099609375, 854.3041381835938), Coord2(597.5442504882813, 858.5637817382813)), Coord2(601.3976440429688, 862.492431640625)) - .curve_to((Coord2(596.497314453125, 864.138427734375), Coord2(605.6493530273438, 863.7943115234375)), Coord2(611.2799682617188, 862.292236328125)) - .curve_to((Coord2(610.607666015625, 857.74365234375), Coord2(606.7666625976563, 851.5074462890625)), Coord2(606.7361450195313, 853.9833984375)) - .curve_to((Coord2(602.8890380859375, 849.8455200195313), Coord2(597.0514526367188, 846.7515869140625)), Coord2(593.0549926757813, 843.3157958984375)) - .curve_to((Coord2(591.688232421875, 841.3230590820313), Coord2(585.3775024414063, 841.3017578125)), Coord2(589.6620483398438, 842.685791015625)) - .build(); + let path = + BezierPathBuilder::::start(Coord2(589.8298950195313, 841.699951171875)) + .curve_to( + ( + Coord2(589.8298950195313, 841.699951171875), + Coord2(589.8298950195313, 841.699951171875), + ), + Coord2(589.8298950195313, 841.699951171875), + ) + .curve_to( + ( + Coord2(585.0781860351563, 841.545166015625), + Coord2(588.116943359375, 846.1569213867188), + ), + Coord2(589.9508056640625, 846.92041015625), + ) + .curve_to( + ( + Coord2(593.9074096679688, 850.3338623046875), + Coord2(596.3680419921875, 855.8639526367188), + ), + Coord2(600.2550048828125, 860.024169921875), + ) + .curve_to( + ( + Coord2(602.3019409179688, 864.72900390625), + Coord2(603.487060546875, 861.721435546875), + ), + Coord2(602.1428833007813, 859.0895385742188), + ) + .curve_to( + ( + Coord2(607.4638061523438, 858.4710693359375), + Coord2(614.4444580078125, 855.14404296875), + ), + Coord2(608.3931884765625, 855.6187133789063), + ) + .curve_to( + ( + Coord2(604.7843627929688, 851.9526977539063), + Coord2(601.4735107421875, 847.9655151367188), + ), + Coord2(597.78515625, 843.8760986328125), + ) + .curve_to( + ( + Coord2(601.0536499023438, 837.7391357421875), + Coord2(590.90966796875, 841.439453125), + ), + Coord2(587.8450927734375, 847.3414916992188), + ) + .curve_to( + ( + Coord2(592.2240600585938, 850.6311645507813), + Coord2(595.8001098632813, 856.1324462890625), + ), + Coord2(599.6971435546875, 861.4691772460938), + ) + .curve_to( + ( + Coord2(599.6600952148438, 866.1685546875), + Coord2(601.5029907226563, 861.010498046875), + ), + Coord2(601.408447265625, 857.6356811523438), + ) + .curve_to( + ( + Coord2(605.051025390625, 858.197509765625), + Coord2(608.0866088867188, 854.1636352539063), + ), + Coord2(597.3378295898438, 846.8604125976563), + ) + .curve_to( + ( + Coord2(597.2238159179688, 836.9576416015625), + Coord2(590.7571411132813, 843.5430297851563), + ), + Coord2(587.1199340820313, 848.599365234375), + ) + .curve_to( + ( + Coord2(588.7532348632813, 853.0540161132813), + Coord2(591.633544921875, 856.119873046875), + ), + Coord2(594.626708984375, 853.6188354492188), + ) + .curve_to( + ( + Coord2(596.7156982421875, 852.8362426757813), + Coord2(595.0059814453125, 845.878662109375), + ), + Coord2(591.52490234375, 845.5113525390625), + ) + .curve_to( + ( + Coord2(585.76171875, 847.6647338867188), + Coord2(580.7750244140625, 855.853759765625), + ), + Coord2(586.7627563476563, 853.3876342773438), + ) + .curve_to( + ( + Coord2(588.5208129882813, 859.3195190429688), + Coord2(594.2566528320313, 860.6160278320313), + ), + Coord2(592.3621826171875, 860.9254760742188), + ) + .curve_to( + ( + Coord2(594.9733276367188, 864.4375), + Coord2(593.3421020507813, 848.7232055664063), + ), + Coord2(586.76220703125, 847.8418579101563), + ) + .curve_to( + ( + Coord2(589.7845458984375, 841.6835327148438), + Coord2(583.6079711914063, 848.498046875), + ), + Coord2(580.9037475585938, 853.9146118164063), + ) + .curve_to( + ( + Coord2(580.701904296875, 853.186767578125), + Coord2(578.50439453125, 857.2315063476563), + ), + Coord2(581.5901489257813, 860.4940795898438), + ) + .curve_to( + ( + Coord2(585.6346435546875, 863.285400390625), + Coord2(589.900146484375, 854.3807373046875), + ), + Coord2(584.1525268554688, 856.2511596679688), + ) + .curve_to( + ( + Coord2(590.3831787109375, 852.05712890625), + Coord2(578.9157104492188, 850.2012329101563), + ), + Coord2(574.5430297851563, 856.5203247070313), + ) + .curve_to( + ( + Coord2(573.6943969726563, 863.1355590820313), + Coord2(580.0052490234375, 871.26220703125), + ), + Coord2(575.3004760742188, 871.1060791015625), + ) + .curve_to( + ( + Coord2(576.81103515625, 870.624267578125), + Coord2(572.30712890625, 859.2913818359375), + ), + Coord2(570.9198608398438, 861.718994140625), + ) + .curve_to( + ( + Coord2(572.5287475585938, 864.7382202148438), + Coord2(581.41259765625, 882.9050903320313), + ), + Coord2(580.4722900390625, 881.7498779296875), + ) + .curve_to( + ( + Coord2(580.0606689453125, 880.2344970703125), + Coord2(575.6553955078125, 869.0311889648438), + ), + Coord2(573.716552734375, 868.6065673828125), + ) + .curve_to( + ( + Coord2(570.4192504882813, 866.5391845703125), + Coord2(572.1432495117188, 889.7837524414063), + ), + Coord2(575.9349365234375, 889.2540893554688), + ) + .curve_to( + ( + Coord2(579.9112548828125, 889.1182250976563), + Coord2(573.3362426757813, 870.1537475585938), + ), + Coord2(570.325439453125, 872.933349609375), + ) + .curve_to( + ( + Coord2(566.7039184570313, 872.4866333007813), + Coord2(575.889892578125, 896.3516845703125), + ), + Coord2(580.193359375, 885.1004028320313), + ) + .curve_to( + ( + Coord2(578.9361572265625, 882.8379516601563), + Coord2(578.29638671875, 880.9623413085938), + ), + Coord2(577.2049560546875, 878.0570678710938), + ) + .curve_to( + ( + Coord2(576.3244018554688, 875.5227661132813), + Coord2(575.8396606445313, 874.0106811523438), + ), + Coord2(575.3523559570313, 871.5857543945313), + ) + .curve_to( + ( + Coord2(567.6146240234375, 879.8153076171875), + Coord2(569.26904296875, 890.168212890625), + ), + Coord2(572.8831176757813, 890.166259765625), + ) + .curve_to( + ( + Coord2(580.7759399414063, 887.835693359375), + Coord2(580.0247802734375, 885.56103515625), + ), + Coord2(572.6173095703125, 889.1515502929688), + ) + .curve_to( + ( + Coord2(572.6390991210938, 889.1546020507813), + Coord2(572.2571411132813, 889.167724609375), + ), + Coord2(572.2820434570313, 889.1630249023438), + ) + .curve_to( + ( + Coord2(570.7896728515625, 887.8728637695313), + Coord2(567.4065551757813, 888.0462036132813), + ), + Coord2(572.1813354492188, 892.5457763671875), + ) + .curve_to( + ( + Coord2(570.9942016601563, 894.4725341796875), + Coord2(577.9598999023438, 900.7188720703125), + ), + Coord2(582.4383544921875, 902.0015258789063), + ) + .curve_to( + ( + Coord2(582.8182373046875, 902.308349609375), + Coord2(586.3283081054688, 901.2371826171875), + ), + Coord2(586.35205078125, 900.798583984375), + ) + .curve_to( + ( + Coord2(588.947998046875, 898.2053833007813), + Coord2(592.195068359375, 891.016845703125), + ), + Coord2(591.5047607421875, 889.0786743164063), + ) + .curve_to( + ( + Coord2(592.836669921875, 884.303955078125), + Coord2(592.759033203125, 882.3919677734375), + ), + Coord2(593.544921875, 881.51806640625), + ) + .curve_to( + ( + Coord2(594.1064453125, 880.7155151367188), + Coord2(593.8582153320313, 881.4864501953125), + ), + Coord2(596.4064331054688, 879.8722534179688), + ) + .curve_to( + ( + Coord2(597.3624877929688, 879.4691162109375), + Coord2(597.849365234375, 879.2901611328125), + ), + Coord2(598.5863037109375, 879.035400390625), + ) + .curve_to( + ( + Coord2(598.9070434570313, 878.928466796875), + Coord2(599.0929565429688, 878.8623657226563), + ), + Coord2(599.3098754882813, 878.7935180664063), + ) + .curve_to( + ( + Coord2(596.8707275390625, 882.4271240234375), + Coord2(601.0760498046875, 876.7950439453125), + ), + Coord2(603.7096557617188, 873.0940551757813), + ) + .curve_to( + ( + Coord2(603.7099609375, 873.09423828125), + Coord2(603.7090454101563, 872.9913940429688), + ), + Coord2(603.708740234375, 872.9912109375), + ) + .curve_to( + ( + Coord2(602.2408447265625, 869.050048828125), + Coord2(594.5162353515625, 860.6947021484375), + ), + Coord2(590.5521850585938, 859.4874267578125), + ) + .curve_to( + ( + Coord2(584.2527465820313, 852.785888671875), + Coord2(581.061279296875, 852.8796997070313), + ), + Coord2(581.9452514648438, 853.1057739257813), + ) + .curve_to( + ( + Coord2(582.593017578125, 852.9660034179688), + Coord2(580.7717895507813, 856.9833984375), + ), + Coord2(580.3909301757813, 856.538818359375), + ) + .curve_to( + ( + Coord2(580.433837890625, 856.8338623046875), + Coord2(577.17236328125, 858.1321411132813), + ), + Coord2(578.2269897460938, 857.4826049804688), + ) + .curve_to( + ( + Coord2(579.6019897460938, 857.3278198242188), + Coord2(581.242431640625, 858.7636108398438), + ), + Coord2(586.4614868164063, 860.9908447265625), + ) + .curve_to( + ( + Coord2(589.0213012695313, 862.7692260742188), + Coord2(595.4768676757813, 865.4718017578125), + ), + Coord2(598.6329345703125, 865.7658081054688), + ) + .curve_to( + ( + Coord2(598.567138671875, 866.820556640625), + Coord2(603.420166015625, 864.4375610351563), + ), + Coord2(603.9707641601563, 863.19189453125), + ) + .curve_to( + ( + Coord2(604.5771484375, 862.9888916015625), + Coord2(605.8325805664063, 859.209716796875), + ), + Coord2(605.4430541992188, 858.7909545898438), + ) + .curve_to( + ( + Coord2(604.993408203125, 855.49951171875), + Coord2(601.7562866210938, 849.7847900390625), + ), + Coord2(600.6087036132813, 850.0838623046875), + ) + .curve_to( + ( + Coord2(598.4024047851563, 846.3101196289063), + Coord2(598.4458618164063, 848.7549438476563), + ), + Coord2(599.054931640625, 849.7504272460938), + ) + .curve_to( + ( + Coord2(599.3753051757813, 849.5570678710938), + Coord2(598.5122680664063, 852.48095703125), + ), + Coord2(598.3043212890625, 852.2940063476563), + ) + .curve_to( + ( + Coord2(594.026123046875, 853.1077270507813), + Coord2(590.7466430664063, 857.636474609375), + ), + Coord2(597.5106811523438, 857.97314453125), + ) + .curve_to( + ( + Coord2(599.0369262695313, 859.3222045898438), + Coord2(599.9699096679688, 856.8018798828125), + ), + Coord2(600.7046508789063, 857.8336181640625), + ) + .curve_to( + ( + Coord2(602.2219848632813, 857.0648803710938), + Coord2(604.2450561523438, 856.0399780273438), + ), + Coord2(605.7623901367188, 855.271240234375), + ) + .curve_to( + ( + Coord2(606.0952758789063, 855.509765625), + Coord2(607.6958618164063, 852.7109985351563), + ), + Coord2(605.1021118164063, 850.0916748046875), + ) + .curve_to( + ( + Coord2(607.8820190429688, 846.5908203125), + Coord2(593.875244140625, 843.7130737304688), + ), + Coord2(588.6094360351563, 846.0635986328125), + ) + .curve_to( + ( + Coord2(588.1766357421875, 846.2265625), + Coord2(587.21044921875, 849.5330200195313), + ), + Coord2(587.5308227539063, 849.7504272460938), + ) + .curve_to( + ( + Coord2(588.139892578125, 853.0325317382813), + Coord2(591.3778076171875, 858.6402587890625), + ), + Coord2(592.365966796875, 858.1364135742188), + ) + .curve_to( + ( + Coord2(594.4129028320313, 861.7054443359375), + Coord2(594.3702392578125, 859.3676147460938), + ), + Coord2(593.9205932617188, 858.7909545898438), + ) + .curve_to( + ( + Coord2(593.531005859375, 859.0397338867188), + Coord2(594.5933837890625, 855.8880004882813), + ), + Coord2(594.7660522460938, 856.260986328125), + ) + .curve_to( + ( + Coord2(595.0291137695313, 855.397216796875), + Coord2(599.3807983398438, 853.1875), + ), + Coord2(598.6329345703125, 854.2422485351563), + ) + .curve_to( + ( + Coord2(598.4532470703125, 854.5361938476563), + Coord2(597.2518920898438, 853.0940551757813), + ), + Coord2(591.7156982421875, 850.7276611328125), + ) + .curve_to( + ( + Coord2(588.8387451171875, 848.8101196289063), + Coord2(581.9440307617188, 846.1011962890625), + ), + Coord2(578.2269897460938, 845.9464111328125), + ) + .curve_to( + ( + Coord2(577.8850708007813, 845.296875), + Coord2(573.4860229492188, 846.9068603515625), + ), + Coord2(572.7304077148438, 847.910888671875), + ) + .curve_to( + ( + Coord2(571.8158569335938, 847.940185546875), + Coord2(569.74658203125, 852.5507202148438), + ), + Coord2(570.394287109375, 853.1057739257813), + ) + .curve_to( + ( + Coord2(571.2783203125, 857.921142578125), + Coord2(578.909423828125, 867.0386962890625), + ), + Coord2(583.4408569335938, 868.6987915039063), + ) + .curve_to( + ( + Coord2(590.3077392578125, 875.8531494140625), + Coord2(593.4223022460938, 875.197265625), + ), + Coord2(591.9874877929688, 873.194091796875), + ) + .curve_to( + ( + Coord2(591.9872436523438, 873.1967163085938), + Coord2(591.986328125, 873.0939331054688), + ), + Coord2(591.9866333007813, 873.0940551757813), + ) + .curve_to( + ( + Coord2(594.6202392578125, 869.4052734375), + Coord2(598.6082153320313, 863.8417358398438), + ), + Coord2(595.7800903320313, 867.597900390625), + ) + .curve_to( + ( + Coord2(595.6080322265625, 867.6517333984375), + Coord2(595.2333374023438, 867.7623901367188), + ), + Coord2(594.86767578125, 867.8843994140625), + ) + .curve_to( + ( + Coord2(594.2318115234375, 868.0874633789063), + Coord2(592.8426513671875, 868.5746459960938), + ), + Coord2(591.7855224609375, 869.0294189453125), + ) + .curve_to( + ( + Coord2(590.3074340820313, 869.1311645507813), + Coord2(585.4846801757813, 872.3850708007813), + ), + Coord2(583.8530883789063, 874.7000122070313), + ) + .curve_to( + ( + Coord2(582.0348510742188, 877.52783203125), + Coord2(580.2197875976563, 883.7698364257813), + ), + Coord2(579.9202880859375, 886.5905151367188), + ) + .curve_to( + ( + Coord2(577.5986328125, 892.2477416992188), + Coord2(579.3203735351563, 892.0960083007813), + ), + Coord2(579.736328125, 890.97021484375), + ) + .curve_to( + ( + Coord2(579.3685302734375, 890.795166015625), + Coord2(582.47412109375, 889.8472900390625), + ), + Coord2(582.4383544921875, 890.154052734375), + ) + .curve_to( + ( + Coord2(583.9168090820313, 891.4367065429688), + Coord2(587.2955322265625, 891.2642211914063), + ), + Coord2(582.5258178710938, 886.7721557617188), + ) + .curve_to( + ( + Coord2(583.718017578125, 884.8529663085938), + Coord2(576.7567138671875, 878.6074829101563), + ), + Coord2(572.2820434570313, 877.3173217773438), + ) + .curve_to( + ( + Coord2(572.260009765625, 877.3126220703125), + Coord2(571.8314208984375, 877.3272705078125), + ), + Coord2(571.8067016601563, 877.3335571289063), + ) + .curve_to( + ( + Coord2(560.010498046875, 881.22509765625), + Coord2(569.1023559570313, 904.441650390625), + ), + Coord2(580.4130249023438, 899.290283203125), + ) + .curve_to( + ( + Coord2(587.358642578125, 896.5389404296875), + Coord2(577.4598388671875, 865.2567138671875), + ), + Coord2(569.1909790039063, 866.8143920898438), + ) + .curve_to( + ( + Coord2(564.2433471679688, 876.4852905273438), + Coord2(565.2119750976563, 879.7418823242188), + ), + Coord2(566.0805053710938, 882.063720703125), + ) + .curve_to( + ( + Coord2(566.91650390625, 884.5101928710938), + Coord2(568.3486328125, 888.1217651367188), + ), + Coord2(569.8612060546875, 890.8428344726563), + ) + .curve_to( + ( + Coord2(587.60546875, 903.775146484375), + Coord2(589.3789672851563, 862.3728637695313), + ), + Coord2(570.9526977539063, 861.138671875), + ) + .curve_to( + ( + Coord2(555.2870483398438, 863.2453002929688), + Coord2(565.8951416015625, 904.8345336914063), + ), + Coord2(580.59521484375, 900.0840454101563), + ) + .curve_to( + ( + Coord2(594.0852661132813, 895.3810424804688), + Coord2(579.9393920898438, 852.4515380859375), + ), + Coord2(565.9549560546875, 859.7515869140625), + ) + .curve_to( + ( + Coord2(557.6361083984375, 864.9191284179688), + Coord2(571.5972900390625, 896.5897216796875), + ), + Coord2(582.2666625976563, 893.362060546875), + ) + .curve_to( + ( + Coord2(596.2720947265625, 889.8972778320313), + Coord2(586.6115112304688, 848.4343872070313), + ), + Coord2(571.9376220703125, 850.0352172851563), + ) + .curve_to( + ( + Coord2(556.0201416015625, 851.1971435546875), + Coord2(561.6185302734375, 885.7030029296875), + ), + Coord2(577.4126586914063, 882.59521484375), + ) + .curve_to( + ( + Coord2(589.552978515625, 879.34228515625), + Coord2(591.1780395507813, 853.8557739257813), + ), + Coord2(584.5911254882813, 850.6495971679688), + ) + .curve_to( + ( + Coord2(573.9320678710938, 846.2090454101563), + Coord2(564.6778564453125, 858.427490234375), + ), + Coord2(573.91796875, 861.5853881835938), + ) + .curve_to( + ( + Coord2(572.7061767578125, 870.7952880859375), + Coord2(589.696533203125, 873.0230712890625), + ), + Coord2(593.0504760742188, 859.9937133789063), + ) + .curve_to( + ( + Coord2(595.9219970703125, 858.3511352539063), + Coord2(586.99267578125, 843.552978515625), + ), + Coord2(581.2372436523438, 842.6605834960938), + ) + .curve_to( + ( + Coord2(576.9470825195313, 848.0301513671875), + Coord2(573.3963623046875, 858.2985229492188), + ), + Coord2(577.3682861328125, 853.6705322265625), + ) + .curve_to( + ( + Coord2(573.3412475585938, 856.9037475585938), + Coord2(593.6327514648438, 872.11083984375), + ), + Coord2(601.6283569335938, 866.2468872070313), + ) + .curve_to( + ( + Coord2(603.8673706054688, 859.3587036132813), + Coord2(597.4412231445313, 846.4760131835938), + ), + Coord2(595.3387451171875, 847.1231689453125), + ) + .curve_to( + ( + Coord2(600.8814697265625, 844.0478515625), + Coord2(586.74169921875, 842.0896606445313), + ), + Coord2(580.974609375, 845.4783935546875), + ) + .curve_to( + ( + Coord2(577.481201171875, 849.10498046875), + Coord2(595.04345703125, 864.8500366210938), + ), + Coord2(599.9298095703125, 862.3732299804688), + ) + .curve_to( + ( + Coord2(603.3001708984375, 859.6436767578125), + Coord2(598.7106323242188, 838.4157104492188), + ), + Coord2(590.7146606445313, 839.1911010742188), + ) + .curve_to( + ( + Coord2(586.1773071289063, 843.9035034179688), + Coord2(581.3427124023438, 853.1783447265625), + ), + Coord2(589.6311645507813, 853.3121948242188), + ) + .curve_to( + ( + Coord2(591.4857788085938, 861.0636596679688), + Coord2(603.1357421875, 865.7894287109375), + ), + Coord2(608.3871459960938, 864.779541015625), + ) + .curve_to( + ( + Coord2(609.6311645507813, 860.09716796875), + Coord2(608.9767456054688, 852.512939453125), + ), + Coord2(607.8642578125, 855.7709350585938), + ) + .curve_to( + ( + Coord2(604.8263549804688, 851.1680908203125), + Coord2(599.0707397460938, 843.4915161132813), + ), + Coord2(593.83251953125, 839.4678955078125), + ) + .curve_to( + ( + Coord2(588.12841796875, 843.3626708984375), + Coord2(584.9580688476563, 854.13720703125), + ), + Coord2(590.4581909179688, 850.477294921875), + ) + .curve_to( + ( + Coord2(593.902099609375, 854.3041381835938), + Coord2(597.5442504882813, 858.5637817382813), + ), + Coord2(601.3976440429688, 862.492431640625), + ) + .curve_to( + ( + Coord2(596.497314453125, 864.138427734375), + Coord2(605.6493530273438, 863.7943115234375), + ), + Coord2(611.2799682617188, 862.292236328125), + ) + .curve_to( + ( + Coord2(610.607666015625, 857.74365234375), + Coord2(606.7666625976563, 851.5074462890625), + ), + Coord2(606.7361450195313, 853.9833984375), + ) + .curve_to( + ( + Coord2(602.8890380859375, 849.8455200195313), + Coord2(597.0514526367188, 846.7515869140625), + ), + Coord2(593.0549926757813, 843.3157958984375), + ) + .curve_to( + ( + Coord2(591.688232421875, 841.3230590820313), + Coord2(585.3775024414063, 841.3017578125), + ), + Coord2(589.6620483398438, 842.685791015625), + ) + .build(); let mut graph_path = GraphPath::from_path(&path, PathLabel(0, PathDirection::Clockwise)); graph_path.self_collide(0.01); @@ -428,10 +2102,12 @@ fn remove_interior_points_complex_2_without_healing() { // Must always be a following edge that's an exterior one for edge in graph_path.all_edges() { - let end_point_idx = edge.end_point_index(); - let edge_ref = edge.into(); + let end_point_idx = edge.end_point_index(); + let edge_ref = edge.into(); if graph_path.edge_kind(edge_ref) == GraphPathEdgeKind::Exterior { - assert!(graph_path.edge_refs_for_point(end_point_idx).any(|edge| graph_path.edge_kind(edge) == GraphPathEdgeKind::Exterior)); + assert!(graph_path + .edge_refs_for_point(end_point_idx) + .any(|edge| graph_path.edge_kind(edge) == GraphPathEdgeKind::Exterior)); } } } @@ -440,84 +2116,541 @@ fn remove_interior_points_complex_2_without_healing() { fn remove_interior_points_3() { // This path produces a failure due to missing collisions in FlowBetween // ... it works *absolutely fine* here (collisions intact) - let path = BezierPathBuilder::::start(Coord2(632.2468872070313, 700.9489135742188)) - .curve_to((Coord2(632.2468872070313, 700.9489135742188), Coord2(632.2468872070313, 700.9489135742188)), Coord2(632.2468872070313, 700.9489135742188)) - .curve_to((Coord2(635.8739624023438, 713.1276245117188), Coord2(634.984375, 724.8035888671875)), Coord2(636.5083618164063, 736.8372192382813)) - .curve_to((Coord2(636.55078125, 737.1727905273438), Coord2(636.52880859375, 737.5175170898438)), Coord2(636.5714721679688, 737.8543090820313)) - .curve_to((Coord2(637.0103759765625, 741.3486328125), Coord2(637.1661376953125, 746.5886840820313)), Coord2(637.3720092773438, 750.5798950195313)) - .curve_to((Coord2(637.3945922851563, 751.0081176757813), Coord2(637.3880615234375, 751.6552124023438)), Coord2(637.397705078125, 752.5184936523438)) - .curve_to((Coord2(633.1192016601563, 754.1141357421875), Coord2(639.5689086914063, 756.6727905273438)), Coord2(644.0848999023438, 755.830810546875)) - .curve_to((Coord2(644.1641235351563, 754.98046875), Coord2(644.1864013671875, 754.6741333007813)), Coord2(644.1986694335938, 754.3605346679688)) - .curve_to((Coord2(644.2012329101563, 754.29443359375), Coord2(644.2059936523438, 754.22802734375)), Coord2(644.2086181640625, 754.1612548828125)) - .curve_to((Coord2(644.2246704101563, 753.1409301757813), Coord2(644.22607421875, 752.155517578125)), Coord2(644.2214965820313, 751.2548217773438)) - .curve_to((Coord2(644.2239990234375, 748.8275756835938), Coord2(644.20458984375, 746.9262084960938)), Coord2(644.4571533203125, 744.779296875)) - .curve_to((Coord2(648.3836059570313, 744.2342529296875), Coord2(643.2928466796875, 738.7249145507813)), Coord2(641.3601684570313, 741.8130493164063)) - .curve_to((Coord2(642.6392822265625, 748.04541015625), Coord2(643.9271240234375, 782.0206298828125)), Coord2(641.6926879882813, 782.131591796875)) - .curve_to((Coord2(642.8214111328125, 783.0655517578125), Coord2(645.96337890625, 783.1430053710938)), Coord2(643.7222900390625, 782.1798095703125)) - .curve_to((Coord2(642.6692504882813, 776.1190795898438), Coord2(643.2499389648438, 766.5458374023438)), Coord2(646.3471069335938, 761.6445922851563)) - .curve_to((Coord2(646.038330078125, 762.0671997070313), Coord2(646.3682250976563, 761.8047485351563)), Coord2(646.708740234375, 761.5353393554688)) - .curve_to((Coord2(644.1248168945313, 761.16552734375), Coord2(641.0634765625, 760.8419189453125)), Coord2(643.1715087890625, 762.1446533203125)) - .curve_to((Coord2(643.171875, 762.147705078125), Coord2(643.178466796875, 762.1910400390625)), Coord2(643.181884765625, 762.213134765625)) - .curve_to((Coord2(643.8663330078125, 769.598388671875), Coord2(642.75048828125, 789.9730834960938)), Coord2(638.2301025390625, 795.2767333984375)) - .curve_to((Coord2(639.1027221679688, 794.624267578125), Coord2(638.9803466796875, 794.6729736328125)), Coord2(638.857666015625, 794.72216796875)) - .curve_to((Coord2(638.6315307617188, 794.8128051757813), Coord2(638.40380859375, 794.9035034179688)), Coord2(638.1716918945313, 794.9965209960938)) - .curve_to((Coord2(638.3427734375, 795.274169921875), Coord2(644.6337280273438, 777.146728515625)), Coord2(645.4585571289063, 767.8198852539063)) - .curve_to((Coord2(645.701171875, 766.77490234375), Coord2(645.90869140625, 765.9208984375)), Coord2(646.0075073242188, 765.452880859375)) - .curve_to((Coord2(646.3751220703125, 763.7075805664063), Coord2(646.7393798828125, 761.952392578125)), Coord2(647.0966186523438, 760.2139892578125)) - .curve_to((Coord2(647.5424194335938, 758.0451049804688), Coord2(647.9866943359375, 755.904052734375)), Coord2(648.4459838867188, 753.752197265625)) - .curve_to((Coord2(652.6451416015625, 750.7460327148438), Coord2(647.7916259765625, 749.9540405273438)), Coord2(644.3892822265625, 749.474365234375)) - .curve_to((Coord2(645.332763671875, 755.0831298828125), Coord2(643.2569580078125, 763.2509155273438)), Coord2(641.4593505859375, 771.6987915039063)) - .curve_to((Coord2(640.708740234375, 774.979736328125), Coord2(639.9559936523438, 778.2863159179688)), Coord2(639.5221557617188, 780.9598388671875)) - .curve_to((Coord2(639.490234375, 781.1691284179688), Coord2(639.4317626953125, 781.6621704101563)), Coord2(639.3948974609375, 782.05224609375)) - .curve_to((Coord2(637.6366577148438, 785.9545288085938), Coord2(639.2575073242188, 789.9832763671875)), Coord2(639.6422119140625, 788.78515625)) - .curve_to((Coord2(640.965576171875, 789.996826171875), Coord2(644.2919921875, 791.590576171875)), Coord2(642.24267578125, 789.4522705078125)) - .curve_to((Coord2(642.2576904296875, 789.2343139648438), Coord2(642.2846069335938, 788.7120361328125)), Coord2(642.2734375, 788.3878173828125)) - .curve_to((Coord2(642.06689453125, 783.2173461914063), Coord2(642.5938110351563, 778.2110595703125)), Coord2(643.05029296875, 772.7711181640625)) - .curve_to((Coord2(643.207275390625, 770.955078125), Coord2(643.3928833007813, 769.0886840820313)), Coord2(643.5234985351563, 767.259765625)) - .curve_to((Coord2(643.5761108398438, 766.5219116210938), Coord2(643.6470947265625, 765.5867309570313)), Coord2(643.7337646484375, 764.4581298828125)) - .curve_to((Coord2(645.1050415039063, 753.1468505859375), Coord2(647.046630859375, 717.7682495117188)), Coord2(641.641845703125, 710.2857055664063)) - .curve_to((Coord2(636.7783203125, 710.5094604492188), Coord2(636.7587280273438, 710.9183349609375)), Coord2(636.489990234375, 711.1563110351563)) - .curve_to((Coord2(635.70361328125, 711.7706298828125), Coord2(635.5377807617188, 713.2534790039063)), Coord2(635.5494995117188, 714.0062255859375)) - .curve_to((Coord2(635.4976806640625, 712.0926513671875), Coord2(635.5260620117188, 713.5062866210938)), Coord2(635.5267333984375, 714.4866943359375)) - .curve_to((Coord2(636.59814453125, 714.7744750976563), Coord2(638.026611328125, 715.1581420898438)), Coord2(639.0980224609375, 715.4459228515625)) - .curve_to((Coord2(639.8369750976563, 713.6776123046875), Coord2(639.678466796875, 715.2540893554688)), Coord2(639.9312133789063, 715.1055908203125)) - .curve_to((Coord2(639.8008422851563, 713.9517211914063), Coord2(639.6350708007813, 715.4345703125)), Coord2(639.9312133789063, 715.1055908203125)) - .curve_to((Coord2(640.1494750976563, 714.919189453125), Coord2(640.8361206054688, 714.7127685546875)), Coord2(641.39453125, 714.1593627929688)) - .curve_to((Coord2(641.607666015625, 719.16650390625), Coord2(638.4246215820313, 750.5006713867188)), Coord2(637.8893432617188, 764.0092163085938)) - .curve_to((Coord2(637.8035888671875, 765.1250610351563), Coord2(637.7284545898438, 766.0917358398438)), Coord2(637.6748657226563, 766.84228515625)) - .curve_to((Coord2(637.5464477539063, 768.6419677734375), Coord2(637.4169921875, 770.42578125)), Coord2(637.2578735351563, 772.2711181640625)) - .curve_to((Coord2(636.795654296875, 777.473388671875), Coord2(636.3956909179688, 783.2040405273438)), Coord2(636.60791015625, 788.612060546875)) - .curve_to((Coord2(636.615234375, 788.7552490234375), Coord2(636.6175537109375, 788.81494140625)), Coord2(636.6064453125, 789.2139282226563)) - .curve_to((Coord2(634.4679565429688, 789.1854248046875), Coord2(637.6878662109375, 793.2979125976563)), Coord2(640.5524291992188, 794.2531127929688)) - .curve_to((Coord2(643.1683959960938, 792.6835327148438), Coord2(646.1170043945313, 783.748046875)), Coord2(644.8746337890625, 782.613525390625)) - .curve_to((Coord2(644.9351196289063, 782.0530395507813), Coord2(644.945068359375, 781.8778686523438)), Coord2(644.9533081054688, 781.839111328125)) - .curve_to((Coord2(645.3638305664063, 779.298095703125), Coord2(646.0396118164063, 776.3313598632813)), Coord2(646.8134155273438, 772.91796875)) - .curve_to((Coord2(648.6615600585938, 765.3552856445313), Coord2(650.780029296875, 755.1043701171875)), Coord2(649.7860107421875, 748.6101684570313)) - .curve_to((Coord2(646.33837890625, 747.8474731445313), Coord2(640.4654541015625, 748.3662719726563)), Coord2(643.1162109375, 752.6146240234375)) - .curve_to((Coord2(642.6539916992188, 754.7799682617188), Coord2(642.2095947265625, 756.9632568359375)), Coord2(641.7667236328125, 759.11865234375)) - .curve_to((Coord2(641.4076538085938, 760.8657836914063), Coord2(641.0556030273438, 762.5961303710938)), Coord2(640.6897583007813, 764.3326416015625)) - .curve_to((Coord2(640.6021728515625, 764.7493896484375), Coord2(640.4375610351563, 765.5008544921875)), Coord2(640.1837768554688, 766.5970458984375)) - .curve_to((Coord2(637.48779296875, 772.45751953125), Coord2(634.00927734375, 796.4724731445313)), Coord2(637.9215087890625, 799.7769775390625)) - .curve_to((Coord2(640.3659057617188, 799.7997436523438), Coord2(640.5935668945313, 799.7078857421875)), Coord2(640.8196411132813, 799.6173095703125)) - .curve_to((Coord2(640.9423217773438, 799.568115234375), Coord2(641.0643920898438, 799.5188598632813)), Coord2(641.1866455078125, 799.4699096679688)) - .curve_to((Coord2(647.5555419921875, 792.8746948242188), Coord2(650.9056396484375, 768.038818359375)), Coord2(649.3676147460938, 761.2432861328125)) - .curve_to((Coord2(649.3682250976563, 761.2474975585938), Coord2(649.37109375, 761.2653198242188)), Coord2(649.3666381835938, 761.2356567382813)) - .curve_to((Coord2(651.3629150390625, 761.7766723632813), Coord2(647.6148681640625, 756.7731323242188)), Coord2(644.4575805664063, 755.9600830078125)) - .curve_to((Coord2(642.4801635742188, 756.8904418945313), Coord2(642.14892578125, 757.1510620117188)), Coord2(641.8184204101563, 757.4124755859375)) - .curve_to((Coord2(637.2826538085938, 763.3823852539063), Coord2(635.7777709960938, 778.0116577148438)), Coord2(637.630126953125, 783.6883544921875)) - .curve_to((Coord2(635.5146484375, 783.2322387695313), Coord2(639.78515625, 787.8326416015625)), Coord2(643.0133056640625, 788.3185424804688)) - .curve_to((Coord2(649.2066650390625, 786.63037109375), Coord2(652.2764892578125, 743.0693359375)), Coord2(647.0385131835938, 738.4141235351563)) - .curve_to((Coord2(641.272216796875, 735.0977172851563), Coord2(634.1425170898438, 743.2423095703125)), Coord2(637.9150390625, 744.0158081054688)) - .curve_to((Coord2(637.6356201171875, 746.4281005859375), Coord2(637.637939453125, 749.1239013671875)), Coord2(637.6622314453125, 751.2858276367188)) - .curve_to((Coord2(637.6663208007813, 752.229248046875), Coord2(637.6764526367188, 753.086669921875)), Coord2(637.6630859375, 753.984619140625)) - .curve_to((Coord2(637.6626586914063, 753.9716186523438), Coord2(637.6622314453125, 754.0381469726563)), Coord2(637.6596069335938, 754.1043090820313)) - .curve_to((Coord2(637.6473388671875, 754.4176025390625), Coord2(637.6455078125, 754.7255859375)), Coord2(637.633544921875, 755.0328369140625)) - .curve_to((Coord2(641.7747802734375, 755.7510986328125), Coord2(648.1893310546875, 754.4743041992188)), Coord2(643.877685546875, 752.4591064453125)) - .curve_to((Coord2(643.8726196289063, 751.7140502929688), Coord2(643.8517456054688, 750.79736328125)), Coord2(643.8246459960938, 750.2509765625)) - .curve_to((Coord2(643.6224365234375, 746.2362670898438), Coord2(642.779296875, 740.5953369140625)), Coord2(642.336669921875, 737.1245727539063)) - .curve_to((Coord2(642.2940673828125, 736.7880249023438), Coord2(642.18701171875, 736.4603881835938)), Coord2(642.14453125, 736.12451171875)) - .curve_to((Coord2(640.6148071289063, 724.0070190429688), Coord2(636.8263549804688, 712.7576293945313)), Coord2(633.2051391601563, 700.6627807617188)) - .build(); + let path = + BezierPathBuilder::::start(Coord2(632.2468872070313, 700.9489135742188)) + .curve_to( + ( + Coord2(632.2468872070313, 700.9489135742188), + Coord2(632.2468872070313, 700.9489135742188), + ), + Coord2(632.2468872070313, 700.9489135742188), + ) + .curve_to( + ( + Coord2(635.8739624023438, 713.1276245117188), + Coord2(634.984375, 724.8035888671875), + ), + Coord2(636.5083618164063, 736.8372192382813), + ) + .curve_to( + ( + Coord2(636.55078125, 737.1727905273438), + Coord2(636.52880859375, 737.5175170898438), + ), + Coord2(636.5714721679688, 737.8543090820313), + ) + .curve_to( + ( + Coord2(637.0103759765625, 741.3486328125), + Coord2(637.1661376953125, 746.5886840820313), + ), + Coord2(637.3720092773438, 750.5798950195313), + ) + .curve_to( + ( + Coord2(637.3945922851563, 751.0081176757813), + Coord2(637.3880615234375, 751.6552124023438), + ), + Coord2(637.397705078125, 752.5184936523438), + ) + .curve_to( + ( + Coord2(633.1192016601563, 754.1141357421875), + Coord2(639.5689086914063, 756.6727905273438), + ), + Coord2(644.0848999023438, 755.830810546875), + ) + .curve_to( + ( + Coord2(644.1641235351563, 754.98046875), + Coord2(644.1864013671875, 754.6741333007813), + ), + Coord2(644.1986694335938, 754.3605346679688), + ) + .curve_to( + ( + Coord2(644.2012329101563, 754.29443359375), + Coord2(644.2059936523438, 754.22802734375), + ), + Coord2(644.2086181640625, 754.1612548828125), + ) + .curve_to( + ( + Coord2(644.2246704101563, 753.1409301757813), + Coord2(644.22607421875, 752.155517578125), + ), + Coord2(644.2214965820313, 751.2548217773438), + ) + .curve_to( + ( + Coord2(644.2239990234375, 748.8275756835938), + Coord2(644.20458984375, 746.9262084960938), + ), + Coord2(644.4571533203125, 744.779296875), + ) + .curve_to( + ( + Coord2(648.3836059570313, 744.2342529296875), + Coord2(643.2928466796875, 738.7249145507813), + ), + Coord2(641.3601684570313, 741.8130493164063), + ) + .curve_to( + ( + Coord2(642.6392822265625, 748.04541015625), + Coord2(643.9271240234375, 782.0206298828125), + ), + Coord2(641.6926879882813, 782.131591796875), + ) + .curve_to( + ( + Coord2(642.8214111328125, 783.0655517578125), + Coord2(645.96337890625, 783.1430053710938), + ), + Coord2(643.7222900390625, 782.1798095703125), + ) + .curve_to( + ( + Coord2(642.6692504882813, 776.1190795898438), + Coord2(643.2499389648438, 766.5458374023438), + ), + Coord2(646.3471069335938, 761.6445922851563), + ) + .curve_to( + ( + Coord2(646.038330078125, 762.0671997070313), + Coord2(646.3682250976563, 761.8047485351563), + ), + Coord2(646.708740234375, 761.5353393554688), + ) + .curve_to( + ( + Coord2(644.1248168945313, 761.16552734375), + Coord2(641.0634765625, 760.8419189453125), + ), + Coord2(643.1715087890625, 762.1446533203125), + ) + .curve_to( + ( + Coord2(643.171875, 762.147705078125), + Coord2(643.178466796875, 762.1910400390625), + ), + Coord2(643.181884765625, 762.213134765625), + ) + .curve_to( + ( + Coord2(643.8663330078125, 769.598388671875), + Coord2(642.75048828125, 789.9730834960938), + ), + Coord2(638.2301025390625, 795.2767333984375), + ) + .curve_to( + ( + Coord2(639.1027221679688, 794.624267578125), + Coord2(638.9803466796875, 794.6729736328125), + ), + Coord2(638.857666015625, 794.72216796875), + ) + .curve_to( + ( + Coord2(638.6315307617188, 794.8128051757813), + Coord2(638.40380859375, 794.9035034179688), + ), + Coord2(638.1716918945313, 794.9965209960938), + ) + .curve_to( + ( + Coord2(638.3427734375, 795.274169921875), + Coord2(644.6337280273438, 777.146728515625), + ), + Coord2(645.4585571289063, 767.8198852539063), + ) + .curve_to( + ( + Coord2(645.701171875, 766.77490234375), + Coord2(645.90869140625, 765.9208984375), + ), + Coord2(646.0075073242188, 765.452880859375), + ) + .curve_to( + ( + Coord2(646.3751220703125, 763.7075805664063), + Coord2(646.7393798828125, 761.952392578125), + ), + Coord2(647.0966186523438, 760.2139892578125), + ) + .curve_to( + ( + Coord2(647.5424194335938, 758.0451049804688), + Coord2(647.9866943359375, 755.904052734375), + ), + Coord2(648.4459838867188, 753.752197265625), + ) + .curve_to( + ( + Coord2(652.6451416015625, 750.7460327148438), + Coord2(647.7916259765625, 749.9540405273438), + ), + Coord2(644.3892822265625, 749.474365234375), + ) + .curve_to( + ( + Coord2(645.332763671875, 755.0831298828125), + Coord2(643.2569580078125, 763.2509155273438), + ), + Coord2(641.4593505859375, 771.6987915039063), + ) + .curve_to( + ( + Coord2(640.708740234375, 774.979736328125), + Coord2(639.9559936523438, 778.2863159179688), + ), + Coord2(639.5221557617188, 780.9598388671875), + ) + .curve_to( + ( + Coord2(639.490234375, 781.1691284179688), + Coord2(639.4317626953125, 781.6621704101563), + ), + Coord2(639.3948974609375, 782.05224609375), + ) + .curve_to( + ( + Coord2(637.6366577148438, 785.9545288085938), + Coord2(639.2575073242188, 789.9832763671875), + ), + Coord2(639.6422119140625, 788.78515625), + ) + .curve_to( + ( + Coord2(640.965576171875, 789.996826171875), + Coord2(644.2919921875, 791.590576171875), + ), + Coord2(642.24267578125, 789.4522705078125), + ) + .curve_to( + ( + Coord2(642.2576904296875, 789.2343139648438), + Coord2(642.2846069335938, 788.7120361328125), + ), + Coord2(642.2734375, 788.3878173828125), + ) + .curve_to( + ( + Coord2(642.06689453125, 783.2173461914063), + Coord2(642.5938110351563, 778.2110595703125), + ), + Coord2(643.05029296875, 772.7711181640625), + ) + .curve_to( + ( + Coord2(643.207275390625, 770.955078125), + Coord2(643.3928833007813, 769.0886840820313), + ), + Coord2(643.5234985351563, 767.259765625), + ) + .curve_to( + ( + Coord2(643.5761108398438, 766.5219116210938), + Coord2(643.6470947265625, 765.5867309570313), + ), + Coord2(643.7337646484375, 764.4581298828125), + ) + .curve_to( + ( + Coord2(645.1050415039063, 753.1468505859375), + Coord2(647.046630859375, 717.7682495117188), + ), + Coord2(641.641845703125, 710.2857055664063), + ) + .curve_to( + ( + Coord2(636.7783203125, 710.5094604492188), + Coord2(636.7587280273438, 710.9183349609375), + ), + Coord2(636.489990234375, 711.1563110351563), + ) + .curve_to( + ( + Coord2(635.70361328125, 711.7706298828125), + Coord2(635.5377807617188, 713.2534790039063), + ), + Coord2(635.5494995117188, 714.0062255859375), + ) + .curve_to( + ( + Coord2(635.4976806640625, 712.0926513671875), + Coord2(635.5260620117188, 713.5062866210938), + ), + Coord2(635.5267333984375, 714.4866943359375), + ) + .curve_to( + ( + Coord2(636.59814453125, 714.7744750976563), + Coord2(638.026611328125, 715.1581420898438), + ), + Coord2(639.0980224609375, 715.4459228515625), + ) + .curve_to( + ( + Coord2(639.8369750976563, 713.6776123046875), + Coord2(639.678466796875, 715.2540893554688), + ), + Coord2(639.9312133789063, 715.1055908203125), + ) + .curve_to( + ( + Coord2(639.8008422851563, 713.9517211914063), + Coord2(639.6350708007813, 715.4345703125), + ), + Coord2(639.9312133789063, 715.1055908203125), + ) + .curve_to( + ( + Coord2(640.1494750976563, 714.919189453125), + Coord2(640.8361206054688, 714.7127685546875), + ), + Coord2(641.39453125, 714.1593627929688), + ) + .curve_to( + ( + Coord2(641.607666015625, 719.16650390625), + Coord2(638.4246215820313, 750.5006713867188), + ), + Coord2(637.8893432617188, 764.0092163085938), + ) + .curve_to( + ( + Coord2(637.8035888671875, 765.1250610351563), + Coord2(637.7284545898438, 766.0917358398438), + ), + Coord2(637.6748657226563, 766.84228515625), + ) + .curve_to( + ( + Coord2(637.5464477539063, 768.6419677734375), + Coord2(637.4169921875, 770.42578125), + ), + Coord2(637.2578735351563, 772.2711181640625), + ) + .curve_to( + ( + Coord2(636.795654296875, 777.473388671875), + Coord2(636.3956909179688, 783.2040405273438), + ), + Coord2(636.60791015625, 788.612060546875), + ) + .curve_to( + ( + Coord2(636.615234375, 788.7552490234375), + Coord2(636.6175537109375, 788.81494140625), + ), + Coord2(636.6064453125, 789.2139282226563), + ) + .curve_to( + ( + Coord2(634.4679565429688, 789.1854248046875), + Coord2(637.6878662109375, 793.2979125976563), + ), + Coord2(640.5524291992188, 794.2531127929688), + ) + .curve_to( + ( + Coord2(643.1683959960938, 792.6835327148438), + Coord2(646.1170043945313, 783.748046875), + ), + Coord2(644.8746337890625, 782.613525390625), + ) + .curve_to( + ( + Coord2(644.9351196289063, 782.0530395507813), + Coord2(644.945068359375, 781.8778686523438), + ), + Coord2(644.9533081054688, 781.839111328125), + ) + .curve_to( + ( + Coord2(645.3638305664063, 779.298095703125), + Coord2(646.0396118164063, 776.3313598632813), + ), + Coord2(646.8134155273438, 772.91796875), + ) + .curve_to( + ( + Coord2(648.6615600585938, 765.3552856445313), + Coord2(650.780029296875, 755.1043701171875), + ), + Coord2(649.7860107421875, 748.6101684570313), + ) + .curve_to( + ( + Coord2(646.33837890625, 747.8474731445313), + Coord2(640.4654541015625, 748.3662719726563), + ), + Coord2(643.1162109375, 752.6146240234375), + ) + .curve_to( + ( + Coord2(642.6539916992188, 754.7799682617188), + Coord2(642.2095947265625, 756.9632568359375), + ), + Coord2(641.7667236328125, 759.11865234375), + ) + .curve_to( + ( + Coord2(641.4076538085938, 760.8657836914063), + Coord2(641.0556030273438, 762.5961303710938), + ), + Coord2(640.6897583007813, 764.3326416015625), + ) + .curve_to( + ( + Coord2(640.6021728515625, 764.7493896484375), + Coord2(640.4375610351563, 765.5008544921875), + ), + Coord2(640.1837768554688, 766.5970458984375), + ) + .curve_to( + ( + Coord2(637.48779296875, 772.45751953125), + Coord2(634.00927734375, 796.4724731445313), + ), + Coord2(637.9215087890625, 799.7769775390625), + ) + .curve_to( + ( + Coord2(640.3659057617188, 799.7997436523438), + Coord2(640.5935668945313, 799.7078857421875), + ), + Coord2(640.8196411132813, 799.6173095703125), + ) + .curve_to( + ( + Coord2(640.9423217773438, 799.568115234375), + Coord2(641.0643920898438, 799.5188598632813), + ), + Coord2(641.1866455078125, 799.4699096679688), + ) + .curve_to( + ( + Coord2(647.5555419921875, 792.8746948242188), + Coord2(650.9056396484375, 768.038818359375), + ), + Coord2(649.3676147460938, 761.2432861328125), + ) + .curve_to( + ( + Coord2(649.3682250976563, 761.2474975585938), + Coord2(649.37109375, 761.2653198242188), + ), + Coord2(649.3666381835938, 761.2356567382813), + ) + .curve_to( + ( + Coord2(651.3629150390625, 761.7766723632813), + Coord2(647.6148681640625, 756.7731323242188), + ), + Coord2(644.4575805664063, 755.9600830078125), + ) + .curve_to( + ( + Coord2(642.4801635742188, 756.8904418945313), + Coord2(642.14892578125, 757.1510620117188), + ), + Coord2(641.8184204101563, 757.4124755859375), + ) + .curve_to( + ( + Coord2(637.2826538085938, 763.3823852539063), + Coord2(635.7777709960938, 778.0116577148438), + ), + Coord2(637.630126953125, 783.6883544921875), + ) + .curve_to( + ( + Coord2(635.5146484375, 783.2322387695313), + Coord2(639.78515625, 787.8326416015625), + ), + Coord2(643.0133056640625, 788.3185424804688), + ) + .curve_to( + ( + Coord2(649.2066650390625, 786.63037109375), + Coord2(652.2764892578125, 743.0693359375), + ), + Coord2(647.0385131835938, 738.4141235351563), + ) + .curve_to( + ( + Coord2(641.272216796875, 735.0977172851563), + Coord2(634.1425170898438, 743.2423095703125), + ), + Coord2(637.9150390625, 744.0158081054688), + ) + .curve_to( + ( + Coord2(637.6356201171875, 746.4281005859375), + Coord2(637.637939453125, 749.1239013671875), + ), + Coord2(637.6622314453125, 751.2858276367188), + ) + .curve_to( + ( + Coord2(637.6663208007813, 752.229248046875), + Coord2(637.6764526367188, 753.086669921875), + ), + Coord2(637.6630859375, 753.984619140625), + ) + .curve_to( + ( + Coord2(637.6626586914063, 753.9716186523438), + Coord2(637.6622314453125, 754.0381469726563), + ), + Coord2(637.6596069335938, 754.1043090820313), + ) + .curve_to( + ( + Coord2(637.6473388671875, 754.4176025390625), + Coord2(637.6455078125, 754.7255859375), + ), + Coord2(637.633544921875, 755.0328369140625), + ) + .curve_to( + ( + Coord2(641.7747802734375, 755.7510986328125), + Coord2(648.1893310546875, 754.4743041992188), + ), + Coord2(643.877685546875, 752.4591064453125), + ) + .curve_to( + ( + Coord2(643.8726196289063, 751.7140502929688), + Coord2(643.8517456054688, 750.79736328125), + ), + Coord2(643.8246459960938, 750.2509765625), + ) + .curve_to( + ( + Coord2(643.6224365234375, 746.2362670898438), + Coord2(642.779296875, 740.5953369140625), + ), + Coord2(642.336669921875, 737.1245727539063), + ) + .curve_to( + ( + Coord2(642.2940673828125, 736.7880249023438), + Coord2(642.18701171875, 736.4603881835938), + ), + Coord2(642.14453125, 736.12451171875), + ) + .curve_to( + ( + Coord2(640.6148071289063, 724.0070190429688), + Coord2(636.8263549804688, 712.7576293945313), + ), + Coord2(633.2051391601563, 700.6627807617188), + ) + .build(); let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); assert!(removed.len() != 0); @@ -532,67 +2665,364 @@ fn remove_interior_points_3() { // Must always be a following edge that's an exterior one for edge in graph_path.all_edges() { - let end_point_idx = edge.end_point_index(); - let edge_ref = edge.into(); + let end_point_idx = edge.end_point_index(); + let edge_ref = edge.into(); if graph_path.edge_kind(edge_ref) == GraphPathEdgeKind::Exterior { - assert!(graph_path.edge_refs_for_point(end_point_idx).any(|edge| graph_path.edge_kind(edge) == GraphPathEdgeKind::Exterior)); + assert!(graph_path + .edge_refs_for_point(end_point_idx) + .any(|edge| graph_path.edge_kind(edge) == GraphPathEdgeKind::Exterior)); } } } #[test] fn remove_interior_points_4() { - let path = BezierPathBuilder::::start(Coord2(579.952392578125, 848.2322998046875)) - .curve_to((Coord2(580.9503784179688, 847.3311157226563), Coord2(579.8958129882813, 847.4636840820313)), Coord2(579.7269897460938, 847.880615234375)) - .curve_to((Coord2(579.593505859375, 848.30517578125), Coord2(579.1264038085938, 849.0411376953125)), Coord2(579.0646362304688, 849.64892578125)) - .curve_to((Coord2(577.8814086914063, 850.4049072265625), Coord2(578.8155517578125, 853.5064697265625)), Coord2(579.9620971679688, 852.8971557617188)) - .curve_to((Coord2(580.7517700195313, 852.6128540039063), Coord2(580.5936889648438, 852.1756591796875)), Coord2(579.82666015625, 852.1787719726563)) - .curve_to((Coord2(578.5782470703125, 852.3163452148438), Coord2(578.22900390625, 859.4969482421875)), Coord2(578.1416625976563, 860.2805786132813)) - .curve_to((Coord2(579.2769775390625, 861.6010131835938), Coord2(581.9407348632813, 857.04541015625)), Coord2(580.010986328125, 854.8056640625)) - .curve_to((Coord2(582.6585083007813, 854.671875), Coord2(578.6830444335938, 854.4993896484375)), Coord2(576.0272827148438, 854.4729614257813)) - .curve_to((Coord2(575.8976440429688, 855.9894409179688), Coord2(576.928466796875, 870.5877685546875)), Coord2(577.4629516601563, 873.9253540039063)) - .curve_to((Coord2(579.9541015625, 874.6844482421875), Coord2(583.33642578125, 873.74853515625)), Coord2(580.8639526367188, 873.3152465820313)) - .curve_to((Coord2(582.581298828125, 871.1629028320313), Coord2(578.4156494140625, 866.2734375)), Coord2(576.1420288085938, 867.1937866210938)) - .curve_to((Coord2(575.8514404296875, 868.4409790039063), Coord2(581.5850219726563, 885.1395263671875)), Coord2(581.817138671875, 885.0364990234375)) - .curve_to((Coord2(585.2017211914063, 884.2693481445313), Coord2(580.9660034179688, 873.0250854492188)), Coord2(577.1375732421875, 874.58740234375)) - .curve_to((Coord2(576.8878173828125, 875.6769409179688), Coord2(584.0165405273438, 892.134521484375)), Coord2(585.7166137695313, 893.5682373046875)) - .curve_to((Coord2(586.0726928710938, 894.0137329101563), Coord2(586.338134765625, 894.3269653320313)), Coord2(586.606201171875, 894.654541015625)) - .curve_to((Coord2(590.0245361328125, 893.7957763671875), Coord2(583.5659790039063, 877.0528564453125)), Coord2(581.8451538085938, 878.669921875)) - .curve_to((Coord2(579.1322021484375, 880.3388671875), Coord2(579.1969604492188, 880.3540649414063)), Coord2(579.22119140625, 880.3956909179688)) - .curve_to((Coord2(579.2321166992188, 880.4144287109375), Coord2(579.2623291015625, 880.4225463867188)), Coord2(579.2734985351563, 880.4417114257813)) - .curve_to((Coord2(581.0283203125, 883.2745971679688), Coord2(584.3287353515625, 887.9052124023438)), Coord2(586.827392578125, 891.2733154296875)) - .curve_to((Coord2(588.0198974609375, 892.8840942382813), Coord2(588.9678955078125, 894.2404174804688)), Coord2(589.553466796875, 895.0764770507813)) - .curve_to((Coord2(591.9968872070313, 894.783935546875), Coord2(591.6580810546875, 890.1458740234375)), Coord2(587.6173706054688, 889.505859375)) - .curve_to((Coord2(586.9524536132813, 886.0717163085938), Coord2(582.7312622070313, 886.6657104492188)), Coord2(582.2239990234375, 889.3804931640625)) - .curve_to((Coord2(583.8734130859375, 891.468994140625), Coord2(595.0052490234375, 898.9844360351563)), Coord2(598.0443115234375, 899.9608764648438)) - .curve_to((Coord2(600.0397338867188, 901.0479736328125), Coord2(601.4949951171875, 899.474853515625)), Coord2(601.6517333984375, 898.8914794921875)) - .curve_to((Coord2(601.4926147460938, 898.7960815429688), Coord2(601.6734008789063, 898.8751220703125)), Coord2(601.414794921875, 898.8958129882813)) - .curve_to((Coord2(601.1598022460937, 898.61005859375), Coord2(600.8198120117188, 898.229052734375)), Coord2(600.5648193359375, 897.9432983398438)) - .curve_to((Coord2(600.4238891601563, 898.099365234375), Coord2(600.2045288085938, 898.0982055664063)), Coord2(600.1848754882813, 898.165283203125)) - .curve_to((Coord2(600.1288452148438, 898.01171875), Coord2(601.2973022460938, 897.0179443359375)), Coord2(599.201171875, 896.6677856445313)) - .curve_to((Coord2(596.1993408203125, 895.5221557617188), Coord2(586.39306640625, 889.2333984375)), Coord2(584.7064208984375, 887.3141479492188)) - .curve_to((Coord2(582.2571411132813, 887.6959838867188), Coord2(581.8307495117188, 891.6290283203125)), Coord2(585.4521484375, 891.966552734375)) - .curve_to((Coord2(586.3348999023438, 895.6588745117188), Coord2(591.5681762695313, 895.923828125)), Coord2(592.2828369140625, 893.1641235351563)) - .curve_to((Coord2(591.6741943359375, 892.2957153320313), Coord2(590.6009521484375, 890.9229736328125)), Coord2(589.4315185546875, 889.3446044921875)) - .curve_to((Coord2(586.8740234375, 885.8859252929688), Coord2(583.55712890625, 881.581787109375)), Coord2(581.8828125, 878.87548828125)) - .curve_to((Coord2(581.8934326171875, 878.8925170898438), Coord2(581.901611328125, 878.8626708984375)), Coord2(581.890625, 878.8438110351563)) - .curve_to((Coord2(581.8668823242188, 878.802978515625), Coord2(581.8843994140625, 878.73681640625)), Coord2(581.8605346679688, 878.69580078125)) - .curve_to((Coord2(577.6611938476563, 882.36572265625), Coord2(588.2183837890625, 893.8323974609375)), Coord2(588.7944946289063, 891.6168823242188)) - .curve_to((Coord2(589.3372802734375, 891.8733520507813), Coord2(589.0824584960938, 891.5507202148438)), Coord2(588.8222045898438, 891.2327270507813)) - .curve_to((Coord2(587.4506225585938, 888.200927734375), Coord2(579.8250732421875, 875.5438842773438)), Coord2(579.8422241210938, 876.18017578125)) - .curve_to((Coord2(576.9286499023438, 876.1892700195313), Coord2(580.5906372070313, 884.2669067382813)), Coord2(583.94775390625, 883.4625854492188)) - .curve_to((Coord2(583.9148559570313, 883.0008544921875), Coord2(579.216064453125, 868.5027465820313)), Coord2(579.3073120117188, 867.8202514648438)) - .curve_to((Coord2(577.37939453125, 866.9944458007813), Coord2(574.9931030273438, 871.8741455078125)), Coord2(577.4772338867188, 873.9320678710938)) - .curve_to((Coord2(575.3856201171875, 875.5897216796875), Coord2(578.8990478515625, 875.3737182617188)), Coord2(580.9719848632813, 873.3901977539063)) - .curve_to((Coord2(580.464111328125, 869.892578125), Coord2(579.9021606445313, 856.1516723632813)), Coord2(580.0052490234375, 854.7948608398438)) - .curve_to((Coord2(577.3517456054688, 854.7406005859375), Coord2(573.3838500976563, 854.7532958984375)), Coord2(576.0455932617188, 854.9674072265625)) - .curve_to((Coord2(574.2247924804688, 855.3988037109375), Coord2(577.3884887695313, 863.1025390625)), Coord2(580.003662109375, 863.5904541015625)) - .curve_to((Coord2(583.629150390625, 862.2855224609375), Coord2(581.4857177734375, 849.1261596679688)), Coord2(579.1959838867188, 849.5098266601563)) - .curve_to((Coord2(577.6004028320313, 849.708740234375), Coord2(577.3341674804688, 855.0685424804688)), Coord2(578.9075927734375, 855.1510620117188)) - .curve_to((Coord2(580.097900390625, 854.562255859375), Coord2(582.2202758789063, 849.3984375)), Coord2(581.0670776367188, 849.8407592773438)) - .curve_to((Coord2(581.1075439453125, 849.38232421875), Coord2(581.0016479492188, 848.8671264648438)), Coord2(581.1563720703125, 848.2933349609375)) - .curve_to((Coord2(581.5310668945313, 846.8276977539063), Coord2(580.760498046875, 845.9767456054688)), Coord2(579.556640625, 847.9266357421875)) - .build(); + let path = + BezierPathBuilder::::start(Coord2(579.952392578125, 848.2322998046875)) + .curve_to( + ( + Coord2(580.9503784179688, 847.3311157226563), + Coord2(579.8958129882813, 847.4636840820313), + ), + Coord2(579.7269897460938, 847.880615234375), + ) + .curve_to( + ( + Coord2(579.593505859375, 848.30517578125), + Coord2(579.1264038085938, 849.0411376953125), + ), + Coord2(579.0646362304688, 849.64892578125), + ) + .curve_to( + ( + Coord2(577.8814086914063, 850.4049072265625), + Coord2(578.8155517578125, 853.5064697265625), + ), + Coord2(579.9620971679688, 852.8971557617188), + ) + .curve_to( + ( + Coord2(580.7517700195313, 852.6128540039063), + Coord2(580.5936889648438, 852.1756591796875), + ), + Coord2(579.82666015625, 852.1787719726563), + ) + .curve_to( + ( + Coord2(578.5782470703125, 852.3163452148438), + Coord2(578.22900390625, 859.4969482421875), + ), + Coord2(578.1416625976563, 860.2805786132813), + ) + .curve_to( + ( + Coord2(579.2769775390625, 861.6010131835938), + Coord2(581.9407348632813, 857.04541015625), + ), + Coord2(580.010986328125, 854.8056640625), + ) + .curve_to( + ( + Coord2(582.6585083007813, 854.671875), + Coord2(578.6830444335938, 854.4993896484375), + ), + Coord2(576.0272827148438, 854.4729614257813), + ) + .curve_to( + ( + Coord2(575.8976440429688, 855.9894409179688), + Coord2(576.928466796875, 870.5877685546875), + ), + Coord2(577.4629516601563, 873.9253540039063), + ) + .curve_to( + ( + Coord2(579.9541015625, 874.6844482421875), + Coord2(583.33642578125, 873.74853515625), + ), + Coord2(580.8639526367188, 873.3152465820313), + ) + .curve_to( + ( + Coord2(582.581298828125, 871.1629028320313), + Coord2(578.4156494140625, 866.2734375), + ), + Coord2(576.1420288085938, 867.1937866210938), + ) + .curve_to( + ( + Coord2(575.8514404296875, 868.4409790039063), + Coord2(581.5850219726563, 885.1395263671875), + ), + Coord2(581.817138671875, 885.0364990234375), + ) + .curve_to( + ( + Coord2(585.2017211914063, 884.2693481445313), + Coord2(580.9660034179688, 873.0250854492188), + ), + Coord2(577.1375732421875, 874.58740234375), + ) + .curve_to( + ( + Coord2(576.8878173828125, 875.6769409179688), + Coord2(584.0165405273438, 892.134521484375), + ), + Coord2(585.7166137695313, 893.5682373046875), + ) + .curve_to( + ( + Coord2(586.0726928710938, 894.0137329101563), + Coord2(586.338134765625, 894.3269653320313), + ), + Coord2(586.606201171875, 894.654541015625), + ) + .curve_to( + ( + Coord2(590.0245361328125, 893.7957763671875), + Coord2(583.5659790039063, 877.0528564453125), + ), + Coord2(581.8451538085938, 878.669921875), + ) + .curve_to( + ( + Coord2(579.1322021484375, 880.3388671875), + Coord2(579.1969604492188, 880.3540649414063), + ), + Coord2(579.22119140625, 880.3956909179688), + ) + .curve_to( + ( + Coord2(579.2321166992188, 880.4144287109375), + Coord2(579.2623291015625, 880.4225463867188), + ), + Coord2(579.2734985351563, 880.4417114257813), + ) + .curve_to( + ( + Coord2(581.0283203125, 883.2745971679688), + Coord2(584.3287353515625, 887.9052124023438), + ), + Coord2(586.827392578125, 891.2733154296875), + ) + .curve_to( + ( + Coord2(588.0198974609375, 892.8840942382813), + Coord2(588.9678955078125, 894.2404174804688), + ), + Coord2(589.553466796875, 895.0764770507813), + ) + .curve_to( + ( + Coord2(591.9968872070313, 894.783935546875), + Coord2(591.6580810546875, 890.1458740234375), + ), + Coord2(587.6173706054688, 889.505859375), + ) + .curve_to( + ( + Coord2(586.9524536132813, 886.0717163085938), + Coord2(582.7312622070313, 886.6657104492188), + ), + Coord2(582.2239990234375, 889.3804931640625), + ) + .curve_to( + ( + Coord2(583.8734130859375, 891.468994140625), + Coord2(595.0052490234375, 898.9844360351563), + ), + Coord2(598.0443115234375, 899.9608764648438), + ) + .curve_to( + ( + Coord2(600.0397338867188, 901.0479736328125), + Coord2(601.4949951171875, 899.474853515625), + ), + Coord2(601.6517333984375, 898.8914794921875), + ) + .curve_to( + ( + Coord2(601.4926147460938, 898.7960815429688), + Coord2(601.6734008789063, 898.8751220703125), + ), + Coord2(601.414794921875, 898.8958129882813), + ) + .curve_to( + ( + Coord2(601.1598022460937, 898.61005859375), + Coord2(600.8198120117188, 898.229052734375), + ), + Coord2(600.5648193359375, 897.9432983398438), + ) + .curve_to( + ( + Coord2(600.4238891601563, 898.099365234375), + Coord2(600.2045288085938, 898.0982055664063), + ), + Coord2(600.1848754882813, 898.165283203125), + ) + .curve_to( + ( + Coord2(600.1288452148438, 898.01171875), + Coord2(601.2973022460938, 897.0179443359375), + ), + Coord2(599.201171875, 896.6677856445313), + ) + .curve_to( + ( + Coord2(596.1993408203125, 895.5221557617188), + Coord2(586.39306640625, 889.2333984375), + ), + Coord2(584.7064208984375, 887.3141479492188), + ) + .curve_to( + ( + Coord2(582.2571411132813, 887.6959838867188), + Coord2(581.8307495117188, 891.6290283203125), + ), + Coord2(585.4521484375, 891.966552734375), + ) + .curve_to( + ( + Coord2(586.3348999023438, 895.6588745117188), + Coord2(591.5681762695313, 895.923828125), + ), + Coord2(592.2828369140625, 893.1641235351563), + ) + .curve_to( + ( + Coord2(591.6741943359375, 892.2957153320313), + Coord2(590.6009521484375, 890.9229736328125), + ), + Coord2(589.4315185546875, 889.3446044921875), + ) + .curve_to( + ( + Coord2(586.8740234375, 885.8859252929688), + Coord2(583.55712890625, 881.581787109375), + ), + Coord2(581.8828125, 878.87548828125), + ) + .curve_to( + ( + Coord2(581.8934326171875, 878.8925170898438), + Coord2(581.901611328125, 878.8626708984375), + ), + Coord2(581.890625, 878.8438110351563), + ) + .curve_to( + ( + Coord2(581.8668823242188, 878.802978515625), + Coord2(581.8843994140625, 878.73681640625), + ), + Coord2(581.8605346679688, 878.69580078125), + ) + .curve_to( + ( + Coord2(577.6611938476563, 882.36572265625), + Coord2(588.2183837890625, 893.8323974609375), + ), + Coord2(588.7944946289063, 891.6168823242188), + ) + .curve_to( + ( + Coord2(589.3372802734375, 891.8733520507813), + Coord2(589.0824584960938, 891.5507202148438), + ), + Coord2(588.8222045898438, 891.2327270507813), + ) + .curve_to( + ( + Coord2(587.4506225585938, 888.200927734375), + Coord2(579.8250732421875, 875.5438842773438), + ), + Coord2(579.8422241210938, 876.18017578125), + ) + .curve_to( + ( + Coord2(576.9286499023438, 876.1892700195313), + Coord2(580.5906372070313, 884.2669067382813), + ), + Coord2(583.94775390625, 883.4625854492188), + ) + .curve_to( + ( + Coord2(583.9148559570313, 883.0008544921875), + Coord2(579.216064453125, 868.5027465820313), + ), + Coord2(579.3073120117188, 867.8202514648438), + ) + .curve_to( + ( + Coord2(577.37939453125, 866.9944458007813), + Coord2(574.9931030273438, 871.8741455078125), + ), + Coord2(577.4772338867188, 873.9320678710938), + ) + .curve_to( + ( + Coord2(575.3856201171875, 875.5897216796875), + Coord2(578.8990478515625, 875.3737182617188), + ), + Coord2(580.9719848632813, 873.3901977539063), + ) + .curve_to( + ( + Coord2(580.464111328125, 869.892578125), + Coord2(579.9021606445313, 856.1516723632813), + ), + Coord2(580.0052490234375, 854.7948608398438), + ) + .curve_to( + ( + Coord2(577.3517456054688, 854.7406005859375), + Coord2(573.3838500976563, 854.7532958984375), + ), + Coord2(576.0455932617188, 854.9674072265625), + ) + .curve_to( + ( + Coord2(574.2247924804688, 855.3988037109375), + Coord2(577.3884887695313, 863.1025390625), + ), + Coord2(580.003662109375, 863.5904541015625), + ) + .curve_to( + ( + Coord2(583.629150390625, 862.2855224609375), + Coord2(581.4857177734375, 849.1261596679688), + ), + Coord2(579.1959838867188, 849.5098266601563), + ) + .curve_to( + ( + Coord2(577.6004028320313, 849.708740234375), + Coord2(577.3341674804688, 855.0685424804688), + ), + Coord2(578.9075927734375, 855.1510620117188), + ) + .curve_to( + ( + Coord2(580.097900390625, 854.562255859375), + Coord2(582.2202758789063, 849.3984375), + ), + Coord2(581.0670776367188, 849.8407592773438), + ) + .curve_to( + ( + Coord2(581.1075439453125, 849.38232421875), + Coord2(581.0016479492188, 848.8671264648438), + ), + Coord2(581.1563720703125, 848.2933349609375), + ) + .curve_to( + ( + Coord2(581.5310668945313, 846.8276977539063), + Coord2(580.760498046875, 845.9767456054688), + ), + Coord2(579.556640625, 847.9266357421875), + ) + .build(); // Fails an internal assertion (cannot determine if a line is in or outside) let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); diff --git a/tests/bezier/path/arithmetic_cut.rs b/tests/bezier/path/arithmetic_cut.rs index 018968dc..6260a998 100644 --- a/tests/bezier/path/arithmetic_cut.rs +++ b/tests/bezier/path/arithmetic_cut.rs @@ -1,23 +1,23 @@ -use flo_curves::*; use flo_curves::bezier::path::*; +use flo_curves::*; #[test] fn cut_square() { - let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) + let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) .line_to(Coord2(10.0, 5.0)) .line_to(Coord2(10.0, 10.0)) .line_to(Coord2(5.0, 10.0)) .line_to(Coord2(5.0, 5.0)) .build(); - let square_2 = BezierPathBuilder::::start(Coord2(7.5, 7.5)) + let square_2 = BezierPathBuilder::::start(Coord2(7.5, 7.5)) .line_to(Coord2(15.0, 7.5)) .line_to(Coord2(15.0, 15.0)) .line_to(Coord2(7.5, 15.0)) .line_to(Coord2(7.5, 7.5)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); assert!(cut_square.exterior_path.len() == 1); assert!(cut_square.interior_path.len() == 1); @@ -28,21 +28,21 @@ fn cut_square() { #[test] fn cut_square_entirely_interior() { - let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) + let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) .line_to(Coord2(10.0, 5.0)) .line_to(Coord2(10.0, 10.0)) .line_to(Coord2(5.0, 10.0)) .line_to(Coord2(5.0, 5.0)) .build(); - let square_2 = BezierPathBuilder::::start(Coord2(2.0, 2.0)) + let square_2 = BezierPathBuilder::::start(Coord2(2.0, 2.0)) .line_to(Coord2(15.0, 2.0)) .line_to(Coord2(15.0, 15.0)) .line_to(Coord2(2.0, 15.0)) .line_to(Coord2(2.0, 2.0)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); assert!(cut_square.exterior_path.len() == 0); assert!(cut_square.interior_path.len() == 1); @@ -50,24 +50,23 @@ fn cut_square_entirely_interior() { assert!(cut_square.interior_path[0].points().len() == 4); } - #[test] fn cut_square_entirely_exterior() { - let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) + let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) .line_to(Coord2(10.0, 5.0)) .line_to(Coord2(10.0, 10.0)) .line_to(Coord2(5.0, 10.0)) .line_to(Coord2(5.0, 5.0)) .build(); - let square_2 = BezierPathBuilder::::start(Coord2(20.0, 20.0)) + let square_2 = BezierPathBuilder::::start(Coord2(20.0, 20.0)) .line_to(Coord2(15.0, 20.0)) .line_to(Coord2(15.0, 15.0)) .line_to(Coord2(20.0, 15.0)) .line_to(Coord2(20.0, 20.0)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); assert!(cut_square.exterior_path.len() == 1); assert!(cut_square.interior_path.len() == 0); @@ -77,21 +76,21 @@ fn cut_square_entirely_exterior() { #[test] fn cut_square_center() { - let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) + let square_1 = BezierPathBuilder::::start(Coord2(5.0, 5.0)) .line_to(Coord2(10.0, 5.0)) .line_to(Coord2(10.0, 10.0)) .line_to(Coord2(5.0, 10.0)) .line_to(Coord2(5.0, 5.0)) .build(); - let square_2 = BezierPathBuilder::::start(Coord2(6.0, 6.0)) + let square_2 = BezierPathBuilder::::start(Coord2(6.0, 6.0)) .line_to(Coord2(9.0, 6.0)) .line_to(Coord2(9.0, 9.0)) .line_to(Coord2(6.0, 9.0)) .line_to(Coord2(6.0, 6.0)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); assert!(cut_square.exterior_path.len() == 2); assert!(cut_square.interior_path.len() == 1); diff --git a/tests/bezier/path/arithmetic_intersect.rs b/tests/bezier/path/arithmetic_intersect.rs index 50060397..6420c37f 100644 --- a/tests/bezier/path/arithmetic_intersect.rs +++ b/tests/bezier/path/arithmetic_intersect.rs @@ -1,8 +1,8 @@ -use flo_curves::*; use flo_curves::arc::*; -use flo_curves::line::*; -use flo_curves::bezier::*; use flo_curves::bezier::path::*; +use flo_curves::bezier::*; +use flo_curves::line::*; +use flo_curves::*; use std::f64; use std::iter; @@ -10,13 +10,17 @@ use std::iter; #[test] fn intersect_two_doughnuts() { // Two overlapping circles - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); // Combine them - let combined_circles = path_intersect::<_, _, SimpleBezierPath>(&vec![circle1, inner_circle1], &vec![circle2, inner_circle2], 0.1); + let combined_circles = path_intersect::<_, _, SimpleBezierPath>( + &vec![circle1, inner_circle1], + &vec![circle2, inner_circle2], + 0.1, + ); println!("{:?}", combined_circles.len()); println!("{:?}", combined_circles); @@ -26,13 +30,17 @@ fn intersect_two_doughnuts() { #[test] fn full_intersect_two_doughnuts() { // Two overlapping circles - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); // Combine them - let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1, inner_circle1], &vec![circle2, inner_circle2], 0.1); + let intersection = path_full_intersect::<_, _, SimpleBezierPath>( + &vec![circle1, inner_circle1], + &vec![circle2, inner_circle2], + 0.1, + ); let combined_circles = &intersection.intersecting_path; println!("{:?}", combined_circles.len()); @@ -42,10 +50,11 @@ fn full_intersect_two_doughnuts() { #[test] fn full_intersect_two_partially_overlapping_circles() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); - let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let intersection = + path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 1); @@ -54,10 +63,11 @@ fn full_intersect_two_partially_overlapping_circles() { #[test] fn full_intersect_two_non_overlapping_circles() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let circle2 = Circle::new(Coord2(15.0, 5.0), 4.0).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle2 = Circle::new(Coord2(15.0, 5.0), 4.0).to_path::(); - let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let intersection = + path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); assert!(intersection.intersecting_path.len() == 0); assert!(intersection.exterior_paths[0].len() == 1); @@ -66,10 +76,11 @@ fn full_intersect_two_non_overlapping_circles() { #[test] fn full_intersect_interior_circles_1() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let circle2 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle2 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); - let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let intersection = + path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 2); @@ -78,10 +89,11 @@ fn full_intersect_interior_circles_1() { #[test] fn full_intersect_interior_circles_2() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); - let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); + let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let intersection = + path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 0); @@ -90,20 +102,22 @@ fn full_intersect_interior_circles_2() { #[test] fn fintersect_two_fully_overlapping_circles() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let intersection = path_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let intersection = + path_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); assert!(intersection.len() == 1); } #[test] fn full_intersect_two_fully_overlapping_circles() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let intersection = + path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); println!("{:?}", intersection); @@ -115,26 +129,38 @@ fn full_intersect_two_fully_overlapping_circles() { #[test] fn repeatedly_full_intersect_circle() { // Start with a circle - let circle = Circle::new(Coord2(500.0, 500.0), 116.0).to_path::(); + let circle = Circle::new(Coord2(500.0, 500.0), 116.0).to_path::(); // Cut 16 triangular slices from it - let mut remaining = vec![circle]; - let mut slices = vec![]; + let mut remaining = vec![circle]; + let mut slices = vec![]; for slice_idx in 0..16 { // Angle in radians of this slice - let middle_angle = f64::consts::PI*2.0 / 16.0 * (slice_idx as f64); - let start_angle = middle_angle - (f64::consts::PI*2.0 / 32.0); - let end_angle = middle_angle + (f64::consts::PI*2.0 / 32.0); + let middle_angle = f64::consts::PI * 2.0 / 16.0 * (slice_idx as f64); + let start_angle = middle_angle - (f64::consts::PI * 2.0 / 32.0); + let end_angle = middle_angle + (f64::consts::PI * 2.0 / 32.0); // Create a triangle slice - let (center_x, center_y) = (500.0, 500.0); - let (x1, y1) = (center_x + (f64::sin(start_angle) * 300.0), center_y + (f64::cos(start_angle) * 300.0)); - let (x2, y2) = (center_x + (f64::sin(end_angle) * 300.0), center_y + (f64::cos(end_angle) * 300.0)); - let (x3, y3) = (center_x + (f64::sin(start_angle) * 16.0), center_y + (f64::cos(start_angle) * 16.0)); - let (x4, y4) = (center_x + (f64::sin(end_angle) * 16.0), center_y + (f64::cos(end_angle) * 16.0)); - - let fragment = BezierPathBuilder::::start(Coord2(x3, y3)) + let (center_x, center_y) = (500.0, 500.0); + let (x1, y1) = ( + center_x + (f64::sin(start_angle) * 300.0), + center_y + (f64::cos(start_angle) * 300.0), + ); + let (x2, y2) = ( + center_x + (f64::sin(end_angle) * 300.0), + center_y + (f64::cos(end_angle) * 300.0), + ); + let (x3, y3) = ( + center_x + (f64::sin(start_angle) * 16.0), + center_y + (f64::cos(start_angle) * 16.0), + ); + let (x4, y4) = ( + center_x + (f64::sin(end_angle) * 16.0), + center_y + (f64::cos(end_angle) * 16.0), + ); + + let fragment = BezierPathBuilder::::start(Coord2(x3, y3)) .line_to(Coord2(x1, y1)) .line_to(Coord2(x2, y2)) .line_to(Coord2(x4, y4)) @@ -142,7 +168,8 @@ fn repeatedly_full_intersect_circle() { .build(); // Cut the circle via the fragment - let cut_circle = path_full_intersect::<_, _, SimpleBezierPath>(&vec![fragment], &remaining, 0.01); + let cut_circle = + path_full_intersect::<_, _, SimpleBezierPath>(&vec![fragment], &remaining, 0.01); // Add the slice and the remaining part of the circle slices.push(cut_circle.intersecting_path); @@ -154,13 +181,15 @@ fn repeatedly_full_intersect_circle() { assert!(circle_fragment.len() == 1); let start_point = circle_fragment[0].start_point(); - let points = circle_fragment[0].points().map(|(_, _, p)| p); - let all_points = iter::once(start_point).chain(points); + let points = circle_fragment[0].points().map(|(_, _, p)| p); + let all_points = iter::once(start_point).chain(points); for circle_point in all_points { let distance_to_center = circle_point.distance_to(&Coord2(500.0, 500.0)); println!("{:?}", distance_to_center); - assert!((distance_to_center-16.0).abs() < 0.1 || (distance_to_center-116.0).abs() < 1.0); + assert!( + (distance_to_center - 16.0).abs() < 0.1 || (distance_to_center - 116.0).abs() < 1.0 + ); } } @@ -168,26 +197,39 @@ fn repeatedly_full_intersect_circle() { assert!(remaining.len() == 1); let start_point = remaining[0].start_point(); - let points = remaining[0].points().map(|(_, _, p)| p); - let all_points = iter::once(start_point).chain(points); + let points = remaining[0].points().map(|(_, _, p)| p); + let all_points = iter::once(start_point).chain(points); for circle_point in all_points { let distance_to_center = circle_point.distance_to(&Coord2(500.0, 500.0)); println!("{:?}", distance_to_center); - assert!((distance_to_center-0.0).abs() < 0.1 || (distance_to_center-16.0).abs() < 1.0); + assert!((distance_to_center - 0.0).abs() < 0.1 || (distance_to_center - 16.0).abs() < 1.0); } } -fn convert_path_to_f32_and_back((start_point, remaining_points): SimpleBezierPath) -> SimpleBezierPath { - let start_f32 = (start_point.x() as f32, start_point.y() as f32); - let remaining_f32 = remaining_points.into_iter().map(|(cp1, cp2, p)| { - ((cp1.x() as f32, cp1.y() as f32), (cp2.x() as f32, cp2.y() as f32), (p.x() as f32, p.y() as f32)) +fn convert_path_to_f32_and_back( + (start_point, remaining_points): SimpleBezierPath, +) -> SimpleBezierPath { + let start_f32 = (start_point.x() as f32, start_point.y() as f32); + let remaining_f32 = remaining_points.into_iter().map(|(cp1, cp2, p)| { + ( + (cp1.x() as f32, cp1.y() as f32), + (cp2.x() as f32, cp2.y() as f32), + (p.x() as f32, p.y() as f32), + ) }); let path_points = remaining_f32.map(|(cp1, cp2, p)| { - (Coord2(cp1.0 as f64, cp1.1 as f64), Coord2(cp2.0 as f64, cp2.1 as f64), Coord2(p.0 as f64, p.1 as f64)) + ( + Coord2(cp1.0 as f64, cp1.1 as f64), + Coord2(cp2.0 as f64, cp2.1 as f64), + Coord2(p.0 as f64, p.1 as f64), + ) }); - (Coord2(start_f32.0 as _, start_f32.1 as _), path_points.collect()) + ( + Coord2(start_f32.0 as _, start_f32.1 as _), + path_points.collect(), + ) } #[test] @@ -195,26 +237,38 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { // Converting the remaining curve to f32 and back again results in a failed intersection due to slightly different line positions, which causes the cut to fail on some slices // Start with a circle - let circle = Circle::new(Coord2(500.0, 500.0), 116.0).to_path::(); + let circle = Circle::new(Coord2(500.0, 500.0), 116.0).to_path::(); // Cut 16 triangular slices from it - let mut remaining = vec![circle]; - let mut slices = vec![]; + let mut remaining = vec![circle]; + let mut slices = vec![]; for slice_idx in 0..16 { // Angle in radians of this slice - let middle_angle = f64::consts::PI*2.0 / 16.0 * (slice_idx as f64); - let start_angle = middle_angle - (f64::consts::PI*2.0 / 32.0); - let end_angle = middle_angle + (f64::consts::PI*2.0 / 32.0); + let middle_angle = f64::consts::PI * 2.0 / 16.0 * (slice_idx as f64); + let start_angle = middle_angle - (f64::consts::PI * 2.0 / 32.0); + let end_angle = middle_angle + (f64::consts::PI * 2.0 / 32.0); // Create a triangle slice - let (center_x, center_y) = (500.0, 500.0); - let (x1, y1) = (center_x + (f64::sin(start_angle) * 300.0), center_y + (f64::cos(start_angle) * 300.0)); - let (x2, y2) = (center_x + (f64::sin(end_angle) * 300.0), center_y + (f64::cos(end_angle) * 300.0)); - let (x3, y3) = (center_x + (f64::sin(start_angle) * 16.0), center_y + (f64::cos(start_angle) * 16.0)); - let (x4, y4) = (center_x + (f64::sin(end_angle) * 16.0), center_y + (f64::cos(end_angle) * 16.0)); - - let fragment = BezierPathBuilder::::start(Coord2(x3, y3)) + let (center_x, center_y) = (500.0, 500.0); + let (x1, y1) = ( + center_x + (f64::sin(start_angle) * 300.0), + center_y + (f64::cos(start_angle) * 300.0), + ); + let (x2, y2) = ( + center_x + (f64::sin(end_angle) * 300.0), + center_y + (f64::cos(end_angle) * 300.0), + ); + let (x3, y3) = ( + center_x + (f64::sin(start_angle) * 16.0), + center_y + (f64::cos(start_angle) * 16.0), + ); + let (x4, y4) = ( + center_x + (f64::sin(end_angle) * 16.0), + center_y + (f64::cos(end_angle) * 16.0), + ); + + let fragment = BezierPathBuilder::::start(Coord2(x3, y3)) .line_to(Coord2(x1, y1)) .line_to(Coord2(x2, y2)) .line_to(Coord2(x4, y4)) @@ -222,31 +276,39 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { .build(); // The edges (x3, y3) -> (x1, y1) and (x2, y2) -> (x4, y4) should both collide with at least one edge in the remaining path - for edge in [(Coord2(x3, y3), Coord2(x1, y1)), (Coord2(x2, y2), Coord2(x4, y4))] { + for edge in [ + (Coord2(x3, y3), Coord2(x1, y1)), + (Coord2(x2, y2), Coord2(x4, y4)), + ] { // Convert the edge to a line - let fragment_edge = line_to_bezier::<_, Curve<_>>(&edge); + let fragment_edge = line_to_bezier::<_, Curve<_>>(&edge); // Iterate through the edges in remaining - let mut first_point = remaining[0].start_point(); - let mut num_collisions = 0; + let mut first_point = remaining[0].start_point(); + let mut num_collisions = 0; for (cp1, cp2, end_point) in remaining[0].points() { // Turn into a curve - let remain_edge = Curve::from_points(first_point, (cp1, cp2), end_point); - let intersections = curve_intersects_curve_clip(&fragment_edge, &remain_edge, 0.01); + let remain_edge = Curve::from_points(first_point, (cp1, cp2), end_point); + let intersections = curve_intersects_curve_clip(&fragment_edge, &remain_edge, 0.01); - num_collisions += intersections.len(); - if intersections.len() > 0 { println!(" {:?}", intersections.len()); } + num_collisions += intersections.len(); + if intersections.len() > 0 { + println!(" {:?}", intersections.len()); + } // There should be at least one collision if the start or end point is near the edge - let start_distance = edge.distance_to(&first_point); - let end_distance = edge.distance_to(&end_point); + let start_distance = edge.distance_to(&first_point); + let end_distance = edge.distance_to(&end_point); if intersections.len() == 0 { - let start_pos = edge.pos_for_point(&first_point); - let end_pos = edge.pos_for_point(&end_point); + let start_pos = edge.pos_for_point(&first_point); + let end_pos = edge.pos_for_point(&end_point); if start_distance.abs() < 1.0 || end_distance.abs() < 1.0 { - println!(" - {:?} {:?} {:?} {:?}", start_distance, end_distance, start_pos, end_pos); + println!( + " - {:?} {:?} {:?} {:?}", + start_distance, end_distance, start_pos, end_pos + ); println!("Fragment: {:?}", fragment_edge); println!("Remaining: {:?}", remain_edge); @@ -257,7 +319,7 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { } // The end point of this curve is the start point of the next curve - first_point = end_point; + first_point = end_point; } println!("Slice {}: {}, {:?}", slice_idx, num_collisions, edge); @@ -266,26 +328,57 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { // Merge the paths and print out the number of edges let mut merged_path = GraphPath::new(); - let fragment_graph = GraphPath::from_merged_paths(vec![fragment.clone()].iter().map(|path| (path, PathLabel(0, PathDirection::from(path))))); - let remain_graph = GraphPath::from_merged_paths(remaining.iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))); - - println!("Slice {}: {} edges in 'remaining' before colliding with the next fragment", slice_idx, remain_graph.all_edges().count()); - - merged_path = merged_path.merge(fragment_graph); - merged_path = merged_path.collide(remain_graph, 0.01); + let fragment_graph = GraphPath::from_merged_paths( + vec![fragment.clone()] + .iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + ); + let remain_graph = GraphPath::from_merged_paths( + remaining + .iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ); + + println!( + "Slice {}: {} edges in 'remaining' before colliding with the next fragment", + slice_idx, + remain_graph.all_edges().count() + ); + + merged_path = merged_path.merge(fragment_graph); + merged_path = merged_path.collide(remain_graph, 0.01); merged_path.round(0.01); - println!("Slice {}: {} edges", slice_idx, merged_path.all_edges().count()); + println!( + "Slice {}: {} edges", + slice_idx, + merged_path.all_edges().count() + ); // Cut the circle via the fragment - let cut_circle = path_full_intersect::<_, _, SimpleBezierPath>(&vec![fragment.clone()], &remaining, 0.01); + let cut_circle = path_full_intersect::<_, _, SimpleBezierPath>( + &vec![fragment.clone()], + &remaining, + 0.01, + ); if cut_circle.exterior_paths[1].len() != 1 { use flo_curves::debug::*; let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(remaining.iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); - merged_path = merged_path.collide(GraphPath::from_merged_paths(vec![fragment.clone()].iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), 0.01); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + remaining + .iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + vec![fragment.clone()] + .iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + 0.01, + ); //merged_path.round(0.01); @@ -307,7 +400,10 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { remaining = cut_circle.exterior_paths[1].clone(); // Reduce and re-increase the precision of the remaining path (this happens in FlowBetween: even though the points will be in slightly different positions we should still be able to slice using this curve) - remaining = remaining.into_iter().map(|path| convert_path_to_f32_and_back(path)).collect(); + remaining = remaining + .into_iter() + .map(|path| convert_path_to_f32_and_back(path)) + .collect(); } // Each fragment should consist of points that are either at the origin or on the circle @@ -315,13 +411,15 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { assert!(circle_fragment.len() == 1); let start_point = circle_fragment[0].start_point(); - let points = circle_fragment[0].points().map(|(_, _, p)| p); - let all_points = iter::once(start_point).chain(points); + let points = circle_fragment[0].points().map(|(_, _, p)| p); + let all_points = iter::once(start_point).chain(points); for circle_point in all_points { let distance_to_center = circle_point.distance_to(&Coord2(500.0, 500.0)); println!("- {} {:?}", idx, distance_to_center); - assert!((distance_to_center-16.0).abs() < 0.1 || (distance_to_center-116.0).abs() < 1.0); + assert!( + (distance_to_center - 16.0).abs() < 0.1 || (distance_to_center - 116.0).abs() < 1.0 + ); } } @@ -329,14 +427,14 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { assert!(remaining.len() > 0); println!("{:?}", remaining[0]); - let start_point = remaining[remaining.len()-1].start_point(); - let points = remaining[remaining.len()-1].points().map(|(_, _, p)| p); - let all_points = iter::once(start_point).chain(points); + let start_point = remaining[remaining.len() - 1].start_point(); + let points = remaining[remaining.len() - 1].points().map(|(_, _, p)| p); + let all_points = iter::once(start_point).chain(points); for circle_point in all_points { let distance_to_center = circle_point.distance_to(&Coord2(500.0, 500.0)); println!("{:?}", distance_to_center); - assert!((distance_to_center-0.0).abs() < 0.1 || (distance_to_center-16.0).abs() < 1.0); + assert!((distance_to_center - 0.0).abs() < 0.1 || (distance_to_center - 16.0).abs() < 1.0); } assert!(remaining.len() == 1); diff --git a/tests/bezier/path/arithmetic_sub.rs b/tests/bezier/path/arithmetic_sub.rs index 9339d536..a0d53430 100644 --- a/tests/bezier/path/arithmetic_sub.rs +++ b/tests/bezier/path/arithmetic_sub.rs @@ -1,6 +1,6 @@ -use flo_curves::*; use flo_curves::arc::*; use flo_curves::bezier::path::*; +use flo_curves::*; #[test] fn subtract_circles() { @@ -14,27 +14,41 @@ fn subtract_circles() { assert!(combined_circles.len() == 1); // All points should be on either circle, and two should be on both - let points = combined_circles[0].points().map(|(_, _, end_point)| end_point); + let points = combined_circles[0] + .points() + .map(|(_, _, end_point)| end_point); - let mut num_points_on_circle1 = 0; - let mut num_points_on_circle2 = 0; - let mut num_points_on_both = 0; + let mut num_points_on_circle1 = 0; + let mut num_points_on_circle2 = 0; + let mut num_points_on_both = 0; for point in points { let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); let distance_to_circle2 = Coord2(7.0, 5.0).distance_to(&point); // Must be on either circle - assert!((distance_to_circle1-4.0).abs() < 0.01 || (distance_to_circle2-4.0).abs() < 0.01); - - println!("{:?} {:?} {:?}", point, distance_to_circle1, distance_to_circle2); - - if (distance_to_circle1-4.0).abs() < 0.01 && (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_both += 1 } - else if (distance_to_circle1-4.0).abs() < 0.01 { num_points_on_circle1 += 1 } - else if (distance_to_circle2-4.0).abs() < 0.01 { num_points_on_circle2 += 1 } + assert!( + (distance_to_circle1 - 4.0).abs() < 0.01 || (distance_to_circle2 - 4.0).abs() < 0.01 + ); + + println!( + "{:?} {:?} {:?}", + point, distance_to_circle1, distance_to_circle2 + ); + + if (distance_to_circle1 - 4.0).abs() < 0.01 && (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_both += 1 + } else if (distance_to_circle1 - 4.0).abs() < 0.01 { + num_points_on_circle1 += 1 + } else if (distance_to_circle2 - 4.0).abs() < 0.01 { + num_points_on_circle2 += 1 + } } - println!("{:?} {:?} {:?}", num_points_on_circle1, num_points_on_circle2, num_points_on_both); + println!( + "{:?} {:?} {:?}", + num_points_on_circle1, num_points_on_circle2, num_points_on_both + ); assert!(num_points_on_circle1 == 2); assert!(num_points_on_circle2 == 2); @@ -77,7 +91,8 @@ fn subtract_from_self_rectangles_1() { let rectangle2 = rectangle1.clone(); // Create a hole in the larger circle - let combined_rectangles = path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let combined_rectangles = + path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); println!("{:?}", combined_rectangles); assert!(combined_rectangles.len() != 1); @@ -96,7 +111,8 @@ fn subtract_from_self_rectangles_2() { let rectangle2 = rectangle1.clone(); // Create a hole in the larger circle - let combined_rectangles = path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let combined_rectangles = + path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); println!("{:?}", combined_rectangles); assert!(combined_rectangles.len() != 1); @@ -136,8 +152,8 @@ fn cut_corners() { assert!(cut_corner.len() == 1); - let cut_corner = &cut_corner[0]; - let points = cut_corner.points().collect::>(); + let cut_corner = &cut_corner[0]; + let points = cut_corner.points().collect::>(); assert!(cut_corner.start_point().distance_to(&Coord2(1.0, 1.0)) < 0.1); assert!(points[0].2.distance_to(&Coord2(5.0, 1.0)) < 0.1); @@ -151,40 +167,172 @@ fn cut_corners() { #[test] fn subtract_triangle_from_partial_circle_graph() { use flo_curves::debug::*; - use std::collections::{HashMap}; + use std::collections::HashMap; // This regenerates a failing test from arithmetic_intersection: problem seems to be that there are overlapping (or near-overlapping lines) that cause two outer edges when subtracting - let remaining = vec![(Coord2(477.3671569824219, 613.7830200195313), vec![(Coord2(483.87042236328125, 581.0888671875), Coord2(490.3741455078125, 548.3924560546875), Coord2(496.8785400390625, 515.6925659179688)), (Coord2(498.9593200683594, 515.6925659179688), Coord2(501.0400695800781, 515.6925659179688), Coord2(503.1199951171875, 515.6900024414063)), (Coord2(505.0438232421875, 514.8963012695313), Coord2(506.9661865234375, 514.1000366210938), Coord2(508.8900146484375, 513.2999877929688)), (Coord2(510.3604431152344, 511.8321838378906), Coord2(511.8317565917969, 510.3608703613281), Coord2(513.2999877929688, 508.8900146484375)), (Coord2(514.0997924804688, 506.9667663574219), Coord2(514.8960571289063, 505.0444030761719), Coord2(515.6900024414063, 503.1199951171875)), (Coord2(515.6925659179688, 501.0406799316406), Coord2(515.6925659179688, 498.9599304199219), Coord2(515.6900024414063, 496.8800048828125)), (Coord2(514.8963012695313, 494.9561767578125), Coord2(514.1000366210938, 493.0338134765625), Coord2(513.2999877929688, 491.1099853515625)), (Coord2(511.8321838378906, 489.6395568847656), Coord2(510.3608703613281, 488.1682434082031), Coord2(508.8900146484375, 486.70001220703125)), (Coord2(506.9667663574219, 485.90020751953125), Coord2(505.0444030761719, 485.10394287109375), Coord2(503.1199951171875, 484.30999755859375)), (Coord2(501.0406799316406, 484.30743408203125), Coord2(498.9599304199219, 484.30743408203125), Coord2(496.8800048828125, 484.30999755859375)), (Coord2(494.9561767578125, 485.10369873046875), Coord2(493.0338134765625, 485.89996337890625), Coord2(491.1099853515625, 486.70001220703125)), (Coord2(489.6395568847656, 488.1678161621094), Coord2(488.1682434082031, 489.6391296386719), Coord2(486.70001220703125, 491.1099853515625)), (Coord2(485.90020751953125, 493.0332336425781), Coord2(485.10394287109375, 494.9555969238281), Coord2(484.30999755859375, 496.8800048828125)), (Coord2(484.30743408203125, 498.9593200683594), Coord2(484.30743408203125, 501.0400695800781), Coord2(484.30999755859375, 503.1199951171875)), (Coord2(485.10369873046875, 505.0438232421875), Coord2(485.89996337890625, 506.9661865234375), Coord2(486.70001220703125, 508.8900146484375)), (Coord2(488.1678161621094, 510.3604431152344), Coord2(489.6391296386719, 511.8317565917969), Coord2(491.1108703613281, 513.3035278320313)), (Coord2(472.5879821777344, 541.0249633789063), Coord2(454.0650939941406, 568.7463989257813), Coord2(435.5415344238281, 596.4689331054688)), (Coord2(448.4329833984375, 605.102783203125), Coord2(462.67291259765625, 610.8741455078125), Coord2(477.3671569824219, 613.7830200195313))])]; - let fragment = vec![(Coord2(491.1108762716864, 513.3035137968407), vec![(Coord2(438.50637541608546, 592.0317129194746), Coord2(385.91765275510227, 670.736298305119), Coord2(333.3289300941191, 749.4408836907635)), (Coord2(369.38413079268656, 764.375436814194), Coord2(405.428517093924, 779.3055104675816), Coord2(441.4729033951614, 794.2355841209692)), (Coord2(459.9451475894517, 701.369341374821), Coord2(478.41185121859684, 608.5309529306364), Coord2(496.87855484774195, 515.6925644864517)), (Coord2(494.95561081048504, 514.8960549865354), Coord2(493.0332435410857, 514.099784391688), Coord2(491.1108762716864, 513.3035137968407))])]; + let remaining = vec![( + Coord2(477.3671569824219, 613.7830200195313), + vec![ + ( + Coord2(483.87042236328125, 581.0888671875), + Coord2(490.3741455078125, 548.3924560546875), + Coord2(496.8785400390625, 515.6925659179688), + ), + ( + Coord2(498.9593200683594, 515.6925659179688), + Coord2(501.0400695800781, 515.6925659179688), + Coord2(503.1199951171875, 515.6900024414063), + ), + ( + Coord2(505.0438232421875, 514.8963012695313), + Coord2(506.9661865234375, 514.1000366210938), + Coord2(508.8900146484375, 513.2999877929688), + ), + ( + Coord2(510.3604431152344, 511.8321838378906), + Coord2(511.8317565917969, 510.3608703613281), + Coord2(513.2999877929688, 508.8900146484375), + ), + ( + Coord2(514.0997924804688, 506.9667663574219), + Coord2(514.8960571289063, 505.0444030761719), + Coord2(515.6900024414063, 503.1199951171875), + ), + ( + Coord2(515.6925659179688, 501.0406799316406), + Coord2(515.6925659179688, 498.9599304199219), + Coord2(515.6900024414063, 496.8800048828125), + ), + ( + Coord2(514.8963012695313, 494.9561767578125), + Coord2(514.1000366210938, 493.0338134765625), + Coord2(513.2999877929688, 491.1099853515625), + ), + ( + Coord2(511.8321838378906, 489.6395568847656), + Coord2(510.3608703613281, 488.1682434082031), + Coord2(508.8900146484375, 486.70001220703125), + ), + ( + Coord2(506.9667663574219, 485.90020751953125), + Coord2(505.0444030761719, 485.10394287109375), + Coord2(503.1199951171875, 484.30999755859375), + ), + ( + Coord2(501.0406799316406, 484.30743408203125), + Coord2(498.9599304199219, 484.30743408203125), + Coord2(496.8800048828125, 484.30999755859375), + ), + ( + Coord2(494.9561767578125, 485.10369873046875), + Coord2(493.0338134765625, 485.89996337890625), + Coord2(491.1099853515625, 486.70001220703125), + ), + ( + Coord2(489.6395568847656, 488.1678161621094), + Coord2(488.1682434082031, 489.6391296386719), + Coord2(486.70001220703125, 491.1099853515625), + ), + ( + Coord2(485.90020751953125, 493.0332336425781), + Coord2(485.10394287109375, 494.9555969238281), + Coord2(484.30999755859375, 496.8800048828125), + ), + ( + Coord2(484.30743408203125, 498.9593200683594), + Coord2(484.30743408203125, 501.0400695800781), + Coord2(484.30999755859375, 503.1199951171875), + ), + ( + Coord2(485.10369873046875, 505.0438232421875), + Coord2(485.89996337890625, 506.9661865234375), + Coord2(486.70001220703125, 508.8900146484375), + ), + ( + Coord2(488.1678161621094, 510.3604431152344), + Coord2(489.6391296386719, 511.8317565917969), + Coord2(491.1108703613281, 513.3035278320313), + ), + ( + Coord2(472.5879821777344, 541.0249633789063), + Coord2(454.0650939941406, 568.7463989257813), + Coord2(435.5415344238281, 596.4689331054688), + ), + ( + Coord2(448.4329833984375, 605.102783203125), + Coord2(462.67291259765625, 610.8741455078125), + Coord2(477.3671569824219, 613.7830200195313), + ), + ], + )]; + let fragment = vec![( + Coord2(491.1108762716864, 513.3035137968407), + vec![ + ( + Coord2(438.50637541608546, 592.0317129194746), + Coord2(385.91765275510227, 670.736298305119), + Coord2(333.3289300941191, 749.4408836907635), + ), + ( + Coord2(369.38413079268656, 764.375436814194), + Coord2(405.428517093924, 779.3055104675816), + Coord2(441.4729033951614, 794.2355841209692), + ), + ( + Coord2(459.9451475894517, 701.369341374821), + Coord2(478.41185121859684, 608.5309529306364), + Coord2(496.87855484774195, 515.6925644864517), + ), + ( + Coord2(494.95561081048504, 514.8960549865354), + Coord2(493.0332435410857, 514.099784391688), + Coord2(491.1108762716864, 513.3035137968407), + ), + ], + )]; // Contains points that are very close and not the same - for exmaple: // 491.11087 03613281, 513.3035 278320313 // 491.11087 62716864, 513.3035 137968407 // Merge the two paths - let mut merged_path = GraphPath::new(); - merged_path = merged_path.merge(GraphPath::from_merged_paths(remaining.iter().map(|path| (path, PathLabel(0, PathDirection::from(path)))))); - merged_path = merged_path.collide(GraphPath::from_merged_paths(fragment.iter().map(|path| (path, PathLabel(1, PathDirection::from(path))))), 0.01); + let mut merged_path = GraphPath::new(); + merged_path = merged_path.merge(GraphPath::from_merged_paths( + remaining + .iter() + .map(|path| (path, PathLabel(0, PathDirection::from(path)))), + )); + merged_path = merged_path.collide( + GraphPath::from_merged_paths( + fragment + .iter() + .map(|path| (path, PathLabel(1, PathDirection::from(path)))), + ), + 0.01, + ); // Ray cast along the fragment edge - let ypos = 570.0; - let collisions = merged_path.ray_collisions(&(Coord2(0.0, ypos), Coord2(1.0, ypos))); + let ypos = 570.0; + let collisions = merged_path.ray_collisions(&(Coord2(0.0, ypos), Coord2(1.0, ypos))); println!("{:?}", collisions); // Subtract fragment from remaining println!(); merged_path.set_exterior_by_subtracting(); - println!("{}", graph_path_svg_string(&merged_path, vec![(Coord2(0.0, ypos), Coord2(1.0, ypos))])); + println!( + "{}", + graph_path_svg_string(&merged_path, vec![(Coord2(0.0, ypos), Coord2(1.0, ypos))]) + ); merged_path.heal_exterior_gaps(); // No points with any edges leaving or arriving at them should be close to each other let mut point_positions = HashMap::new(); for edge in merged_path.all_edges() { - let start_idx = edge.start_point_index(); - let end_idx = edge.end_point_index(); + let start_idx = edge.start_point_index(); + let end_idx = edge.end_point_index(); - let start_pos = edge.start_point(); - let end_pos = edge.end_point(); + let start_pos = edge.start_point(); + let end_pos = edge.end_point(); point_positions.insert(start_idx, start_pos); point_positions.insert(end_idx, end_pos); @@ -193,7 +341,7 @@ fn subtract_triangle_from_partial_circle_graph() { // All points along the ray should be interior points as they're subtracting from each other (edges very nearly overlap though)\ for (edge_type, _curve_t, _line_t, _pos) in collisions { let edge_ref = match edge_type { - GraphRayCollision::SingleEdge(edge) | GraphRayCollision::Intersection(edge) => edge + GraphRayCollision::SingleEdge(edge) | GraphRayCollision::Intersection(edge) => edge, }; assert!(merged_path.edge_kind(edge_ref) != GraphPathEdgeKind::Exterior); @@ -203,7 +351,9 @@ fn subtract_triangle_from_partial_circle_graph() { println!(); for (idx, pos) in point_positions.iter() { for (cmp_idx, cmp_pos) in point_positions.iter() { - if cmp_idx == idx { continue; } + if cmp_idx == idx { + continue; + } if pos.distance_to(cmp_pos) < 1.0 { println!("Overlapping points: {} {}", idx, cmp_idx); @@ -212,7 +362,7 @@ fn subtract_triangle_from_partial_circle_graph() { } // Extract the resulting path - let subtracted_path = merged_path.exterior_paths::(); + let subtracted_path = merged_path.exterior_paths::(); // This should entirely subtract the triangle from the remaining path assert!(subtracted_path.len() == 1); @@ -221,15 +371,133 @@ fn subtract_triangle_from_partial_circle_graph() { #[test] fn subtract_triangle_from_partial_circle() { // This regenerates a failing test from arithmetic_intersection: problem seems to be that there are overlapping (or near-overlapping lines) that cause two outer edges when subtracting - let remaining = vec![(Coord2(477.3671569824219, 613.7830200195313), vec![(Coord2(483.87042236328125, 581.0888671875), Coord2(490.3741455078125, 548.3924560546875), Coord2(496.8785400390625, 515.6925659179688)), (Coord2(498.9593200683594, 515.6925659179688), Coord2(501.0400695800781, 515.6925659179688), Coord2(503.1199951171875, 515.6900024414063)), (Coord2(505.0438232421875, 514.8963012695313), Coord2(506.9661865234375, 514.1000366210938), Coord2(508.8900146484375, 513.2999877929688)), (Coord2(510.3604431152344, 511.8321838378906), Coord2(511.8317565917969, 510.3608703613281), Coord2(513.2999877929688, 508.8900146484375)), (Coord2(514.0997924804688, 506.9667663574219), Coord2(514.8960571289063, 505.0444030761719), Coord2(515.6900024414063, 503.1199951171875)), (Coord2(515.6925659179688, 501.0406799316406), Coord2(515.6925659179688, 498.9599304199219), Coord2(515.6900024414063, 496.8800048828125)), (Coord2(514.8963012695313, 494.9561767578125), Coord2(514.1000366210938, 493.0338134765625), Coord2(513.2999877929688, 491.1099853515625)), (Coord2(511.8321838378906, 489.6395568847656), Coord2(510.3608703613281, 488.1682434082031), Coord2(508.8900146484375, 486.70001220703125)), (Coord2(506.9667663574219, 485.90020751953125), Coord2(505.0444030761719, 485.10394287109375), Coord2(503.1199951171875, 484.30999755859375)), (Coord2(501.0406799316406, 484.30743408203125), Coord2(498.9599304199219, 484.30743408203125), Coord2(496.8800048828125, 484.30999755859375)), (Coord2(494.9561767578125, 485.10369873046875), Coord2(493.0338134765625, 485.89996337890625), Coord2(491.1099853515625, 486.70001220703125)), (Coord2(489.6395568847656, 488.1678161621094), Coord2(488.1682434082031, 489.6391296386719), Coord2(486.70001220703125, 491.1099853515625)), (Coord2(485.90020751953125, 493.0332336425781), Coord2(485.10394287109375, 494.9555969238281), Coord2(484.30999755859375, 496.8800048828125)), (Coord2(484.30743408203125, 498.9593200683594), Coord2(484.30743408203125, 501.0400695800781), Coord2(484.30999755859375, 503.1199951171875)), (Coord2(485.10369873046875, 505.0438232421875), Coord2(485.89996337890625, 506.9661865234375), Coord2(486.70001220703125, 508.8900146484375)), (Coord2(488.1678161621094, 510.3604431152344), Coord2(489.6391296386719, 511.8317565917969), Coord2(491.1108703613281, 513.3035278320313)), (Coord2(472.5879821777344, 541.0249633789063), Coord2(454.0650939941406, 568.7463989257813), Coord2(435.5415344238281, 596.4689331054688)), (Coord2(448.4329833984375, 605.102783203125), Coord2(462.67291259765625, 610.8741455078125), Coord2(477.3671569824219, 613.7830200195313))])]; - let fragment = vec![(Coord2(491.1108762716864, 513.3035137968407), vec![(Coord2(438.50637541608546, 592.0317129194746), Coord2(385.91765275510227, 670.736298305119), Coord2(333.3289300941191, 749.4408836907635)), (Coord2(369.38413079268656, 764.375436814194), Coord2(405.428517093924, 779.3055104675816), Coord2(441.4729033951614, 794.2355841209692)), (Coord2(459.9451475894517, 701.369341374821), Coord2(478.41185121859684, 608.5309529306364), Coord2(496.87855484774195, 515.6925644864517)), (Coord2(494.95561081048504, 514.8960549865354), Coord2(493.0332435410857, 514.099784391688), Coord2(491.1108762716864, 513.3035137968407))])]; + let remaining = vec![( + Coord2(477.3671569824219, 613.7830200195313), + vec![ + ( + Coord2(483.87042236328125, 581.0888671875), + Coord2(490.3741455078125, 548.3924560546875), + Coord2(496.8785400390625, 515.6925659179688), + ), + ( + Coord2(498.9593200683594, 515.6925659179688), + Coord2(501.0400695800781, 515.6925659179688), + Coord2(503.1199951171875, 515.6900024414063), + ), + ( + Coord2(505.0438232421875, 514.8963012695313), + Coord2(506.9661865234375, 514.1000366210938), + Coord2(508.8900146484375, 513.2999877929688), + ), + ( + Coord2(510.3604431152344, 511.8321838378906), + Coord2(511.8317565917969, 510.3608703613281), + Coord2(513.2999877929688, 508.8900146484375), + ), + ( + Coord2(514.0997924804688, 506.9667663574219), + Coord2(514.8960571289063, 505.0444030761719), + Coord2(515.6900024414063, 503.1199951171875), + ), + ( + Coord2(515.6925659179688, 501.0406799316406), + Coord2(515.6925659179688, 498.9599304199219), + Coord2(515.6900024414063, 496.8800048828125), + ), + ( + Coord2(514.8963012695313, 494.9561767578125), + Coord2(514.1000366210938, 493.0338134765625), + Coord2(513.2999877929688, 491.1099853515625), + ), + ( + Coord2(511.8321838378906, 489.6395568847656), + Coord2(510.3608703613281, 488.1682434082031), + Coord2(508.8900146484375, 486.70001220703125), + ), + ( + Coord2(506.9667663574219, 485.90020751953125), + Coord2(505.0444030761719, 485.10394287109375), + Coord2(503.1199951171875, 484.30999755859375), + ), + ( + Coord2(501.0406799316406, 484.30743408203125), + Coord2(498.9599304199219, 484.30743408203125), + Coord2(496.8800048828125, 484.30999755859375), + ), + ( + Coord2(494.9561767578125, 485.10369873046875), + Coord2(493.0338134765625, 485.89996337890625), + Coord2(491.1099853515625, 486.70001220703125), + ), + ( + Coord2(489.6395568847656, 488.1678161621094), + Coord2(488.1682434082031, 489.6391296386719), + Coord2(486.70001220703125, 491.1099853515625), + ), + ( + Coord2(485.90020751953125, 493.0332336425781), + Coord2(485.10394287109375, 494.9555969238281), + Coord2(484.30999755859375, 496.8800048828125), + ), + ( + Coord2(484.30743408203125, 498.9593200683594), + Coord2(484.30743408203125, 501.0400695800781), + Coord2(484.30999755859375, 503.1199951171875), + ), + ( + Coord2(485.10369873046875, 505.0438232421875), + Coord2(485.89996337890625, 506.9661865234375), + Coord2(486.70001220703125, 508.8900146484375), + ), + ( + Coord2(488.1678161621094, 510.3604431152344), + Coord2(489.6391296386719, 511.8317565917969), + Coord2(491.1108703613281, 513.3035278320313), + ), + ( + Coord2(472.5879821777344, 541.0249633789063), + Coord2(454.0650939941406, 568.7463989257813), + Coord2(435.5415344238281, 596.4689331054688), + ), + ( + Coord2(448.4329833984375, 605.102783203125), + Coord2(462.67291259765625, 610.8741455078125), + Coord2(477.3671569824219, 613.7830200195313), + ), + ], + )]; + let fragment = vec![( + Coord2(491.1108762716864, 513.3035137968407), + vec![ + ( + Coord2(438.50637541608546, 592.0317129194746), + Coord2(385.91765275510227, 670.736298305119), + Coord2(333.3289300941191, 749.4408836907635), + ), + ( + Coord2(369.38413079268656, 764.375436814194), + Coord2(405.428517093924, 779.3055104675816), + Coord2(441.4729033951614, 794.2355841209692), + ), + ( + Coord2(459.9451475894517, 701.369341374821), + Coord2(478.41185121859684, 608.5309529306364), + Coord2(496.87855484774195, 515.6925644864517), + ), + ( + Coord2(494.95561081048504, 514.8960549865354), + Coord2(493.0332435410857, 514.099784391688), + Coord2(491.1108762716864, 513.3035137968407), + ), + ], + )]; // Contains points that are very close and not the same - for exmaple: // 491.11087 03613281, 513.3035 278320313 // 491.11087 62716864, 513.3035 137968407 // Merge the two paths - let subtracted_path = path_sub::<_, _, SimpleBezierPath>(&remaining, &fragment, 0.01); + let subtracted_path = path_sub::<_, _, SimpleBezierPath>(&remaining, &fragment, 0.01); // This should entirely subtract the triangle from the remaining path assert!(subtracted_path.len() == 1); diff --git a/tests/bezier/path/bounds.rs b/tests/bezier/path/bounds.rs index b4ba6276..b2fef8cb 100644 --- a/tests/bezier/path/bounds.rs +++ b/tests/bezier/path/bounds.rs @@ -1,6 +1,6 @@ -use flo_curves::*; use flo_curves::arc::*; use flo_curves::bezier::path::*; +use flo_curves::*; #[test] fn circle_path_bounds() { diff --git a/tests/bezier/path/graph_path.rs b/tests/bezier/path/graph_path.rs index 380fe753..bc038c9a 100644 --- a/tests/bezier/path/graph_path.rs +++ b/tests/bezier/path/graph_path.rs @@ -1,13 +1,19 @@ -use flo_curves::*; use flo_curves::arc::*; use flo_curves::bezier::path::*; +use flo_curves::*; use std::f64; #[test] pub fn create_and_read_simple_graph_path() { - let path = (Coord2(10.0, 11.0), vec![(Coord2(15.0, 16.0), Coord2(17.0, 18.0), Coord2(19.0, 20.0)), (Coord2(21.0, 22.0), Coord2(23.0, 24.0), Coord2(25.0, 26.0))]); - let graph_path = GraphPath::from_path(&path, ()); + let path = ( + Coord2(10.0, 11.0), + vec![ + (Coord2(15.0, 16.0), Coord2(17.0, 18.0), Coord2(19.0, 20.0)), + (Coord2(21.0, 22.0), Coord2(23.0, 24.0), Coord2(25.0, 26.0)), + ], + ); + let graph_path = GraphPath::from_path(&path, ()); assert!(graph_path.num_points() == 3); @@ -45,8 +51,14 @@ pub fn create_and_read_simple_graph_path() { #[test] pub fn create_and_read_simple_graph_path_reverse() { - let path = (Coord2(10.0, 11.0), vec![(Coord2(15.0, 16.0), Coord2(17.0, 18.0), Coord2(19.0, 20.0)), (Coord2(21.0, 22.0), Coord2(23.0, 24.0), Coord2(25.0, 26.0))]); - let graph_path = GraphPath::from_path(&path, ()); + let path = ( + Coord2(10.0, 11.0), + vec![ + (Coord2(15.0, 16.0), Coord2(17.0, 18.0), Coord2(19.0, 20.0)), + (Coord2(21.0, 22.0), Coord2(23.0, 24.0), Coord2(25.0, 26.0)), + ], + ); + let graph_path = GraphPath::from_path(&path, ()); assert!(graph_path.num_points() == 3); @@ -98,7 +110,7 @@ pub fn collide_two_rectangles() { .line_to(Coord2(4.0, 9.0)) .line_to(Coord2(4.0, 4.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle1, 1); let rectangle2 = GraphPath::from_path(&rectangle2, 2); @@ -158,8 +170,12 @@ pub fn collide_two_rectangles() { check_count += 1; assert!(edges.len() == 2); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 4.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(1.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 4.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(1.0, 5.0)) < 0.1)); assert!(edges.iter().any(|edge| edge.label() == 1)); assert!(edges.iter().any(|edge| edge.label() == 2)); } @@ -168,8 +184,12 @@ pub fn collide_two_rectangles() { check_count += 1; assert!(edges.len() == 2); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(9.0, 4.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(9.0, 4.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); assert!(edges.iter().any(|edge| edge.label() == 1)); assert!(edges.iter().any(|edge| edge.label() == 2)); } @@ -189,7 +209,7 @@ pub fn collide_identical_rectangles() { .line_to(Coord2(1.0, 1.0)) .build(); let rectangle2 = rectangle1.clone(); - + let rectangle1 = GraphPath::from_path(&rectangle1, 1); let rectangle2 = GraphPath::from_path(&rectangle2, 2); @@ -233,7 +253,7 @@ fn multiple_collisions_on_one_edge() { .line_to(Coord2(4.0, 0.0)) .line_to(Coord2(2.0, 0.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle1, ()); let rectangle2 = GraphPath::from_path(&rectangle2, ()); @@ -250,17 +270,33 @@ fn multiple_collisions_on_one_edge() { assert!(edges.len() <= 2); if edges.len() == 2 { if edges[0].start_point().distance_to(&Coord2(2.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 5.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 0.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 0.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(2.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 6.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 6.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 1.0)) < 0.1)); } else { // These are the only four intersection points that should exist println!("{:?}", edges[0].start_point()); @@ -285,7 +321,7 @@ fn multiple_collisions_on_one_edge_opposite_direction() { .line_to(Coord2(2.0, 0.0)) .line_to(Coord2(4.0, 0.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle1, ()); let rectangle2 = GraphPath::from_path(&rectangle2, ()); @@ -306,34 +342,54 @@ fn multiple_collisions_on_one_edge_opposite_direction() { num_intersects += 1; if edges[0].start_point().distance_to(&Coord2(2.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(2.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); } else { // These are the only four intersection points that should exist println!("{:?}", edges[0].start_point()); assert!(false) } } else if edges.len() == 1 { - let edge = edges.iter().nth(0).unwrap(); + let edge = edges.iter().nth(0).unwrap(); let start_point = edge.start_point(); - assert!((start_point.x()-1.0).abs() < 0.01 || - (start_point.x()-5.0).abs() < 0.01 || - (start_point.x()-2.0).abs() < 0.01 || - (start_point.x()-4.0).abs() < 0.01); - assert!((start_point.y()-1.0).abs() < 0.01 || - (start_point.y()-5.0).abs() < 0.01 || - (start_point.y()-0.0).abs() < 0.01 || - (start_point.y()-6.0).abs() < 0.01); + assert!( + (start_point.x() - 1.0).abs() < 0.01 + || (start_point.x() - 5.0).abs() < 0.01 + || (start_point.x() - 2.0).abs() < 0.01 + || (start_point.x() - 4.0).abs() < 0.01 + ); + assert!( + (start_point.y() - 1.0).abs() < 0.01 + || (start_point.y() - 5.0).abs() < 0.01 + || (start_point.y() - 0.0).abs() < 0.01 + || (start_point.y() - 6.0).abs() < 0.01 + ); } } @@ -357,7 +413,7 @@ fn collision_at_same_point() { .line_to(Coord2(2.0, 0.0)) .line_to(Coord2(4.0, 0.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle1, ()); let rectangle2 = GraphPath::from_path(&rectangle2, ()); @@ -372,7 +428,9 @@ fn collision_at_same_point() { let mut num_orphaned_points = 0; for point_idx in 0..13 { let edges = collision.edges_for_point(point_idx).collect::>(); - if edges.len() == 0 { num_orphaned_points += 1; } + if edges.len() == 0 { + num_orphaned_points += 1; + } } assert!(num_orphaned_points <= 1); @@ -387,34 +445,54 @@ fn collision_at_same_point() { num_intersects += 1; if edges[0].start_point().distance_to(&Coord2(2.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(2.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); } else { // These are the only four intersection points that should exist println!("{:?}", edges[0].start_point()); assert!(false) } } else if edges.len() == 1 { - let edge = edges.iter().nth(0).unwrap(); + let edge = edges.iter().nth(0).unwrap(); let start_point = edge.start_point(); - assert!((start_point.x()-1.0).abs() < 0.01 || - (start_point.x()-5.0).abs() < 0.01 || - (start_point.x()-2.0).abs() < 0.01 || - (start_point.x()-4.0).abs() < 0.01); - assert!((start_point.y()-1.0).abs() < 0.01 || - (start_point.y()-5.0).abs() < 0.01 || - (start_point.y()-0.0).abs() < 0.01 || - (start_point.y()-6.0).abs() < 0.01); + assert!( + (start_point.x() - 1.0).abs() < 0.01 + || (start_point.x() - 5.0).abs() < 0.01 + || (start_point.x() - 2.0).abs() < 0.01 + || (start_point.x() - 4.0).abs() < 0.01 + ); + assert!( + (start_point.y() - 1.0).abs() < 0.01 + || (start_point.y() - 5.0).abs() < 0.01 + || (start_point.y() - 0.0).abs() < 0.01 + || (start_point.y() - 6.0).abs() < 0.01 + ); } else { // Should only be 1 edge (corners) or 2 edges (collision points) println!("{:?}", edges); @@ -441,7 +519,7 @@ fn collision_exactly_on_edge_src() { .line_to(Coord2(2.0, 0.0)) .line_to(Coord2(4.0, 0.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle1, ()); let rectangle2 = GraphPath::from_path(&rectangle2, ()); @@ -456,7 +534,9 @@ fn collision_exactly_on_edge_src() { let mut num_orphaned_points = 0; for point_idx in 0..13 { let edges = collision.edges_for_point(point_idx).collect::>(); - if edges.len() == 0 { num_orphaned_points += 1; } + if edges.len() == 0 { + num_orphaned_points += 1; + } } assert!(num_orphaned_points <= 1); @@ -471,34 +551,54 @@ fn collision_exactly_on_edge_src() { num_intersects += 1; if edges[0].start_point().distance_to(&Coord2(2.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(2.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); } else { // These are the only four intersection points that should exist println!("{:?}", edges[0].start_point()); assert!(false) } } else if edges.len() == 1 { - let edge = edges.iter().nth(0).unwrap(); + let edge = edges.iter().nth(0).unwrap(); let start_point = edge.start_point(); - assert!((start_point.x()-1.0).abs() < 0.01 || - (start_point.x()-5.0).abs() < 0.01 || - (start_point.x()-2.0).abs() < 0.01 || - (start_point.x()-4.0).abs() < 0.01); - assert!((start_point.y()-1.0).abs() < 0.01 || - (start_point.y()-5.0).abs() < 0.01 || - (start_point.y()-0.0).abs() < 0.01 || - (start_point.y()-6.0).abs() < 0.01); + assert!( + (start_point.x() - 1.0).abs() < 0.01 + || (start_point.x() - 5.0).abs() < 0.01 + || (start_point.x() - 2.0).abs() < 0.01 + || (start_point.x() - 4.0).abs() < 0.01 + ); + assert!( + (start_point.y() - 1.0).abs() < 0.01 + || (start_point.y() - 5.0).abs() < 0.01 + || (start_point.y() - 0.0).abs() < 0.01 + || (start_point.y() - 6.0).abs() < 0.01 + ); } else { // Should only be 1 edge (corners) or 2 edges (collision points) println!("{:?}", edges); @@ -525,7 +625,7 @@ fn collision_exactly_on_edge_tgt() { .line_to(Coord2(2.0, 0.0)) .line_to(Coord2(4.0, 0.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle1, ()); let rectangle2 = GraphPath::from_path(&rectangle2, ()); @@ -540,7 +640,9 @@ fn collision_exactly_on_edge_tgt() { let mut num_orphaned_points = 0; for point_idx in 0..13 { let edges = collision.edges_for_point(point_idx).collect::>(); - if edges.len() == 0 { num_orphaned_points += 1; } + if edges.len() == 0 { + num_orphaned_points += 1; + } } assert!(num_orphaned_points <= 1); @@ -555,34 +657,54 @@ fn collision_exactly_on_edge_tgt() { num_intersects += 1; if edges[0].start_point().distance_to(&Coord2(2.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 0.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(1.0, 1.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 1.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(2.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(2.0, 1.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 5.0)) < 0.1)); } else if edges[0].start_point().distance_to(&Coord2(4.0, 5.0)) < 0.1 { - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); - assert!(edges.iter().any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(5.0, 5.0)) < 0.1)); + assert!(edges + .iter() + .any(|edge| edge.end_point().distance_to(&Coord2(4.0, 6.0)) < 0.1)); } else { // These are the only four intersection points that should exist println!("{:?}", edges[0].start_point()); assert!(false) } } else if edges.len() == 1 { - let edge = edges.iter().nth(0).unwrap(); + let edge = edges.iter().nth(0).unwrap(); let start_point = edge.start_point(); - assert!((start_point.x()-1.0).abs() < 0.01 || - (start_point.x()-5.0).abs() < 0.01 || - (start_point.x()-2.0).abs() < 0.01 || - (start_point.x()-4.0).abs() < 0.01); - assert!((start_point.y()-1.0).abs() < 0.01 || - (start_point.y()-5.0).abs() < 0.01 || - (start_point.y()-0.0).abs() < 0.01 || - (start_point.y()-6.0).abs() < 0.01); + assert!( + (start_point.x() - 1.0).abs() < 0.01 + || (start_point.x() - 5.0).abs() < 0.01 + || (start_point.x() - 2.0).abs() < 0.01 + || (start_point.x() - 4.0).abs() < 0.01 + ); + assert!( + (start_point.y() - 1.0).abs() < 0.01 + || (start_point.y() - 5.0).abs() < 0.01 + || (start_point.y() - 0.0).abs() < 0.01 + || (start_point.y() - 6.0).abs() < 0.01 + ); } else { // Should only be 1 edge (corners) or 2 edges (collision points) println!("{:?}", edges); @@ -593,10 +715,16 @@ fn collision_exactly_on_edge_tgt() { assert!(num_intersects == 4); } -fn to_collision_with_edges<'a, Point, Label>(collisions: Vec<(GraphRayCollision, f64, f64, Coord2)>, graph_path: &'a GraphPath) -> Vec<(GraphEdge<'a, Point, Label>, f64, f64)> -where Point: Coordinate+Coordinate2D, - Label: Copy { - collisions.into_iter() +fn to_collision_with_edges<'a, Point, Label>( + collisions: Vec<(GraphRayCollision, f64, f64, Coord2)>, + graph_path: &'a GraphPath, +) -> Vec<(GraphEdge<'a, Point, Label>, f64, f64)> +where + Point: Coordinate + Coordinate2D, + Label: Copy, +{ + collisions + .into_iter() .map(move |(collision, curve_t, line_t, _pos)| { let edge = collision.edge(); (graph_path.get_edge(edge), curve_t, line_t) @@ -623,7 +751,7 @@ fn cast_ray_to_rectangle_corner() { let collision = &collision[0]; assert!(collision.0.start_point() == Coord2(1.0, 1.0)); - assert!((collision.1-0.0).abs() < 0.01); + assert!((collision.1 - 0.0).abs() < 0.01); } #[test] @@ -644,8 +772,11 @@ fn casting_ray_to_exact_point_produces_one_collision() { let collision = rectangle1.ray_collisions(&(Coord2(0.0, 0.0), Coord2(1.0, 1.0))); let collision = to_collision_with_edges(collision, &rectangle1); - let collisions_with_corner = collision.into_iter() - .filter(|(edge, curve_t, _line_t)| edge.point_at_pos(*curve_t).distance_to(&Coord2(1.0, 1.0)) < 0.1) + let collisions_with_corner = collision + .into_iter() + .filter(|(edge, curve_t, _line_t)| { + edge.point_at_pos(*curve_t).distance_to(&Coord2(1.0, 1.0)) < 0.1 + }) .collect::>(); assert!(collisions_with_corner.len() != 0); assert!(collisions_with_corner.len() != 2); @@ -675,7 +806,7 @@ fn casting_ray_across_corner_produces_no_collision() { fn casting_ray_to_intersection_point_produces_two_collisions() { // A ray hitting an exact point that is an intersection (has two edges leaving it) should produce two collisions, one on each edge // ... also this case where we have an overlapping line might be weird (but I don't think we'll generate it properly yet): - // + // // +-----+ // | | // | +----+ @@ -683,9 +814,9 @@ fn casting_ray_to_intersection_point_produces_two_collisions() { // | +----+ // | | // +-----+ - // + // // (There's an intersection where there are two edges entering it but only one leaving) - // + // // This test should still be valid if the 'shared' edge is stored in the graph as two edges // Create a rectangle @@ -712,8 +843,11 @@ fn casting_ray_to_intersection_point_produces_two_collisions() { let collision = collided.ray_collisions(&(Coord2(0.0, 0.0), Coord2(5.0, 3.0))); let collision = to_collision_with_edges(collision, &collided); - let collisions_with_corner = collision.into_iter() - .filter(|(edge, curve_t, _line_t)| edge.point_at_pos(*curve_t).distance_to(&Coord2(5.0, 3.0)) < 0.1) + let collisions_with_corner = collision + .into_iter() + .filter(|(edge, curve_t, _line_t)| { + edge.point_at_pos(*curve_t).distance_to(&Coord2(5.0, 3.0)) < 0.1 + }) .collect::>(); assert!(collisions_with_corner.len() != 0); assert!(collisions_with_corner.len() != 4); @@ -738,9 +872,15 @@ fn cast_ray_across_rectangle() { assert!(collision.len() > 0); let collision = &collision[0]; - assert!(collision.0.point_at_pos(collision.1).distance_to(&Coord2(1.0, 3.0)) < 0.001); + assert!( + collision + .0 + .point_at_pos(collision.1) + .distance_to(&Coord2(1.0, 3.0)) + < 0.001 + ); assert!(collision.0.start_point() == Coord2(1.0, 1.0)); - assert!((collision.1-0.5).abs() < 0.01); + assert!((collision.1 - 0.5).abs() < 0.01); } #[test] @@ -762,7 +902,7 @@ fn cast_ray_to_rectangle_far_corner() { let collision = &collision[0]; assert!(collision.0.start_point() == Coord2(1.0, 1.0)); - assert!((collision.1-0.0).abs() < 0.01); + assert!((collision.1 - 0.0).abs() < 0.01); } #[test] @@ -784,7 +924,7 @@ fn cast_ray_to_rectangle_far_corner_backwards() { let collision = &collision[0]; assert!(collision.0.start_point().distance_to(&Coord2(5.0, 5.0)) < 0.1); - assert!((collision.1-0.0).abs() < 0.01); + assert!((collision.1 - 0.0).abs() < 0.01); } #[test] @@ -860,7 +1000,9 @@ fn set_collision_as_exterior() { assert!(edges.len() == 1); assert!(edges[0].kind() == GraphPathEdgeKind::Exterior); - let edges = collided.reverse_edges_for_point(point_idx).collect::>(); + let edges = collided + .reverse_edges_for_point(point_idx) + .collect::>(); assert!(edges.len() == 1); assert!(edges[0].kind() == GraphPathEdgeKind::Exterior); @@ -870,7 +1012,10 @@ fn set_collision_as_exterior() { for point_idx in 4..(collided.num_points()) { let edges = collided.edges_for_point(point_idx).collect::>(); - assert!(edges.into_iter().all(|edge| edge.end_point_index() < 4 || edge.kind() == GraphPathEdgeKind::Uncategorised)); + assert!(edges + .into_iter() + .all(|edge| edge.end_point_index() < 4 + || edge.kind() == GraphPathEdgeKind::Uncategorised)); } } @@ -921,9 +1066,9 @@ fn get_path_from_exterior_lines_multiple_paths() { .line_to(Coord2(15.0, 1.0)) .line_to(Coord2(11.0, 1.0)) .build(); - let rectangle1 = GraphPath::from_path(&rectangle1, ()); - let rectangle2 = GraphPath::from_path(&rectangle2, ()); - let mut rectangle1 = rectangle1.merge(rectangle2); + let rectangle1 = GraphPath::from_path(&rectangle1, ()); + let rectangle2 = GraphPath::from_path(&rectangle2, ()); + let mut rectangle1 = rectangle1.merge(rectangle2); // Mark everything as an exterior path let first_edge = rectangle1.edges_for_point(0).nth(0).unwrap().into(); @@ -975,12 +1120,17 @@ fn collide_circles() { for point_idx in 0..10 { println!("Point {:?}", point_idx); for edge in graph_path.edges_for_point(point_idx) { - println!(" {:?} -> {:?} ({:?})", edge.start_point(), edge.end_point(), edge.end_point_index()); + println!( + " {:?} -> {:?} ({:?})", + edge.start_point(), + edge.end_point(), + edge.end_point_index() + ); } } // First four points should correspond to the four points in circle1 (and should all have one edge) - // Some implementation details depended on here: + // Some implementation details depended on here: // * we preserve at least the points from the first path when colliding assert!(graph_path.edges_for_point(0).collect::>().len() == 1); assert!(graph_path.edges_for_point(1).collect::>().len() == 1); @@ -988,29 +1138,51 @@ fn collide_circles() { assert!(graph_path.edges_for_point(3).collect::>().len() == 1); // Point 1 should lead to the intersection point - let to_intersection = graph_path.edges_for_point(0).nth(0).unwrap(); - let intersection_point = to_intersection.end_point_index(); + let to_intersection = graph_path.edges_for_point(0).nth(0).unwrap(); + let intersection_point = to_intersection.end_point_index(); assert!(intersection_point > 3); // Intersection point should lead to another intersection point - let intersection_edges = graph_path.edges_for_point(intersection_point).collect::>(); + let intersection_edges = graph_path + .edges_for_point(intersection_point) + .collect::>(); assert!(intersection_edges.len() == 2); // Should lead to one point in the second circle, and one other intersection point - let is_intersection = |point_num| { graph_path.edges_for_point(point_num).collect::>().len() > 1 }; - - assert!(intersection_edges.iter().any(|edge| !is_intersection(edge.end_point_index()))); - assert!(intersection_edges.iter().any(|edge| is_intersection(edge.end_point_index()))); + let is_intersection = |point_num| { + graph_path + .edges_for_point(point_num) + .collect::>() + .len() + > 1 + }; + + assert!(intersection_edges + .iter() + .any(|edge| !is_intersection(edge.end_point_index()))); + assert!(intersection_edges + .iter() + .any(|edge| is_intersection(edge.end_point_index()))); // The following intersection point should have one point that leads back into our path - let following_intersection = intersection_edges.iter().filter(|edge| is_intersection(edge.end_point_index())).nth(0).unwrap(); - let second_intersection_edges = graph_path.edges_for_point(following_intersection.end_point_index()).collect::>(); + let following_intersection = intersection_edges + .iter() + .filter(|edge| is_intersection(edge.end_point_index())) + .nth(0) + .unwrap(); + let second_intersection_edges = graph_path + .edges_for_point(following_intersection.end_point_index()) + .collect::>(); - assert!(second_intersection_edges.iter().any(|edge| edge.end_point_index() <= 3)); + assert!(second_intersection_edges + .iter() + .any(|edge| edge.end_point_index() <= 3)); // It should also have a point that leads back to the first intersection, forming a loop - assert!(second_intersection_edges.iter().any(|edge| edge.end_point_index() == intersection_point)); + assert!(second_intersection_edges + .iter() + .any(|edge| edge.end_point_index() == intersection_point)); } #[test] @@ -1037,7 +1209,8 @@ fn self_collide_simple_path() { assert!(with_interior_point.num_points() == 7); // One intersection - let num_intersections = (0..(with_interior_point.num_points())).into_iter() + let num_intersections = (0..(with_interior_point.num_points())) + .into_iter() .filter(|point_idx| with_interior_point.edges_for_point(*point_idx).count() > 1) .count(); assert!(num_intersections == 1); @@ -1066,14 +1239,35 @@ fn collide_at_shared_point() { let graph = graph.collide(GraphPath::from_path(&rectangle2, ()), 0.01); // Should be two points at 3.0, 5.0 with only one having any edges - let edges_at_shared = graph.all_edges().filter(|edge| edge.start_point().distance_to(&Coord2(3.0, 5.0)) < 0.1).collect::>(); + let edges_at_shared = graph + .all_edges() + .filter(|edge| edge.start_point().distance_to(&Coord2(3.0, 5.0)) < 0.1) + .collect::>(); assert!(edges_at_shared.len() == 2); assert!(edges_at_shared[0].start_point_index() == edges_at_shared[1].start_point_index()); - assert!(edges_at_shared[0].end_point().distance_to(&Coord2(1.0, 5.0)) < 0.1); - assert!(edges_at_shared[1].end_point().distance_to(&Coord2(3.0, 3.0)) < 0.1); - - let points_at_shared = (0..(graph.num_points())).into_iter().filter(|point_idx| graph.point_position(*point_idx).distance_to(&Coord2(3.0, 5.0)) < 0.01).collect::>(); + assert!( + edges_at_shared[0] + .end_point() + .distance_to(&Coord2(1.0, 5.0)) + < 0.1 + ); + assert!( + edges_at_shared[1] + .end_point() + .distance_to(&Coord2(3.0, 3.0)) + < 0.1 + ); + + let points_at_shared = (0..(graph.num_points())) + .into_iter() + .filter(|point_idx| { + graph + .point_position(*point_idx) + .distance_to(&Coord2(3.0, 5.0)) + < 0.01 + }) + .collect::>(); assert!(points_at_shared.len() == 2); } @@ -1086,7 +1280,7 @@ pub fn collide_rectangle_with_self() { .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(1.0, 1.0)) .build(); - + let rectangle1 = GraphPath::from_path(&rectangle, 1); let rectangle2 = GraphPath::from_path(&rectangle, 2); @@ -1103,7 +1297,9 @@ pub fn collide_rectangle_with_self() { let num_edges = collision.edges_for_point(point_idx).count(); assert!(num_edges == 2 || num_edges == 0); - if num_edges != 0 { num_connected_points += 1 } + if num_edges != 0 { + num_connected_points += 1 + } } assert!(num_connected_points == 4); @@ -1122,8 +1318,8 @@ fn ray_collide_along_convex_edge() { // Collide along the vertical seam of this graph let gp = GraphPath::from_path(&rectangle1, PathLabel(0, PathDirection::Clockwise)); - let collisions_seam = gp.ray_collisions(&(Coord2(5.0, 0.0), Coord2(5.0, 5.0))); - let collisions_no_seam = gp.ray_collisions(&(Coord2(4.9, 0.0), Coord2(4.9, 5.0))); + let collisions_seam = gp.ray_collisions(&(Coord2(5.0, 0.0), Coord2(5.0, 5.0))); + let collisions_no_seam = gp.ray_collisions(&(Coord2(4.9, 0.0), Coord2(4.9, 5.0))); assert!(collisions_no_seam.len() == 2); @@ -1147,8 +1343,8 @@ fn ray_collide_along_concave_edge() { // Collide along the vertical seam of this graph let gp = GraphPath::from_path(&concave_shape, PathLabel(0, PathDirection::Clockwise)); - let collisions_seam = gp.ray_collisions(&(Coord2(5.0, 0.0), Coord2(5.0, 5.0))); - let collisions_no_seam = gp.ray_collisions(&(Coord2(4.9, 0.0), Coord2(4.9, 5.0))); + let collisions_seam = gp.ray_collisions(&(Coord2(5.0, 0.0), Coord2(5.0, 5.0))); + let collisions_no_seam = gp.ray_collisions(&(Coord2(4.9, 0.0), Coord2(4.9, 5.0))); assert!(collisions_no_seam.len() == 2); @@ -1175,12 +1371,15 @@ fn ray_collide_along_seam_with_intersection() { .build(); // Collide along the vertical seam of this graph - let gp = GraphPath::from_path(&rectangle1, PathLabel(0, PathDirection::Clockwise)).collide(GraphPath::from_path(&rectangle2, PathLabel(1, PathDirection::Clockwise)), 0.01); + let gp = GraphPath::from_path(&rectangle1, PathLabel(0, PathDirection::Clockwise)).collide( + GraphPath::from_path(&rectangle2, PathLabel(1, PathDirection::Clockwise)), + 0.01, + ); println!("{:?}", gp); - let collisions_seam = gp.ray_collisions(&(Coord2(5.0, 0.0), Coord2(5.0, 5.0))); - let collisions_no_seam = gp.ray_collisions(&(Coord2(5.1, 0.0), Coord2(5.1, 5.0))); + let collisions_seam = gp.ray_collisions(&(Coord2(5.0, 0.0), Coord2(5.0, 5.0))); + let collisions_no_seam = gp.ray_collisions(&(Coord2(5.1, 0.0), Coord2(5.1, 5.0))); // Should collide with the line crossing the intersection, and the top line (so two collisions total) assert!(collisions_no_seam.len() == 2); @@ -1188,12 +1387,12 @@ fn ray_collide_along_seam_with_intersection() { assert!(collisions_seam.len() != 4); assert!(collisions_seam.len() != 3); assert!(collisions_seam.len() != 1); - assert!(collisions_seam.len()&1 == 0); + assert!(collisions_seam.len() & 1 == 0); assert!(collisions_seam.len() == 2); let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0)); - let first_collision = ray.point_at_pos(collisions_seam[0].2); - let second_collision = ray.point_at_pos(collisions_seam[1].2); + let first_collision = ray.point_at_pos(collisions_seam[0].2); + let second_collision = ray.point_at_pos(collisions_seam[1].2); println!("{:?} {:?}", first_collision, second_collision); @@ -1253,56 +1452,59 @@ fn ray_collide_with_edges_and_convex_point_intersection() { #[test] fn ray_collide_doughnuts_near_intersection() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); - let mut circle1 = GraphPath::from_path(&circle1, ()); - circle1 = circle1.merge(GraphPath::from_path(&inner_circle1, ())); - let mut circle2 = GraphPath::from_path(&circle2, ()); - circle2 = circle2.merge(GraphPath::from_path(&inner_circle2, ())); + let mut circle1 = GraphPath::from_path(&circle1, ()); + circle1 = circle1.merge(GraphPath::from_path(&inner_circle1, ())); + let mut circle2 = GraphPath::from_path(&circle2, ()); + circle2 = circle2.merge(GraphPath::from_path(&inner_circle2, ())); - let graph_path = circle1.collide(circle2, 0.1); + let graph_path = circle1.collide(circle2, 0.1); - let collisions = graph_path.ray_collisions(&(Coord2(7.000584357101389, 8.342524209216537), Coord2(6.941479643691172, 8.441210096108172))); + let collisions = graph_path.ray_collisions(&( + Coord2(7.000584357101389, 8.342524209216537), + Coord2(6.941479643691172, 8.441210096108172), + )); let collision_count = collisions.len(); println!("{:?}", collisions); - assert!((collision_count&1) == 0); + assert!((collision_count & 1) == 0); } #[test] fn ray_collide_doughnuts_many_angles() { - let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); - let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); - let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); + let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); + let inner_circle1 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); + let circle2 = Circle::new(Coord2(9.0, 5.0), 4.0).to_path::(); + let inner_circle2 = Circle::new(Coord2(9.0, 5.0), 3.9).to_path::(); - let mut circle1 = GraphPath::from_path(&circle1, ()); - circle1 = circle1.merge(GraphPath::from_path(&inner_circle1, ())); - let mut circle2 = GraphPath::from_path(&circle2, ()); - circle2 = circle2.merge(GraphPath::from_path(&inner_circle2, ())); + let mut circle1 = GraphPath::from_path(&circle1, ()); + circle1 = circle1.merge(GraphPath::from_path(&inner_circle1, ())); + let mut circle2 = GraphPath::from_path(&circle2, ()); + circle2 = circle2.merge(GraphPath::from_path(&inner_circle2, ())); - let graph_path = circle1.collide(circle2, 0.01); + let graph_path = circle1.collide(circle2, 0.01); for angle in 0..3600 { - let angle = (angle as f64)/3600.0; - let angle = (angle/360.0) * 2.0*f64::consts::PI; - let ray_start = Coord2(9.0, 5.0) + Coord2(5.0*angle.sin(), 5.0*angle.cos()); - let ray_end = Coord2(5.0, 5.0) - Coord2(5.0*angle.sin(), 5.0*angle.cos()); + let angle = (angle as f64) / 3600.0; + let angle = (angle / 360.0) * 2.0 * f64::consts::PI; + let ray_start = Coord2(9.0, 5.0) + Coord2(5.0 * angle.sin(), 5.0 * angle.cos()); + let ray_end = Coord2(5.0, 5.0) - Coord2(5.0 * angle.sin(), 5.0 * angle.cos()); - let collisions = graph_path.ray_collisions(&(ray_start, ray_end)); + let collisions = graph_path.ray_collisions(&(ray_start, ray_end)); let collision_count = collisions.len(); - assert!((collision_count&1) == 0); + assert!((collision_count & 1) == 0); } } #[test] fn self_collide_removes_shared_point_1() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(5.0, 5.0)) @@ -1311,7 +1513,7 @@ fn self_collide_removes_shared_point_1() { .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); + let mut graph_path = GraphPath::from_path(&path, ()); graph_path.self_collide(0.01); let mut edges_ending_at_center = vec![]; @@ -1322,12 +1524,14 @@ fn self_collide_removes_shared_point_1() { } assert!(edges_ending_at_center.len() == 2); - assert!(edges_ending_at_center.iter().all(|edge| edge.end_point_index() == edges_ending_at_center[0].end_point_index())); + assert!(edges_ending_at_center + .iter() + .all(|edge| edge.end_point_index() == edges_ending_at_center[0].end_point_index())); } #[test] fn self_collide_removes_shared_point_2() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(5.0, 1.0)) @@ -1336,7 +1540,7 @@ fn self_collide_removes_shared_point_2() { .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); + let mut graph_path = GraphPath::from_path(&path, ()); graph_path.self_collide(0.01); let mut edges_ending_at_center = vec![]; @@ -1347,12 +1551,14 @@ fn self_collide_removes_shared_point_2() { } assert!(edges_ending_at_center.len() == 2); - assert!(edges_ending_at_center.iter().all(|edge| edge.end_point_index() == edges_ending_at_center[0].end_point_index())); + assert!(edges_ending_at_center + .iter() + .all(|edge| edge.end_point_index() == edges_ending_at_center[0].end_point_index())); } #[test] fn self_collide_divides_lines_1() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(3.0, 0.0)) .line_to(Coord2(5.0, 5.0)) @@ -1360,7 +1566,7 @@ fn self_collide_divides_lines_1() { .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); + let mut graph_path = GraphPath::from_path(&path, ()); assert!(graph_path.all_edges().count() == 5); @@ -1371,7 +1577,7 @@ fn self_collide_divides_lines_1() { #[test] fn self_collide_divides_lines_2() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(3.0, 0.985)) .line_to(Coord2(5.0, 5.0)) @@ -1379,7 +1585,7 @@ fn self_collide_divides_lines_2() { .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); + let mut graph_path = GraphPath::from_path(&path, ()); assert!(graph_path.all_edges().count() == 5); @@ -1391,7 +1597,7 @@ fn self_collide_divides_lines_2() { #[test] fn self_collide_divides_lines_3() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(3.0, 0.999)) .line_to(Coord2(5.0, 5.0)) @@ -1399,7 +1605,7 @@ fn self_collide_divides_lines_3() { .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); + let mut graph_path = GraphPath::from_path(&path, ()); assert!(graph_path.all_edges().count() == 5); @@ -1408,23 +1614,26 @@ fn self_collide_divides_lines_3() { println!("{:?}", graph_path); // So close we should just collide as if the 3.0, 0.999 point is at 3.0, 1.0 - assert!(!graph_path.all_edges().any(|edge| edge.start_point().distance_to(&edge.end_point()) < 0.001)); - assert!(graph_path.all_edges().count() != 9); // Technically valid, indicates a change in the precision of the collision + assert!(!graph_path + .all_edges() + .any(|edge| edge.start_point().distance_to(&edge.end_point()) < 0.001)); + assert!(graph_path.all_edges().count() != 9); // Technically valid, indicates a change in the precision of the collision assert!(graph_path.all_edges().count() != 7); assert!(graph_path.all_edges().count() == 6); } #[test] fn heal_one_line_gap() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(5.0, 5.0)) .line_to(Coord2(5.0, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); - let edges = (0..4).into_iter() + let mut graph_path = GraphPath::from_path(&path, ()); + let edges = (0..4) + .into_iter() .map(|point_idx| graph_path.edges_for_point(point_idx).nth(0).unwrap().into()) .collect::>(); @@ -1439,18 +1648,18 @@ fn heal_one_line_gap() { assert!(graph_path.get_edge(edges[1]).kind() == GraphPathEdgeKind::Exterior); } - #[test] fn heal_two_line_gap() { - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(5.0, 5.0)) .line_to(Coord2(5.0, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let mut graph_path = GraphPath::from_path(&path, ()); - let edges = (0..4).into_iter() + let mut graph_path = GraphPath::from_path(&path, ()); + let edges = (0..4) + .into_iter() .map(|point_idx| graph_path.edges_for_point(point_idx).nth(0).unwrap().into()) .collect::>(); @@ -1510,9 +1719,10 @@ fn ray_cast_at_tiny_line_2() { // Should be able to cast a ray and hit our line and none of the others for p in 0..10 { - let offset = ((p as f64)/10.0) * 0.01; + let offset = ((p as f64) / 10.0) * 0.01; - let collisions = path.ray_collisions(&(Coord2(2.9945 + offset, 0.0), Coord2(2.9945+offset, 1.0))); + let collisions = + path.ray_collisions(&(Coord2(2.9945 + offset, 0.0), Coord2(2.9945 + offset, 1.0))); println!("{:?}", collisions); assert!(collisions.len() == 2); @@ -1627,18 +1837,39 @@ fn ray_cast_at_tiny_line_6() { // Path with a pair of lines with a known failure on them let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(525.2388916015625, 931.7135009765625)) - .curve_to((Coord2(525.4012451171875, 931.7196044921875), Coord2(525.5686645507813, 931.7201538085938)), Coord2(525.7626342773438, 931.6915893554688)) - .curve_to((Coord2(526.2460327148438, 931.761962890625), Coord2(526.3161010742188, 931.8532104492188)), Coord2(526.6378173828125, 931.9375610351563)) - .curve_to((Coord2(529.997314453125, 935.0886840820313), Coord2(508.8724365234375, 903.5847778320313)), Coord2(508.7933654785156, 901.745849609375)) + .curve_to( + ( + Coord2(525.4012451171875, 931.7196044921875), + Coord2(525.5686645507813, 931.7201538085938), + ), + Coord2(525.7626342773438, 931.6915893554688), + ) + .curve_to( + ( + Coord2(526.2460327148438, 931.761962890625), + Coord2(526.3161010742188, 931.8532104492188), + ), + Coord2(526.6378173828125, 931.9375610351563), + ) + .curve_to( + ( + Coord2(529.997314453125, 935.0886840820313), + Coord2(508.8724365234375, 903.5847778320313), + ), + Coord2(508.7933654785156, 901.745849609375), + ) .line_to(Coord2(700.0, 900.0)) .line_to(Coord2(1.0, 900.0)) .line_to(Coord2(1.0, 1.0)) .build(); let path = GraphPath::from_path(&path, ()); - let collisions = path.ray_collisions(&(Coord2(543.606689453125, 925.3496704101563), Coord2(553.524658203125, 921.505126953125))); + let collisions = path.ray_collisions(&( + Coord2(543.606689453125, 925.3496704101563), + Coord2(553.524658203125, 921.505126953125), + )); println!("{:?}", collisions); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4); } @@ -1656,7 +1887,7 @@ fn ray_cast_grazing_circle_produces_0_hits() { let collisions = path.ray_collisions(&(Coord2(24.0, 0.0), Coord2(24.0, 1.0))); // Should not actually hit the circle - assert!(collisions.len() != 2); // 2 collisions would produce no bug + assert!(collisions.len() != 2); // 2 collisions would produce no bug assert!(collisions.len() != 1); assert!(collisions.len() == 0); } @@ -1683,36 +1914,36 @@ fn ray_cast_close_to_circle_produces_2_hits() { #[test] pub fn ray_cast_identical_rectangles() { // Create the two rectangles - let rectangle1 = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let rectangle1 = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(5.0, 1.0)) .line_to(Coord2(5.0, 5.0)) .line_to(Coord2(1.0, 5.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let rectangle2 = rectangle1.clone(); - - let rectangle1 = GraphPath::from_path(&rectangle1, 1); - let rectangle2 = GraphPath::from_path(&rectangle2, 2); + let rectangle2 = rectangle1.clone(); + + let rectangle1 = GraphPath::from_path(&rectangle1, 1); + let rectangle2 = GraphPath::from_path(&rectangle2, 2); // Collide them - let path = rectangle1.collide(rectangle2, 0.1); + let path = rectangle1.collide(rectangle2, 0.1); // The edges are identical, so we need to process them in a consistent order - let collisions = path.ray_collisions(&(Coord2(3.0, 0.0), Coord2(3.0, 10.0))); + let collisions = path.ray_collisions(&(Coord2(3.0, 0.0), Coord2(3.0, 10.0))); // Collides with two edges twice, so four total collisions assert!(collisions.len() == 4); // First two collisions should hit path 1 and path 2 - let edge1 = collisions[0].0.edge(); - let edge2 = collisions[1].0.edge(); - let edge3 = collisions[2].0.edge(); - let edge4 = collisions[3].0.edge(); - - let edge1 = path.get_edge(edge1); - let edge2 = path.get_edge(edge2); - let edge3 = path.get_edge(edge3); - let edge4 = path.get_edge(edge4); + let edge1 = collisions[0].0.edge(); + let edge2 = collisions[1].0.edge(); + let edge3 = collisions[2].0.edge(); + let edge4 = collisions[3].0.edge(); + + let edge1 = path.get_edge(edge1); + let edge2 = path.get_edge(edge2); + let edge3 = path.get_edge(edge3); + let edge4 = path.get_edge(edge4); // edge1, edge2 and edge3, edge4 should all have the same start and end points (ie, be duplicate edges) assert!(edge1.start_point_index() == edge2.start_point_index()); diff --git a/tests/bezier/path/intersection.rs b/tests/bezier/path/intersection.rs index d30a1ba3..bd9d061a 100644 --- a/tests/bezier/path/intersection.rs +++ b/tests/bezier/path/intersection.rs @@ -1,7 +1,7 @@ -use flo_curves::*; use flo_curves::arc::*; -use flo_curves::bezier::*; use flo_curves::bezier::path::*; +use flo_curves::bezier::*; +use flo_curves::*; use std::f64; @@ -14,22 +14,22 @@ fn awkward_line_intersects_circle() { let circle: SimpleBezierPath = Circle::new(center, radius).to_path(); // Line from the center to the edge - let line = (Coord2(5.0, 5.0), Coord2(5.0, 9.5)); - let intersection = path_intersects_line(&circle, &line).collect::>(); + let line = (Coord2(5.0, 5.0), Coord2(5.0, 9.5)); + let intersection = path_intersects_line(&circle, &line).collect::>(); // This should be an intersection (straight up from the center) assert!(intersection.len() == 1); // Line from the center to the edge - let line = (Coord2(5.0, 5.0), Coord2(5.0, 0.5)); - let intersection = path_intersects_line(&circle, &line).collect::>(); + let line = (Coord2(5.0, 5.0), Coord2(5.0, 0.5)); + let intersection = path_intersects_line(&circle, &line).collect::>(); // This should be an intersection (straight down from the center) assert!(intersection.len() == 1); // Line from the center to the edge - let line = (Coord2(5.0, 5.0), Coord2(4.999999999999999, 9.5)); - let intersection = path_intersects_line(&circle, &line).collect::>(); + let line = (Coord2(5.0, 5.0), Coord2(4.999999999999999, 9.5)); + let intersection = path_intersects_line(&circle, &line).collect::>(); // This should be an intersection (almost straight up from the center) assert!(intersection.len() == 1); @@ -49,22 +49,22 @@ fn line_intersects_circle() { let circle_sections = circle.to_curves::>(); for angle in 0..=20 { - let angle = angle as f64; - let radians = (2.0*f64::consts::PI)*(angle/20.0); + let angle = angle as f64; + let radians = (2.0 * f64::consts::PI) * (angle / 20.0); - let target = Coord2(radians.sin()*length, radians.cos()*length); - let target = target + center; + let target = Coord2(radians.sin() * length, radians.cos() * length); + let target = target + center; - let expected = Coord2(radians.sin()*radius, radians.cos()*radius); - let expected = expected + center; + let expected = Coord2(radians.sin() * radius, radians.cos() * radius); + let expected = expected + center; // Should be one intersection with the circle here - let line = (center, target); - let intersection = path_intersects_line(&circle, &line).collect::>(); + let line = (center, target); + let intersection = path_intersects_line(&circle, &line).collect::>(); assert!(intersection.len() == 1); if intersection.len() > 0 { - let intersection = intersection[0]; + let intersection = intersection[0]; let intersect_point = circle_sections[intersection.0].point_at_pos(intersection.1); assert!(expected.distance_to(&intersect_point).abs() < 0.01); @@ -84,15 +84,15 @@ fn line_does_not_intersect_circle() { let length = 3.9999; for angle in 0..=20 { - let angle = angle as f64; - let radians = (2.0*f64::consts::PI)*(angle/20.0); + let angle = angle as f64; + let radians = (2.0 * f64::consts::PI) * (angle / 20.0); - let target = Coord2(radians.sin()*length, radians.cos()*length); - let target = target + center; + let target = Coord2(radians.sin() * length, radians.cos() * length); + let target = target + center; // Should be one intersection with the circle here - let line = (center, target); - let intersection = path_intersects_line(&circle, &line).collect::>(); + let line = (center, target); + let intersection = path_intersects_line(&circle, &line).collect::>(); assert!(intersection.len() == 0); } } @@ -122,7 +122,12 @@ fn circle_intersects_circle() { let point1 = curves1[*index1].point_at_pos(*t1); let point2 = curves2[*index2].point_at_pos(*t2); - println!("{:?} {:?} {:?}", point1, point2, point1.distance_to(&point2)); + println!( + "{:?} {:?} {:?}", + point1, + point2, + point1.distance_to(&point2) + ); } for ((index1, t1), (index2, t2)) in intersections.iter() { diff --git a/tests/bezier/path/is_clockwise.rs b/tests/bezier/path/is_clockwise.rs index 62309163..9644a97a 100644 --- a/tests/bezier/path/is_clockwise.rs +++ b/tests/bezier/path/is_clockwise.rs @@ -1,5 +1,5 @@ -use flo_curves::*; use flo_curves::bezier::path::*; +use flo_curves::*; #[test] pub fn rectangle_is_clockwise() { diff --git a/tests/bezier/path/mod.rs b/tests/bezier/path/mod.rs index a73730f8..9ff6ea6b 100644 --- a/tests/bezier/path/mod.rs +++ b/tests/bezier/path/mod.rs @@ -1,15 +1,15 @@ -mod svg; -mod to_curves; -mod point; -mod path; -mod intersection; -mod bounds; -mod graph_path; -mod is_clockwise; mod arithmetic_add; mod arithmetic_chain_add; -mod arithmetic_sub; +mod arithmetic_complicated_paths; mod arithmetic_cut; mod arithmetic_intersect; -mod arithmetic_complicated_paths; +mod arithmetic_sub; +mod bounds; +mod graph_path; +mod intersection; +mod is_clockwise; +mod path; +mod point; mod rays; +mod svg; +mod to_curves; diff --git a/tests/bezier/path/path.rs b/tests/bezier/path/path.rs index d3a8f98a..695e972c 100644 --- a/tests/bezier/path/path.rs +++ b/tests/bezier/path/path.rs @@ -1,5 +1,5 @@ -use flo_curves::*; use flo_curves::bezier::path::*; +use flo_curves::*; #[test] fn reverse_rectangle() { diff --git a/tests/bezier/path/point.rs b/tests/bezier/path/point.rs index b01c4675..3a515140 100644 --- a/tests/bezier/path/point.rs +++ b/tests/bezier/path/point.rs @@ -1,16 +1,19 @@ -use flo_curves::*; use flo_curves::arc::*; use flo_curves::bezier::path::*; +use flo_curves::*; #[test] fn simple_path_contains_point() { // Path is a square - let path = (Coord2(1.0, 2.0), vec![ - (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), - (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), - (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), - (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)) - ]); + let path = ( + Coord2(1.0, 2.0), + vec![ + (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), + (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), + (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), + (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)), + ], + ); // Point should be inside assert!(path_contains_point(&path, &Coord2(5.0, 5.0))); @@ -56,12 +59,15 @@ fn circle_edge_is_inside() { #[test] fn point_on_edge_is_not_in_path() { // Path is a square - let path = (Coord2(1.0, 2.0), vec![ - (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), - (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), - (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), - (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)) - ]); + let path = ( + Coord2(1.0, 2.0), + vec![ + (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), + (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), + (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), + (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)), + ], + ); // Points just on the boundary should be outside of the path assert!(!path_contains_point(&path, &Coord2(5.0, 2.0))); @@ -71,12 +77,15 @@ fn point_on_edge_is_not_in_path() { #[test] fn corner_is_in_path() { // Path is a square - let path = (Coord2(1.0, 2.0), vec![ - (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), - (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), - (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), - (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)) - ]); + let path = ( + Coord2(1.0, 2.0), + vec![ + (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), + (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), + (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), + (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)), + ], + ); // Points right on the edge but on the corners are in the path assert!(path_contains_point(&path, &Coord2(1.001, 2.001))); @@ -88,12 +97,15 @@ fn corner_is_in_path() { #[test] fn points_outside_bounds_are_outside_path() { // Path is a square - let path = (Coord2(1.0, 2.0), vec![ - (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), - (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), - (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), - (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)) - ]); + let path = ( + Coord2(1.0, 2.0), + vec![ + (Coord2(3.0, 2.0), Coord2(6.0, 2.0), Coord2(9.0, 2.0)), + (Coord2(9.0, 4.0), Coord2(9.0, 6.0), Coord2(9.0, 8.0)), + (Coord2(6.0, 8.0), Coord2(3.0, 8.0), Coord2(1.0, 8.0)), + (Coord2(1.0, 6.0), Coord2(1.0, 4.0), Coord2(1.0, 2.0)), + ], + ); // Points far outside the path should be outside assert!(!path_contains_point(&path, &Coord2(5.0, 20.0))); diff --git a/tests/bezier/path/rays.rs b/tests/bezier/path/rays.rs index e70fa5b8..b07e3a61 100644 --- a/tests/bezier/path/rays.rs +++ b/tests/bezier/path/rays.rs @@ -1,6 +1,6 @@ -use flo_curves::*; -use flo_curves::bezier::*; use flo_curves::bezier::path::*; +use flo_curves::bezier::*; +use flo_curves::*; use std::collections::HashMap; @@ -12,22 +12,22 @@ fn crossing_figure_of_8_intersection_from_inside() { // | + | <--- RAY // | / \ | // + + - // + // // This ray hits a corner but it should generate either 0 or 2 collisions at this point, and particularly not 1. - // (0 intersections implies the ray never leaves the shape and 2 intersections indicates it leaves and immediately + // (0 intersections implies the ray never leaves the shape and 2 intersections indicates it leaves and immediately // re-enters) - // + // // (Interestingly, either behaviour is correct: if there are 0 collisions we won't categorise the edges and if there // are 2 we'll mark the edges as exterior when the ray is used to set edge kinds) - // + // // This is interesting because of this case: - // + // // + // | \ // | + <--- RAY // | / // + - // + // // As the 'same' point here should always generate 1 intersection as the ray enters the shape at this point (or leaves // in the reverse direction) // @@ -49,25 +49,33 @@ fn crossing_figure_of_8_intersection_from_inside() { let collisions = graph_path.ray_collisions(&(Coord2(8.0, 2.0), Coord2(7.0, 2.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4); // The intersection point should be an actual intersection - assert!((0..(graph_path.num_points())).into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).count()) - .filter(|num_edges_for_point| num_edges_for_point == &2) - .count() == 1); - assert!((0..(graph_path.num_points())).into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).count()) - .filter(|num_edges_for_point| num_edges_for_point == &1) - .count() == 4); + assert!( + (0..(graph_path.num_points())) + .into_iter() + .map(|point_idx| graph_path.edges_for_point(point_idx).count()) + .filter(|num_edges_for_point| num_edges_for_point == &2) + .count() + == 1 + ); + assert!( + (0..(graph_path.num_points())) + .into_iter() + .map(|point_idx| graph_path.edges_for_point(point_idx).count()) + .filter(|num_edges_for_point| num_edges_for_point == &1) + .count() + == 4 + ); // Also test the ray travelling the other way let collisions = graph_path.ray_collisions(&(Coord2(-2.0, 2.0), Coord2(-1.0, 2.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4); } @@ -80,7 +88,7 @@ fn crossing_figure_of_8_intersection_from_inside_reversed() { // | + | <--- RAY // | / \ | // + + - // + // let left_triangle = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 3.0)) @@ -99,25 +107,33 @@ fn crossing_figure_of_8_intersection_from_inside_reversed() { let collisions = graph_path.ray_collisions(&(Coord2(8.0, 2.0), Coord2(7.0, 2.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4); // The intersection point should be an actual intersection - assert!((0..(graph_path.num_points())).into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).count()) - .filter(|num_edges_for_point| num_edges_for_point == &2) - .count() == 1); - assert!((0..(graph_path.num_points())).into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).count()) - .filter(|num_edges_for_point| num_edges_for_point == &1) - .count() == 4); + assert!( + (0..(graph_path.num_points())) + .into_iter() + .map(|point_idx| graph_path.edges_for_point(point_idx).count()) + .filter(|num_edges_for_point| num_edges_for_point == &2) + .count() + == 1 + ); + assert!( + (0..(graph_path.num_points())) + .into_iter() + .map(|point_idx| graph_path.edges_for_point(point_idx).count()) + .filter(|num_edges_for_point| num_edges_for_point == &1) + .count() + == 4 + ); // Also test the ray travelling the other way let collisions = graph_path.ray_collisions(&(Coord2(-2.0, 2.0), Coord2(-1.0, 2.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4); } @@ -130,7 +146,7 @@ fn crossing_figure_of_8_intersection_from_inside_nearby() { // | + | <--- RAY // | / \ | // + + - // + // let left_triangle = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 3.0)) @@ -146,11 +162,16 @@ fn crossing_figure_of_8_intersection_from_inside_nearby() { let graph_path = GraphPath::from_path(&left_triangle, ()); let graph_path = graph_path.collide(GraphPath::from_path(&right_triangle, ()), 0.01); - for y in [1.9, 1.99, 1.999, 1.9999, 1.99999, 1.99999, 2.1, 2.01, 2.001, 2.0001, 2.00001, 2.000001, 2.0000001].iter() { + for y in [ + 1.9, 1.99, 1.999, 1.9999, 1.99999, 1.99999, 2.1, 2.01, 2.001, 2.0001, 2.00001, 2.000001, + 2.0000001, + ] + .iter() + { let collisions = graph_path.ray_collisions(&(Coord2(8.0, *y), Coord2(7.0, *y))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4 || collisions.len() == 2); @@ -158,7 +179,7 @@ fn crossing_figure_of_8_intersection_from_inside_nearby() { let collisions = graph_path.ray_collisions(&(Coord2(-2.0, *y), Coord2(-1.0, *y))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 4); } @@ -176,7 +197,7 @@ fn crossing_figure_of_8_intersection_collinear() { // | + | // | / \ | // + + - // + // let left_triangle = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 3.0)) @@ -195,7 +216,7 @@ fn crossing_figure_of_8_intersection_collinear() { let collisions = graph_path.ray_collisions(&(Coord2(1.0, 1.0), Coord2(3.0, 2.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 0); } @@ -220,7 +241,7 @@ fn crossing_figure_of_8_intersection_from_outside() { let collisions = graph_path.ray_collisions(&(Coord2(3.0, 0.0), Coord2(3.0, 1.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 0 || collisions.len() == 2); } @@ -231,8 +252,8 @@ fn crossing_intersection_with_collinear_edge() { // + + // | \ / \ // | + ----- + <--- RAY - // | / - // + + // | / + // + // let left_triangle = BezierPathBuilder::::start(Coord2(1.0, 1.0)) @@ -252,20 +273,20 @@ fn crossing_intersection_with_collinear_edge() { let collisions = graph_path.ray_collisions(&(Coord2(8.0, 2.0), Coord2(7.0, 2.0))); assert!(collisions.len() != 3); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 2); } #[test] fn ray_entering_triangle_through_apex_1() { - // + // // + // | \ // | + <--- RAY // | / // + - // + // // As the 'same' point here should always generate 1 intersection as the ray enters the shape at this point (or leaves // in the reverse direction) // @@ -280,19 +301,19 @@ fn ray_entering_triangle_through_apex_1() { let collisions = graph_path.ray_collisions(&(Coord2(8.0, 2.0), Coord2(7.0, 2.0))); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 2); } #[test] fn ray_entering_triangle_through_apex_2() { - // + // // + // | \ // | + ---> RAY // | / // + - // + // // As the 'same' point here should always generate 1 intersection as the ray enters the shape at this point (or leaves // in the reverse direction) // @@ -307,19 +328,19 @@ fn ray_entering_triangle_through_apex_2() { let collisions = graph_path.ray_collisions(&(Coord2(0.0, 2.0), Coord2(1.0, 2.0))); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 2); } #[test] fn ray_entering_triangle_through_apex_3() { - // + // // + // | \ // | + ---> RAY // | / // + - // + // // As the 'same' point here should always generate 1 intersection as the ray enters the shape at this point (or leaves // in the reverse direction) // @@ -332,10 +353,15 @@ fn ray_entering_triangle_through_apex_3() { let graph_path = GraphPath::from_path(&left_triangle, ()); - for y in [1.9, 1.99, 1.999, 1.9999, 1.99999, 1.99999, 2.1, 2.01, 2.001, 2.0001, 2.00001, 2.000001, 2.0000001].iter() { + for y in [ + 1.9, 1.99, 1.999, 1.9999, 1.99999, 1.99999, 2.1, 2.01, 2.001, 2.0001, 2.00001, 2.000001, + 2.0000001, + ] + .iter() + { let collisions = graph_path.ray_collisions(&(Coord2(0.0, *y), Coord2(1.0, *y))); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 2); } } @@ -352,7 +378,7 @@ fn ray_hitting_tangent_at_point() { let collisions = graph_path.ray_collisions(&(Coord2(3.0, 0.0), Coord2(3.0, 1.0))); - assert!((collisions.len()&1) == 0); + assert!((collisions.len() & 1) == 0); assert!(collisions.len() == 0); } @@ -360,10 +386,34 @@ fn ray_hitting_tangent_at_point() { fn ray_hitting_intersection_bad() { // These three edges form an intersection that has a known bad intersection with the specified ray // edge2 here generates 2 collisions at the intersection for some reason, which seems to be what's causing a bug - let ray = (Coord2(614.1064453125, 904.1033935546875), Coord2(614.3379516601563, 903.910888671875)); - let edge1 = Curve::from_points(Coord2(612.35302734375, 902.1972045898438), (Coord2(611.9544677734375, 904.4937744140625), Coord2(612.1427001953125, 905.798828125)), Coord2(613.4901123046875, 904.6159057617188)); - let edge2 = Curve::from_points(Coord2(613.4901123046875, 904.6159057617188), (Coord2(613.6087646484375, 904.5118408203125), Coord2(613.736328125, 904.388427734375)), Coord2(613.873291015625, 904.2447509765625)); - let edge3 = Curve::from_points(Coord2(613.1998901367188, 904.267822265625), (Coord2(613.2864379882813, 904.4163818359375), Coord2(613.3829956054688, 904.5339965820313)), Coord2(613.4901123046875, 904.6159057617188)); + let ray = ( + Coord2(614.1064453125, 904.1033935546875), + Coord2(614.3379516601563, 903.910888671875), + ); + let edge1 = Curve::from_points( + Coord2(612.35302734375, 902.1972045898438), + ( + Coord2(611.9544677734375, 904.4937744140625), + Coord2(612.1427001953125, 905.798828125), + ), + Coord2(613.4901123046875, 904.6159057617188), + ); + let edge2 = Curve::from_points( + Coord2(613.4901123046875, 904.6159057617188), + ( + Coord2(613.6087646484375, 904.5118408203125), + Coord2(613.736328125, 904.388427734375), + ), + Coord2(613.873291015625, 904.2447509765625), + ); + let edge3 = Curve::from_points( + Coord2(613.1998901367188, 904.267822265625), + ( + Coord2(613.2864379882813, 904.4163818359375), + Coord2(613.3829956054688, 904.5339965820313), + ), + Coord2(613.4901123046875, 904.6159057617188), + ); let ray1 = curve_intersects_ray(&edge1, &ray); let ray2 = curve_intersects_ray(&edge2, &ray); @@ -394,18 +444,18 @@ fn ray_hitting_start_and_end_of_line_1() { // ^ // | // Ray - // - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + // + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(2.0, 3.0)) .line_to(Coord2(2.0, 2.0)) .line_to(Coord2(1.0, 2.0)) .build(); - let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); - let graph_path = GraphPath::from_path(&path, ()); + let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); + let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let collisions = graph_path.ray_collisions(&ray); assert!(collisions.len() == 2); } @@ -423,78 +473,78 @@ fn ray_hitting_start_and_end_of_line_2() { // ^ // | // Ray - // - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + // + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(2.0, 3.0)) .line_to(Coord2(2.001, 2.0)) .line_to(Coord2(1.0, 2.0)) .build(); - let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); - let graph_path = GraphPath::from_path(&path, ()); + let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); + let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let collisions = graph_path.ray_collisions(&ray); println!("{:?}", collisions); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); assert!(collisions.len() == 2); } #[test] fn ray_hitting_start_and_end_of_line_3() { // As above but crossing even closer - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(2.0, 3.0)) .line_to(Coord2(2.000999, 2.0)) .line_to(Coord2(1.0, 2.0)) .build(); - let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); - let graph_path = GraphPath::from_path(&path, ()); + let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); + let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let collisions = graph_path.ray_collisions(&ray); println!("{:?}", collisions); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); assert!(collisions.len() == 2); } #[test] fn ray_hitting_start_and_end_of_line_4() { // Moving one of the other points - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(1.999, 3.0)) .line_to(Coord2(2.0, 2.0)) .line_to(Coord2(1.0, 2.0)) .build(); - let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); - let graph_path = GraphPath::from_path(&path, ()); + let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); + let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let collisions = graph_path.ray_collisions(&ray); println!("{:?}", collisions); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); assert!(collisions.len() == 2); } #[test] fn ray_hitting_start_and_end_of_curve_1() { // As above, but curve bowing outwards - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(2.0, 3.0)) .curve_to((Coord2(1.0, 3.0), Coord2(1.0, 2.0)), Coord2(2.0, 2.0)) .line_to(Coord2(1.0, 2.0)) .build(); - let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); - let graph_path = GraphPath::from_path(&path, ()); + let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); + let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let collisions = graph_path.ray_collisions(&ray); assert!(collisions.len() == 2); } @@ -502,17 +552,17 @@ fn ray_hitting_start_and_end_of_curve_1() { #[test] fn ray_hitting_start_and_end_of_curve_2() { // As above, but curve bowing inwards - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(3.0, 1.0)) .line_to(Coord2(3.0, 3.0)) .line_to(Coord2(2.0, 3.0)) .curve_to((Coord2(3.0, 3.0), Coord2(3.0, 2.0)), Coord2(2.0, 2.0)) .line_to(Coord2(1.0, 2.0)) .build(); - let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); - let graph_path = GraphPath::from_path(&path, ()); + let ray = (Coord2(2.0, 0.0), Coord2(2.0, 1.0)); + let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let collisions = graph_path.ray_collisions(&ray); assert!(collisions.len() == 2); } @@ -521,82 +571,147 @@ fn ray_hitting_start_and_end_of_curve_2() { fn ray_crossing_and_glancing() { // This is a ray that scores a crossing collision followed by a glancing collision along a very short curve that is almost but not quite // collinear. We should remove the glancing collision but we do not for some reason - let ray = (Coord2(694.1428833007813, 884.8551025390625), Coord2(692.2153930664063, 883.697509765625)); - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let ray = ( + Coord2(694.1428833007813, 884.8551025390625), + Coord2(692.2153930664063, 883.697509765625), + ); + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(632.3152465820313, 838.1866455078125)) - .curve_to((Coord2(634.3826904296875, 844.4097290039063), Coord2(635.91357421875, 849.9752807617188)), Coord2(635.8788452148438, 849.8710327148438)) - .curve_to((Coord2(635.9103393554688, 849.8935546875), Coord2(635.9293212890625, 849.9025268554688)), Coord2(635.9400634765625, 849.9002075195313)) - .curve_to((Coord2(635.97216796875, 850.158447265625), Coord2(635.9955444335938, 850.357666015625)), Coord2(636.0320434570313, 850.4244384765625)) + .curve_to( + ( + Coord2(634.3826904296875, 844.4097290039063), + Coord2(635.91357421875, 849.9752807617188), + ), + Coord2(635.8788452148438, 849.8710327148438), + ) + .curve_to( + ( + Coord2(635.9103393554688, 849.8935546875), + Coord2(635.9293212890625, 849.9025268554688), + ), + Coord2(635.9400634765625, 849.9002075195313), + ) + .curve_to( + ( + Coord2(635.97216796875, 850.158447265625), + Coord2(635.9955444335938, 850.357666015625), + ), + Coord2(636.0320434570313, 850.4244384765625), + ) .line_to(Coord2(635.97216796875, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let graph_path = GraphPath::from_path(&path, ()); + let collisions = graph_path.ray_collisions(&ray); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); } #[test] fn ray_glancing_1() { - let ray = (Coord2(798.2357788085938, 783.7974853515625), Coord2(798.6553344726563, 782.6351928710938)); - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let ray = ( + Coord2(798.2357788085938, 783.7974853515625), + Coord2(798.6553344726563, 782.6351928710938), + ); + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 677.4369506835938)) .line_to(Coord2(839.3995361328125, 677.4369506835938)) - .curve_to((Coord2(839.6207275390625, 674.3713989257813), Coord2(838.3349609375, 674.1100463867188)), Coord2(837.8158569335938, 674.1475830078125)) - .curve_to((Coord2(838.1270141601563, 674.0364990234375), Coord2(838.5419311523438, 673.8883056640625)), Coord2(838.8530883789063, 673.7772216796875)) + .curve_to( + ( + Coord2(839.6207275390625, 674.3713989257813), + Coord2(838.3349609375, 674.1100463867188), + ), + Coord2(837.8158569335938, 674.1475830078125), + ) + .curve_to( + ( + Coord2(838.1270141601563, 674.0364990234375), + Coord2(838.5419311523438, 673.8883056640625), + ), + Coord2(838.8530883789063, 673.7772216796875), + ) .line_to(Coord2(838.8530883789063, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let graph_path = GraphPath::from_path(&path, ()); + let collisions = graph_path.ray_collisions(&ray); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); } #[test] fn ray_glancing_1_reversed() { - let ray = (Coord2(798.2357788085938, 783.7974853515625), Coord2(798.6553344726563, 782.6351928710938)); - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let ray = ( + Coord2(798.2357788085938, 783.7974853515625), + Coord2(798.6553344726563, 782.6351928710938), + ); + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 677.4369506835938)) .line_to(Coord2(839.3995361328125, 677.4369506835938)) - .curve_to((Coord2(839.6207275390625, 674.3713989257813), Coord2(838.3349609375, 674.1100463867188)), Coord2(837.8158569335938, 674.1475830078125)) - .curve_to((Coord2(838.1270141601563, 674.0364990234375), Coord2(838.5419311523438, 673.8883056640625)), Coord2(838.8530883789063, 673.7772216796875)) + .curve_to( + ( + Coord2(839.6207275390625, 674.3713989257813), + Coord2(838.3349609375, 674.1100463867188), + ), + Coord2(837.8158569335938, 674.1475830078125), + ) + .curve_to( + ( + Coord2(838.1270141601563, 674.0364990234375), + Coord2(838.5419311523438, 673.8883056640625), + ), + Coord2(838.8530883789063, 673.7772216796875), + ) .line_to(Coord2(838.8530883789063, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let path = path.reversed::(); + let path = path.reversed::(); - let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let graph_path = GraphPath::from_path(&path, ()); + let collisions = graph_path.ray_collisions(&ray); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); } #[test] fn ray_glancing_2() { - let ray = (Coord2(576.3092041015625, 854.6082153320313), Coord2(576.0201416015625, 854.5975341796875)); - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let ray = ( + Coord2(576.3092041015625, 854.6082153320313), + Coord2(576.0201416015625, 854.5975341796875), + ); + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 854.7772216796875)) .line_to(Coord2(580.010986328125, 854.7772216796875)) - .curve_to((Coord2(580.0106201171875, 854.7659301757813), Coord2(580.01025390625, 854.7548828125)), Coord2(580.0098266601563, 854.7442016601563)) - .curve_to((Coord2(580.0198974609375, 854.7529296875), Coord2(580.0298461914063, 854.761474609375)), Coord2(580.0394897460938, 854.7695922851563)) + .curve_to( + ( + Coord2(580.0106201171875, 854.7659301757813), + Coord2(580.01025390625, 854.7548828125), + ), + Coord2(580.0098266601563, 854.7442016601563), + ) + .curve_to( + ( + Coord2(580.0198974609375, 854.7529296875), + Coord2(580.0298461914063, 854.761474609375), + ), + Coord2(580.0394897460938, 854.7695922851563), + ) .line_to(Coord2(580.0394897460938, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let graph_path = GraphPath::from_path(&path, ()); + let collisions = graph_path.ray_collisions(&ray); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); let mut edge_collisions = HashMap::new(); for (collision, _curve_t, _line_t, _position) in collisions { - let edge = collision.edge(); + let edge = collision.edge(); - *(edge_collisions.entry(edge) - .or_insert(0)) += 1; + *(edge_collisions.entry(edge).or_insert(0)) += 1; } assert!(edge_collisions.into_iter().all(|(_, count)| count == 1)); @@ -604,28 +719,42 @@ fn ray_glancing_2() { #[test] fn ray_glancing_2_reversed() { - let ray = (Coord2(576.3092041015625, 854.6082153320313), Coord2(576.0201416015625, 854.5975341796875)); - let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) + let ray = ( + Coord2(576.3092041015625, 854.6082153320313), + Coord2(576.0201416015625, 854.5975341796875), + ); + let path = BezierPathBuilder::::start(Coord2(1.0, 1.0)) .line_to(Coord2(1.0, 854.7772216796875)) .line_to(Coord2(580.010986328125, 854.7772216796875)) - .curve_to((Coord2(580.0106201171875, 854.7659301757813), Coord2(580.01025390625, 854.7548828125)), Coord2(580.0098266601563, 854.7442016601563)) - .curve_to((Coord2(580.0198974609375, 854.7529296875), Coord2(580.0298461914063, 854.761474609375)), Coord2(580.0394897460938, 854.7695922851563)) + .curve_to( + ( + Coord2(580.0106201171875, 854.7659301757813), + Coord2(580.01025390625, 854.7548828125), + ), + Coord2(580.0098266601563, 854.7442016601563), + ) + .curve_to( + ( + Coord2(580.0198974609375, 854.7529296875), + Coord2(580.0298461914063, 854.761474609375), + ), + Coord2(580.0394897460938, 854.7695922851563), + ) .line_to(Coord2(580.0394897460938, 1.0)) .line_to(Coord2(1.0, 1.0)) .build(); - let path = path.reversed::(); + let path = path.reversed::(); - let graph_path = GraphPath::from_path(&path, ()); - let collisions = graph_path.ray_collisions(&ray); + let graph_path = GraphPath::from_path(&path, ()); + let collisions = graph_path.ray_collisions(&ray); - assert!(collisions.len()&1 == 0); + assert!(collisions.len() & 1 == 0); let mut edge_collisions = HashMap::new(); for (collision, _curve_t, _line_t, _position) in collisions { - let edge = collision.edge(); + let edge = collision.edge(); - *(edge_collisions.entry(edge) - .or_insert(0)) += 1; + *(edge_collisions.entry(edge).or_insert(0)) += 1; } assert!(edge_collisions.into_iter().all(|(_, count)| count == 1)); diff --git a/tests/bezier/path/svg.rs b/tests/bezier/path/svg.rs index 6c09172a..5e6c08f1 100644 --- a/tests/bezier/path/svg.rs +++ b/tests/bezier/path/svg.rs @@ -1,15 +1,33 @@ -use flo_curves::geo::*; use flo_curves::bezier::path::*; +use flo_curves::geo::*; use std::fmt::Write; -pub fn svg_path_string(path: &Path) -> String -where Path::Point: Coordinate2D { +pub fn svg_path_string(path: &Path) -> String +where + Path::Point: Coordinate2D, +{ let mut svg = String::new(); - write!(&mut svg, "M {} {}", path.start_point().x(), path.start_point().y()).unwrap(); + write!( + &mut svg, + "M {} {}", + path.start_point().x(), + path.start_point().y() + ) + .unwrap(); for (cp1, cp2, end) in path.points() { - write!(&mut svg, " C {} {}, {} {}, {} {}", cp1.x(), cp1.y(), cp2.x(), cp2.y(), end.x(), end.y()).unwrap(); + write!( + &mut svg, + " C {} {}, {} {}, {} {}", + cp1.x(), + cp1.y(), + cp2.x(), + cp2.y(), + end.x(), + end.y() + ) + .unwrap(); } svg diff --git a/tests/bezier/path/to_curves.rs b/tests/bezier/path/to_curves.rs index ac331270..4d96d5a1 100644 --- a/tests/bezier/path/to_curves.rs +++ b/tests/bezier/path/to_curves.rs @@ -1,31 +1,43 @@ -use flo_curves::*; -use flo_curves::bezier::*; use flo_curves::bezier::path::*; +use flo_curves::bezier::*; +use flo_curves::*; #[test] pub fn convert_path_to_bezier_curves() { - let path = (Coord2(10.0, 11.0), vec![(Coord2(15.0, 16.0), Coord2(17.0, 18.0), Coord2(19.0, 20.0)), (Coord2(21.0, 22.0), Coord2(23.0, 24.0), Coord2(25.0, 26.0))]); - let curve = path_to_curves::<_, Curve<_>>(&path); - let curve: Vec<_> = curve.collect(); + let path = ( + Coord2(10.0, 11.0), + vec![ + (Coord2(15.0, 16.0), Coord2(17.0, 18.0), Coord2(19.0, 20.0)), + (Coord2(21.0, 22.0), Coord2(23.0, 24.0), Coord2(25.0, 26.0)), + ], + ); + let curve = path_to_curves::<_, Curve<_>>(&path); + let curve: Vec<_> = curve.collect(); assert!(curve.len() == 2); - assert!(curve[0] == Curve { - start_point: Coord2(10.0, 11.0), - end_point: Coord2(19.0, 20.0), - control_points: (Coord2(15.0, 16.0), Coord2(17.0, 18.0)) - }); - assert!(curve[1] == Curve { - start_point: Coord2(19.0, 20.0), - end_point: Coord2(25.0, 26.0), - control_points: (Coord2(21.0, 22.0), Coord2(23.0, 24.0)) - }); + assert!( + curve[0] + == Curve { + start_point: Coord2(10.0, 11.0), + end_point: Coord2(19.0, 20.0), + control_points: (Coord2(15.0, 16.0), Coord2(17.0, 18.0)) + } + ); + assert!( + curve[1] + == Curve { + start_point: Coord2(19.0, 20.0), + end_point: Coord2(25.0, 26.0), + control_points: (Coord2(21.0, 22.0), Coord2(23.0, 24.0)) + } + ); } #[test] pub fn no_points_means_no_curve() { - let path = (Coord2(10.0, 11.0), vec![]); - let curve = path_to_curves::<_, Curve<_>>(&path); - let curve: Vec<_> = curve.collect(); + let path = (Coord2(10.0, 11.0), vec![]); + let curve = path_to_curves::<_, Curve<_>>(&path); + let curve: Vec<_> = curve.collect(); assert!(curve.len() == 0); } diff --git a/tests/bezier/search.rs b/tests/bezier/search.rs index 568b7291..e93501c7 100644 --- a/tests/bezier/search.rs +++ b/tests/bezier/search.rs @@ -6,15 +6,16 @@ fn search_for_x_coordinate() { let (w1, w2, w3, w4) = (1.0, -2.0, 3.0, 4.0); // Search for the t value for a particular X coord - let x_coord = 1.5; - let matching_values = bezier::search_bounds4(0.01, w1, w2, w3, w4, |p1, p2| p1 < x_coord && p2 > x_coord); + let x_coord = 1.5; + let matching_values = + bezier::search_bounds4(0.01, w1, w2, w3, w4, |p1, p2| p1 < x_coord && p2 > x_coord); // Should be only 1 coordinate with this curve assert!(matching_values.len() == 1); // Basis function should be within 0.01 let actual_val = bezier::basis(matching_values[0], w1, w2, w3, w4); - assert!((actual_val-x_coord).abs() < 0.01); + assert!((actual_val - x_coord).abs() < 0.01); } #[test] @@ -23,8 +24,9 @@ fn coordinate_outside_curve_produces_no_results() { let (w1, w2, w3, w4) = (1.0, -2.0, 3.0, 4.0); // Search for the t value for a particular X coord, which is outside the curve - let x_coord = 5.0; - let matching_values = bezier::search_bounds4(0.01, w1, w2, w3, w4, |p1, p2| p1 < x_coord && p2 > x_coord); + let x_coord = 5.0; + let matching_values = + bezier::search_bounds4(0.01, w1, w2, w3, w4, |p1, p2| p1 < x_coord && p2 > x_coord); // No points on the curve match this coordinate assert!(matching_values.len() == 0); diff --git a/tests/bezier/section.rs b/tests/bezier/section.rs index 4ed94000..cfb4d027 100644 --- a/tests/bezier/section.rs +++ b/tests/bezier/section.rs @@ -1,14 +1,18 @@ -use flo_curves::*; use flo_curves::bezier::*; +use flo_curves::*; #[test] fn section_points_match() { - let original_curve = Curve::from_points(Coord2(2.0, 3.0), (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), Coord2(6.0, 2.0)); - let mid_section = original_curve.section(0.25, 0.75); + let original_curve = Curve::from_points( + Coord2(2.0, 3.0), + (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), + Coord2(6.0, 2.0), + ); + let mid_section = original_curve.section(0.25, 0.75); for t in 0..=10 { - let t = (t as f64)/10.0; - let t2 = t*0.5 + 0.25; + let t = (t as f64) / 10.0; + let t2 = t * 0.5 + 0.25; let p1 = mid_section.point_at_pos(t); let p2 = original_curve.point_at_pos(t2); @@ -19,12 +23,16 @@ fn section_points_match() { #[test] fn generate_curve_from_section() { - let original_curve = Curve::from_points(Coord2(2.0, 3.0), (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), Coord2(6.0, 2.0)); - let mid_section = Curve::from_curve(&original_curve.section(0.2, 0.6)); + let original_curve = Curve::from_points( + Coord2(2.0, 3.0), + (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), + Coord2(6.0, 2.0), + ); + let mid_section = Curve::from_curve(&original_curve.section(0.2, 0.6)); for t in 0..=10 { - let t = (t as f64)/10.0; - let t2 = t*0.4 + 0.2; + let t = (t as f64) / 10.0; + let t2 = t * 0.4 + 0.2; let p1 = mid_section.point_at_pos(t); let p2 = original_curve.point_at_pos(t2); @@ -35,13 +43,17 @@ fn generate_curve_from_section() { #[test] fn section_of_section() { - let original_curve = Curve::from_points(Coord2(2.0, 3.0), (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), Coord2(6.0, 2.0)); + let original_curve = Curve::from_points( + Coord2(2.0, 3.0), + (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), + Coord2(6.0, 2.0), + ); let mut mid_section = original_curve.section(0.25, 0.75); - mid_section = mid_section.subsection(0.25, 0.75); + mid_section = mid_section.subsection(0.25, 0.75); for t in 0..=10 { - let t = (t as f64)/10.0; - let t2 = t*0.25 + 0.375; + let t = (t as f64) / 10.0; + let t2 = t * 0.25 + 0.375; let p1 = mid_section.point_at_pos(t); let p2 = original_curve.point_at_pos(t2); @@ -52,27 +64,39 @@ fn section_of_section() { #[test] fn recover_original_t_values() { - let original_curve = Curve::from_points(Coord2(2.0, 3.0), (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), Coord2(6.0, 2.0)); - let mid_section = original_curve.section(0.2, 0.6); + let original_curve = Curve::from_points( + Coord2(2.0, 3.0), + (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), + Coord2(6.0, 2.0), + ); + let mid_section = original_curve.section(0.2, 0.6); assert!(mid_section.original_curve_t_values() == (0.2, 0.6)); } #[test] fn map_t_values_back_to_section() { - let original_curve = Curve::from_points(Coord2(2.0, 3.0), (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), Coord2(6.0, 2.0)); - let mid_section = original_curve.section(0.2, 0.6); - - assert!((mid_section.section_t_for_original_t(0.2)-0.0).abs() < 0.01); - assert!((mid_section.section_t_for_original_t(0.4)-0.5).abs() < 0.01); - assert!((mid_section.section_t_for_original_t(0.6)-1.0).abs() < 0.01); + let original_curve = Curve::from_points( + Coord2(2.0, 3.0), + (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), + Coord2(6.0, 2.0), + ); + let mid_section = original_curve.section(0.2, 0.6); + + assert!((mid_section.section_t_for_original_t(0.2) - 0.0).abs() < 0.01); + assert!((mid_section.section_t_for_original_t(0.4) - 0.5).abs() < 0.01); + assert!((mid_section.section_t_for_original_t(0.6) - 1.0).abs() < 0.01); } #[test] fn recover_original_t_values_from_subsection() { - let original_curve = Curve::from_points(Coord2(2.0, 3.0), (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), Coord2(6.0, 2.0)); - let mid_section = original_curve.section(0.25, 0.75); - let sub_section = mid_section.subsection(0.25, 0.75); + let original_curve = Curve::from_points( + Coord2(2.0, 3.0), + (Coord2(4.0, 5.0), Coord2(5.0, 0.0)), + Coord2(6.0, 2.0), + ); + let mid_section = original_curve.section(0.25, 0.75); + let sub_section = mid_section.subsection(0.25, 0.75); assert!(sub_section.original_curve_t_values() == (0.375, 0.625)); } diff --git a/tests/bezier/self_intersection.rs b/tests/bezier/self_intersection.rs index 5d580d44..d6071690 100644 --- a/tests/bezier/self_intersection.rs +++ b/tests/bezier/self_intersection.rs @@ -1,28 +1,42 @@ -use flo_curves::*; use flo_curves::bezier::*; +use flo_curves::*; #[test] fn find_simple_self_intersection() { - let curve_with_loop = Curve::from_points(Coord2(148.0, 151.0), (Coord2(292.0, 199.0), Coord2(73.0, 221.0)), Coord2(249.0, 136.0)); - let intersection_point = find_self_intersection_point(&curve_with_loop, 0.01); + let curve_with_loop = Curve::from_points( + Coord2(148.0, 151.0), + (Coord2(292.0, 199.0), Coord2(73.0, 221.0)), + Coord2(249.0, 136.0), + ); + let intersection_point = find_self_intersection_point(&curve_with_loop, 0.01); assert!(intersection_point.is_some()); let (t1, t2) = intersection_point.unwrap(); - let (p1, p2) = (curve_with_loop.point_at_pos(t1), curve_with_loop.point_at_pos(t2)); + let (p1, p2) = ( + curve_with_loop.point_at_pos(t1), + curve_with_loop.point_at_pos(t2), + ); assert!(p1.is_near_to(&p2, 0.01)); } #[test] fn whole_curve_is_a_loop() { - let curve_with_loop = Curve::from_points(Coord2(205.0, 159.0), (Coord2(81.0, 219.0), Coord2(287.0, 227.0)), Coord2(205.0, 159.0)); - let intersection_point = find_self_intersection_point(&curve_with_loop, 0.01); + let curve_with_loop = Curve::from_points( + Coord2(205.0, 159.0), + (Coord2(81.0, 219.0), Coord2(287.0, 227.0)), + Coord2(205.0, 159.0), + ); + let intersection_point = find_self_intersection_point(&curve_with_loop, 0.01); assert!(intersection_point.is_some()); let (t1, t2) = intersection_point.unwrap(); - let (p1, p2) = (curve_with_loop.point_at_pos(t1), curve_with_loop.point_at_pos(t2)); + let (p1, p2) = ( + curve_with_loop.point_at_pos(t1), + curve_with_loop.point_at_pos(t2), + ); assert!(p1.is_near_to(&p2, 0.01)); assert!(t1 <= 0.0); @@ -31,13 +45,23 @@ fn whole_curve_is_a_loop() { #[test] fn narrow_loop() { - let curve_with_loop = Curve::from_points(Coord2(549.2899780273438, 889.4202270507813), (Coord2(553.4288330078125, 893.8638305664063), Coord2(542.5203247070313, 889.04931640625)), Coord2(548.051025390625, 891.1853637695313)); - let intersection_point = find_self_intersection_point(&curve_with_loop, 0.01); + let curve_with_loop = Curve::from_points( + Coord2(549.2899780273438, 889.4202270507813), + ( + Coord2(553.4288330078125, 893.8638305664063), + Coord2(542.5203247070313, 889.04931640625), + ), + Coord2(548.051025390625, 891.1853637695313), + ); + let intersection_point = find_self_intersection_point(&curve_with_loop, 0.01); assert!(intersection_point.is_some()); let (t1, t2) = intersection_point.unwrap(); - let (p1, p2) = (curve_with_loop.point_at_pos(t1), curve_with_loop.point_at_pos(t2)); + let (p1, p2) = ( + curve_with_loop.point_at_pos(t1), + curve_with_loop.point_at_pos(t2), + ); assert!(p1.is_near_to(&p2, 0.01)); } diff --git a/tests/bezier/solve.rs b/tests/bezier/solve.rs index 7cc507c8..55e8083c 100644 --- a/tests/bezier/solve.rs +++ b/tests/bezier/solve.rs @@ -2,8 +2,8 @@ use flo_curves::bezier::*; #[test] fn basis_solve_middle() { - assert!((solve_basis_for_t(0.0, 0.33, 0.66, 1.0, 0.5)[0]-0.5).abs() < 0.01); - assert!((solve_basis_for_t(0.0, 1.0, 2.0, 3.0, 1.5)[0]-0.5).abs() < 0.01); + assert!((solve_basis_for_t(0.0, 0.33, 0.66, 1.0, 0.5)[0] - 0.5).abs() < 0.01); + assert!((solve_basis_for_t(0.0, 1.0, 2.0, 3.0, 1.5)[0] - 0.5).abs() < 0.01); } #[test] @@ -11,18 +11,21 @@ fn basis_solve_many() { fn test_for(w1: f64, w2: f64, w3: f64, w4: f64) { for p in 0..=16 { // Pick a point between w1 and w4 - let p = ((p as f64)/16.0)*(w4-w1) + w1; + let p = ((p as f64) / 16.0) * (w4 - w1) + w1; // Solve for t values let t_values = solve_basis_for_t(w1, w2, w3, w4, p); // Computing the points for these values should result in a valid curve - let pos_for_t = t_values.iter() + let pos_for_t = t_values + .iter() .map(|t| basis(*t, w1, w2, w3, w4)) .collect::>(); // Should all evaluate to positions on the curve - pos_for_t.iter().for_each(|pos| assert!((pos-p).abs() < 0.01)); + pos_for_t + .iter() + .for_each(|pos| assert!((pos - p).abs() < 0.01)); } } @@ -33,53 +36,73 @@ fn basis_solve_many() { #[test] fn solve_t_for_pos() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); - let point_at_one_third = curve1.point_at_pos(0.3333); - let solved = curve1.t_for_point(&point_at_one_third); + let point_at_one_third = curve1.point_at_pos(0.3333); + let solved = curve1.t_for_point(&point_at_one_third); assert!(solved.is_some()); - assert!((solved.unwrap()-0.3333).abs() < 0.001); + assert!((solved.unwrap() - 0.3333).abs() < 0.001); } #[test] fn solve_t_for_start() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); - let solved = curve1.t_for_point(&Coord2(10.0, 100.0)); + let solved = curve1.t_for_point(&Coord2(10.0, 100.0)); assert!(solved.is_some()); - assert!((solved.unwrap()-0.0).abs() < 0.001); + assert!((solved.unwrap() - 0.0).abs() < 0.001); } #[test] fn solve_t_for_end() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); - let solved = curve1.t_for_point(&Coord2(220.0, 220.0)); + let solved = curve1.t_for_point(&Coord2(220.0, 220.0)); assert!(solved.is_some()); - assert!((solved.unwrap()-1.0).abs() < 0.001); + assert!((solved.unwrap() - 1.0).abs() < 0.001); } #[test] fn solve_t_for_many_positions() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); for p in 0..10 { - let p = (p as f64)/10.0; - let point = curve1.point_at_pos(p); - let solved = curve1.t_for_point(&point); + let p = (p as f64) / 10.0; + let point = curve1.point_at_pos(p); + let solved = curve1.t_for_point(&point); assert!(solved.is_some()); - assert!((solved.unwrap()-p).abs() < 0.001); + assert!((solved.unwrap() - p).abs() < 0.001); } } #[test] fn solve_t_for_out_of_bounds() { - let curve1 = Curve::from_points(Coord2(10.0, 100.0), (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), Coord2(220.0, 220.0)); + let curve1 = Curve::from_points( + Coord2(10.0, 100.0), + (Coord2(90.0, 30.0), Coord2(40.0, 140.0)), + Coord2(220.0, 220.0), + ); - let solved = curve1.t_for_point(&Coord2(45.0, 23.0)); + let solved = curve1.t_for_point(&Coord2(45.0, 23.0)); assert!(solved.is_none()); } diff --git a/tests/bezier/subdivide.rs b/tests/bezier/subdivide.rs index 50940f5a..fd9a31fe 100644 --- a/tests/bezier/subdivide.rs +++ b/tests/bezier/subdivide.rs @@ -12,9 +12,9 @@ fn subdivide_1() { // Check that the original curve corresponds to the basis function for wa for x in 0..100 { - let t = (x as f64)/100.0; + let t = (x as f64) / 100.0; - let original = bezier::basis(t*0.33, w1, w2, w3, w4); + let original = bezier::basis(t * 0.33, w1, w2, w3, w4); let subdivision = bezier::basis(t, wa1, wa2, wa3, wa4); assert!(approx_equal(original, subdivision)); @@ -31,9 +31,9 @@ fn subdivide_2() { // Check that the original curve corresponds to the basis function for wb for x in 0..100 { - let t = (x as f64)/100.0; + let t = (x as f64) / 100.0; - let original = bezier::basis(0.33+(t*(1.0-0.33)), w1, w2, w3, w4); + let original = bezier::basis(0.33 + (t * (1.0 - 0.33)), w1, w2, w3, w4); let subdivision = bezier::basis(t, wb1, wb2, wb3, wb4); assert!(approx_equal(original, subdivision)); diff --git a/tests/bezier/tangent.rs b/tests/bezier/tangent.rs index e810e64c..d39cc892 100644 --- a/tests/bezier/tangent.rs +++ b/tests/bezier/tangent.rs @@ -1,10 +1,14 @@ -use flo_curves::*; use flo_curves::bezier; +use flo_curves::*; #[test] fn calculate_tangent_for_straight_line() { - let straight_line = bezier::Curve::from_points(Coord2(0.0, 1.0), (Coord2(0.5, 1.5), Coord2(1.5, 2.5)), Coord2(2.0, 3.0)); - let tangent = bezier::Tangent::from(&straight_line); + let straight_line = bezier::Curve::from_points( + Coord2(0.0, 1.0), + (Coord2(0.5, 1.5), Coord2(1.5, 2.5)), + Coord2(2.0, 3.0), + ); + let tangent = bezier::Tangent::from(&straight_line); assert!(tangent.tangent(0.5) == Coord2(2.25, 2.25)); diff --git a/tests/bezier/walk.rs b/tests/bezier/walk.rs index 7ebee6cf..938d6e61 100644 --- a/tests/bezier/walk.rs +++ b/tests/bezier/walk.rs @@ -2,15 +2,19 @@ use flo_curves::bezier::*; #[test] fn uneven_walk_1() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let sections = walk_curve_unevenly(&c, 10).collect::>(); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let sections = walk_curve_unevenly(&c, 10).collect::>(); assert!(sections.len() == 10); assert!(sections[0].original_curve_t_values() == (0.0, 0.1)); for section_num in 0..10 { - let expected_t_min = (section_num as f64)/10.0; - let expected_t_max = (section_num as f64)/10.0 + 0.1; + let expected_t_min = (section_num as f64) / 10.0; + let expected_t_max = (section_num as f64) / 10.0 + 0.1; let (actual_t_min, actual_t_max) = sections[section_num].original_curve_t_values(); @@ -21,178 +25,245 @@ fn uneven_walk_1() { #[test] fn even_walk_1() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); - let actual_length = curve_length(&c, 0.1); - - let mut total_length = 0.0; - let mut last_t = 0.0; + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); + let actual_length = curve_length(&c, 0.1); + + let mut total_length = 0.0; + let mut last_t = 0.0; for section in sections.iter() { let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; - assert!((chord_length(section)-1.0).abs() <= 0.1 || (t_max >= 1.0 && chord_length(section)-1.0 <= 0.0)); + assert!( + (chord_length(section) - 1.0).abs() <= 0.1 + || (t_max >= 1.0 && chord_length(section) - 1.0 <= 0.0) + ); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 4.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 4.0); } #[test] fn even_walk_2() { - let c = Curve::from_points(Coord2(170.83203, 534.28906), (Coord2(140.99219, 492.1289), Coord2(0.52734375, 478.67188)), Coord2(262.95313, 533.2656)); - let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); - let actual_length = curve_length(&c, 0.1); - - let mut total_length = 0.0; - let mut last_t = 0.0; + let c = Curve::from_points( + Coord2(170.83203, 534.28906), + (Coord2(140.99219, 492.1289), Coord2(0.52734375, 478.67188)), + Coord2(262.95313, 533.2656), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); + let actual_length = curve_length(&c, 0.1); + + let mut total_length = 0.0; + let mut last_t = 0.0; for section in sections.iter() { let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; - assert!((chord_length(section)-1.0).abs() <= 0.1 || (t_max >= 1.0 && chord_length(section)-1.0 <= 0.0)); + assert!( + (chord_length(section) - 1.0).abs() <= 0.1 + || (t_max >= 1.0 && chord_length(section) - 1.0 <= 0.0) + ); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 4.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 4.0); } #[test] fn even_walk_3() { - let c = Curve::from_points(Coord2(987.7637, 993.9645), (Coord2(991.1699, 994.0231), Coord2(1043.5605, 853.44885)), Coord2(1064.9473, 994.277)); - let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); - let actual_length = curve_length(&c, 0.1); - - let mut total_length = 0.0; - let mut last_t = 0.0; + let c = Curve::from_points( + Coord2(987.7637, 993.9645), + (Coord2(991.1699, 994.0231), Coord2(1043.5605, 853.44885)), + Coord2(1064.9473, 994.277), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); + let actual_length = curve_length(&c, 0.1); + + let mut total_length = 0.0; + let mut last_t = 0.0; for section in sections.iter() { let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; - assert!((chord_length(section)-1.0).abs() <= 0.1 || (t_max >= 1.0 && chord_length(section)-1.0 <= 0.0)); + assert!( + (chord_length(section) - 1.0).abs() <= 0.1 + || (t_max >= 1.0 && chord_length(section) - 1.0 <= 0.0) + ); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 4.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 4.0); } #[test] fn even_walk_4() { - let c = Curve::from_points(Coord2(222.37538991853827, 99.16540392815092), (Coord2(224.47523575883392, 100.31557953334229), Coord2(223.19303980237945, 101.8075327562316)), Coord2(225.42363518033414, 99.716688142193)); - let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); - let actual_length = curve_length(&c, 0.1); - - let mut total_length = 0.0; - let mut last_t = 0.0; + let c = Curve::from_points( + Coord2(222.37538991853827, 99.16540392815092), + ( + Coord2(224.47523575883392, 100.31557953334229), + Coord2(223.19303980237945, 101.8075327562316), + ), + Coord2(225.42363518033414, 99.716688142193), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); + let actual_length = curve_length(&c, 0.1); + + let mut total_length = 0.0; + let mut last_t = 0.0; for section in sections.iter() { let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; - assert!((chord_length(section)-1.0).abs() <= 0.1 || (t_max >= 1.0 && chord_length(section)-1.0 <= 0.0)); + assert!( + (chord_length(section) - 1.0).abs() <= 0.1 + || (t_max >= 1.0 && chord_length(section) - 1.0 <= 0.0) + ); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 4.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 4.0); } #[test] fn even_walk_5() { - let c = Curve::from_points(Coord2(128.51366414207797, 100.43540868606826), (Coord2(128.8517120419268, 100.53996562501626), Coord2(131.79687993559304, 99.36123524249854)), Coord2(131.8239019605053, 99.36980615298116)); - let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); - let actual_length = curve_length(&c, 0.1); - - let mut total_length = 0.0; - let mut last_t = 0.0; + let c = Curve::from_points( + Coord2(128.51366414207797, 100.43540868606826), + ( + Coord2(128.8517120419268, 100.53996562501626), + Coord2(131.79687993559304, 99.36123524249854), + ), + Coord2(131.8239019605053, 99.36980615298116), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); + let actual_length = curve_length(&c, 0.1); + + let mut total_length = 0.0; + let mut last_t = 0.0; for section in sections.iter() { - println!("{:?} {:?}", chord_length(section)-1.0, section.original_curve_t_values()); + println!( + "{:?} {:?}", + chord_length(section) - 1.0, + section.original_curve_t_values() + ); let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; - assert!((chord_length(section)-1.0).abs() <= 0.1 || (t_max >= 1.0 && chord_length(section)-1.0 <= 0.0)); + assert!( + (chord_length(section) - 1.0).abs() <= 0.1 + || (t_max >= 1.0 && chord_length(section) - 1.0 <= 0.0) + ); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 4.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 4.0); } #[test] fn even_walk_6() { - let c = Curve::from_points(Coord2(771.375, 195.0959930419922), (Coord2(771.375, 195.0959930419922), Coord2(629.2169799804688, 161.80499267578125)), Coord2(622.0430297851563, 160.3459930419922)); - let sections = walk_curve_evenly(&c, 2.0, 0.5).collect::>(); - let actual_length = curve_length(&c, 0.1); - - let mut total_length = 0.0; - let mut last_t = 0.0; + let c = Curve::from_points( + Coord2(771.375, 195.0959930419922), + ( + Coord2(771.375, 195.0959930419922), + Coord2(629.2169799804688, 161.80499267578125), + ), + Coord2(622.0430297851563, 160.3459930419922), + ); + let sections = walk_curve_evenly(&c, 2.0, 0.5).collect::>(); + let actual_length = curve_length(&c, 0.1); + + let mut total_length = 0.0; + let mut last_t = 0.0; for section in sections.iter() { - println!("{:?} {:?}", chord_length(section)-2.0, section.original_curve_t_values()); + println!( + "{:?} {:?}", + chord_length(section) - 2.0, + section.original_curve_t_values() + ); let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; - assert!((chord_length(section)-2.0).abs() <= 0.5 || (t_max >= 1.0 && chord_length(section)-2.0 <= 0.0)); + assert!( + (chord_length(section) - 2.0).abs() <= 0.5 + || (t_max >= 1.0 && chord_length(section) - 2.0 <= 0.0) + ); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 16.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 16.0); } #[test] fn even_walk_point() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(412.0, 500.0)), Coord2(412.0, 500.0)); - let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(412.0, 500.0)), + Coord2(412.0, 500.0), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1).collect::>(); assert!(sections.len() == 1); - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); } #[test] fn varying_walk_1() { - let c = Curve::from_points(Coord2(412.0, 500.0), (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), Coord2(308.0, 665.0)); - let sections = walk_curve_evenly(&c, 1.0, 0.1) + let c = Curve::from_points( + Coord2(412.0, 500.0), + (Coord2(412.0, 500.0), Coord2(163.0, 504.0)), + Coord2(308.0, 665.0), + ); + let sections = walk_curve_evenly(&c, 1.0, 0.1) .vary_by(vec![1.0, 2.0, 3.0].into_iter().cycle()) .collect::>(); - let actual_length = curve_length(&c, 0.1); + let actual_length = curve_length(&c, 0.1); - let mut total_length = 0.0; - let mut last_t = 0.0; + let mut total_length = 0.0; + let mut last_t = 0.0; let mut expected_length = vec![1.0, 2.0, 3.0].into_iter().cycle(); - for section in sections.iter().take(sections.len()-1) { + for section in sections.iter().take(sections.len() - 1) { let (_, t_max) = section.original_curve_t_values(); assert!(t_max > last_t); last_t = t_max; let expected_length = expected_length.next().unwrap(); - assert!((chord_length(section)-expected_length).abs() <= 0.1); + assert!((chord_length(section) - expected_length).abs() <= 0.1); total_length += chord_length(section); } - assert!(sections[sections.len()-1].original_curve_t_values().1 == 1.0); + assert!(sections[sections.len() - 1].original_curve_t_values().1 == 1.0); - println!("{:?}", (total_length-actual_length).abs()); - assert!((total_length-actual_length).abs() < 4.0); + println!("{:?}", (total_length - actual_length).abs()); + assert!((total_length - actual_length).abs() < 4.0); } diff --git a/tests/bounds.rs b/tests/bounds.rs index b65472de..f39a73d2 100644 --- a/tests/bounds.rs +++ b/tests/bounds.rs @@ -55,7 +55,7 @@ fn from_points() { Coord2(30.0, 30.0), Coord2(60.0, 40.0), Coord2(45.0, 70.0), - Coord2(10.0, 35.0) + Coord2(10.0, 35.0), ]); assert!(r.min() == Coord2(10.0, 30.0)); diff --git a/tests/coordinates.rs b/tests/coordinates.rs index b9ca70fb..a4e2f260 100644 --- a/tests/coordinates.rs +++ b/tests/coordinates.rs @@ -14,7 +14,14 @@ fn can_find_unit_vector() { assert!(Coord2(0.0, 1.0).to_unit_vector() == Coord2(0.0, 1.0)); assert!(Coord2(0.0, 2.0).to_unit_vector() == Coord2(0.0, 1.0)); - assert!(f64::abs(Coord2(4.0, 2.0).to_unit_vector().distance_to(&Coord2(0.0, 0.0))-1.0) < 0.01); + assert!( + f64::abs( + Coord2(4.0, 2.0) + .to_unit_vector() + .distance_to(&Coord2(0.0, 0.0)) + - 1.0 + ) < 0.01 + ); } #[test] @@ -24,7 +31,7 @@ fn unit_vector_of_0_0_is_0_0() { #[test] fn can_get_dot_product() { - assert!(Coord2(2.0,1.0).dot(&Coord2(3.0, 4.0)) == 10.0); + assert!(Coord2(2.0, 1.0).dot(&Coord2(3.0, 4.0)) == 10.0); } #[test] @@ -49,5 +56,7 @@ fn unit_vector_0_degrees() { #[test] fn unit_vector_90_degrees() { - assert!(Coord2::unit_vector_at_angle(f64::consts::PI / 2.0).distance_to(&Coord2(0.0, 1.0)) < 0.001); + assert!( + Coord2::unit_vector_at_angle(f64::consts::PI / 2.0).distance_to(&Coord2(0.0, 1.0)) < 0.001 + ); } diff --git a/tests/line/coefficients.rs b/tests/line/coefficients.rs index 0fac8756..40f2e78d 100644 --- a/tests/line/coefficients.rs +++ b/tests/line/coefficients.rs @@ -2,66 +2,66 @@ use flo_curves::line::*; #[test] fn points_on_line_are_on_line_1() { - let line = (Coord2(2.0, 3.0), Coord2(7.0, 6.0)); - let (a, b, c) = line_coefficients_2d(&line); + let line = (Coord2(2.0, 3.0), Coord2(7.0, 6.0)); + let (a, b, c) = line_coefficients_2d(&line); for t in 0..=16 { - let t = (t as f64) / 16.0; - let point = line.point_at_pos(t); + let t = (t as f64) / 16.0; + let point = line.point_at_pos(t); - assert!((a*point.x() + b*point.y() + c).abs() < 0.001); + assert!((a * point.x() + b * point.y() + c).abs() < 0.001); } } #[test] fn points_on_line_are_on_line_2() { - let line = (Coord2(7.0, 6.0), Coord2(2.0, 3.0)); - let (a, b, c) = line_coefficients_2d(&line); + let line = (Coord2(7.0, 6.0), Coord2(2.0, 3.0)); + let (a, b, c) = line_coefficients_2d(&line); for t in 0..=16 { - let t = (t as f64) / 16.0; - let point = line.point_at_pos(t); + let t = (t as f64) / 16.0; + let point = line.point_at_pos(t); - assert!((a*point.x() + b*point.y() + c).abs() < 0.001); + assert!((a * point.x() + b * point.y() + c).abs() < 0.001); } } #[test] fn points_on_line_are_on_line_3() { - let line = (Coord2(2.0, 3.0), Coord2(7.0, 3.0)); - let (a, b, c) = line_coefficients_2d(&line); + let line = (Coord2(2.0, 3.0), Coord2(7.0, 3.0)); + let (a, b, c) = line_coefficients_2d(&line); for t in 0..=16 { - let t = (t as f64) / 16.0; - let point = line.point_at_pos(t); + let t = (t as f64) / 16.0; + let point = line.point_at_pos(t); - assert!((a*point.x() + b*point.y() + c).abs() < 0.001); + assert!((a * point.x() + b * point.y() + c).abs() < 0.001); } } #[test] fn points_on_line_are_on_line_4() { - let line = (Coord2(2.0, 3.0), Coord2(2.0, 6.0)); - let (a, b, c) = line_coefficients_2d(&line); + let line = (Coord2(2.0, 3.0), Coord2(2.0, 6.0)); + let (a, b, c) = line_coefficients_2d(&line); for t in 0..=16 { - let t = (t as f64) / 16.0; - let point = line.point_at_pos(t); + let t = (t as f64) / 16.0; + let point = line.point_at_pos(t); - assert!((a*point.x() + b*point.y() + c).abs() < 0.001); + assert!((a * point.x() + b * point.y() + c).abs() < 0.001); } } #[test] fn points_on_line_are_on_line_5() { - let line = (Coord2(2.0, 3.0), Coord2(2.0, 6.0)); - let (a, b, c) = line.coefficients(); + let line = (Coord2(2.0, 3.0), Coord2(2.0, 6.0)); + let (a, b, c) = line.coefficients(); for t in 0..=16 { - let t = (t as f64) / 16.0; - let point = line.point_at_pos(t); + let t = (t as f64) / 16.0; + let point = line.point_at_pos(t); - assert!((a*point.x() + b*point.y() + c).abs() < 0.001); + assert!((a * point.x() + b * point.y() + c).abs() < 0.001); } } diff --git a/tests/line/intersection.rs b/tests/line/intersection.rs index 6feb2a89..2bda1c16 100644 --- a/tests/line/intersection.rs +++ b/tests/line/intersection.rs @@ -1,35 +1,72 @@ -use flo_curves::*; use flo_curves::line::*; +use flo_curves::*; #[test] fn intersection_at_0_0() { - assert!(line_intersects_line(&(Coord2(-1.0, 0.0), Coord2(1.0, 0.0)), &(Coord2(0.0, 1.0), Coord2(0.0, -1.0))).unwrap().distance_to(&Coord2(0.0, 0.0)) < 0.01); + assert!( + line_intersects_line( + &(Coord2(-1.0, 0.0), Coord2(1.0, 0.0)), + &(Coord2(0.0, 1.0), Coord2(0.0, -1.0)) + ) + .unwrap() + .distance_to(&Coord2(0.0, 0.0)) + < 0.01 + ); } #[test] fn intersection_at_other_point() { - assert!(line_intersects_line(&(Coord2(10.0, 20.0), Coord2(50.0, 60.0)), &(Coord2(10.0, 45.0), Coord2(50.0, 35.0))).unwrap().distance_to(&Coord2(30.0, 40.0)) < 0.01); + assert!( + line_intersects_line( + &(Coord2(10.0, 20.0), Coord2(50.0, 60.0)), + &(Coord2(10.0, 45.0), Coord2(50.0, 35.0)) + ) + .unwrap() + .distance_to(&Coord2(30.0, 40.0)) + < 0.01 + ); } #[test] fn ray_intersects_line() { - assert!(line_intersects_ray(&(Coord2(10.0, 20.0), Coord2(50.0, 60.0)), &(Coord2(10.0, 45.0), Coord2(14.0, 44.0))).unwrap().distance_to(&Coord2(30.0, 40.0)) < 0.01); + assert!( + line_intersects_ray( + &(Coord2(10.0, 20.0), Coord2(50.0, 60.0)), + &(Coord2(10.0, 45.0), Coord2(14.0, 44.0)) + ) + .unwrap() + .distance_to(&Coord2(30.0, 40.0)) + < 0.01 + ); } #[test] fn two_rays_intersect() { - assert!(ray_intersects_ray(&(Coord2(10.0, 20.0), Coord2(50.0, 60.0)), &(Coord2(10.0, 45.0), Coord2(14.0, 44.0))).unwrap().distance_to(&Coord2(30.0, 40.0)) < 0.01); + assert!( + ray_intersects_ray( + &(Coord2(10.0, 20.0), Coord2(50.0, 60.0)), + &(Coord2(10.0, 45.0), Coord2(14.0, 44.0)) + ) + .unwrap() + .distance_to(&Coord2(30.0, 40.0)) + < 0.01 + ); } #[test] fn no_intersection() { - assert!(line_intersects_line(&(Coord2(12.0, 13.0), Coord2(24.0, 30.0)), &(Coord2(1.0, 1.0), Coord2(0.0, -1.0))) == None); + assert!( + line_intersects_line( + &(Coord2(12.0, 13.0), Coord2(24.0, 30.0)), + &(Coord2(1.0, 1.0), Coord2(0.0, -1.0)) + ) == None + ); } #[test] fn line_in_bounds() { - let line = (Coord2(5.0, 3.0), Coord2(7.0, 9.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(5.0, 3.0), Coord2(7.0, 9.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_some()); @@ -41,8 +78,8 @@ fn line_in_bounds() { #[test] fn horizontal_clipped_line() { - let line = (Coord2(-10.0, 4.0), Coord2(20.0, 4.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(-10.0, 4.0), Coord2(20.0, 4.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_some()); @@ -54,8 +91,8 @@ fn horizontal_clipped_line() { #[test] fn horizontal_clipped_line_inside_to_outside() { - let line = (Coord2(5.0, 4.0), Coord2(20.0, 4.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(5.0, 4.0), Coord2(20.0, 4.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_some()); @@ -67,8 +104,8 @@ fn horizontal_clipped_line_inside_to_outside() { #[test] fn horizontal_clipped_line_inside_to_outside_reverse() { - let line = (Coord2(20.0, 4.0), Coord2(5.0, 4.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(20.0, 4.0), Coord2(5.0, 4.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_some()); @@ -80,8 +117,8 @@ fn horizontal_clipped_line_inside_to_outside_reverse() { #[test] fn vertical_clipped_line_inside_to_outside() { - let line = (Coord2(5.0, 4.0), Coord2(5.0, 20.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(5.0, 4.0), Coord2(5.0, 20.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_some()); @@ -93,8 +130,8 @@ fn vertical_clipped_line_inside_to_outside() { #[test] fn line_out_of_bounds_right() { - let line = (Coord2(11.0, 9.5), Coord2(20.0, 9.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(11.0, 9.5), Coord2(20.0, 9.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_none()); @@ -102,8 +139,8 @@ fn line_out_of_bounds_right() { #[test] fn line_out_of_bounds_left() { - let line = (Coord2(-11.0, 9.5), Coord2(-20.0, 9.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(-11.0, 9.5), Coord2(-20.0, 9.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_none()); @@ -111,10 +148,9 @@ fn line_out_of_bounds_left() { #[test] fn line_out_of_bounds_crossing() { - let line = (Coord2(9.0, 0.0), Coord2(20.0, 9.0)); - let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); + let line = (Coord2(9.0, 0.0), Coord2(20.0, 9.0)); + let bounds = (Coord2(1.0, 1.0), Coord2(10.0, 10.0)); let clipped = line_clip_to_bounds(&line, &bounds); assert!(clipped.is_none()); } - diff --git a/tests/line/mod.rs b/tests/line/mod.rs index 318cef3a..f32f0e15 100644 --- a/tests/line/mod.rs +++ b/tests/line/mod.rs @@ -1,3 +1,3 @@ -mod to_curve; -mod intersection; mod coefficients; +mod intersection; +mod to_curve; diff --git a/tests/line/to_curve.rs b/tests/line/to_curve.rs index 8f30aed2..df0b76a5 100644 --- a/tests/line/to_curve.rs +++ b/tests/line/to_curve.rs @@ -1,11 +1,11 @@ -use flo_curves::*; -use flo_curves::line::*; use flo_curves::bezier::*; +use flo_curves::line::*; +use flo_curves::*; #[test] fn convert_line_to_bezier_curve() { - let line = (Coord2(10.0, 20.0), Coord2(40.0, 30.0)); - let curve = line_to_bezier::<_, Curve<_>>(&line); + let line = (Coord2(10.0, 20.0), Coord2(40.0, 30.0)); + let curve = line_to_bezier::<_, Curve<_>>(&line); assert!(curve.start_point == Coord2(10.0, 20.0)); assert!(curve.end_point == Coord2(40.0, 30.0)); diff --git a/tests/readme.rs b/tests/readme.rs index e5eb19ce..9369043f 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -4,8 +4,8 @@ use std::str; /// Reads the README file for the crate /// fn readme() -> &'static str { - let readme_bytes = include_bytes!("../README.md"); - let readme_str = str::from_utf8(readme_bytes); + let readme_bytes = include_bytes!("../README.md"); + let readme_str = str::from_utf8(readme_bytes); readme_str.expect("Could not decode README.md") } @@ -15,9 +15,12 @@ fn starts_with_version_number_toml() { let major_version = env!("CARGO_PKG_VERSION_MAJOR"); let minor_version = env!("CARGO_PKG_VERSION_MINOR"); - let expected = format!("```toml + let expected = format!( + "```toml flo_curves = \"{}.{}\" -```", major_version, minor_version); +```", + major_version, minor_version + ); println!("{}", expected); assert!(readme().starts_with(&expected)); diff --git a/tests/sweep.rs b/tests/sweep.rs index 3f147d31..10679237 100644 --- a/tests/sweep.rs +++ b/tests/sweep.rs @@ -1,7 +1,7 @@ use flo_curves::geo::*; use rand::prelude::*; -use std::cmp::{Ordering}; +use std::cmp::Ordering; #[test] fn sweep_self_single_overlap() { @@ -9,7 +9,12 @@ fn sweep_self_single_overlap() { Bounds::from_min_max(Coord2(100.0, 200.0), Coord2(200.0, 300.0)), Bounds::from_min_max(Coord2(150.0, 250.0), Coord2(250.0, 350.0)), ]; - bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_self(bounds.iter()).collect::>(); @@ -23,7 +28,12 @@ fn sweep_self_double_overlap() { Bounds::from_min_max(Coord2(150.0, 250.0), Coord2(250.0, 350.0)), Bounds::from_min_max(Coord2(220.0, 330.0), Coord2(350.0, 450.0)), ]; - bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_self(bounds.iter()).collect::>(); @@ -37,7 +47,12 @@ fn sweep_self_triple_overlap() { Bounds::from_min_max(Coord2(150.0, 250.0), Coord2(250.0, 350.0)), Bounds::from_min_max(Coord2(190.0, 290.0), Coord2(290.0, 390.0)), ]; - bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_self(bounds.iter()).collect::>(); @@ -52,7 +67,12 @@ fn sweep_self_quad_overlap() { Bounds::from_min_max(Coord2(190.0, 290.0), Coord2(290.0, 390.0)), Bounds::from_min_max(Coord2(0.0, 0.0), Coord2(1000.0, 1000.0)), ]; - bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_self(bounds.iter()).collect::>(); @@ -61,14 +81,26 @@ fn sweep_self_quad_overlap() { #[test] fn sweep_against_single_overlap() { - let mut bounds1 = vec![ - Bounds::from_min_max(Coord2(100.0, 200.0), Coord2(200.0, 300.0)) - ]; - let mut bounds2 = vec![ - Bounds::from_min_max(Coord2(150.0, 250.0), Coord2(250.0, 350.0)), - ]; - bounds1.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); - bounds2.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + let mut bounds1 = vec![Bounds::from_min_max( + Coord2(100.0, 200.0), + Coord2(200.0, 300.0), + )]; + let mut bounds2 = vec![Bounds::from_min_max( + Coord2(150.0, 250.0), + Coord2(250.0, 350.0), + )]; + bounds1.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); + bounds2.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); @@ -81,11 +113,22 @@ fn sweep_against_double_overlap_1() { Bounds::from_min_max(Coord2(100.0, 200.0), Coord2(200.0, 300.0)), Bounds::from_min_max(Coord2(220.0, 330.0), Coord2(350.0, 450.0)), ]; - let mut bounds2 = vec![ - Bounds::from_min_max(Coord2(150.0, 250.0), Coord2(250.0, 350.0)), - ]; - bounds1.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); - bounds2.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + let mut bounds2 = vec![Bounds::from_min_max( + Coord2(150.0, 250.0), + Coord2(250.0, 350.0), + )]; + bounds1.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); + bounds2.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); @@ -94,15 +137,26 @@ fn sweep_against_double_overlap_1() { #[test] fn sweep_against_double_overlap_2() { - let mut bounds1 = vec![ - Bounds::from_min_max(Coord2(150.0, 250.0), Coord2(250.0, 350.0)), - ]; - let mut bounds2 = vec![ + let mut bounds1 = vec![Bounds::from_min_max( + Coord2(150.0, 250.0), + Coord2(250.0, 350.0), + )]; + let mut bounds2 = vec![ Bounds::from_min_max(Coord2(100.0, 200.0), Coord2(200.0, 300.0)), Bounds::from_min_max(Coord2(220.0, 330.0), Coord2(350.0, 450.0)), ]; - bounds1.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); - bounds2.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds1.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); + bounds2.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); @@ -119,8 +173,18 @@ fn sweep_against_quad_overlap() { Bounds::from_min_max(Coord2(190.0, 290.0), Coord2(290.0, 390.0)), Bounds::from_min_max(Coord2(0.0, 0.0), Coord2(1000.0, 1000.0)), ]; - bounds1.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); - bounds2.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds1.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); + bounds2.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); @@ -129,27 +193,38 @@ fn sweep_against_quad_overlap() { #[test] fn sweep_self_1000_random() { - let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let mut bounds = (0..1000).into_iter() + let mut rng = StdRng::from_seed([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]); + let mut bounds = (0..1000) + .into_iter() .map(|_| { let x = rng.gen::() * 900.0; let y = rng.gen::() * 900.0; let w = rng.gen::() * 400.0; let h = rng.gen::() * 400.0; - Bounds::from_min_max(Coord2(x, y), Coord2(x+w, y+h)) + Bounds::from_min_max(Coord2(x, y), Coord2(x + w, y + h)) }) .collect::>(); - bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal)); + bounds.sort_by(|b1, b2| { + b1.min() + .x() + .partial_cmp(&b2.min().x()) + .unwrap_or(Ordering::Equal) + }); - let collisions = sweep_self(bounds.iter()).collect::>(); + let collisions = sweep_self(bounds.iter()).collect::>(); // Use the slow approach to detecting the collisions to test against let mut slow_collisions = vec![]; for i1 in 0..bounds.len() { for i2 in 0..i1 { - if i1 == i2 { continue; } + if i1 == i2 { + continue; + } if bounds[i1].overlaps(&bounds[i2]) { slow_collisions.push((&bounds[i1], &bounds[i2])); From fedfdd9716368a31e3ed5f9487be98033010798b Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:02:05 +0100 Subject: [PATCH 02/31] remove unused imports --- tests/bezier/algorithms/fill_concave.rs | 1 - tests/bezier/algorithms/fill_convex.rs | 1 - tests/bezier/offset.rs | 1 - tests/bezier/path/intersection.rs | 1 - tests/bezier/path/rays.rs | 1 - tests/bezier/section.rs | 1 - tests/bezier/self_intersection.rs | 1 - 7 files changed, 7 deletions(-) diff --git a/tests/bezier/algorithms/fill_concave.rs b/tests/bezier/algorithms/fill_concave.rs index f93511a2..198ee6c2 100644 --- a/tests/bezier/algorithms/fill_concave.rs +++ b/tests/bezier/algorithms/fill_concave.rs @@ -1,7 +1,6 @@ use flo_curves::bezier::path::algorithms::*; use flo_curves::bezier::path::*; use flo_curves::bezier::*; -use flo_curves::*; fn circle_ray_cast( circle_center: Coord2, diff --git a/tests/bezier/algorithms/fill_convex.rs b/tests/bezier/algorithms/fill_convex.rs index 2eb83132..01d12d65 100644 --- a/tests/bezier/algorithms/fill_convex.rs +++ b/tests/bezier/algorithms/fill_convex.rs @@ -1,7 +1,6 @@ use flo_curves::bezier::path::algorithms::*; use flo_curves::bezier::path::*; use flo_curves::bezier::*; -use flo_curves::*; fn circle_ray_cast( circle_center: Coord2, diff --git a/tests/bezier/offset.rs b/tests/bezier/offset.rs index 2ca5fdf8..e678dafe 100644 --- a/tests/bezier/offset.rs +++ b/tests/bezier/offset.rs @@ -2,7 +2,6 @@ use flo_curves::bezier::NormalCurve; use flo_curves::bezier::*; use flo_curves::line; use flo_curves::line::Line2D; -use flo_curves::*; use std::f64; diff --git a/tests/bezier/path/intersection.rs b/tests/bezier/path/intersection.rs index bd9d061a..affd3f74 100644 --- a/tests/bezier/path/intersection.rs +++ b/tests/bezier/path/intersection.rs @@ -1,7 +1,6 @@ use flo_curves::arc::*; use flo_curves::bezier::path::*; use flo_curves::bezier::*; -use flo_curves::*; use std::f64; diff --git a/tests/bezier/path/rays.rs b/tests/bezier/path/rays.rs index b07e3a61..c37703bb 100644 --- a/tests/bezier/path/rays.rs +++ b/tests/bezier/path/rays.rs @@ -1,6 +1,5 @@ use flo_curves::bezier::path::*; use flo_curves::bezier::*; -use flo_curves::*; use std::collections::HashMap; diff --git a/tests/bezier/section.rs b/tests/bezier/section.rs index cfb4d027..cd84beb3 100644 --- a/tests/bezier/section.rs +++ b/tests/bezier/section.rs @@ -1,5 +1,4 @@ use flo_curves::bezier::*; -use flo_curves::*; #[test] fn section_points_match() { diff --git a/tests/bezier/self_intersection.rs b/tests/bezier/self_intersection.rs index d6071690..9e6fea50 100644 --- a/tests/bezier/self_intersection.rs +++ b/tests/bezier/self_intersection.rs @@ -1,5 +1,4 @@ use flo_curves::bezier::*; -use flo_curves::*; #[test] fn find_simple_self_intersection() { From 5599b11e0c99a042120b0f02652f31504ac8bb7e Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:50:37 +0100 Subject: [PATCH 03/31] fix clippy::redundant_field_names --- src/arc/circle.rs | 9 +++------ src/bezier/intersection/fat_line.rs | 12 ++++++------ src/bezier/path/algorithms/fill_convex.rs | 2 +- src/bezier/path/arithmetic/full_intersect.rs | 2 +- src/bezier/path/graph_path/edge.rs | 5 +---- src/bezier/path/graph_path/edge_ref.rs | 2 +- src/bezier/path/graph_path/mod.rs | 16 ++++++++-------- src/bezier/path/graph_path/path_collision.rs | 4 ++-- src/bezier/path/graph_path/ray_collision.rs | 9 +++------ src/bezier/path/ray.rs | 2 +- src/bezier/section.rs | 6 +++--- src/bezier/walk.rs | 12 ++++++------ tests/bezier/distort.rs | 1 - tests/bezier/path/arithmetic_intersect.rs | 1 - tests/bezier/path/to_curves.rs | 1 - tests/line/intersection.rs | 1 - tests/line/to_curve.rs | 1 - 17 files changed, 36 insertions(+), 50 deletions(-) diff --git a/src/arc/circle.rs b/src/arc/circle.rs index 79536c38..ba5be62b 100644 --- a/src/arc/circle.rs +++ b/src/arc/circle.rs @@ -38,10 +38,7 @@ impl Circle { /// Creates a new circle with a center and a radius /// pub fn new(center: Coord, radius: f64) -> Circle { - Circle { - center: center, - radius: radius, - } + Circle { center, radius } } /// @@ -50,8 +47,8 @@ impl Circle { pub fn arc<'a>(&'a self, start_radians: f64, end_radians: f64) -> CircularArc<'a, Coord> { CircularArc { circle: self, - start_radians: start_radians, - end_radians: end_radians, + start_radians, + end_radians, } } diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index 524d5bb4..e081373b 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -298,8 +298,8 @@ impl FatLine { }; FatLine { - d_min: d_min, - d_max: d_max, + d_min, + d_max, coeff: (a, b, c), } } @@ -371,8 +371,8 @@ impl FatLine { FatLine { coeff: (a, b, c), - d_min: d_min, - d_max: d_max, + d_min, + d_max, } } } @@ -392,8 +392,8 @@ mod test { let (a, b, c) = line_coefficients_2d(&line); FatLine { - d_min: d_min, - d_max: d_max, + d_min, + d_max, coeff: (a, b, c), } } diff --git a/src/bezier/path/algorithms/fill_convex.rs b/src/bezier/path/algorithms/fill_convex.rs index f223090c..258086ba 100644 --- a/src/bezier/path/algorithms/fill_convex.rs +++ b/src/bezier/path/algorithms/fill_convex.rs @@ -161,7 +161,7 @@ where stack.push(StackEntry { angle: theta..end_theta, - start_pos: start_pos, + start_pos, end_pos: end_pos.map(|(end_pos, _distance)| end_pos.position), }); } diff --git a/src/bezier/path/arithmetic/full_intersect.rs b/src/bezier/path/arithmetic/full_intersect.rs index e3179486..bf492356 100644 --- a/src/bezier/path/arithmetic/full_intersect.rs +++ b/src/bezier/path/arithmetic/full_intersect.rs @@ -111,7 +111,7 @@ where let exterior_from_path_2 = merged_path.exterior_paths(); PathIntersection { - intersecting_path: intersecting_path, + intersecting_path, exterior_paths: [exterior_from_path_1, exterior_from_path_2], } } diff --git a/src/bezier/path/graph_path/edge.rs b/src/bezier/path/graph_path/edge.rs index d1841820..4bfbb696 100644 --- a/src/bezier/path/graph_path/edge.rs +++ b/src/bezier/path/graph_path/edge.rs @@ -50,10 +50,7 @@ impl<'a, Point: 'a, Label: 'a + Copy> GraphEdge<'a, Point, Label> { test_assert!(edge.start_idx < graph.points.len()); test_assert!(edge.edge_idx < graph.points[edge.start_idx].forward_edges.len()); - GraphEdge { - graph: graph, - edge: edge, - } + GraphEdge { graph, edge } } /// diff --git a/src/bezier/path/graph_path/edge_ref.rs b/src/bezier/path/graph_path/edge_ref.rs index ac0cc0d5..a835d2a1 100644 --- a/src/bezier/path/graph_path/edge_ref.rs +++ b/src/bezier/path/graph_path/edge_ref.rs @@ -50,7 +50,7 @@ impl GraphPath { if edge.end_idx == edge_ref.start_idx { return GraphEdgeRef { start_idx: *connected_from, - edge_idx: edge_idx, + edge_idx, reverse: true, }; } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index f69c1d25..9673d726 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -244,7 +244,7 @@ impl GraphPath { // Create the graph path from the points let mut path = GraphPath { - points: points, + points, next_path_index: 1, }; path.recalculate_reverse_connections(); @@ -327,7 +327,7 @@ impl GraphPath { .into_iter() .map(move |edge_idx| GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }) }) @@ -350,7 +350,7 @@ impl GraphPath { self, GraphEdgeRef { start_idx: point_num, - edge_idx: edge_idx, + edge_idx, reverse: false, }, ) @@ -365,7 +365,7 @@ impl GraphPath { .into_iter() .map(move |edge_idx| GraphEdgeRef { start_idx: point_num, - edge_idx: edge_idx, + edge_idx, reverse: false, }) } @@ -402,7 +402,7 @@ impl GraphPath { { Some(GraphEdgeRef { start_idx: connected_from, - edge_idx: edge_idx, + edge_idx, reverse: true, }) } else { @@ -506,7 +506,7 @@ impl GraphPath { { Some(GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }) } else { @@ -594,7 +594,7 @@ impl GraphPath { // Remove this edge if it's very short let edge_ref = GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }; if self.edge_is_very_short(edge_ref) { @@ -989,7 +989,7 @@ impl GraphPath { // If this edge has a gap... if self.edge_has_gap(GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }) { // ... try to heal it diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index d11073d8..1e523e9b 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -58,7 +58,7 @@ impl GraphPath { }) .map(|(point_idx, edge_idx)| GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }) .map(|edge_ref| GraphEdge::new(self, edge_ref)) @@ -355,7 +355,7 @@ impl GraphPath { // Create a copy of the edge. Our future edges will all have the same kind and label as the edge that's being divided let edge = self.get_edge(GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }); let kind = edge.kind(); diff --git a/src/bezier/path/graph_path/ray_collision.rs b/src/bezier/path/graph_path/ray_collision.rs index b58260d1..af21cbc8 100644 --- a/src/bezier/path/graph_path/ray_collision.rs +++ b/src/bezier/path/graph_path/ray_collision.rs @@ -80,7 +80,7 @@ where .into_iter() .map(move |edge_idx| GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }) .collect() @@ -102,7 +102,7 @@ where }) .map(move |edge_idx| GraphEdgeRef { start_idx: *connected_point_idx, - edge_idx: edge_idx, + edge_idx, reverse: true, }) }) @@ -111,10 +111,7 @@ where #[inline] fn get_edge(&self, edge: GraphEdgeRef) -> Self::Curve { - GraphEdge { - graph: *self, - edge: edge, - } + GraphEdge { graph: *self, edge } } #[inline] diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index 3d4fcfa8..7a361a33 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -234,7 +234,7 @@ where for edge_idx in 0..(path.num_edges(point_idx)) { let edge_ref = GraphEdgeRef { start_idx: point_idx, - edge_idx: edge_idx, + edge_idx, reverse: false, }; let edge = path.get_edge(edge_ref); diff --git a/src/bezier/section.rs b/src/bezier/section.rs index 2007cdb0..345bfa1f 100644 --- a/src/bezier/section.rs +++ b/src/bezier/section.rs @@ -32,9 +32,9 @@ impl<'a, C: 'a + BezierCurve> CurveSection<'a, C> { let t_m = t_max - t_c; CurveSection { - curve: curve, - t_m: t_m, - t_c: t_c, + curve, + t_m, + t_c, cached_control_points: RefCell::new(None), } } diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index f6fcadd4..ec21eca7 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -18,14 +18,14 @@ pub fn walk_curve_unevenly<'a, Curve: BezierCurve>( ) -> impl 'a + Iterator> { if num_subdivisions > 0 { UnevenWalkIterator { - curve: curve, + curve, step: (1.0) / (num_subdivisions as f64), - num_subdivisions: num_subdivisions, + num_subdivisions, last_subdivision: 0, } } else { UnevenWalkIterator { - curve: curve, + curve, step: 0.0, num_subdivisions: 0, last_subdivision: 0, @@ -75,13 +75,13 @@ pub fn walk_curve_evenly<'a, Curve: BezierCurve>( }; EvenWalkIterator { - curve: curve, + curve, derivative: (wn1, wn2, wn3), last_t: 0.0, last_point: curve.start_point(), last_increment: initial_increment, - distance: distance, - max_error: max_error, + distance, + max_error, } } diff --git a/tests/bezier/distort.rs b/tests/bezier/distort.rs index 00a81921..656fff85 100644 --- a/tests/bezier/distort.rs +++ b/tests/bezier/distort.rs @@ -1,5 +1,4 @@ use flo_curves::bezier::*; -use flo_curves::geo::*; #[test] fn line_to_sine_wave() { diff --git a/tests/bezier/path/arithmetic_intersect.rs b/tests/bezier/path/arithmetic_intersect.rs index 6420c37f..6512ea2d 100644 --- a/tests/bezier/path/arithmetic_intersect.rs +++ b/tests/bezier/path/arithmetic_intersect.rs @@ -2,7 +2,6 @@ use flo_curves::arc::*; use flo_curves::bezier::path::*; use flo_curves::bezier::*; use flo_curves::line::*; -use flo_curves::*; use std::f64; use std::iter; diff --git a/tests/bezier/path/to_curves.rs b/tests/bezier/path/to_curves.rs index 4d96d5a1..ffcaeadb 100644 --- a/tests/bezier/path/to_curves.rs +++ b/tests/bezier/path/to_curves.rs @@ -1,6 +1,5 @@ use flo_curves::bezier::path::*; use flo_curves::bezier::*; -use flo_curves::*; #[test] pub fn convert_path_to_bezier_curves() { diff --git a/tests/line/intersection.rs b/tests/line/intersection.rs index 2bda1c16..d52e9d13 100644 --- a/tests/line/intersection.rs +++ b/tests/line/intersection.rs @@ -1,5 +1,4 @@ use flo_curves::line::*; -use flo_curves::*; #[test] fn intersection_at_0_0() { diff --git a/tests/line/to_curve.rs b/tests/line/to_curve.rs index df0b76a5..ef51620d 100644 --- a/tests/line/to_curve.rs +++ b/tests/line/to_curve.rs @@ -1,6 +1,5 @@ use flo_curves::bezier::*; use flo_curves::line::*; -use flo_curves::*; #[test] fn convert_line_to_bezier_curve() { From 1e69ddb6d54508dd6693cf8f93e28cd06379ef32 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:51:48 +0100 Subject: [PATCH 04/31] fix clippy::collapsible_if and clippy::collapsible_else_if --- src/bezier/characteristics.rs | 26 ++++++++++---------------- src/bezier/offset_scaling.rs | 23 ++++++++++++----------- src/bezier/path/graph_path/mod.rs | 11 +++++------ src/bezier/path/point.rs | 8 +++----- src/bezier/walk.rs | 12 +++++------- 5 files changed, 35 insertions(+), 45 deletions(-) diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index 21015d03..63682c43 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -179,18 +179,14 @@ fn characterize_from_canonical_point(b4: (f64, f64)) -> CurveCategory { // Loop is outside of 0<=t<=1 (double point is t > 1) CurveCategory::Arch } + } else if y >= 1.0 { + CurveCategory::SingleInflectionPoint + } else if x <= 0.0 { + CurveCategory::DoubleInflectionPoint + } else if (x - 3.0).abs() <= f64::EPSILON && (y - 0.0).abs() <= f64::EPSILON { + CurveCategory::Parabolic } else { - if y >= 1.0 { - CurveCategory::SingleInflectionPoint - } else if x <= 0.0 { - CurveCategory::DoubleInflectionPoint - } else { - if (x - 3.0).abs() <= f64::EPSILON && (y - 0.0).abs() <= f64::EPSILON { - CurveCategory::Parabolic - } else { - CurveCategory::Arch - } - } + CurveCategory::Arch } } @@ -296,12 +292,10 @@ fn find_inflection_points(b4: (f64, f64)) -> InflectionPoints { } else { InflectionPoints::One(t2) } + } else if t2 < 0.0 || t2 > 1.0 { + InflectionPoints::One(t1) } else { - if t2 < 0.0 || t2 > 1.0 { - InflectionPoints::One(t1) - } else { - InflectionPoints::Two(t1, t2) - } + InflectionPoints::Two(t1, t2) } } } diff --git a/src/bezier/offset_scaling.rs b/src/bezier/offset_scaling.rs index 2b9ac944..fecef7db 100644 --- a/src/bezier/offset_scaling.rs +++ b/src/bezier/offset_scaling.rs @@ -160,20 +160,21 @@ where let intersect_point = ray_intersects_ray(&(start, start + normal_start), &(end, end + normal_end)); - if intersect_point.is_none() { - if characterize_curve(curve) != CurveCategory::Linear && depth < MAX_DEPTH { - // Collinear normals - let divide_point = 0.5; + if intersect_point.is_none() + && characterize_curve(curve) != CurveCategory::Linear + && depth < MAX_DEPTH + { + // Collinear normals + let divide_point = 0.5; - let mid_offset = initial_offset + (final_offset - initial_offset) * divide_point; - let left_curve = curve.subsection(0.0, divide_point); - let right_curve = curve.subsection(divide_point, 1.0); + let mid_offset = initial_offset + (final_offset - initial_offset) * divide_point; + let left_curve = curve.subsection(0.0, divide_point); + let right_curve = curve.subsection(divide_point, 1.0); - let left_offset = subdivide_offset(&left_curve, initial_offset, mid_offset, depth + 1); - let right_offset = subdivide_offset(&right_curve, mid_offset, final_offset, depth + 1); + let left_offset = subdivide_offset(&left_curve, initial_offset, mid_offset, depth + 1); + let right_offset = subdivide_offset(&right_curve, mid_offset, final_offset, depth + 1); - return left_offset.into_iter().chain(right_offset).collect(); - } + return left_offset.into_iter().chain(right_offset).collect(); } if let Some(intersect_point) = intersect_point { diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index 9673d726..9ea09dc5 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -183,12 +183,11 @@ impl GraphPath { // Iterate through the points in the path for (cp1, cp2, end_point) in path.points() { // Ignore points that are too close to the last point - if end_point.is_near_to(&last_point_pos, CLOSE_DISTANCE) { - if cp1.is_near_to(&last_point_pos, CLOSE_DISTANCE) - && cp2.is_near_to(&cp1, CLOSE_DISTANCE) - { - continue; - } + if end_point.is_near_to(&last_point_pos, CLOSE_DISTANCE) + && cp1.is_near_to(&last_point_pos, CLOSE_DISTANCE) + && cp2.is_near_to(&cp1, CLOSE_DISTANCE) + { + continue; } // Push the points diff --git a/src/bezier/path/point.rs b/src/bezier/path/point.rs index 33522c06..a3ffbe20 100644 --- a/src/bezier/path/point.rs +++ b/src/bezier/path/point.rs @@ -130,12 +130,10 @@ where fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize { if edge.reverse { unimplemented!() + } else if edge.start_idx + 1 == self.len() { + 0 } else { - if edge.start_idx + 1 == self.len() { - 0 - } else { - edge.start_idx + 1 - } + edge.start_idx + 1 } } diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index ec21eca7..ebd750e2 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -201,13 +201,11 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { return None; } - if next_t >= 1.0 { - if last_point.distance_to(&curve.point_at_pos(1.0)) < distance { - // End point is closer than the target distance - let last_section = curve.section(last_t, 1.0); - self.last_t = 1.0; - return Some(last_section); - } + if next_t >= 1.0 && last_point.distance_to(&curve.point_at_pos(1.0)) < distance { + // End point is closer than the target distance + let last_section = curve.section(last_t, 1.0); + self.last_t = 1.0; + return Some(last_section); } let mut count = 0; From 84621cbd395ba1f39947e86e63e44140f86940be Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:54:01 +0100 Subject: [PATCH 05/31] fix clippy::write_with_newline and clippy::write_literal --- src/debug/graph_path_debug.rs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/debug/graph_path_debug.rs b/src/debug/graph_path_debug.rs index cb16867d..72ca94d6 100644 --- a/src/debug/graph_path_debug.rs +++ b/src/debug/graph_path_debug.rs @@ -37,7 +37,7 @@ pub fn graph_path_svg_string( let end_point = edge.end_point(); let (cp1, cp2) = edge.control_points(); - write!(result, "\n", + writeln!(result, "", index, start_point.x(), start_point.y(), cp1.x(), cp1.y(), @@ -56,17 +56,17 @@ pub fn graph_path_svg_string( GraphPathEdgeKind::Interior => "green", }; - write!(result, "\n", + writeln!(result, "", start_point.x(), start_point.y(), cp1.x(), cp1.y(), cp2.x(), cp2.y(), end_point.x(), end_point.y(), kind).unwrap(); - write!(result, "\n", end_point.x(), end_point.y()).unwrap(); + writeln!(result, "", end_point.x(), end_point.y()).unwrap(); - write!( + writeln!( result, - "{} <- {} - {}\n", + "{} <- {} - {}", end_point.x() + 4.0, end_point.y() + 8.0, edge.end_point_index(), @@ -80,9 +80,9 @@ pub fn graph_path_svg_string( } for (p1, p2) in rays { - write!( + writeln!( result, - "\n", + "", p1.x(), p1.y(), p2.x(), @@ -99,7 +99,7 @@ pub fn graph_path_svg_string( let p1 = p1 - (point_offset * 1000.0); let p2 = p2 + (point_offset * 1000.0); - write!(result, "\n", + writeln!(result, "", p1.x(), p1.y(), p2.x(), p2.y()).unwrap(); @@ -130,7 +130,7 @@ pub fn graph_path_svg_string( let end_point = edge.end_point(); let (cp1, cp2) = edge.control_points(); - write!(result, "\n", + writeln!(result, "", collision_num, path_number, start_point.x(), start_point.y(), cp1.x(), cp1.y(), @@ -142,23 +142,22 @@ pub fn graph_path_svg_string( let cp1 = (cp1 - offset) * scale; let cp2 = (cp2 - offset) * scale; - write!(result, "\n", + writeln!(result, "", start_point.x(), start_point.y(), cp1.x(), cp1.y(), cp2.x(), cp2.y(), - end_point.x(), end_point.y(), - "cyan").unwrap(); + end_point.x(), end_point.y()).unwrap(); - write!( + writeln!( result, - "\n", + "", pos.x(), pos.y() ) .unwrap(); - write!( + writeln!( result, - "{}: C{} ({})\n", + "{}: C{} ({})", pos.x() + 2.0, pos.y() + 3.0, collision_num, From 061fcc7cbe8ae6ca77e7ed6c8ae15ca273af87d2 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:55:07 +0100 Subject: [PATCH 06/31] fix clippy::clone_on_copy --- src/bezier/characteristics.rs | 6 +++--- src/bezier/intersection/fat_line.rs | 4 ++-- src/bezier/path/algorithms/fill_concave.rs | 20 ++++++++++---------- src/bezier/path/algorithms/fill_convex.rs | 6 +++--- src/bezier/path/graph_path/edge.rs | 8 ++++---- src/bezier/path/graph_path/mod.rs | 4 ++-- src/bezier/path/graph_path/path_collision.rs | 14 +++++++------- src/bezier/path/is_clockwise.rs | 2 +- src/bezier/path/path.rs | 2 +- src/bezier/section.rs | 3 +-- src/bezier/walk.rs | 2 +- src/geo/bounding_box.rs | 2 +- src/line/line.rs | 2 +- tests/bezier/algorithms/fill_concave.rs | 20 ++++++++++---------- tests/bezier/algorithms/fill_convex.rs | 4 ++-- 15 files changed, 49 insertions(+), 50 deletions(-) diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index 63682c43..c9b809b3 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -224,7 +224,7 @@ pub fn characterize_cubic_bezier( CurveCategory::Linear } else { // w2 and w3 are the same. If w1, w2, w3 and w4 are collinear then we have a straight line, otherwise we have a curve with an inflection point. - let line = (w1.clone(), w3.clone()); + let line = (*w1, *w3); let (a, b, c) = line_coefficients_2d(&line); let distance = a * w4.x() + b * w4.y() + c; @@ -333,7 +333,7 @@ fn features_from_canonical_point( find_inflection_points((x, y)).into() } CurveCategory::Loop => { - let curve = Curve::from_points(w1.clone(), (w2.clone(), w3.clone()), w4.clone()); + let curve = Curve::from_points(*w1, (*w2, *w3), *w4); let loop_pos = find_self_intersection_point(&curve, accuracy); // TODO: if we can't find the loop_pos, we could probably find a cusp position instead @@ -381,7 +381,7 @@ pub fn features_for_cubic_bezier( CurveFeatures::Linear } else { // w2 and w3 are the same. If w1, w2, w3 and w4 are collinear then we have a straight line, otherwise we have a curve with an inflection point. - let line = (w1.clone(), w3.clone()); + let line = (*w1, *w3); let (a, b, c) = line_coefficients_2d(&line); let distance = a * w4.x() + b * w4.y() + c; diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index e081373b..407466e2 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -585,7 +585,7 @@ mod test { Coord2(5.0, 8.0), ); - let mut clipped = clip_curve.clone(); + let mut clipped = clip_curve; // Should be able to iteratively refine to a curve clipped to the fat line for _x in 0..5 { @@ -707,7 +707,7 @@ mod test { Coord2(5.0, 8.0), ); - let mut clipped = clip_curve.clone(); + let mut clipped = clip_curve; for _x in 0..100 { let next_clipped = fat_line.clip(&clipped); diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index ec25cd04..6cd7b474 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -65,8 +65,8 @@ where // Add to the list of long edges if it's long enough to need further ray-casting if edge_distance_squared >= edge_min_len_squared { long_edges.push(LongEdge { - start: edges[last_edge].position.clone(), - end: edges[edge_num].position.clone(), + start: edges[last_edge].position, + end: edges[edge_num].position, edge_index: (last_edge, edge_num), ray_collided: false, }) @@ -84,7 +84,7 @@ where Coord: Coordinate + Coordinate2D, { // Determine where the 'to' point is along this ray - let ray = (center.clone(), from.clone()); + let ray = (*center, *from); let pos = ray.pos_for_point(to); // Position will be > 1.0 if the 'to' position is further away that 'from' @@ -138,7 +138,7 @@ fn remove_small_gaps( if distance_sq <= min_gap_sq { // Move all the points between the two 'long' edges onto a line between the start and end point // Alternatively: could remove the points here to produce a smoother shape later on - let gap_line = (edge1.start.clone(), edge2.end.clone()); + let gap_line = (edge1.start, edge2.end); let mut edge_num = edge1.edge_index.1; loop { @@ -149,9 +149,9 @@ fn remove_small_gaps( // Map this edge to the gap line let edge = &mut edges[edge_num]; - let edge_ray = (center.clone(), edge.position.clone()); + let edge_ray = (*center, edge.position); edge.position = line_intersects_ray(&edge_ray, &gap_line) - .unwrap_or_else(|| edge.position.clone()); + .unwrap_or_else(|| edge.position); // Move to the next edge edge_num += 1; @@ -254,8 +254,8 @@ where // Generate a version of the raycasting function that inspects the existing list of long edges let cast_ray_to_edges = |from: Coord, to: Coord| { // Generate the edge collisions from the main raycasting function - let edge_collisions = cast_ray(from.clone(), to.clone()); - let ray_line = (from.clone(), to.clone()); + let edge_collisions = cast_ray(from, to); + let ray_line = (from, to); // Generate the collisions with the 'long edges' where we'll be considering casting more rays later on let extra_collisions = long_edges @@ -264,7 +264,7 @@ where .filter(|(edge_index, _edge)| *edge_index != long_edge_index) .filter_map(move |(edge_index, edge)| { // Create lines from the ray and the lines - let edge_line = (edge.start.clone(), edge.end.clone()); + let edge_line = (edge.start, edge.end); // Detect where they intersect if let Some(intersection_point) = line_intersects_ray(&edge_line, &ray_line) @@ -389,7 +389,7 @@ where let curves = fit_curve::>( &collisions .iter() - .map(|collision| collision.position.clone()) + .map(|collision| collision.position) .collect::>(), options.fit_error, ); diff --git a/src/bezier/path/algorithms/fill_convex.rs b/src/bezier/path/algorithms/fill_convex.rs index 258086ba..8402ca7e 100644 --- a/src/bezier/path/algorithms/fill_convex.rs +++ b/src/bezier/path/algorithms/fill_convex.rs @@ -184,7 +184,7 @@ where let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); let mid_ray_pos = mid_ray .as_ref() - .map(|(collision, _)| collision.position.clone()); + .map(|(collision, _)| collision.position); // If there's a discontinuity (eg, a corner we can't see around), we'll see that the mid point is very close to the end point and far from the start point if let Some(mid_ray_pos) = mid_ray_pos { @@ -227,7 +227,7 @@ where let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); let mid_ray_pos = mid_ray .as_ref() - .map(|(collision, _)| collision.position.clone()); + .map(|(collision, _)| collision.position); // Divide into two pairs of ranges (process the earlier one first) stack.push(StackEntry { @@ -272,7 +272,7 @@ where let curves = fit_curve::>( &collisions .iter() - .map(|collision| collision.position.clone()) + .map(|collision| collision.position) .collect::>(), options.fit_error, ); diff --git a/src/bezier/path/graph_path/edge.rs b/src/bezier/path/graph_path/edge.rs index 4bfbb696..cc1db27a 100644 --- a/src/bezier/path/graph_path/edge.rs +++ b/src/bezier/path/graph_path/edge.rs @@ -119,7 +119,7 @@ impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> BezierCurve for GraphEdge<'a, /// #[inline] fn start_point(&self) -> Self::Point { - self.graph.points[self.start_point_index()].position.clone() + self.graph.points[self.start_point_index()].position } /// @@ -127,7 +127,7 @@ impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> BezierCurve for GraphEdge<'a, /// #[inline] fn end_point(&self) -> Self::Point { - self.graph.points[self.end_point_index()].position.clone() + self.graph.points[self.end_point_index()].position } /// @@ -138,9 +138,9 @@ impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> BezierCurve for GraphEdge<'a, let edge = self.edge(); if self.edge.reverse { - (edge.cp2.clone(), edge.cp1.clone()) + (edge.cp2, edge.cp1) } else { - (edge.cp1.clone(), edge.cp2.clone()) + (edge.cp1, edge.cp2) } } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index 9ea09dc5..a2519be0 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -374,7 +374,7 @@ impl GraphPath { /// #[inline] pub fn point_position(&self, point_num: usize) -> Point { - self.points[point_num].position.clone() + self.points[point_num].position } /// @@ -1120,7 +1120,7 @@ impl GraphPath { } // Start point of the path is the initial point we checked - let start_point = self.points[point_idx].position.clone(); + let start_point = self.points[point_idx].position; let new_path = POut::from_points(start_point, path_points); exterior_paths.push(new_path); diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index 1e523e9b..d71a285f 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -599,7 +599,7 @@ impl GraphPath { let end_point = &self.points[end_point_idx].position; if start_point.is_near_to(end_point, accuracy) { - self.points[end_point_idx].position = self.points[point_idx].position.clone(); + self.points[end_point_idx].position = self.points[point_idx].position; } } } @@ -629,14 +629,14 @@ impl GraphPath { let moved = p1_pos.is_some() || p2_pos.is_some(); let p1_pos = if let Some(pos) = p1_pos { - pos.clone() + *pos } else { - self.points[*p1_idx].position.clone() + self.points[*p1_idx].position }; let p2_pos = if let Some(pos) = p2_pos { - pos.clone() + *pos } else { - self.points[*p2_idx].position.clone() + self.points[*p2_idx].position }; if !moved || Self::point_is_near(&p1_pos, &p2_pos, min_distance_squared) { @@ -644,7 +644,7 @@ impl GraphPath { let pos = Self::snap_points(&p1_pos, &p2_pos); let remap_idx = usize::min(*p1_idx, *p2_idx); - remapped_points[p1_orig_idx] = (remap_idx, Some(pos.clone())); + remapped_points[p1_orig_idx] = (remap_idx, Some(pos)); remapped_points[p2_orig_idx] = (remap_idx, Some(pos)); } @@ -662,7 +662,7 @@ impl GraphPath { for original_idx in 0..self.points.len() { if let (new_idx, Some(new_pos)) = &remapped_points[original_idx] { // This point has been moved - self.points[original_idx].position = new_pos.clone(); + self.points[original_idx].position = *new_pos; // If this is the target point, then don't move any edges if *new_idx == original_idx { diff --git a/src/bezier/path/is_clockwise.rs b/src/bezier/path/is_clockwise.rs index f5b1ff96..edcd0371 100644 --- a/src/bezier/path/is_clockwise.rs +++ b/src/bezier/path/is_clockwise.rs @@ -15,7 +15,7 @@ pub fn points_are_clockwise BezierPath for (Point, Vec<(Point, Point, Point) /// Retrieves the initial point of this path /// fn start_point(&self) -> Self::Point { - self.0.clone() + self.0 } /// diff --git a/src/bezier/section.rs b/src/bezier/section.rs index 345bfa1f..d3c9e665 100644 --- a/src/bezier/section.rs +++ b/src/bezier/section.rs @@ -112,7 +112,7 @@ impl<'a, C: 'a + BezierCurve> BezierCurve for CurveSection<'a, C> { /// The control points in this curve /// fn control_points(&self) -> (Self::Point, Self::Point) { - self.cached_control_points + *self.cached_control_points .borrow_mut() .get_or_insert_with(move || { // This is the de-casteljau subdivision algorithm (ran twice to cut out a section of the curve) @@ -153,7 +153,6 @@ impl<'a, C: 'a + BezierCurve> BezierCurve for CurveSection<'a, C> { // Curve is (w1, wn1, wnn1, p) so control points are wn1 and wnn1 (wn1, wnn1) }) - .clone() } /// diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index ebd750e2..4c42cc21 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -193,7 +193,7 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { let mut t_increment = self.last_increment; let last_t = self.last_t; let mut next_t = last_t + t_increment; - let last_point = self.last_point.clone(); + let last_point = self.last_point; let mut next_point; // If the next point appears to be after the end of the curve, and the end of the curve is further away than the closest distance, return None diff --git a/src/geo/bounding_box.rs b/src/geo/bounding_box.rs index ef4adc7c..091f65b7 100644 --- a/src/geo/bounding_box.rs +++ b/src/geo/bounding_box.rs @@ -21,7 +21,7 @@ pub trait BoundingBox: Geo + Sized { let first_point = points.next(); if let Some(first_point) = first_point { // min, max is just the first point initially - let (mut min, mut max) = (first_point.clone(), first_point); + let (mut min, mut max) = (first_point, first_point); // Update with the remainder of the points for point in points { diff --git a/src/line/line.rs b/src/line/line.rs index 7b7a3940..ebb75210 100644 --- a/src/line/line.rs +++ b/src/line/line.rs @@ -98,7 +98,7 @@ impl Line for (Point, Point) { /// #[inline] fn points(&self) -> (Self::Point, Self::Point) { - self.clone() + *self } } diff --git a/tests/bezier/algorithms/fill_concave.rs b/tests/bezier/algorithms/fill_concave.rs index 198ee6c2..ddcb988d 100644 --- a/tests/bezier/algorithms/fill_concave.rs +++ b/tests/bezier/algorithms/fill_concave.rs @@ -158,7 +158,7 @@ fn fill_concave_doughnut() { let outer_circle = circle_ray_cast(circle_center, outer_radius); let inner_circle = circle_ray_cast(circle_center, inner_radius); let doughnut = |from: Coord2, to: Coord2| { - outer_circle(from.clone(), to.clone()) + outer_circle(from, to) .into_iter() .chain(inner_circle(from, to)) }; @@ -204,8 +204,8 @@ fn fill_doughnut_with_extra_holes() { let outer_circle = circle_ray_cast(circle_center, outer_radius); let inner_circle = circle_ray_cast(circle_center, inner_radius); let doughnut = |from: Coord2, to: Coord2| { - let inner_collisions = inner_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); + let inner_collisions = inner_circle(from, to); + let outer_collisions = outer_circle(from, to); let ray = to - from; if (ray.x() / ray.y()).abs() < 0.1 || (ray.y() / ray.x()) < 0.1 { @@ -258,8 +258,8 @@ fn fill_circle_without_escaping_gaps() { let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); let outer_circle = circle_ray_cast(circle_center, outer_radius); let doughnut = |from: Coord2, to: Coord2| { - let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); + let enclosing_collisions = enclosing_circle(from, to); + let outer_collisions = outer_circle(from, to); let ray = to - from; if (ray.x() / ray.y()).abs() < 0.01 { @@ -302,8 +302,8 @@ fn fill_circle_without_escaping_gaps_offset() { let enclosing_circle = circle_ray_cast(circle_center, enclosing_radius); let outer_circle = circle_ray_cast(circle_center, outer_radius); let doughnut = |from: Coord2, to: Coord2| { - let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); + let enclosing_collisions = enclosing_circle(from, to); + let outer_collisions = outer_circle(from, to); let ray = to - from; if (ray.x() / ray.y()).abs() < 0.01 { @@ -348,9 +348,9 @@ fn fill_doughnut_without_escaping_gaps() { let outer_circle = circle_ray_cast(circle_center, outer_radius); let inner_circle = circle_ray_cast(circle_center, inner_radius); let doughnut = |from: Coord2, to: Coord2| { - let enclosing_collisions = enclosing_circle(from.clone(), to.clone()); - let inner_collisions = inner_circle(from.clone(), to.clone()); - let outer_collisions = outer_circle(from.clone(), to.clone()); + let enclosing_collisions = enclosing_circle(from, to); + let inner_collisions = inner_circle(from, to); + let outer_collisions = outer_circle(from, to); let ray = to - from; if (ray.x() / ray.y()).abs() < 0.01 { diff --git a/tests/bezier/algorithms/fill_convex.rs b/tests/bezier/algorithms/fill_convex.rs index 01d12d65..e5b16891 100644 --- a/tests/bezier/algorithms/fill_convex.rs +++ b/tests/bezier/algorithms/fill_convex.rs @@ -97,7 +97,7 @@ fn trace_convex_doughnut() { let outer_circle = circle_ray_cast(circle_center, outer_radius); let inner_circle = circle_ray_cast(circle_center, inner_radius); let doughnut = |from: Coord2, to: Coord2| { - outer_circle(from.clone(), to.clone()) + outer_circle(from, to) .into_iter() .chain(inner_circle(from, to)) }; @@ -130,7 +130,7 @@ fn fill_convex_doughnut() { let outer_circle = circle_ray_cast(circle_center, outer_radius); let inner_circle = circle_ray_cast(circle_center, inner_radius); let doughnut = |from: Coord2, to: Coord2| { - outer_circle(from.clone(), to.clone()) + outer_circle(from, to) .into_iter() .chain(inner_circle(from, to)) }; From e8446acddfedc1caad4117510f93f8f4e0bf81c6 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:57:40 +0100 Subject: [PATCH 07/31] fix clippy::question_mark --- src/geo/sweep.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/geo/sweep.rs b/src/geo/sweep.rs index e012bdeb..bbb135e5 100644 --- a/src/geo/sweep.rs +++ b/src/geo/sweep.rs @@ -86,12 +86,7 @@ where // Attempt to fill the pending queue by reading from the bounds iterator loop { // Read the next item and retrieve its bounding box - let next_item = if let Some(next_item) = self.bounds_iterator.next() { - next_item - } else { - // No more items to read, and the pending queue is empty - return None; - }; + let next_item = self.bounds_iterator.next()?; // Fetch the bounding box let next_bounds = next_item.get_bounding_box::>(); @@ -188,11 +183,7 @@ where // Read a new target item. Target items determine the sweep position (we read things in order such that there'll be no collisions before this point) let next_tgt = self.tgt_iterator.next(); - let next_tgt = if let Some(next_tgt) = next_tgt { - next_tgt - } else { - return None; - }; + let next_tgt = next_tgt?; let next_tgt_bounds = next_tgt.get_bounding_box::>(); From 5463bcf13dee98e7e2ee850e66092de7cfd775e6 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 10:58:19 +0100 Subject: [PATCH 08/31] fix clippy::len_zero --- src/bezier/intersection/curve_curve_clip.rs | 6 +- src/bezier/intersection/self_intersection.rs | 4 +- src/bezier/offset_scaling.rs | 2 +- src/bezier/path/algorithms/fill_concave.rs | 4 +- src/bezier/path/algorithms/fill_convex.rs | 2 +- src/bezier/path/arithmetic/add.rs | 8 +-- src/bezier/path/arithmetic/cut.rs | 4 +- src/bezier/path/arithmetic/full_intersect.rs | 4 +- src/bezier/path/arithmetic/intersect.rs | 4 +- src/bezier/path/arithmetic/ray_cast.rs | 2 +- src/bezier/path/arithmetic/sub.rs | 4 +- src/bezier/path/graph_path/mod.rs | 2 +- src/bezier/path/graph_path/path_collision.rs | 12 ++-- src/bezier/path/path_builder.rs | 2 +- src/bezier/path/ray.rs | 4 +- tests/bezier/algorithms/fill_concave.rs | 10 +-- tests/bezier/algorithms/fill_convex.rs | 4 +- tests/bezier/curve_intersection_clip.rs | 66 +++++++++---------- tests/bezier/intersection.rs | 16 ++--- tests/bezier/path/arithmetic_add.rs | 4 +- .../path/arithmetic_complicated_paths.rs | 14 ++-- tests/bezier/path/arithmetic_cut.rs | 4 +- tests/bezier/path/arithmetic_intersect.rs | 16 ++--- tests/bezier/path/arithmetic_sub.rs | 8 +-- tests/bezier/path/graph_path.rs | 34 +++++----- tests/bezier/path/intersection.rs | 6 +- tests/bezier/path/rays.rs | 6 +- tests/bezier/path/to_curves.rs | 2 +- tests/bezier/search.rs | 2 +- 29 files changed, 128 insertions(+), 128 deletions(-) diff --git a/src/bezier/intersection/curve_curve_clip.rs b/src/bezier/intersection/curve_curve_clip.rs index af8ffa34..6a1ad19f 100644 --- a/src/bezier/intersection/curve_curve_clip.rs +++ b/src/bezier/intersection/curve_curve_clip.rs @@ -50,7 +50,7 @@ where .collect::>(); // Rarely: the linear section might be very short and the solver might miss that it's essentially a point - if curve_intersections.len() == 0 && ray_intersections.len() != 0 { + if curve_intersections.is_empty() && !ray_intersections.is_empty() { // If the linear section seems short if linear_section .point_at_pos(0.0) @@ -154,10 +154,10 @@ fn join_subsections<'a, C: BezierCurve>( where C::Point: Coordinate2D, { - if left.len() == 0 { + if left.is_empty() { // No further work to do right - } else if right.len() == 0 { + } else if right.is_empty() { // No further work to do left } else { diff --git a/src/bezier/intersection/self_intersection.rs b/src/bezier/intersection/self_intersection.rs index 14da6338..a078a481 100644 --- a/src/bezier/intersection/self_intersection.rs +++ b/src/bezier/intersection/self_intersection.rs @@ -58,14 +58,14 @@ where // Can find the intersection by using the clipping algorithm let intersections = curve_intersects_curve_clip(&left, &right, accuracy); - if intersections.len() == 0 + if intersections.is_empty() && left.start_point().is_near_to(&right.end_point(), accuracy) { // Didn't find an intersection but the left and right curves start and end at the same position return Some((left.t_for_t(0.0), right.t_for_t(1.0))); } - test_assert!(intersections.len() != 0); + test_assert!(!intersections.is_empty()); if intersections.len() == 1 { // Only found a single intersection diff --git a/src/bezier/offset_scaling.rs b/src/bezier/offset_scaling.rs index fecef7db..4dd334ef 100644 --- a/src/bezier/offset_scaling.rs +++ b/src/bezier/offset_scaling.rs @@ -191,7 +191,7 @@ where let mut extremeties = curve.find_extremities(); extremeties.retain(|item| item > &0.01 && item < &0.99); - if extremeties.len() == 0 || true { + if extremeties.is_empty() || true { // No extremeties (or they're all too close to the edges) let divide_point = 0.5; diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index 6cd7b474..f37b6be6 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -168,7 +168,7 @@ fn remove_small_gaps( } // Remove any long edges that were affected by the gap removal operation - if long_edges_to_remove.len() > 0 { + if !long_edges_to_remove.is_empty() { long_edges_to_remove.sort(); for long_edge_num in long_edges_to_remove.into_iter().rev() { long_edges.remove(long_edge_num); @@ -395,7 +395,7 @@ where ); if let Some(curves) = curves { - if curves.len() > 0 { + if !curves.is_empty() { // Convert the curves into a path let initial_point = curves[0].start_point(); let overlapped_path = Path::from_points( diff --git a/src/bezier/path/algorithms/fill_convex.rs b/src/bezier/path/algorithms/fill_convex.rs index 8402ca7e..53313801 100644 --- a/src/bezier/path/algorithms/fill_convex.rs +++ b/src/bezier/path/algorithms/fill_convex.rs @@ -278,7 +278,7 @@ where ); if let Some(curves) = curves { - if curves.len() > 0 { + if !curves.is_empty() { // Convert the curves into a path let initial_point = curves[0].start_point(); Some(Path::from_points( diff --git a/src/bezier/path/arithmetic/add.rs b/src/bezier/path/arithmetic/add.rs index eba3f4a2..dcc1f2d8 100644 --- a/src/bezier/path/arithmetic/add.rs +++ b/src/bezier/path/arithmetic/add.rs @@ -49,9 +49,9 @@ where POut: BezierPathFactory, { // If either path is empty, short-circuit by returning the other - if path1.len() == 0 { + if path1.is_empty() { return path2.iter().map(|path| POut::from_path(path)).collect(); - } else if path2.len() == 0 { + } else if path2.is_empty() { return path1.iter().map(|path| POut::from_path(path)).collect(); } @@ -117,7 +117,7 @@ where // Produce the final result let result = merged_path.exterior_paths(); - test_assert!(result.len() != 0 || path.len() == 0); + test_assert!(!result.is_empty() || path.is_empty()); result } @@ -160,7 +160,7 @@ where // Produce the final result let result = merged_path.exterior_paths(); - test_assert!(result.len() != 0 || path.len() == 0); + test_assert!(!result.is_empty() || path.is_empty()); result } diff --git a/src/bezier/path/arithmetic/cut.rs b/src/bezier/path/arithmetic/cut.rs index 147ea15b..25cd88b7 100644 --- a/src/bezier/path/arithmetic/cut.rs +++ b/src/bezier/path/arithmetic/cut.rs @@ -29,12 +29,12 @@ where POut: BezierPathFactory, { // If path1 is empty, then there are no points in the result. If path2 is empty, then all points are exterior - if path1.len() == 0 { + if path1.is_empty() { return PathCut { interior_path: vec![], exterior_path: vec![], }; - } else if path2.len() == 0 { + } else if path2.is_empty() { return PathCut { interior_path: vec![], exterior_path: path1.iter().map(|path| POut::from_path(path)).collect(), diff --git a/src/bezier/path/arithmetic/full_intersect.rs b/src/bezier/path/arithmetic/full_intersect.rs index bf492356..41ed87c0 100644 --- a/src/bezier/path/arithmetic/full_intersect.rs +++ b/src/bezier/path/arithmetic/full_intersect.rs @@ -29,7 +29,7 @@ where POut: BezierPathFactory, { // If path1 is empty, then there are no points in the result. If path2 is empty, then all points are exterior - if path1.len() == 0 { + if path1.is_empty() { return PathIntersection { intersecting_path: vec![], exterior_paths: [ @@ -37,7 +37,7 @@ where path2.iter().map(|path| POut::from_path(path)).collect(), ], }; - } else if path2.len() == 0 { + } else if path2.is_empty() { return PathIntersection { intersecting_path: vec![], exterior_paths: [ diff --git a/src/bezier/path/arithmetic/intersect.rs b/src/bezier/path/arithmetic/intersect.rs index 0800723a..1d17f2c7 100644 --- a/src/bezier/path/arithmetic/intersect.rs +++ b/src/bezier/path/arithmetic/intersect.rs @@ -32,9 +32,9 @@ where POut: BezierPathFactory, { // If either path is empty, short-circuit by returning the other - if path1.len() == 0 { + if path1.is_empty() { return path2.iter().map(|path| POut::from_path(path)).collect(); - } else if path2.len() == 0 { + } else if path2.is_empty() { return path1.iter().map(|path| POut::from_path(path)).collect(); } diff --git a/src/bezier/path/arithmetic/ray_cast.rs b/src/bezier/path/arithmetic/ray_cast.rs index 7a5f97ab..abedb163 100644 --- a/src/bezier/path/arithmetic/ray_cast.rs +++ b/src/bezier/path/arithmetic/ray_cast.rs @@ -124,7 +124,7 @@ impl GraphPath { // For collisions that overlap, ensure that the first shape is outermost so that subtractions work (swap based on the direction) // This interacts with the ordering chosen in ray_collisions: if that ordering changes this may no longer be correct - if collisions.len() > 0 { + if !collisions.is_empty() { for collision_idx in 0..(collisions.len() - 1) { let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx + 0]; diff --git a/src/bezier/path/arithmetic/sub.rs b/src/bezier/path/arithmetic/sub.rs index 875d86e3..82a47287 100644 --- a/src/bezier/path/arithmetic/sub.rs +++ b/src/bezier/path/arithmetic/sub.rs @@ -32,9 +32,9 @@ where POut: BezierPathFactory, { // If either path is empty, short-circuit by returning the other - if path1.len() == 0 { + if path1.is_empty() { return path2.iter().map(|path| POut::from_path(path)).collect(); - } else if path2.len() == 0 { + } else if path2.is_empty() { return path1.iter().map(|path| POut::from_path(path)).collect(); } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index a2519be0..39bf6b39 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -1034,7 +1034,7 @@ impl GraphPath { // Loop until we find a previous point for the initial point (indicating we've got a loop of points) while previous_point[point_idx].is_none() { - if points_to_check.len() == 0 { + if points_to_check.is_empty() { // Ran out of points to check to find a loop (there is no loop for this point) break; } diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index d71a285f..958d8df6 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -109,7 +109,7 @@ impl GraphPath { for (src_curve, tgt_curve) in sweep_self(ordered_edges.iter()) { // Find any collisions between the two edges (to the required accuracy) let mut edge_collisions = curve_intersects_curve_clip(src_curve, tgt_curve, accuracy); - if edge_collisions.len() == 0 { + if edge_collisions.is_empty() { continue; } @@ -191,7 +191,7 @@ impl GraphPath { for (src_curve, tgt_curve) in sweep_against(collide_src.iter(), collide_tgt.iter()) { // Find any collisions between the two edges (to the required accuracy) let mut edge_collisions = curve_intersects_curve_clip(src_curve, tgt_curve, accuracy); - if edge_collisions.len() == 0 { + if edge_collisions.is_empty() { continue; } @@ -321,7 +321,7 @@ impl GraphPath { ) -> bool { // Find all of the collision points let all_collisions = self.find_collisions(collide_from, collide_to, accuracy); - if all_collisions.len() == 0 { + if all_collisions.is_empty() { let collided_at_point = self.combine_overlapping_points(accuracy); self.remove_all_very_short_edges(); return collided_at_point; @@ -346,7 +346,7 @@ impl GraphPath { for (point_idx, edge_collisions) in collisions_by_point { for (edge_idx, mut collisions) in edge_collisions.into_iter().enumerate() { // Skip edges with no collisions - if collisions.len() == 0 { + if collisions.is_empty() { continue; } @@ -782,7 +782,7 @@ fn remove_and_round_close_collisions( C::Point: Coordinate + Coordinate2D, { // Nothing to do if there are no collisions - if collisions.len() == 0 { + if collisions.is_empty() { return; } @@ -815,7 +815,7 @@ fn remove_and_round_close_collisions( } // If the first point or the last point is close to the end of the source or target curve, clip to 0 or 1 - if collisions.len() > 0 { + if !collisions.is_empty() { // Get the start/end points of the source and target let src_start = src.start_point(); let src_end = src.end_point(); diff --git a/src/bezier/path/path_builder.rs b/src/bezier/path/path_builder.rs index 500952d4..a47c3c80 100644 --- a/src/bezier/path/path_builder.rs +++ b/src/bezier/path/path_builder.rs @@ -34,7 +34,7 @@ impl BezierPathBuilder

{ /// pub fn line_to(mut self, point: P::Point) -> Self { // Get the vector from the last point to the new point - let distance = if self.points.len() == 0 { + let distance = if self.points.is_empty() { point - self.start_point } else { point - self.points[self.points.len() - 1].2 diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index 7a361a33..d42ec7a0 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -832,7 +832,7 @@ mod test { let collisions = crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))).1; - assert!(collisions.len() == 0); + assert!(collisions.is_empty()); } #[test] @@ -860,7 +860,7 @@ mod test { ); let collisions = collisions.collect::>(); - assert!(collisions.len() == 0); + assert!(collisions.is_empty()); } #[test] diff --git a/tests/bezier/algorithms/fill_concave.rs b/tests/bezier/algorithms/fill_concave.rs index ddcb988d..b3cdfa62 100644 --- a/tests/bezier/algorithms/fill_concave.rs +++ b/tests/bezier/algorithms/fill_concave.rs @@ -172,7 +172,7 @@ fn fill_concave_doughnut() { ); assert!(path.is_some()); - assert!(path.as_ref().unwrap().len() != 0); + assert!(!path.as_ref().unwrap().is_empty()); assert!(path.as_ref().unwrap().len() != 1); assert!(path.as_ref().unwrap().len() == 2); @@ -226,7 +226,7 @@ fn fill_doughnut_with_extra_holes() { ); assert!(path.is_some()); - assert!(path.as_ref().unwrap().len() != 0); + assert!(!path.as_ref().unwrap().is_empty()); assert!(path.as_ref().unwrap().len() != 1); assert!(path.as_ref().unwrap().len() == 2); @@ -280,7 +280,7 @@ fn fill_circle_without_escaping_gaps() { ); assert!(path.is_some()); - assert!(path.as_ref().unwrap().len() != 0); + assert!(!path.as_ref().unwrap().is_empty()); assert!(path.as_ref().unwrap().len() == 1); for curve in path.as_ref().unwrap()[0].to_curves::>() { @@ -324,7 +324,7 @@ fn fill_circle_without_escaping_gaps_offset() { ); assert!(path.is_some()); - assert!(path.as_ref().unwrap().len() != 0); + assert!(!path.as_ref().unwrap().is_empty()); assert!(path.as_ref().unwrap().len() == 1); for curve in path.as_ref().unwrap()[0].to_curves::>() { @@ -377,7 +377,7 @@ fn fill_doughnut_without_escaping_gaps() { ); assert!(path.is_some()); - assert!(path.as_ref().unwrap().len() != 0); + assert!(!path.as_ref().unwrap().is_empty()); assert!(path.as_ref().unwrap().len() != 1); assert!(path.as_ref().unwrap().len() == 2); diff --git a/tests/bezier/algorithms/fill_convex.rs b/tests/bezier/algorithms/fill_convex.rs index e5b16891..fc424638 100644 --- a/tests/bezier/algorithms/fill_convex.rs +++ b/tests/bezier/algorithms/fill_convex.rs @@ -46,7 +46,7 @@ fn trace_convex_circle() { let outline = trace_outline_convex(circle_center, &FillSettings::default(), circle_ray_cast); // Should be at least one point - assert!(outline.len() > 0); + assert!(!outline.is_empty()); // Points should be no more that 4.0 pixels apart and should be the correct distance from the circle for point_idx in 0..outline.len() { @@ -107,7 +107,7 @@ fn trace_convex_doughnut() { let outline = trace_outline_convex(start_point, &FillSettings::default(), doughnut); // Should be at least one point - assert!(outline.len() > 0); + assert!(!outline.is_empty()); for point_idx in 0..outline.len() { let point = &outline[point_idx]; diff --git a/tests/bezier/curve_intersection_clip.rs b/tests/bezier/curve_intersection_clip.rs index 2fd607b8..f92c1ae2 100644 --- a/tests/bezier/curve_intersection_clip.rs +++ b/tests/bezier/curve_intersection_clip.rs @@ -19,7 +19,7 @@ fn find_intersection_on_straight_line_not_middle() { .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) .collect::>() ); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersect_point.distance_to(&Coord2(5.0, 5.0)) < 0.1); @@ -47,7 +47,7 @@ fn find_intersection_on_straight_line_middle() { .map(|(t1, t2)| (curve1.point_at_pos(*t1), curve2.point_at_pos(*t2))) .collect::>() ); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersect_point.distance_to(&Coord2(5.0, 5.0)) < 0.1); @@ -67,7 +67,7 @@ fn find_intersection_on_straight_line_start() { line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(0.0, 10.0))); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersections[0].0 < 0.01); @@ -89,7 +89,7 @@ fn find_intersection_on_straight_line_end_1() { line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(0.0, 10.0), Coord2(5.0, 5.0))); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersections[0].0 > 0.99); @@ -111,7 +111,7 @@ fn find_intersection_on_straight_line_end_to_start_1() { line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(0.0, 10.0))); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersections[0].0 > 0.99); @@ -131,7 +131,7 @@ fn find_intersection_on_line_end_to_end_2() { let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 5.0), Coord2(3.0, 3.0))); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersect_point.distance_to(&Coord2(3.0, 3.0)) < 0.1); @@ -151,7 +151,7 @@ fn find_intersection_on_line_end_to_end_3() { let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(5.0, 1.0), Coord2(3.0, 3.0))); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersect_point.distance_to(&Coord2(3.0, 3.0)) < 0.1); @@ -187,7 +187,7 @@ fn find_intersection_on_line_end_to_start_2() { let curve2 = line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(3.0, 3.0), Coord2(5.0, 5.0))); let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.1); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); let intersect_point = curve1.point_at_pos(intersections[0].0); assert!(intersect_point.distance_to(&Coord2(3.0, 3.0)) < 0.1); @@ -216,7 +216,7 @@ fn find_intersection_on_straight_line_near_end() { .collect::>() ); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } @@ -443,7 +443,7 @@ fn intersection_curve_1() { let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() != 1); assert!(intersections.len() == 2); @@ -462,7 +462,7 @@ fn intersection_curve_1() { let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 2); assert!( @@ -500,7 +500,7 @@ fn intersection_curve_2() { let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() != 1); assert!(intersections.len() == 2); @@ -519,7 +519,7 @@ fn intersection_curve_2() { let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 2); assert!( @@ -557,7 +557,7 @@ fn intersection_curve_3() { let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); assert!( @@ -569,7 +569,7 @@ fn intersection_curve_3() { let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); assert!( @@ -601,7 +601,7 @@ fn intersection_curve_4() { let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); assert!( @@ -613,7 +613,7 @@ fn intersection_curve_4() { let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); assert!( @@ -645,7 +645,7 @@ fn intersection_curve_5() { let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); assert!( @@ -657,7 +657,7 @@ fn intersection_curve_5() { let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); assert!( @@ -689,12 +689,12 @@ fn intersection_curve_6() { let intersections = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); let intersections = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 2); } @@ -780,8 +780,8 @@ fn intersection_curve_8() { println!("{:?}", intersections1); println!("{:?}", intersections2); - assert!(intersections1.len() > 0); - assert!(intersections2.len() > 0); + assert!(!intersections1.is_empty()); + assert!(!intersections2.is_empty()); assert!(intersections1.len() == 1); assert!(intersections2.len() == 1); @@ -815,8 +815,8 @@ fn intersection_curve_9() { println!("{:?}", intersections1); println!("{:?}", intersections2); - assert!(intersections1.len() > 0); - assert!(intersections2.len() > 0); + assert!(!intersections1.is_empty()); + assert!(!intersections2.is_empty()); assert!(intersections1.len() == 2); assert!(intersections2.len() == 2); @@ -842,15 +842,15 @@ fn intersection_curve_10() { }; let intersections1 = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); - assert!(intersections1.len() > 0); + assert!(!intersections1.is_empty()); let intersections2 = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections1); println!("{:?}", intersections2); - assert!(intersections1.len() > 0); - assert!(intersections2.len() > 0); + assert!(!intersections1.is_empty()); + assert!(!intersections2.is_empty()); assert!(intersections1.len() == 1); assert!(intersections2.len() == 1); @@ -885,15 +885,15 @@ fn intersection_curve_11() { let curve2 = curve2.section(0.47428429377321385, 0.4934869656848157); let intersections1 = bezier::curve_intersects_curve_clip(&curve1, &curve2, 0.01); - assert!(intersections1.len() > 0); + assert!(!intersections1.is_empty()); let intersections2 = bezier::curve_intersects_curve_clip(&curve2, &curve1, 0.01); println!("{:?}", intersections1); println!("{:?}", intersections2); - assert!(intersections1.len() > 0); - assert!(intersections2.len() > 0); + assert!(!intersections1.is_empty()); + assert!(!intersections2.is_empty()); assert!(intersections1.len() == 1); assert!(intersections2.len() == 1); @@ -944,8 +944,8 @@ fn intersection_very_close_to_start_1() { println!("{:?}", intersections1); println!("{:?}", intersections2); - assert!(intersections1.len() > 0); - assert!(intersections2.len() > 0); + assert!(!intersections1.is_empty()); + assert!(!intersections2.is_empty()); assert!(intersections1.len() == 1); assert!(intersections2.len() == 1); diff --git a/tests/bezier/intersection.rs b/tests/bezier/intersection.rs index 8134067a..6f66006b 100644 --- a/tests/bezier/intersection.rs +++ b/tests/bezier/intersection.rs @@ -52,7 +52,7 @@ fn no_intersection_if_line_does_not_cross_curve() { line::line_to_bezier::<_, bezier::Curve<_>>(&(Coord2(10.0, 0.0), Coord2(0.0, 10.0))); let intersections = bezier::curve_intersects_line(&curve, &line); - assert!(intersections.len() == 0); + assert!(intersections.is_empty()); } #[test] @@ -150,7 +150,7 @@ fn dot_intersects_nothing() { let intersections = bezier::curve_intersects_line(&curve, &line); // Should be no intersections - assert!(intersections.len() == 0); + assert!(intersections.is_empty()); } #[test] @@ -274,7 +274,7 @@ fn ray_intersects_curve_1() { let intersections = bezier::curve_intersects_ray(&curve, &ray); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } @@ -296,7 +296,7 @@ fn ray_intersects_curve_1a() { let intersections = bezier::curve_intersects_ray(&curve, &ray); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } @@ -318,7 +318,7 @@ fn ray_intersects_curve_1b() { let intersections = bezier::curve_intersects_ray(&curve, &ray); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } @@ -340,7 +340,7 @@ fn ray_intersects_curve_1c() { let intersections = bezier::curve_intersects_ray(&curve, &ray); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } @@ -362,7 +362,7 @@ fn ray_intersects_curve_1d() { let intersections = bezier::curve_intersects_ray(&curve, &ray); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } @@ -385,7 +385,7 @@ fn ray_intersects_curve_1e() { let intersections = bezier::curve_intersects_curve_clip(&curve, &ray, 0.01); - assert!(intersections.len() != 0); + assert!(!intersections.is_empty()); assert!(intersections.len() == 1); } diff --git a/tests/bezier/path/arithmetic_add.rs b/tests/bezier/path/arithmetic_add.rs index 5fa8c8e8..bd30743b 100644 --- a/tests/bezier/path/arithmetic_add.rs +++ b/tests/bezier/path/arithmetic_add.rs @@ -97,7 +97,7 @@ fn add_two_very_close_circles() { let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); println!("{:?}", combined_circles.len()); - assert!(combined_circles.len() != 0); + assert!(!combined_circles.is_empty()); assert!(combined_circles.len() != 2); assert!(combined_circles.len() == 1); @@ -125,7 +125,7 @@ fn add_two_close_circles() { // Combine them let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); - assert!(combined_circles.len() != 0); + assert!(!combined_circles.is_empty()); assert!(combined_circles.len() != 2); assert!(combined_circles.len() == 1); diff --git a/tests/bezier/path/arithmetic_complicated_paths.rs b/tests/bezier/path/arithmetic_complicated_paths.rs index 396950c0..aaa071de 100644 --- a/tests/bezier/path/arithmetic_complicated_paths.rs +++ b/tests/bezier/path/arithmetic_complicated_paths.rs @@ -431,7 +431,7 @@ fn remove_interior_points_1() { .collect::>() ); - assert!(with_points_removed.len() > 0); + assert!(!with_points_removed.is_empty()); } #[test] @@ -508,7 +508,7 @@ fn remove_interior_points_1_without_failing_section() { .collect::>() ); - assert!(with_points_removed.len() > 0); + assert!(!with_points_removed.is_empty()); } #[test] @@ -1312,7 +1312,7 @@ fn remove_interior_points_complex_2() { assert!(graph_path.num_points() == initial_num_points); */ - assert!(without_interior_points.len() != 0); + assert!(!without_interior_points.is_empty()); assert!(without_interior_points.len() == 1); } @@ -2097,7 +2097,7 @@ fn remove_interior_points_complex_2_without_healing() { graph_path.set_exterior_by_removing_interior_points(); let paths = graph_path.exterior_paths::(); - assert!(paths.len() != 0); + assert!(!paths.is_empty()); assert!(paths.len() == 1); // Must always be a following edge that's an exterior one @@ -2653,14 +2653,14 @@ fn remove_interior_points_3() { .build(); let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); - assert!(removed.len() != 0); + assert!(!removed.is_empty()); let mut graph_path = GraphPath::from_path(&path, PathLabel(0, PathDirection::Clockwise)); graph_path.self_collide(0.01); graph_path.set_exterior_by_removing_interior_points(); let paths = graph_path.exterior_paths::(); - assert!(paths.len() != 0); + assert!(!paths.is_empty()); assert!(paths.len() == 3); // Must always be a following edge that's an exterior one @@ -3026,7 +3026,7 @@ fn remove_interior_points_4() { // Fails an internal assertion (cannot determine if a line is in or outside) let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); - assert!(removed.len() != 0); + assert!(!removed.is_empty()); /* -- we do generate extra points right now, indicating that lines are overlapping -- overlapping is occurring because we move 'close' points together after performing the self-collide diff --git a/tests/bezier/path/arithmetic_cut.rs b/tests/bezier/path/arithmetic_cut.rs index 6260a998..2cc8650a 100644 --- a/tests/bezier/path/arithmetic_cut.rs +++ b/tests/bezier/path/arithmetic_cut.rs @@ -44,7 +44,7 @@ fn cut_square_entirely_interior() { let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); - assert!(cut_square.exterior_path.len() == 0); + assert!(cut_square.exterior_path.is_empty()); assert!(cut_square.interior_path.len() == 1); assert!(cut_square.interior_path[0].points().len() == 4); @@ -69,7 +69,7 @@ fn cut_square_entirely_exterior() { let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); assert!(cut_square.exterior_path.len() == 1); - assert!(cut_square.interior_path.len() == 0); + assert!(cut_square.interior_path.is_empty()); assert!(cut_square.exterior_path[0].points().len() == 4); } diff --git a/tests/bezier/path/arithmetic_intersect.rs b/tests/bezier/path/arithmetic_intersect.rs index 6512ea2d..15532bb9 100644 --- a/tests/bezier/path/arithmetic_intersect.rs +++ b/tests/bezier/path/arithmetic_intersect.rs @@ -68,7 +68,7 @@ fn full_intersect_two_non_overlapping_circles() { let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); - assert!(intersection.intersecting_path.len() == 0); + assert!(intersection.intersecting_path.is_empty()); assert!(intersection.exterior_paths[0].len() == 1); assert!(intersection.exterior_paths[1].len() == 1); } @@ -83,7 +83,7 @@ fn full_intersect_interior_circles_1() { assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 2); - assert!(intersection.exterior_paths[1].len() == 0); + assert!(intersection.exterior_paths[1].is_empty()); } #[test] @@ -95,7 +95,7 @@ fn full_intersect_interior_circles_2() { path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); - assert!(intersection.exterior_paths[0].len() == 0); + assert!(intersection.exterior_paths[0].is_empty()); assert!(intersection.exterior_paths[1].len() == 2); } @@ -121,8 +121,8 @@ fn full_intersect_two_fully_overlapping_circles() { println!("{:?}", intersection); assert!(intersection.intersecting_path.len() == 1); - assert!(intersection.exterior_paths[0].len() == 0); - assert!(intersection.exterior_paths[1].len() == 0); + assert!(intersection.exterior_paths[0].is_empty()); + assert!(intersection.exterior_paths[1].is_empty()); } #[test] @@ -291,7 +291,7 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { let intersections = curve_intersects_curve_clip(&fragment_edge, &remain_edge, 0.01); num_collisions += intersections.len(); - if intersections.len() > 0 { + if !intersections.is_empty() { println!(" {:?}", intersections.len()); } @@ -299,7 +299,7 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { let start_distance = edge.distance_to(&first_point); let end_distance = edge.distance_to(&end_point); - if intersections.len() == 0 { + if intersections.is_empty() { let start_pos = edge.pos_for_point(&first_point); let end_pos = edge.pos_for_point(&end_point); @@ -423,7 +423,7 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { } // Should be a 16x16 polygon left over for the circle - assert!(remaining.len() > 0); + assert!(!remaining.is_empty()); println!("{:?}", remaining[0]); let start_point = remaining[remaining.len() - 1].start_point(); diff --git a/tests/bezier/path/arithmetic_sub.rs b/tests/bezier/path/arithmetic_sub.rs index a0d53430..b3625282 100644 --- a/tests/bezier/path/arithmetic_sub.rs +++ b/tests/bezier/path/arithmetic_sub.rs @@ -76,7 +76,7 @@ fn erase_all() { // Create a hole in the larger circle let combined_circles = path_sub::<_, _, SimpleBezierPath>(&vec![circle2], &vec![circle1], 0.01); - assert!(combined_circles.len() == 0); + assert!(combined_circles.is_empty()); } #[test] @@ -96,7 +96,7 @@ fn subtract_from_self_rectangles_1() { println!("{:?}", combined_rectangles); assert!(combined_rectangles.len() != 1); - assert!(combined_rectangles.len() == 0); + assert!(combined_rectangles.is_empty()); } #[test] @@ -116,7 +116,7 @@ fn subtract_from_self_rectangles_2() { println!("{:?}", combined_rectangles); assert!(combined_rectangles.len() != 1); - assert!(combined_rectangles.len() == 0); + assert!(combined_rectangles.is_empty()); } #[test] @@ -128,7 +128,7 @@ fn subtract_from_self_circles() { // Create a hole in the larger circle let combined_circles = path_sub::<_, _, SimpleBezierPath>(&vec![circle2], &vec![circle1], 0.01); - assert!(combined_circles.len() == 0); + assert!(combined_circles.is_empty()); } #[test] diff --git a/tests/bezier/path/graph_path.rs b/tests/bezier/path/graph_path.rs index bc038c9a..8d8262f8 100644 --- a/tests/bezier/path/graph_path.rs +++ b/tests/bezier/path/graph_path.rs @@ -127,7 +127,7 @@ pub fn collide_two_rectangles() { let edges = collision.edges_for_point(point_idx).collect::>(); assert!(edges.len() <= 2); - assert!(edges.len() >= 1); + assert!(!edges.is_empty()); assert!(edges[0].kind() == GraphPathEdgeKind::Uncategorised); assert!(edges.len() == 1 || edges[1].kind() == GraphPathEdgeKind::Uncategorised); @@ -233,7 +233,7 @@ pub fn collide_identical_rectangles() { if point_idx < 4 { assert!(edges.len() == 2); } else { - assert!(edges.len() == 0); + assert!(edges.is_empty()); } } } @@ -337,7 +337,7 @@ fn multiple_collisions_on_one_edge_opposite_direction() { let edges = collision.edges_for_point(point_idx).collect::>(); assert!(edges.len() <= 2); - assert!(edges.len() > 0); + assert!(!edges.is_empty()); if edges.len() == 2 { num_intersects += 1; @@ -428,7 +428,7 @@ fn collision_at_same_point() { let mut num_orphaned_points = 0; for point_idx in 0..13 { let edges = collision.edges_for_point(point_idx).collect::>(); - if edges.len() == 0 { + if edges.is_empty() { num_orphaned_points += 1; } } @@ -534,7 +534,7 @@ fn collision_exactly_on_edge_src() { let mut num_orphaned_points = 0; for point_idx in 0..13 { let edges = collision.edges_for_point(point_idx).collect::>(); - if edges.len() == 0 { + if edges.is_empty() { num_orphaned_points += 1; } } @@ -640,7 +640,7 @@ fn collision_exactly_on_edge_tgt() { let mut num_orphaned_points = 0; for point_idx in 0..13 { let edges = collision.edges_for_point(point_idx).collect::>(); - if edges.len() == 0 { + if edges.is_empty() { num_orphaned_points += 1; } } @@ -747,7 +747,7 @@ fn cast_ray_to_rectangle_corner() { let collision = rectangle1.ray_collisions(&(Coord2(0.0, 0.0), Coord2(1.0, 1.0))); let collision = to_collision_with_edges(collision, &rectangle1); - assert!(collision.len() > 0); + assert!(!collision.is_empty()); let collision = &collision[0]; assert!(collision.0.start_point() == Coord2(1.0, 1.0)); @@ -778,7 +778,7 @@ fn casting_ray_to_exact_point_produces_one_collision() { edge.point_at_pos(*curve_t).distance_to(&Coord2(1.0, 1.0)) < 0.1 }) .collect::>(); - assert!(collisions_with_corner.len() != 0); + assert!(!collisions_with_corner.is_empty()); assert!(collisions_with_corner.len() != 2); assert!(collisions_with_corner.len() == 1); } @@ -799,7 +799,7 @@ fn casting_ray_across_corner_produces_no_collision() { let collision = rectangle1.ray_collisions(&(Coord2(0.0, 2.0), Coord2(2.0, 0.0))); assert!(collision.len() != 1); - assert!(collision.len() == 0); + assert!(collision.is_empty()); } #[test] @@ -849,7 +849,7 @@ fn casting_ray_to_intersection_point_produces_two_collisions() { edge.point_at_pos(*curve_t).distance_to(&Coord2(5.0, 3.0)) < 0.1 }) .collect::>(); - assert!(collisions_with_corner.len() != 0); + assert!(!collisions_with_corner.is_empty()); assert!(collisions_with_corner.len() != 4); assert!(collisions_with_corner.len() == 2); } @@ -869,7 +869,7 @@ fn cast_ray_across_rectangle() { let collision = rectangle1.ray_collisions(&(Coord2(0.0, 3.0), Coord2(6.0, 3.0))); let collision = to_collision_with_edges(collision, &rectangle1); - assert!(collision.len() > 0); + assert!(!collision.is_empty()); let collision = &collision[0]; assert!( @@ -898,7 +898,7 @@ fn cast_ray_to_rectangle_far_corner() { let collision = rectangle1.ray_collisions(&(Coord2(0.0, 0.0), Coord2(6.0, 6.0))); let collision = to_collision_with_edges(collision, &rectangle1); - assert!(collision.len() > 0); + assert!(!collision.is_empty()); let collision = &collision[0]; assert!(collision.0.start_point() == Coord2(1.0, 1.0)); @@ -920,7 +920,7 @@ fn cast_ray_to_rectangle_far_corner_backwards() { let collision = rectangle1.ray_collisions(&(Coord2(6.0, 6.0), Coord2(0.0, 0.0))); let collision = to_collision_with_edges(collision, &rectangle1); - assert!(collision.len() > 0); + assert!(!collision.is_empty()); let collision = &collision[0]; assert!(collision.0.start_point().distance_to(&Coord2(5.0, 5.0)) < 0.1); @@ -941,7 +941,7 @@ fn cast_ray_to_nowhere() { // Line that entirely misses the rectangle let collision = rectangle1.ray_collisions(&(Coord2(0.0, 0.0), Coord2(0.0, 10.0))); - assert!(collision.len() == 0); + assert!(collision.is_empty()); } #[test] @@ -1326,7 +1326,7 @@ fn ray_collide_along_convex_edge() { // As the ray never actually enters the shape along the seam, there should be 0 collisions println!("{:?}", collisions_seam); assert!(collisions_seam.len() != 2); - assert!(collisions_seam.len() == 0); + assert!(collisions_seam.is_empty()); } #[test] @@ -1889,7 +1889,7 @@ fn ray_cast_grazing_circle_produces_0_hits() { // Should not actually hit the circle assert!(collisions.len() != 2); // 2 collisions would produce no bug assert!(collisions.len() != 1); - assert!(collisions.len() == 0); + assert!(collisions.is_empty()); } #[test] @@ -1906,7 +1906,7 @@ fn ray_cast_close_to_circle_produces_2_hits() { let collisions = path.ray_collisions(&(Coord2(23.99, 0.0), Coord2(23.99, 1.0))); // Should not actually hit the circle - assert!(collisions.len() != 0); + assert!(!collisions.is_empty()); assert!(collisions.len() != 1); assert!(collisions.len() == 2); } diff --git a/tests/bezier/path/intersection.rs b/tests/bezier/path/intersection.rs index affd3f74..20f230f7 100644 --- a/tests/bezier/path/intersection.rs +++ b/tests/bezier/path/intersection.rs @@ -62,7 +62,7 @@ fn line_intersects_circle() { let intersection = path_intersects_line(&circle, &line).collect::>(); assert!(intersection.len() == 1); - if intersection.len() > 0 { + if !intersection.is_empty() { let intersection = intersection[0]; let intersect_point = circle_sections[intersection.0].point_at_pos(intersection.1); @@ -92,7 +92,7 @@ fn line_does_not_intersect_circle() { // Should be one intersection with the circle here let line = (center, target); let intersection = path_intersects_line(&circle, &line).collect::>(); - assert!(intersection.len() == 0); + assert!(intersection.is_empty()); } } @@ -109,7 +109,7 @@ fn circle_intersects_circle() { let intersections = path_intersects_path(&circle1, &circle2, 0.5); // The circles should intersect at least once - assert!(intersections.len() > 0); + assert!(!intersections.is_empty()); println!("{:?}", intersections); // Convert to curves diff --git a/tests/bezier/path/rays.rs b/tests/bezier/path/rays.rs index c37703bb..f587bc66 100644 --- a/tests/bezier/path/rays.rs +++ b/tests/bezier/path/rays.rs @@ -217,7 +217,7 @@ fn crossing_figure_of_8_intersection_collinear() { assert!(collisions.len() != 3); assert!((collisions.len() & 1) == 0); - assert!(collisions.len() == 0); + assert!(collisions.is_empty()); } #[test] @@ -242,7 +242,7 @@ fn crossing_figure_of_8_intersection_from_outside() { assert!(collisions.len() != 3); assert!((collisions.len() & 1) == 0); - assert!(collisions.len() == 0 || collisions.len() == 2); + assert!(collisions.is_empty() || collisions.len() == 2); } #[test] @@ -378,7 +378,7 @@ fn ray_hitting_tangent_at_point() { let collisions = graph_path.ray_collisions(&(Coord2(3.0, 0.0), Coord2(3.0, 1.0))); assert!((collisions.len() & 1) == 0); - assert!(collisions.len() == 0); + assert!(collisions.is_empty()); } #[test] diff --git a/tests/bezier/path/to_curves.rs b/tests/bezier/path/to_curves.rs index ffcaeadb..b1ed244a 100644 --- a/tests/bezier/path/to_curves.rs +++ b/tests/bezier/path/to_curves.rs @@ -38,5 +38,5 @@ pub fn no_points_means_no_curve() { let curve = path_to_curves::<_, Curve<_>>(&path); let curve: Vec<_> = curve.collect(); - assert!(curve.len() == 0); + assert!(curve.is_empty()); } diff --git a/tests/bezier/search.rs b/tests/bezier/search.rs index e93501c7..a50aed11 100644 --- a/tests/bezier/search.rs +++ b/tests/bezier/search.rs @@ -29,5 +29,5 @@ fn coordinate_outside_curve_produces_no_results() { bezier::search_bounds4(0.01, w1, w2, w3, w4, |p1, p2| p1 < x_coord && p2 > x_coord); // No points on the curve match this coordinate - assert!(matching_values.len() == 0); + assert!(matching_values.is_empty()); } From aaea207f76073a04d729c5f7f1954bbf238761e0 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:03:19 +0100 Subject: [PATCH 09/31] fix clippy::needless_bool --- src/bezier/intersection/fat_line.rs | 6 +---- src/bezier/path/ray.rs | 37 ++++++----------------------- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index 407466e2..9ed03a5a 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -259,11 +259,7 @@ impl FatLine { /// Returns true if this line is flat (indicating the source curve is a straight line) /// pub fn is_flat(&self) -> bool { - if self.d_min.abs() < SMALL_DISTANCE && self.d_max.abs() < SMALL_DISTANCE { - true - } else { - false - } + self.d_min.abs() < SMALL_DISTANCE && self.d_max.abs() < SMALL_DISTANCE } } diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index d42ec7a0..98cbe89f 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -82,15 +82,9 @@ where let (cp1, cp2) = edge.control_points(); // The curve is collinear if all of the points lie on the ray - if (start_point.x() * a + start_point.y() * b + c).abs() < SMALL_DISTANCE + (start_point.x() * a + start_point.y() * b + c).abs() < SMALL_DISTANCE && (end_point.x() * a + end_point.y() * b + c).abs() < SMALL_DISTANCE - && (cp1.x() * a + cp1.y() * b + c).abs() < SMALL_DISTANCE - && (cp2.x() * a + cp2.y() * b + c).abs() < SMALL_DISTANCE - { - true - } else { - false - } + && (cp1.x() * a + cp1.y() * b + c).abs() < SMALL_DISTANCE && (cp2.x() * a + cp2.y() * b + c).abs() < SMALL_DISTANCE } #[derive(PartialEq)] @@ -332,34 +326,21 @@ where let end_point = path.point_position(end_point_idx); // If any following edge is collinear, remove this collision - if position.is_near_to(&end_point, CLOSE_DISTANCE) - && path + !(position.is_near_to(&end_point, CLOSE_DISTANCE) && path .edges_for_point(end_point_idx) .into_iter() .map(|edge| path.get_edge(edge)) - .any(|next| curve_is_collinear(&next, ray_coeffs)) - { - false - } else { - true - } + .any(|next| curve_is_collinear(&next, ray_coeffs))) } else if *curve_t < 0.1 { let start_point_idx = path.edge_start_point_idx(*collision); let start_point = path.point_position(start_point_idx); // If any preceding edge is collinear, remove this collision - if position.is_near_to(&start_point, CLOSE_DISTANCE) - && path + !(position.is_near_to(&start_point, CLOSE_DISTANCE) && path .reverse_edges_for_point(start_point_idx) .into_iter() .map(|edge| path.get_edge(edge)) - .any(|previous| curve_is_collinear(&previous, ray_coeffs)) - { - // Collisions crossing collinear sections are taken care of during the collinear collision phase - false - } else { - true - } + .any(|previous| curve_is_collinear(&previous, ray_coeffs))) } else { // Not at the end of a curve true @@ -650,11 +631,7 @@ where let dot_product_mag = dot_product.abs() - 1.0; // Dot product of two unit vectors will be 1.0 or -1.0 for a tangent collision - if dot_product_mag > -0.00000001 && dot_product_mag < 0.00000001 { - false - } else { - true - } + !(dot_product_mag > -0.00000001 && dot_product_mag < 0.00000001) }) } From 38639f861be350653c1717f4de45a0fa70d44c31 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:03:57 +0100 Subject: [PATCH 10/31] fix clippy::into_iter_on_ref --- src/bezier/path/arithmetic/add.rs | 8 ++++---- src/bezier/path/arithmetic/chain_add.rs | 2 +- src/bezier/path/arithmetic/cut.rs | 4 ++-- src/bezier/path/arithmetic/full_intersect.rs | 8 ++++---- src/bezier/path/arithmetic/intersect.rs | 4 ++-- src/bezier/path/arithmetic/sub.rs | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/bezier/path/arithmetic/add.rs b/src/bezier/path/arithmetic/add.rs index dcc1f2d8..48a6d184 100644 --- a/src/bezier/path/arithmetic/add.rs +++ b/src/bezier/path/arithmetic/add.rs @@ -59,7 +59,7 @@ where let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path1 - .into_iter() + .iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); @@ -67,7 +67,7 @@ where merged_path = merged_path.collide( GraphPath::from_merged_paths( path2 - .into_iter() + .iter() .map(|path| (path, PathLabel(1, PathDirection::from(path)))), ), accuracy, @@ -103,7 +103,7 @@ where // Create the graph path from the source side let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( - path.into_iter() + path.iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); @@ -146,7 +146,7 @@ where // Create the graph path from the source side let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( - path.into_iter() + path.iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); diff --git a/src/bezier/path/arithmetic/chain_add.rs b/src/bezier/path/arithmetic/chain_add.rs index cf22568c..aea3fffa 100644 --- a/src/bezier/path/arithmetic/chain_add.rs +++ b/src/bezier/path/arithmetic/chain_add.rs @@ -21,7 +21,7 @@ where let path_idx = path_idx as u32; merged_path = merged_path.collide( GraphPath::from_merged_paths( - path.into_iter() + path.iter() .map(|path| (path, PathLabel(path_idx, PathDirection::from(path)))), ), accuracy, diff --git a/src/bezier/path/arithmetic/cut.rs b/src/bezier/path/arithmetic/cut.rs index 25cd88b7..5874bf65 100644 --- a/src/bezier/path/arithmetic/cut.rs +++ b/src/bezier/path/arithmetic/cut.rs @@ -45,7 +45,7 @@ where let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path1 - .into_iter() + .iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); @@ -53,7 +53,7 @@ where merged_path = merged_path.collide( GraphPath::from_merged_paths( path2 - .into_iter() + .iter() .map(|path| (path, PathLabel(1, PathDirection::from(path)))), ), accuracy, diff --git a/src/bezier/path/arithmetic/full_intersect.rs b/src/bezier/path/arithmetic/full_intersect.rs index 41ed87c0..6fba40d6 100644 --- a/src/bezier/path/arithmetic/full_intersect.rs +++ b/src/bezier/path/arithmetic/full_intersect.rs @@ -51,7 +51,7 @@ where let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path1 - .into_iter() + .iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); @@ -59,7 +59,7 @@ where merged_path = merged_path.collide( GraphPath::from_merged_paths( path2 - .into_iter() + .iter() .map(|path| (path, PathLabel(1, PathDirection::from(path)))), ), accuracy, @@ -91,13 +91,13 @@ where let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path2 - .into_iter() + .iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); merged_path = merged_path.collide( GraphPath::from_merged_paths( path1 - .into_iter() + .iter() .map(|path| (path, PathLabel(1, PathDirection::from(path)))), ), accuracy, diff --git a/src/bezier/path/arithmetic/intersect.rs b/src/bezier/path/arithmetic/intersect.rs index 1d17f2c7..934ffb6c 100644 --- a/src/bezier/path/arithmetic/intersect.rs +++ b/src/bezier/path/arithmetic/intersect.rs @@ -42,7 +42,7 @@ where let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path1 - .into_iter() + .iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); @@ -50,7 +50,7 @@ where merged_path = merged_path.collide( GraphPath::from_merged_paths( path2 - .into_iter() + .iter() .map(|path| (path, PathLabel(1, PathDirection::from(path)))), ), accuracy, diff --git a/src/bezier/path/arithmetic/sub.rs b/src/bezier/path/arithmetic/sub.rs index 82a47287..13e2215a 100644 --- a/src/bezier/path/arithmetic/sub.rs +++ b/src/bezier/path/arithmetic/sub.rs @@ -42,7 +42,7 @@ where let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path1 - .into_iter() + .iter() .map(|path| (path, PathLabel(0, PathDirection::from(path)))), )); @@ -50,7 +50,7 @@ where merged_path = merged_path.collide( GraphPath::from_merged_paths( path2 - .into_iter() + .iter() .map(|path| (path, PathLabel(1, PathDirection::from(path)))), ), accuracy, From 6fa75838b8724c33cf95c510b46813e44d9b82c2 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:10:31 +0100 Subject: [PATCH 11/31] fix clippy::needless_lifetimes --- src/arc/circle.rs | 2 +- src/bezier/curve.rs | 2 +- src/bezier/intersection/curve_curve_clip.rs | 6 +++--- src/bezier/length.rs | 2 +- src/bezier/offset_scaling.rs | 4 ++-- src/bezier/path/graph_path/edge.rs | 2 +- src/bezier/path/graph_path/mod.rs | 18 +++++++++--------- src/bezier/path/graph_path/path_collision.rs | 2 +- src/bezier/walk.rs | 12 ++++++------ 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/arc/circle.rs b/src/arc/circle.rs index ba5be62b..15bbb669 100644 --- a/src/arc/circle.rs +++ b/src/arc/circle.rs @@ -44,7 +44,7 @@ impl Circle { /// /// Returns an object representing an arc from this circle /// - pub fn arc<'a>(&'a self, start_radians: f64, end_radians: f64) -> CircularArc<'a, Coord> { + pub fn arc(&self, start_radians: f64, end_radians: f64) -> CircularArc { CircularArc { circle: self, start_radians, diff --git a/src/bezier/curve.rs b/src/bezier/curve.rs index 43b22b39..8893a629 100644 --- a/src/bezier/curve.rs +++ b/src/bezier/curve.rs @@ -194,7 +194,7 @@ pub trait BezierCurve: Geo + Clone + Sized { /// Create a section from this curve. Consider calling `subsection` for curves /// that are already `CurveSections`. /// - fn section<'a>(&'a self, t_min: f64, t_max: f64) -> CurveSection<'a, Self> { + fn section(&self, t_min: f64, t_max: f64) -> CurveSection { CurveSection::new(self, t_min, t_max) } } diff --git a/src/bezier/intersection/curve_curve_clip.rs b/src/bezier/intersection/curve_curve_clip.rs index 6a1ad19f..ddc93d34 100644 --- a/src/bezier/intersection/curve_curve_clip.rs +++ b/src/bezier/intersection/curve_curve_clip.rs @@ -9,7 +9,7 @@ use smallvec::*; /// /// Determines the length of a curve's hull as a sum of squares /// -fn curve_hull_length_sq<'a, C: BezierCurve>(curve: &CurveSection<'a, C>) -> f64 { +fn curve_hull_length_sq(curve: &CurveSection) -> f64 { if curve.is_tiny() { 0.0 } else { @@ -145,8 +145,8 @@ where /// /// Given a set of intersections found on a left and right curve, joins them in a way that eliminates duplicates /// -fn join_subsections<'a, C: BezierCurve>( - curve1: &CurveSection<'a, C>, +fn join_subsections( + curve1: &CurveSection, left: SmallVec<[(f64, f64); 8]>, right: SmallVec<[(f64, f64); 8]>, accuracy_squared: f64, diff --git a/src/bezier/length.rs b/src/bezier/length.rs index fff3eca1..52d31a2b 100644 --- a/src/bezier/length.rs +++ b/src/bezier/length.rs @@ -33,7 +33,7 @@ pub fn curve_length(curve: &Curve, max_error: f64) -> f64 { /// /// Computes the length of a section of a bezier curve /// -fn section_length<'a, Curve>(section: CurveSection<'a, Curve>, max_error: f64) -> f64 +fn section_length(section: CurveSection, max_error: f64) -> f64 where Curve: BezierCurve, { diff --git a/src/bezier/offset_scaling.rs b/src/bezier/offset_scaling.rs index 4dd334ef..025e173d 100644 --- a/src/bezier/offset_scaling.rs +++ b/src/bezier/offset_scaling.rs @@ -133,8 +133,8 @@ where /// /// Attempts a simple offset of a curve, and subdivides it if the midpoint is too far away from the expected distance /// -fn subdivide_offset<'a, CurveIn, CurveOut>( - curve: &CurveSection<'a, CurveIn>, +fn subdivide_offset( + curve: &CurveSection, initial_offset: f64, final_offset: f64, depth: usize, diff --git a/src/bezier/path/graph_path/edge.rs b/src/bezier/path/graph_path/edge.rs index cc1db27a..89d0a238 100644 --- a/src/bezier/path/graph_path/edge.rs +++ b/src/bezier/path/graph_path/edge.rs @@ -65,7 +65,7 @@ impl<'a, Point: 'a, Label: 'a + Copy> GraphEdge<'a, Point, Label> { /// Retrieves a reference to the edge in the graph /// #[inline] - fn edge<'b>(&'b self) -> &'b GraphPathEdge { + fn edge(&self) -> &GraphPathEdge { &self.graph.points[self.edge.start_idx].forward_edges[self.edge.edge_idx] } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index 39bf6b39..ad0229cd 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -308,7 +308,7 @@ impl GraphPath { /// Returns an iterator of all edges in this graph /// #[inline] - pub fn all_edges<'a>(&'a self) -> impl 'a + Iterator> { + pub fn all_edges(&self) -> impl Iterator> { (0..(self.points.len())) .into_iter() .flat_map(move |point_num| self.edges_for_point(point_num)) @@ -318,7 +318,7 @@ impl GraphPath { /// Returns an iterator of all the edges in this graph, as references /// #[inline] - pub fn all_edge_refs<'a>(&'a self) -> impl 'a + Iterator { + pub fn all_edge_refs(&self) -> impl Iterator + '_ { (0..(self.points.len())) .into_iter() .flat_map(move |point_idx| { @@ -338,10 +338,10 @@ impl GraphPath { /// Edges are directional: this will provide the edges that leave the supplied point /// #[inline] - pub fn edges_for_point<'a>( - &'a self, + pub fn edges_for_point( + &self, point_num: usize, - ) -> impl 'a + Iterator> { + ) -> impl Iterator> { (0..(self.points[point_num].forward_edges.len())) .into_iter() .map(move |edge_idx| { @@ -382,10 +382,10 @@ impl GraphPath { /// /// Edges are directional: this will provide the edges that connect to the supplied point /// - pub fn reverse_edges_for_point<'a>( - &'a self, + pub fn reverse_edges_for_point( + &self, point_num: usize, - ) -> impl 'a + Iterator> { + ) -> impl Iterator> { // Fetch the points that connect to this point self.points[point_num] .connected_from @@ -701,7 +701,7 @@ impl GraphPath { /// Returns the GraphEdge for an edgeref /// #[inline] - pub fn get_edge<'a>(&'a self, edge: GraphEdgeRef) -> GraphEdge<'a, Point, Label> { + pub fn get_edge(&self, edge: GraphEdgeRef) -> GraphEdge { GraphEdge::new(self, edge) } diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index 958d8df6..4847e365 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -48,7 +48,7 @@ impl GraphPath { /// /// Retrieves the ordered graph edges for a range of points /// - fn get_ordered_edges<'a>(&'a self, points: Range) -> Vec> { + fn get_ordered_edges(&self, points: Range) -> Vec> { let mut ordered_edges = points .into_iter() .flat_map(|point_idx| { diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index 4c42cc21..f9ada073 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -12,10 +12,10 @@ use crate::geo::*; /// be spaced according to the shape of the curve (will have an uneven distance between them) /// #[inline] -pub fn walk_curve_unevenly<'a, Curve: BezierCurve>( - curve: &'a Curve, +pub fn walk_curve_unevenly( + curve: &Curve, num_subdivisions: usize, -) -> impl 'a + Iterator> { +) -> impl Iterator> { if num_subdivisions > 0 { UnevenWalkIterator { curve, @@ -39,11 +39,11 @@ pub fn walk_curve_unevenly<'a, Curve: BezierCurve>( /// This walks evenly using the curve's chord length rather than the arc length: each section returned will have a `chord_length()` /// of `distance`. The call `vary_by()` can be used on the result to vary the step size at each point. /// -pub fn walk_curve_evenly<'a, Curve: BezierCurve>( - curve: &'a Curve, +pub fn walk_curve_evenly( + curve: &Curve, distance: f64, max_error: f64, -) -> EvenWalkIterator<'a, Curve> { +) -> EvenWalkIterator { const INITIAL_INCREMENT: f64 = 0.01; // Too small or negative values might produce bad effects due to floating point inprecision From b041ec7463885ef0104934576e64a1a9dedd619a Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:13:42 +0100 Subject: [PATCH 12/31] fix clippy::from_over_into --- src/bezier/characteristics.rs | 6 +++--- src/bezier/path/algorithms/fill_concave.rs | 6 +++--- src/geo/coordinate.rs | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index c9b809b3..ea02264b 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -300,10 +300,10 @@ fn find_inflection_points(b4: (f64, f64)) -> InflectionPoints { } } -impl Into for InflectionPoints { +impl From for CurveFeatures { #[inline] - fn into(self) -> CurveFeatures { - match self { + fn from(ip: InflectionPoints) -> CurveFeatures { + match ip { InflectionPoints::Zero => CurveFeatures::Arch, InflectionPoints::One(t) => CurveFeatures::SingleInflectionPoint(t), InflectionPoints::Two(t1, t2) => CurveFeatures::DoubleInflectionPoint(t1, t2), diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index f37b6be6..30bf5766 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -20,9 +20,9 @@ enum ConcaveItem { SelfIntersection(usize), } -impl Into> for ConcaveItem { - fn into(self) -> Option { - match self { +impl From> for Option { + fn from(ci: ConcaveItem) -> Option { + match ci { ConcaveItem::Edge(item) => Some(item), ConcaveItem::SelfIntersection(_) => None, } diff --git a/src/geo/coordinate.rs b/src/geo/coordinate.rs index e212913e..4b5cb5cd 100644 --- a/src/geo/coordinate.rs +++ b/src/geo/coordinate.rs @@ -306,9 +306,9 @@ impl From<(f64, f64)> for Coord2 { } } -impl Into<(f64, f64)> for Coord2 { - fn into(self) -> (f64, f64) { - (self.0, self.1) +impl From for (f64, f64) { + fn from(c: Coord2) -> (f64, f64) { + (c.0, c.1) } } @@ -318,9 +318,9 @@ impl From<(f32, f32)> for Coord2 { } } -impl Into<(f32, f32)> for Coord2 { - fn into(self) -> (f32, f32) { - (self.0 as _, self.1 as _) +impl From for (f32, f32) { + fn from(c: Coord2) -> (f32, f32) { + (c.0 as _, c.1 as _) } } From 50bf4334c37082e460ad6464f63a194f05eecefb Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:14:28 +0100 Subject: [PATCH 13/31] fix clippy::needless_return --- src/bezier/path/arithmetic/chain_add.rs | 2 +- src/geo/coordinate.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bezier/path/arithmetic/chain_add.rs b/src/bezier/path/arithmetic/chain_add.rs index aea3fffa..a5f92a9f 100644 --- a/src/bezier/path/arithmetic/chain_add.rs +++ b/src/bezier/path/arithmetic/chain_add.rs @@ -37,7 +37,7 @@ where return true; } } - return false; + false }); merged_path.heal_exterior_gaps(); diff --git a/src/geo/coordinate.rs b/src/geo/coordinate.rs index 4b5cb5cd..5c6f0fc1 100644 --- a/src/geo/coordinate.rs +++ b/src/geo/coordinate.rs @@ -112,7 +112,7 @@ pub trait Coordinate: } } - return false; + false } /// From 0f76e964150ab307370a9d6eaca56edd2f45a911 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:15:06 +0100 Subject: [PATCH 14/31] fix clippy::needless_borrow --- src/bezier/overlaps.rs | 2 +- src/bezier/path/intersection.rs | 2 +- src/debug/path_to_string.rs | 6 +++--- tests/bezier/path/arithmetic_add.rs | 2 +- tests/bezier/path/svg.rs | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bezier/overlaps.rs b/src/bezier/overlaps.rs index 58b56f85..13b48d87 100644 --- a/src/bezier/overlaps.rs +++ b/src/bezier/overlaps.rs @@ -74,7 +74,7 @@ where // Start and end points match at t1, t2 #[inline] fn close_enough(p1: &P, p2: &P) -> bool { - p1.is_near_to(&p2, SMALL_DISTANCE) + p1.is_near_to(p2, SMALL_DISTANCE) } // Get the control points for the two curves diff --git a/src/bezier/path/intersection.rs b/src/bezier/path/intersection.rs index e23d15a8..505ea496 100644 --- a/src/bezier/path/intersection.rs +++ b/src/bezier/path/intersection.rs @@ -90,7 +90,7 @@ where // Only search for intersections if these two sections have overlapping bounding boxes if p1_curve_bounds.overlaps(p2_curve_bounds) { // Determine the intersections (if any) between these two curves - let intersections = curve_intersects_curve_clip(&p1_curve, &p2_curve, accuracy); + let intersections = curve_intersects_curve_clip(&p1_curve, p2_curve, accuracy); // Combine with the section IDs to generate the results result.extend( diff --git a/src/debug/path_to_string.rs b/src/debug/path_to_string.rs index de669958..04f93059 100644 --- a/src/debug/path_to_string.rs +++ b/src/debug/path_to_string.rs @@ -15,7 +15,7 @@ pub fn bezier_path_to_rust_definition::start(Coord2({}, {}))", start.x(), start.y() @@ -24,7 +24,7 @@ pub fn bezier_path_to_rust_definition>(); for point in points.iter() { - let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(&point); + let distance_to_circle1 = Coord2(5.0, 5.0).distance_to(point); // Must be on either circle assert!((distance_to_circle1 - 4.0).abs() < 0.01); diff --git a/tests/bezier/path/svg.rs b/tests/bezier/path/svg.rs index 5e6c08f1..4a7cb7d9 100644 --- a/tests/bezier/path/svg.rs +++ b/tests/bezier/path/svg.rs @@ -10,7 +10,7 @@ where let mut svg = String::new(); write!( - &mut svg, + svg, "M {} {}", path.start_point().x(), path.start_point().y() @@ -18,7 +18,7 @@ where .unwrap(); for (cp1, cp2, end) in path.points() { write!( - &mut svg, + svg, " C {} {}, {} {}, {} {}", cp1.x(), cp1.y(), From 388d19a6bae0057058231ba073bf7185d850fc38 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:22:35 +0100 Subject: [PATCH 15/31] fix clippy::manual_range_contains --- src/bezier/characteristics.rs | 6 +++--- src/bezier/intersection/curve_line.rs | 4 ++-- src/bezier/intersection/fat_line.rs | 4 ++-- src/bezier/path/ray.rs | 2 +- src/bezier/solve.rs | 2 +- src/line/intersection.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index ea02264b..2d2314aa 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -286,13 +286,13 @@ fn find_inflection_points(b4: (f64, f64)) -> InflectionPoints { let t2 = lhs + rhs; // Want points between 0 and 1 - if t1 < 0.0 || t1 > 1.0 { - if t2 < 0.0 || t2 > 1.0 { + if !(0.0..=1.0).contains(&t1) { + if !(0.0..=1.0).contains(&t2) { InflectionPoints::Zero } else { InflectionPoints::One(t2) } - } else if t2 < 0.0 || t2 > 1.0 { + } else if !(0.0..=1.0).contains(&t2) { InflectionPoints::One(t1) } else { InflectionPoints::Two(t1, t2) diff --git a/src/bezier/intersection/curve_line.rs b/src/bezier/intersection/curve_line.rs index 1bd7ce4b..b610f962 100644 --- a/src/bezier/intersection/curve_line.rs +++ b/src/bezier/intersection/curve_line.rs @@ -107,7 +107,7 @@ where t }; - if t >= 0.0 && t <= 1.0 { + if (0.0..=1.0).contains(&t) { // Calculate the position on the curve let pos = de_casteljau4(t, w1, w2, w3, w4); @@ -145,7 +145,7 @@ where C::Point: Coordinate2D, { let mut ray_intersections = curve_intersects_ray(curve, line); - ray_intersections.retain(|(_t, s, _pos)| s >= &mut 0.0 && s <= &mut 1.0); + ray_intersections.retain(|(_t, s, _pos)| (&mut 0.0..=&mut 1.0).contains(&s)); ray_intersections } diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index 9ed03a5a..6b6d5991 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -186,7 +186,7 @@ impl FatLine { // The y axis indicates where the hull crosses from inside to outside the fat line if let Some(t1a) = t1a { // Line crossed d_min - if t1a >= 0.0 && t1a <= 1.0 { + if (0.0..=1.0).contains(&t1a) { t1 = t1.min(t1a); t2 = t2.max(t1a); } @@ -194,7 +194,7 @@ impl FatLine { if let Some(t2a) = t2a { // Line crossed d_max - if t2a >= 0.0 && t2a <= 1.0 { + if (0.0..=1.0).contains(&t2a) { t1 = t1.min(t2a); t2 = t2.max(t2a); } diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index 98cbe89f..5d02e1ef 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -125,7 +125,7 @@ where { // If all the distances are small enough, this section is collinear RayCanIntersect::Collinear - } else if side < -3.99 || side > 3.99 { + } else if !(-3.99..=3.99).contains(&side) { // If the side sums to 4, all points are on the same side RayCanIntersect::WrongSide } else { diff --git a/src/bezier/solve.rs b/src/bezier/solve.rs index 08238dcf..38d93bfe 100644 --- a/src/bezier/solve.rs +++ b/src/bezier/solve.rs @@ -94,7 +94,7 @@ pub fn solve_curve_for_t_along_axis( for possible_t in possible_t_values { // Ignore values outside the range of the curve - if possible_t < -0.001 || possible_t > 1.001 { + if !(-0.001..=1.001).contains(&possible_t) { continue; } diff --git a/src/line/intersection.rs b/src/line/intersection.rs index 464c54f3..930cc907 100644 --- a/src/line/intersection.rs +++ b/src/line/intersection.rs @@ -27,7 +27,7 @@ where let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); - if ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0 { + if (0.0..=1.0).contains(&ua) && ub >= 0.0 && ub <= 1.0 { Some(L::Point::from_components(&[ x1 + (ua * (x2 - x1)), y1 + (ua * (y2 - y1)), @@ -57,7 +57,7 @@ where let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); - if ua >= 0.0 && ua <= 1.0 { + if (0.0..=1.0).contains(&ua) { Some(L::Point::from_components(&[ x1 + (ua * (x2 - x1)), y1 + (ua * (y2 - y1)), From f0bc8e3677c4038e67366173c57199e10e31af15 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:27:41 +0100 Subject: [PATCH 16/31] fix clippy::needless_collect --- src/bezier/path/path.rs | 7 ++++--- tests/sweep.rs | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/bezier/path/path.rs b/src/bezier/path/path.rs index 08031618..7122830b 100644 --- a/src/bezier/path/path.rs +++ b/src/bezier/path/path.rs @@ -63,12 +63,13 @@ pub trait BezierPath: Geo + Clone + Sized { let points = iter::once(fake_first_point).chain(points); // Reverse the direction of the path - let reversed_points = points + let mut reversed_points = points .tuple_windows() .map(|((_, _, start_point), (cp1, cp2, _))| (cp2, cp1, start_point)) - .collect::>(); + .collect_vec(); + reversed_points.reverse(); - POut::from_points(self.start_point(), reversed_points.into_iter().rev()) + POut::from_points(self.start_point(), reversed_points.into_iter()) } } diff --git a/tests/sweep.rs b/tests/sweep.rs index 10679237..4eec7f49 100644 --- a/tests/sweep.rs +++ b/tests/sweep.rs @@ -16,9 +16,9 @@ fn sweep_self_single_overlap() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_self(bounds.iter()).collect::>(); + let collisions = sweep_self(bounds.iter()); - assert!(collisions.len() == 1); + assert!(collisions.count() == 1); } #[test] @@ -35,9 +35,9 @@ fn sweep_self_double_overlap() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_self(bounds.iter()).collect::>(); + let collisions = sweep_self(bounds.iter()); - assert!(collisions.len() == 2); + assert!(collisions.count() == 2); } #[test] @@ -54,9 +54,9 @@ fn sweep_self_triple_overlap() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_self(bounds.iter()).collect::>(); + let collisions = sweep_self(bounds.iter()); - assert!(collisions.len() == 3); + assert!(collisions.count() == 3); } #[test] @@ -74,9 +74,9 @@ fn sweep_self_quad_overlap() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_self(bounds.iter()).collect::>(); + let collisions = sweep_self(bounds.iter()); - assert!(collisions.len() == 6); + assert!(collisions.count() == 6); } #[test] @@ -102,9 +102,9 @@ fn sweep_against_single_overlap() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); + let collisions = sweep_against(bounds1.iter(), bounds2.iter()); - assert!(collisions.len() == 1); + assert!(collisions.count() == 1); } #[test] @@ -130,9 +130,9 @@ fn sweep_against_double_overlap_1() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); + let collisions = sweep_against(bounds1.iter(), bounds2.iter()); - assert!(collisions.len() == 2); + assert!(collisions.count() == 2); } #[test] @@ -158,9 +158,9 @@ fn sweep_against_double_overlap_2() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); + let collisions = sweep_against(bounds1.iter(), bounds2.iter()); - assert!(collisions.len() == 2); + assert!(collisions.count() == 2); } #[test] @@ -186,9 +186,9 @@ fn sweep_against_quad_overlap() { .unwrap_or(Ordering::Equal) }); - let collisions = sweep_against(bounds1.iter(), bounds2.iter()).collect::>(); + let collisions = sweep_against(bounds1.iter(), bounds2.iter()); - assert!(collisions.len() == 4); + assert!(collisions.count() == 4); } #[test] From 454d369c3ddbc2ec34dd02ff770f45db3441e075 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:28:54 +0100 Subject: [PATCH 17/31] fix clippy::iter_nth_zero --- src/bezier/intersection/self_intersection.rs | 4 ++-- src/bezier/path/graph_path/mod.rs | 2 +- src/bezier/path/ray.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bezier/intersection/self_intersection.rs b/src/bezier/intersection/self_intersection.rs index a078a481..46de2679 100644 --- a/src/bezier/intersection/self_intersection.rs +++ b/src/bezier/intersection/self_intersection.rs @@ -71,14 +71,14 @@ where // Only found a single intersection intersections .into_iter() - .nth(0) + .next() .map(|(t1, t2)| (left.t_for_t(t1), right.t_for_t(t2))) } else { // Intersection may include the point between the left and right curves (ignore any point that's at t=1 on the left or t=0 on the right) intersections .into_iter() .filter(|(t1, t2)| *t1 < 1.0 && *t2 > 0.0) - .nth(0) + .next() .map(|(t1, t2)| (left.t_for_t(t1), right.t_for_t(t2))) } } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index ad0229cd..868a1457 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -512,7 +512,7 @@ impl GraphPath { None } }) - .nth(0); + .next(); test_assert!(previous_edge_ref.is_some()); diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index 5d02e1ef..c9c0c12c 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -528,7 +528,7 @@ where .filter(|previous_edge| { path.edge_following_edge_idx(*previous_edge) == edge.edge_idx }) - .nth(0) + .next() .expect("Previous edge for a collision at start"); (previous_edge, edge) From a246fce4ed58351bd035b00645c8af576611879f Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:34:02 +0100 Subject: [PATCH 18/31] fix clippy::filter_next --- src/bezier/intersection/self_intersection.rs | 3 +-- src/bezier/path/ray.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bezier/intersection/self_intersection.rs b/src/bezier/intersection/self_intersection.rs index 46de2679..cb3eb5c1 100644 --- a/src/bezier/intersection/self_intersection.rs +++ b/src/bezier/intersection/self_intersection.rs @@ -77,8 +77,7 @@ where // Intersection may include the point between the left and right curves (ignore any point that's at t=1 on the left or t=0 on the right) intersections .into_iter() - .filter(|(t1, t2)| *t1 < 1.0 && *t2 > 0.0) - .next() + .find(|(t1, t2)| *t1 < 1.0 && *t2 > 0.0) .map(|(t1, t2)| (left.t_for_t(t1), right.t_for_t(t2))) } } diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index c9c0c12c..89408421 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -525,10 +525,9 @@ where .reverse_edges_for_point(edge.start_idx) .into_iter() .map(|previous_edge| previous_edge.reversed()) - .filter(|previous_edge| { + .find(|previous_edge| { path.edge_following_edge_idx(*previous_edge) == edge.edge_idx }) - .next() .expect("Previous edge for a collision at start"); (previous_edge, edge) From 1e5bb48261b89a69918a042b26c76033342d4acc Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:44:09 +0100 Subject: [PATCH 19/31] fix clippy::stable_sort_primitive --- src/bezier/path/algorithms/fill_concave.rs | 2 +- src/bezier/path/graph_path/mod.rs | 4 ++-- src/bezier/path/graph_path/path_collision.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index 30bf5766..04d6e0b7 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -169,7 +169,7 @@ fn remove_small_gaps( // Remove any long edges that were affected by the gap removal operation if !long_edges_to_remove.is_empty() { - long_edges_to_remove.sort(); + long_edges_to_remove.sort_unstable(); for long_edge_num in long_edges_to_remove.into_iter().rev() { long_edges.remove(long_edge_num); } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index 868a1457..cc02a145 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -291,7 +291,7 @@ impl GraphPath { // Sort and deduplicate them for point_idx in 0..(self.points.len()) { - self.points[point_idx].connected_from.sort(); + self.points[point_idx].connected_from.sort_unstable(); self.points[point_idx].connected_from.dedup(); } } @@ -542,7 +542,7 @@ impl GraphPath { // For all the connected points, update the following edge refs let mut still_connected = false; - self.points[edge_ref.start_idx].connected_from.sort(); + self.points[edge_ref.start_idx].connected_from.sort_unstable(); self.points[edge_ref.start_idx].connected_from.dedup(); for connected_point_idx in self.points[edge_ref.start_idx].connected_from.clone() { for edge_idx in 0..(self.points[connected_point_idx].forward_edges.len()) { diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index 4847e365..5bd63144 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -726,7 +726,7 @@ impl GraphPath { // If we introduced duplicates, remove them if remapped { - point.connected_from.sort(); + point.connected_from.sort_unstable(); point.connected_from.dedup(); } } From bd30abc96507deebe700887921aae785835c6fab Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:45:06 +0100 Subject: [PATCH 20/31] fix clippy::map_flatten --- src/bezier/path/graph_path/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index cc02a145..ef97fcfc 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -490,7 +490,7 @@ impl GraphPath { let previous_edge_ref = self.points[edge_ref.start_idx] .connected_from .iter() - .map(|point_idx| { + .flat_map(|point_idx| { let point_idx = *point_idx; self.points[point_idx] .forward_edges @@ -498,7 +498,6 @@ impl GraphPath { .enumerate() .map(move |(edge_idx, edge)| (point_idx, edge_idx, edge)) }) - .flatten() .filter_map(|(point_idx, edge_idx, edge)| { if edge.end_idx == edge_ref.start_idx && edge.following_edge_idx == edge_ref.edge_idx From 441fd7c67502d143062c0784f6a35d69fa80faf7 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:46:58 +0100 Subject: [PATCH 21/31] fix clippy::option_map_unit_fn --- src/bezier/path/graph_path/path_collision.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index 5bd63144..72f34f5b 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -421,10 +421,10 @@ impl GraphPath { for (t, end_point_idx) in collisions { // Point the previous edge at the new edge we're adding let new_edge_idx = self.points[last_point_idx].forward_edges.len(); - previous_edge.map(|(point_idx, edge_idx)| { + if let Some((point_idx, edge_idx)) = previous_edge { self.points[point_idx].forward_edges[edge_idx].following_edge_idx = new_edge_idx - }); + } // Subdivide the remaining edge let t2 = (t - (1.0 - remaining_t)) / remaining_t; @@ -454,10 +454,10 @@ impl GraphPath { if found_collisions { // Point the previous edge at the new edge we're adding let new_edge_idx = self.points[last_point_idx].forward_edges.len(); - previous_edge.map(|(point_idx, edge_idx)| { + if let Some((point_idx, edge_idx)) = previous_edge { self.points[point_idx].forward_edges[edge_idx].following_edge_idx = new_edge_idx - }); + } // This edge ends where the original edge ended let end_point_idx = final_point_idx; From 4fa57ade34dca0b1011ce64cfe1394560bbaf5a0 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:49:22 +0100 Subject: [PATCH 22/31] fix clippy::nonminimal_bool --- src/bezier/path/graph_path/path_collision.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index 72f34f5b..cc79781a 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -154,7 +154,7 @@ impl GraphPath { for edge in ordered_edges { // Colliding edge against itself if let Some((t1, t2)) = find_self_intersection_point(&edge, accuracy) { - if !(t1 <= 0.0 && t2 >= 1.0) && !(t1 >= 1.0 && t2 <= 0.0) { + if !(t1 <= 0.0 && t2 >= 1.0 || t1 >= 1.0 && t2 <= 0.0) { collisions.push(Collision { edge_1: edge.edge, edge_2: edge.edge, From ede868e8b792ba331668ea7a9bfcaeb120986843 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:53:46 +0100 Subject: [PATCH 23/31] fix clippy::len_zero --- src/bezier/path/arithmetic/ray_cast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bezier/path/arithmetic/ray_cast.rs b/src/bezier/path/arithmetic/ray_cast.rs index abedb163..5e102bb3 100644 --- a/src/bezier/path/arithmetic/ray_cast.rs +++ b/src/bezier/path/arithmetic/ray_cast.rs @@ -55,7 +55,7 @@ impl GraphPath { // For collisions that overlap, ensure that the first shape is outermost so that subtractions work (swap based on the direction) // This interacts with the ordering chosen in ray_collisions: if that ordering changes this may no longer be correct - if collisions.len() > 0 { + if !collisions.is_empty() { for collision_idx in 0..(collisions.len() - 1) { let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx + 0]; let (collision_b, _curve_t, line_t_b, _pos) = &collisions[collision_idx + 1]; From 4071454effce9372d8fc98f7b161d75bd7413f8a Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:56:00 +0100 Subject: [PATCH 24/31] fix clippy::identity_op --- src/bezier/path/arithmetic/ray_cast.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bezier/path/arithmetic/ray_cast.rs b/src/bezier/path/arithmetic/ray_cast.rs index 5e102bb3..98f48f78 100644 --- a/src/bezier/path/arithmetic/ray_cast.rs +++ b/src/bezier/path/arithmetic/ray_cast.rs @@ -57,7 +57,7 @@ impl GraphPath { // This interacts with the ordering chosen in ray_collisions: if that ordering changes this may no longer be correct if !collisions.is_empty() { for collision_idx in 0..(collisions.len() - 1) { - let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx + 0]; + let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx]; let (collision_b, _curve_t, line_t_b, _pos) = &collisions[collision_idx + 1]; if line_t_a == line_t_b { @@ -126,8 +126,7 @@ impl GraphPath { // This interacts with the ordering chosen in ray_collisions: if that ordering changes this may no longer be correct if !collisions.is_empty() { for collision_idx in 0..(collisions.len() - 1) { - let (collision_a, _curve_t, line_t_a, _pos) = - &collisions[collision_idx + 0]; + let (collision_a, _curve_t, line_t_a, _pos) = &collisions[collision_idx]; let (collision_b, _curve_t, line_t_b, _pos) = &collisions[collision_idx + 1]; From 923bb78d9a8fe5b5d04ba9bd70f0a0bb9aa49dcb Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 11:57:21 +0100 Subject: [PATCH 25/31] fix clippy::while_let_on_iterator --- src/bezier/path/arithmetic/chain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bezier/path/arithmetic/chain.rs b/src/bezier/path/arithmetic/chain.rs index 934d214f..c7c5902d 100644 --- a/src/bezier/path/arithmetic/chain.rs +++ b/src/bezier/path/arithmetic/chain.rs @@ -68,7 +68,7 @@ where .unwrap_or_else(|| PathCombine::Path(vec![])); let mut result = path_combine(result, accuracy); - while let Some(to_subtract) = path_iter.next() { + for to_subtract in path_iter { let to_subtract = path_combine(to_subtract, accuracy); result = path_sub(&result, &to_subtract, accuracy); } @@ -83,7 +83,7 @@ where .unwrap_or_else(|| PathCombine::Path(vec![])); let mut result = path_combine(result, accuracy); - while let Some(to_intersect) = path_iter.next() { + for to_intersect in path_iter { let to_intersect = path_combine(to_intersect, accuracy); result = path_intersect(&result, &to_intersect, accuracy); } From a6d1f7b7615b287a33b3e96aaeee7294f75eea37 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 13:06:25 +0100 Subject: [PATCH 26/31] fix clippy::needless_range_loop --- src/bezier/fit.rs | 8 ++++---- src/bezier/path/algorithms/fill_concave.rs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bezier/fit.rs b/src/bezier/fit.rs index b4a30a39..479a390b 100644 --- a/src/bezier/fit.rs +++ b/src/bezier/fit.rs @@ -182,14 +182,14 @@ fn chords_for_points(points: &[Point]) -> Vec { // Compute the distances for each point distances.push(total_distance); - for p in 1..points.len() { - total_distance += points[p - 1].distance_to(&points[p]); + for ps in points.windows(2) { + total_distance += ps[0].distance_to(&ps[1]); distances.push(total_distance); } // Normalize to the range 0..1 - for p in 0..points.len() { - distances[p] /= total_distance; + for distance in &mut distances { + *distance /= total_distance; } distances diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index 04d6e0b7..b48ad6d6 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -330,13 +330,13 @@ where ); // Update the remaining long edge indexes - for update_idx in long_edge_index..long_edges.len() { - if long_edges[update_idx].edge_index.0 >= edge_index { - long_edges[update_idx].edge_index.0 += num_new_edges; + for long_edge in long_edges.iter_mut().skip(long_edge_index) { + if long_edge.edge_index.0 >= edge_index { + long_edge.edge_index.0 += num_new_edges; } - if long_edges[update_idx].edge_index.1 >= edge_index { - long_edges[update_idx].edge_index.1 += num_new_edges; + if long_edge.edge_index.1 >= edge_index { + long_edge.edge_index.1 += num_new_edges; } } From 71fed64f1c8e3b7dd577aada3a17de51e7ecc949 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 13:17:57 +0100 Subject: [PATCH 27/31] fix clippy::ptr_arg --- src/bezier/path/algorithms/fill_concave.rs | 2 +- src/bezier/path/arithmetic/add.rs | 8 ++++---- src/bezier/path/arithmetic/chain.rs | 5 +++-- src/bezier/path/arithmetic/chain_add.rs | 2 +- src/bezier/path/arithmetic/cut.rs | 4 ++-- src/bezier/path/arithmetic/full_intersect.rs | 4 ++-- src/bezier/path/arithmetic/intersect.rs | 4 ++-- src/bezier/path/arithmetic/sub.rs | 4 ++-- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index b48ad6d6..8d94f483 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -101,7 +101,7 @@ where /// fn remove_small_gaps( center: &Coord, - edges: &mut Vec>, + edges: &mut [RayCollision], long_edges: &mut Vec>, min_gap_size: f64, ) where diff --git a/src/bezier/path/arithmetic/add.rs b/src/bezier/path/arithmetic/add.rs index 48a6d184..ae2a28f0 100644 --- a/src/bezier/path/arithmetic/add.rs +++ b/src/bezier/path/arithmetic/add.rs @@ -39,8 +39,8 @@ impl GraphPath { /// effectively represents a path intended to be rendered with an even-odd winding rule) /// pub fn path_add( - path1: &Vec, - path2: &Vec, + path1: &[P1], + path2: &[P2], accuracy: f64, ) -> Vec where @@ -93,7 +93,7 @@ where /// See `path_remove_overlapped_points()` for a version that considers all edges within the path to be exterior edges. /// pub fn path_remove_interior_points( - path: &Vec, + path: &[P1], accuracy: f64, ) -> Vec where @@ -136,7 +136,7 @@ where /// winding rules presuppose you can tell if a subpath is inside or outside of an existing path. /// pub fn path_remove_overlapped_points( - path: &Vec, + path: &[P1], accuracy: f64, ) -> Vec where diff --git a/src/bezier/path/arithmetic/chain.rs b/src/bezier/path/arithmetic/chain.rs index c7c5902d..30f55a55 100644 --- a/src/bezier/path/arithmetic/chain.rs +++ b/src/bezier/path/arithmetic/chain.rs @@ -54,10 +54,11 @@ where PathCombine::Path(result) => result, PathCombine::RemoveInteriorPoints(path) => path_remove_interior_points(&path, accuracy), PathCombine::Add(paths) => path_add_chain( - &paths + paths .into_iter() .map(|path| path_combine(path, accuracy)) - .collect(), + .collect::>() + .as_slice(), accuracy, ), diff --git a/src/bezier/path/arithmetic/chain_add.rs b/src/bezier/path/arithmetic/chain_add.rs index a5f92a9f..b5246c3a 100644 --- a/src/bezier/path/arithmetic/chain_add.rs +++ b/src/bezier/path/arithmetic/chain_add.rs @@ -7,7 +7,7 @@ use super::ray_cast::*; /// Adds multiple paths in a single operation /// pub fn path_add_chain( - paths: &Vec>, + paths: &[Vec

], accuracy: f64, ) -> Vec where diff --git a/src/bezier/path/arithmetic/cut.rs b/src/bezier/path/arithmetic/cut.rs index 5874bf65..81df83f1 100644 --- a/src/bezier/path/arithmetic/cut.rs +++ b/src/bezier/path/arithmetic/cut.rs @@ -19,8 +19,8 @@ pub struct PathCut { /// the part that was exterior in one operation /// pub fn path_cut( - path1: &Vec, - path2: &Vec, + path1: &[P1], + path2: &[P2], accuracy: f64, ) -> PathCut where diff --git a/src/bezier/path/arithmetic/full_intersect.rs b/src/bezier/path/arithmetic/full_intersect.rs index 6fba40d6..1e58db47 100644 --- a/src/bezier/path/arithmetic/full_intersect.rs +++ b/src/bezier/path/arithmetic/full_intersect.rs @@ -19,8 +19,8 @@ pub struct PathIntersection { /// Intersects two paths, returning both the path that is the intersection and the paths that are outside /// pub fn path_full_intersect( - path1: &Vec, - path2: &Vec, + path1: &[P1], + path2: &[P2], accuracy: f64, ) -> PathIntersection where diff --git a/src/bezier/path/arithmetic/intersect.rs b/src/bezier/path/arithmetic/intersect.rs index 934ffb6c..cd40c67a 100644 --- a/src/bezier/path/arithmetic/intersect.rs +++ b/src/bezier/path/arithmetic/intersect.rs @@ -22,8 +22,8 @@ impl GraphPath { /// effectively represents a path intended to be rendered with an even-odd winding rule) /// pub fn path_intersect( - path1: &Vec, - path2: &Vec, + path1: &[P1], + path2: &[P2], accuracy: f64, ) -> Vec where diff --git a/src/bezier/path/arithmetic/sub.rs b/src/bezier/path/arithmetic/sub.rs index 13e2215a..a6760b74 100644 --- a/src/bezier/path/arithmetic/sub.rs +++ b/src/bezier/path/arithmetic/sub.rs @@ -22,8 +22,8 @@ impl GraphPath { /// effectively represents a path intended to be rendered with an even-odd winding rule) /// pub fn path_sub( - path1: &Vec, - path2: &Vec, + path1: &[P1], + path2: &[P2], accuracy: f64, ) -> Vec where From 0977b529bbb8293218e6fa9ef18de9fcfbeb02be Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 13:28:38 +0100 Subject: [PATCH 28/31] smaller fixes --- src/bezier/normal.rs | 4 +--- src/bezier/offset_scaling.rs | 3 +-- src/bezier/path/algorithms/fill_concave.rs | 6 +++--- src/bezier/walk.rs | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/bezier/normal.rs b/src/bezier/normal.rs index f68e296a..57b602cf 100644 --- a/src/bezier/normal.rs +++ b/src/bezier/normal.rs @@ -96,9 +96,7 @@ where let (d1, d2, d3) = derivative4(w1, w2, w3, w4); // Get the tangent and the point at the specified t value - let tangent = de_casteljau3(t, d1, d2, d3); - - tangent + de_casteljau3(t, d1, d2, d3) } fn normal_at_pos(&self, t: f64) -> Curve::Point { diff --git a/src/bezier/offset_scaling.rs b/src/bezier/offset_scaling.rs index 025e173d..bef2bddc 100644 --- a/src/bezier/offset_scaling.rs +++ b/src/bezier/offset_scaling.rs @@ -217,8 +217,7 @@ where let subsection = curve.subsection(t1, t2); let offset1 = initial_offset + (final_offset - initial_offset) * t1; let offset2 = initial_offset + (final_offset - initial_offset) * t2; - let res = subdivide_offset(&subsection, offset1, offset2, depth + 1); - res + subdivide_offset(&subsection, offset1, offset2, depth + 1) }) .collect() } diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index 8d94f483..66a372a1 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -150,8 +150,8 @@ fn remove_small_gaps( // Map this edge to the gap line let edge = &mut edges[edge_num]; let edge_ray = (*center, edge.position); - edge.position = line_intersects_ray(&edge_ray, &gap_line) - .unwrap_or_else(|| edge.position); + edge.position = + line_intersects_ray(&edge_ray, &gap_line).unwrap_or(edge.position); // Move to the next edge edge_num += 1; @@ -408,7 +408,7 @@ where ); // Remove any interior points that the path might have (this happens when the fill path overlaps itself) - Some(path_remove_interior_points(&vec![overlapped_path], 0.01)) + Some(path_remove_interior_points(&[overlapped_path], 0.01)) } else { // No curves in the path None diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index f9ada073..b0107686 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -248,7 +248,7 @@ impl<'a, Curve: BezierCurve> Iterator for EvenWalkIterator<'a, Curve> { if adjustment >= t_increment { t_increment *= 0.3333333; } else { - t_increment = t_increment - adjustment; + t_increment -= adjustment; } } From ea39ce24282c68543ca13a107e2efdbee5544ac0 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 13:31:28 +0100 Subject: [PATCH 29/31] fix more clippy issues in tests --- benches/sweep.rs | 4 +- src/bezier/path/graph_path/test.rs | 2 +- tests/bezier/overlaps.rs | 6 +- tests/bezier/path/arithmetic_add.rs | 64 +++++++++---------- tests/bezier/path/arithmetic_chain_add.rs | 4 +- .../path/arithmetic_complicated_paths.rs | 14 ++-- tests/bezier/path/arithmetic_cut.rs | 8 +-- tests/bezier/path/arithmetic_intersect.rs | 26 ++++---- tests/bezier/path/arithmetic_sub.rs | 14 ++-- tests/bezier/path/graph_path.rs | 20 +++--- 10 files changed, 79 insertions(+), 83 deletions(-) diff --git a/benches/sweep.rs b/benches/sweep.rs index dfb68763..0830fa3f 100644 --- a/benches/sweep.rs +++ b/benches/sweep.rs @@ -80,9 +80,9 @@ fn create_graph_path(rng: &mut StdRng, n: usize) -> GraphPath { } let path = path_builder.build(); - let graph_path = GraphPath::from_path(&path, ()); + - graph_path + GraphPath::from_path(&path, ()) } fn detect_collisions(mut graph_path: GraphPath) { diff --git a/src/bezier/path/graph_path/test.rs b/src/bezier/path/graph_path/test.rs index 39534889..cf7bc6d4 100644 --- a/src/bezier/path/graph_path/test.rs +++ b/src/bezier/path/graph_path/test.rs @@ -342,7 +342,7 @@ fn find_gaps() { let mut graph_path = GraphPath::from_path(&path, ()); let edges = (0..4) .into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).nth(0).unwrap().into()) + .map(|point_idx| graph_path.edges_for_point(point_idx).next().unwrap().into()) .collect::>(); graph_path.set_edge_kind(edges[0], GraphPathEdgeKind::Exterior); diff --git a/tests/bezier/overlaps.rs b/tests/bezier/overlaps.rs index 57daa723..e09e9e2b 100644 --- a/tests/bezier/overlaps.rs +++ b/tests/bezier/overlaps.rs @@ -148,7 +148,7 @@ fn overlaps_with_known_curve_1() { || curve2.t_for_point(&curve1.end_point()).is_some() ); - assert!(!overlapping_region(&curve1, &curve2).is_some()); + assert!(overlapping_region(&curve1, &curve2).is_none()); } #[test] @@ -173,7 +173,7 @@ fn overlaps_with_known_curve_2() { // They currently don't assert!( - !curve1.t_for_point(&curve2.start_point()).is_some() + curve1.t_for_point(&curve2.start_point()).is_none() || curve2.t_for_point(&curve1.start_point()).is_some() ); assert!( @@ -181,7 +181,7 @@ fn overlaps_with_known_curve_2() { || curve2.t_for_point(&curve1.end_point()).is_some() ); - assert!(!overlapping_region(&curve1, &curve2).is_some()); + assert!(overlapping_region(&curve1, &curve2).is_none()); } #[test] diff --git a/tests/bezier/path/arithmetic_add.rs b/tests/bezier/path/arithmetic_add.rs index c520d1ed..cd1ec73e 100644 --- a/tests/bezier/path/arithmetic_add.rs +++ b/tests/bezier/path/arithmetic_add.rs @@ -12,7 +12,7 @@ fn add_two_overlapping_circles() { let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(combined_circles.len() == 1); @@ -65,7 +65,7 @@ fn add_two_identical_circles() { let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(combined_circles.len() == 1); @@ -94,7 +94,7 @@ fn add_two_very_close_circles() { let circle2 = Circle::new(Coord2(5.01, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); println!("{:?}", combined_circles.len()); assert!(!combined_circles.is_empty()); @@ -123,7 +123,7 @@ fn add_two_close_circles() { let circle2 = Circle::new(Coord2(503.0002955064407, 5.0), 300.0).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(!combined_circles.is_empty()); assert!(combined_circles.len() != 2); @@ -225,7 +225,7 @@ fn add_circle_inside_circle() { let circle2 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(combined_circles.len() == 1); @@ -256,7 +256,7 @@ fn add_two_overlapping_circles_further_apart() { let circle2 = Circle::new(Coord2(12.9, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(combined_circles.len() == 1); @@ -310,7 +310,7 @@ fn add_two_overlapping_circles_with_one_reversed() { let circle2 = circle2.reversed::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); println!("{:?}", combined_circles); assert!(combined_circles.len() == 1); @@ -364,7 +364,7 @@ fn add_two_non_overlapping_circles() { let circle2 = Circle::new(Coord2(20.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_add::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + let combined_circles = path_add::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); println!("{:?}", combined_circles); assert!(combined_circles.len() == 2); @@ -385,8 +385,8 @@ fn add_two_doughnuts() { // Combine them let combined_circles = path_add::<_, _, SimpleBezierPath>( - &vec![circle1, inner_circle1], - &vec![circle2, inner_circle2], + &[circle1, inner_circle1], + &[circle2, inner_circle2], 0.09, ); @@ -396,7 +396,7 @@ fn add_two_doughnuts() { "{:?}", combined_circles .iter() - .map(|path| svg_path_string(path)) + .map(svg_path_string) .collect::>() ); assert!(combined_circles.len() == 4); @@ -405,7 +405,7 @@ fn add_two_doughnuts() { #[test] fn remove_interior_from_circle_removes_nothing() { let circle = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![circle.clone()], 0.1); + let removed = path_remove_interior_points::<_, SimpleBezierPath>(&[circle.clone()], 0.1); assert!(removed.len() == 1); assert!(removed[0].1.len() == circle.1.len()); @@ -423,7 +423,7 @@ fn remove_interior_for_ring_removes_center() { let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &vec![ring1.clone(), ring2.clone()], + &[ring1.clone(), ring2], 0.01, ); @@ -448,7 +448,7 @@ fn remove_interior_for_ring_with_crossbar_removes_center() { .build(); let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + &[ring1.clone(), ring2, crossbar1], 0.01, ); @@ -490,7 +490,7 @@ fn remove_interior_for_ring_with_offset_crossbar_removes_center() { // Try the actual removing operation let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + &[ring1, ring2, crossbar1], 0.01, ); @@ -509,7 +509,7 @@ fn ring_with_offset_crossbar_ray_casting_issue() { .line_to(Coord2(0.2, 0.9)) .build(); - let path = vec![ring1.clone(), ring2.clone(), crossbar1.clone()]; + let path = vec![ring1, ring2, crossbar1]; let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( path.iter() @@ -543,12 +543,10 @@ fn remove_interior_for_ring_with_cross_removes_center() { .build(); let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &vec![ - ring1.clone(), - ring2.clone(), - crossbar1.clone(), - crossbar2.clone(), - ], + &[ring1.clone(), + ring2, + crossbar1, + crossbar2], 0.01, ); @@ -568,7 +566,7 @@ fn remove_overlapped_for_ring_does_not_remove_center() { let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( - &vec![ring1.clone(), ring2.clone()], + &[ring1.clone(), ring2.clone()], 0.01, ); @@ -600,7 +598,7 @@ fn remove_overlapped_for_ring_with_overlapping_crossbar() { .build(); let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( - &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + &[ring1, ring2, crossbar1], 0.01, ); @@ -619,7 +617,7 @@ fn remove_overlapped_for_ring_with_crossbar_in_space() { .build(); let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( - &vec![ring1.clone(), ring2.clone(), crossbar1.clone()], + &[ring1, ring2, crossbar1], 0.01, ); @@ -638,7 +636,7 @@ fn remove_interior_points_basic() { .build(); let with_points_removed: Vec = - path_remove_interior_points(&vec![with_interior_point], 0.1); + path_remove_interior_points(&[with_interior_point], 0.1); // Should be 5 points in the path with points removed assert!(with_points_removed.len() == 1); @@ -725,7 +723,7 @@ fn rectangle_add() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -763,7 +761,7 @@ fn rectangle_add_with_shared_point() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -801,7 +799,7 @@ fn rectangle_add_with_shared_point_2() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -841,7 +839,7 @@ fn rectangle_add_with_shared_point_3() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -890,7 +888,7 @@ fn rectangle_add_with_shared_point_4() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -931,7 +929,7 @@ fn rectangle_add_with_shared_point_5() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -970,7 +968,7 @@ fn rectangle_add_with_shared_point_6() { // Add them let shared_point = - path_add::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); diff --git a/tests/bezier/path/arithmetic_chain_add.rs b/tests/bezier/path/arithmetic_chain_add.rs index b723f430..cc3b6afb 100644 --- a/tests/bezier/path/arithmetic_chain_add.rs +++ b/tests/bezier/path/arithmetic_chain_add.rs @@ -239,7 +239,7 @@ fn add_two_doughnuts() { "{:?}", combined_circles .iter() - .map(|path| svg_path_string(path)) + .map(svg_path_string) .collect::>() ); assert!(combined_circles.len() == 4); @@ -257,7 +257,7 @@ fn remove_interior_points_basic() { .build(); let with_points_removed: Vec = - path_remove_interior_points(&vec![with_interior_point], 0.1); + path_remove_interior_points(&[with_interior_point], 0.1); // Should be 5 points in the path with points removed assert!(with_points_removed.len() == 1); diff --git a/tests/bezier/path/arithmetic_complicated_paths.rs b/tests/bezier/path/arithmetic_complicated_paths.rs index aaa071de..e2fe3881 100644 --- a/tests/bezier/path/arithmetic_complicated_paths.rs +++ b/tests/bezier/path/arithmetic_complicated_paths.rs @@ -421,13 +421,13 @@ fn remove_interior_points_1() { println!("{:?}", svg_path_string(&curve)); let with_points_removed: Vec = - path_remove_interior_points(&vec![curve], 0.01); + path_remove_interior_points(&[curve], 0.01); println!( "{:?}", with_points_removed .iter() - .map(|path| svg_path_string(path)) + .map(svg_path_string) .collect::>() ); @@ -498,13 +498,13 @@ fn remove_interior_points_1_without_failing_section() { println!("{:?}", svg_path_string(&curve)); let with_points_removed: Vec = - path_remove_interior_points(&vec![curve], 0.01); + path_remove_interior_points(&[curve], 0.01); println!( "{:?}", with_points_removed .iter() - .map(|path| svg_path_string(path)) + .map(svg_path_string) .collect::>() ); @@ -1289,7 +1289,7 @@ fn remove_interior_points_complex_2() { // This path has generated an error that indicates that no result path was generated (unfortunately it seems this version does not produce the error) let without_interior_points = - path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); + path_remove_interior_points::<_, SimpleBezierPath>(&[path], 0.01); /* // Bug appears to be that not all collisions are generated (so two self-collides in a row will generate more points) @@ -2652,7 +2652,7 @@ fn remove_interior_points_3() { ) .build(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); + let removed = path_remove_interior_points::<_, SimpleBezierPath>(&[path.clone()], 0.01); assert!(!removed.is_empty()); let mut graph_path = GraphPath::from_path(&path, PathLabel(0, PathDirection::Clockwise)); @@ -3025,7 +3025,7 @@ fn remove_interior_points_4() { .build(); // Fails an internal assertion (cannot determine if a line is in or outside) - let removed = path_remove_interior_points::<_, SimpleBezierPath>(&vec![path.clone()], 0.01); + let removed = path_remove_interior_points::<_, SimpleBezierPath>(&[path], 0.01); assert!(!removed.is_empty()); /* -- we do generate extra points right now, indicating that lines are overlapping diff --git a/tests/bezier/path/arithmetic_cut.rs b/tests/bezier/path/arithmetic_cut.rs index 2cc8650a..9409102a 100644 --- a/tests/bezier/path/arithmetic_cut.rs +++ b/tests/bezier/path/arithmetic_cut.rs @@ -17,7 +17,7 @@ fn cut_square() { .line_to(Coord2(7.5, 7.5)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&[square_1], &[square_2], 0.01); assert!(cut_square.exterior_path.len() == 1); assert!(cut_square.interior_path.len() == 1); @@ -42,7 +42,7 @@ fn cut_square_entirely_interior() { .line_to(Coord2(2.0, 2.0)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&[square_1], &[square_2], 0.01); assert!(cut_square.exterior_path.is_empty()); assert!(cut_square.interior_path.len() == 1); @@ -66,7 +66,7 @@ fn cut_square_entirely_exterior() { .line_to(Coord2(20.0, 20.0)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&[square_1], &[square_2], 0.01); assert!(cut_square.exterior_path.len() == 1); assert!(cut_square.interior_path.is_empty()); @@ -90,7 +90,7 @@ fn cut_square_center() { .line_to(Coord2(6.0, 6.0)) .build(); - let cut_square = path_cut::<_, _, SimpleBezierPath>(&vec![square_1], &vec![square_2], 0.01); + let cut_square = path_cut::<_, _, SimpleBezierPath>(&[square_1], &[square_2], 0.01); assert!(cut_square.exterior_path.len() == 2); assert!(cut_square.interior_path.len() == 1); diff --git a/tests/bezier/path/arithmetic_intersect.rs b/tests/bezier/path/arithmetic_intersect.rs index 15532bb9..dfb3f6d5 100644 --- a/tests/bezier/path/arithmetic_intersect.rs +++ b/tests/bezier/path/arithmetic_intersect.rs @@ -16,8 +16,8 @@ fn intersect_two_doughnuts() { // Combine them let combined_circles = path_intersect::<_, _, SimpleBezierPath>( - &vec![circle1, inner_circle1], - &vec![circle2, inner_circle2], + &[circle1, inner_circle1], + &[circle2, inner_circle2], 0.1, ); @@ -36,8 +36,8 @@ fn full_intersect_two_doughnuts() { // Combine them let intersection = path_full_intersect::<_, _, SimpleBezierPath>( - &vec![circle1, inner_circle1], - &vec![circle2, inner_circle2], + &[circle1, inner_circle1], + &[circle2, inner_circle2], 0.1, ); @@ -53,7 +53,7 @@ fn full_intersect_two_partially_overlapping_circles() { let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 1); @@ -66,7 +66,7 @@ fn full_intersect_two_non_overlapping_circles() { let circle2 = Circle::new(Coord2(15.0, 5.0), 4.0).to_path::(); let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.is_empty()); assert!(intersection.exterior_paths[0].len() == 1); @@ -79,7 +79,7 @@ fn full_intersect_interior_circles_1() { let circle2 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 2); @@ -92,7 +92,7 @@ fn full_intersect_interior_circles_2() { let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].is_empty()); @@ -105,7 +105,7 @@ fn fintersect_two_fully_overlapping_circles() { let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let intersection = - path_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + path_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.len() == 1); } @@ -116,7 +116,7 @@ fn full_intersect_two_fully_overlapping_circles() { let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.1); + path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); println!("{:?}", intersection); @@ -168,7 +168,7 @@ fn repeatedly_full_intersect_circle() { // Cut the circle via the fragment let cut_circle = - path_full_intersect::<_, _, SimpleBezierPath>(&vec![fragment], &remaining, 0.01); + path_full_intersect::<_, _, SimpleBezierPath>(&[fragment], &remaining, 0.01); // Add the slice and the remaining part of the circle slices.push(cut_circle.intersecting_path); @@ -356,7 +356,7 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { // Cut the circle via the fragment let cut_circle = path_full_intersect::<_, _, SimpleBezierPath>( - &vec![fragment.clone()], + &[fragment.clone()], &remaining, 0.01, ); @@ -401,7 +401,7 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { // Reduce and re-increase the precision of the remaining path (this happens in FlowBetween: even though the points will be in slightly different positions we should still be able to slice using this curve) remaining = remaining .into_iter() - .map(|path| convert_path_to_f32_and_back(path)) + .map(convert_path_to_f32_and_back) .collect(); } diff --git a/tests/bezier/path/arithmetic_sub.rs b/tests/bezier/path/arithmetic_sub.rs index b3625282..1a1c7a53 100644 --- a/tests/bezier/path/arithmetic_sub.rs +++ b/tests/bezier/path/arithmetic_sub.rs @@ -9,7 +9,7 @@ fn subtract_circles() { let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); // Combine them - let combined_circles = path_sub::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_sub::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(combined_circles.len() == 1); @@ -62,7 +62,7 @@ fn create_doughnut() { let circle2 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); // Create a hole in the larger circle - let combined_circles = path_sub::<_, _, SimpleBezierPath>(&vec![circle1], &vec![circle2], 0.01); + let combined_circles = path_sub::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.01); assert!(combined_circles.len() == 2); } @@ -74,7 +74,7 @@ fn erase_all() { let circle2 = Circle::new(Coord2(5.0, 5.0), 3.9).to_path::(); // Create a hole in the larger circle - let combined_circles = path_sub::<_, _, SimpleBezierPath>(&vec![circle2], &vec![circle1], 0.01); + let combined_circles = path_sub::<_, _, SimpleBezierPath>(&[circle2], &[circle1], 0.01); assert!(combined_circles.is_empty()); } @@ -92,7 +92,7 @@ fn subtract_from_self_rectangles_1() { // Create a hole in the larger circle let combined_rectangles = - path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_sub::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); println!("{:?}", combined_rectangles); assert!(combined_rectangles.len() != 1); @@ -112,7 +112,7 @@ fn subtract_from_self_rectangles_2() { // Create a hole in the larger circle let combined_rectangles = - path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + path_sub::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); println!("{:?}", combined_rectangles); assert!(combined_rectangles.len() != 1); @@ -126,7 +126,7 @@ fn subtract_from_self_circles() { let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); // Create a hole in the larger circle - let combined_circles = path_sub::<_, _, SimpleBezierPath>(&vec![circle2], &vec![circle1], 0.01); + let combined_circles = path_sub::<_, _, SimpleBezierPath>(&[circle2], &[circle1], 0.01); assert!(combined_circles.is_empty()); } @@ -148,7 +148,7 @@ fn cut_corners() { .build(); // Subtract them - let cut_corner = path_sub::<_, _, SimpleBezierPath>(&vec![rectangle1], &vec![rectangle2], 0.01); + let cut_corner = path_sub::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(cut_corner.len() == 1); diff --git a/tests/bezier/path/graph_path.rs b/tests/bezier/path/graph_path.rs index 8d8262f8..33553f83 100644 --- a/tests/bezier/path/graph_path.rs +++ b/tests/bezier/path/graph_path.rs @@ -956,7 +956,7 @@ fn set_simple_path_as_interior() { let mut rectangle1 = GraphPath::from_path(&rectangle1, ()); // Mark everything as an exterior path - let first_edge_ref = rectangle1.all_edges().nth(0).unwrap().into(); + let first_edge_ref = rectangle1.all_edges().next().unwrap().into(); rectangle1.set_edge_kind_connected(first_edge_ref, GraphPathEdgeKind::Interior); // All edges should be exterior @@ -990,7 +990,7 @@ fn set_collision_as_exterior() { let mut collided = rectangle1.collide(rectangle2, 0.01); // Mark everything as an exterior path - let first_edge_ref = collided.edges_for_point(0).nth(0).unwrap().into(); + let first_edge_ref = collided.edges_for_point(0).next().unwrap().into(); collided.set_edge_kind_connected(first_edge_ref, GraphPathEdgeKind::Exterior); // Edges 0 -> 1, 1 -> , -> 2, 2 -> 3 and 3 -> 0 should all be exterior @@ -1031,7 +1031,7 @@ fn get_path_from_exterior_lines() { let mut rectangle1 = GraphPath::from_path(&rectangle1, ()); // Mark everything as an exterior path - let first_edge = rectangle1.edges_for_point(0).nth(0).unwrap().into(); + let first_edge = rectangle1.edges_for_point(0).next().unwrap().into(); rectangle1.set_edge_kind_connected(first_edge, GraphPathEdgeKind::Exterior); // Turn back into a path @@ -1071,10 +1071,10 @@ fn get_path_from_exterior_lines_multiple_paths() { let mut rectangle1 = rectangle1.merge(rectangle2); // Mark everything as an exterior path - let first_edge = rectangle1.edges_for_point(0).nth(0).unwrap().into(); + let first_edge = rectangle1.edges_for_point(0).next().unwrap().into(); rectangle1.set_edge_kind_connected(first_edge, GraphPathEdgeKind::Exterior); - let first_edge = rectangle1.edges_for_point(4).nth(0).unwrap().into(); + let first_edge = rectangle1.edges_for_point(4).next().unwrap().into(); rectangle1.set_edge_kind_connected(first_edge, GraphPathEdgeKind::Exterior); // Turn back into a path @@ -1138,7 +1138,7 @@ fn collide_circles() { assert!(graph_path.edges_for_point(3).collect::>().len() == 1); // Point 1 should lead to the intersection point - let to_intersection = graph_path.edges_for_point(0).nth(0).unwrap(); + let to_intersection = graph_path.edges_for_point(0).next().unwrap(); let intersection_point = to_intersection.end_point_index(); assert!(intersection_point > 3); @@ -1167,9 +1167,7 @@ fn collide_circles() { // The following intersection point should have one point that leads back into our path let following_intersection = intersection_edges - .iter() - .filter(|edge| is_intersection(edge.end_point_index())) - .nth(0) + .iter().find(|edge| is_intersection(edge.end_point_index())) .unwrap(); let second_intersection_edges = graph_path .edges_for_point(following_intersection.end_point_index()) @@ -1634,7 +1632,7 @@ fn heal_one_line_gap() { let mut graph_path = GraphPath::from_path(&path, ()); let edges = (0..4) .into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).nth(0).unwrap().into()) + .map(|point_idx| graph_path.edges_for_point(point_idx).next().unwrap().into()) .collect::>(); graph_path.set_edge_kind(edges[0], GraphPathEdgeKind::Exterior); @@ -1660,7 +1658,7 @@ fn heal_two_line_gap() { let mut graph_path = GraphPath::from_path(&path, ()); let edges = (0..4) .into_iter() - .map(|point_idx| graph_path.edges_for_point(point_idx).nth(0).unwrap().into()) + .map(|point_idx| graph_path.edges_for_point(point_idx).next().unwrap().into()) .collect::>(); graph_path.set_edge_kind(edges[0], GraphPathEdgeKind::Exterior); From c93c8f2867bf6aa2cb30bb1d8c0d813a36584a98 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 13:32:11 +0100 Subject: [PATCH 30/31] fix clippy::use_self --- src/arc/circle.rs | 4 +- src/bezier/characteristics.rs | 8 +-- src/bezier/curve.rs | 2 +- src/bezier/intersection/fat_line.rs | 14 ++--- src/bezier/path/algorithms/fill_concave.rs | 2 +- src/bezier/path/algorithms/fill_convex.rs | 4 +- src/bezier/path/algorithms/fill_settings.rs | 10 ++-- src/bezier/path/arithmetic/ray_cast.rs | 6 +- src/bezier/path/graph_path/edge.rs | 4 +- src/bezier/path/graph_path/edge_ref.rs | 6 +- src/bezier/path/graph_path/mod.rs | 28 +++++----- src/bezier/path/path_builder.rs | 4 +- src/bezier/tangent.rs | 4 +- src/geo/bounding_box.rs | 2 +- src/geo/coordinate.rs | 62 ++++++++++----------- 15 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/arc/circle.rs b/src/arc/circle.rs index 15bbb669..890b42ce 100644 --- a/src/arc/circle.rs +++ b/src/arc/circle.rs @@ -37,8 +37,8 @@ impl Circle { /// /// Creates a new circle with a center and a radius /// - pub fn new(center: Coord, radius: f64) -> Circle { - Circle { center, radius } + pub fn new(center: Coord, radius: f64) -> Self { + Self { center, radius } } /// diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index 2d2314aa..f04cd1f1 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -302,11 +302,11 @@ fn find_inflection_points(b4: (f64, f64)) -> InflectionPoints { impl From for CurveFeatures { #[inline] - fn from(ip: InflectionPoints) -> CurveFeatures { + fn from(ip: InflectionPoints) -> Self { match ip { - InflectionPoints::Zero => CurveFeatures::Arch, - InflectionPoints::One(t) => CurveFeatures::SingleInflectionPoint(t), - InflectionPoints::Two(t1, t2) => CurveFeatures::DoubleInflectionPoint(t1, t2), + InflectionPoints::Zero => Self::Arch, + InflectionPoints::One(t) => Self::SingleInflectionPoint(t), + InflectionPoints::Two(t1, t2) => Self::DoubleInflectionPoint(t1, t2), } } } diff --git a/src/bezier/curve.rs b/src/bezier/curve.rs index 8893a629..d4945b7e 100644 --- a/src/bezier/curve.rs +++ b/src/bezier/curve.rs @@ -219,7 +219,7 @@ impl BezierCurveFactory for Curve { (control_point1, control_point2): (Coord, Coord), end: Coord, ) -> Self { - Curve { + Self { start_point: start, control_points: (control_point1, control_point2), end_point: end, diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index 6b6d5991..70f84712 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -267,7 +267,7 @@ impl FatLine { /// /// Creates a new fatline from a central line and two points representing its outer edges /// - fn from_line_and_points(line: L, p1: L::Point, p2: L::Point) -> FatLine + fn from_line_and_points(line: L, p1: L::Point, p2: L::Point) -> Self where L::Point: Coordinate + Coordinate2D, { @@ -293,7 +293,7 @@ impl FatLine { ) }; - FatLine { + Self { d_min, d_max, coeff: (a, b, c), @@ -303,7 +303,7 @@ impl FatLine { /// /// Creates a new fatline from a curve /// - pub fn from_curve(curve: &C) -> FatLine + pub fn from_curve(curve: &C) -> Self where C::Point: Coordinate + Coordinate2D, { @@ -324,7 +324,7 @@ impl FatLine { /// /// Creates a perpendicular fatline from a curve /// - pub fn from_curve_perpendicular(curve: &C) -> FatLine + pub fn from_curve_perpendicular(curve: &C) -> Self where C::Point: Coordinate + Coordinate2D, { @@ -365,7 +365,7 @@ impl FatLine { let d_min = d1.min(d2).min(d3).min(d4); let d_max = d1.max(d2).max(d3).max(d4); - FatLine { + Self { coeff: (a, b, c), d_min, d_max, @@ -381,13 +381,13 @@ mod test { /// /// Creates a new fat line /// - pub fn new(line: L, d_min: f64, d_max: f64) -> FatLine + pub fn new(line: L, d_min: f64, d_max: f64) -> Self where L::Point: Coordinate2D, { let (a, b, c) = line_coefficients_2d(&line); - FatLine { + Self { d_min, d_max, coeff: (a, b, c), diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index 66a372a1..bc441820 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -21,7 +21,7 @@ enum ConcaveItem { } impl From> for Option { - fn from(ci: ConcaveItem) -> Option { + fn from(ci: ConcaveItem) -> Self { match ci { ConcaveItem::Edge(item) => Some(item), ConcaveItem::SelfIntersection(_) => None, diff --git a/src/bezier/path/algorithms/fill_convex.rs b/src/bezier/path/algorithms/fill_convex.rs index 53313801..52fd9f24 100644 --- a/src/bezier/path/algorithms/fill_convex.rs +++ b/src/bezier/path/algorithms/fill_convex.rs @@ -28,8 +28,8 @@ where /// /// Creates a new collision at a specific point /// - pub fn new(position: Coord, what: Item) -> RayCollision { - RayCollision { position, what } + pub fn new(position: Coord, what: Item) -> Self { + Self { position, what } } } diff --git a/src/bezier/path/algorithms/fill_settings.rs b/src/bezier/path/algorithms/fill_settings.rs index 563a895e..b9299d75 100644 --- a/src/bezier/path/algorithms/fill_settings.rs +++ b/src/bezier/path/algorithms/fill_settings.rs @@ -23,7 +23,7 @@ impl FillSettings { /// The step size defines how accurately the flood-filled region reflects the area defined by the /// ray-casting function. Higher steps will result in a faster but less accurate result. /// - pub fn with_step(self, new_step: f64) -> FillSettings { + pub fn with_step(self, new_step: f64) -> Self { let mut new_options = self; new_options.step = new_step; new_options @@ -37,7 +37,7 @@ impl FillSettings { /// fit, which may produce a simpler (and smoother) resulting path but which will not necessarily /// fit the points as well. /// - pub fn with_fit_error(self, new_fit_error: f64) -> FillSettings { + pub fn with_fit_error(self, new_fit_error: f64) -> Self { let mut new_options = self; new_options.fit_error = new_fit_error; new_options @@ -48,7 +48,7 @@ impl FillSettings { /// /// This makes it possible to fill regions that are not perfectly enclosed /// - pub fn with_min_gap(self, new_min_gap: Option) -> FillSettings { + pub fn with_min_gap(self, new_min_gap: Option) -> Self { let mut new_options = self; new_options.min_gap = new_min_gap; new_options @@ -59,8 +59,8 @@ impl Default for FillSettings { /// /// Creates the default set of fill options /// - fn default() -> FillSettings { - FillSettings { + fn default() -> Self { + Self { step: 2.0, fit_error: 0.5, min_gap: Some(5.0), diff --git a/src/bezier/path/arithmetic/ray_cast.rs b/src/bezier/path/arithmetic/ray_cast.rs index 98f48f78..47f4bfa9 100644 --- a/src/bezier/path/arithmetic/ray_cast.rs +++ b/src/bezier/path/arithmetic/ray_cast.rs @@ -22,11 +22,11 @@ where P::Point: Coordinate2D, { #[inline] - fn from(path: &'a P) -> PathDirection { + fn from(path: &'a P) -> Self { if path.is_clockwise() { - PathDirection::Clockwise + Self::Clockwise } else { - PathDirection::Anticlockwise + Self::Anticlockwise } } } diff --git a/src/bezier/path/graph_path/edge.rs b/src/bezier/path/graph_path/edge.rs index 89d0a238..0d17b9a8 100644 --- a/src/bezier/path/graph_path/edge.rs +++ b/src/bezier/path/graph_path/edge.rs @@ -17,8 +17,8 @@ impl GraphPathEdge { end_idx: usize, label: Label, following_edge_idx: usize, - ) -> GraphPathEdge { - GraphPathEdge { + ) -> Self { + Self { label, kind, cp1, diff --git a/src/bezier/path/graph_path/edge_ref.rs b/src/bezier/path/graph_path/edge_ref.rs index a835d2a1..f530581e 100644 --- a/src/bezier/path/graph_path/edge_ref.rs +++ b/src/bezier/path/graph_path/edge_ref.rs @@ -5,7 +5,7 @@ impl GraphEdgeRef { /// /// Creates a reversed version of this edge ref /// - pub fn reversed(mut self) -> GraphEdgeRef { + pub fn reversed(mut self) -> Self { self.reverse = !self.reverse; self } @@ -17,7 +17,7 @@ impl GraphEdgeRef { impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> From> for GraphEdgeRef { - fn from(edge: GraphEdge<'a, Point, Label>) -> GraphEdgeRef { + fn from(edge: GraphEdge<'a, Point, Label>) -> Self { edge.edge } } @@ -28,7 +28,7 @@ impl<'a, Point: 'a + Coordinate, Label: 'a + Copy> From From<&'b GraphEdge<'a, Point, Label>> for GraphEdgeRef { - fn from(edge: &'b GraphEdge<'a, Point, Label>) -> GraphEdgeRef { + fn from(edge: &'b GraphEdge<'a, Point, Label>) -> Self { edge.edge } } diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index ef97fcfc..e798217b 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -111,8 +111,8 @@ impl GraphPathPoint { position: Point, forward_edges: SmallVec<[GraphPathEdge; 2]>, connected_from: SmallVec<[usize; 2]>, - ) -> GraphPathPoint { - GraphPathPoint { + ) -> Self { + Self { position, forward_edges, connected_from, @@ -154,8 +154,8 @@ impl GraphPath { /// /// Creates a new graph path with no points /// - pub fn new() -> GraphPath { - GraphPath { + pub fn new() -> Self { + Self { points: vec![], next_path_index: 0, } @@ -167,7 +167,7 @@ impl GraphPath { pub fn from_path>( path: &P, label: Label, - ) -> GraphPath { + ) -> Self { // All edges are exterior for a single path let mut points = vec![]; @@ -242,7 +242,7 @@ impl GraphPath { } // Create the graph path from the points - let mut path = GraphPath { + let mut path = Self { points, next_path_index: 1, }; @@ -259,13 +259,13 @@ impl GraphPath { PathIter: IntoIterator, >( paths: PathIter, - ) -> GraphPath { + ) -> Self { // Create an empty path - let mut merged_path = GraphPath::new(); + let mut merged_path = Self::new(); // Merge each path in turn for (path, label) in paths { - let path = GraphPath::from_path(path, label); + let path = Self::from_path(path, label); merged_path = merged_path.merge(path); } @@ -417,7 +417,7 @@ impl GraphPath { /// /// This adds the edges in the new path to this path without considering if they are internal or external /// - pub fn merge(self, merge_path: GraphPath) -> GraphPath { + pub fn merge(self, merge_path: Self) -> Self { // Copy the points from this graph let mut new_points = self.points; let next_path_idx = self.next_path_index; @@ -439,7 +439,7 @@ impl GraphPath { })); // Combined path - GraphPath { + Self { points: new_points, next_path_index: next_path_idx + merge_path.next_path_index, } @@ -619,7 +619,7 @@ impl GraphPath { /// pub fn collide_or_merge( mut self, - collide_path: GraphPath, + collide_path: Self, accuracy: f64, ) -> CollidedGraphPath { // Generate a merged path with all of the edges @@ -651,9 +651,9 @@ impl GraphPath { /// pub fn collide( mut self, - collide_path: GraphPath, + collide_path: Self, accuracy: f64, - ) -> GraphPath { + ) -> Self { // Generate a merged path with all of the edges let collision_offset = self.points.len(); self = self.merge(collide_path); diff --git a/src/bezier/path/path_builder.rs b/src/bezier/path/path_builder.rs index a47c3c80..db82ea2f 100644 --- a/src/bezier/path/path_builder.rs +++ b/src/bezier/path/path_builder.rs @@ -15,8 +15,8 @@ impl BezierPathBuilder

{ /// /// Creates a new bezier path builder with the specified start point /// - pub fn start(start: P::Point) -> BezierPathBuilder

{ - BezierPathBuilder { + pub fn start(start: P::Point) -> Self { + Self { start_point: start, points: vec![], } diff --git a/src/bezier/tangent.rs b/src/bezier/tangent.rs index 7f72c628..e66432cb 100644 --- a/src/bezier/tangent.rs +++ b/src/bezier/tangent.rs @@ -14,10 +14,10 @@ impl<'a, Curve: BezierCurve> From<&'a Curve> for Tangent { /// /// Creates a structure that can computes the tangents for a bezier curve /// - fn from(curve: &'a Curve) -> Tangent { + fn from(curve: &'a Curve) -> Self { let control_points = curve.control_points(); - Tangent { + Self { derivative: derivative4( curve.start_point(), control_points.0, diff --git a/src/geo/bounding_box.rs b/src/geo/bounding_box.rs index 091f65b7..03db15db 100644 --- a/src/geo/bounding_box.rs +++ b/src/geo/bounding_box.rs @@ -135,7 +135,7 @@ impl Geo for Bounds { impl BoundingBox for Bounds { #[inline] fn from_min_max(min: Self::Point, max: Self::Point) -> Self { - Bounds(min, max) + Self(min, max) } #[inline] diff --git a/src/geo/coordinate.rs b/src/geo/coordinate.rs index 5c6f0fc1..1ee193e5 100644 --- a/src/geo/coordinate.rs +++ b/src/geo/coordinate.rs @@ -206,12 +206,12 @@ pub trait Coordinate3D { } impl Coordinate for f64 { - fn from_components(components: &[f64]) -> f64 { + fn from_components(components: &[f64]) -> Self { components[0] } #[inline] - fn origin() -> f64 { + fn origin() -> Self { 0.0 } #[inline] @@ -224,7 +224,7 @@ impl Coordinate for f64 { } #[inline] - fn from_biggest_components(p1: f64, p2: f64) -> f64 { + fn from_biggest_components(p1: Self, p2: Self) -> Self { if p1 > p2 { p1 } else { @@ -233,7 +233,7 @@ impl Coordinate for f64 { } #[inline] - fn from_smallest_components(p1: f64, p2: f64) -> f64 { + fn from_smallest_components(p1: Self, p2: Self) -> Self { if p1 < p2 { p1 } else { @@ -242,11 +242,11 @@ impl Coordinate for f64 { } #[inline] - fn distance_to(&self, target: &f64) -> f64 { - f64::abs(self - target) + fn distance_to(&self, target: &Self) -> f64 { + Self::abs(self - target) } - fn dot(&self, target: &f64) -> f64 { + fn dot(&self, target: &Self) -> f64 { self * target } } @@ -273,36 +273,36 @@ impl Coordinate2D for Coord2 { } } -impl Add for Coord2 { - type Output = Coord2; +impl Add for Coord2 { + type Output = Self; #[inline] - fn add(self, rhs: Coord2) -> Coord2 { - Coord2(self.0 + rhs.0, self.1 + rhs.1) + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0, self.1 + rhs.1) } } -impl Sub for Coord2 { - type Output = Coord2; +impl Sub for Coord2 { + type Output = Self; #[inline] - fn sub(self, rhs: Coord2) -> Coord2 { - Coord2(self.0 - rhs.0, self.1 - rhs.1) + fn sub(self, rhs: Self) -> Self { + Self(self.0 - rhs.0, self.1 - rhs.1) } } impl Mul for Coord2 { - type Output = Coord2; + type Output = Self; #[inline] - fn mul(self, rhs: f64) -> Coord2 { - Coord2(self.0 * rhs, self.1 * rhs) + fn mul(self, rhs: f64) -> Self { + Self(self.0 * rhs, self.1 * rhs) } } impl From<(f64, f64)> for Coord2 { - fn from((x, y): (f64, f64)) -> Coord2 { - Coord2(x, y) + fn from((x, y): (f64, f64)) -> Self { + Self(x, y) } } @@ -313,8 +313,8 @@ impl From for (f64, f64) { } impl From<(f32, f32)> for Coord2 { - fn from((x, y): (f32, f32)) -> Coord2 { - Coord2(x as _, y as _) + fn from((x, y): (f32, f32)) -> Self { + Self(x as _, y as _) } } @@ -326,13 +326,13 @@ impl From for (f32, f32) { impl Coordinate for Coord2 { #[inline] - fn from_components(components: &[f64]) -> Coord2 { - Coord2(components[0], components[1]) + fn from_components(components: &[f64]) -> Self { + Self(components[0], components[1]) } #[inline] - fn origin() -> Coord2 { - Coord2(0.0, 0.0) + fn origin() -> Self { + Self(0.0, 0.0) } #[inline] @@ -349,22 +349,22 @@ impl Coordinate for Coord2 { } } - fn from_biggest_components(p1: Coord2, p2: Coord2) -> Coord2 { - Coord2( + fn from_biggest_components(p1: Self, p2: Self) -> Self { + Self( f64::from_biggest_components(p1.0, p2.0), f64::from_biggest_components(p1.1, p2.1), ) } - fn from_smallest_components(p1: Coord2, p2: Coord2) -> Coord2 { - Coord2( + fn from_smallest_components(p1: Self, p2: Self) -> Self { + Self( f64::from_smallest_components(p1.0, p2.0), f64::from_smallest_components(p1.1, p2.1), ) } #[inline] - fn distance_to(&self, target: &Coord2) -> f64 { + fn distance_to(&self, target: &Self) -> f64 { let dist_x = target.0 - self.0; let dist_y = target.1 - self.1; From 8807b446271f0d2212d3f27d86741beeab5abebb Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 7 Feb 2022 13:42:17 +0100 Subject: [PATCH 31/31] replace asterisk imports with specific ones --- benches/sweep.rs | 7 +- src/arc/circle.rs | 9 ++- src/bezier/basis.rs | 2 +- src/bezier/bounds.rs | 4 +- src/bezier/characteristics.rs | 11 +-- src/bezier/curve.rs | 24 ++++--- src/bezier/deform.rs | 4 +- src/bezier/derivative.rs | 2 +- src/bezier/distort.rs | 12 ++-- src/bezier/fit.rs | 6 +- src/bezier/intersection/curve_curve_clip.rs | 12 ++-- src/bezier/intersection/curve_line.rs | 12 ++-- src/bezier/intersection/fat_line.rs | 10 +-- src/bezier/intersection/self_intersection.rs | 10 +-- src/bezier/length.rs | 6 +- src/bezier/normal.rs | 8 +-- src/bezier/offset.rs | 8 +-- src/bezier/offset_lms.rs | 12 ++-- src/bezier/offset_scaling.rs | 16 +++-- src/bezier/overlaps.rs | 8 +-- src/bezier/path/algorithms/fill_concave.rs | 12 ++-- src/bezier/path/algorithms/fill_convex.rs | 16 ++--- src/bezier/path/arithmetic/add.rs | 8 +-- src/bezier/path/arithmetic/chain.rs | 12 ++-- src/bezier/path/arithmetic/chain_add.rs | 8 +-- src/bezier/path/arithmetic/cut.rs | 8 +-- src/bezier/path/arithmetic/full_intersect.rs | 8 +-- src/bezier/path/arithmetic/intersect.rs | 8 +-- src/bezier/path/arithmetic/ray_cast.rs | 18 ++--- src/bezier/path/arithmetic/sub.rs | 8 +-- src/bezier/path/bounds.rs | 8 +-- src/bezier/path/graph_path/edge.rs | 8 +-- src/bezier/path/graph_path/edge_ref.rs | 2 +- src/bezier/path/graph_path/mod.rs | 27 +++----- src/bezier/path/graph_path/path_collision.rs | 14 ++-- src/bezier/path/graph_path/ray_collision.rs | 8 +-- src/bezier/path/graph_path/test.rs | 1 + src/bezier/path/intersection.rs | 12 ++-- src/bezier/path/is_clockwise.rs | 6 +- src/bezier/path/path.rs | 10 +-- src/bezier/path/path_builder.rs | 2 +- src/bezier/path/point.rs | 18 ++--- src/bezier/path/ray.rs | 29 ++++---- src/bezier/path/to_curves.rs | 6 +- src/bezier/search.rs | 6 +- src/bezier/section.rs | 13 ++-- src/bezier/solve.rs | 8 +-- src/bezier/subdivide.rs | 4 +- src/bezier/tangent.rs | 6 +- src/bezier/walk.rs | 10 +-- src/debug/graph_path_debug.rs | 26 +++++-- src/debug/path_to_string.rs | 6 +- src/geo/bounding_box.rs | 6 +- src/geo/coordinate.rs | 4 +- src/geo/coordinate_ext.rs | 2 +- src/geo/geo.rs | 2 +- src/geo/has_bounds.rs | 4 +- src/geo/sweep.rs | 4 +- src/line/coefficients.rs | 4 +- src/line/intersection.rs | 4 +- src/line/line.rs | 4 +- src/line/to_curve.rs | 4 +- tests/bezier/algorithms/fill_concave.rs | 8 ++- tests/bezier/algorithms/fill_convex.rs | 10 ++- tests/bezier/basis.rs | 2 +- tests/bezier/characteristics.rs | 7 +- tests/bezier/curve_intersection_clip.rs | 4 +- tests/bezier/deform.rs | 4 +- tests/bezier/distort.rs | 5 +- tests/bezier/intersection.rs | 8 +-- tests/bezier/length.rs | 4 +- tests/bezier/mod.rs | 2 +- tests/bezier/normal.rs | 2 +- tests/bezier/offset.rs | 13 ++-- tests/bezier/overlaps.rs | 2 +- tests/bezier/path/arithmetic_add.rs | 69 +++++++------------ tests/bezier/path/arithmetic_chain_add.rs | 13 ++-- .../path/arithmetic_complicated_paths.rs | 20 +++--- tests/bezier/path/arithmetic_cut.rs | 4 +- tests/bezier/path/arithmetic_intersect.rs | 41 +++++------ tests/bezier/path/arithmetic_sub.rs | 11 +-- tests/bezier/path/bounds.rs | 6 +- tests/bezier/path/graph_path.rs | 12 ++-- tests/bezier/path/intersection.rs | 8 ++- tests/bezier/path/is_clockwise.rs | 4 +- tests/bezier/path/path.rs | 4 +- tests/bezier/path/point.rs | 6 +- tests/bezier/path/rays.rs | 8 ++- tests/bezier/path/svg.rs | 4 +- tests/bezier/path/to_curves.rs | 4 +- tests/bezier/section.rs | 2 +- tests/bezier/self_intersection.rs | 4 +- tests/bezier/solve.rs | 4 +- tests/bezier/subdivide.rs | 2 +- tests/bezier/tangent.rs | 2 +- tests/bezier/walk.rs | 5 +- tests/bounds.rs | 2 +- tests/coordinates.rs | 2 +- tests/line/coefficients.rs | 4 +- tests/line/intersection.rs | 5 +- tests/line/to_curve.rs | 4 +- tests/sweep.rs | 4 +- 102 files changed, 468 insertions(+), 414 deletions(-) diff --git a/benches/sweep.rs b/benches/sweep.rs index 0830fa3f..33f720f4 100644 --- a/benches/sweep.rs +++ b/benches/sweep.rs @@ -1,7 +1,9 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use flo_curves::bezier::path::*; -use flo_curves::geo::*; +use flo_curves::bezier::path::{BezierPathBuilder, BezierPathFactory, GraphPath, SimpleBezierPath}; +use flo_curves::geo::{ + sweep_self, BoundingBox, Bounds, Coord2, Coordinate, Coordinate2D, Coordinate3D, +}; use rand::prelude::*; use std::cmp::Ordering; @@ -80,7 +82,6 @@ fn create_graph_path(rng: &mut StdRng, n: usize) -> GraphPath { } let path = path_builder.build(); - GraphPath::from_path(&path, ()) } diff --git a/src/arc/circle.rs b/src/arc/circle.rs index 890b42ce..b41e49ed 100644 --- a/src/arc/circle.rs +++ b/src/arc/circle.rs @@ -1,5 +1,5 @@ -use super::super::bezier::path::*; -use super::super::bezier::*; +use super::super::bezier::path::BezierPathFactory; +use super::super::bezier::{BezierCurve, BezierCurveFactory, Coordinate, Coordinate2D, Curve}; use std::f64; @@ -152,7 +152,10 @@ impl<'a, Coord: Coordinate2D + Coordinate> CircularArc<'a, Coord> { #[cfg(test)] mod test { use super::*; - use std::f64; + use crate::{ + bezier::path::{path_to_curves, SimpleBezierPath}, + Coord2, + }; #[test] fn can_convert_unit_arc() { diff --git a/src/bezier/basis.rs b/src/bezier/basis.rs index 378de1e3..aa874f2c 100644 --- a/src/bezier/basis.rs +++ b/src/bezier/basis.rs @@ -1,4 +1,4 @@ -use super::super::geo::*; +use super::super::geo::Coordinate; /// /// Computes the bezier coefficients (A, B, C, D) for a bezier curve diff --git a/src/bezier/bounds.rs b/src/bezier/bounds.rs index 9229d91a..ed867322 100644 --- a/src/bezier/bounds.rs +++ b/src/bezier/bounds.rs @@ -1,5 +1,5 @@ -use super::super::geo::*; -use super::basis::*; +use super::super::geo::{BoundingBox, Coordinate}; +use super::basis::de_casteljau4; /// /// Finds the t values of the extremities of a curve (these are the points at which diff --git a/src/bezier/characteristics.rs b/src/bezier/characteristics.rs index f04cd1f1..8047e258 100644 --- a/src/bezier/characteristics.rs +++ b/src/bezier/characteristics.rs @@ -1,8 +1,8 @@ -use super::super::consts::*; -use super::super::geo::*; -use super::super::line::*; -use super::curve::*; -use super::intersection::*; +use super::super::consts::SMALL_DISTANCE; +use super::super::geo::{Coordinate, Coordinate2D}; +use super::super::line::line_coefficients_2d; +use super::curve::{BezierCurve, BezierCurveFactory, Curve}; +use super::intersection::find_self_intersection_point; use std::f64; @@ -456,6 +456,7 @@ where #[cfg(test)] mod test { use super::*; + use crate::Coord2; #[test] fn canonical_curve_coeffs_are_valid_1() { diff --git a/src/bezier/curve.rs b/src/bezier/curve.rs index d4945b7e..a1add4d4 100644 --- a/src/bezier/curve.rs +++ b/src/bezier/curve.rs @@ -1,14 +1,16 @@ -use super::basis::*; -use super::bounds::*; -use super::characteristics::*; -use super::fit::*; -use super::length::*; -use super::search::*; -use super::section::*; -use super::solve::*; -use super::subdivide::*; - -use crate::geo::*; +use super::basis::basis; +use super::bounds::{bounding_box4, find_extremities}; +use super::characteristics::{ + characterize_cubic_bezier, features_for_cubic_bezier, CurveCategory, CurveFeatures, +}; +use super::fit::fit_curve; +use super::length::curve_length; +use super::search::search_bounds4; +use super::section::CurveSection; +use super::solve::solve_curve_for_t; +use super::subdivide::subdivide4; + +use crate::geo::{BoundingBox, Coordinate, Coordinate2D, Geo, HasBoundingBox}; /// /// Trait implemented by bezier curves that can create new versions of themselves diff --git a/src/bezier/deform.rs b/src/bezier/deform.rs index 77f5cb2c..269315fd 100644 --- a/src/bezier/deform.rs +++ b/src/bezier/deform.rs @@ -1,5 +1,5 @@ -use super::super::geo::*; -use super::curve::*; +use super::super::geo::Coordinate; +use super::curve::{BezierCurve, BezierCurveFactory}; /// /// Moves the point at 't' on the curve by the offset vector diff --git a/src/bezier/derivative.rs b/src/bezier/derivative.rs index 9949dbb5..0ae86d88 100644 --- a/src/bezier/derivative.rs +++ b/src/bezier/derivative.rs @@ -1,4 +1,4 @@ -use super::super::geo::*; +use super::super::geo::Coordinate; /// /// Returns the 1st derivative of a cubic bezier curve diff --git a/src/bezier/distort.rs b/src/bezier/distort.rs index 66f519b6..b97244a5 100644 --- a/src/bezier/distort.rs +++ b/src/bezier/distort.rs @@ -1,9 +1,9 @@ -use super::curve::*; -use super::fit::*; -use super::normal::*; -use super::path::*; -use super::walk::*; -use crate::geo::*; +use super::curve::{BezierCurve, BezierCurveFactory, Curve}; +use super::fit::fit_curve; +use super::normal::Normalize; +use super::path::{BezierPath, BezierPathFactory}; +use super::walk::walk_curve_evenly; +use crate::geo::Coordinate2D; use std::iter; diff --git a/src/bezier/fit.rs b/src/bezier/fit.rs index 479a390b..6a0c2485 100644 --- a/src/bezier/fit.rs +++ b/src/bezier/fit.rs @@ -1,6 +1,6 @@ -use super::basis::*; -use super::curve::*; -use crate::geo::*; +use super::basis::{de_casteljau2, de_casteljau3}; +use super::curve::{BezierCurve, BezierCurveFactory}; +use crate::geo::Coordinate; /// Maximum number of iterations to perform when trying to improve the curve fit const MAX_ITERATIONS: usize = 4; diff --git a/src/bezier/intersection/curve_curve_clip.rs b/src/bezier/intersection/curve_curve_clip.rs index ddc93d34..069e0c1a 100644 --- a/src/bezier/intersection/curve_curve_clip.rs +++ b/src/bezier/intersection/curve_curve_clip.rs @@ -1,10 +1,10 @@ -use super::curve_line::*; -use super::fat_line::*; -use crate::bezier::solve::*; -use crate::bezier::*; -use crate::geo::*; +use super::curve_line::curve_intersects_ray; +use super::fat_line::FatLine; +use crate::bezier::solve::{solve_curve_for_t, CLOSE_ENOUGH}; +use crate::bezier::{overlapping_region, BezierCurve, CurveSection}; +use crate::geo::{BoundingBox, Bounds, Coordinate, Coordinate2D}; -use smallvec::*; +use smallvec::{smallvec, SmallVec}; /// /// Determines the length of a curve's hull as a sum of squares diff --git a/src/bezier/intersection/curve_line.rs b/src/bezier/intersection/curve_line.rs index b610f962..6cb9b6b4 100644 --- a/src/bezier/intersection/curve_line.rs +++ b/src/bezier/intersection/curve_line.rs @@ -1,11 +1,11 @@ -use super::super::basis::*; -use super::super::curve::*; -use crate::consts::*; -use crate::geo::*; -use crate::line::*; +use super::super::basis::{bezier_coefficients, de_casteljau4}; +use super::super::curve::BezierCurve; +use crate::consts::SMALL_DISTANCE; +use crate::geo::Coordinate2D; +use crate::line::Line; use roots::{find_roots_cubic, find_roots_quadratic, Roots}; -use smallvec::*; +use smallvec::{smallvec, SmallVec}; /// /// Solves the roots for a set of cubic coefficients diff --git a/src/bezier/intersection/fat_line.rs b/src/bezier/intersection/fat_line.rs index 70f84712..c0d14723 100644 --- a/src/bezier/intersection/fat_line.rs +++ b/src/bezier/intersection/fat_line.rs @@ -1,7 +1,7 @@ -use super::super::super::consts::*; -use super::super::super::geo::*; -use super::super::super::line::*; -use super::super::curve::*; +use super::super::super::consts::SMALL_DISTANCE; +use super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::super::line::{line_coefficients_2d, Line}; +use super::super::curve::{BezierCurve, BezierCurveFactory, Curve}; use std::f64; @@ -375,6 +375,8 @@ impl FatLine { #[cfg(test)] mod test { + use crate::{line::line_to_bezier, Coord2}; + use super::*; impl FatLine { diff --git a/src/bezier/intersection/self_intersection.rs b/src/bezier/intersection/self_intersection.rs index cb3eb5c1..cec21539 100644 --- a/src/bezier/intersection/self_intersection.rs +++ b/src/bezier/intersection/self_intersection.rs @@ -1,8 +1,8 @@ -use super::super::super::geo::*; -use super::super::characteristics::*; -use super::super::curve::*; -use super::super::section::*; -use super::curve_curve_clip::*; +use super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::characteristics::CurveCategory; +use super::super::curve::{BezierCurve, BezierCurve2D}; +use super::super::section::CurveSection; +use super::curve_curve_clip::curve_intersects_curve_clip; /// /// If a cubic curve contains a loop, finds the t values where the curve self-intersects diff --git a/src/bezier/length.rs b/src/bezier/length.rs index 52d31a2b..caa3cacf 100644 --- a/src/bezier/length.rs +++ b/src/bezier/length.rs @@ -1,6 +1,6 @@ -use super::curve::*; -use super::section::*; -use crate::geo::*; +use super::curve::BezierCurve; +use super::section::CurveSection; +use crate::geo::Coordinate; /// /// Returns the length of the control polygon for a bezier curve diff --git a/src/bezier/normal.rs b/src/bezier/normal.rs index 57b602cf..7bfd2fb0 100644 --- a/src/bezier/normal.rs +++ b/src/bezier/normal.rs @@ -1,7 +1,7 @@ -use super::super::geo::*; -use super::basis::*; -use super::curve::*; -use super::derivative::*; +use super::super::geo::{Coordinate, Coordinate2D}; +use super::basis::{de_casteljau3, de_casteljau4}; +use super::curve::BezierCurve; +use super::derivative::derivative4; // TODO: normalize should be a trait associated with coordinate rather than bezier curves (move outwards) diff --git a/src/bezier/offset.rs b/src/bezier/offset.rs index c97d2ed3..1ea9d560 100644 --- a/src/bezier/offset.rs +++ b/src/bezier/offset.rs @@ -1,7 +1,7 @@ -use super::super::geo::*; -use super::curve::*; -use super::normal::*; -use super::offset_scaling::*; +use super::super::geo::Coordinate2D; +use super::curve::BezierCurveFactory; +use super::normal::{NormalCurve, Normalize}; +use super::offset_scaling::offset_scaling; /// /// Computes a series of curves that approximate an offset curve from the specified origin curve. diff --git a/src/bezier/offset_lms.rs b/src/bezier/offset_lms.rs index fd9af198..fa3c8693 100644 --- a/src/bezier/offset_lms.rs +++ b/src/bezier/offset_lms.rs @@ -1,10 +1,10 @@ -use super::characteristics::*; -use super::curve::*; -use super::fit::*; -use super::normal::*; -use crate::geo::*; +use super::characteristics::{features_for_curve, CurveFeatures}; +use super::curve::BezierCurveFactory; +use super::fit::fit_curve_cubic; +use super::normal::{NormalCurve, Normalize}; +use crate::geo::{Coordinate, Coordinate2D}; -use smallvec::*; +use smallvec::{smallvec, SmallVec}; use std::iter; /// diff --git a/src/bezier/offset_scaling.rs b/src/bezier/offset_scaling.rs index bef2bddc..7068fdf8 100644 --- a/src/bezier/offset_scaling.rs +++ b/src/bezier/offset_scaling.rs @@ -1,12 +1,14 @@ -use super::characteristics::*; -use super::curve::*; -use super::normal::*; +use super::characteristics::{ + characterize_curve, features_for_curve, CurveCategory, CurveFeatures, +}; +use super::curve::{BezierCurve, BezierCurveFactory}; +use super::normal::{NormalCurve, Normalize}; use crate::bezier::CurveSection; -use crate::geo::*; -use crate::line::*; +use crate::geo::{Coordinate, Coordinate2D}; +use crate::line::ray_intersects_ray; -use itertools::*; -use smallvec::*; +use itertools::Itertools; +use smallvec::{smallvec, SmallVec}; // This is loosely based on the algorithm described at: https://pomax.github.io/bezierinfo/#offsetting, // with numerous changes to allow for variable-width offsets and consistent behaviour (in particular, diff --git a/src/bezier/overlaps.rs b/src/bezier/overlaps.rs index 13b48d87..243be870 100644 --- a/src/bezier/overlaps.rs +++ b/src/bezier/overlaps.rs @@ -1,7 +1,7 @@ -use super::super::consts::*; -use super::super::geo::*; -use super::super::line::*; -use super::curve::*; +use super::super::consts::{SMALL_DISTANCE, SMALL_T_DISTANCE}; +use super::super::geo::{Coordinate, Coordinate2D}; +use super::super::line::Line2D; +use super::curve::BezierCurve; /// /// If `curve2` overlaps `curve1`, returns two sets of `t` values (those for `curve1` and those for `curve2`) diff --git a/src/bezier/path/algorithms/fill_concave.rs b/src/bezier/path/algorithms/fill_concave.rs index bc441820..c07e73f5 100644 --- a/src/bezier/path/algorithms/fill_concave.rs +++ b/src/bezier/path/algorithms/fill_concave.rs @@ -1,10 +1,10 @@ -use super::fill_convex::*; -use super::fill_settings::*; +use super::fill_convex::{trace_outline_convex, trace_outline_convex_partial, RayCollision}; +use super::fill_settings::FillSettings; -use crate::bezier::path::*; -use crate::bezier::*; -use crate::geo::*; -use crate::line::*; +use crate::bezier::path::{path_remove_interior_points, BezierPathFactory}; +use crate::bezier::{fit_curve, BezierCurve, Curve}; +use crate::geo::{Coordinate, Coordinate2D}; +use crate::line::{line_intersects_ray, Line}; use std::f64; diff --git a/src/bezier/path/algorithms/fill_convex.rs b/src/bezier/path/algorithms/fill_convex.rs index 52fd9f24..7f0de194 100644 --- a/src/bezier/path/algorithms/fill_convex.rs +++ b/src/bezier/path/algorithms/fill_convex.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::super::*; -use super::super::*; -use super::fill_settings::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::super::{fit_curve, BezierCurve, Curve}; +use super::super::BezierPathFactory; +use super::fill_settings::FillSettings; use std::f64; use std::ops::Range; @@ -182,9 +182,7 @@ where // Divide the entry into two by casting a ray between the two points let mid_point = (entry.angle.start + entry.angle.end) / 2.0; let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); - let mid_ray_pos = mid_ray - .as_ref() - .map(|(collision, _)| collision.position); + let mid_ray_pos = mid_ray.as_ref().map(|(collision, _)| collision.position); // If there's a discontinuity (eg, a corner we can't see around), we'll see that the mid point is very close to the end point and far from the start point if let Some(mid_ray_pos) = mid_ray_pos { @@ -225,9 +223,7 @@ where // Cast a ray between the two points let mid_point = (entry.angle.start + entry.angle.end) / 2.0; let mid_ray = perform_ray_cast(center, mid_point, &cast_ray); - let mid_ray_pos = mid_ray - .as_ref() - .map(|(collision, _)| collision.position); + let mid_ray_pos = mid_ray.as_ref().map(|(collision, _)| collision.position); // Divide into two pairs of ranges (process the earlier one first) stack.push(StackEntry { diff --git a/src/bezier/path/arithmetic/add.rs b/src/bezier/path/arithmetic/add.rs index ae2a28f0..fb1ba10a 100644 --- a/src/bezier/path/arithmetic/add.rs +++ b/src/bezier/path/arithmetic/add.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::graph_path::*; -use super::super::path::*; -use super::ray_cast::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::graph_path::GraphPath; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::ray_cast::{PathDirection, PathLabel}; // // This uses a simple ray casting algorithm to perform the addition diff --git a/src/bezier/path/arithmetic/chain.rs b/src/bezier/path/arithmetic/chain.rs index 30f55a55..5ece28e2 100644 --- a/src/bezier/path/arithmetic/chain.rs +++ b/src/bezier/path/arithmetic/chain.rs @@ -1,9 +1,9 @@ -use super::super::super::super::geo::*; -use super::super::path::*; -use super::add::*; -use super::chain_add::*; -use super::intersect::*; -use super::sub::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::add::path_remove_interior_points; +use super::chain_add::path_add_chain; +use super::intersect::path_intersect; +use super::sub::path_sub; /// /// Description of an arithmetic operation to perform on a bezier path diff --git a/src/bezier/path/arithmetic/chain_add.rs b/src/bezier/path/arithmetic/chain_add.rs index b5246c3a..f82128fd 100644 --- a/src/bezier/path/arithmetic/chain_add.rs +++ b/src/bezier/path/arithmetic/chain_add.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::graph_path::*; -use super::super::path::*; -use super::ray_cast::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::graph_path::GraphPath; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::ray_cast::{PathDirection, PathLabel}; /// /// Adds multiple paths in a single operation diff --git a/src/bezier/path/arithmetic/cut.rs b/src/bezier/path/arithmetic/cut.rs index 81df83f1..f322b770 100644 --- a/src/bezier/path/arithmetic/cut.rs +++ b/src/bezier/path/arithmetic/cut.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::graph_path::*; -use super::super::path::*; -use super::ray_cast::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::graph_path::GraphPath; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::ray_cast::{PathDirection, PathLabel}; /// /// The result of a path cut operation diff --git a/src/bezier/path/arithmetic/full_intersect.rs b/src/bezier/path/arithmetic/full_intersect.rs index 1e58db47..67a49a66 100644 --- a/src/bezier/path/arithmetic/full_intersect.rs +++ b/src/bezier/path/arithmetic/full_intersect.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::graph_path::*; -use super::super::path::*; -use super::ray_cast::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::graph_path::GraphPath; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::ray_cast::{PathDirection, PathLabel}; /// /// The result of a path cut operation diff --git a/src/bezier/path/arithmetic/intersect.rs b/src/bezier/path/arithmetic/intersect.rs index cd40c67a..17c77d23 100644 --- a/src/bezier/path/arithmetic/intersect.rs +++ b/src/bezier/path/arithmetic/intersect.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::graph_path::*; -use super::super::path::*; -use super::ray_cast::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::graph_path::GraphPath; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::ray_cast::{PathDirection, PathLabel}; impl GraphPath { /// diff --git a/src/bezier/path/arithmetic/ray_cast.rs b/src/bezier/path/arithmetic/ray_cast.rs index 47f4bfa9..fb9963f0 100644 --- a/src/bezier/path/arithmetic/ray_cast.rs +++ b/src/bezier/path/arithmetic/ray_cast.rs @@ -1,12 +1,12 @@ -use super::super::super::super::geo::*; -use super::super::super::curve::*; -use super::super::super::normal::*; -use super::super::graph_path::*; -use super::super::is_clockwise::*; -use super::super::path::*; -use crate::line::*; - -use smallvec::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::super::curve::BezierCurve; +use super::super::super::normal::NormalCurve; +use super::super::graph_path::{GraphPath, GraphPathEdgeKind, GraphRayCollision}; +use super::super::is_clockwise::PathWithIsClockwise; +use super::super::path::BezierPath; +use crate::line::Line; + +use smallvec::{smallvec, SmallVec}; /// /// Winding direction of a particular path diff --git a/src/bezier/path/arithmetic/sub.rs b/src/bezier/path/arithmetic/sub.rs index a6760b74..1708df55 100644 --- a/src/bezier/path/arithmetic/sub.rs +++ b/src/bezier/path/arithmetic/sub.rs @@ -1,7 +1,7 @@ -use super::super::super::super::geo::*; -use super::super::graph_path::*; -use super::super::path::*; -use super::ray_cast::*; +use super::super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::graph_path::GraphPath; +use super::super::path::{BezierPath, BezierPathFactory}; +use super::ray_cast::{PathDirection, PathLabel}; impl GraphPath { /// diff --git a/src/bezier/path/bounds.rs b/src/bezier/path/bounds.rs index b99fbaae..20e9044f 100644 --- a/src/bezier/path/bounds.rs +++ b/src/bezier/path/bounds.rs @@ -1,7 +1,7 @@ -use super::super::super::geo::*; -use super::super::curve::*; -use super::path::*; -use super::to_curves::*; +use super::super::super::geo::{BoundingBox, Coordinate}; +use super::super::curve::{BezierCurve, Curve}; +use super::path::BezierPath; +use super::to_curves::path_to_curves; /// /// Finds the bounds of a path diff --git a/src/bezier/path/graph_path/edge.rs b/src/bezier/path/graph_path/edge.rs index 0d17b9a8..d4572471 100644 --- a/src/bezier/path/graph_path/edge.rs +++ b/src/bezier/path/graph_path/edge.rs @@ -1,9 +1,9 @@ use super::{GraphEdge, GraphEdgeRef, GraphPath, GraphPathEdge, GraphPathEdgeKind}; -use crate::bezier::bounds::*; -use crate::bezier::curve::*; -use crate::geo::*; +use crate::bezier::bounds::bounding_box4; +use crate::bezier::curve::BezierCurve; +use crate::geo::{BoundingBox, Coordinate, Geo, HasBoundingBox}; -use std::cell::*; +use std::cell::RefCell; use std::fmt; impl GraphPathEdge { diff --git a/src/bezier/path/graph_path/edge_ref.rs b/src/bezier/path/graph_path/edge_ref.rs index f530581e..30661c2d 100644 --- a/src/bezier/path/graph_path/edge_ref.rs +++ b/src/bezier/path/graph_path/edge_ref.rs @@ -1,5 +1,5 @@ use super::{GraphEdge, GraphEdgeRef, GraphPath}; -use crate::geo::*; +use crate::geo::{Coordinate, Coordinate2D}; impl GraphEdgeRef { /// diff --git a/src/bezier/path/graph_path/mod.rs b/src/bezier/path/graph_path/mod.rs index e798217b..6055deb8 100644 --- a/src/bezier/path/graph_path/mod.rs +++ b/src/bezier/path/graph_path/mod.rs @@ -1,11 +1,11 @@ -use super::path::*; -use crate::bezier::curve::*; -use crate::consts::*; -use crate::geo::*; +use super::path::{BezierPath, BezierPathFactory}; +use crate::bezier::curve::BezierCurve; +use crate::consts::CLOSE_DISTANCE; +use crate::geo::{Coordinate, Coordinate2D, Geo}; -use smallvec::*; +use smallvec::{smallvec, SmallVec}; -use std::cell::*; +use std::cell::RefCell; use std::fmt; mod edge; @@ -164,10 +164,7 @@ impl GraphPath { /// /// Creates a graph path from a bezier path /// - pub fn from_path>( - path: &P, - label: Label, - ) -> Self { + pub fn from_path>(path: &P, label: Label) -> Self { // All edges are exterior for a single path let mut points = vec![]; @@ -541,7 +538,9 @@ impl GraphPath { // For all the connected points, update the following edge refs let mut still_connected = false; - self.points[edge_ref.start_idx].connected_from.sort_unstable(); + self.points[edge_ref.start_idx] + .connected_from + .sort_unstable(); self.points[edge_ref.start_idx].connected_from.dedup(); for connected_point_idx in self.points[edge_ref.start_idx].connected_from.clone() { for edge_idx in 0..(self.points[connected_point_idx].forward_edges.len()) { @@ -649,11 +648,7 @@ impl GraphPath { /// to specify edge types - knowing if an edge is an interior or exterior edge makes it possible to tell the difference /// between a hole cut into a shape and an intersection. /// - pub fn collide( - mut self, - collide_path: Self, - accuracy: f64, - ) -> Self { + pub fn collide(mut self, collide_path: Self, accuracy: f64) -> Self { // Generate a merged path with all of the edges let collision_offset = self.points.len(); self = self.merge(collide_path); diff --git a/src/bezier/path/graph_path/path_collision.rs b/src/bezier/path/graph_path/path_collision.rs index cc79781a..247c336b 100644 --- a/src/bezier/path/graph_path/path_collision.rs +++ b/src/bezier/path/graph_path/path_collision.rs @@ -1,10 +1,12 @@ use super::{GraphEdge, GraphEdgeRef, GraphPath, GraphPathEdge, GraphPathPoint}; -use crate::bezier::curve::*; -use crate::bezier::intersection::*; -use crate::consts::*; -use crate::geo::*; - -use smallvec::*; +use crate::bezier::curve::{BezierCurve, BezierCurveFactory, Curve}; +use crate::bezier::intersection::{curve_intersects_curve_clip, find_self_intersection_point}; +use crate::consts::{CLOSE_DISTANCE, SMALL_T_DISTANCE}; +use crate::geo::{ + sweep_against, sweep_self, BoundingBox, Bounds, Coordinate, Coordinate2D, Geo, HasBoundingBox, +}; + +use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; use std::mem; diff --git a/src/bezier/path/graph_path/ray_collision.rs b/src/bezier/path/graph_path/ray_collision.rs index af21cbc8..1848b589 100644 --- a/src/bezier/path/graph_path/ray_collision.rs +++ b/src/bezier/path/graph_path/ray_collision.rs @@ -1,9 +1,9 @@ use super::{GraphEdge, GraphEdgeRef, GraphPath}; -use crate::bezier::path::ray::*; -use crate::geo::*; -use crate::line::*; +use crate::bezier::path::ray::{ray_collisions, RayPath}; +use crate::geo::{Coordinate, Coordinate2D}; +use crate::line::Line; -use smallvec::*; +use smallvec::SmallVec; /// /// Represents a collision between a ray and a GraphPath diff --git a/src/bezier/path/graph_path/test.rs b/src/bezier/path/graph_path/test.rs index cf7bc6d4..c29720d7 100644 --- a/src/bezier/path/graph_path/test.rs +++ b/src/bezier/path/graph_path/test.rs @@ -2,6 +2,7 @@ use super::*; use crate::arc::*; use crate::bezier::normal::*; use crate::bezier::path::*; +use crate::Coord2; pub(crate) fn donut() -> GraphPath { let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); diff --git a/src/bezier/path/intersection.rs b/src/bezier/path/intersection.rs index 505ea496..8e6348c5 100644 --- a/src/bezier/path/intersection.rs +++ b/src/bezier/path/intersection.rs @@ -1,9 +1,9 @@ -use super::super::super::geo::*; -use super::super::super::line::*; -use super::super::curve::*; -use super::super::intersection::*; -use super::path::*; -use super::to_curves::*; +use super::super::super::geo::{BoundingBox, Bounds, Coordinate2D}; +use super::super::super::line::Line; +use super::super::curve::{BezierCurve, Curve}; +use super::super::intersection::{curve_intersects_curve_clip, curve_intersects_line}; +use super::path::BezierPath; +use super::to_curves::path_to_curves; /// /// Determines the intersections of a path and a line diff --git a/src/bezier/path/is_clockwise.rs b/src/bezier/path/is_clockwise.rs index edcd0371..a554e6bb 100644 --- a/src/bezier/path/is_clockwise.rs +++ b/src/bezier/path/is_clockwise.rs @@ -1,7 +1,7 @@ -use super::super::super::geo::*; -use super::path::*; +use super::super::super::geo::{Coordinate, Coordinate2D}; +use super::path::BezierPath; -use itertools::*; +use itertools::Itertools; /// /// Determines if a set of points are in a clockwise ordering (assuming that a positive y value indicates an upwards direction) diff --git a/src/bezier/path/path.rs b/src/bezier/path/path.rs index 7122830b..a1a84f2b 100644 --- a/src/bezier/path/path.rs +++ b/src/bezier/path/path.rs @@ -1,9 +1,9 @@ -use super::super::super::geo::*; -use super::super::curve::*; -use super::bounds::*; -use super::to_curves::*; +use super::super::super::geo::{BoundingBox, Coord2, Coordinate, Geo}; +use super::super::curve::BezierCurveFactory; +use super::bounds::{path_bounding_box, path_fast_bounding_box}; +use super::to_curves::path_to_curves; -use itertools::*; +use itertools::Itertools; use std::iter; use std::vec; diff --git a/src/bezier/path/path_builder.rs b/src/bezier/path/path_builder.rs index db82ea2f..c38f3120 100644 --- a/src/bezier/path/path_builder.rs +++ b/src/bezier/path/path_builder.rs @@ -1,4 +1,4 @@ -use super::path::*; +use super::path::{BezierPath, BezierPathFactory}; /// /// Used to build a bezier path diff --git a/src/bezier/path/point.rs b/src/bezier/path/point.rs index a3ffbe20..b9e7104e 100644 --- a/src/bezier/path/point.rs +++ b/src/bezier/path/point.rs @@ -1,12 +1,12 @@ -use super::super::super::geo::*; -use super::super::curve::*; -use super::super::normal::*; -use super::graph_path::*; -use super::path::*; -use super::ray::*; -use super::to_curves::*; - -use smallvec::*; +use super::super::super::geo::{Coordinate, Coordinate2D, Geo}; +use super::super::curve::{BezierCurve, Curve}; +use super::super::normal::NormalCurve; +use super::graph_path::GraphEdgeRef; +use super::path::BezierPath; +use super::ray::{ray_collisions, RayPath}; +use super::to_curves::path_to_curves; + +use smallvec::{smallvec, SmallVec}; /// /// Represents a curve that can be represented either forwards or backwards diff --git a/src/bezier/path/ray.rs b/src/bezier/path/ray.rs index 89408421..908bea8c 100644 --- a/src/bezier/path/ray.rs +++ b/src/bezier/path/ray.rs @@ -1,12 +1,12 @@ -use super::super::super::consts::*; -use super::super::super::geo::*; -use super::super::super::line::*; -use super::super::curve::*; -use super::super::intersection::*; -use super::super::normal::*; -use super::graph_path::*; - -use smallvec::*; +use super::super::super::consts::{CLOSE_DISTANCE, SMALL_DISTANCE}; +use super::super::super::geo::{Coordinate, Coordinate2D}; +use super::super::super::line::{Line, Line2D}; +use super::super::curve::BezierCurve; +use super::super::intersection::curve_intersects_ray; +use super::super::normal::NormalCurve; +use super::graph_path::{GraphEdgeRef, GraphRayCollision}; + +use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; /// @@ -84,7 +84,8 @@ where // The curve is collinear if all of the points lie on the ray (start_point.x() * a + start_point.y() * b + c).abs() < SMALL_DISTANCE && (end_point.x() * a + end_point.y() * b + c).abs() < SMALL_DISTANCE - && (cp1.x() * a + cp1.y() * b + c).abs() < SMALL_DISTANCE && (cp2.x() * a + cp2.y() * b + c).abs() < SMALL_DISTANCE + && (cp1.x() * a + cp1.y() * b + c).abs() < SMALL_DISTANCE + && (cp2.x() * a + cp2.y() * b + c).abs() < SMALL_DISTANCE } #[derive(PartialEq)] @@ -326,7 +327,8 @@ where let end_point = path.point_position(end_point_idx); // If any following edge is collinear, remove this collision - !(position.is_near_to(&end_point, CLOSE_DISTANCE) && path + !(position.is_near_to(&end_point, CLOSE_DISTANCE) + && path .edges_for_point(end_point_idx) .into_iter() .map(|edge| path.get_edge(edge)) @@ -336,7 +338,8 @@ where let start_point = path.point_position(start_point_idx); // If any preceding edge is collinear, remove this collision - !(position.is_near_to(&start_point, CLOSE_DISTANCE) && path + !(position.is_near_to(&start_point, CLOSE_DISTANCE) + && path .reverse_edges_for_point(start_point_idx) .into_iter() .map(|edge| path.get_edge(edge)) @@ -772,6 +775,8 @@ mod test { use super::super::path::*; use super::super::path_builder::*; use super::*; + use crate::bezier::path::GraphPath; + use crate::Coord2; #[test] fn raw_donut_collisions() { diff --git a/src/bezier/path/to_curves.rs b/src/bezier/path/to_curves.rs index f9ffcfde..5cb24d74 100644 --- a/src/bezier/path/to_curves.rs +++ b/src/bezier/path/to_curves.rs @@ -1,7 +1,7 @@ -use super::super::curve::*; -use super::path::*; +use super::super::curve::BezierCurveFactory; +use super::path::BezierPath; -use itertools::*; +use itertools::Itertools; /// /// Converts a path to a series of bezier curves diff --git a/src/bezier/search.rs b/src/bezier/search.rs index 96a37ab4..d0a39d60 100644 --- a/src/bezier/search.rs +++ b/src/bezier/search.rs @@ -1,6 +1,6 @@ -use super::super::geo::*; -use super::bounds::*; -use super::subdivide::*; +use super::super::geo::Coordinate; +use super::bounds::bounding_box4; +use super::subdivide::subdivide4; /// /// Performs a subdivision search on a curve for a point matching a function diff --git a/src/bezier/section.rs b/src/bezier/section.rs index d3c9e665..78ec261b 100644 --- a/src/bezier/section.rs +++ b/src/bezier/section.rs @@ -1,9 +1,9 @@ -use super::super::consts::*; -use super::super::geo::*; -use super::basis::*; -use super::curve::*; +use super::super::consts::SMALL_DISTANCE; +use super::super::geo::Geo; +use super::basis::de_casteljau2; +use super::curve::BezierCurve; -use std::cell::*; +use std::cell::RefCell; /// /// Represents a subsection of a bezier curve @@ -112,7 +112,8 @@ impl<'a, C: 'a + BezierCurve> BezierCurve for CurveSection<'a, C> { /// The control points in this curve /// fn control_points(&self) -> (Self::Point, Self::Point) { - *self.cached_control_points + *self + .cached_control_points .borrow_mut() .get_or_insert_with(move || { // This is the de-casteljau subdivision algorithm (ran twice to cut out a section of the curve) diff --git a/src/bezier/solve.rs b/src/bezier/solve.rs index 38d93bfe..6c261f78 100644 --- a/src/bezier/solve.rs +++ b/src/bezier/solve.rs @@ -1,9 +1,9 @@ -use super::super::consts::*; -use super::super::geo::*; -use super::curve::*; +use super::super::consts::SMALL_DISTANCE; +use super::super::geo::Coordinate; +use super::curve::BezierCurve; use roots::{find_roots_cubic, find_roots_quadratic, Roots}; -use smallvec::*; +use smallvec::{smallvec, SmallVec}; pub(crate) const CLOSE_ENOUGH: f64 = SMALL_DISTANCE * 50.0; diff --git a/src/bezier/subdivide.rs b/src/bezier/subdivide.rs index 370ce241..d5c8913a 100644 --- a/src/bezier/subdivide.rs +++ b/src/bezier/subdivide.rs @@ -1,5 +1,5 @@ -use super::super::geo::*; -use super::basis::*; +use super::super::geo::Coordinate; +use super::basis::de_casteljau2; /// /// Subdivides a cubic bezier curve at a particular point, returning the weights of diff --git a/src/bezier/tangent.rs b/src/bezier/tangent.rs index e66432cb..099d9869 100644 --- a/src/bezier/tangent.rs +++ b/src/bezier/tangent.rs @@ -1,6 +1,6 @@ -use super::basis::*; -use super::curve::*; -use super::derivative::*; +use super::basis::de_casteljau3; +use super::curve::BezierCurve; +use super::derivative::derivative4; /// /// A structure that can be used to compute the tangent of a bezier curve diff --git a/src/bezier/walk.rs b/src/bezier/walk.rs index b0107686..48300a0e 100644 --- a/src/bezier/walk.rs +++ b/src/bezier/walk.rs @@ -1,9 +1,9 @@ -use super::basis::*; -use super::curve::*; -use super::derivative::*; -use super::section::*; +use super::basis::de_casteljau3; +use super::curve::BezierCurve; +use super::derivative::derivative4; +use super::section::CurveSection; -use crate::geo::*; +use crate::geo::Coordinate; /// /// Walks a bezier curve by dividing it into a number of sections diff --git a/src/debug/graph_path_debug.rs b/src/debug/graph_path_debug.rs index 72ca94d6..03093888 100644 --- a/src/debug/graph_path_debug.rs +++ b/src/debug/graph_path_debug.rs @@ -1,5 +1,7 @@ -use super::super::bezier::path::*; -use super::super::bezier::*; +use super::super::bezier::path::{GraphPath, GraphPathEdgeKind, PathDirection, PathLabel}; +use super::super::bezier::{ + BezierCurve, BoundingBox, Bounds, Coordinate, Coordinate2D, NormalCurve, +}; use std::fmt::Write; @@ -62,7 +64,13 @@ pub fn graph_path_svg_string( cp2.x(), cp2.y(), end_point.x(), end_point.y(), kind).unwrap(); - writeln!(result, "", end_point.x(), end_point.y()).unwrap(); + writeln!( + result, + "", + end_point.x(), + end_point.y() + ) + .unwrap(); writeln!( result, @@ -99,9 +107,15 @@ pub fn graph_path_svg_string( let p1 = p1 - (point_offset * 1000.0); let p2 = p2 + (point_offset * 1000.0); - writeln!(result, "", - p1.x(), p1.y(), - p2.x(), p2.y()).unwrap(); + writeln!( + result, + "", + p1.x(), + p1.y(), + p2.x(), + p2.y() + ) + .unwrap(); let ray_direction = p2 - p1; let mut collision_count = 0; diff --git a/src/debug/path_to_string.rs b/src/debug/path_to_string.rs index 04f93059..59f56809 100644 --- a/src/debug/path_to_string.rs +++ b/src/debug/path_to_string.rs @@ -1,7 +1,7 @@ -use super::super::bezier::path::*; -use super::super::geo::*; +use super::super::bezier::path::BezierPath; +use super::super::geo::{Coordinate, Coordinate2D}; -use std::fmt::*; +use std::fmt::Write; /// /// Writes out a path as a Rust simple bezier path definition diff --git a/src/geo/bounding_box.rs b/src/geo/bounding_box.rs index 03db15db..c356b522 100644 --- a/src/geo/bounding_box.rs +++ b/src/geo/bounding_box.rs @@ -1,6 +1,6 @@ -use super::coordinate::*; -use super::geo::*; -use super::has_bounds::*; +use super::coordinate::Coordinate; +use super::geo::Geo; +use super::has_bounds::HasBoundingBox; /// /// Trait implemented by things representing axis-aligned bounding boxes diff --git a/src/geo/coordinate.rs b/src/geo/coordinate.rs index 1ee193e5..51d43ea0 100644 --- a/src/geo/coordinate.rs +++ b/src/geo/coordinate.rs @@ -10,9 +10,9 @@ //! any type for which the `Coordinate2D` trait is defined. //! -use smallvec::*; +use smallvec::{smallvec, SmallVec}; -use std::ops::*; +use std::ops::{Add, Mul, Sub}; /// /// Represents a value that can be used as a coordinate in a bezier curve diff --git a/src/geo/coordinate_ext.rs b/src/geo/coordinate_ext.rs index 24168624..a9648866 100644 --- a/src/geo/coordinate_ext.rs +++ b/src/geo/coordinate_ext.rs @@ -1,4 +1,4 @@ -use crate::geo::coordinate::*; +use crate::geo::coordinate::{Coordinate, Coordinate2D}; /// /// Extra functions provided for coordinate types diff --git a/src/geo/geo.rs b/src/geo/geo.rs index 324643a7..1d62af9d 100644 --- a/src/geo/geo.rs +++ b/src/geo/geo.rs @@ -1,4 +1,4 @@ -use super::coordinate::*; +use super::coordinate::Coordinate; /// /// Simple base trait implemented by things representing geometry diff --git a/src/geo/has_bounds.rs b/src/geo/has_bounds.rs index 7fdd3533..b78c2c90 100644 --- a/src/geo/has_bounds.rs +++ b/src/geo/has_bounds.rs @@ -1,5 +1,5 @@ -use super::bounding_box::*; -use super::geo::*; +use super::bounding_box::BoundingBox; +use super::geo::Geo; /// /// Trait implemented by types that have a bounding box associated with them diff --git a/src/geo/sweep.rs b/src/geo/sweep.rs index bbb135e5..cc9539c0 100644 --- a/src/geo/sweep.rs +++ b/src/geo/sweep.rs @@ -1,6 +1,6 @@ -use crate::geo::*; +use crate::geo::{BoundingBox, Bounds, Coordinate2D, HasBoundingBox}; -use smallvec::*; +use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; diff --git a/src/line/coefficients.rs b/src/line/coefficients.rs index 9098fdc6..76e2a0f6 100644 --- a/src/line/coefficients.rs +++ b/src/line/coefficients.rs @@ -1,5 +1,5 @@ -use super::super::geo::*; -use super::line::*; +use super::super::geo::{Coordinate, Coordinate2D}; +use super::line::Line; /// /// For a two-dimensional line, computes the coefficients of the line equation ax+by+c=0 diff --git a/src/line/intersection.rs b/src/line/intersection.rs index 930cc907..a071ffb5 100644 --- a/src/line/intersection.rs +++ b/src/line/intersection.rs @@ -1,5 +1,5 @@ -use super::super::geo::*; -use super::line::*; +use super::super::geo::{Coordinate, Coordinate2D}; +use super::line::Line; /// Smallest divisor magnitude to use in ray_intersects_ray (the closer the divisor is to 0, the more close to parallel the lines are), so this /// determines the shallowest angle allowed between two lines before we consider them to be parallel. diff --git a/src/line/line.rs b/src/line/line.rs index ebb75210..d2d55b30 100644 --- a/src/line/line.rs +++ b/src/line/line.rs @@ -1,5 +1,5 @@ -use super::super::geo::*; -use super::coefficients::*; +use super::super::geo::{Coordinate, Coordinate2D, Geo}; +use super::coefficients::line_coefficients_2d; /// /// Represents a straight line diff --git a/src/line/to_curve.rs b/src/line/to_curve.rs index 07200afe..ea036524 100644 --- a/src/line/to_curve.rs +++ b/src/line/to_curve.rs @@ -1,5 +1,5 @@ -use super::super::bezier::*; -use super::line::*; +use super::super::bezier::BezierCurveFactory; +use super::line::Line; /// /// Changes a line to a bezier curve diff --git a/tests/bezier/algorithms/fill_concave.rs b/tests/bezier/algorithms/fill_concave.rs index b3cdfa62..73fe394d 100644 --- a/tests/bezier/algorithms/fill_concave.rs +++ b/tests/bezier/algorithms/fill_concave.rs @@ -1,6 +1,8 @@ -use flo_curves::bezier::path::algorithms::*; -use flo_curves::bezier::path::*; -use flo_curves::bezier::*; +use flo_curves::bezier::path::algorithms::{flood_fill_concave, FillSettings, RayCollision}; +use flo_curves::bezier::path::{BezierPath, SimpleBezierPath}; +use flo_curves::bezier::{ + BezierCurve, BoundingBox, Coord2, Coordinate, Coordinate2D, Coordinate3D, Curve, +}; fn circle_ray_cast( circle_center: Coord2, diff --git a/tests/bezier/algorithms/fill_convex.rs b/tests/bezier/algorithms/fill_convex.rs index fc424638..1ef02808 100644 --- a/tests/bezier/algorithms/fill_convex.rs +++ b/tests/bezier/algorithms/fill_convex.rs @@ -1,6 +1,10 @@ -use flo_curves::bezier::path::algorithms::*; -use flo_curves::bezier::path::*; -use flo_curves::bezier::*; +use flo_curves::bezier::path::algorithms::{ + flood_fill_convex, trace_outline_convex, FillSettings, RayCollision, +}; +use flo_curves::bezier::path::{BezierPath, SimpleBezierPath}; +use flo_curves::bezier::{ + BezierCurve, BoundingBox, Coord2, Coordinate, Coordinate2D, Coordinate3D, Curve, +}; fn circle_ray_cast( circle_center: Coord2, diff --git a/tests/bezier/basis.rs b/tests/bezier/basis.rs index d5f63f69..d68a17fa 100644 --- a/tests/bezier/basis.rs +++ b/tests/bezier/basis.rs @@ -1,4 +1,4 @@ -use super::*; +use super::approx_equal; use flo_curves::bezier; #[test] diff --git a/tests/bezier/characteristics.rs b/tests/bezier/characteristics.rs index 5f762a17..b40ffe83 100644 --- a/tests/bezier/characteristics.rs +++ b/tests/bezier/characteristics.rs @@ -1,5 +1,8 @@ -use flo_curves::bezier::*; -use flo_curves::*; +use flo_curves::bezier::{ + characterize_curve, features_for_curve, BezierCurve, BezierCurve2D, BezierCurveFactory, Coord2, + Coordinate, Curve, CurveFeatures, +}; +use flo_curves::{bezier, Line}; #[test] fn detect_loop_1() { diff --git a/tests/bezier/curve_intersection_clip.rs b/tests/bezier/curve_intersection_clip.rs index f92c1ae2..d9017160 100644 --- a/tests/bezier/curve_intersection_clip.rs +++ b/tests/bezier/curve_intersection_clip.rs @@ -1,6 +1,6 @@ use flo_curves::bezier; use flo_curves::line; -use flo_curves::*; +use flo_curves::{BezierCurve, BezierCurveFactory, BoundingBox, Coord2, Coordinate, Line}; #[test] fn find_intersection_on_straight_line_not_middle() { @@ -969,7 +969,7 @@ fn intersection_very_close_to_start_1() { #[test] fn solve_t_close_to_start() { - use flo_curves::bezier::*; + use flo_curves::bezier::{BezierCurve, BezierCurve2D, Coord2, Coordinate, CurveCategory}; // Same curve as above, but we try to solve the closest point for one curve against another let fragment = bezier::Curve { diff --git a/tests/bezier/deform.rs b/tests/bezier/deform.rs index f743363b..7489db09 100644 --- a/tests/bezier/deform.rs +++ b/tests/bezier/deform.rs @@ -1,5 +1,7 @@ use flo_curves::bezier; -use flo_curves::*; +use flo_curves::{ + BezierCurve, BezierCurveFactory, Coord2, Coordinate, Coordinate2D, Coordinate3D, Line, +}; #[test] fn deform_line_upwards() { diff --git a/tests/bezier/distort.rs b/tests/bezier/distort.rs index 656fff85..833a9fb0 100644 --- a/tests/bezier/distort.rs +++ b/tests/bezier/distort.rs @@ -1,4 +1,7 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{ + distort_curve, walk_curve_evenly, BezierCurve, BezierCurveFactory, Coord2, Coordinate2D, + Coordinate3D, Curve, +}; #[test] fn line_to_sine_wave() { diff --git a/tests/bezier/intersection.rs b/tests/bezier/intersection.rs index 6f66006b..b7659da8 100644 --- a/tests/bezier/intersection.rs +++ b/tests/bezier/intersection.rs @@ -1,6 +1,6 @@ use flo_curves::bezier; use flo_curves::line; -use flo_curves::*; +use flo_curves::{BezierCurve, BezierCurveFactory, BoundingBox, Coord2, Coordinate, Line}; #[test] fn find_intersection_on_straight_line() { @@ -391,7 +391,7 @@ fn ray_intersects_curve_1e() { #[test] fn roots_library_does_not_have_missing_root_bug() { - use roots::*; + use roots::{find_roots_cubic, FloatType, Roots}; // Known root of a set of coefficients (which happen to be the coefficients from the failing tests above) let a = -0.000000000000000040410628481035; @@ -421,7 +421,7 @@ fn roots_library_does_not_have_missing_root_bug() { #[test] fn ray_missing_root_2() { - use roots::*; + use roots::{find_roots_cubic, FloatType, Roots}; // As above but with the slightly weird coefficent a set to 0.0 let a = -0.0; @@ -451,7 +451,7 @@ fn ray_missing_root_2() { #[test] fn ray_missing_root_3() { - use roots::*; + use roots::{find_roots_cubic, FloatType, Roots}; // Again, but with the smallest value of a that we get a sensible answer for let a = -0.0000000002; diff --git a/tests/bezier/length.rs b/tests/bezier/length.rs index 01efc097..32b013f1 100644 --- a/tests/bezier/length.rs +++ b/tests/bezier/length.rs @@ -1,4 +1,6 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{ + chord_length, curve_length, walk_curve_unevenly, BezierCurve, BezierCurveFactory, Coord2, Curve, +}; /// /// Estimates a curve's length by subdividing it a lot diff --git a/tests/bezier/mod.rs b/tests/bezier/mod.rs index b5050841..1f1a24c6 100644 --- a/tests/bezier/mod.rs +++ b/tests/bezier/mod.rs @@ -1,5 +1,5 @@ use flo_curves::bezier; -use flo_curves::*; +use flo_curves::{BezierCurve, BezierCurveFactory, Coord2, Coordinate, Line}; mod algorithms; mod path; diff --git a/tests/bezier/normal.rs b/tests/bezier/normal.rs index 541e8aae..e523239f 100644 --- a/tests/bezier/normal.rs +++ b/tests/bezier/normal.rs @@ -1,6 +1,6 @@ use flo_curves::bezier; use flo_curves::bezier::NormalCurve; -use flo_curves::*; +use flo_curves::{BezierCurveFactory, Coord2, Coordinate, Coordinate2D, Coordinate3D, Line}; #[test] fn normal_for_line_is_straight_up() { diff --git a/tests/bezier/offset.rs b/tests/bezier/offset.rs index e678dafe..e8d7d7e0 100644 --- a/tests/bezier/offset.rs +++ b/tests/bezier/offset.rs @@ -1,5 +1,8 @@ use flo_curves::bezier::NormalCurve; -use flo_curves::bezier::*; +use flo_curves::bezier::{ + curve_intersects_ray, offset, offset_lms_sampling, BezierCurve, BezierCurveFactory, + BoundingBox, Coord2, Coordinate, Coordinate2D, Coordinate3D, Curve, Normalize, +}; use flo_curves::line; use flo_curves::line::Line2D; @@ -276,7 +279,7 @@ fn normals_for_line_do_not_meet_at_intersection() { #[test] fn offset_lms_sampling_arc_start_tangent() { - use flo_curves::arc::*; + use flo_curves::arc::Circle; // 90 degree circle arc let circle = Circle::new(Coord2(0.0, 0.0), 100.0); @@ -297,7 +300,7 @@ fn offset_lms_sampling_arc_start_tangent() { #[test] fn offset_lms_sampling_arc_end_tangent() { - use flo_curves::arc::*; + use flo_curves::arc::Circle; // 90 degree circle arc let circle = Circle::new(Coord2(0.0, 0.0), 100.0); @@ -320,7 +323,7 @@ fn offset_lms_sampling_arc_end_tangent() { #[test] fn offset_lms_sampling_arc_end_point() { - use flo_curves::arc::*; + use flo_curves::arc::Circle; // 90 degree circle arc let circle = Circle::new(Coord2(0.0, 0.0), 100.0); @@ -343,7 +346,7 @@ fn offset_lms_sampling_arc_end_point() { #[test] fn offset_lms_sampling_arc_fit_single_curve() { - use flo_curves::arc::*; + use flo_curves::arc::Circle; // 90 degree circle arc let circle = Circle::new(Coord2(0.0, 0.0), 100.0); diff --git a/tests/bezier/overlaps.rs b/tests/bezier/overlaps.rs index e09e9e2b..6111239d 100644 --- a/tests/bezier/overlaps.rs +++ b/tests/bezier/overlaps.rs @@ -1,4 +1,4 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{overlapping_region, BezierCurve, BezierCurveFactory, Coord2, Curve}; #[test] fn simple_overlapping_curves() { diff --git a/tests/bezier/path/arithmetic_add.rs b/tests/bezier/path/arithmetic_add.rs index cd1ec73e..9041151f 100644 --- a/tests/bezier/path/arithmetic_add.rs +++ b/tests/bezier/path/arithmetic_add.rs @@ -1,9 +1,13 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::debug::*; -use flo_curves::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{ + path_add, path_combine, path_remove_interior_points, path_remove_overlapped_points, BezierPath, + BezierPathBuilder, BezierPathFactory, GraphPath, PathCombine, PathDirection, PathLabel, + SimpleBezierPath, +}; +use flo_curves::debug::graph_path_svg_string; +use flo_curves::{BezierCurve, BoundingBox, Coord2, Coordinate, Line}; -use super::svg::*; +use super::svg::svg_path_string; #[test] fn add_two_overlapping_circles() { @@ -422,10 +426,7 @@ fn remove_interior_for_ring_removes_center() { let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &[ring1.clone(), ring2], - 0.01, - ); + let removed = path_remove_interior_points::<_, SimpleBezierPath>(&[ring1.clone(), ring2], 0.01); assert!(removed.len() == 1); @@ -489,10 +490,8 @@ fn remove_interior_for_ring_with_offset_crossbar_removes_center() { println!("{}", graph_path_svg_string(&merged_path, vec![])); // Try the actual removing operation - let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &[ring1, ring2, crossbar1], - 0.01, - ); + let removed = + path_remove_interior_points::<_, SimpleBezierPath>(&[ring1, ring2, crossbar1], 0.01); assert!(removed.len() == 1); } @@ -543,10 +542,7 @@ fn remove_interior_for_ring_with_cross_removes_center() { .build(); let removed = path_remove_interior_points::<_, SimpleBezierPath>( - &[ring1.clone(), - ring2, - crossbar1, - crossbar2], + &[ring1.clone(), ring2, crossbar1, crossbar2], 0.01, ); @@ -565,10 +561,8 @@ fn remove_overlapped_for_ring_does_not_remove_center() { let ring1 = Circle::new(Coord2(2.0, 2.0), 2.0).to_path::(); let ring2 = Circle::new(Coord2(2.0, 2.0), 1.5).to_path::(); - let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( - &[ring1.clone(), ring2.clone()], - 0.01, - ); + let removed = + path_remove_overlapped_points::<_, SimpleBezierPath>(&[ring1.clone(), ring2.clone()], 0.01); assert!(removed.len() == 2); @@ -597,10 +591,8 @@ fn remove_overlapped_for_ring_with_overlapping_crossbar() { .line_to(Coord2(0.2, 1.9)) .build(); - let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( - &[ring1, ring2, crossbar1], - 0.01, - ); + let removed = + path_remove_overlapped_points::<_, SimpleBezierPath>(&[ring1, ring2, crossbar1], 0.01); assert!(removed.len() == 5); } @@ -616,10 +608,8 @@ fn remove_overlapped_for_ring_with_crossbar_in_space() { .line_to(Coord2(2.4, 0.9)) .build(); - let removed = path_remove_overlapped_points::<_, SimpleBezierPath>( - &[ring1, ring2, crossbar1], - 0.01, - ); + let removed = + path_remove_overlapped_points::<_, SimpleBezierPath>(&[ring1, ring2, crossbar1], 0.01); assert!(removed.len() == 3); } @@ -722,8 +712,7 @@ fn rectangle_add() { .build(); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -760,8 +749,7 @@ fn rectangle_add_with_shared_point() { .build(); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -798,8 +786,7 @@ fn rectangle_add_with_shared_point_2() { .build(); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -838,8 +825,7 @@ fn rectangle_add_with_shared_point_3() { .build(); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -887,8 +873,7 @@ fn rectangle_add_with_shared_point_4() { println!("{:?}", gp); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -928,8 +913,7 @@ fn rectangle_add_with_shared_point_5() { .build(); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); @@ -967,8 +951,7 @@ fn rectangle_add_with_shared_point_6() { .build(); // Add them - let shared_point = - path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); + let shared_point = path_add::<_, _, SimpleBezierPath>(&[rectangle1], &[rectangle2], 0.01); assert!(shared_point.len() == 1); diff --git a/tests/bezier/path/arithmetic_chain_add.rs b/tests/bezier/path/arithmetic_chain_add.rs index cc3b6afb..4a94dc99 100644 --- a/tests/bezier/path/arithmetic_chain_add.rs +++ b/tests/bezier/path/arithmetic_chain_add.rs @@ -1,8 +1,11 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::*; - -use super::svg::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{ + path_add_chain, path_remove_interior_points, BezierPath, BezierPathBuilder, BezierPathFactory, + GraphPath, SimpleBezierPath, +}; +use flo_curves::{BezierCurve, Coord2, Coordinate, Line}; + +use super::svg::svg_path_string; #[test] fn add_two_overlapping_circles() { diff --git a/tests/bezier/path/arithmetic_complicated_paths.rs b/tests/bezier/path/arithmetic_complicated_paths.rs index e2fe3881..56ec45e7 100644 --- a/tests/bezier/path/arithmetic_complicated_paths.rs +++ b/tests/bezier/path/arithmetic_complicated_paths.rs @@ -1,8 +1,11 @@ -use flo_curves::bezier::path::*; -use flo_curves::debug::*; -use flo_curves::*; +use flo_curves::bezier::path::{ + path_remove_interior_points, BezierPathBuilder, BezierPathFactory, GraphPath, + GraphPathEdgeKind, PathDirection, PathLabel, SimpleBezierPath, +}; +use flo_curves::debug::graph_path_svg_string; +use flo_curves::{BoundingBox, Coord2, Coordinate}; -use super::svg::*; +use super::svg::svg_path_string; #[test] fn remove_interior_points_1() { @@ -420,8 +423,7 @@ fn remove_interior_points_1() { println!("{}", graph_path_svg_string(&merged_path, vec![])); println!("{:?}", svg_path_string(&curve)); - let with_points_removed: Vec = - path_remove_interior_points(&[curve], 0.01); + let with_points_removed: Vec = path_remove_interior_points(&[curve], 0.01); println!( "{:?}", @@ -497,8 +499,7 @@ fn remove_interior_points_1_without_failing_section() { .build(); println!("{:?}", svg_path_string(&curve)); - let with_points_removed: Vec = - path_remove_interior_points(&[curve], 0.01); + let with_points_removed: Vec = path_remove_interior_points(&[curve], 0.01); println!( "{:?}", @@ -1288,8 +1289,7 @@ fn remove_interior_points_complex_2() { .build(); // This path has generated an error that indicates that no result path was generated (unfortunately it seems this version does not produce the error) - let without_interior_points = - path_remove_interior_points::<_, SimpleBezierPath>(&[path], 0.01); + let without_interior_points = path_remove_interior_points::<_, SimpleBezierPath>(&[path], 0.01); /* // Bug appears to be that not all collisions are generated (so two self-collides in a row will generate more points) diff --git a/tests/bezier/path/arithmetic_cut.rs b/tests/bezier/path/arithmetic_cut.rs index 9409102a..10723738 100644 --- a/tests/bezier/path/arithmetic_cut.rs +++ b/tests/bezier/path/arithmetic_cut.rs @@ -1,5 +1,5 @@ -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::bezier::path::{path_cut, BezierPath, BezierPathBuilder, SimpleBezierPath}; +use flo_curves::{BoundingBox, Coord2, Coordinate, Line}; #[test] fn cut_square() { diff --git a/tests/bezier/path/arithmetic_intersect.rs b/tests/bezier/path/arithmetic_intersect.rs index dfb3f6d5..4bf761df 100644 --- a/tests/bezier/path/arithmetic_intersect.rs +++ b/tests/bezier/path/arithmetic_intersect.rs @@ -1,7 +1,13 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::bezier::*; -use flo_curves::line::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{ + path_full_intersect, path_intersect, BezierPath, BezierPathBuilder, BezierPathFactory, + GraphPath, PathDirection, PathLabel, SimpleBezierPath, +}; +use flo_curves::bezier::{ + curve_intersects_curve_clip, BezierCurve, BezierCurveFactory, BoundingBox, Coord2, Coordinate, + Coordinate2D, Coordinate3D, Curve, +}; +use flo_curves::line::{line_to_bezier, Line, Line2D}; use std::f64; use std::iter; @@ -52,8 +58,7 @@ fn full_intersect_two_partially_overlapping_circles() { let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let circle2 = Circle::new(Coord2(7.0, 5.0), 4.0).to_path::(); - let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); + let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 1); @@ -65,8 +70,7 @@ fn full_intersect_two_non_overlapping_circles() { let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let circle2 = Circle::new(Coord2(15.0, 5.0), 4.0).to_path::(); - let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); + let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.is_empty()); assert!(intersection.exterior_paths[0].len() == 1); @@ -78,8 +82,7 @@ fn full_intersect_interior_circles_1() { let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let circle2 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); - let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); + let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].len() == 2); @@ -91,8 +94,7 @@ fn full_intersect_interior_circles_2() { let circle1 = Circle::new(Coord2(5.0, 5.0), 3.5).to_path::(); let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); + let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.intersecting_path.len() == 1); assert!(intersection.exterior_paths[0].is_empty()); @@ -104,8 +106,7 @@ fn fintersect_two_fully_overlapping_circles() { let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let intersection = - path_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); + let intersection = path_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); assert!(intersection.len() == 1); } @@ -115,8 +116,7 @@ fn full_intersect_two_fully_overlapping_circles() { let circle1 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); let circle2 = Circle::new(Coord2(5.0, 5.0), 4.0).to_path::(); - let intersection = - path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); + let intersection = path_full_intersect::<_, _, SimpleBezierPath>(&[circle1], &[circle2], 0.1); println!("{:?}", intersection); @@ -355,14 +355,11 @@ fn repeatedly_full_intersect_circle_f32_intermediate_representation() { ); // Cut the circle via the fragment - let cut_circle = path_full_intersect::<_, _, SimpleBezierPath>( - &[fragment.clone()], - &remaining, - 0.01, - ); + let cut_circle = + path_full_intersect::<_, _, SimpleBezierPath>(&[fragment.clone()], &remaining, 0.01); if cut_circle.exterior_paths[1].len() != 1 { - use flo_curves::debug::*; + use flo_curves::debug::graph_path_svg_string; let mut merged_path = GraphPath::new(); merged_path = merged_path.merge(GraphPath::from_merged_paths( diff --git a/tests/bezier/path/arithmetic_sub.rs b/tests/bezier/path/arithmetic_sub.rs index 1a1c7a53..cf8bc97e 100644 --- a/tests/bezier/path/arithmetic_sub.rs +++ b/tests/bezier/path/arithmetic_sub.rs @@ -1,6 +1,9 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{ + path_sub, BezierPath, BezierPathBuilder, GraphPath, GraphPathEdgeKind, GraphRayCollision, + PathDirection, PathLabel, SimpleBezierPath, +}; +use flo_curves::{BezierCurve, BoundingBox, Coord2, Coordinate, Line}; #[test] fn subtract_circles() { @@ -166,7 +169,7 @@ fn cut_corners() { #[test] fn subtract_triangle_from_partial_circle_graph() { - use flo_curves::debug::*; + use flo_curves::debug::graph_path_svg_string; use std::collections::HashMap; // This regenerates a failing test from arithmetic_intersection: problem seems to be that there are overlapping (or near-overlapping lines) that cause two outer edges when subtracting diff --git a/tests/bezier/path/bounds.rs b/tests/bezier/path/bounds.rs index b2fef8cb..5424f9f8 100644 --- a/tests/bezier/path/bounds.rs +++ b/tests/bezier/path/bounds.rs @@ -1,6 +1,6 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{BezierPath, SimpleBezierPath}; +use flo_curves::{BezierCurve, Coord2, Coordinate, Coordinate2D, Coordinate3D}; #[test] fn circle_path_bounds() { diff --git a/tests/bezier/path/graph_path.rs b/tests/bezier/path/graph_path.rs index 33553f83..ec978ee1 100644 --- a/tests/bezier/path/graph_path.rs +++ b/tests/bezier/path/graph_path.rs @@ -1,6 +1,9 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{ + BezierPath, BezierPathBuilder, BezierPathFactory, GraphEdge, GraphPath, GraphPathEdgeKind, + GraphRayCollision, PathDirection, PathLabel, SimpleBezierPath, +}; +use flo_curves::{BezierCurve, BoundingBox, Coord2, Coordinate, Coordinate2D, Coordinate3D, Line}; use std::f64; @@ -1167,7 +1170,8 @@ fn collide_circles() { // The following intersection point should have one point that leads back into our path let following_intersection = intersection_edges - .iter().find(|edge| is_intersection(edge.end_point_index())) + .iter() + .find(|edge| is_intersection(edge.end_point_index())) .unwrap(); let second_intersection_edges = graph_path .edges_for_point(following_intersection.end_point_index()) diff --git a/tests/bezier/path/intersection.rs b/tests/bezier/path/intersection.rs index 20f230f7..60bedf2a 100644 --- a/tests/bezier/path/intersection.rs +++ b/tests/bezier/path/intersection.rs @@ -1,6 +1,8 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::bezier::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{ + path_intersects_line, path_intersects_path, BezierPath, SimpleBezierPath, +}; +use flo_curves::bezier::{BezierCurve, BoundingBox, Coord2, Coordinate, Curve}; use std::f64; diff --git a/tests/bezier/path/is_clockwise.rs b/tests/bezier/path/is_clockwise.rs index 9644a97a..90530a02 100644 --- a/tests/bezier/path/is_clockwise.rs +++ b/tests/bezier/path/is_clockwise.rs @@ -1,5 +1,5 @@ -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::bezier::path::{BezierPathBuilder, PathWithIsClockwise, SimpleBezierPath}; +use flo_curves::Coord2; #[test] pub fn rectangle_is_clockwise() { diff --git a/tests/bezier/path/path.rs b/tests/bezier/path/path.rs index 695e972c..480f11ee 100644 --- a/tests/bezier/path/path.rs +++ b/tests/bezier/path/path.rs @@ -1,5 +1,5 @@ -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::bezier::path::{BezierPath, BezierPathBuilder, SimpleBezierPath}; +use flo_curves::{BezierCurve, Coord2, Coordinate, Line}; #[test] fn reverse_rectangle() { diff --git a/tests/bezier/path/point.rs b/tests/bezier/path/point.rs index 3a515140..04cf14af 100644 --- a/tests/bezier/path/point.rs +++ b/tests/bezier/path/point.rs @@ -1,6 +1,6 @@ -use flo_curves::arc::*; -use flo_curves::bezier::path::*; -use flo_curves::*; +use flo_curves::arc::Circle; +use flo_curves::bezier::path::{path_contains_point, SimpleBezierPath}; +use flo_curves::Coord2; #[test] fn simple_path_contains_point() { diff --git a/tests/bezier/path/rays.rs b/tests/bezier/path/rays.rs index f587bc66..1e2d2b22 100644 --- a/tests/bezier/path/rays.rs +++ b/tests/bezier/path/rays.rs @@ -1,5 +1,9 @@ -use flo_curves::bezier::path::*; -use flo_curves::bezier::*; +use flo_curves::bezier::path::{ + BezierPath, BezierPathBuilder, BezierPathFactory, GraphPath, SimpleBezierPath, +}; +use flo_curves::bezier::{ + curve_intersects_ray, BezierCurveFactory, BoundingBox, Coord2, Coordinate, Curve, +}; use std::collections::HashMap; diff --git a/tests/bezier/path/svg.rs b/tests/bezier/path/svg.rs index 4a7cb7d9..cbb9ac72 100644 --- a/tests/bezier/path/svg.rs +++ b/tests/bezier/path/svg.rs @@ -1,5 +1,5 @@ -use flo_curves::bezier::path::*; -use flo_curves::geo::*; +use flo_curves::bezier::path::BezierPath; +use flo_curves::geo::{Coordinate2D, Coordinate3D}; use std::fmt::Write; diff --git a/tests/bezier/path/to_curves.rs b/tests/bezier/path/to_curves.rs index b1ed244a..131307d4 100644 --- a/tests/bezier/path/to_curves.rs +++ b/tests/bezier/path/to_curves.rs @@ -1,5 +1,5 @@ -use flo_curves::bezier::path::*; -use flo_curves::bezier::*; +use flo_curves::bezier::path::path_to_curves; +use flo_curves::bezier::{BoundingBox, Coord2, Coordinate, Curve}; #[test] pub fn convert_path_to_bezier_curves() { diff --git a/tests/bezier/section.rs b/tests/bezier/section.rs index cd84beb3..1e22a823 100644 --- a/tests/bezier/section.rs +++ b/tests/bezier/section.rs @@ -1,4 +1,4 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{BezierCurve, BezierCurveFactory, Coord2, Coordinate, Curve}; #[test] fn section_points_match() { diff --git a/tests/bezier/self_intersection.rs b/tests/bezier/self_intersection.rs index 9e6fea50..98cb089d 100644 --- a/tests/bezier/self_intersection.rs +++ b/tests/bezier/self_intersection.rs @@ -1,4 +1,6 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{ + find_self_intersection_point, BezierCurve, BezierCurveFactory, Coord2, Coordinate, Curve, +}; #[test] fn find_simple_self_intersection() { diff --git a/tests/bezier/solve.rs b/tests/bezier/solve.rs index 55e8083c..88d1bc0f 100644 --- a/tests/bezier/solve.rs +++ b/tests/bezier/solve.rs @@ -1,4 +1,6 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{ + basis, solve_basis_for_t, BezierCurve, BezierCurveFactory, Coord2, Curve, +}; #[test] fn basis_solve_middle() { diff --git a/tests/bezier/subdivide.rs b/tests/bezier/subdivide.rs index fd9a31fe..705a9b6e 100644 --- a/tests/bezier/subdivide.rs +++ b/tests/bezier/subdivide.rs @@ -1,4 +1,4 @@ -use super::*; +use super::approx_equal; use flo_curves::bezier; diff --git a/tests/bezier/tangent.rs b/tests/bezier/tangent.rs index d39cc892..32c5d9cb 100644 --- a/tests/bezier/tangent.rs +++ b/tests/bezier/tangent.rs @@ -1,5 +1,5 @@ use flo_curves::bezier; -use flo_curves::*; +use flo_curves::{BezierCurveFactory, Coord2, Coordinate2D, Coordinate3D, Line}; #[test] fn calculate_tangent_for_straight_line() { diff --git a/tests/bezier/walk.rs b/tests/bezier/walk.rs index 938d6e61..cb03d81b 100644 --- a/tests/bezier/walk.rs +++ b/tests/bezier/walk.rs @@ -1,4 +1,7 @@ -use flo_curves::bezier::*; +use flo_curves::bezier::{ + chord_length, curve_length, walk_curve_evenly, walk_curve_unevenly, BezierCurveFactory, Coord2, + Coordinate, Curve, +}; #[test] fn uneven_walk_1() { diff --git a/tests/bounds.rs b/tests/bounds.rs index f39a73d2..7d6a7960 100644 --- a/tests/bounds.rs +++ b/tests/bounds.rs @@ -1,6 +1,6 @@ extern crate flo_curves; -use flo_curves::*; +use flo_curves::{BoundingBox, Bounds, Coord2}; #[test] fn overlapping_rects() { diff --git a/tests/coordinates.rs b/tests/coordinates.rs index a4e2f260..de4b37bd 100644 --- a/tests/coordinates.rs +++ b/tests/coordinates.rs @@ -1,6 +1,6 @@ extern crate flo_curves; -use flo_curves::*; +use flo_curves::{Coord2, Coordinate, Coordinate2DExt}; use std::f64; diff --git a/tests/line/coefficients.rs b/tests/line/coefficients.rs index 40f2e78d..2e882e0a 100644 --- a/tests/line/coefficients.rs +++ b/tests/line/coefficients.rs @@ -1,4 +1,6 @@ -use flo_curves::line::*; +use flo_curves::line::{ + line_coefficients_2d, Coord2, Coordinate, Coordinate2D, Coordinate3D, Line, Line2D, +}; #[test] fn points_on_line_are_on_line_1() { diff --git a/tests/line/intersection.rs b/tests/line/intersection.rs index d52e9d13..eda0d2b9 100644 --- a/tests/line/intersection.rs +++ b/tests/line/intersection.rs @@ -1,4 +1,7 @@ -use flo_curves::line::*; +use flo_curves::line::{ + line_clip_to_bounds, line_intersects_line, line_intersects_ray, ray_intersects_ray, Coord2, + Coordinate, Line2D, +}; #[test] fn intersection_at_0_0() { diff --git a/tests/line/to_curve.rs b/tests/line/to_curve.rs index ef51620d..afa3e552 100644 --- a/tests/line/to_curve.rs +++ b/tests/line/to_curve.rs @@ -1,5 +1,5 @@ -use flo_curves::bezier::*; -use flo_curves::line::*; +use flo_curves::bezier::{BezierCurve, Coord2, Coordinate, Curve}; +use flo_curves::line::{line_to_bezier, Line2D}; #[test] fn convert_line_to_bezier_curve() { diff --git a/tests/sweep.rs b/tests/sweep.rs index 4eec7f49..9a071c57 100644 --- a/tests/sweep.rs +++ b/tests/sweep.rs @@ -1,4 +1,6 @@ -use flo_curves::geo::*; +use flo_curves::geo::{ + sweep_against, sweep_self, BoundingBox, Bounds, Coord2, Coordinate, Coordinate2D, Coordinate3D, +}; use rand::prelude::*; use std::cmp::Ordering;