1use fj_math::{Plane, Point, Scalar};
4
5use crate::{
6 algorithms::intersect::face_point::FacePointIntersection,
7 geometry::curve::GlobalPath,
8 objects::{Face, HalfEdge},
9 storage::Handle,
10};
11
12use super::{HorizontalRayToTheRight, Intersect};
13
14impl Intersect for (&HorizontalRayToTheRight<3>, &Face) {
15 type Intersection = RayFaceIntersection;
16
17 fn intersect(self) -> Option<Self::Intersection> {
18 let (ray, face) = self;
19
20 let plane = match face.surface().geometry().u {
21 GlobalPath::Circle(_) => todo!(
22 "Casting a ray against a swept circle is not supported yet"
23 ),
24 GlobalPath::Line(line) => Plane::from_parametric(
25 line.origin(),
26 line.direction(),
27 face.surface().geometry().v,
28 ),
29 };
30
31 if plane.is_parallel_to_vector(&ray.direction()) {
32 let a = plane.origin();
33 let b = plane.origin() + plane.u();
34 let c = plane.origin() + plane.v();
35 let d = ray.origin;
36
37 let [a, b, c, d] = [a, b, c, d]
38 .map(|point| [point.x, point.y, point.z])
39 .map(|point| point.map(Scalar::into_f64))
40 .map(|[x, y, z]| robust::Coord3D { x, y, z });
41
42 if robust::orient3d(a, b, c, d) == 0. {
43 return Some(RayFaceIntersection::RayHitsFaceAndAreParallel);
44 } else {
45 return None;
46 }
47 }
48
49 assert_ne!(
64 plane.u().y * plane.v().z,
65 plane.u().z * plane.v().y,
66 "Plane and ray are parallel; should have been ruled out previously"
67 );
68
69 let (t, u, v) = {
71 let orx = ray.origin.x;
74 let ory = ray.origin.y;
75 let orz = ray.origin.z;
76 let opx = plane.origin().x;
77 let opy = plane.origin().y;
78 let opz = plane.origin().z;
79 let d1x = plane.u().x;
80 let d1y = plane.u().y;
81 let d1z = plane.u().z;
82 let d2x = plane.v().x;
83 let d2y = plane.v().y;
84 let d2z = plane.v().z;
85
86 let v = (d1y * (orz - opz) + (opy - ory) * d1z)
98 / (d1y * d2z - d2y * d1z);
99 let u = (ory - opy - d2y * v) / d1y;
100 let t = opx - orx + d1x * u + d2x * v;
101
102 (t, u, v)
103 };
104
105 if t < Scalar::ZERO {
106 return None;
108 }
109
110 let point = Point::from([u, v]);
111 let intersection = match (face, &point).intersect()? {
112 FacePointIntersection::PointIsInsideFace => {
113 RayFaceIntersection::RayHitsFace
114 }
115 FacePointIntersection::PointIsOnEdge(edge) => {
116 RayFaceIntersection::RayHitsEdge(edge)
117 }
118 FacePointIntersection::PointIsOnVertex(vertex) => {
119 RayFaceIntersection::RayHitsVertex(vertex)
120 }
121 };
122
123 Some(intersection)
124 }
125}
126
127#[derive(Clone, Debug, Eq, PartialEq)]
129pub enum RayFaceIntersection {
130 RayHitsFace,
132
133 RayHitsFaceAndAreParallel,
135
136 RayHitsEdge(Handle<HalfEdge>),
138
139 RayHitsVertex(Point<2>),
141}
142
143#[cfg(test)]
144mod tests {
145 use fj_math::Point;
146
147 use crate::{
148 algorithms::{
149 intersect::{
150 ray_face::RayFaceIntersection, HorizontalRayToTheRight,
151 Intersect,
152 },
153 transform::TransformObject,
154 },
155 objects::{Cycle, Face},
156 operations::{BuildCycle, BuildFace, Insert, UpdateFace},
157 services::Services,
158 };
159
160 #[test]
161 fn ray_misses_whole_surface() {
162 let mut services = Services::new();
163
164 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
165
166 let face =
167 Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
168 .update_exterior(|_| {
169 Cycle::polygon(
170 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
171 &mut services,
172 )
173 .insert(&mut services)
174 });
175 let face = face.translate([-1., 0., 0.], &mut services);
176
177 assert_eq!((&ray, &face).intersect(), None);
178
179 services.only_validate(face);
180 }
181
182 #[test]
183 fn ray_hits_face() {
184 let mut services = Services::new();
185
186 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
187
188 let face =
189 Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
190 .update_exterior(|_| {
191 Cycle::polygon(
192 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
193 &mut services,
194 )
195 .insert(&mut services)
196 });
197 let face = face.translate([1., 0., 0.], &mut services);
198
199 assert_eq!(
200 (&ray, &face).intersect(),
201 Some(RayFaceIntersection::RayHitsFace)
202 );
203
204 services.only_validate(face);
205 }
206
207 #[test]
208 fn ray_hits_surface_but_misses_face() {
209 let mut services = Services::new();
210
211 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
212
213 let face =
214 Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
215 .update_exterior(|_| {
216 Cycle::polygon(
217 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
218 &mut services,
219 )
220 .insert(&mut services)
221 });
222 let face = face.translate([0., 0., 2.], &mut services);
223
224 assert_eq!((&ray, &face).intersect(), None);
225
226 services.only_validate(face);
227 }
228
229 #[test]
230 fn ray_hits_edge() {
231 let mut services = Services::new();
232
233 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
234
235 let face =
236 Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
237 .update_exterior(|_| {
238 Cycle::polygon(
239 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
240 &mut services,
241 )
242 .insert(&mut services)
243 });
244 let face = face.translate([1., 1., 0.], &mut services);
245
246 let edge = face
247 .exterior()
248 .half_edges()
249 .find(|edge| edge.start_position() == Point::from([-1., 1.]))
250 .unwrap();
251 assert_eq!(
252 (&ray, &face).intersect(),
253 Some(RayFaceIntersection::RayHitsEdge(edge.clone()))
254 );
255
256 services.only_validate(face);
257 }
258
259 #[test]
260 fn ray_hits_vertex() {
261 let mut services = Services::new();
262
263 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
264
265 let face =
266 Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
267 .update_exterior(|_| {
268 Cycle::polygon(
269 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
270 &mut services,
271 )
272 .insert(&mut services)
273 });
274 let face = face.translate([1., 1., 1.], &mut services);
275
276 let vertex = face
277 .exterior()
278 .half_edges()
279 .find(|half_edge| {
280 half_edge.start_position() == Point::from([-1., -1.])
281 })
282 .map(|half_edge| half_edge.start_position())
283 .unwrap();
284 assert_eq!(
285 (&ray, &face).intersect(),
286 Some(RayFaceIntersection::RayHitsVertex(vertex))
287 );
288
289 services.only_validate(face);
290 }
291
292 #[test]
293 fn ray_is_parallel_to_surface_and_hits() {
294 let mut services = Services::new();
295
296 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
297
298 let face =
299 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
300 .update_exterior(|_| {
301 Cycle::polygon(
302 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
303 &mut services,
304 )
305 .insert(&mut services)
306 });
307
308 assert_eq!(
309 (&ray, &face).intersect(),
310 Some(RayFaceIntersection::RayHitsFaceAndAreParallel)
311 );
312
313 services.only_validate(face);
314 }
315
316 #[test]
317 fn ray_is_parallel_to_surface_and_misses() {
318 let mut services = Services::new();
319
320 let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
321
322 let face =
323 Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
324 .update_exterior(|_| {
325 Cycle::polygon(
326 [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
327 &mut services,
328 )
329 .insert(&mut services)
330 });
331 let face = face.translate([0., 0., 1.], &mut services);
332
333 assert_eq!((&ray, &face).intersect(), None);
334
335 services.only_validate(face);
336 }
337}