From 3b232c70879c3c080cb0e9c0eb0d789e7c30a19b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:38:15 +0200 Subject: [PATCH 1/7] Add `CurveFaceIntersections` --- .../src/algorithms/intersection/curve_face.rs | 15 ++++++++++++--- .../fj-kernel/src/algorithms/intersection/mod.rs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 267d4bd67..5571a45b1 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -3,10 +3,14 @@ use parry2d_f64::query::{Ray, RayCast}; use crate::objects::{Curve, Face}; +/// The intersections between a [`Curve`] and a [`Face`], in curve coordinates +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct CurveFaceIntersections(Vec<[Scalar; 2]>); + /// Determine the intersection between a [`Curve`] and a [`Face`] /// /// Returns a list of intersections in curve coordinates. -pub fn curve_face(curve: &Curve<2>, face: &Face) -> Vec<[Scalar; 2]> { +pub fn curve_face(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { let line = match curve { Curve::Line(line) => line, _ => todo!("Curve-face intersection only supports lines"), @@ -75,13 +79,15 @@ pub fn curve_face(curve: &Curve<2>, face: &Face) -> Vec<[Scalar; 2]> { // Can be cleaned up, once `array_chunks` is stable: // https://doc.rust-lang.org/std/primitive.slice.html#method.array_chunks - intersections + let intersections = intersections .chunks(2) .map(|chunk| { // Can't panic, as we passed `2` to `windows`. [chunk[0], chunk[1]] }) - .collect() + .collect(); + + CurveFaceIntersections(intersections) } #[cfg(test)] @@ -90,6 +96,8 @@ mod tests { use crate::objects::{Curve, Face, Surface}; + use super::CurveFaceIntersections; + #[test] fn curve_face() { let curve = Curve::Line(Line { @@ -121,6 +129,7 @@ mod tests { .into_iter() .map(|interval: [f64; 2]| interval.map(Scalar::from)) .collect(); + let expected = CurveFaceIntersections(expected); assert_eq!(super::curve_face(&curve, &face), expected); } } diff --git a/crates/fj-kernel/src/algorithms/intersection/mod.rs b/crates/fj-kernel/src/algorithms/intersection/mod.rs index 0a5b0b0ac..511b93f08 100644 --- a/crates/fj-kernel/src/algorithms/intersection/mod.rs +++ b/crates/fj-kernel/src/algorithms/intersection/mod.rs @@ -5,7 +5,7 @@ mod line_segment; mod surface_surface; pub use self::{ - curve_face::curve_face, + curve_face::{curve_face, CurveFaceIntersections}, line_segment::{line_segment, LineSegmentIntersection}, surface_surface::surface_surface, }; From 98576a0a6b01ad7915b8bc6a05de60466446ebd8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:42:04 +0200 Subject: [PATCH 2/7] Convert free function into method --- .../src/algorithms/intersection/curve_face.rs | 146 +++++++++--------- .../src/algorithms/intersection/mod.rs | 2 +- 2 files changed, 77 insertions(+), 71 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 5571a45b1..06414ca89 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -7,87 +7,93 @@ use crate::objects::{Curve, Face}; #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct CurveFaceIntersections(Vec<[Scalar; 2]>); -/// Determine the intersection between a [`Curve`] and a [`Face`] -/// -/// Returns a list of intersections in curve coordinates. -pub fn curve_face(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { - let line = match curve { - Curve::Line(line) => line, - _ => todo!("Curve-face intersection only supports lines"), - }; - - let face_as_polygon = face - .exteriors() - .chain(face.interiors()) - .flat_map(|cycle| { - let edges: Vec<_> = cycle.edges().collect(); - edges - }) - .map(|edge| { - let line = match edge.curve.local() { - Curve::Line(line) => line, - _ => todo!("Curve-face intersection only supports polygons"), - }; +impl CurveFaceIntersections { + /// Determine the intersection between a [`Curve`] and a [`Face`] + /// + /// Returns a list of intersections in curve coordinates. + pub fn curve_face(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { + let line = match curve { + Curve::Line(line) => line, + _ => todo!("Curve-face intersection only supports lines"), + }; - let vertices = match edge.vertices() { - Some(vertices) => vertices, - None => todo!( - "Curve-face intersection does not support faces with \ + let face_as_polygon = face + .exteriors() + .chain(face.interiors()) + .flat_map(|cycle| { + let edges: Vec<_> = cycle.edges().collect(); + edges + }) + .map(|edge| { + let line = match edge.curve.local() { + Curve::Line(line) => line, + _ => { + todo!("Curve-face intersection only supports polygons") + } + }; + + let vertices = match edge.vertices() { + Some(vertices) => vertices, + None => todo!( + "Curve-face intersection does not support faces with \ continuous edges" - ), - }; + ), + }; - (line, vertices) - }); + (line, vertices) + }); - let mut intersections = Vec::new(); + let mut intersections = Vec::new(); - for (edge_line, vertices) in face_as_polygon { - let vertices = vertices - .map(|vertex| edge_line.point_from_line_coords(vertex.position())); - let segment = Segment::from_points(vertices); + for (edge_line, vertices) in face_as_polygon { + let vertices = vertices.map(|vertex| { + edge_line.point_from_line_coords(vertex.position()) + }); + let segment = Segment::from_points(vertices); - let ray = Ray { - origin: line.origin.to_na(), - dir: line.direction.to_na(), - }; - let ray_inv = Ray { - origin: line.origin.to_na(), - dir: -line.direction.to_na(), - }; + let ray = Ray { + origin: line.origin.to_na(), + dir: line.direction.to_na(), + }; + let ray_inv = Ray { + origin: line.origin.to_na(), + dir: -line.direction.to_na(), + }; - let result = - segment - .to_parry() - .cast_local_ray(&ray, f64::INFINITY, false); - let result_inv = - segment - .to_parry() - .cast_local_ray(&ray_inv, f64::INFINITY, false); - - if let Some(result) = result { - intersections.push(Scalar::from(result)); - } - if let Some(result_inv) = result_inv { - intersections.push(-Scalar::from(result_inv)); + let result = + segment + .to_parry() + .cast_local_ray(&ray, f64::INFINITY, false); + let result_inv = segment.to_parry().cast_local_ray( + &ray_inv, + f64::INFINITY, + false, + ); + + if let Some(result) = result { + intersections.push(Scalar::from(result)); + } + if let Some(result_inv) = result_inv { + intersections.push(-Scalar::from(result_inv)); + } } - } - assert!(intersections.len() % 2 == 0); + assert!(intersections.len() % 2 == 0); - intersections.sort(); + intersections.sort(); - // Can be cleaned up, once `array_chunks` is stable: - // https://doc.rust-lang.org/std/primitive.slice.html#method.array_chunks - let intersections = intersections - .chunks(2) - .map(|chunk| { - // Can't panic, as we passed `2` to `windows`. - [chunk[0], chunk[1]] - }) - .collect(); + // Can be cleaned up, once `array_chunks` is stable: + // https://doc.rust-lang.org/std/primitive.slice.html#method.array_chunks + let intersections = intersections + .chunks(2) + .map(|chunk| { + // Can't panic, as we passed `2` to `windows`. + [chunk[0], chunk[1]] + }) + .collect(); - CurveFaceIntersections(intersections) + CurveFaceIntersections(intersections) + } } #[cfg(test)] @@ -130,6 +136,6 @@ mod tests { .map(|interval: [f64; 2]| interval.map(Scalar::from)) .collect(); let expected = CurveFaceIntersections(expected); - assert_eq!(super::curve_face(&curve, &face), expected); + assert_eq!(CurveFaceIntersections::curve_face(&curve, &face), expected); } } diff --git a/crates/fj-kernel/src/algorithms/intersection/mod.rs b/crates/fj-kernel/src/algorithms/intersection/mod.rs index 511b93f08..dab421082 100644 --- a/crates/fj-kernel/src/algorithms/intersection/mod.rs +++ b/crates/fj-kernel/src/algorithms/intersection/mod.rs @@ -5,7 +5,7 @@ mod line_segment; mod surface_surface; pub use self::{ - curve_face::{curve_face, CurveFaceIntersections}, + curve_face::CurveFaceIntersections, line_segment::{line_segment, LineSegmentIntersection}, surface_surface::surface_surface, }; From 2ffc1cdfd0bb485abd1dc02eff417740a684d2e4 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:42:27 +0200 Subject: [PATCH 3/7] Make method name more clear --- crates/fj-kernel/src/algorithms/intersection/curve_face.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 06414ca89..b3b1ad55a 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -11,7 +11,7 @@ impl CurveFaceIntersections { /// Determine the intersection between a [`Curve`] and a [`Face`] /// /// Returns a list of intersections in curve coordinates. - pub fn curve_face(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { + pub fn compute(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { let line = match curve { Curve::Line(line) => line, _ => todo!("Curve-face intersection only supports lines"), @@ -136,6 +136,6 @@ mod tests { .map(|interval: [f64; 2]| interval.map(Scalar::from)) .collect(); let expected = CurveFaceIntersections(expected); - assert_eq!(CurveFaceIntersections::curve_face(&curve, &face), expected); + assert_eq!(CurveFaceIntersections::compute(&curve, &face), expected); } } From 1c2f1f8cfd1271e92b51dc6b0896fe5f5aa86e50 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:43:24 +0200 Subject: [PATCH 4/7] Update doc comment --- crates/fj-kernel/src/algorithms/intersection/curve_face.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index b3b1ad55a..45fc4463b 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -8,9 +8,7 @@ use crate::objects::{Curve, Face}; pub struct CurveFaceIntersections(Vec<[Scalar; 2]>); impl CurveFaceIntersections { - /// Determine the intersection between a [`Curve`] and a [`Face`] - /// - /// Returns a list of intersections in curve coordinates. + /// Compute the intersections between a [`Curve`] and a [`Face`] pub fn compute(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { let line = match curve { Curve::Line(line) => line, From 385780c931a4f1a8aaf5104f82125f2d9290306a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:43:40 +0200 Subject: [PATCH 5/7] Refactor --- crates/fj-kernel/src/algorithms/intersection/curve_face.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 45fc4463b..748a0b3e5 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -9,7 +9,7 @@ pub struct CurveFaceIntersections(Vec<[Scalar; 2]>); impl CurveFaceIntersections { /// Compute the intersections between a [`Curve`] and a [`Face`] - pub fn compute(curve: &Curve<2>, face: &Face) -> CurveFaceIntersections { + pub fn compute(curve: &Curve<2>, face: &Face) -> Self { let line = match curve { Curve::Line(line) => line, _ => todo!("Curve-face intersection only supports lines"), From 4552ac5f2da1741ba49438fdb835ea782f6092da Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:44:02 +0200 Subject: [PATCH 6/7] Update test name --- crates/fj-kernel/src/algorithms/intersection/curve_face.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 748a0b3e5..bd9341371 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -103,7 +103,7 @@ mod tests { use super::CurveFaceIntersections; #[test] - fn curve_face() { + fn compute() { let curve = Curve::Line(Line { origin: Point::from([-3., 0.]), direction: Vector::from([1., 0.]), From 9cf47eab5a3787935c52e0b1b4ecce2f3e104c53 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 13 Jul 2022 15:50:44 +0200 Subject: [PATCH 7/7] Add `CurveFaceIntersections::from_intervals` --- .../src/algorithms/intersection/curve_face.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index bd9341371..423487423 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -8,6 +8,19 @@ use crate::objects::{Curve, Face}; pub struct CurveFaceIntersections(Vec<[Scalar; 2]>); impl CurveFaceIntersections { + /// Create a new instance from the intersection intervals + /// + /// This method is useful for test code. + pub fn from_intervals( + intervals: impl IntoIterator; 2]>, + ) -> Self { + let intervals = intervals + .into_iter() + .map(|interval| interval.map(Into::into)) + .collect(); + Self(intervals) + } + /// Compute the intersections between a [`Curve`] and a [`Face`] pub fn compute(curve: &Curve<2>, face: &Face) -> Self { let line = match curve { @@ -96,7 +109,7 @@ impl CurveFaceIntersections { #[cfg(test)] mod tests { - use fj_math::{Line, Point, Scalar, Vector}; + use fj_math::{Line, Point, Vector}; use crate::objects::{Curve, Face, Surface}; @@ -129,11 +142,8 @@ mod tests { .with_interior_polygon(interior) .build(); - let expected: Vec<_> = [[1., 2.], [4., 5.]] - .into_iter() - .map(|interval: [f64; 2]| interval.map(Scalar::from)) - .collect(); - let expected = CurveFaceIntersections(expected); + let expected = + CurveFaceIntersections::from_intervals([[1., 2.], [4., 5.]]); assert_eq!(CurveFaceIntersections::compute(&curve, &face), expected); } }