microcad_core/geo3d/
bounds.rs1use crate::{Mat4, RenderResolution, Vec3};
5
6#[derive(Debug, Clone)]
8struct BoundsInner {
9 min: Vec3,
11 max: Vec3,
13}
14
15pub struct Bounds3DCorners {
17 bounds: BoundsInner,
18 index: u8, }
20
21impl Iterator for Bounds3DCorners {
22 type Item = Vec3;
23
24 fn next(&mut self) -> Option<Self::Item> {
25 if self.index >= 8 {
26 return None;
27 }
28
29 let i = self.index;
30 self.index += 1;
31
32 let x = if i & 1 == 0 {
33 self.bounds.min.x
34 } else {
35 self.bounds.max.x
36 };
37 let y = if i & 2 == 0 {
38 self.bounds.min.y
39 } else {
40 self.bounds.max.y
41 };
42 let z = if i & 4 == 0 {
43 self.bounds.min.z
44 } else {
45 self.bounds.max.z
46 };
47
48 Some(Vec3 { x, y, z })
49 }
50}
51
52#[derive(Debug, Clone, Default)]
54pub struct Bounds3D(Option<BoundsInner>);
55
56impl Bounds3D {
57 pub fn new(min: Vec3, max: Vec3) -> Self {
59 Self(Some(BoundsInner { min, max }))
60 }
61
62 pub fn min(&self) -> Option<Vec3> {
64 self.0.as_ref().map(|s| s.min)
65 }
66
67 pub fn max(&self) -> Option<Vec3> {
69 self.0.as_ref().map(|s| s.max)
70 }
71
72 pub fn min_max(&self) -> Option<(Vec3, Vec3)> {
74 self.0.as_ref().map(|s| (s.min, s.max))
75 }
76
77 pub fn extend(self, other: Bounds3D) -> Self {
79 match (self.0, other.0) {
80 (None, None) => Self(None),
81 (None, Some(b)) | (Some(b), None) => Self(Some(b)),
82 (Some(b1), Some(b2)) => Self::new(
83 Vec3::new(
84 b1.min.x.min(b2.min.x),
85 b1.min.y.min(b2.min.y),
86 b1.min.z.min(b2.min.z),
87 ),
88 Vec3::new(
89 b1.max.x.max(b2.max.x),
90 b1.max.y.max(b2.max.y),
91 b1.max.z.max(b2.max.z),
92 ),
93 ),
94 }
95 }
96
97 pub fn extend_by_point(&mut self, p: Vec3) {
99 match &mut self.0 {
100 Some(bounds) => {
101 *bounds = BoundsInner {
102 min: Vec3::new(
103 bounds.min.x.min(p.x),
104 bounds.min.y.min(p.y),
105 bounds.min.z.min(p.z),
106 ),
107 max: Vec3::new(
108 bounds.max.x.max(p.x),
109 bounds.max.y.max(p.y),
110 bounds.max.z.max(p.z),
111 ),
112 }
113 }
114 None => *self = Self::new(p, p),
115 }
116 }
117
118 pub fn corners(&self) -> Bounds3DCorners {
120 Bounds3DCorners {
121 bounds: self.0.clone().expect("Bounds"),
122 index: 0,
123 }
124 }
125}
126
127impl FromIterator<Vec3> for Bounds3D {
128 fn from_iter<I: IntoIterator<Item = Vec3>>(iter: I) -> Self {
129 let mut iter = iter.into_iter();
130 let first_point = match iter.next() {
131 Some(point) => point,
132 None => return Bounds3D(None),
133 };
134
135 let mut min = first_point;
136 let mut max = first_point;
137
138 iter.for_each(|p| {
139 min.x = min.x.min(p.x);
140 min.y = min.y.min(p.y);
141 min.z = min.z.min(p.z);
142
143 max.x = max.x.max(p.x);
144 max.y = max.y.max(p.y);
145 max.z = max.z.max(p.z);
146 });
147
148 Bounds3D::new(min, max)
149 }
150}
151
152impl Transformed3D for Bounds3D {
153 fn transformed_3d(&self, _: &crate::RenderResolution, mat: &crate::Mat4) -> Self {
154 let mut bounds = Bounds3D::default();
155 self.corners()
156 .for_each(|corner| bounds.extend_by_point((mat * corner.extend(1.0)).truncate()));
157
158 bounds
159 }
160}
161
162pub trait FetchBounds3D {
164 fn fetch_bounds_3d(&self) -> Bounds3D;
166}
167
168pub trait Transformed3D<T = Self> {
170 fn transformed_3d(&self, render_resolution: &RenderResolution, mat: &Mat4) -> T;
172}