fj_kernel/algorithms/intersect/
face_point.rs1use fj_math::Point;
4use itertools::Itertools;
5
6use crate::{
7 objects::{Face, HalfEdge},
8 storage::Handle,
9};
10
11use super::{
12 ray_segment::RaySegmentIntersection, HorizontalRayToTheRight, Intersect,
13};
14
15impl Intersect for (&Face, &Point<2>) {
16 type Intersection = FacePointIntersection;
17
18 fn intersect(self) -> Option<Self::Intersection> {
19 let (face, point) = self;
20
21 let ray = HorizontalRayToTheRight { origin: *point };
22
23 let mut num_hits = 0;
24
25 for cycle in face.all_cycles() {
26 let mut previous_hit = cycle
32 .half_edges()
33 .last()
34 .cloned()
35 .and_then(|edge| (&ray, &edge).intersect());
36
37 for (half_edge, next_half_edge) in
38 cycle.half_edges().circular_tuple_windows::<(_, _)>()
39 {
40 let hit = (&ray, half_edge).intersect();
41
42 let count_hit = match (hit, previous_hit) {
43 (
44 Some(RaySegmentIntersection::RayStartsOnSegment),
45 _,
46 ) => {
47 return Some(FacePointIntersection::PointIsOnEdge(
50 half_edge.clone()
51 ));
52 }
53 (Some(RaySegmentIntersection::RayStartsOnOnFirstVertex), _) => {
54 let vertex = half_edge.start_position();
55 return Some(
56 FacePointIntersection::PointIsOnVertex(vertex)
57 );
58 }
59 (Some(RaySegmentIntersection::RayStartsOnSecondVertex), _) => {
60 let vertex = next_half_edge.start_position();
61 return Some(
62 FacePointIntersection::PointIsOnVertex(vertex)
63 );
64 }
65 (Some(RaySegmentIntersection::RayHitsSegment), _) => {
66 true
68 }
69 (
70 Some(RaySegmentIntersection::RayHitsUpperVertex),
71 Some(RaySegmentIntersection::RayHitsLowerVertex),
72 )
73 | (
74 Some(RaySegmentIntersection::RayHitsLowerVertex),
75 Some(RaySegmentIntersection::RayHitsUpperVertex),
76 ) => {
77 true
91 }
92 (Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel), _) => {
93 continue;
98 }
99 _ => {
100 false
102 }
103 };
104
105 if count_hit {
106 num_hits += 1;
107 }
108
109 previous_hit = hit;
110 }
111 }
112
113 if num_hits % 2 == 1 {
114 Some(FacePointIntersection::PointIsInsideFace)
115 } else {
116 None
117 }
118 }
119}
120
121#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
123pub enum FacePointIntersection {
124 PointIsInsideFace,
126
127 PointIsOnEdge(Handle<HalfEdge>),
129
130 PointIsOnVertex(Point<2>),
132}
133
134#[cfg(test)]
135mod tests {
136 use fj_math::Point;
137 use pretty_assertions::assert_eq;
138
139 use crate::{
140 algorithms::intersect::{face_point::FacePointIntersection, Intersect},
141 objects::{Cycle, Face},
142 operations::{BuildCycle, BuildFace, Insert, UpdateFace},
143 services::Services,
144 };
145
146 #[test]
147 fn point_is_outside_face() {
148 let mut services = Services::new();
149
150 let face =
151 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
152 .update_exterior(|_| {
153 Cycle::polygon(
154 [[0., 0.], [1., 1.], [0., 2.]],
155 &mut services,
156 )
157 .insert(&mut services)
158 });
159 let point = Point::from([2., 1.]);
160
161 let intersection = (&face, &point).intersect();
162 assert_eq!(intersection, None);
163
164 services.only_validate(face);
165 }
166
167 #[test]
168 fn ray_hits_vertex_while_passing_outside() {
169 let mut services = Services::new();
170
171 let face =
172 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
173 .update_exterior(|_| {
174 Cycle::polygon(
175 [[0., 0.], [2., 1.], [0., 2.]],
176 &mut services,
177 )
178 .insert(&mut services)
179 });
180 let point = Point::from([1., 1.]);
181
182 let intersection = (&face, &point).intersect();
183 assert_eq!(
184 intersection,
185 Some(FacePointIntersection::PointIsInsideFace)
186 );
187
188 services.only_validate(face);
189 }
190
191 #[test]
192 fn ray_hits_vertex_at_cycle_seam() {
193 let mut services = Services::new();
194
195 let face =
196 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
197 .update_exterior(|_| {
198 Cycle::polygon(
199 [[4., 2.], [0., 4.], [0., 0.]],
200 &mut services,
201 )
202 .insert(&mut services)
203 });
204 let point = Point::from([1., 2.]);
205
206 let intersection = (&face, &point).intersect();
207 assert_eq!(
208 intersection,
209 Some(FacePointIntersection::PointIsInsideFace)
210 );
211
212 services.only_validate(face);
213 }
214
215 #[test]
216 fn ray_hits_vertex_while_staying_inside() {
217 let mut services = Services::new();
218
219 let face =
220 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
221 .update_exterior(|_| {
222 Cycle::polygon(
223 [[0., 0.], [2., 1.], [3., 0.], [3., 4.]],
224 &mut services,
225 )
226 .insert(&mut services)
227 });
228 let point = Point::from([1., 1.]);
229
230 let intersection = (&face, &point).intersect();
231 assert_eq!(
232 intersection,
233 Some(FacePointIntersection::PointIsInsideFace)
234 );
235
236 services.only_validate(face);
237 }
238
239 #[test]
240 fn ray_hits_parallel_edge_and_leaves_face_at_vertex() {
241 let mut services = Services::new();
242
243 let face =
244 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
245 .update_exterior(|_| {
246 Cycle::polygon(
247 [[0., 0.], [2., 1.], [3., 1.], [0., 2.]],
248 &mut services,
249 )
250 .insert(&mut services)
251 });
252 let point = Point::from([1., 1.]);
253
254 let intersection = (&face, &point).intersect();
255 assert_eq!(
256 intersection,
257 Some(FacePointIntersection::PointIsInsideFace)
258 );
259
260 services.only_validate(face);
261 }
262
263 #[test]
264 fn ray_hits_parallel_edge_and_does_not_leave_face_there() {
265 let mut services = Services::new();
266
267 let face =
268 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
269 .update_exterior(|_| {
270 Cycle::polygon(
271 [[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]],
272 &mut services,
273 )
274 .insert(&mut services)
275 });
276 let point = Point::from([1., 1.]);
277
278 let intersection = (&face, &point).intersect();
279 assert_eq!(
280 intersection,
281 Some(FacePointIntersection::PointIsInsideFace)
282 );
283
284 services.only_validate(face);
285 }
286
287 #[test]
288 fn point_is_coincident_with_edge() {
289 let mut services = Services::new();
290
291 let face =
292 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
293 .update_exterior(|_| {
294 Cycle::polygon(
295 [[0., 0.], [2., 0.], [0., 1.]],
296 &mut services,
297 )
298 .insert(&mut services)
299 });
300 let point = Point::from([1., 0.]);
301
302 let intersection = (&face, &point).intersect();
303
304 let edge = face
305 .exterior()
306 .half_edges()
307 .find(|edge| edge.start_position() == Point::from([0., 0.]))
308 .unwrap();
309 assert_eq!(
310 intersection,
311 Some(FacePointIntersection::PointIsOnEdge(edge.clone()))
312 );
313
314 services.only_validate(face);
315 }
316
317 #[test]
318 fn point_is_coincident_with_vertex() {
319 let mut services = Services::new();
320
321 let face =
322 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
323 .update_exterior(|_| {
324 Cycle::polygon(
325 [[0., 0.], [1., 0.], [0., 1.]],
326 &mut services,
327 )
328 .insert(&mut services)
329 });
330 let point = Point::from([1., 0.]);
331
332 let intersection = (&face, &point).intersect();
333
334 let vertex = face
335 .exterior()
336 .half_edges()
337 .find(|half_edge| {
338 half_edge.start_position() == Point::from([1., 0.])
339 })
340 .map(|half_edge| half_edge.start_position())
341 .unwrap();
342 assert_eq!(
343 intersection,
344 Some(FacePointIntersection::PointIsOnVertex(vertex))
345 );
346
347 services.only_validate(face);
348 }
349}