anathema_geometry/
position.rs1use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
2
3use crate::Size;
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd)]
12pub struct Pos {
13 pub x: i32,
15 pub y: i32,
17}
18
19impl Pos {
20 pub const ZERO: Self = Self::new(0, 0);
22
23 pub const fn new(x: i32, y: i32) -> Self {
25 Self { x, y }
26 }
27}
28
29impl Default for Pos {
30 fn default() -> Self {
31 Self::ZERO
32 }
33}
34
35impl From<(i32, i32)> for Pos {
36 fn from(val: (i32, i32)) -> Self {
37 Self::new(val.0, val.1)
38 }
39}
40
41impl From<(u16, u16)> for Pos {
42 fn from(val: (u16, u16)) -> Self {
43 Self::new(val.0 as i32, val.1 as i32)
44 }
45}
46
47impl From<LocalPos> for Pos {
48 fn from(LocalPos { x, y }: LocalPos) -> Self {
49 Self::new(x as i32, y as i32)
50 }
51}
52
53impl From<(usize, usize)> for Pos {
54 fn from(val: (usize, usize)) -> Self {
55 Self::new(val.0 as i32, val.1 as i32)
56 }
57}
58
59impl Add for Pos {
60 type Output = Self;
61
62 fn add(self, rhs: Self) -> Self::Output {
63 Pos::new(self.x + rhs.x, self.y + rhs.y)
64 }
65}
66
67impl Add<Size> for Pos {
68 type Output = Self;
69
70 fn add(self, rhs: Size) -> Self::Output {
71 Pos::new(self.x + rhs.width as i32, self.y + rhs.height as i32)
72 }
73}
74
75impl Add<LocalPos> for Pos {
76 type Output = Self;
77
78 fn add(self, rhs: LocalPos) -> Self::Output {
79 Pos::new(self.x + rhs.x as i32, self.y + rhs.y as i32)
80 }
81}
82
83impl Sub<LocalPos> for Pos {
84 type Output = Self;
85
86 fn sub(self, rhs: LocalPos) -> Self::Output {
87 Pos::new(self.x - rhs.x as i32, self.y - rhs.y as i32)
88 }
89}
90
91impl Mul<f32> for Pos {
92 type Output = Self;
93
94 fn mul(self, rhs: f32) -> Self::Output {
95 Self {
96 x: (self.x as f32 * rhs).round() as i32,
97 y: (self.y as f32 * rhs).round() as i32,
98 }
99 }
100}
101
102impl AddAssign for Pos {
103 fn add_assign(&mut self, rhs: Pos) {
104 self.x += rhs.x;
105 self.y += rhs.y;
106 }
107}
108
109impl Sub for Pos {
110 type Output = Self;
111
112 fn sub(self, rhs: Self) -> Self::Output {
113 Pos::new(self.x - rhs.x, self.y - rhs.y)
114 }
115}
116
117impl SubAssign for Pos {
118 fn sub_assign(&mut self, rhs: Pos) {
119 self.x -= rhs.x;
120 self.y -= rhs.y;
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
132pub struct LocalPos {
133 pub x: u16,
135 pub y: u16,
137}
138
139impl LocalPos {
140 pub const ZERO: Self = Self::new(0, 0);
142
143 pub const fn new(x: u16, y: u16) -> Self {
145 Self { x, y }
146 }
147
148 pub const fn to_index(self, width: u16) -> usize {
149 (self.y * width + self.x) as usize
150 }
151
152 pub const fn saturating_sub(mut self, other: Self) -> Self {
153 self.x = self.x.saturating_sub(other.x);
154 self.y = self.y.saturating_sub(other.y);
155 self
156 }
157}
158
159impl From<(u16, u16)> for LocalPos {
160 fn from((x, y): (u16, u16)) -> Self {
161 Self { x, y }
162 }
163}
164
165impl From<(i32, i32)> for LocalPos {
166 fn from((x, y): (i32, i32)) -> Self {
167 Self {
168 x: x as u16,
169 y: y as u16,
170 }
171 }
172}
173
174impl From<(usize, usize)> for LocalPos {
175 fn from((x, y): (usize, usize)) -> Self {
176 Self {
177 x: x as u16,
178 y: y as u16,
179 }
180 }
181}
182
183impl TryFrom<Pos> for LocalPos {
184 type Error = ();
185
186 fn try_from(value: Pos) -> Result<Self, Self::Error> {
187 if value.x < 0 || value.y < 0 {
188 return Err(());
189 }
190
191 if value.x > u16::MAX as i32 || value.y > u16::MAX as i32 {
192 return Err(());
193 }
194
195 Ok(Self {
196 x: value.x as u16,
197 y: value.y as u16,
198 })
199 }
200}
201
202impl Add for LocalPos {
203 type Output = Self;
204
205 fn add(self, rhs: Self) -> Self::Output {
206 LocalPos {
207 x: self.x + rhs.x,
208 y: self.y + rhs.y,
209 }
210 }
211}
212
213impl AddAssign for LocalPos {
214 fn add_assign(&mut self, rhs: Self) {
215 self.x += rhs.x;
216 self.y += rhs.y;
217 }
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223
224 #[test]
225 fn index_from_coords() {
226 let width = 20;
227
228 let actual = LocalPos::new(0, 0).to_index(width);
229 let expected = 0;
230 assert_eq!(expected, actual);
231
232 let actual = LocalPos::new(10, 0).to_index(width);
233 let expected = 10;
234 assert_eq!(expected, actual);
235
236 let actual = LocalPos::new(4, 20).to_index(width);
237 let expected = (width * width) as usize + 4;
238 assert_eq!(expected, actual);
239 }
240}