1use std::{cmp::Ordering, ops::Sub};
26
27use crate::*;
28
29#[inline(always)]
35pub fn center_2d<P>(p1: &P, p2: &P) -> P
36where
37 P: IsBuildable2D,
38{
39 P::new(
40 p1.x() + (p2.x() - p1.x()) / 2.0,
41 p1.y() + (p2.y() - p1.y()) / 2.0,
42 )
43}
44
45#[inline(always)]
47pub fn center_3d<P>(p1: &P, p2: &P) -> P
48where
49 P: IsBuildable3D,
50{
51 P::new(
52 p1.x() + (p2.x() - p1.x()) / 2.0,
53 p1.y() + (p2.y() - p1.y()) / 2.0,
54 p1.z() + (p2.z() - p1.z()) / 2.0,
55 )
56}
57
58#[inline(always)]
60pub fn cross<P, U>(first: &P, other: &U) -> U
61where
62 P: Is3D,
63 U: IsBuildable3D,
64{
65 let x = first.y() * other.z() - first.z() * other.y();
66 let y = first.z() * other.x() - first.x() * other.z();
67 let z = first.x() * other.y() - first.y() * other.x();
68 U::new(x, y, z)
69}
70
71pub fn dimension_compare<P1, P2>(lhs: &P1, rhs: &P2, dim: i8) -> Result<Ordering>
73where
74 P1: Is3D,
75 P2: Is3D,
76{
77 match dim {
78 0 => lhs
79 .x()
80 .partial_cmp(&rhs.x())
81 .ok_or(ErrorKind::ComparisionFailed),
82 1 => lhs
83 .y()
84 .partial_cmp(&rhs.y())
85 .ok_or(ErrorKind::ComparisionFailed),
86 2 => lhs
87 .z()
88 .partial_cmp(&rhs.z())
89 .ok_or(ErrorKind::ComparisionFailed),
90 _ => Err(ErrorKind::DimensionsDontMatch),
91 }
92}
93
94pub fn dimension_dist<P1, P2>(lhs: &P1, rhs: &P2, dim: i8) -> Result<f64>
96where
97 P1: Is3D,
98 P2: Is3D,
99{
100 match dim {
101 0 => Ok((lhs.x() - rhs.x()).abs()),
102 1 => Ok((lhs.y() - rhs.y()).abs()),
103 2 => Ok((lhs.z() - rhs.z()).abs()),
104 _ => Err(ErrorKind::DimensionsDontMatch),
105 }
106}
107
108pub fn sort_vec_2d_x<P>(xs: &mut Vec<P>)
110where
111 P: Is2D,
112{
113 xs.sort_by(|a, b| {
114 a.x()
115 .partial_cmp(&b.x())
116 .or_else(|| a.y().partial_cmp(&b.y()))
117 .unwrap_or(Ordering::Equal)
118 });
119}
120
121pub fn sort_vec_2d_y<P>(xs: &mut Vec<P>)
123where
124 P: Is2D,
125{
126 xs.sort_by(|a, b| {
127 a.y()
128 .partial_cmp(&b.y())
129 .or_else(|| a.x().partial_cmp(&b.x()))
130 .unwrap_or(Ordering::Equal)
131 });
132}
133
134pub fn sort_vec_3d_x<P>(xs: &mut Vec<P>)
136where
137 P: Is3D,
138{
139 xs.sort_by(|a, b| {
140 a.x()
141 .partial_cmp(&b.x())
142 .or_else(|| a.y().partial_cmp(&b.y()))
143 .or_else(|| a.z().partial_cmp(&b.z()))
144 .unwrap_or(Ordering::Equal)
145 });
146}
147
148pub fn sort_vec_3d_y<P>(xs: &mut Vec<P>)
150where
151 P: Is3D,
152{
153 xs.sort_by(|a, b| {
154 a.y()
155 .partial_cmp(&b.y())
156 .or_else(|| a.z().partial_cmp(&b.z()))
157 .or_else(|| a.x().partial_cmp(&b.x()))
158 .unwrap_or(Ordering::Equal)
159 });
160}
161
162pub fn sort_vec_3d_z<P>(xs: &mut Vec<P>)
164where
165 P: Is3D,
166{
167 xs.sort_by(|a, b| {
168 a.z()
169 .partial_cmp(&b.z())
170 .or_else(|| a.x().partial_cmp(&b.x()))
171 .or_else(|| a.y().partial_cmp(&b.y()))
172 .unwrap_or(Ordering::Equal)
173 });
174}
175
176pub fn extrude<P2, P3>(pc2d: &Vec<P2>, dir: &P3) -> (PointCloud3D<P3>, PointCloud3D<P3>)
179where
180 P2: IsTransFormableTo3D,
181 P3: IsBuildable3D + IsMovable3D + Clone,
182{
183 let mut pc_3d_a = PointCloud3D::new();
184 let mut pc_3d_b = PointCloud3D::new();
185
186 for p in pc2d {
187 let p_transformed = p.transform_to_3d::<P3>(0.0);
188 pc_3d_a.push(p_transformed.clone());
189 pc_3d_b.push(p_transformed);
190 }
191
192 pc_3d_b.move_by(dir.x(), dir.y(), dir.z());
193 (pc_3d_a, pc_3d_b)
194}
195
196#[inline(always)]
200pub fn conn<P>(p_from: &P, p_to: &P) -> P
201where
202 P: IsBuildable3D,
203{
204 P::new(
205 p_to.x() - p_from.x(),
206 p_to.y() - p_from.y(),
207 p_to.z() - p_from.z(),
208 )
209}
210
211pub fn center<T>(x: &mut T)
213where
214 T: HasBoundingBox3DMaybe + IsMovable3D,
215{
216 if let Ok(bb) = x.bounding_box_maybe() {
217 let center = bb.center_bb();
218 x.move_by(-center.x(), -center.y(), -center.z());
219 }
220}
221
222pub fn set_size<T>(x: &mut T, size: [Positive; 3])
224where
225 T: HasBoundingBox3DMaybe + IsMatrix4Transformable,
226{
227 if let Ok(bb) = x.bounding_box_maybe() {
228 let m = Matrix4::scale(
229 size[0].get() / bb.size_x().get(),
230 size[1].get() / bb.size_y().get(),
231 size[2].get() / bb.size_z().get(),
232 );
233 x.transform(&m);
234 }
235}
236
237pub fn collect_intersections_ray_mesh<P, M>(ray: &Ray3D, mesh: &M, intersections: &mut Vec<P>)
239where
240 M: IsMesh<P, Face3>,
241 P: IsBuildable3D + Sub<Output = P> + Clone,
242{
243 let nf = mesh.num_faces();
244
245 for i in 0..nf {
246 let [v1, v2, v3] = mesh.face_vertices(FId { val: i }).unwrap(); if let Some(intersection) = intersection_ray_triangle(ray, &v1, &v2, &v3) {
249 intersections.push(intersection);
250 }
251 }
252}
253
254pub fn intersection_ray_triangle<P>(ray: &Ray3D, v1: &P, v2: &P, v3: &P) -> Option<P>
257where
258 P: IsBuildable3D + Sub<Output = P> + Clone,
259{
260 let orig = &ray.line.anchor;
261 let dir = &ray.line.dir;
262 let n = normal_of_face(v1, v2, v3);
263
264 let w1 = orig - v1;
265 let a = -n.dot(&w1);
266 let b = n.dot(dir);
267
268 if b == 0.0 {
269 return None;
270 } let r = a / b;
273
274 if r <= 0.0 {
275 return None;
276 }
277
278 let p = orig + (dir * r);
279
280 let e1 = v2.clone() - v1.clone();
281 let vp1 = &p - v1;
282 if n.dot(&cross(&e1, &vp1)) <= 0.0 {
283 return None;
284 }
285
286 let e2 = v3.clone() - v2.clone();
287 let vp2 = &p - v2;
288 if n.dot(&cross(&e2, &vp2)) <= 0.0 {
289 return None;
290 }
291
292 let e3 = v1.clone() - v3.clone();
293 let vp3 = &p - v3;
294 if n.dot(&cross(&e3, &vp3)) <= 0.0 {
295 return None;
296 }
297
298 Some(P::new_from(&p))
299}
300
301pub fn for_each_intersecting<'c, I, HB>(
303 ray: &Ray3D,
304 hbs: I,
305 f: &mut dyn FnMut(&Point3D, &'c mut HB),
306) where
307 I: Iterator<Item = &'c mut HB>,
308 HB: HasBoundingBox3DMaybe,
309{
310 for hb in hbs {
311 if let Ok(bb) = hb.bounding_box_maybe() {
312 if let Some(i) = intersection(&ray.line, &bb) {
313 f(&i, hb)
314 }
315 }
316 }
317}
318
319pub fn closest_intersecting_mut<'c, I, HB>(ray: &Ray3D, hbs: I) -> Option<(Point3D, &'c mut HB)>
321where
322 I: Iterator<Item = &'c mut HB>,
323 HB: HasBoundingBox3DMaybe,
324{
325 let mut result: Option<(Point3D, &'c mut HB)> = None;
326
327 for hb in hbs {
328 if let Ok(bb) = hb.bounding_box_maybe() {
329 if let Some(i) = intersection(&ray.line, &bb) {
330 if let Some(r) = &result {
331 if sqr_dist_3d(&ray.line.anchor, &i) < sqr_dist_3d(&ray.line.anchor, &r.0) {
332 result = Some((i, hb))
333 }
334 } else {
335 result = Some((i, hb))
336 }
337 }
338 }
339 }
340
341 result
342}
343
344pub fn closest_intersecting<'c, I, HB>(ray: &Ray3D, hbs: I) -> Option<(Point3D, &'c HB)>
346where
347 I: Iterator<Item = &'c HB>,
348 HB: HasBoundingBox3DMaybe,
349{
350 let mut result: Option<(Point3D, &'c HB)> = None;
351
352 for hb in hbs {
353 if let Ok(bb) = hb.bounding_box_maybe() {
354 if let Some(i) = intersection(&ray.line, &bb) {
355 if let Some(r) = &result {
356 if sqr_dist_3d(&ray.line.anchor, &i) < sqr_dist_3d(&ray.line.anchor, &r.0) {
357 result = Some((i, hb))
358 }
359 } else {
360 result = Some((i, hb))
361 }
362 }
363 }
364 }
365
366 result
367}
368
369pub fn index_closest_intersecting<'c, I, HB>(ray: &Ray3D, hbs: I) -> Option<(Point3D, usize)>
371where
372 I: Iterator<Item = &'c HB>,
373 HB: 'c + HasBoundingBox3DMaybe,
374{
375 let mut result: Option<(Point3D, usize)> = None;
376
377 for (i, hb) in hbs.enumerate() {
378 if let Ok(bb) = hb.bounding_box_maybe() {
379 if let Some(inter) = intersection(&ray.line, &bb) {
380 if let Some(r) = &result {
381 if sqr_dist_3d(&ray.line.anchor, &inter) < sqr_dist_3d(&ray.line.anchor, &r.0) {
382 result = Some((inter, i))
383 }
384 } else {
385 result = Some((inter, i))
386 }
387 }
388 }
389 }
390
391 result
392}
393
394pub fn normal_of_face<P>(v1: &P, v2: &P, v3: &P) -> Norm3D
396where
397 P: IsBuildable3D,
398{
399 let v12 = conn(v1, v2);
400 let v23 = conn(v2, v3);
401 Norm3D::new(cross(&v12, &v23)).unwrap_or(Norm3D::norm_z())
402}
403
404pub fn project_point_on_plane<PL, P2, P3, N>(plane: &PL, point: &P3) -> P2
406where
407 PL: IsPlane3D<P3, N>,
408 P2: IsBuildable2D,
409 P3: IsBuildable3D + IsTransFormableTo2D,
410 N: IsNormalized3D,
411{
412 let relative = conn(&plane.origin(), point);
413 let mut p2transf = point.transform_to_2d::<P2>();
414 let mut tmp = Point2D::default();
415
416 tmp.set_x(plane.u().dot(&relative));
417 tmp.set_y(plane.v().dot(&relative));
418
419 p2transf.from(&tmp);
420 p2transf
421}
422
423pub fn min64(a: f64, b: f64) -> f64 {
425 if a < b {
426 a
427 } else {
428 b
429 }
430}
431
432pub fn max64(a: f64, b: f64) -> f64 {
434 if a > b {
435 a
436 } else {
437 b
438 }
439}
440
441pub fn intersection(l: &Line3D, b: &BoundingBox3D) -> Option<Point3D> {
444 let inv_dir = [1.0 / l.dir.x(), 1.0 / l.dir.y(), 1.0 / l.dir.z()];
445 let min = b.min_p();
446 let max = b.max_p();
447
448 let tx1 = (min.x() - l.anchor.x()) * inv_dir[0];
449 let tx2 = (max.x() - l.anchor.x()) * inv_dir[0];
450
451 let mut tmin = min64(tx1, tx2);
452 let mut tmax = max64(tx1, tx2);
453
454 let ty1 = (min.y() - l.anchor.y()) * inv_dir[1];
455 let ty2 = (max.y() - l.anchor.y()) * inv_dir[1];
456
457 tmin = max64(tmin, min64(ty1, ty2));
458 tmax = min64(tmax, max64(ty1, ty2));
459
460 let tz1 = (min.z() - l.anchor.z()) * inv_dir[2];
461 let tz2 = (max.z() - l.anchor.z()) * inv_dir[2];
462
463 tmin = max64(tmin, min64(tz1, tz2));
464 tmax = min64(tmax, max64(tz1, tz2));
465
466 if tmax >= tmin && tmax >= 0.0 {
467 Some(&l.anchor + &l.dir * tmin)
468 } else {
469 None
470 }
471}