1use core::fmt;
7use core::ops::{Add, AddAssign, Sub, SubAssign};
8
9use crate::common::FloatExt;
10use crate::Vec2;
11
12#[cfg(not(feature = "std"))]
13use crate::common::FloatFuncs;
14
15#[derive(Clone, Copy, Default, PartialEq)]
26#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Point {
29 pub x: f64,
31 pub y: f64,
33}
34
35impl Point {
36 pub const ZERO: Point = Point::new(0., 0.);
38
39 pub const ORIGIN: Point = Point::new(0., 0.);
41
42 #[inline(always)]
44 pub const fn new(x: f64, y: f64) -> Self {
45 Point { x, y }
46 }
47
48 #[inline(always)]
50 pub const fn to_vec2(self) -> Vec2 {
51 Vec2::new(self.x, self.y)
52 }
53
54 #[inline]
56 pub fn lerp(self, other: Point, t: f64) -> Point {
57 self.to_vec2().lerp(other.to_vec2(), t).to_point()
58 }
59
60 #[inline]
62 pub fn midpoint(self, other: Point) -> Point {
63 Point::new(0.5 * (self.x + other.x), 0.5 * (self.y + other.y))
64 }
65
66 #[inline]
70 pub fn distance(self, other: Point) -> f64 {
71 (self - other).hypot()
72 }
73
74 #[inline]
78 pub fn distance_squared(self, other: Point) -> f64 {
79 (self - other).hypot2()
80 }
81
82 #[inline]
98 pub fn round(self) -> Point {
99 Point::new(self.x.round(), self.y.round())
100 }
101
102 #[inline]
120 pub fn ceil(self) -> Point {
121 Point::new(self.x.ceil(), self.y.ceil())
122 }
123
124 #[inline]
142 pub fn floor(self) -> Point {
143 Point::new(self.x.floor(), self.y.floor())
144 }
145
146 #[inline]
164 pub fn expand(self) -> Point {
165 Point::new(self.x.expand(), self.y.expand())
166 }
167
168 #[inline]
186 pub fn trunc(self) -> Point {
187 Point::new(self.x.trunc(), self.y.trunc())
188 }
189
190 #[inline]
194 pub fn is_finite(self) -> bool {
195 self.x.is_finite() && self.y.is_finite()
196 }
197
198 #[inline]
202 pub fn is_nan(self) -> bool {
203 self.x.is_nan() || self.y.is_nan()
204 }
205}
206
207impl From<(f32, f32)> for Point {
208 #[inline(always)]
209 fn from(v: (f32, f32)) -> Point {
210 Point {
211 x: v.0 as f64,
212 y: v.1 as f64,
213 }
214 }
215}
216
217impl From<(f64, f64)> for Point {
218 #[inline(always)]
219 fn from(v: (f64, f64)) -> Point {
220 Point { x: v.0, y: v.1 }
221 }
222}
223
224impl From<Point> for (f64, f64) {
225 #[inline(always)]
226 fn from(v: Point) -> (f64, f64) {
227 (v.x, v.y)
228 }
229}
230
231impl Add<Vec2> for Point {
232 type Output = Point;
233
234 #[inline]
235 fn add(self, other: Vec2) -> Self {
236 Point::new(self.x + other.x, self.y + other.y)
237 }
238}
239
240impl AddAssign<Vec2> for Point {
241 #[inline]
242 fn add_assign(&mut self, other: Vec2) {
243 *self = Point::new(self.x + other.x, self.y + other.y);
244 }
245}
246
247impl Sub<Vec2> for Point {
248 type Output = Point;
249
250 #[inline]
251 fn sub(self, other: Vec2) -> Self {
252 Point::new(self.x - other.x, self.y - other.y)
253 }
254}
255
256impl SubAssign<Vec2> for Point {
257 #[inline]
258 fn sub_assign(&mut self, other: Vec2) {
259 *self = Point::new(self.x - other.x, self.y - other.y);
260 }
261}
262
263impl Add<(f64, f64)> for Point {
264 type Output = Point;
265
266 #[inline]
267 fn add(self, (x, y): (f64, f64)) -> Self {
268 Point::new(self.x + x, self.y + y)
269 }
270}
271
272impl AddAssign<(f64, f64)> for Point {
273 #[inline]
274 fn add_assign(&mut self, (x, y): (f64, f64)) {
275 *self = Point::new(self.x + x, self.y + y);
276 }
277}
278
279impl Sub<(f64, f64)> for Point {
280 type Output = Point;
281
282 #[inline]
283 fn sub(self, (x, y): (f64, f64)) -> Self {
284 Point::new(self.x - x, self.y - y)
285 }
286}
287
288impl SubAssign<(f64, f64)> for Point {
289 #[inline]
290 fn sub_assign(&mut self, (x, y): (f64, f64)) {
291 *self = Point::new(self.x - x, self.y - y);
292 }
293}
294
295impl Sub<Point> for Point {
296 type Output = Vec2;
297
298 #[inline]
299 fn sub(self, other: Point) -> Vec2 {
300 Vec2::new(self.x - other.x, self.y - other.y)
301 }
302}
303
304impl fmt::Debug for Point {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 write!(f, "({:?}, {:?})", self.x, self.y)
307 }
308}
309
310impl fmt::Display for Point {
311 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
312 write!(formatter, "(")?;
313 fmt::Display::fmt(&self.x, formatter)?;
314 write!(formatter, ", ")?;
315 fmt::Display::fmt(&self.y, formatter)?;
316 write!(formatter, ")")
317 }
318}
319
320#[cfg(feature = "mint")]
321impl From<Point> for mint::Point2<f64> {
322 #[inline(always)]
323 fn from(p: Point) -> mint::Point2<f64> {
324 mint::Point2 { x: p.x, y: p.y }
325 }
326}
327
328#[cfg(feature = "mint")]
329impl From<mint::Point2<f64>> for Point {
330 #[inline(always)]
331 fn from(p: mint::Point2<f64>) -> Point {
332 Point { x: p.x, y: p.y }
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 #[test]
340 fn point_arithmetic() {
341 assert_eq!(
342 Point::new(0., 0.) - Vec2::new(10., 0.),
343 Point::new(-10., 0.)
344 );
345 assert_eq!(
346 Point::new(0., 0.) - Point::new(-5., 101.),
347 Vec2::new(5., -101.)
348 );
349 }
350
351 #[test]
352 #[allow(clippy::float_cmp)]
353 fn distance() {
354 let p1 = Point::new(0., 10.);
355 let p2 = Point::new(0., 5.);
356 assert_eq!(p1.distance(p2), 5.);
357
358 let p1 = Point::new(-11., 1.);
359 let p2 = Point::new(-7., -2.);
360 assert_eq!(p1.distance(p2), 5.);
361 }
362
363 #[test]
364 fn display() {
365 let p = Point::new(0.12345, 9.87654);
366 assert_eq!(format!("{p}"), "(0.12345, 9.87654)");
367
368 let p = Point::new(0.12345, 9.87654);
369 assert_eq!(format!("{p:.2}"), "(0.12, 9.88)");
370 }
371}