microcad_core/geo3d/
bounds.rs1use cgmath::{ElementWise, InnerSpace};
5use derive_more::Deref;
6
7use crate::*;
8
9pub type Bounds3D = Bounds<Vec3>;
11
12pub struct Bounds3DCorners {
14 bounds: Bounds3D,
15 index: u8, }
17
18impl Iterator for Bounds3DCorners {
19 type Item = Vec3;
20
21 fn next(&mut self) -> Option<Self::Item> {
22 if self.index >= 8 {
23 return None;
24 }
25
26 let i = self.index;
27 self.index += 1;
28
29 let x = if i & 1 == 0 {
30 self.bounds.min.x
31 } else {
32 self.bounds.max.x
33 };
34 let y = if i & 2 == 0 {
35 self.bounds.min.y
36 } else {
37 self.bounds.max.y
38 };
39 let z = if i & 4 == 0 {
40 self.bounds.min.z
41 } else {
42 self.bounds.max.z
43 };
44
45 Some(Vec3 { x, y, z })
46 }
47}
48
49impl Bounds3D {
50 pub fn extend(self, other: Bounds3D) -> Self {
52 match (self.is_valid(), other.is_valid()) {
53 (false, false) => Self::default(),
54 (false, true) => other,
55 (true, false) => self,
56 (true, true) => Self::new(
57 Vec3::new(
58 self.min.x.min(other.min.x),
59 self.min.y.min(other.min.y),
60 self.min.z.min(other.min.z),
61 ),
62 Vec3::new(
63 self.max.x.max(other.max.x),
64 self.max.y.max(other.max.y),
65 self.max.z.max(other.max.z),
66 ),
67 ),
68 }
69 }
70
71 pub fn is_valid(&self) -> bool {
73 self.min.x <= self.max.x && self.min.y <= self.max.y && self.min.z <= self.max.z
74 }
75
76 pub fn extend_by_point(&mut self, p: Vec3) {
78 self.min.x = p.x.min(self.min.x);
79 self.min.y = p.y.min(self.min.y);
80 self.min.z = p.z.min(self.min.z);
81 self.max.x = p.x.max(self.max.x);
82 self.max.y = p.y.max(self.max.y);
83 self.max.z = p.z.max(self.max.z);
84 }
85
86 pub fn corners(&self) -> Bounds3DCorners {
88 Bounds3DCorners {
89 bounds: self.clone(),
90 index: 0,
91 }
92 }
93
94 pub fn map_vec3(&self, v: Vec3) -> Vec3 {
98 (v - self.min).div_element_wise(self.max - self.min)
99 }
100
101 pub fn radius(&self) -> Scalar {
103 (self.max - self.min).magnitude() * 0.5
104 }
105
106 pub fn center(&self) -> Vec3 {
108 (self.min + self.max) * 0.5
109 }
110
111 pub fn distance_center_to_boundary(&self, dir: Vec3) -> Length {
113 let center = self.center();
114
115 let tx = if dir.x > 0.0 {
117 (self.max.x - center.x) / dir.x
118 } else if dir.x < 0.0 {
119 (self.min.x - center.x) / dir.x
120 } else {
121 f64::INFINITY
122 };
123
124 let ty = if dir.y > 0.0 {
126 (self.max.y - center.y) / dir.y
127 } else if dir.y < 0.0 {
128 (self.min.y - center.y) / dir.y
129 } else {
130 f64::INFINITY
131 };
132
133 let tz = if dir.z > 0.0 {
135 (self.max.z - center.z) / dir.z
136 } else if dir.y < 0.0 {
137 (self.min.z - center.z) / dir.z
138 } else {
139 f64::INFINITY
140 };
141
142 Length::mm(tx.min(ty).min(tz))
144 }
145}
146
147impl Default for Bounds3D {
148 fn default() -> Self {
149 let min = Scalar::MAX;
151 let max = Scalar::MIN;
152 Self::new(Vec3::new(min, min, min), Vec3::new(max, max, max))
153 }
154}
155
156impl FromIterator<Vec3> for Bounds3D {
157 fn from_iter<I: IntoIterator<Item = Vec3>>(iter: I) -> Self {
158 let mut iter = iter.into_iter();
159 let first_point = match iter.next() {
160 Some(point) => point,
161 None => return Bounds3D::default(),
162 };
163
164 let mut min = first_point;
165 let mut max = first_point;
166
167 iter.for_each(|p| {
168 min.x = min.x.min(p.x);
169 min.y = min.y.min(p.y);
170 min.z = min.z.min(p.z);
171
172 max.x = max.x.max(p.x);
173 max.y = max.y.max(p.y);
174 max.z = max.z.max(p.z);
175 });
176
177 Bounds3D::new(min, max)
178 }
179}
180
181impl Transformed3D for Bounds3D {
182 fn transformed_3d(&self, mat: &Mat4) -> Self {
183 let mut bounds = Bounds3D::default();
184 self.corners()
185 .for_each(|corner| bounds.extend_by_point((mat * corner.extend(1.0)).truncate()));
186
187 bounds
188 }
189}
190
191pub trait CalcBounds3D {
193 fn calc_bounds_3d(&self) -> Bounds3D;
195}
196
197pub trait Transformed3D<T = Self> {
199 fn transformed_3d(&self, mat: &Mat4) -> T;
201}
202
203#[derive(Clone, Default, Debug, Deref)]
205pub struct WithBounds3D<T: CalcBounds3D + Transformed3D> {
206 pub bounds: Bounds3D,
208 #[deref]
210 pub inner: T,
211}
212
213impl<T: CalcBounds3D + Transformed3D> WithBounds3D<T> {
214 pub fn new(inner: T, bounds: Bounds3D) -> Self {
216 Self { bounds, inner }
217 }
218
219 pub fn update_bounds(&mut self) {
221 self.bounds = self.inner.calc_bounds_3d()
222 }
223}
224
225impl<T: CalcBounds3D + Transformed3D> Transformed3D for WithBounds3D<T> {
226 fn transformed_3d(&self, mat: &Mat4) -> Self {
227 let inner = self.inner.transformed_3d(mat);
228 let bounds = inner.calc_bounds_3d();
229 Self { inner, bounds }
230 }
231}
232
233impl From<Geometry3D> for WithBounds3D<Geometry3D> {
234 fn from(geo: Geometry3D) -> Self {
235 let bounds = geo.calc_bounds_3d();
236 Self::new(geo, bounds)
237 }
238}