1use crate::{Number, Polygon, PolygonClassification};
2use core::ops::{AddAssign, MulAssign, SubAssign};
3use nalgebra::{Point, Point3, SVector};
4use num_traits::real::Real;
5
6pub type Plane3<T> = Plane<T, 3>;
7
8#[repr(C)]
10#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
11pub struct Plane<T: Number, const N: usize> {
12 distance: T,
13 normal: SVector<T, N>,
14}
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Hash)]
17pub enum PlaneSide {
18 Front,
19 Back,
20 On,
21}
22
23impl core::ops::Neg for PlaneSide {
24 type Output = Self;
25
26 fn neg(self) -> Self::Output {
27 match self {
28 PlaneSide::Front => PlaneSide::Back,
29 PlaneSide::Back => PlaneSide::Front,
30 PlaneSide::On => PlaneSide::On,
31 }
32 }
33}
34
35impl<T: Number> Plane<T, 3> {
36 pub fn xy_plane() -> Self {
37 Self {
38 normal: *SVector::ith_axis(2),
39 distance: T::zero(),
40 }
41 }
42
43 pub fn xz_plane() -> Self {
44 Self {
45 normal: *SVector::ith_axis(1),
46 distance: T::zero(),
47 }
48 }
49
50 pub fn yz_plane() -> Self {
51 Self {
52 normal: *SVector::ith_axis(0),
53 distance: T::zero(),
54 }
55 }
56}
57
58impl<T, const N: usize> Plane<T, N>
59where
60 T: Number,
61{
62 pub fn new(normal: SVector<T, N>, distance: T) -> Self {
63 let len = normal.magnitude();
64 if len > T::default_epsilon() {
65 Self {
66 normal: normal / len,
67 distance: distance / len,
68 }
69 } else {
70 Self { normal, distance }
71 }
72 }
73
74 #[inline]
75 pub fn normal(&self) -> SVector<T, N> {
76 self.normal
77 }
78
79 pub fn with_normal(mut self, normal: SVector<T, N>) -> Self {
80 self.normal = normal;
81 self
82 }
83
84 #[inline]
86 pub fn distance(&self) -> T {
87 self.distance
88 }
89
90 pub fn roughly_eq(&self, other: &Self) -> bool {
92 let eps = T::default_epsilon();
93 (self.normal - other.normal).norm() < eps && Real::abs(self.distance - other.distance) < eps
94 }
95
96 #[inline]
99 pub fn origin_projection(&self) -> Point<T, N> {
100 (self.normal * self.distance).into()
101 }
102
103 #[inline]
104 pub fn classify_point(&self, point: &Point<T, N>) -> PlaneSide {
105 let eps = T::from(1e-5).expect("BUG: 1e-5 is representable as T");
109 let distance = self.normal.dot(&point.coords) - self.distance;
110 if distance > eps {
111 PlaneSide::Front
112 } else if distance < -eps {
113 PlaneSide::Back
114 } else {
115 PlaneSide::On
116 }
117 }
118
119 #[inline(always)]
120 pub fn has_point(&self, point: &Point<T, N>) -> bool {
121 self.classify_point(point) == PlaneSide::On
122 }
123
124 pub fn cast<T2: Number>(self) -> Plane<T2, N>
126 where
127 T: num_traits::cast::AsPrimitive<T2>,
128 {
129 Plane {
130 normal: self.normal.map(|x| x.as_()),
131 distance: self.distance.as_(),
132 }
133 }
134
135 #[inline]
136 #[must_use = "flip takes ownership of the original plane and returns a new one with the flipped normal and distance"]
137 pub fn flip(mut self) -> Self {
138 self.normal *= -T::one();
139 self.distance *= -T::one();
140 self
141 }
142
143 pub fn classify_polygon(&self, polygon: &Polygon<T, N>) -> PolygonClassification {
148 let eps = T::from(1e-5).expect("BUG: 1e-5 is representable as T");
149 let neg_eps = -eps;
150 let mut min_dist =
151 T::from(f64::INFINITY).expect("BUG: f64::INFINITY is representable as T");
152 let mut max_dist =
153 T::from(f64::NEG_INFINITY).expect("BUG: f64::NEG_INFINITY is representable as T");
154
155 for vertex in polygon.vertices().iter() {
156 let d = self.normal.dot(&vertex.coords) - self.distance;
157 if d < min_dist {
158 min_dist = d;
159 }
160 if d > max_dist {
161 max_dist = d;
162 }
163 }
164
165 match (max_dist > eps, min_dist < neg_eps) {
166 (true, true) => PolygonClassification::Spanning,
167 (true, false) => PolygonClassification::Front,
168 (false, true) => PolygonClassification::Back,
169 (false, false) => PolygonClassification::Coplanar,
170 }
171 }
172}
173
174impl<T: Number, const N: usize> Plane<T, N> {
175 #[allow(clippy::type_complexity)]
176 pub fn split_edge(
177 &self,
178 a: &Point<T, N>,
179 b: &Point<T, N>,
180 ) -> Result<(Point<T, N>, Point<T, N>, Point<T, N>), PlaneSide> {
181 let a_class = self.classify_point(a);
183 let b_class = self.classify_point(b);
184 if a_class == b_class {
185 Err(a_class)?;
186 }
187 let t = (self.distance() - self.normal().dot(&a.coords)) / self.normal().dot(&(b - a));
188 let p = a + (b - a) * t;
189 Ok((*a, p, *b))
190 }
191
192 pub fn split_polygon(&self, polygon: Polygon<T, N>) -> (Polygon<T, N>, Polygon<T, N>) {
207 let verts = polygon.vertices();
208 let n = verts.len();
209 let cap = n + 1;
210 let mut front = Vec::with_capacity(cap);
211 let mut back = Vec::with_capacity(cap);
212
213 let classes: Vec<PlaneSide> = verts.iter().map(|v| self.classify_point(v)).collect();
216
217 for i in 0..n {
218 let j = if i + 1 < n { i + 1 } else { 0 };
219 let this = &verts[i];
220 let this_class = classes[i];
221 let next_class = classes[j];
222
223 match this_class {
224 PlaneSide::Front => front.push(*this),
225 PlaneSide::Back => back.push(*this),
226 PlaneSide::On => {
227 front.push(*this);
228 back.push(*this);
229 }
230 }
231 if this_class != PlaneSide::On
232 && next_class != PlaneSide::On
233 && this_class != next_class
234 {
235 let next = &verts[j];
236 let t = (self.distance() - self.normal().dot(&this.coords))
237 / self.normal().dot(&(next - this));
238 let p = this + (next - this) * t;
239 front.push(p);
240 back.push(p);
241 }
242 }
243
244 let a = ndarray::Array1::from_vec(front);
245 let b = ndarray::Array1::from_vec(back);
246 (polygon.clone_with_points(a), polygon.clone_with_points(b))
247 }
248}
249
250impl<T> Plane<T, 3>
251where
252 T: Number + AddAssign + SubAssign + MulAssign + nalgebra::RealField,
253{
254 pub fn from_points(p1: Point3<T>, p2: Point3<T>, p3: Point3<T>) -> Self {
257 let v1 = p2 - p1;
258 let v2 = p3 - p1;
259 let normal = v1.cross(&v2).normalize();
260 let distance = normal.dot(&p1.coords);
261 Self { normal, distance }
262 }
263
264 pub fn rotation_to(&self, other: Plane<T, 3>) -> nalgebra::Matrix3<T>
311 where
312 T: num_traits::Signed,
313 {
314 let a = self.normal().normalize();
316 let b = other.normal().normalize();
317
318 let cos_theta = a.dot(&b);
320 let v = a.cross(&b);
321
322 let one = T::one();
323 let two = one + one;
324
325 let anti_parallel_threshold = T::from(1e-6).expect("BUG: 1e-6 is representable as T");
347 if cos_theta < -one + anti_parallel_threshold {
348 let least_aligned_axis = a.iamin();
350 let world_axis = SVector::<T, 3>::ith_axis(least_aligned_axis);
351
352 let rot_axis = a.cross(&world_axis.into_inner()).normalize();
354
355 return -nalgebra::Matrix3::identity() + rot_axis * rot_axis.transpose() * two;
358 }
359
360 let vx = nalgebra::Matrix3::new(
370 T::zero(),
371 -v[2],
372 v[1],
373 v[2],
374 T::zero(),
375 -v[0],
376 -v[1],
377 v[0],
378 T::zero(),
379 );
380
381 nalgebra::Matrix3::identity() + vx + vx * vx * (one / (one + cos_theta))
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use super::*;
389
390 #[test]
391 fn classify_point_front() {
392 let plane = Plane::xy_plane();
393 let point = Point::from([0f32, 0f32, 1f32]);
394 assert_eq!(plane.classify_point(&point), PlaneSide::Front);
395 }
396
397 #[test]
398 fn classify_point_back() {
399 let plane = Plane::xy_plane();
400 let point = Point::from([0f32, 0f32, -1f32]);
401 assert_eq!(plane.classify_point(&point), PlaneSide::Back);
402 }
403
404 #[test]
405 fn classify_point_on_plane() {
406 let plane = Plane::xy_plane();
407 let point = Point::from([5f32, -3f32, 0f32]);
408 assert_eq!(plane.classify_point(&point), PlaneSide::On);
409 }
410
411 #[test]
412 fn classify_point_offset_plane_front() {
413 let normal = SVector::from([0f32, 0f32, 1f32]);
414 let plane = Plane::new(normal, 5f32);
415 let point = Point::from([10f32, 20f32, 6f32]);
416 assert_eq!(plane.classify_point(&point), PlaneSide::Front);
417 }
418
419 #[test]
420 fn classify_point_offset_plane_back() {
421 let normal = SVector::from([0f32, 0f32, 1f32]);
422 let plane = Plane::new(normal, 5f32);
423 let point = Point::from([10f32, 20f32, 4f32]);
424 assert_eq!(plane.classify_point(&point), PlaneSide::Back);
425 }
426
427 #[test]
428 fn classify_point_offset_plane_on() {
429 let normal = SVector::from([0f32, 0f32, 1f32]);
430 let plane = Plane::new(normal, 5f32);
431 let point = Point::from([-10f32, 30f32, 5f32]);
432 assert_eq!(plane.classify_point(&point), PlaneSide::On);
433 }
434
435 #[test]
436 fn edge_both_front() {
437 let plane = Plane::xy_plane();
438 let a = Point::from([0f32, 0f32, 5f32]);
439 let b = Point::from([10f32, 10f32, 3f32]);
440 assert_eq!(plane.split_edge(&a, &b), Err(PlaneSide::Front));
441 }
442
443 #[test]
444 fn edge_both_back() {
445 let plane = Plane::xy_plane();
446 let a = Point::from([0f32, 0f32, -5f32]);
447 let b = Point::from([10f32, 10f32, -3f32]);
448 assert_eq!(plane.split_edge(&a, &b), Err(PlaneSide::Back));
449 }
450
451 #[test]
452 fn edge_both_on_plane() {
453 let plane = Plane::xy_plane();
454 let a = Point::from([10f32, 10f32, 0f32]);
455 let b = Point::from([-10f32, -10f32, 0f32]);
456 assert_eq!(plane.split_edge(&a, &b), Err(PlaneSide::On));
457 }
458
459 #[test]
460 fn edge_crossing_front_to_back() {
461 let plane = Plane::xy_plane();
462 let a = Point::from([0f32, 0f32, 2f32]);
463 let b = Point::from([0f32, 0f32, -2f32]);
464 let result = plane.split_edge(&a, &b);
465 assert!(result.is_ok());
466 let (pa, intersection, pb) = result.unwrap();
467 assert_eq!(pa, a);
468 assert_eq!(pb, b);
469 assert_eq!(intersection, Point::from([0f32, 0f32, 0f32]));
470 }
471
472 #[test]
473 fn edge_crossing_back_to_front() {
474 let plane = Plane::xy_plane();
475 let a = Point::from([0f32, 0f32, -2f32]);
476 let b = Point::from([0f32, 0f32, 2f32]);
477 let result = plane.split_edge(&a, &b);
478 assert!(result.is_ok());
479 let (pa, intersection, pb) = result.unwrap();
480 assert_eq!(pa, a);
481 assert_eq!(pb, b);
482 assert_eq!(intersection, Point::from([0f32, 0f32, 0f32]));
483 }
484
485 #[test]
486 fn edge_crossing_offset_plane() {
487 let normal = SVector::from([0f32, 0f32, 1f32]);
488 let plane = Plane::new(normal, 3f32);
489 let a = Point::from([1f32, 2f32, 1f32]);
490 let b = Point::from([1f32, 2f32, 5f32]);
491 let result = plane.split_edge(&a, &b);
492 assert!(result.is_ok());
493 let (_, intersection, _) = result.unwrap();
494 assert_eq!(intersection, Point::from([1f32, 2f32, 3f32]));
495 }
496
497 #[test]
498 fn edge_crossing_offset_plane_negative_distance() {
499 let normal = SVector::from([0f32, 0f32, 1f32]);
500 let plane = Plane::new(normal, -2f32);
501 let a = Point::from([5f32, 7f32, -5f32]);
502 let b = Point::from([5f32, 7f32, 1f32]);
503 let result = plane.split_edge(&a, &b);
504 assert!(result.is_ok());
505 let (_, intersection, _) = result.unwrap();
506 assert_eq!(intersection, Point::from([5f32, 7f32, -2f32]));
507 }
508
509 #[test]
510 fn edge_crossing_positive_x_plane() {
511 let normal = SVector::from([1f32, 0f32, 0f32]);
512 let plane = Plane::new(normal, 4f32);
513 let a = Point::from([2f32, 3f32, 5f32]);
514 let b = Point::from([8f32, 3f32, 5f32]);
515 let result = plane.split_edge(&a, &b);
516 assert!(result.is_ok());
517 let (_, intersection, _) = result.unwrap();
518 assert_eq!(intersection, Point::from([4f32, 3f32, 5f32]));
519 }
520
521 #[test]
522 fn edge_crossing_positive_y_plane() {
523 let normal = SVector::from([0f32, 1f32, 0f32]);
524 let plane = Plane::new(normal, -6f32);
525 let a = Point::from([1f32, -10f32, 2f32]);
526 let b = Point::from([1f32, 0f32, 2f32]);
527 let result = plane.split_edge(&a, &b);
528 assert!(result.is_ok());
529 let (_, intersection, _) = result.unwrap();
530 assert_eq!(intersection, Point::from([1f32, -6f32, 2f32]));
531 }
532
533 #[test]
534 fn edge_crossing_diagonal_plane() {
535 let normal = SVector::from([1f32, 0f32, 0f32]);
536 let plane = Plane::new(normal, 0f32);
537 let a = Point::from([-2f32, 3f32, 7f32]);
538 let b = Point::from([4f32, 3f32, 7f32]);
539 let result = plane.split_edge(&a, &b);
540 assert!(result.is_ok());
541 let (_, intersection, _) = result.unwrap();
542 assert_eq!(intersection, Point::from([0f32, 3f32, 7f32]));
543 }
544
545 #[test]
546 fn edge_crossing_arbitrary_points() {
547 let normal = SVector::from([0f32, 0f32, 1f32]);
548 let plane = Plane::new(normal, 10f32);
549 let a = Point::from([100f32, 200f32, 5f32]);
550 let b = Point::from([100f32, 200f32, 20f32]);
551 let result = plane.split_edge(&a, &b);
552 assert!(result.is_ok());
553 let (_, intersection, _) = result.unwrap();
554 assert_eq!(intersection, Point::from([100f32, 200f32, 10f32]));
555 }
556
557 #[test]
558 fn edge_crossing_slanted_edge() {
559 let normal = SVector::from([0f32, 0f32, 1f32]);
560 let plane = Plane::new(normal, 1f32);
561 let a = Point::from([0f32, 0f32, 0f32]);
562 let b = Point::from([10f32, 20f32, 2f32]);
563 let result = plane.split_edge(&a, &b);
564 assert!(result.is_ok());
565 let (_, intersection, _) = result.unwrap();
566 assert_eq!(intersection.x, 5f32);
567 assert_eq!(intersection.y, 10f32);
568 assert_eq!(intersection.z, 1f32);
569 }
570
571 #[test]
572 fn edge_crossing_asymmetric() {
573 let normal = SVector::from([0f32, 0f32, 1f32]);
574 let plane = Plane::new(normal, 5f32);
575 let a = Point::from([2f32, -3f32, 2f32]);
576 let b = Point::from([2f32, -3f32, 8f32]);
577 let result = plane.split_edge(&a, &b);
578 assert!(result.is_ok());
579 let (_, intersection, _) = result.unwrap();
580 assert_eq!(intersection, Point::from([2f32, -3f32, 5f32]));
581 }
582
583 #[test]
584 fn edge_crossing_diagonal() {
585 let plane = Plane::xy_plane();
586 let a = Point::from([0f32, 0f32, 1f32]);
587 let b = Point::from([0f32, 0f32, -1f32]);
588 let result = plane.split_edge(&a, &b);
589 assert!(result.is_ok());
590 let (_, intersection, _) = result.unwrap();
591 assert_eq!(intersection.z, 0f32);
592 }
593
594 #[test]
595 fn edge_one_on_plane_with_front() {
596 let plane = Plane::xy_plane();
597 let a = Point::from([0f32, 0f32, 0f32]);
598 let b = Point::from([0f32, 0f32, 5f32]);
599 let result = plane.split_edge(&a, &b);
600 assert!(result.is_ok());
601 let (_, intersection, _) = result.unwrap();
602 assert_eq!(intersection, Point::from([0f32, 0f32, 0f32]));
603 }
604
605 #[test]
606 fn edge_one_on_plane_with_front_reversed() {
607 let plane = Plane::xy_plane();
608 let a = Point::from([0f32, 0f32, 5f32]);
609 let b = Point::from([0f32, 0f32, 0f32]);
610 let result = plane.split_edge(&a, &b);
611 assert!(result.is_ok());
612 let (_, intersection, _) = result.unwrap();
613 assert_eq!(intersection, Point::from([0f32, 0f32, 0f32]));
614 }
615
616 #[test]
617 fn edge_one_on_plane_with_back() {
618 let plane = Plane::xy_plane();
619 let a = Point::from([0f32, 0f32, 0f32]);
620 let b = Point::from([0f32, 0f32, -5f32]);
621 let result = plane.split_edge(&a, &b);
622 assert!(result.is_ok());
623 let (_, intersection, _) = result.unwrap();
624 assert_eq!(intersection, Point::from([0f32, 0f32, 0f32]));
625 }
626
627 #[test]
628 fn edge_one_on_plane_with_back_reversed() {
629 let plane = Plane::xy_plane();
630 let a = Point::from([0f32, 0f32, -5f32]);
631 let b = Point::from([0f32, 0f32, 0f32]);
632 let result = plane.split_edge(&a, &b);
633 assert!(result.is_ok());
634 let (_, intersection, _) = result.unwrap();
635 assert_eq!(intersection, Point::from([0f32, 0f32, 0f32]));
636 }
637
638 #[test]
639 fn split_polygon_triangle_spanning_xy_plane() {
640 let plane = Plane::xy_plane();
641 let polygon = Polygon::from_points(vec![
642 Point3::new(0f32, 0f32, 1f32),
643 Point3::new(1f32, 0f32, 1f32),
644 Point3::new(0.5f32, 1f32, -1f32),
645 ])
646 .unwrap();
647 assert_eq!(
648 plane.classify_polygon(&polygon),
649 PolygonClassification::Spanning
650 );
651 let (front, back) = plane.split_polygon(polygon);
652 assert!(front.vertices().len() >= 3);
653 assert!(back.vertices().len() >= 3);
654 for v in front.vertices().iter() {
655 assert!(plane.classify_point(v) != PlaneSide::Back);
656 }
657 for v in back.vertices().iter() {
658 assert!(plane.classify_point(v) != PlaneSide::Front);
659 }
660 }
661
662 #[test]
663 fn split_polygon_square_spanning_xy_plane() {
664 let plane = Plane::xy_plane();
665 let polygon: Polygon<f32, 3> = Polygon::from_points(vec![
666 Point3::new(-1f32, -1f32, 1f32),
667 Point3::new(1f32, -1f32, 1f32),
668 Point3::new(1f32, 1f32, -1f32),
669 Point3::new(-1f32, 1f32, -1f32),
670 ])
671 .unwrap();
672 assert_eq!(
673 plane.classify_polygon(&polygon),
674 PolygonClassification::Spanning
675 );
676 let (front, back) = plane.split_polygon(polygon);
677 assert!(front.vertices().len() >= 3);
678 assert!(back.vertices().len() >= 3);
679 for v in front.vertices().iter() {
680 assert!(plane.classify_point(v) != PlaneSide::Back);
681 }
682 for v in back.vertices().iter() {
683 assert!(plane.classify_point(v) != PlaneSide::Front);
684 }
685 }
686
687 #[test]
688 fn split_polygon_vertex_counts_sum() {
689 let plane = Plane::xy_plane();
690 let polygon: Polygon<f32, 3> = Polygon::from_points(vec![
691 Point3::new(0f32, 0f32, 2f32),
692 Point3::new(2f32, 0f32, 2f32),
693 Point3::new(2f32, 2f32, -2f32),
694 Point3::new(0f32, 2f32, -2f32),
695 ])
696 .unwrap();
697 let (front, back) = plane.split_polygon(polygon);
698 assert_eq!(front.vertices().len() + back.vertices().len(), 8);
699 }
700
701 #[test]
702 fn split_polygon_offset_plane() {
703 let plane = Plane::new(SVector::from([0f32, 0f32, 1f32]), 1f32);
704 let polygon: Polygon<f32, 3> = Polygon::from_points(vec![
705 Point3::new(0f32, 0f32, 0f32),
706 Point3::new(2f32, 0f32, 0f32),
707 Point3::new(2f32, 2f32, 2f32),
708 Point3::new(0f32, 2f32, 2f32),
709 ])
710 .unwrap();
711 let (front, back) = plane.split_polygon(polygon);
712 for v in front.vertices().iter() {
713 assert!(plane.classify_point(v) != PlaneSide::Back);
714 }
715 for v in back.vertices().iter() {
716 assert!(plane.classify_point(v) != PlaneSide::Front);
717 }
718 }
719
720 fn assert_valid_rotation(a: SVector<f64, 3>, b: SVector<f64, 3>, map_tol: f64) {
724 let src = Plane::new(a, 0.0);
725 let dst = Plane::new(b, 0.0);
726 let r = src.rotation_to(dst);
727
728 let ortho_err = (r.transpose() * r - nalgebra::Matrix3::<f64>::identity()).norm();
729 assert!(
730 ortho_err < 1e-9,
731 "‖RᵀR−I‖ = {ortho_err} (a = {a:?}, b = {b:?})"
732 );
733
734 let det = r.determinant();
735 assert!(
736 (det - 1.0).abs() < 1e-9,
737 "det(R) = {det} (a = {a:?}, b = {b:?})"
738 );
739
740 let map_err = (r * src.normal() - dst.normal()).norm();
741 assert!(
742 map_err < map_tol,
743 "‖R·a − b‖ = {map_err} exceeds {map_tol} (a = {a:?}, b = {b:?})"
744 );
745 }
746
747 #[test]
748 fn rotation_to_generic_pair() {
749 let a = SVector::from([1.0, 0.0, 0.0]);
751 let b = SVector::from([0.0, 1.0, 0.0]);
752 assert_valid_rotation(a, b, 1e-12);
753 }
754
755 #[test]
756 fn rotation_to_near_parallel_is_identity() {
757 let a = SVector::from([0.3, -0.5, 0.8]).normalize();
760 let perp = a.cross(&SVector::from([1.0, 0.0, 0.0])).normalize();
761 let b = (a * 1e-7_f64.cos() + perp * 1e-7_f64.sin()).normalize();
762 assert_valid_rotation(a, b, 1e-6);
763 }
764
765 #[test]
766 fn rotation_to_near_and_exact_anti_parallel() {
767 let a = SVector::from([0.3, -0.5, 0.8]).normalize();
772 let perp = a.cross(&SVector::from([1.0, 0.0, 0.0])).normalize();
773
774 for &delta in &[0.0_f64, 5e-8, 1e-7, 1e-6] {
777 let b = (-a * delta.cos() + perp * delta.sin()).normalize();
778 assert_valid_rotation(a, b, 1e-5);
781 }
782 }
783}