diff --git a/crates/fj-kernel/src/algorithms/intersection/mod.rs b/crates/fj-kernel/src/algorithms/intersection/mod.rs index c79e801bb..df03b6abd 100644 --- a/crates/fj-kernel/src/algorithms/intersection/mod.rs +++ b/crates/fj-kernel/src/algorithms/intersection/mod.rs @@ -9,5 +9,5 @@ pub use self::{ curve_edge::CurveEdgeIntersection, curve_face::{CurveFaceIntersection, CurveFaceIntersectionList}, line_segment::LineSegmentIntersection, - surface_surface::surface_surface, + surface_surface::SurfaceSurfaceIntersection, }; diff --git a/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs index 5b771d384..820cca925 100644 --- a/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs @@ -2,49 +2,61 @@ use fj_math::{Line, Point, Scalar, Vector}; use crate::objects::{Curve, Surface}; -/// Test intersection between two surfaces -pub fn surface_surface( - a: &Surface, - b: &Surface, -) -> Option<(Curve<2>, Curve<2>, Curve<3>)> { - // Algorithm from Real-Time Collision Detection by Christer Ericson. See - // section 5.4.4, Intersection of Two Planes. - // - // Adaptations were made to get the intersection curves in local coordinates - // for each surface. - - let a_parametric = PlaneParametric::extract_from_surface(a); - let b_parametric = PlaneParametric::extract_from_surface(b); - - let a = PlaneConstantNormal::from_parametric_plane(&a_parametric); - let b = PlaneConstantNormal::from_parametric_plane(&b_parametric); - - let direction = a.normal.cross(&b.normal); - - let denom = direction.dot(&direction); - if denom == Scalar::ZERO { - // Comparing `denom` against zero looks fishy. It's probably better to - // compare it against an epsilon value, but I don't know how large that - // epsilon should be. +/// The intersection between two surfaces +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct SurfaceSurfaceIntersection { + /// The intersection curves, in the coordinates of the input surfaces + pub local_intersection_curves: [Curve<2>; 2], + + /// The intersection curve, in global coordinates + pub global_intersection_curve: Curve<3>, +} + +impl SurfaceSurfaceIntersection { + /// Compute the intersection between two surfaces + pub fn compute(a: &Surface, b: &Surface) -> Option { + // Algorithm from Real-Time Collision Detection by Christer Ericson. See + // section 5.4.4, Intersection of Two Planes. // - // I'll just leave it like that, until we had the opportunity to collect - // some experience with this code. - // - @hannobraun - return None; - } + // Adaptations were made to get the intersection curves in local + // coordinates for each surface. + + let a_parametric = PlaneParametric::extract_from_surface(a); + let b_parametric = PlaneParametric::extract_from_surface(b); + + let a = PlaneConstantNormal::from_parametric_plane(&a_parametric); + let b = PlaneConstantNormal::from_parametric_plane(&b_parametric); + + let direction = a.normal.cross(&b.normal); + + let denom = direction.dot(&direction); + if denom == Scalar::ZERO { + // Comparing `denom` against zero looks fishy. It's probably better + // to compare it against an epsilon value, but I don't know how + // large that epsilon should be. + // + // I'll just leave it like that, until we had the opportunity to + // collect some experience with this code. + // - @hannobraun + return None; + } - let origin = (b.normal * a.distance - a.normal * b.distance) - .cross(&direction) - / denom; - let origin = Point { coords: origin }; + let origin = (b.normal * a.distance - a.normal * b.distance) + .cross(&direction) + / denom; + let origin = Point { coords: origin }; - let line = Line { origin, direction }; + let line = Line { origin, direction }; - let curve_a = project_line_into_plane(&line, &a_parametric); - let curve_b = project_line_into_plane(&line, &b_parametric); - let curve_global = Curve::Line(Line { origin, direction }); + let curve_a = project_line_into_plane(&line, &a_parametric); + let curve_b = project_line_into_plane(&line, &b_parametric); + let curve_global = Curve::Line(Line { origin, direction }); - Some((curve_a, curve_b, curve_global)) + Some(Self { + local_intersection_curves: [curve_a, curve_b], + global_intersection_curve: curve_global, + }) + } } /// A plane in parametric form @@ -134,7 +146,7 @@ mod tests { objects::{Curve, Surface}, }; - use super::surface_surface; + use super::SurfaceSurfaceIntersection; #[test] fn plane_plane() { @@ -142,9 +154,9 @@ mod tests { let xz = Surface::xz_plane(); // Coincident and parallel planes don't have an intersection curve. - assert_eq!(surface_surface(&xy, &xy), None); + assert_eq!(SurfaceSurfaceIntersection::compute(&xy, &xy), None); assert_eq!( - surface_surface( + SurfaceSurfaceIntersection::compute( &xy, &xy.transform(&Transform::translation([0., 0., 1.])) ), @@ -156,8 +168,11 @@ mod tests { let expected_global = Curve::x_axis(); assert_eq!( - surface_surface(&xy, &xz), - Some((expected_xy, expected_xz, expected_global)) + SurfaceSurfaceIntersection::compute(&xy, &xz), + Some(SurfaceSurfaceIntersection { + local_intersection_curves: [expected_xy, expected_xz], + global_intersection_curve: expected_global, + }) ); } }