Skip to main content

blockworx_geom/
pos2.rs

1use std::fmt;
2use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign};
3
4use crate::{Vec2, lerp};
5
6/// A point in space: world coordinates on the canvas, points on screen.
7#[derive(Clone, Copy, Default, PartialEq, serde::Serialize, serde::Deserialize)]
8pub struct Pos2 {
9    /// How far to the right.
10    pub x: f32,
11
12    /// How far down.
13    pub y: f32,
14}
15
16/// `pos2(x, y) == Pos2::new(x, y)`
17#[inline]
18pub const fn pos2(x: f32, y: f32) -> Pos2 {
19    Pos2 { x, y }
20}
21
22impl Pos2 {
23    /// The origin — the left-top corner in screen coordinates.
24    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
25
26    #[inline]
27    pub const fn new(x: f32, y: f32) -> Self {
28        Self { x, y }
29    }
30
31    /// The vector from the origin to this position.
32    #[inline]
33    pub const fn to_vec2(self) -> Vec2 {
34        Vec2 {
35            x: self.x,
36            y: self.y,
37        }
38    }
39
40    #[inline]
41    pub fn distance(self, other: Self) -> f32 {
42        (self - other).length()
43    }
44
45    #[inline]
46    pub fn distance_sq(self, other: Self) -> f32 {
47        (self - other).length_sq()
48    }
49
50    #[must_use]
51    #[inline]
52    pub fn floor(self) -> Self {
53        pos2(self.x.floor(), self.y.floor())
54    }
55
56    #[must_use]
57    #[inline]
58    pub fn round(self) -> Self {
59        pos2(self.x.round(), self.y.round())
60    }
61
62    #[must_use]
63    #[inline]
64    pub fn ceil(self) -> Self {
65        pos2(self.x.ceil(), self.y.ceil())
66    }
67
68    #[inline]
69    pub fn is_finite(self) -> bool {
70        self.x.is_finite() && self.y.is_finite()
71    }
72
73    /// Per-axis minimum.
74    #[must_use]
75    #[inline]
76    pub fn min(self, other: Self) -> Self {
77        pos2(self.x.min(other.x), self.y.min(other.y))
78    }
79
80    /// Per-axis maximum.
81    #[must_use]
82    #[inline]
83    pub fn max(self, other: Self) -> Self {
84        pos2(self.x.max(other.x), self.y.max(other.y))
85    }
86
87    #[must_use]
88    #[inline]
89    pub fn clamp(self, min: Self, max: Self) -> Self {
90        Self {
91            x: self.x.clamp(min.x, max.x),
92            y: self.y.clamp(min.y, max.y),
93        }
94    }
95
96    /// `0.0 => self`, `1.0 => other`.
97    #[must_use]
98    pub fn lerp(self, other: Self, t: f32) -> Self {
99        Self {
100            x: lerp((self.x, other.x), t),
101            y: lerp((self.y, other.y), t),
102        }
103    }
104}
105
106// Sound in the same sense emath's is: nothing constructs a NaN coordinate
107// deliberately, and `==` on grid-derived coordinates is an exact test.
108impl Eq for Pos2 {}
109
110impl From<[f32; 2]> for Pos2 {
111    #[inline]
112    fn from([x, y]: [f32; 2]) -> Self {
113        Self { x, y }
114    }
115}
116
117impl From<Pos2> for [f32; 2] {
118    #[inline]
119    fn from(p: Pos2) -> Self {
120        [p.x, p.y]
121    }
122}
123
124impl From<(f32, f32)> for Pos2 {
125    #[inline]
126    fn from((x, y): (f32, f32)) -> Self {
127        Self { x, y }
128    }
129}
130
131impl From<Pos2> for (f32, f32) {
132    #[inline]
133    fn from(p: Pos2) -> Self {
134        (p.x, p.y)
135    }
136}
137
138impl Add<Vec2> for Pos2 {
139    type Output = Self;
140
141    #[inline]
142    fn add(self, rhs: Vec2) -> Self {
143        pos2(self.x + rhs.x, self.y + rhs.y)
144    }
145}
146
147impl AddAssign<Vec2> for Pos2 {
148    #[inline]
149    fn add_assign(&mut self, rhs: Vec2) {
150        *self = *self + rhs;
151    }
152}
153
154impl Sub for Pos2 {
155    type Output = Vec2;
156
157    #[inline]
158    fn sub(self, rhs: Self) -> Vec2 {
159        Vec2::new(self.x - rhs.x, self.y - rhs.y)
160    }
161}
162
163impl Sub<Vec2> for Pos2 {
164    type Output = Self;
165
166    #[inline]
167    fn sub(self, rhs: Vec2) -> Self {
168        pos2(self.x - rhs.x, self.y - rhs.y)
169    }
170}
171
172impl SubAssign<Vec2> for Pos2 {
173    #[inline]
174    fn sub_assign(&mut self, rhs: Vec2) {
175        *self = *self - rhs;
176    }
177}
178
179impl Mul<f32> for Pos2 {
180    type Output = Self;
181
182    #[inline]
183    fn mul(self, factor: f32) -> Self {
184        pos2(self.x * factor, self.y * factor)
185    }
186}
187
188impl Mul<Pos2> for f32 {
189    type Output = Pos2;
190
191    #[inline]
192    fn mul(self, p: Pos2) -> Pos2 {
193        pos2(self * p.x, self * p.y)
194    }
195}
196
197impl MulAssign<f32> for Pos2 {
198    #[inline]
199    fn mul_assign(&mut self, rhs: f32) {
200        *self = *self * rhs;
201    }
202}
203
204impl Div<f32> for Pos2 {
205    type Output = Self;
206
207    #[inline]
208    fn div(self, factor: f32) -> Self {
209        pos2(self.x / factor, self.y / factor)
210    }
211}
212
213impl fmt::Debug for Pos2 {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        if let Some(precision) = f.precision() {
216            write!(f, "[{1:.0$} {2:.0$}]", precision, self.x, self.y)
217        } else {
218            write!(f, "[{:.1} {:.1}]", self.x, self.y)
219        }
220    }
221}
222
223impl fmt::Display for Pos2 {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        write!(f, "[{} {}]", self.x, self.y)
226    }
227}