diff --git a/crates/fj-kernel/src/algorithms/cast_ray/edge.rs b/crates/fj-kernel/src/algorithms/cast_ray/edge.rs deleted file mode 100644 index e7e88a62a..000000000 --- a/crates/fj-kernel/src/algorithms/cast_ray/edge.rs +++ /dev/null @@ -1,29 +0,0 @@ -use fj_math::Segment; - -use crate::objects::{CurveKind, Edge}; - -use super::CastRay; - -impl CastRay<2> for Edge { - type Hit = as CastRay<2>>::Hit; - - fn cast_ray( - &self, - ray: super::HorizontalRayToTheRight<2>, - ) -> Option { - let line = match self.curve().kind() { - CurveKind::Line(line) => line, - CurveKind::Circle(_) => { - todo!("Casting rays against circles is not supported yet") - } - }; - - let points = self.vertices().expect_vertices().map(|vertex| { - let point = vertex.position(); - line.point_from_line_coords(point) - }); - let segment = Segment::from_points(points); - - segment.cast_ray(ray) - } -} diff --git a/crates/fj-kernel/src/algorithms/cast_ray/mod.rs b/crates/fj-kernel/src/algorithms/cast_ray/mod.rs deleted file mode 100644 index 707355d13..000000000 --- a/crates/fj-kernel/src/algorithms/cast_ray/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! Ray casting - -mod edge; -mod segment; - -pub use self::segment::RaySegmentHit; - -use fj_math::Point; - -/// Implemented by types that support ray casting -/// -/// # Implementation Note -/// -/// This is basically a more limited version of [`Intersect`]. It probably makes -/// sense to migrate all of this trait's implementations to [`Intersect`] and -/// remove this trait. -/// -/// [`Intersect`]: super::intersect::Intersect -pub trait CastRay { - /// The type that describes a hit of the ray on the implementing type - type Hit; - - /// Cast a ray against `self` - fn cast_ray(&self, ray: HorizontalRayToTheRight) -> Option; -} - -/// A horizontal ray that goes to the right -/// -/// For in-kernel use, we don't need anything more flexible, and being exactly -/// horizontal simplifies some calculations. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct HorizontalRayToTheRight { - /// The point where the ray originates - pub origin: Point, -} - -impl From

