1 | // pathfinder/geometry/src/basic/rect.rs |
2 | // |
3 | // Copyright © 2019 The Pathfinder Project Developers. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | //! 2D axis-aligned rectangles, optimized with SIMD. |
12 | |
13 | use crate::vector::{IntoVector2F, Vector2F, Vector2I}; |
14 | use pathfinder_simd::default::{F32x4, I32x4}; |
15 | use std::ops::{Add, Mul, Sub}; |
16 | |
17 | #[derive (Clone, Copy, Debug, PartialEq, Default)] |
18 | pub struct RectF(pub F32x4); |
19 | |
20 | impl RectF { |
21 | #[inline ] |
22 | pub fn new(origin: Vector2F, size: Vector2F) -> RectF { |
23 | RectF(origin.0.concat_xy_xy(origin.0 + size.0)) |
24 | } |
25 | |
26 | #[inline ] |
27 | pub fn from_points(origin: Vector2F, lower_right: Vector2F) -> RectF { |
28 | RectF(origin.0.concat_xy_xy(lower_right.0)) |
29 | } |
30 | |
31 | // Accessors |
32 | |
33 | #[inline ] |
34 | pub fn origin(self) -> Vector2F { |
35 | Vector2F(self.0.xy()) |
36 | } |
37 | |
38 | #[inline ] |
39 | pub fn size(self) -> Vector2F { |
40 | Vector2F(self.0.zw() - self.0.xy()) |
41 | } |
42 | |
43 | #[inline ] |
44 | pub fn origin_x(self) -> f32 { |
45 | self.0.x() |
46 | } |
47 | |
48 | #[inline ] |
49 | pub fn origin_y(self) -> f32 { |
50 | self.0.y() |
51 | } |
52 | |
53 | #[inline ] |
54 | pub fn width(self) -> f32 { |
55 | self.0.z() - self.0.x() |
56 | } |
57 | |
58 | #[inline ] |
59 | pub fn height(self) -> f32 { |
60 | self.0.w() - self.0.y() |
61 | } |
62 | |
63 | #[inline ] |
64 | pub fn upper_right(self) -> Vector2F { |
65 | Vector2F(self.0.zy()) |
66 | } |
67 | |
68 | #[inline ] |
69 | pub fn lower_left(self) -> Vector2F { |
70 | Vector2F(self.0.xw()) |
71 | } |
72 | |
73 | #[inline ] |
74 | pub fn lower_right(self) -> Vector2F { |
75 | Vector2F(self.0.zw()) |
76 | } |
77 | |
78 | // Mutators |
79 | |
80 | #[inline ] |
81 | pub fn set_origin_x(&mut self, x: f32) { |
82 | self.0.set_x(x) |
83 | } |
84 | |
85 | #[inline ] |
86 | pub fn set_origin_y(&mut self, y: f32) { |
87 | self.0.set_y(y) |
88 | } |
89 | |
90 | #[inline ] |
91 | pub fn contains_point(self, point: Vector2F) -> bool { |
92 | // self.origin <= point && point <= self.lower_right |
93 | let point = point.0.to_f32x4(); |
94 | self.0.concat_xy_xy(point).packed_le(point.concat_xy_zw(self.0)).all_true() |
95 | } |
96 | |
97 | #[inline ] |
98 | pub fn contains_rect(self, other: RectF) -> bool { |
99 | // self.origin <= other.origin && other.lower_right <= self.lower_right |
100 | self.0.concat_xy_zw(other.0).packed_le(other.0.concat_xy_zw(self.0)).all_true() |
101 | } |
102 | |
103 | #[inline ] |
104 | pub fn is_empty(self) -> bool { |
105 | self.origin() == self.lower_right() |
106 | } |
107 | |
108 | #[inline ] |
109 | pub fn union_point(self, point: Vector2F) -> RectF { |
110 | RectF::from_points(self.origin().min(point), self.lower_right().max(point)) |
111 | } |
112 | |
113 | #[inline ] |
114 | pub fn union_rect(self, other: RectF) -> RectF { |
115 | RectF::from_points( |
116 | self.origin().min(other.origin()), |
117 | self.lower_right().max(other.lower_right()), |
118 | ) |
119 | } |
120 | |
121 | #[inline ] |
122 | pub fn intersects(self, other: RectF) -> bool { |
123 | // self.origin < other.lower_right && other.origin < self.lower_right |
124 | self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true() |
125 | } |
126 | |
127 | #[inline ] |
128 | pub fn intersection(self, other: RectF) -> Option<RectF> { |
129 | if !self.intersects(other) { |
130 | None |
131 | } else { |
132 | Some(RectF::from_points( |
133 | self.origin().max(other.origin()), |
134 | self.lower_right().min(other.lower_right()), |
135 | )) |
136 | } |
137 | } |
138 | |
139 | #[inline ] |
140 | pub fn min_x(self) -> f32 { |
141 | self.0[0] |
142 | } |
143 | |
144 | #[inline ] |
145 | pub fn min_y(self) -> f32 { |
146 | self.0[1] |
147 | } |
148 | |
149 | #[inline ] |
150 | pub fn max_x(self) -> f32 { |
151 | self.0[2] |
152 | } |
153 | |
154 | #[inline ] |
155 | pub fn max_y(self) -> f32 { |
156 | self.0[3] |
157 | } |
158 | |
159 | #[inline ] |
160 | pub fn center(self) -> Vector2F { |
161 | self.origin() + self.size() * 0.5 |
162 | } |
163 | |
164 | /// Rounds all points to the nearest integer. |
165 | #[inline ] |
166 | pub fn round(self) -> RectF { |
167 | RectF(self.0.to_i32x4().to_f32x4()) |
168 | } |
169 | |
170 | #[inline ] |
171 | pub fn round_out(self) -> RectF { |
172 | RectF::from_points(self.origin().floor(), self.lower_right().ceil()) |
173 | } |
174 | |
175 | #[inline ] |
176 | pub fn dilate<A>(self, amount: A) -> RectF where A: IntoVector2F { |
177 | let amount = amount.into_vector_2f(); |
178 | RectF::from_points(self.origin() - amount, self.lower_right() + amount) |
179 | } |
180 | |
181 | #[inline ] |
182 | pub fn contract<A>(self, amount: A) -> RectF where A: IntoVector2F { |
183 | let amount = amount.into_vector_2f(); |
184 | RectF::from_points(self.origin() + amount, self.lower_right() - amount) |
185 | } |
186 | |
187 | #[inline ] |
188 | pub fn to_i32(&self) -> RectI { |
189 | RectI(self.0.to_i32x4()) |
190 | } |
191 | } |
192 | |
193 | impl Add<Vector2F> for RectF { |
194 | type Output = RectF; |
195 | #[inline ] |
196 | fn add(self, other: Vector2F) -> RectF { |
197 | RectF::new(self.origin() + other, self.size()) |
198 | } |
199 | } |
200 | |
201 | impl Add<f32> for RectF { |
202 | type Output = RectF; |
203 | #[inline ] |
204 | fn add(self, other: f32) -> RectF { |
205 | RectF::new(self.origin() + other, self.size()) |
206 | } |
207 | } |
208 | |
209 | impl Mul<Vector2F> for RectF { |
210 | type Output = RectF; |
211 | #[inline ] |
212 | fn mul(self, factors: Vector2F) -> RectF { |
213 | RectF(self.0 * factors.0.concat_xy_xy(factors.0)) |
214 | } |
215 | } |
216 | |
217 | impl Mul<f32> for RectF { |
218 | type Output = RectF; |
219 | #[inline ] |
220 | fn mul(self, factor: f32) -> RectF { |
221 | RectF(self.0 * F32x4::splat(factor)) |
222 | } |
223 | } |
224 | |
225 | impl Sub<Vector2F> for RectF { |
226 | type Output = RectF; |
227 | #[inline ] |
228 | fn sub(self, other: Vector2F) -> RectF { |
229 | RectF::new(self.origin() - other, self.size()) |
230 | } |
231 | } |
232 | |
233 | impl Sub<f32> for RectF { |
234 | type Output = RectF; |
235 | #[inline ] |
236 | fn sub(self, other: f32) -> RectF { |
237 | RectF::new(self.origin() - other, self.size()) |
238 | } |
239 | } |
240 | |
241 | /// NB: The origin is inclusive, while the lower right point is exclusive. |
242 | #[derive (Clone, Copy, Debug, PartialEq, Default)] |
243 | pub struct RectI(pub I32x4); |
244 | |
245 | impl RectI { |
246 | #[inline ] |
247 | pub fn new(origin: Vector2I, size: Vector2I) -> RectI { |
248 | RectI(origin.0.concat_xy_xy(origin.0 + size.0)) |
249 | } |
250 | |
251 | #[inline ] |
252 | pub fn from_points(origin: Vector2I, lower_right: Vector2I) -> RectI { |
253 | RectI(origin.0.concat_xy_xy(lower_right.0)) |
254 | } |
255 | |
256 | // Accessors |
257 | |
258 | #[inline ] |
259 | pub fn origin(&self) -> Vector2I { |
260 | Vector2I(self.0.xy()) |
261 | } |
262 | |
263 | #[inline ] |
264 | pub fn size(&self) -> Vector2I { |
265 | Vector2I(self.0.zw() - self.0.xy()) |
266 | } |
267 | |
268 | #[inline ] |
269 | pub fn origin_x(self) -> i32 { |
270 | self.0.x() |
271 | } |
272 | |
273 | #[inline ] |
274 | pub fn origin_y(self) -> i32 { |
275 | self.0.y() |
276 | } |
277 | |
278 | #[inline ] |
279 | pub fn width(self) -> i32 { |
280 | self.0.z() - self.0.x() |
281 | } |
282 | |
283 | #[inline ] |
284 | pub fn height(self) -> i32 { |
285 | self.0.w() - self.0.y() |
286 | } |
287 | |
288 | #[inline ] |
289 | pub fn upper_right(&self) -> Vector2I { |
290 | Vector2I(self.0.zy()) |
291 | } |
292 | |
293 | #[inline ] |
294 | pub fn lower_left(&self) -> Vector2I { |
295 | Vector2I(self.0.xw()) |
296 | } |
297 | |
298 | #[inline ] |
299 | pub fn lower_right(&self) -> Vector2I { |
300 | Vector2I(self.0.zw()) |
301 | } |
302 | |
303 | #[inline ] |
304 | pub fn scale(self, factor: i32) -> RectI { |
305 | RectI(self.0 * I32x4::splat(factor)) |
306 | } |
307 | |
308 | #[inline ] |
309 | pub fn scale_xy(self, factors: Vector2I) -> RectI { |
310 | RectI(self.0 * factors.0.concat_xy_xy(factors.0)) |
311 | } |
312 | |
313 | #[inline ] |
314 | pub fn min_x(self) -> i32 { |
315 | self.0[0] |
316 | } |
317 | |
318 | #[inline ] |
319 | pub fn min_y(self) -> i32 { |
320 | self.0[1] |
321 | } |
322 | |
323 | #[inline ] |
324 | pub fn max_x(self) -> i32 { |
325 | self.0[2] |
326 | } |
327 | |
328 | #[inline ] |
329 | pub fn max_y(self) -> i32 { |
330 | self.0[3] |
331 | } |
332 | |
333 | #[inline ] |
334 | pub fn intersects(self, other: RectI) -> bool { |
335 | // self.origin < other.lower_right && other.origin < self.lower_right |
336 | self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true() |
337 | } |
338 | |
339 | #[inline ] |
340 | pub fn intersection(self, other: RectI) -> Option<RectI> { |
341 | if !self.intersects(other) { |
342 | None |
343 | } else { |
344 | Some(RectI::from_points( |
345 | self.origin().max(other.origin()), |
346 | self.lower_right().min(other.lower_right()), |
347 | )) |
348 | } |
349 | } |
350 | |
351 | #[inline ] |
352 | pub fn contains_point(&self, point: Vector2I) -> bool { |
353 | // self.origin <= point && point <= self.lower_right - 1 |
354 | let lower_right = self.lower_right() - 1; |
355 | self.origin() |
356 | .0 |
357 | .concat_xy_xy(point.0) |
358 | .packed_le(point.0.concat_xy_xy(lower_right.0)) |
359 | .all_true() |
360 | } |
361 | |
362 | #[inline ] |
363 | pub fn contract(self, amount: Vector2I) -> RectI { |
364 | RectI::from_points(self.origin() + amount, self.lower_right() - amount) |
365 | } |
366 | |
367 | #[inline ] |
368 | pub fn to_f32(&self) -> RectF { |
369 | RectF(self.0.to_f32x4()) |
370 | } |
371 | } |
372 | |