1use geo_nd::vector;
20
21use crate::{Point, Range, Transform};
22
23#[derive(Debug, Clone, Copy, Default, PartialEq)]
26pub struct BBox {
30 pub x: Range,
32 pub y: Range,
34}
35
36impl std::fmt::Display for BBox {
38 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
41 write!(
42 f,
43 "[({},{}):({},{})]",
44 self.x[0], self.y[0], self.x[1], self.y[1]
45 )
46 }
47
48 }
50
51impl BBox {
53 pub fn none() -> Self {
56 Self {
57 x: Range::none(),
58 y: Range::none(),
59 }
60 }
61
62 pub fn is_none(&self) -> bool {
65 self.x.is_none() || self.y.is_none()
66 }
67
68 pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Self {
72 let x = if x0 < x1 {
73 Range::new(x0, x1)
74 } else {
75 Range::new(x1, x0)
76 };
77 let y = if y0 < y1 {
78 Range::new(y0, y1)
79 } else {
80 Range::new(y1, y0)
81 };
82 Self { x, y }
83 }
84
85 pub fn of_ranges(x: Range, y: Range) -> Self {
88 Self { x, y }
89 }
90
91 pub fn of_points(pts: &[Point]) -> Self {
94 let mut s = Self::none();
95 for p in pts.iter() {
96 s.x = s.x.include(p[0]);
97 s.y = s.y.include(p[1]);
98 }
99 s
100 }
101
102 pub fn of_cwh(centre: Point, width: f64, height: f64) -> Self {
105 Self::new(
106 centre[0] - width / 2.,
107 centre[1] - height / 2.,
108 centre[0] + width / 2.,
109 centre[1] + height / 2.,
110 )
111 }
112
113 pub fn pt_within(&self, pt: Point) -> Point {
120 if self.is_none() {
121 pt
122 } else {
123 let (rx, ry) = (pt[0] - self.x[0], pt[1] - self.y[0]);
124 [rx / self.x.size(), ry / self.y.size()].into()
125 }
126 }
127
128 pub fn add_as_points(&self, close: bool, mut v: Vec<Point>) -> Vec<Point> {
133 v.push([self.x[0], self.y[0]].into());
134 v.push([self.x[1], self.y[0]].into());
135 v.push([self.x[1], self.y[1]].into());
136 v.push([self.x[0], self.y[1]].into());
137 if close {
138 v.push([self.x[0], self.y[0]].into());
139 }
140 v
141 }
142
143 pub fn get_wh(&self) -> (f64, f64) {
146 (self.x.size(), self.y.size())
147 }
148
149 pub fn center(&self) -> Point {
152 [self.x.center(), self.y.center()].into()
153 }
154
155 pub fn width(&self) -> f64 {
158 self.x.size()
159 }
160
161 pub fn height(&self) -> f64 {
165 self.y.size()
166 }
167
168 pub fn get_cwh(&self) -> (Point, f64, f64) {
171 (self.center(), self.width(), self.height())
172 }
173
174 pub fn get_bounds(&self) -> (f64, f64, f64, f64) {
177 (self.x[0], self.y[0], self.width(), self.height())
178 }
179
180 #[must_use]
185 pub fn enlarge(mut self, value: f64) -> Self {
186 self.x = self.x.enlarge(value);
187 self.y = self.y.enlarge(value);
188 self
189 }
190
191 #[must_use]
195 pub fn reduce(mut self, value: f64) -> Self {
196 self.x = self.x.reduce(value);
197 self.y = self.y.reduce(value);
198 self
199 }
200
201 #[must_use]
206 pub fn expand(mut self, other: &[f64; 4], scale: f64) -> Self {
207 self.x = Range::new(self.x[0] - scale * other[0], self.x[1] + scale * other[2]);
208 self.y = Range::new(self.y[0] - scale * other[1], self.y[1] + scale * other[3]);
209 self
210 }
211
212 #[must_use]
215 #[inline]
216 pub fn shrink(self, other: &[f64; 4], scale: f64) -> Self {
217 self.expand(other, -scale)
218 }
219
220 #[must_use]
223 #[inline]
224 pub fn include(mut self, p: Point) -> Self {
225 self.x = self.x.include(p[0]);
226 self.y = self.y.include(p[1]);
227 self
228 }
229
230 #[must_use]
233 #[inline]
234 pub fn union(mut self, other: Self) -> Self {
235 if other.is_none() {
236 self
237 } else if self.is_none() {
238 other
239 } else {
240 self.x = self.x.union(&other.x);
241 self.y = self.y.union(&other.y);
242 self
243 }
244 }
245
246 #[must_use]
249 #[inline]
250 pub fn intersect(mut self, other: Self) -> Self {
251 if other.is_none() {
252 other
253 } else if self.is_none() {
254 self
255 } else {
256 self.x = self.x.intersect(&other.x);
257 self.y = self.y.intersect(&other.y);
258 self
259 }
260 }
261
262 #[must_use]
266 pub fn new_rotated_around(&self, pt: &Point, degrees: f64) -> Self {
267 let radians = degrees.to_radians();
268 let p0 = vector::rotate_around([self.x[0], self.y[0]], pt.as_ref(), radians, 0, 1);
269 let p1 = vector::rotate_around([self.x[1], self.y[0]], pt.as_ref(), radians, 0, 1);
270 let p2 = vector::rotate_around([self.x[0], self.y[1]], pt.as_ref(), radians, 0, 1);
271 let p3 = vector::rotate_around([self.x[1], self.y[1]], pt.as_ref(), radians, 0, 1);
272 let mut x = Range::none();
273 let mut y = Range::none();
274 x = x
275 .include(p0[0])
276 .include(p1[0])
277 .include(p2[0])
278 .include(p3[0]);
279 y = y
280 .include(p0[1])
281 .include(p1[1])
282 .include(p2[1])
283 .include(p3[1]);
284 Self { x, y }
285 }
286
287 #[must_use]
289 #[inline]
290 pub fn transform(mut self, transform: &Transform) -> Self {
291 let corners: [Point; 4] = [
292 [self.x[0], self.y[0]].into(),
293 [self.x[1], self.y[0]].into(),
294 [self.x[0], self.y[1]].into(),
295 [self.x[1], self.y[1]].into(),
296 ];
297 self = Self::none();
298 for c in corners {
299 self = self.include(transform.apply(c));
300 }
301 self
302 }
303
304 }
306
307impl std::ops::Add<Point> for BBox {
309 type Output = Self;
310 fn add(mut self, dxy: Point) -> Self {
311 self.x += dxy[0];
312 self.y += dxy[1];
313 self
314 }
315}
316impl std::ops::AddAssign<Point> for BBox {
318 fn add_assign(&mut self, dxy: Point) {
319 self.x += dxy[0];
320 self.y += dxy[1];
321 }
322}
323
324impl std::ops::Sub<Point> for BBox {
326 type Output = Self;
327 fn sub(mut self, dxy: Point) -> Self {
328 self.x -= dxy[0];
329 self.y -= dxy[1];
330 self
331 }
332}
333impl std::ops::SubAssign<Point> for BBox {
335 fn sub_assign(&mut self, dxy: Point) {
336 self.x -= dxy[0];
337 self.y -= dxy[1];
338 }
339}
340
341impl std::ops::Mul<f64> for BBox {
343 type Output = Self;
344 fn mul(mut self, scale: f64) -> Self {
345 self.x *= scale;
346 self.y *= scale;
347 self
348 }
349}
350
351impl std::ops::MulAssign<f64> for BBox {
353 fn mul_assign(&mut self, scale: f64) {
354 self.x *= scale;
355 self.y *= scale;
356 }
357}
358
359impl std::ops::Div<f64> for BBox {
361 type Output = Self;
362 fn div(mut self, scale: f64) -> Self {
363 self.x /= scale;
364 self.y /= scale;
365 self
366 }
367}
368
369impl std::ops::DivAssign<f64> for BBox {
371 fn div_assign(&mut self, scale: f64) {
372 self.x /= scale;
373 self.y /= scale;
374 }
375}
376
377#[cfg(test)]
379mod tests_polygon {
380 use super::*;
381 pub fn range_eq(pt: &Range, x: f64, y: f64) {
382 assert!(
383 (pt[0] - x).abs() < 1E-8,
384 "mismatch in x {:?} {} {}",
385 pt,
386 x,
387 y
388 );
389 assert!(
390 (pt[1] - y).abs() < 1E-8,
391 "mismatch in y {:?} {} {}",
392 pt,
393 x,
394 y
395 );
396 }
397 pub fn pt_eq(pt: &Point, x: f64, y: f64) {
398 assert!(
399 (pt[0] - x).abs() < 1E-8,
400 "mismatch in x {:?} {} {}",
401 pt,
402 x,
403 y
404 );
405 assert!(
406 (pt[1] - y).abs() < 1E-8,
407 "mismatch in y {:?} {} {}",
408 pt,
409 x,
410 y
411 );
412 }
413 pub fn pair_eq(pt: &(f64, f64), x: f64, y: f64) {
414 assert!(
415 (pt.0 - x).abs() < 1E-8,
416 "mismatch in x {:?} {} {}",
417 pt,
418 x,
419 y
420 );
421 assert!(
422 (pt.1 - y).abs() < 1E-8,
423 "mismatch in y {:?} {} {}",
424 pt,
425 x,
426 y
427 );
428 }
429 #[test]
430 fn test_zero() {
431 let x = BBox::none();
432 assert!(x.is_none());
433 dbg!("center {:?}", x.get_cwh());
434 assert_eq!(x.width(), 0.);
435 assert_eq!(x.height(), 0.);
436 }
437 #[test]
438 fn test_0() {
439 let x = BBox::new(-3., 1., 5., 7.);
440 pt_eq(&x.center(), 1., 4.);
441 assert_eq!(x.width(), 8.);
442 assert_eq!(x.height(), 6.);
443 pair_eq(&x.get_wh(), 8., 6.);
444 range_eq(&x.x, -3., 5.);
445 range_eq(&x.y, 1., 7.);
446 pt_eq(&x.get_cwh().0, 1., 4.);
447 assert_eq!(x.get_cwh().1, 8.);
448 assert_eq!(x.get_cwh().2, 6.);
449 }
450 #[test]
451 fn test_ops_0() {
452 let x = BBox::new(2., 1., 5., 7.);
453 let y = BBox::new(4., 0., 6., 3.);
454 let z = BBox::new(5., 1., 7., 4.);
455 let x_and_y = x.clone().intersect(y);
456 let x_or_y = x.clone().union(y);
457 let x_and_z = x.clone().intersect(z);
458 let x_or_z = x.clone().union(z);
459 println!("x_and_y:{}", x_and_y);
460 println!("x_or_y:{}", x_or_y);
461 println!("x_and_z:{}", x_and_z);
462 println!("x_or_z:{}", x_or_z);
463 range_eq(&x_and_y.x, 4., 5.);
464 range_eq(&x_and_y.y, 1., 3.);
465 range_eq(&x_or_y.x, 2., 6.);
466 range_eq(&x_or_y.y, 0., 7.);
467
468 assert!(!x_and_z.is_none()); dbg!(x_and_z.x);
470 dbg!(x_and_z.y);
471 dbg!(x_or_z.x);
472 dbg!(x_or_z.y);
473 range_eq(&x_and_z.x, 5., 5.);
474 range_eq(&x_and_z.y, 1., 4.);
475 range_eq(&x_or_z.x, 2., 7.);
476 range_eq(&x_or_z.y, 1., 7.);
477 }
478 #[test]
479 fn test_ops_1() {
480 let x = BBox::new(2., 1., 5., 7.);
481 let y = [0.1, 0.2, 0.3, 0.5];
482 let x_p_y = x.clone().expand(&y, 1.);
483 let x_p_2y = x.clone().expand(&y, 2.);
484 println!("x_p_y:{}", x_p_y);
485 println!("x_p_2y:{}", x_p_2y);
486 range_eq(&x_p_y.x, 1.9, 5.3);
487 range_eq(&x_p_y.y, 0.8, 7.5);
488 range_eq(&x_p_2y.x, 1.8, 5.6);
489 range_eq(&x_p_2y.y, 0.6, 8.);
490 }
491 #[test]
492 fn test_ops_2() {
493 let x = BBox::new(2., 1., 5., 7.);
494 let y = [0.1, 0.2, 0.3, 0.5];
495 let x_m_y = x.clone().shrink(&y, 1.);
496 let x_m_2y = x.clone().shrink(&y, 2.);
497 println!("x_m_y:{}", x_m_y);
498 println!("x_m_2y:{}", x_m_2y);
499 range_eq(&x_m_y.x, 2.1, 4.7);
500 range_eq(&x_m_y.y, 1.2, 6.5);
501 range_eq(&x_m_2y.x, 2.2, 4.4);
502 range_eq(&x_m_2y.y, 1.4, 6.);
503 }
504}