index/utils/
bounding_box.rs1use wasm_bindgen::prelude::*;
2
3use super::point2d::{Path2D, Point2D};
4
5#[wasm_bindgen]
7#[derive(Clone, Debug)]
8pub struct BoundingBox {
9 min_x: f32,
11 min_y: f32,
13 width: f32,
15 height: f32,
17}
18
19#[wasm_bindgen]
20impl BoundingBox {
21 #[wasm_bindgen(constructor, return_description = "A bounding box with the given dimensions.")]
23 pub fn new(
24 #[wasm_bindgen(param_description = "The minimum x-coordinate of the bounding box.")]
25 min_x: f32,
26 #[wasm_bindgen(param_description = "The minimum y-coordinate of the bounding box.")]
27 min_y: f32,
28 #[wasm_bindgen(param_description = "The width of the bounding box.")]
29 width: f32,
30 #[wasm_bindgen(param_description = "The height of the bounding box.")]
31 height: f32,
32 ) -> Result<BoundingBox, JsError> {
33 if width < 0.0 || height < 0.0 {
34 return Err(JsError::new("The width and height must be non-negative."));
35 }
36 Ok(BoundingBox {
37 min_x,
38 min_y,
39 width,
40 height,
41 })
42 }
43 #[wasm_bindgen(js_name = clone)]
45 pub fn copy(&self) -> BoundingBox {
46 self.clone()
47 }
48 #[wasm_bindgen(return_description = "The bounding box of the path.")]
50 pub fn from_path(
51 #[wasm_bindgen(param_description = "The path to calculate the bounding box of.")]
52 path: &Path2D
53 ) -> Option<BoundingBox> {
54 if path.is_empty() {
55 return None;
56 }
57
58 let mut min_x = f32::INFINITY;
59 let mut min_y = f32::INFINITY;
60 let mut max_x = f32::NEG_INFINITY;
61 let mut max_y = f32::NEG_INFINITY;
62
63 for point in path.points().iter() {
64 min_x = min_x.min(point.x);
65 min_y = min_y.min(point.y);
66 max_x = max_x.max(point.x);
67 max_y = max_y.max(point.y);
68 }
69
70 Some(BoundingBox {
71 min_x,
72 min_y,
73 width: max_x - min_x,
74 height: max_y - min_y,
75 })
76 }
77
78 #[wasm_bindgen(getter, return_description = "The minimum x-coordinate of the bounding box.")]
80 pub fn min_x(&self) -> f32 {
81 self.min_x
82 }
83
84 #[wasm_bindgen(getter, return_description = "The minimum y-coordinate of the bounding box.")]
86 pub fn min_y(&self) -> f32 {
87 self.min_y
88 }
89
90 #[wasm_bindgen(getter, return_description = "The width of the bounding box.")]
92 pub fn width(&self) -> f32 {
93 self.width
94 }
95
96 #[wasm_bindgen(getter, return_description = "The height of the bounding box.")]
98 pub fn height(&self) -> f32 {
99 self.height
100 }
101
102 #[wasm_bindgen(return_description = "A boolean indicating if the point is contained within the bounding box.")]
104 pub fn contains(
105 &self,
106 #[wasm_bindgen(param_description = "The point to check if it is contained within the bounding box.")]
107 point: Point2D,
108 ) -> bool {
109 let x = point.x;
110 let y = point.y;
111 x >= self.min_x && x <= self.min_x + self.width && y >= self.min_y && y <= self.min_y + self.height
112 }
113
114 #[wasm_bindgen(return_description = "A boolean indicating if the bounding box overlaps with the other bounding box.")]
116 pub fn intersects(
117 &self,
118 #[wasm_bindgen(param_description = "The other bounding box to check for intersection.")]
119 other: &BoundingBox
120 ) -> bool {
121 self.min_x < other.min_x + other.width
122 && self.min_x + self.width > other.min_x
123 && self.min_y < other.min_y + other.height
124 && self.min_y + self.height > other.min_y
125 }
126
127 #[wasm_bindgen(return_description = "The intersection of the bounding box with the other bounding box.")]
129 pub fn intersection(
130 #[wasm_bindgen(param_description = "The bounding box to intersect with.")]
131 this: Option<BoundingBox>,
132 #[wasm_bindgen(param_description = "The other bounding box to intersect with.")]
133 other: Option<BoundingBox>
134 ) -> Option<BoundingBox> {
135 if this.is_none() {
136 return None;
137 }
138 let this = this.unwrap();
139 if other.is_none() {
140 return None;
141 }
142 let other = other.unwrap();
143
144 if !this.intersects(&other) {
145 return None;
146 }
147
148 let min_x = this.min_x.max(other.min_x);
149 let min_y = this.min_y.max(other.min_y);
150 let max_x = (this.min_x + this.width).min(other.min_x + other.width);
151 let max_y = (this.min_y + this.height).min(other.min_y + other.height);
152
153 Some(BoundingBox {
154 min_x,
155 min_y,
156 width: max_x - min_x,
157 height: max_y - min_y,
158 })
159 }
160
161 #[wasm_bindgen(return_description = "The union of the bounding box with the other bounding box.")]
163 pub fn union(
164 #[wasm_bindgen(param_description = "The bounding box to union with.")]
165 this: Option<BoundingBox>,
166 #[wasm_bindgen(param_description = "The other bounding box to union with.")]
167 other: Option<BoundingBox>
168 ) -> Option<BoundingBox> {
169 if this.is_none() {
170 return other;
171 }
172 let this = this.unwrap();
173 if other.is_none() {
174 return Some(this);
175 }
176 let other = other.unwrap();
177
178 let min_x = this.min_x.min(other.min_x);
179 let min_y = this.min_y.min(other.min_y);
180 let max_x = (this.min_x + this.width).max(other.min_x + other.width);
181 let max_y = (this.min_y + this.height).max(other.min_y + other.height);
182
183 Some(BoundingBox {
184 min_x,
185 min_y,
186 width: max_x - min_x,
187 height: max_y - min_y,
188 })
189 }
190
191 #[wasm_bindgen(getter, return_description = "The center of the bounding box.")]
193 pub fn center(&self) -> Point2D {
194 Point2D {
195 x: self.min_x + self.width / 2.0,
196 y: self.min_y + self.height / 2.0,
197 }
198 }
199}