x_graphics/
geometry.rs

1use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
2
3use crate::Float;
4
5#[derive(Clone, Copy, Debug, Default, PartialEq)]
6pub struct Point<T> {
7    pub x: T,
8    pub y: T,
9}
10
11#[derive(Clone, Copy, Debug, Default, PartialEq)]
12pub struct Size<T> {
13    pub width: T,
14    pub height: T,
15}
16
17#[derive(Clone, Copy, Debug, Default, PartialEq)]
18pub struct Rect<T> {
19    pub point: Point<T>,
20    pub size: Size<T>,
21}
22
23pub type FPoint = Point<Float>;
24pub type FSize = Size<Float>;
25pub type FRect = Rect<Float>;
26
27pub type IPoint = Point<i32>;
28pub type ISize = Size<i32>;
29pub type IRect = Rect<i32>;
30
31impl<T> Point<T> {
32    pub fn new(x: T, y: T) -> Self {
33        Self {
34            x,
35            y,
36        }
37    }
38}
39
40impl<T> Size<T> {
41    pub fn new(width: T, height: T) -> Self {
42        Self {
43            width,
44            height,
45        }
46    }
47}
48
49impl<T> Rect<T>
50where
51    T: Add<Output = T> + AddAssign + Sub<Output = T> + SubAssign + Neg<Output = T> + Copy + Default + PartialOrd + PartialEq,
52{
53    pub fn new(x: T, y: T, width: T, height: T) -> Self {
54        Self {
55            point: Point {
56                x,
57                y,
58            },
59            size: Size {
60                width,
61                height,
62            },
63        }
64    }
65
66    pub fn from_bounds(left: T, top: T, right: T, bottom: T) -> Self {
67        Self {
68            point: Point {
69                x: left,
70                y: top,
71            },
72            size: Size {
73                width: right - left,
74                height: bottom - top,
75            },
76        }
77    }
78
79    pub fn standardize(&self) {
80        let mut rect = *self;
81        if rect.size.width < T::default() {
82            rect.point.x += rect.size.width;
83            rect.size.width = -rect.size.width;
84        }
85        if rect.size.height < T::default() {
86            rect.point.y += rect.size.height;
87            rect.size.height = -rect.size.height;
88        }
89    }
90
91    pub fn is_empty(&self) -> bool {
92        self.size.width <= T::default() || self.size.height <= T::default()
93    }
94
95    pub fn is_null(&self) -> bool {
96        self.point.x == T::default() && self.point.y == T::default() && self.size.width == T::default() && self.size.height == T::default()
97    }
98
99    pub fn offset(&self, dx: T, dy: T) -> Rect<T> {
100        let mut rect = *self;
101        rect.point.x += dx;
102        rect.point.y += dy;
103        rect
104    }
105
106    pub fn inflate(&self, dx: T, dy: T) -> Rect<T> {
107        let mut rect = *self;
108        rect.point.x -= dx;
109        rect.point.y -= dy;
110        rect.size.width += dx + dx;
111        rect.size.height += dy + dy;
112        rect
113    }
114
115    pub fn deflate(&self, dx: T, dy: T) -> Rect<T> {
116        let mut rect = *self;
117        rect.point.x += dx;
118        rect.point.y += dy;
119        rect.size.width -= dx + dx;
120        rect.size.height -= dy + dy;
121        rect
122    }
123
124    pub fn contains_point(&self, point: Point<T>) -> bool {
125        point.x >= self.point.x && point.x < self.point.x + self.size.width && point.y >= self.point.y && point.y < self.point.y + self.size.height
126    }
127
128    pub fn contains_rect(&self, rect: Rect<T>) -> bool {
129        self.point.x <= rect.point.x &&
130            self.point.y <= rect.point.y &&
131            self.point.x + self.size.width >= rect.point.x + rect.size.width &&
132            self.point.y + self.size.height >= rect.point.y + rect.size.height
133    }
134
135    pub fn intersects(&self, rect: Rect<T>) -> bool {
136        self.point.x < rect.point.x + rect.size.width &&
137            rect.point.x < self.point.x + self.size.width &&
138            self.point.y < rect.point.y + rect.size.height &&
139            rect.point.y < self.point.y + self.size.height
140    }
141
142    pub fn union(&self, rect: Rect<T>) -> Option<Rect<T>> {
143        let left = if self.point.x < rect.point.x {
144            self.point.x
145        } else {
146            rect.point.x
147        };
148        let top = if self.point.y < rect.point.y {
149            self.point.y
150        } else {
151            rect.point.y
152        };
153        let right = if self.point.x + self.size.width > rect.point.x + rect.size.width {
154            self.point.x + self.size.width
155        } else {
156            rect.point.x + rect.size.width
157        };
158        let bottom = if self.point.y + self.size.height > rect.point.y + rect.size.height {
159            self.point.y + self.size.height
160        } else {
161            rect.point.y + rect.size.height
162        };
163        if left > right || top > bottom {
164            None
165        } else {
166            Some(Rect::from_bounds(left, top, right, bottom))
167        }
168    }
169
170    pub fn intersection(&self, rect: Rect<T>) -> Option<Rect<T>> {
171        let left = if self.point.x > rect.point.x {
172            self.point.x
173        } else {
174            rect.point.x
175        };
176        let top = if self.point.y > rect.point.y {
177            self.point.y
178        } else {
179            rect.point.y
180        };
181        let right = if self.point.x + self.size.width < rect.point.x + rect.size.width {
182            self.point.x + self.size.width
183        } else {
184            rect.point.x + rect.size.width
185        };
186        let bottom = if self.point.y + self.size.height < rect.point.y + rect.size.height {
187            self.point.y + self.size.height
188        } else {
189            rect.point.y + rect.size.height
190        };
191        if left > right || top > bottom {
192            None
193        } else {
194            Some(Rect::from_bounds(left, top, right, bottom))
195        }
196    }
197}
198
199impl From<IPoint> for (i32, i32) {
200    fn from(point: IPoint) -> Self {
201        (point.x, point.y)
202    }
203}
204
205impl From<FPoint> for (Float, Float) {
206    fn from(point: FPoint) -> Self {
207        (point.x, point.y)
208    }
209}
210
211impl From<IPoint> for FPoint {
212    fn from(point: IPoint) -> Self {
213        Self {
214            x: point.x as Float,
215            y: point.y as Float,
216        }
217    }
218}
219
220impl From<FPoint> for IPoint {
221    fn from(point: FPoint) -> Self {
222        Self {
223            x: point.x as i32,
224            y: point.y as i32,
225        }
226    }
227}
228
229impl From<ISize> for (i32, i32) {
230    fn from(size: ISize) -> Self {
231        (size.width, size.height)
232    }
233}
234
235impl From<FSize> for (Float, Float) {
236    fn from(size: FSize) -> Self {
237        (size.width, size.height)
238    }
239}
240
241impl From<ISize> for FSize {
242    fn from(size: ISize) -> Self {
243        Self {
244            width: size.width as Float,
245            height: size.height as Float,
246        }
247    }
248}
249
250impl From<FSize> for ISize {
251    fn from(size: FSize) -> Self {
252        Self {
253            width: size.width as i32,
254            height: size.height as i32,
255        }
256    }
257}
258
259impl From<IRect> for (i32, i32, i32, i32) {
260    fn from(rect: IRect) -> Self {
261        (rect.point.x, rect.point.y, rect.size.width, rect.size.height)
262    }
263}
264
265impl From<FRect> for (Float, Float, Float, Float) {
266    fn from(rect: FRect) -> Self {
267        (rect.point.x, rect.point.y, rect.size.width, rect.size.height)
268    }
269}
270
271impl From<IRect> for FRect {
272    fn from(rect: IRect) -> Self {
273        Self {
274            point: FPoint {
275                x: rect.point.x as Float,
276                y: rect.point.y as Float,
277            },
278            size: FSize {
279                width: rect.size.width as Float,
280                height: rect.size.height as Float,
281            },
282        }
283    }
284}
285
286impl From<FRect> for IRect {
287    fn from(rect: FRect) -> Self {
288        Self {
289            point: IPoint {
290                x: rect.point.x as i32,
291                y: rect.point.y as i32,
292            },
293            size: ISize {
294                width: rect.size.width as i32,
295                height: rect.size.height as i32,
296            },
297        }
298    }
299}