1use crate::prelude::*;
7use approx::{AbsDiffEq, UlpsEq};
8use num_traits::Float;
9use std::borrow::Borrow;
10
11#[allow(clippy::upper_case_acronyms)]
13#[derive(Debug, Copy, Clone, Eq, PartialEq)]
14pub enum Plane {
15 XY,
16 XZ,
17 YZ,
18}
19
20impl Plane {
21 #[allow(dead_code)] #[inline(always)]
23 pub(crate) fn get_plane<T>(aabb: &T) -> Option<Self>
31 where
32 T: Aabb3,
33 T::Vector: GenericVector3<Aabb = T> + HasXY,
34 {
35 Plane::get_plane_relaxed::<T::Vector>(
36 aabb,
37 <<T as Aabb3>::Vector as HasXY>::Scalar::default_epsilon(),
38 <<T as Aabb3>::Vector as HasXY>::Scalar::default_max_ulps(),
39 )
40 }
41
42 #[allow(dead_code)] pub(crate) fn get_plane_relaxed<T>(
51 aabb: &T::Aabb,
52 epsilon: T::Scalar,
53 max_ulps: u32,
54 ) -> Option<Plane>
55 where
56 T: GenericVector3,
57 T::Aabb: Aabb3<Vector = T>,
58 {
59 if aabb.is_empty() {
60 return None;
61 }
62
63 let (_, _, delta) = aabb.extents();
64 let max_delta = T::Scalar::max(T::Scalar::max(delta.x(), delta.y()), delta.z());
65
66 if T::Scalar::ZERO.ulps_eq(&max_delta, epsilon, max_ulps) {
68 return None;
69 }
70
71 let norm_dx = delta.x() / max_delta;
73 let norm_dy = delta.y() / max_delta;
74 let norm_dz = delta.z() / max_delta;
75
76 let is_x_negligible = T::Scalar::ZERO.ulps_eq(&norm_dx, epsilon, max_ulps);
78 let is_y_negligible = T::Scalar::ZERO.ulps_eq(&norm_dy, epsilon, max_ulps);
79 let is_z_negligible = T::Scalar::ZERO.ulps_eq(&norm_dz, epsilon, max_ulps);
80
81 match (is_x_negligible, is_y_negligible, is_z_negligible) {
82 (true, false, false) => Some(Plane::YZ), (false, true, false) => Some(Plane::XZ), (false, false, true) => Some(Plane::XY), _ => None, }
87 }
88
89 #[inline(always)]
94 pub fn point_to_3d<T: GenericVector3>(&self, point: T::Vector2) -> T {
95 match self {
96 Plane::XY => T::new_3d(point.x(), point.y(), T::Scalar::ZERO),
97 Plane::XZ => T::new_3d(point.x(), T::Scalar::ZERO, point.y()),
98 Plane::YZ => T::new_3d(T::Scalar::ZERO, point.y(), point.x()),
99 }
100 }
101
102 #[inline(always)]
107 pub fn point_to_2d<T: GenericVector3>(&self, point: T) -> T::Vector2 {
108 match self {
109 Plane::XY => T::Vector2::new_2d(point.x(), point.y()),
110 Plane::XZ => T::Vector2::new_2d(point.x(), point.z()),
111 Plane::YZ => T::Vector2::new_2d(point.z(), point.y()),
112 }
113 }
114
115 #[inline]
121 pub fn points_to_3d<'a, T: GenericVector3 + 'a, I>(
122 &self,
123 iter: I,
124 ) -> impl Iterator<Item = T> + 'a
125 where
126 I: IntoIterator + 'a,
127 I::Item: Borrow<T::Vector2>,
128 <T as GenericVector3>::Vector2: 'a + ToOwned<Owned = T::Vector2>,
129 {
130 let mapper = match self {
131 Plane::XY => |p: T::Vector2| T::new_3d(p.x(), p.y(), T::Scalar::ZERO),
132 Plane::XZ => |p: T::Vector2| T::new_3d(p.x(), T::Scalar::ZERO, p.y()),
133 Plane::YZ => |p: T::Vector2| T::new_3d(T::Scalar::ZERO, p.y(), p.x()),
134 };
135
136 iter.into_iter().map(move |v| mapper(v.borrow().to_owned()))
137 }
138
139 #[inline]
145 pub fn points_to_2d<'a, T: GenericVector2 + 'a, I>(
146 &self,
147 iter: I,
148 ) -> impl Iterator<Item = T> + 'a
149 where
150 I: IntoIterator + 'a,
151 I::Item: Borrow<T::Vector3>,
152 <T as GenericVector2>::Vector3: 'a + ToOwned<Owned = T::Vector3>,
153 {
154 let mapper = match self {
155 Plane::XY => |p: T::Vector3| T::new_2d(p.x(), p.y()),
156 Plane::XZ => |p: T::Vector3| T::new_2d(p.x(), p.z()),
157 Plane::YZ => |p: T::Vector3| T::new_2d(p.z(), p.y()),
158 };
159 iter.into_iter().map(move |v| mapper(v.borrow().to_owned()))
160 }
161}