1#[derive(Clone, Copy, Debug, Default, PartialEq)]
6pub struct BoundingBox {
7 pub x: f32,
9 pub y: f32,
11 pub w: f32,
13 pub h: f32,
15}
16
17impl BoundingBox {
18 pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
20 Self { x, y, w, h }
21 }
22
23 pub fn right(&self) -> f32 {
25 self.x + self.w
26 }
27
28 pub fn bottom(&self) -> f32 {
30 self.y + self.h
31 }
32
33 pub fn center(&self) -> [f32; 2] {
35 [self.x + self.w * 0.5, self.y + self.h * 0.5]
36 }
37
38 pub fn contains(&self, p: [f32; 2]) -> bool {
40 p[0] >= self.x && p[0] <= self.right() && p[1] >= self.y && p[1] <= self.bottom()
41 }
42
43 pub fn union(self, other: Self) -> Self {
45 let x = self.x.min(other.x);
46 let y = self.y.min(other.y);
47 let r = self.right().max(other.right());
48 let b = self.bottom().max(other.bottom());
49 Self {
50 x,
51 y,
52 w: r - x,
53 h: b - y,
54 }
55 }
56}
57
58#[derive(Clone, Copy, Debug, Default)]
60pub struct DataBounds {
61 pub x_min: f64,
63 pub x_max: f64,
65 pub y_min: f64,
67 pub y_max: f64,
69}
70
71impl DataBounds {
72 pub fn new(x_min: f64, x_max: f64, y_min: f64, y_max: f64) -> Self {
74 Self {
75 x_min,
76 x_max,
77 y_min,
78 y_max,
79 }
80 }
81
82 pub fn union(self, other: Self) -> Self {
84 Self {
85 x_min: self.x_min.min(other.x_min),
86 x_max: self.x_max.max(other.x_max),
87 y_min: self.y_min.min(other.y_min),
88 y_max: self.y_max.max(other.y_max),
89 }
90 }
91
92 pub fn include_point(&mut self, x: f64, y: f64) {
94 self.x_min = self.x_min.min(x);
95 self.x_max = self.x_max.max(x);
96 self.y_min = self.y_min.min(y);
97 self.y_max = self.y_max.max(y);
98 }
99
100 pub fn pad(self, fraction: f64) -> Self {
102 let dx = (self.x_max - self.x_min) * fraction;
103 let dy = (self.y_max - self.y_min) * fraction;
104 Self {
105 x_min: self.x_min - dx,
106 x_max: self.x_max + dx,
107 y_min: self.y_min - dy,
108 y_max: self.y_max + dy,
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn bbox_contains() {
119 let bb = BoundingBox::new(10.0, 20.0, 100.0, 50.0);
120 assert!(bb.contains([50.0, 40.0]));
121 assert!(!bb.contains([0.0, 0.0]));
122 }
123
124 #[test]
125 fn bbox_union() {
126 let a = BoundingBox::new(0.0, 0.0, 10.0, 10.0);
127 let b = BoundingBox::new(5.0, 5.0, 10.0, 10.0);
128 let u = a.union(b);
129 assert!((u.x).abs() < 1e-6);
130 assert!((u.y).abs() < 1e-6);
131 assert!((u.w - 15.0).abs() < 1e-6);
132 assert!((u.h - 15.0).abs() < 1e-6);
133 }
134
135 #[test]
136 fn data_bounds_pad() {
137 let b = DataBounds::new(0.0, 100.0, 0.0, 50.0);
138 let padded = b.pad(0.1);
139 assert!((padded.x_min - (-10.0)).abs() < 1e-6);
140 assert!((padded.x_max - 110.0).abs() < 1e-6);
141 }
142}