fj_core/algorithms/intersect/
ray_segment.rs1use fj_math::Segment;
4
5use super::{HorizontalRayToTheRight, Intersect};
6
7impl Intersect for (&HorizontalRayToTheRight<2>, &Segment<2>) {
8 type Intersection = RaySegmentIntersection;
9
10 fn intersect(self) -> Option<Self::Intersection> {
11 let (ray, segment) = self;
12
13 let [a, b] = segment.points();
14 let [lower, upper] = if a.v <= b.v { [a, b] } else { [b, a] };
15 let [left, right] = if a.u <= b.u { [a, b] } else { [b, a] };
16
17 if ray.origin.v > upper.v {
18 return None;
20 }
21 if ray.origin.v < lower.v {
22 return None;
24 }
25
26 if ray.origin.v == lower.v && lower.v == upper.v {
27 if ray.origin.u > right.u {
30 return None;
31 }
32
33 if ray.origin.u == a.u {
34 return Some(RaySegmentIntersection::RayStartsOnOnFirstVertex);
35 }
36 if ray.origin.u == b.u {
37 return Some(RaySegmentIntersection::RayStartsOnSecondVertex);
38 }
39 if ray.origin.u > left.u && ray.origin.u < right.u {
40 return Some(RaySegmentIntersection::RayStartsOnSegment);
41 }
42
43 return Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel);
44 }
45
46 let pa = robust::Coord {
47 x: lower.u,
48 y: lower.v,
49 };
50 let pb = robust::Coord {
51 x: upper.u,
52 y: upper.v,
53 };
54 let pc = robust::Coord {
55 x: ray.origin.u,
56 y: ray.origin.v,
57 };
58
59 let orient2d = robust::orient2d(pa, pb, pc);
60
61 if orient2d == 0. {
62 if ray.origin.v == a.v {
65 return Some(RaySegmentIntersection::RayStartsOnOnFirstVertex);
66 }
67 if ray.origin.v == b.v {
68 return Some(RaySegmentIntersection::RayStartsOnSecondVertex);
69 }
70
71 return Some(RaySegmentIntersection::RayStartsOnSegment);
72 }
73
74 if orient2d > 0. {
75 if ray.origin.v == upper.v {
78 return Some(RaySegmentIntersection::RayHitsUpperVertex);
79 }
80 if ray.origin.v == lower.v {
81 return Some(RaySegmentIntersection::RayHitsLowerVertex);
82 }
83
84 return Some(RaySegmentIntersection::RayHitsSegment);
85 }
86
87 None
88 }
89}
90
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub enum RaySegmentIntersection {
94 RayHitsSegment,
96
97 RayHitsLowerVertex,
99
100 RayHitsUpperVertex,
102
103 RayHitsSegmentAndAreParallel,
105
106 RayStartsOnSegment,
108
109 RayStartsOnOnFirstVertex,
111
112 RayStartsOnSecondVertex,
114}
115
116#[cfg(test)]
117mod tests {
118 use fj_math::Segment;
119
120 use crate::algorithms::intersect::Intersect;
121
122 use super::{HorizontalRayToTheRight, RaySegmentIntersection};
123
124 #[test]
125 fn ray_is_left_of_segment() {
126 let ray = HorizontalRayToTheRight::from([0., 2.]);
127
128 let below = Segment::from([[1., 0.], [1., 1.]]);
129 let above = Segment::from([[1., 3.], [1., 4.]]);
130 let same_level = Segment::from([[1., 1.], [1., 3.]]);
131
132 assert!((&ray, &below).intersect().is_none());
133 assert!((&ray, &above).intersect().is_none());
134 assert!(matches!(
135 (&ray, &same_level).intersect(),
136 Some(RaySegmentIntersection::RayHitsSegment)
137 ));
138 }
139
140 #[test]
141 fn ray_is_right_of_segment() {
142 let ray = HorizontalRayToTheRight::from([1., 2.]);
143
144 let same_level = Segment::from([[0., 1.], [0., 3.]]);
145 assert!((&ray, &same_level).intersect().is_none());
146 }
147
148 #[test]
149 fn ray_overlaps_with_segment_along_x_axis() {
150 let ray = HorizontalRayToTheRight::from([1., 1.]);
151
152 let no_hit = Segment::from([[0., 0.], [2., 3.]]);
153
154 let hit_segment = Segment::from([[0., 0.], [3., 2.]]);
155 let hit_upper = Segment::from([[0., 0.], [2., 1.]]);
156 let hit_lower = Segment::from([[0., 2.], [2., 1.]]);
157
158 assert!((&ray, &no_hit).intersect().is_none());
159 assert!(matches!(
160 (&ray, &hit_segment).intersect(),
161 Some(RaySegmentIntersection::RayHitsSegment)
162 ));
163 assert!(matches!(
164 (&ray, &hit_upper).intersect(),
165 Some(RaySegmentIntersection::RayHitsUpperVertex),
166 ));
167 assert!(matches!(
168 (&ray, &hit_lower).intersect(),
169 Some(RaySegmentIntersection::RayHitsLowerVertex),
170 ));
171 }
172
173 #[test]
174 fn ray_starts_on_segment() {
175 let ray = HorizontalRayToTheRight::from([1., 1.]);
176
177 let hit_segment = Segment::from([[0., 0.], [2., 2.]]);
178 let hit_upper = Segment::from([[0., 0.], [1., 1.]]);
179 let hit_lower = Segment::from([[1., 1.], [2., 2.]]);
180
181 assert!(matches!(
182 (&ray, &hit_segment).intersect(),
183 Some(RaySegmentIntersection::RayStartsOnSegment)
184 ));
185 assert!(matches!(
186 (&ray, &hit_upper).intersect(),
187 Some(RaySegmentIntersection::RayStartsOnSecondVertex),
188 ));
189 assert!(matches!(
190 (&ray, &hit_lower).intersect(),
191 Some(RaySegmentIntersection::RayStartsOnOnFirstVertex),
192 ));
193 }
194
195 #[test]
196 fn ray_and_segment_are_parallel_and_on_same_level() {
197 let ray = HorizontalRayToTheRight::from([2., 0.]);
198
199 let left = Segment::from([[0., 0.], [1., 0.]]);
200 let right = Segment::from([[3., 0.], [4., 0.]]);
201
202 assert!((&ray, &left).intersect().is_none());
203 assert!(matches!(
204 (&ray, &right).intersect(),
205 Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel)
206 ));
207 }
208
209 #[test]
210 fn ray_starts_on_parallel_segment() {
211 let ray = HorizontalRayToTheRight::from([2., 0.]);
212
213 let left = Segment::from([[0., 0.], [2., 0.]]);
214 let overlapping = Segment::from([[1., 0.], [3., 0.]]);
215 let right = Segment::from([[2., 0.], [4., 0.]]);
216
217 assert!(matches!(
218 (&ray, &left).intersect(),
219 Some(RaySegmentIntersection::RayStartsOnSecondVertex)
220 ));
221 assert!(matches!(
222 (&ray, &overlapping).intersect(),
223 Some(RaySegmentIntersection::RayStartsOnSegment),
224 ));
225 assert!(matches!(
226 (&ray, &right).intersect(),
227 Some(RaySegmentIntersection::RayStartsOnOnFirstVertex),
228 ));
229 }
230}