Skip to main content

blockworx/document_ng/
coord.rs

1//! Pure grid-space geometry types. These carry no egui/emath dependency; the
2//! pixel conversions and `From<Grid*> for emath` impls live in [`crate::grid`].
3
4#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
5pub struct GridPos {
6    pub x: i32,
7    pub y: i32,
8}
9
10#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
11pub struct GridSize {
12    pub w: u32,
13    pub h: u32,
14}
15
16#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
17pub struct GridVec {
18    pub dx: i32,
19    pub dy: i32,
20}
21
22#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
23pub struct GridRect {
24    pub min: GridPos,
25    pub size: GridSize,
26}
27
28impl GridPos {
29    pub const ZERO: GridPos = GridPos { x: 0, y: 0 };
30
31    pub const fn new(x: i32, y: i32) -> Self {
32        Self { x, y }
33    }
34}
35
36impl GridSize {
37    pub const ZERO: GridSize = GridSize { w: 0, h: 0 };
38
39    pub const fn new(w: u32, h: u32) -> Self {
40        Self { w, h }
41    }
42}
43
44impl GridVec {
45    pub const ZERO: GridVec = GridVec { dx: 0, dy: 0 };
46
47    pub const fn new(dx: i32, dy: i32) -> Self {
48        Self { dx, dy }
49    }
50}
51
52impl GridRect {
53    pub const fn new(min: GridPos, size: GridSize) -> Self {
54        Self { min, size }
55    }
56
57    pub fn from_min_size(min: GridPos, size: GridSize) -> Self {
58        Self { min, size }
59    }
60
61    pub fn from_two_pos(a: GridPos, b: GridPos) -> Self {
62        let min_x = a.x.min(b.x);
63        let min_y = a.y.min(b.y);
64        let max_x = a.x.max(b.x);
65        let max_y = a.y.max(b.y);
66        Self {
67            min: GridPos::new(min_x, min_y),
68            size: GridSize::new((max_x - min_x) as u32, (max_y - min_y) as u32),
69        }
70    }
71
72    pub fn max(self) -> GridPos {
73        GridPos::new(
74            self.min.x + self.size.w as i32,
75            self.min.y + self.size.h as i32,
76        )
77    }
78
79    pub fn width(self) -> u32 {
80        self.size.w
81    }
82
83    pub fn height(self) -> u32 {
84        self.size.h
85    }
86
87    pub fn left(self) -> i32 {
88        self.min.x
89    }
90
91    pub fn top(self) -> i32 {
92        self.min.y
93    }
94
95    pub fn right(self) -> i32 {
96        self.min.x + self.size.w as i32
97    }
98
99    pub fn bottom(self) -> i32 {
100        self.min.y + self.size.h as i32
101    }
102
103    pub fn left_top(self) -> GridPos {
104        self.min
105    }
106
107    pub fn right_top(self) -> GridPos {
108        GridPos::new(self.right(), self.top())
109    }
110
111    pub fn left_bottom(self) -> GridPos {
112        GridPos::new(self.left(), self.bottom())
113    }
114
115    pub fn right_bottom(self) -> GridPos {
116        self.max()
117    }
118
119    pub fn contains(self, p: GridPos) -> bool {
120        p.x >= self.left() && p.x <= self.right() && p.y >= self.top() && p.y <= self.bottom()
121    }
122
123    pub fn intersects(self, other: GridRect) -> bool {
124        self.left() < other.right()
125            && other.left() < self.right()
126            && self.top() < other.bottom()
127            && other.top() < self.bottom()
128    }
129
130    pub fn translate(self, delta: GridVec) -> Self {
131        Self {
132            min: GridPos::new(self.min.x + delta.dx, self.min.y + delta.dy),
133            size: self.size,
134        }
135    }
136}
137
138impl std::ops::Add<GridVec> for GridPos {
139    type Output = GridPos;
140    fn add(self, rhs: GridVec) -> GridPos {
141        GridPos::new(self.x + rhs.dx, self.y + rhs.dy)
142    }
143}
144
145impl std::ops::Sub<GridPos> for GridPos {
146    type Output = GridVec;
147    fn sub(self, rhs: GridPos) -> GridVec {
148        GridVec::new(self.x - rhs.x, self.y - rhs.y)
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn gridrect_intersects_matches_rect_semantics() {
158        let a = GridRect::new(GridPos::new(0, 0), GridSize::new(2, 2));
159        let b = GridRect::new(GridPos::new(2, 0), GridSize::new(2, 2));
160        // Touching edges should not intersect (open-on-the-right).
161        assert!(!a.intersects(b));
162        let c = GridRect::new(GridPos::new(1, 0), GridSize::new(2, 2));
163        assert!(a.intersects(c));
164    }
165}