1use crate::{iline, rect_points, ILine, LineStripPixelIterator, UnsignedPixelIterator};
2use bevy_math::{ivec2, vec2, IRect, IVec2, Vec2};
3use std::cmp::Ordering;
4
5#[derive(Debug)]
7pub struct RotatedIRect {
8 pub rect: IRect,
10
11 pub rotation: f32,
13}
14
15impl RotatedIRect {
16 #[inline]
19 #[must_use]
20 pub fn new(rect: IRect, rotation: f32) -> Self {
21 Self { rect, rotation }
22 }
23
24 #[inline]
26 #[must_use]
27 pub fn rotated_points(&self) -> [Vec2; 4] {
28 let center = self.rect.center().as_vec2();
29 let cos_theta = self.rotation.cos();
30 let sin_theta = self.rotation.sin();
31
32 let mut corners = rect_points(&self.rect.as_rect());
33 for corner in corners.iter_mut() {
34 let x_rotated = corner.x - center.x;
35 let y_rotated = corner.y - center.y;
36 corner.x = cos_theta * x_rotated - sin_theta * y_rotated + center.x;
37 corner.y = sin_theta * x_rotated + cos_theta * y_rotated + center.y;
38 }
39 corners
40 }
41
42 #[inline]
44 #[must_use]
45 pub fn rotated_edges(&self) -> [ILine; 4] {
46 let points = self.rotated_points();
47 let (p1, p2, p3, p4) = (
48 points[0].as_ivec2(),
49 points[1].as_ivec2(),
50 points[2].as_ivec2(),
51 points[3].as_ivec2(),
52 );
53
54 [iline(p1, p2), iline(p2, p3), iline(p3, p4), iline(p4, p1)]
55 }
56
57 #[inline]
59 #[must_use]
60 pub fn aabb(&self) -> IRect {
61 let corners = self.rotated_points();
62
63 let min_x = corners[0]
64 .x
65 .min(corners[1].x)
66 .min(corners[2].x)
67 .min(corners[3].x);
68 let min_y = corners[0]
69 .y
70 .min(corners[1].y)
71 .min(corners[2].y)
72 .min(corners[3].y);
73 let max_x = corners[0]
74 .x
75 .max(corners[1].x)
76 .max(corners[2].x)
77 .max(corners[3].x);
78 let max_y = corners[0]
79 .y
80 .max(corners[1].y)
81 .max(corners[2].y)
82 .max(corners[3].y);
83
84 IRect::from_corners(
85 ivec2(min_x as i32, min_y as i32),
86 ivec2(max_x as i32, max_y as i32),
87 )
88 }
89
90 #[inline]
92 #[must_use]
93 pub fn inner_rect(&self) -> IRect {
94 let w = self.rect.width() as f32;
95 let h = self.rect.height() as f32;
96 if w <= 0.0 || h <= 0.0 {
97 return IRect::new(0, 0, 0, 0);
98 }
99
100 let width_is_longer = w >= h;
101 let (side_long, side_short) = if width_is_longer { (w, h) } else { (h, w) };
102
103 let sin_a = self.rotation.sin().abs();
104 let cos_a = self.rotation.cos().abs();
105
106 let size = if side_short <= 2.0 * sin_a * cos_a * side_long || (sin_a - cos_a).abs() < 1e-10
107 {
108 let x = 0.5 * side_short;
109 let (wr, hr) = if width_is_longer {
110 (x / sin_a, x / cos_a)
111 } else {
112 (x / cos_a, x / sin_a)
113 };
114 vec2(wr, hr)
115 } else {
116 let cos_2a = cos_a * cos_a - sin_a * sin_a;
117 let wr = (w * cos_a - h * sin_a) / cos_2a;
118 let hr = (h * cos_a - w * sin_a) / cos_2a;
119 vec2(wr, hr)
120 };
121
122 IRect::from_center_size(self.rect.center(), size.as_ivec2())
123 }
124
125 #[inline]
127 #[must_use]
128 pub fn edge_pixels(&self) -> LineStripPixelIterator {
129 let mut points: Vec<IVec2> = self
130 .rotated_points()
131 .map(|p| p.as_ivec2())
132 .into_iter()
133 .collect();
134 points.push(points[0]);
136 LineStripPixelIterator::from_points(&points)
137 }
138
139 #[inline]
141 #[must_use]
142 pub fn unsigned_edge_pixels(&self) -> UnsignedPixelIterator<LineStripPixelIterator> {
143 UnsignedPixelIterator::<LineStripPixelIterator>::new(self.edge_pixels())
144 }
145
146 #[must_use]
148 pub fn pixels(&self) -> LineStripPixelIterator {
149 let mut edge_pixels: Vec<IVec2> = self.edge_pixels().collect();
151
152 edge_pixels.sort_by(|a, b| {
154 if a.y == b.y {
155 a.x.cmp(&b.x)
156 } else {
157 a.y.cmp(&b.y)
158 }
159 });
160
161 let mut rows: Vec<ILine> = Vec::with_capacity(edge_pixels.len() / 2);
162
163 let mut row_points = 0;
164 let mut cur_y = i32::MIN;
165 let mut min_x = 0;
166 let mut max_x = 0;
167 for p in edge_pixels {
168 if p.y == cur_y {
169 max_x = p.x;
171 row_points += 1;
172 } else {
173 match row_points.cmp(&1) {
175 Ordering::Equal => {
176 rows.push(iline(ivec2(min_x, cur_y), ivec2(min_x, cur_y)));
177 }
178 Ordering::Greater => {
179 rows.push(iline(ivec2(min_x, cur_y), ivec2(max_x, cur_y)));
180 }
181 Ordering::Less => {}
182 }
183 cur_y = p.y;
184 min_x = p.x;
185 row_points = 1;
186 }
187 }
188
189 match row_points.cmp(&1) {
191 Ordering::Equal => {
192 rows.push(iline(ivec2(min_x, cur_y), ivec2(min_x, cur_y)));
193 }
194 Ordering::Greater => {
195 rows.push(iline(ivec2(min_x, cur_y), ivec2(max_x, cur_y)));
196 }
197 Ordering::Less => {}
198 }
199
200 LineStripPixelIterator::from_lines(&rows)
201 }
202
203 #[inline]
205 #[must_use]
206 pub fn unsigned_pixels(&self) -> UnsignedPixelIterator<LineStripPixelIterator> {
207 UnsignedPixelIterator::<LineStripPixelIterator>::new(self.pixels())
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_rotated_points() {
217 let rect = IRect::new(0, 0, 10, 10);
218 let rotated_rect = RotatedIRect::new(rect, 0.0);
219 let points = rotated_rect.rotated_points();
220 assert_eq!(points[0], vec2(0.0, 0.0));
221 assert_eq!(points[1], vec2(10.0, 0.0));
222 assert_eq!(points[2], vec2(10.0, 10.0));
223 assert_eq!(points[3], vec2(0.0, 10.0));
224
225 let rect = IRect::new(0, 0, 10, 10);
226 let rotated_rect = RotatedIRect::new(rect, std::f32::consts::PI / 2.0);
227 let points = rotated_rect.rotated_points();
228 assert_eq!(points[0], vec2(10.0, 0.0));
229 assert_eq!(points[1], vec2(10.0, 10.0));
230 assert_eq!(points[2], vec2(0.0, 10.0));
231 assert_eq!(points[3], vec2(0.0, 0.0));
232
233 let rect = IRect::new(0, 0, 10, 10);
234 let rotated_rect = RotatedIRect::new(rect, std::f32::consts::PI);
235 let points = rotated_rect.rotated_points();
236 assert_eq!(points[0], vec2(10.0, 10.0));
237 assert_eq!(points[1], vec2(-4.7683716e-7, 10.0));
238 assert_eq!(points[2], vec2(4.7683716e-7, -4.7683716e-7));
239 assert_eq!(points[3], vec2(10.0, 4.7683716e-7));
240
241 let rect = IRect::new(0, 0, 10, 10);
242 let rotated_rect = RotatedIRect::new(rect, 3.0 * std::f32::consts::PI / 2.0);
243 let points = rotated_rect.rotated_points();
244 assert_eq!(points[0], vec2(0.0, 10.0));
245 assert_eq!(points[1], vec2(0.0, 0.0));
246 assert_eq!(points[2], vec2(10.0, 0.0));
247 assert_eq!(points[3], vec2(10.0, 10.0));
248 }
249
250 #[test]
251 fn test_pixels() {
252 let rect = IRect::new(0, 0, 4, 4);
253 let rotated_rect = RotatedIRect::new(rect, 0.);
254 let mut pixels = rotated_rect.pixels();
255
256 for y in 0..=4 {
257 for x in 0..=4 {
258 assert_eq!(pixels.next().unwrap(), ivec2(x, y));
259 }
260 }
261 }
262}