fj_core/algorithms/intersect/
line_segment.rs1use fj_math::{Aabb, Line, Point, Scalar, Segment, Vector};
2
3#[derive(Debug, Eq, PartialEq)]
5pub enum LineSegmentIntersection {
6 Point {
8 point_on_line: Point<1>,
10 },
11
12 Coincident {
14 points_on_line: [Point<1>; 2],
16 },
17}
18
19impl LineSegmentIntersection {
20 pub fn compute(line: &Line<2>, segment: &Segment<2>) -> Option<Self> {
22 let [a, b] = segment.points();
26
27 let n = {
29 let ab = b - a;
30 Vector::from([ab.v, ab.u])
31 };
32
33 let n_dot_origin = n.dot(&(b - line.origin()));
34 let n_dot_direction = n.dot(&line.direction());
35
36 if n_dot_direction == Scalar::ZERO {
37 if n_dot_origin == Scalar::ZERO {
40 return Some(Self::Coincident {
42 points_on_line: segment
43 .points()
44 .map(|point| line.point_to_line_coords(point)),
45 });
46 }
47
48 return None;
49 }
50
51 let t = n_dot_origin / n_dot_direction;
54
55 let point_is_on_segment = Aabb::<2>::from_points(segment.points())
56 .contains(line.point_from_line_coords([t]));
57 if !point_is_on_segment {
58 return None;
59 }
60
61 Some(Self::Point {
62 point_on_line: Point::from([t]),
63 })
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use fj_math::{Line, Point, Scalar, Segment, Vector};
70
71 use super::LineSegmentIntersection;
72
73 #[test]
74 fn compute_one_hit() {
75 let line =
76 Line::from_origin_and_direction(Point::origin(), Vector::unit_u());
77
78 assert_eq!(
79 LineSegmentIntersection::compute(
80 &line,
81 &Segment::from_points([[1., -1.], [1., 1.]]),
82 ),
83 Some(LineSegmentIntersection::Point {
84 point_on_line: Point::from([Scalar::ONE])
85 }),
86 );
87 }
88
89 #[test]
90 fn compute_coincident() {
91 let line =
92 Line::from_origin_and_direction(Point::origin(), Vector::unit_u());
93
94 assert_eq!(
95 LineSegmentIntersection::compute(
96 &line,
97 &Segment::from_points([[1., 0.], [2., 0.]]),
98 ),
99 Some(LineSegmentIntersection::Coincident {
100 points_on_line: [Point::from([1.]), Point::from([2.])],
101 }),
102 );
103 }
104
105 #[test]
106 fn compute_no_hit_above() {
107 let line =
108 Line::from_origin_and_direction(Point::origin(), Vector::unit_u());
109
110 assert_eq!(
111 LineSegmentIntersection::compute(
112 &line,
113 &Segment::from_points([[1., 1.], [1., 2.]]),
114 ),
115 None,
116 );
117 }
118
119 #[test]
120 fn compute_no_hit_below() {
121 let line =
122 Line::from_origin_and_direction(Point::origin(), Vector::unit_u());
123
124 assert_eq!(
125 LineSegmentIntersection::compute(
126 &line,
127 &Segment::from_points([[1., -2.], [1., -1.]]),
128 ),
129 None,
130 );
131 }
132
133 #[test]
134 fn compute_no_hit_parallel() {
135 let line =
136 Line::from_origin_and_direction(Point::origin(), Vector::unit_u());
137
138 assert_eq!(
139 LineSegmentIntersection::compute(
140 &line,
141 &Segment::from_points([[-1., 1.], [1., 1.]]),
142 ),
143 None,
144 );
145 }
146}