lighthouse_protocol/utils/
rect.rs1use std::{fmt::Debug, ops::{Add, Div, Mul, Range, Sub}};
2
3use rand::{Rng, seq::IteratorRandom, thread_rng};
4
5use crate::{Vec2, LIGHTHOUSE_COLS};
6
7use super::{RemEuclid, Unity, Zero};
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
11pub struct Rect<T> {
12 pub origin: Vec2<T>,
13 pub size: Vec2<T>,
14}
15
16impl<T> Rect<T> {
17 pub const fn new(origin: Vec2<T>, size: Vec2<T>) -> Self {
19 Self { origin, size }
20 }
21}
22
23impl<T> Rect<T> where T: Copy {
24 pub const fn width(self) -> T {
26 self.size.x
27 }
28
29 pub const fn height(self) -> T {
31 self.size.y
32 }
33
34 pub const fn top_left(self) -> Vec2<T> {
36 self.origin
37 }
38}
39
40impl<T> Rect<T> where T: Add<Output = T> + Copy {
41 pub fn bottom_right(self) -> Vec2<T> {
43 self.origin + self.size
44 }
45}
46
47impl<T> Rect<T> where T: Add<Output = T> + Copy + Zero {
48 pub fn bottom_left(self) -> Vec2<T> {
50 self.origin + Vec2::new(T::ZERO, self.size.y)
51 }
52
53 pub fn top_right(self) -> Vec2<T> {
55 self.origin + Vec2::new(self.size.x, T::ZERO)
56 }
57}
58
59impl<T> Rect<T> where T: Add<Output = T> + Div<Output = T> + Copy + Zero + Unity {
60 fn two() -> T {
62 T::ONE + T::ONE
63 }
64
65 pub fn top_center(self) -> Vec2<T> {
67 self.origin + Vec2::new(self.size.x / Self::two(), T::ZERO)
68 }
69
70 pub fn center_left(self) -> Vec2<T> {
72 self.origin + Vec2::new(T::ZERO, self.size.y / Self::two())
73 }
74
75 pub fn center_right(self) -> Vec2<T> {
77 self.origin + Vec2::new(self.size.x, self.size.y / Self::two())
78 }
79
80 pub fn bottom_center(self) -> Vec2<T> {
82 self.origin + Vec2::new(self.size.x / Self::two(), self.size.y)
83 }
84
85 pub fn center(self) -> Vec2<T> {
87 self.origin + self.size.map(|c| c / Self::two())
88 }
89}
90
91impl<T> Rect<T> where T: Mul<Output = T> + Copy {
92 pub fn area(self) -> T {
94 self.width() * self.height()
95 }
96}
97
98impl<T> Rect<T> where T: RemEuclid + Copy {
99 pub fn wrap(self, pos: Vec2<T>) -> Vec2<T> {
101 Vec2::new(
102 pos.x.rem_euclid(self.width()),
103 pos.y.rem_euclid(self.height()),
104 )
105 }
106}
107
108impl<T> Rect<T> where T: Zero + Eq + Copy {
109 pub fn is_empty(self) -> bool {
111 self.size.x == T::ZERO && self.size.y == T::ZERO
112 }
113}
114
115impl<T> Rect<T> where T: Add<Output = T> + Copy {
116 pub fn x_range(self) -> Range<T> {
118 self.origin.x..(self.origin.x + self.size.x)
119 }
120
121 pub fn y_range(self) -> Range<T> {
123 self.origin.y..(self.origin.y + self.size.y)
124 }
125}
126
127impl<T> Rect<T> where T: Add<Output = T> + Copy, Range<T>: IteratorRandom<Item = T> {
128 pub fn sample_random_with(self, rng: &mut impl Rng) -> Option<Vec2<T>> {
130 let x = self.x_range().choose(rng)?;
131 let y = self.y_range().choose(rng)?;
132 Some(Vec2::<T>::new(x, y))
133 }
134
135 pub fn sample_random(self) -> Option<Vec2<T>> {
137 self.sample_random_with(&mut thread_rng())
138 }
139}
140
141impl<T> Rect<T> where T: Add<Output = T> + Ord + Copy {
142 pub fn contains(self, pos: Vec2<T>) -> bool {
144 pos.x >= self.origin.x && pos.x < self.origin.x + self.width()
145 && pos.y >= self.origin.y && pos.y < self.origin.y + self.height()
146 }
147
148 pub fn intersects(self, other: Rect<T>) -> bool {
150 let s1 = self.top_left();
151 let e1 = self.bottom_right();
152 let s2 = other.top_left();
153 let e2 = other.bottom_right();
154 s2.x < e1.x && s1.x < e2.x &&
155 s2.y < e1.y && s1.y < e2.y
156 }
157}
158
159impl<T> Rect<T> where T: Add<Output = T> + Sub<Output = T> + TryInto<usize> + Ord + Copy, T::Error: Debug {
160 pub fn index_of(self, pos: Vec2<T>) -> usize {
162 debug_assert!(self.contains(pos));
163 let relative = pos - self.origin;
164 relative.y.try_into().unwrap() * LIGHTHOUSE_COLS + relative.x.try_into().unwrap()
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use std::ops::Sub;
171
172 use crate::Vec2;
173
174 use super::Rect;
175
176 #[test]
177 fn unit_points() {
178 let rect = Rect::new(Vec2::new(-1, -1), Vec2::new(2, 2));
179 assert_eq!(rect.top_left(), Vec2::new(-1, -1));
180 assert_eq!(rect.top_center(), Vec2::new(0, -1));
181 assert_eq!(rect.top_right(), Vec2::new(1, -1));
182
183 assert_eq!(rect.center_left(), Vec2::new(-1, 0));
184 assert_eq!(rect.center(), Vec2::new(0, 0));
185 assert_eq!(rect.center_right(), Vec2::new(1, 0));
186
187 assert_eq!(rect.bottom_left(), Vec2::new(-1, 1));
188 assert_eq!(rect.bottom_center(), Vec2::new(0, 1));
189 assert_eq!(rect.bottom_right(), Vec2::new(1, 1));
190 }
191
192 #[test]
193 fn intersections() {
194 assert!(rect(0, 0, 2, 2).intersects(rect(1, 1, 3, 3)));
195 assert!(rect(0, 0, 2, 2).intersects(rect(1, -1, 1, 3)));
196 assert!(!rect(0, -2, 1, 1).intersects(rect(1, -1, 1, 3)));
197 assert!(!rect(0, 0, 1, 1).intersects(rect(1, 0, 2, 1)));
198 assert!(rect(0, 0, 2, 1).intersects(rect(1, 0, 2, 1)));
199 assert!(!rect(0, 0, 2, 0).intersects(rect(1, 0, 2, 1)));
200 }
201
202 fn rect<T>(sx: T, sy: T, ex: T, ey: T) -> Rect<T> where T: Copy + Sub<Output = T> {
203 Rect::new(Vec2::new(sx, sy), Vec2::new(ex - sx, ey - sy))
204 }
205}