for HorizontalRayToTheRight -where - P: Into>, -{ - fn from(point: P) -> Self { - Self { - origin: point.into(), - } - } -} diff --git a/crates/fj-kernel/src/algorithms/intersect/face_point.rs b/crates/fj-kernel/src/algorithms/intersect/face_point.rs index acc873221..fdfe5f975 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_point.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_point.rs @@ -2,12 +2,11 @@ use fj_math::Point; -use crate::{ - algorithms::cast_ray::{CastRay, HorizontalRayToTheRight, RaySegmentHit}, - objects::Face, -}; +use crate::objects::Face; -use super::Intersect; +use super::{ + ray_segment::RaySegmentIntersection, HorizontalRayToTheRight, Intersect, +}; impl Intersect for (&Face, &Point<2>) { type Intersection = FacePointIntersection; @@ -29,23 +28,23 @@ impl Intersect for (&Face, &Point<2>) { .edges() .last() .copied() - .and_then(|edge| edge.cast_ray(ray)); + .and_then(|edge| (&ray, &edge).intersect()); for edge in cycle.edges() { - let hit = edge.cast_ray(ray); + let hit = (&ray, edge).intersect(); let count_hit = match (hit, previous_hit) { - (Some(RaySegmentHit::Segment), _) => { + (Some(RaySegmentIntersection::Segment), _) => { // We're hitting a segment right-on. Clear case. true } ( - Some(RaySegmentHit::UpperVertex), - Some(RaySegmentHit::LowerVertex), + Some(RaySegmentIntersection::UpperVertex), + Some(RaySegmentIntersection::LowerVertex), ) | ( - Some(RaySegmentHit::LowerVertex), - Some(RaySegmentHit::UpperVertex), + Some(RaySegmentIntersection::LowerVertex), + Some(RaySegmentIntersection::UpperVertex), ) => { // If we're hitting a vertex, only count it if we've hit // the other kind of vertex right before. @@ -62,7 +61,7 @@ impl Intersect for (&Face, &Point<2>) { // passing through anything. true } - (Some(RaySegmentHit::Parallel), _) => { + (Some(RaySegmentIntersection::Parallel), _) => { // A parallel edge must be completely ignored. Its // presence won't change anything, so we can treat it as // if it wasn't there, and its neighbors were connected diff --git a/crates/fj-kernel/src/algorithms/intersect/mod.rs b/crates/fj-kernel/src/algorithms/intersect/mod.rs index 9cb251191..b20889f2e 100644 --- a/crates/fj-kernel/src/algorithms/intersect/mod.rs +++ b/crates/fj-kernel/src/algorithms/intersect/mod.rs @@ -1,6 +1,8 @@ //! Intersection algorithms pub mod face_point; +pub mod ray_edge; +pub mod ray_segment; mod curve_edge; mod curve_face; @@ -8,6 +10,8 @@ mod face_face; mod line_segment; mod surface_surface; +use fj_math::Point; + pub use self::{ curve_edge::CurveEdgeIntersection, curve_face::{CurveFaceIntersection, CurveFaceIntersectionInterval}, @@ -29,3 +33,24 @@ pub trait Intersect { /// Compute the intersection between a tuple of objects fn intersect(self) -> Option; } + +/// A horizontal ray that goes to the right +/// +/// For in-kernel use, we don't need anything more flexible, and being exactly +/// horizontal simplifies some calculations. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct HorizontalRayToTheRight { + /// The point where the ray originates + pub origin: Point, +} + +impl From

for HorizontalRayToTheRight +where + P: Into>, +{ + fn from(point: P) -> Self { + Self { + origin: point.into(), + } + } +} diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs b/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs new file mode 100644 index 000000000..784300f1a --- /dev/null +++ b/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs @@ -0,0 +1,33 @@ +//! Intersection between a ray and an edge in 2D + +use fj_math::Segment; + +use crate::{ + algorithms::intersect::{HorizontalRayToTheRight, Intersect}, + objects::{CurveKind, Edge}, +}; + +use super::ray_segment::RaySegmentIntersection; + +impl Intersect for (&HorizontalRayToTheRight<2>, &Edge) { + type Intersection = RaySegmentIntersection; + + fn intersect(self) -> Option { + let (ray, edge) = self; + + let line = match edge.curve().kind() { + CurveKind::Line(line) => line, + CurveKind::Circle(_) => { + todo!("Casting rays against circles is not supported yet") + } + }; + + let points = edge.vertices().expect_vertices().map(|vertex| { + let point = vertex.position(); + line.point_from_line_coords(point) + }); + let segment = Segment::from_points(points); + + (ray, &segment).intersect() + } +} diff --git a/crates/fj-kernel/src/algorithms/cast_ray/segment.rs b/crates/fj-kernel/src/algorithms/intersect/ray_segment.rs similarity index 61% rename from crates/fj-kernel/src/algorithms/cast_ray/segment.rs rename to crates/fj-kernel/src/algorithms/intersect/ray_segment.rs index e0fcdebe6..a864fd12a 100644 --- a/crates/fj-kernel/src/algorithms/cast_ray/segment.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_segment.rs @@ -1,15 +1,16 @@ +//! Intersection between a ray and a line segment in 2D + use fj_math::Segment; -use super::{CastRay, HorizontalRayToTheRight}; +use super::{HorizontalRayToTheRight, Intersect}; + +impl Intersect for (&HorizontalRayToTheRight<2>, &Segment<2>) { + type Intersection = RaySegmentIntersection; -impl CastRay<2> for Segment<2> { - type Hit = RaySegmentHit; + fn intersect(self) -> Option { + let (ray, segment) = self; - fn cast_ray( - &self, - ray: HorizontalRayToTheRight<2>, - ) -> Option { - let [a, b] = self.points(); + let [a, b] = segment.points(); let [lower, upper] = if a.v <= b.v { [a, b] } else { [b, a] }; let right = if a.u > b.u { a } else { b }; @@ -29,7 +30,7 @@ impl CastRay<2> for Segment<2> { return None; } - return Some(RaySegmentHit::Parallel); + return Some(RaySegmentIntersection::Parallel); } let pa = robust::Coord { @@ -49,22 +50,22 @@ impl CastRay<2> for Segment<2> { // ray starts on the line or left of it if ray.origin.v == upper.v { - return Some(RaySegmentHit::UpperVertex); + return Some(RaySegmentIntersection::UpperVertex); } if ray.origin.v == lower.v { - return Some(RaySegmentHit::LowerVertex); + return Some(RaySegmentIntersection::LowerVertex); } - return Some(RaySegmentHit::Segment); + return Some(RaySegmentIntersection::Segment); } None } } -/// A hit between a ray and a line segment +/// An intersection between a ray and a line segment #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum RaySegmentHit { +pub enum RaySegmentIntersection { /// The ray hit the segment itself Segment, @@ -82,9 +83,9 @@ pub enum RaySegmentHit { mod tests { use fj_math::Segment; - use crate::algorithms::cast_ray::CastRay; + use crate::algorithms::intersect::Intersect; - use super::{HorizontalRayToTheRight, RaySegmentHit}; + use super::{HorizontalRayToTheRight, RaySegmentIntersection}; #[test] fn hits_segment_right() { @@ -94,11 +95,11 @@ mod tests { let above = Segment::from([[1., 3.], [1., 4.]]); let same_level = Segment::from([[1., 1.], [1., 3.]]); - assert!(below.cast_ray(ray).is_none()); - assert!(above.cast_ray(ray).is_none()); + assert!((&ray, &below).intersect().is_none()); + assert!((&ray, &above).intersect().is_none()); assert!(matches!( - same_level.cast_ray(ray), - Some(RaySegmentHit::Segment) + (&ray, &same_level).intersect(), + Some(RaySegmentIntersection::Segment) )); } @@ -107,7 +108,7 @@ mod tests { let ray = HorizontalRayToTheRight::from([1., 2.]); let same_level = Segment::from([[0., 1.], [0., 3.]]); - assert!(same_level.cast_ray(ray).is_none()); + assert!((&ray, &same_level).intersect().is_none()); } #[test] @@ -120,18 +121,18 @@ mod tests { let hit_upper = Segment::from([[0., 0.], [2., 1.]]); let hit_lower = Segment::from([[0., 2.], [2., 1.]]); - assert!(no_hit.cast_ray(ray).is_none()); + assert!((&ray, &no_hit).intersect().is_none()); assert!(matches!( - hit_segment.cast_ray(ray), - Some(RaySegmentHit::Segment) + (&ray, &hit_segment).intersect(), + Some(RaySegmentIntersection::Segment) )); assert!(matches!( - hit_upper.cast_ray(ray), - Some(RaySegmentHit::UpperVertex), + (&ray, &hit_upper).intersect(), + Some(RaySegmentIntersection::UpperVertex), )); assert!(matches!( - hit_lower.cast_ray(ray), - Some(RaySegmentHit::LowerVertex), + (&ray, &hit_lower).intersect(), + Some(RaySegmentIntersection::LowerVertex), )); } @@ -144,16 +145,16 @@ mod tests { let hit_lower = Segment::from([[1., 1.], [2., 2.]]); assert!(matches!( - hit_segment.cast_ray(ray), - Some(RaySegmentHit::Segment) + (&ray, &hit_segment).intersect(), + Some(RaySegmentIntersection::Segment) )); assert!(matches!( - hit_upper.cast_ray(ray), - Some(RaySegmentHit::UpperVertex), + (&ray, &hit_upper).intersect(), + Some(RaySegmentIntersection::UpperVertex), )); assert!(matches!( - hit_lower.cast_ray(ray), - Some(RaySegmentHit::LowerVertex), + (&ray, &hit_lower).intersect(), + Some(RaySegmentIntersection::LowerVertex), )); } @@ -165,11 +166,14 @@ mod tests { let overlapping = Segment::from([[1., 0.], [3., 0.]]); let right = Segment::from([[3., 0.], [4., 0.]]); - assert!(left.cast_ray(ray).is_none()); + assert!((&ray, &left).intersect().is_none()); + assert!(matches!( + (&ray, &overlapping).intersect(), + Some(RaySegmentIntersection::Parallel) + )); assert!(matches!( - overlapping.cast_ray(ray), - Some(RaySegmentHit::Parallel) + (&ray, &right).intersect(), + Some(RaySegmentIntersection::Parallel) )); - assert!(matches!(right.cast_ray(ray), Some(RaySegmentHit::Parallel))); } } diff --git a/crates/fj-kernel/src/algorithms/mod.rs b/crates/fj-kernel/src/algorithms/mod.rs index 98c7a549e..b7f7b63ce 100644 --- a/crates/fj-kernel/src/algorithms/mod.rs +++ b/crates/fj-kernel/src/algorithms/mod.rs @@ -9,7 +9,6 @@ mod sweep; mod transform; mod triangulate; -pub mod cast_ray; pub mod intersect; pub use self::{ diff --git a/crates/fj-kernel/src/algorithms/triangulate/polygon.rs b/crates/fj-kernel/src/algorithms/triangulate/polygon.rs index 081f076d0..563512e7e 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/polygon.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/polygon.rs @@ -2,7 +2,9 @@ use fj_interop::debug::{DebugInfo, TriangleEdgeCheck}; use fj_math::{Point, PolyChain, Segment}; use crate::{ - algorithms::cast_ray::{CastRay, HorizontalRayToTheRight, RaySegmentHit}, + algorithms::intersect::{ + ray_segment::RaySegmentIntersection, HorizontalRayToTheRight, Intersect, + }, objects::Surface, }; @@ -161,24 +163,26 @@ impl Polygon { // first segment. The logic in the loop properly takes care of that, // as long as we initialize the `previous_hit` variable with the // result of the last segment. - let mut previous_hit = - edges.last().copied().and_then(|edge| edge.cast_ray(ray)); + let mut previous_hit = edges + .last() + .copied() + .and_then(|edge| (&ray, &edge).intersect()); for edge in edges { - let hit = edge.cast_ray(ray); + let hit = (&ray, &edge).intersect(); let count_hit = match (hit, previous_hit) { - (Some(RaySegmentHit::Segment), _) => { + (Some(RaySegmentIntersection::Segment), _) => { // We're hitting a segment right-on. Clear case. true } ( - Some(RaySegmentHit::UpperVertex), - Some(RaySegmentHit::LowerVertex), + Some(RaySegmentIntersection::UpperVertex), + Some(RaySegmentIntersection::LowerVertex), ) | ( - Some(RaySegmentHit::LowerVertex), - Some(RaySegmentHit::UpperVertex), + Some(RaySegmentIntersection::LowerVertex), + Some(RaySegmentIntersection::UpperVertex), ) => { // If we're hitting a vertex, only count it if we've hit // the other kind of vertex right before. @@ -195,7 +199,7 @@ impl Polygon { // passing through anything. true } - (Some(RaySegmentHit::Parallel), _) => { + (Some(RaySegmentIntersection::Parallel), _) => { // A parallel edge must be completely ignored. Its // presence won't change anything, so we can treat it as // if it wasn't there, and its neighbors were connected