1- use core:: fmt:: Debug ;
1+ use core:: fmt:: { Debug , Formatter } ;
22
3- #[ cfg( feature = "std" ) ]
4- use retrofire_core:: geom:: Sphere ;
53use retrofire_core:: {
6- geom:: { Plane3 , Ray , Ray3 } ,
7- math:: { Point3 , vec3} ,
4+ geom:: { Edge , Line2 , Plane3 , Ray , Ray2 , Ray3 } ,
5+ mat,
6+ math:: { ApproxEq , Mat2 , Point2 , Point3 , pt2, vec3} ,
87 render:: scene:: BBox ,
98} ;
109
11- /// Trait for calculating whether and at which points two objects intersect.
10+ #[ cfg( feature = "std" ) ]
11+ use retrofire_core:: geom:: Sphere ;
12+
13+ /// Trait for finding intersection points of geometric objects.
1214pub trait Intersect < T > {
1315 /// The result of an intersection test.
1416 type Result ;
@@ -20,7 +22,48 @@ pub trait Intersect<T> {
2022 fn intersect ( & self , other : & T ) -> Self :: Result ;
2123}
2224
23- type RayIntersect3 < B > = Option < ( f32 , Point3 < B > ) > ;
25+ pub type RayIntersect3 < B > = Option < ( f32 , Point3 < B > ) > ;
26+ pub type RayIntersect2 < B > = Option < ( f32 , Point2 < B > ) > ;
27+
28+ #[ derive( Copy , Clone , PartialEq ) ]
29+ pub enum LineIntersect < B > {
30+ /// Unique intersection point.
31+ Point ( Point2 < B > ) ,
32+ /// Line is coincident with the intersecting object.
33+ Coincident ,
34+ }
35+
36+ //
37+ // Inherent impls
38+ //
39+
40+ impl < B > LineIntersect < B > {
41+ /// Returns the intersection point if `self` is a `LineIntersect::Point`,
42+ /// `None` otherwise.
43+ pub fn point ( & self ) -> Option < Point2 < B > > {
44+ match self {
45+ LineIntersect :: Point ( p) => Some ( * p) ,
46+ LineIntersect :: Coincident => None ,
47+ }
48+ }
49+ }
50+
51+ //
52+ // Trait impls
53+ //
54+
55+ impl < B : Debug + Default > Debug for LineIntersect < B > {
56+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> core:: fmt:: Result {
57+ match self {
58+ Self :: Point ( p) => write ! ( f, "Point({:?})" , p) ,
59+ Self :: Coincident => f. write_str ( "Coincident" ) ,
60+ }
61+ }
62+ }
63+
64+ //
65+ // 3D Intersect impls
66+ //
2467
2568impl < B > Intersect < Plane3 < B > > for Ray3 < B > {
2669 type Result = RayIntersect3 < B > ;
@@ -243,6 +286,215 @@ impl<B> Intersect<Sphere<B>> for Ray3<B> {
243286 }
244287}
245288
289+ //
290+ // 2D intersection
291+ //
292+
293+ impl < B > Intersect < Self > for Line2 < B > {
294+ type Result = Option < LineIntersect < B > > ;
295+
296+ /// Computes the intersection point of `self` and another 2-line.
297+ ///
298+ /// If the lines are parallel but not coincident, returns `None`. Otherwise,
299+ /// if the lines are coincident, returns `Some(LineIntersect::Coincident)`.
300+ /// Otherwise, returns `Some(LineIntersect::Point(p))`, where `p` is the
301+ /// unique intersection point.
302+ ///
303+ /// # Examples
304+ /// ```
305+ /// use retrofire_core::{
306+ /// assert_approx_eq, geom::{Ray, Line2}, math::{pt2, vec2},
307+ /// };
308+ /// use retrofire_geom::{Intersect, isect::LineIntersect::*};
309+ ///
310+ /// let horiz = Line2::<()>::from(Ray(pt2(0.0, 2.0), vec2(1.0, 0.0)));
311+ /// let vert = Line2::<()>::from(Ray(pt2(3.0, 0.0), vec2(0.0, 1.0)));
312+ /// assert_eq!(horiz.intersect(&vert), Some(Point(pt2(3.0, 2.0))));
313+ ///
314+ /// let horiz2 = Line2::<()>::from(Ray(pt2(0.0, 3.0), vec2(1.0, 0.0)));
315+ /// assert_eq!(horiz.intersect(&horiz2), None);
316+ ///
317+ /// assert_eq!(horiz.intersect(&horiz), Some(Coincident));
318+ ///
319+ ///
320+ /// ```
321+ fn intersect ( & self , other : & Self ) -> Self :: Result {
322+ let [ a, b, c] = self . coeffs ( ) ;
323+ let [ d, e, f] = other. coeffs ( ) ;
324+
325+ // Solve the system of equations for x and y:
326+ // ax + by = c // self
327+ // dx + ey = f // other
328+ //
329+ // Write in matrix form and solve:
330+ // (a b) (x) = (c)
331+ // (d e) (y) (f)
332+ //
333+ // -1
334+ // (x) = (a b) (c)
335+ // (y) (d e) (f)
336+ let abde: Mat2 < B > = mat ! [
337+ a, b;
338+ d, e;
339+ ] ;
340+ match abde. checked_inverse ( ) {
341+ Some ( inv) => {
342+ let res = inv. apply ( & pt2 ( c, f) ) ;
343+ Some ( LineIntersect :: Point ( res) )
344+ }
345+ None if [ a, b, c] . approx_eq ( & [ d, e, f] ) => {
346+ Some ( LineIntersect :: Coincident )
347+ }
348+ None => None ,
349+ }
350+ }
351+ }
352+
353+ impl < B > Intersect < Line2 < B > > for Ray2 < B > {
354+ type Result = RayIntersect2 < B > ;
355+
356+ // Returns the intersection point of self and a line.
357+ fn intersect ( & self , line : & Line2 < B > ) -> Self :: Result {
358+ // TODO if degenerate ray, could return if ray origin on edge
359+
360+ // First find intersection of lines
361+ let ray_line = Line2 :: from ( * self ) ;
362+ let isect = ray_line. intersect ( line) ?;
363+
364+ let Self ( orig, dir) = self ;
365+ match isect {
366+ LineIntersect :: Point ( pt) => {
367+ // Check if the point lies in the correct half-line
368+ let t = ( pt - * orig) . dot ( dir) ;
369+ ( t >= 0.0 ) . then_some ( ( t / dir. len_sqr ( ) , pt) )
370+ }
371+ LineIntersect :: Coincident => {
372+ // Ray lies on the line, the closest common point
373+ // is simply the origin point
374+ Some ( ( 0.0 , * orig) )
375+ }
376+ }
377+ }
378+ }
379+
380+ impl < B > Intersect < Edge < Point2 < B > > > for Ray2 < B > {
381+ type Result = RayIntersect2 < B > ;
382+
383+ /// Returns the intersection of `self` and an edge, or `None` if there is
384+ /// no intersection point.
385+ ///
386+ /// # Examples
387+ /// ```
388+ /// use retrofire_core::{
389+ /// assert_approx_eq, geom::{Edge, Ray}, math::{pt2, vec2, Point2},
390+ /// };
391+ /// use retrofire_geom::isect::Intersect;
392+ ///
393+ /// // ^
394+ /// // 3 O
395+ /// // | \
396+ /// // | v
397+ /// // E==1=======X==E
398+ /// // |
399+ /// // <----+-------------->
400+ /// let ray: Ray<Point2> = Ray(pt2(2.0, 3.0), vec2(1.0, -2.0));
401+ /// let edge = Edge(pt2(-1.0, 1.0), pt2(4.0, 1.0));
402+ ///
403+ /// let (t, point) = ray.intersect(&edge).unwrap();
404+ /// assert_eq!(t, 1.0);
405+ /// assert_approx_eq!(point, pt2(3.0, 1.0));
406+ ///
407+ /// // O
408+ /// // \
409+ /// // E=====E v
410+ /// //
411+ /// let edge = Edge(pt2(-1.0, 1.0), pt2(2.0, 1.0));
412+ /// assert_eq!(ray.intersect(&edge), None);
413+ /// ```
414+ // // TODO check that these are handled by actual unit tests
415+ // // E=====E <---O
416+ // let ray: Ray<Point2> = Ray(pt2(4.0, 1.0), vec2(-1.0, 0.0));
417+ // assert_eq!(ray.intersect(&edge), Some((2.0, pt2(2.0, 1.0))));
418+ //
419+ // // E==O--E-->
420+ // let ray: Ray<Point2> = Ray(pt2(1.0, 1.0), vec2(1.0, 0.0));
421+ // assert_eq!(ray.intersect(&edge), Some((0.0, pt2(1.0, 1.0))));
422+ //
423+ // // E=====E O--->
424+ // let ray: Ray<Point2> = Ray(pt2(4.0, 1.0), vec2(1.0, 0.0));
425+ // assert_eq!(ray.intersect(&edge), None);
426+ fn intersect ( & self , edge : & Edge < Point2 < B > > ) -> Self :: Result {
427+ // Compute ray-line intersection
428+ let ( t, pt) = self . intersect ( & Line2 :: from ( * edge) ) ?;
429+
430+ // Ray intersects the line of edge, but still have to check
431+ // whether the point is between edge endpoints
432+ let e01 = edge. 1 - edge. 0 ;
433+ let u = ( pt - edge. 0 ) . dot ( & e01) ;
434+ if u. approx_in ( 0.0 ..e01. len_sqr ( ) ) {
435+ return Some ( ( t, pt) ) ;
436+ }
437+
438+ // If ray is coincident with the edge, ray-line gives ray.0 as the
439+ // intersection point. If ray.0 is outside the edge, there are two cases:
440+ // e-----e r--->
441+ // where there is no intersection, and
442+ // e-----e <---r
443+ // where the intersection point is the closest edge endpoint.
444+ if !self . 0 . approx_eq ( & pt) {
445+ return None ;
446+ }
447+ let t0 = ( edge. 0 - pt) . dot ( & self . 1 ) ;
448+ if t0. approx_le ( & 0.0 ) {
449+ return None ;
450+ }
451+ let t1 = ( edge. 1 - pt) . dot ( & self . 1 ) ;
452+ if t0 <= t1 {
453+ Some ( ( t0 / self . 1 . len_sqr ( ) , edge. 0 ) )
454+ } else {
455+ Some ( ( t1 / self . 1 . len_sqr ( ) , edge. 1 ) )
456+ }
457+ }
458+ }
459+
460+ impl < B > Intersect < Self > for Edge < Point2 < B > > {
461+ type Result = Option < Point2 < B > > ;
462+
463+ /// Returns the intersection point of `self` and another edge, or `None`
464+ /// if the edges do not intersect.
465+ ///
466+ /// # Examples
467+ /// ```
468+ /// use retrofire_core::{
469+ /// assert_approx_eq,
470+ /// geom::Edge,
471+ /// math::{pt2, Point2}
472+ /// };
473+ /// use retrofire_geom::isect::Intersect;
474+ ///
475+ /// //
476+ /// // O1
477+ /// // \
478+ /// // \
479+ /// // \
480+ /// // E1---X----E2
481+ /// // \
482+ /// // O2
483+ /// let edge: Edge<Point2> = Edge(pt2(0.0, 1.0), pt2(4.0, 1.0));
484+ /// let other = Edge(pt2(0.0, 3.0), pt2(3.0, 0.0));
485+ ///
486+ /// assert_approx_eq!(edge.intersect(&other), Some(pt2(2.0, 1.0)));
487+ ///
488+ /// ```
489+ fn intersect ( & self , edge : & Self ) -> Self :: Result {
490+ let ray = Ray ( self . 0 , self . 1 - self . 0 ) ;
491+ match ray. intersect ( edge) {
492+ Some ( ( t, pt) ) if t <= 1.0 => Some ( pt) ,
493+ _ => None ,
494+ }
495+ }
496+ }
497+
246498#[ cfg( test) ]
247499mod tests {
248500 use retrofire_core:: math:: { Linear , Vec3 , pt3} ;
@@ -453,4 +705,5 @@ mod tests {
453705 assert_eq ! ( ray. intersect( & SPHERE ) , None ) ;
454706 }
455707 }
708+ // TODO 2D tests from stash
456709}
0 commit comments