1use crate::*;
26
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct BoundingBox2D {
32 min: Point2D,
33 max: Point2D,
34}
35
36impl BoundingBox2D {
37 pub fn new<P1, P2>(min: &P1, max: &P2) -> Result<BoundingBox2D>
39 where
40 P1: Is2D,
41 P2: Is2D,
42 {
43 if min.x() == max.x() || min.y() == max.y() {
44 Err(ErrorKind::MinMaxEqual)
45 } else if min.x() > max.x() || min.y() > max.y() {
46 Err(ErrorKind::MinMaxSwapped)
47 } else {
48 Ok(BoundingBox2D {
49 min: Point2D {
50 x: min.x(),
51 y: min.y(),
52 },
53 max: Point2D {
54 x: max.x(),
55 y: max.y(),
56 },
57 })
58 }
59 }
60 pub fn from_iterator<'a, It2D, P>(source: It2D) -> Result<BoundingBox2D>
62 where
63 It2D: IntoIterator<Item = &'a P>,
64 P: 'a + Is2D + Sized,
65 {
66 let mut count = 0;
67
68 let mut minx: f64 = 0.0;
69 let mut miny: f64 = 0.0;
70 let mut maxx: f64 = 0.0;
71 let mut maxy: f64 = 0.0;
72
73 for p in source {
74 if count == 0 {
75 minx = p.x();
76 miny = p.y();
77 maxx = p.x();
78 maxy = p.y();
79 count += 1;
80 continue;
81 }
82 if p.x() < minx {
83 minx = p.x();
84 }
85 if p.y() < miny {
86 miny = p.y();
87 }
88 if p.x() > maxx {
89 maxx = p.x();
90 }
91 if p.y() > maxy {
92 maxy = p.y();
93 }
94 count += 1;
95 }
96 if count >= 2 {
97 Self::new(&Point2D { x: minx, y: miny }, &Point2D { x: maxx, y: maxy })
98 } else {
99 Err(ErrorKind::TooFewPoints)
100 }
101 }
102 pub fn from_into_iterator<It2D, P>(source: It2D) -> Result<BoundingBox2D>
104 where
105 It2D: IntoIterator<Item = P>,
106 P: Is2D + Sized,
107 {
108 let mut count = 0;
109
110 let mut minx: f64 = 0.0;
111 let mut miny: f64 = 0.0;
112 let mut maxx: f64 = 0.0;
113 let mut maxy: f64 = 0.0;
114
115 for p in source {
116 if count == 0 {
117 minx = p.x();
118 miny = p.y();
119 maxx = p.x();
120 maxy = p.y();
121 count += 1;
122 continue;
123 }
124 if p.x() < minx {
125 minx = p.x();
126 }
127 if p.y() < miny {
128 miny = p.y();
129 }
130 if p.x() > maxx {
131 maxx = p.x();
132 }
133 if p.y() > maxy {
134 maxy = p.y();
135 }
136 count += 1;
137 }
138 if count >= 2 {
139 Self::new(&Point2D { x: minx, y: miny }, &Point2D { x: maxx, y: maxy })
140 } else {
141 Err(ErrorKind::TooFewPoints)
142 }
143 }
144 pub fn min_p(&self) -> Point2D {
146 self.min.clone()
147 }
148 pub fn max_p(&self) -> Point2D {
150 self.max.clone()
151 }
152 pub fn size_x(&self) -> Positive {
154 Positive::new((self.max.x() - self.min.x()).abs()).unwrap() }
156 pub fn size_y(&self) -> Positive {
158 Positive::new((self.max.y() - self.min.y()).abs()).unwrap() }
160 pub fn sizes(&self) -> [Positive; 2] {
162 [self.size_x(), self.size_y()]
163 }
164 pub fn center_bb(&self) -> Point2D {
166 Point2D {
167 x: self.min.x() + (self.max.x() - self.min.x()) / 2.0,
168 y: self.min.y() + (self.max.y() - self.min.y()) / 2.0,
169 }
170 }
171 pub fn is_inside(&self, other: &BoundingBox2D) -> bool {
173 self.min.x() > other.min.x()
174 && self.min.y() > other.min.y()
175 && self.max.x() < other.max.x()
176 && self.max.y() < other.max.y()
177 }
178 pub fn contains<P>(&self, other: &P) -> bool
180 where
181 Self: Sized,
182 P: Is2D,
183 {
184 other.x() > self.min.x()
185 && other.x() < self.max.x()
186 && other.y() > self.min.y()
187 && other.y() < self.max.y()
188 }
189 pub fn has_inside(&self, other: &BoundingBox2D) -> bool {
191 self.min.x() < other.min.x()
192 && self.min.y() < other.min.y()
193 && self.max.x() > other.max.x()
194 && self.max.y() > other.max.y()
195 }
196 pub fn collides_with(&self, other: &BoundingBox2D) -> bool {
198 2.0 * (self.center_bb().x - other.center_bb().x).abs()
199 < ((self.size_x() + other.size_x()).get())
200 && 2.0 * (self.center_bb().y - other.center_bb().y).abs()
201 < ((self.size_y() + other.size_y()).get())
202 }
203 pub fn crossing_x_value(&self, x: f64) -> bool {
205 self.min.x() < x && self.max.x() > x
206 }
207 pub fn crossing_y_value(&self, y: f64) -> bool {
209 self.min.y() < y && self.max.y() > y
210 }
211 pub fn corners(&self) -> [Point2D; 4] {
213 [
214 Point2D::new(self.min.x(), self.min.y()),
215 Point2D::new(self.min.x(), self.max.y()),
216 Point2D::new(self.max.x(), self.min.y()),
217 Point2D::new(self.max.x(), self.max.y()),
218 ]
219 }
220 pub fn distance<P>(&self, other: &P) -> NonNegative
222 where
223 P: Is2D,
224 {
225 let sqr_dist = self.sqr_distance(other).get();
226 NonNegative::new(sqr_dist.sqrt()).unwrap()
227 }
228 pub fn sqr_distance<P>(&self, other: &P) -> NonNegative
230 where
231 P: Is2D,
232 {
233 let dx = max_f64_3(
234 self.min_p().x() - other.x(),
235 0.0,
236 other.x() - self.max_p().x(),
237 );
238 let dy = max_f64_3(
239 self.min_p().y() - other.y(),
240 0.0,
241 other.y() - self.max_p().y(),
242 );
243 NonNegative::new(dx * dx + dy * dy).unwrap()
244 }
245}
246
247impl Default for BoundingBox2D {
250 fn default() -> Self {
251 BoundingBox2D {
252 min: Point2D { x: -0.5, y: -0.5 },
253 max: Point2D { x: 0.5, y: 0.5 },
254 }
255 }
256}
257
258impl IsND for BoundingBox2D {
259 fn n_dimensions() -> usize {
260 2
261 }
262
263 fn position_nd(&self, dimension: usize) -> Result<f64> {
264 self.center_bb().position_nd(dimension)
265 }
266}
267
268impl Is2D for BoundingBox2D {
269 #[inline(always)]
270 fn x(&self) -> f64 {
271 self.center_bb().x()
272 }
273
274 #[inline(always)]
275 fn y(&self) -> f64 {
276 self.center_bb().y()
277 }
278}
279
280impl IsMovable2D for BoundingBox2D {
281 fn move_by(&mut self, x: f64, y: f64) {
282 self.min.move_by(x, y);
283 self.max.move_by(x, y);
284 }
285}
286
287impl HasBoundingBox2D for BoundingBox2D {
288 fn bounding_box(&self) -> BoundingBox2D {
289 BoundingBox2D::new(&self.min, &self.max).unwrap() }
291}
292
293impl HasBoundingBox2DMaybe for BoundingBox2D {
294 fn bounding_box_maybe(&self) -> Result<BoundingBox2D> {
295 Ok(self.bounding_box())
296 }
297}
298
299impl HasDistanceTo<BoundingBox2D> for BoundingBox2D {
300 fn sqr_distance(&self, other: &BoundingBox2D) -> NonNegative {
301 let mut dx = 0.0;
302 let mut dy = 0.0;
303
304 if other.max_p().x() < self.min_p().x() {
305 dx = other.max_p().x() - self.min_p().x();
306 } else if other.min_p().x() > self.max_p().x() {
307 dx = other.min_p().x() - self.max_p().x();
308 }
309
310 if other.max_p().y() < self.min_p().y() {
311 dy = other.max_p().y() - self.min_p().y();
312 } else if other.min_p().y() > self.max_p().y() {
313 dy = other.min_p().y() - self.max_p().y();
314 }
315
316 NonNegative::new(dx * dx + dy * dy).unwrap()
317 }
318}
319
320impl IsScalable for BoundingBox2D {
321 fn scale(&mut self, factor: Positive) {
322 let c = self.center_bb();
323 let min_x = c.x - (0.5 * factor.get() * self.size_x().get());
324 let max_x = c.x + (0.5 * factor.get() * self.size_x().get());
325 let min_y = c.y - (0.5 * factor.get() * self.size_y().get());
326 let max_y = c.y + (0.5 * factor.get() * self.size_y().get());
327
328 self.min.set_xy(min_x, min_y);
329 self.max.set_xy(max_x, max_y);
330 }
331}
332
333impl IsMergeable for BoundingBox2D {
334 fn consume(&mut self, other: Self) {
335 let (mut min_x, mut min_y) = (self.min.x(), self.min.y());
336 let (mut max_x, mut max_y) = (self.max.x(), self.max.y());
337
338 if other.min.x() < min_x {
339 min_x = other.min.x()
340 }
341 if other.min.y() < min_y {
342 min_y = other.min.y()
343 }
344
345 if other.max.x() > max_x {
346 max_x = other.max.x()
347 }
348 if other.max.y() > max_y {
349 max_y = other.max.y()
350 }
351
352 self.min.set_xy(min_x, min_y);
353 self.max.set_xy(max_x, max_y);
354 }
355
356 fn combine(&self, other: &Self) -> Self {
357 let (mut min_x, mut min_y) = (self.min.x(), self.min.y());
358 let (mut max_x, mut max_y) = (self.max.x(), self.max.y());
359
360 if other.min.x() < min_x {
361 min_x = other.min.x()
362 }
363 if other.min.y() < min_y {
364 min_y = other.min.y()
365 }
366
367 if other.max.x() > max_x {
368 max_x = other.max.x()
369 }
370 if other.max.y() > max_y {
371 max_y = other.max.y()
372 }
373
374 let min = Point2D::new(min_x, min_y);
375 let max = Point2D::new(max_x, max_y);
376
377 BoundingBox2D { min, max }
378 }
379}