1use core::fmt;
7use core::ops::{Add, AddAssign, Sub, SubAssign};
8
9use crate::common::FloatExt;
10use crate::{Axis, 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 #[inline]
208 pub fn get_coord(self, axis: Axis) -> f64 {
209 match axis {
210 Axis::Horizontal => self.x,
211 Axis::Vertical => self.y,
212 }
213 }
214
215 #[inline]
217 pub fn get_coord_mut(&mut self, axis: Axis) -> &mut f64 {
218 match axis {
219 Axis::Horizontal => &mut self.x,
220 Axis::Vertical => &mut self.y,
221 }
222 }
223
224 #[inline]
226 pub fn set_coord(&mut self, axis: Axis, value: f64) {
227 match axis {
228 Axis::Horizontal => self.x = value,
229 Axis::Vertical => self.y = value,
230 }
231 }
232}
233
234impl From<(f32, f32)> for Point {
235 #[inline(always)]
236 fn from(v: (f32, f32)) -> Point {
237 Point {
238 x: v.0 as f64,
239 y: v.1 as f64,
240 }
241 }
242}
243
244impl From<(f64, f64)> for Point {
245 #[inline(always)]
246 fn from(v: (f64, f64)) -> Point {
247 Point { x: v.0, y: v.1 }
248 }
249}
250
251impl From<Point> for (f64, f64) {
252 #[inline(always)]
253 fn from(v: Point) -> (f64, f64) {
254 (v.x, v.y)
255 }
256}
257
258impl Add<Vec2> for Point {
259 type Output = Point;
260
261 #[inline]
262 fn add(self, other: Vec2) -> Self {
263 Point::new(self.x + other.x, self.y + other.y)
264 }
265}
266
267impl AddAssign<Vec2> for Point {
268 #[inline]
269 fn add_assign(&mut self, other: Vec2) {
270 *self = Point::new(self.x + other.x, self.y + other.y);
271 }
272}
273
274impl Sub<Vec2> for Point {
275 type Output = Point;
276
277 #[inline]
278 fn sub(self, other: Vec2) -> Self {
279 Point::new(self.x - other.x, self.y - other.y)
280 }
281}
282
283impl SubAssign<Vec2> for Point {
284 #[inline]
285 fn sub_assign(&mut self, other: Vec2) {
286 *self = Point::new(self.x - other.x, self.y - other.y);
287 }
288}
289
290impl Add<(f64, f64)> for Point {
291 type Output = Point;
292
293 #[inline]
294 fn add(self, (x, y): (f64, f64)) -> Self {
295 Point::new(self.x + x, self.y + y)
296 }
297}
298
299impl AddAssign<(f64, f64)> for Point {
300 #[inline]
301 fn add_assign(&mut self, (x, y): (f64, f64)) {
302 *self = Point::new(self.x + x, self.y + y);
303 }
304}
305
306impl Sub<(f64, f64)> for Point {
307 type Output = Point;
308
309 #[inline]
310 fn sub(self, (x, y): (f64, f64)) -> Self {
311 Point::new(self.x - x, self.y - y)
312 }
313}
314
315impl SubAssign<(f64, f64)> for Point {
316 #[inline]
317 fn sub_assign(&mut self, (x, y): (f64, f64)) {
318 *self = Point::new(self.x - x, self.y - y);
319 }
320}
321
322impl Sub<Point> for Point {
323 type Output = Vec2;
324
325 #[inline]
326 fn sub(self, other: Point) -> Vec2 {
327 Vec2::new(self.x - other.x, self.y - other.y)
328 }
329}
330
331impl fmt::Debug for Point {
332 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
333 write!(f, "({:?}, {:?})", self.x, self.y)
334 }
335}
336
337impl fmt::Display for Point {
338 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
339 write!(formatter, "(")?;
340 fmt::Display::fmt(&self.x, formatter)?;
341 write!(formatter, ", ")?;
342 fmt::Display::fmt(&self.y, formatter)?;
343 write!(formatter, ")")
344 }
345}
346
347#[cfg(feature = "mint")]
348impl From<Point> for mint::Point2<f64> {
349 #[inline(always)]
350 fn from(p: Point) -> mint::Point2<f64> {
351 mint::Point2 { x: p.x, y: p.y }
352 }
353}
354
355#[cfg(feature = "mint")]
356impl From<mint::Point2<f64>> for Point {
357 #[inline(always)]
358 fn from(p: mint::Point2<f64>) -> Point {
359 Point { x: p.x, y: p.y }
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 #[test]
367 fn point_arithmetic() {
368 assert_eq!(
369 Point::new(0., 0.) - Vec2::new(10., 0.),
370 Point::new(-10., 0.)
371 );
372 assert_eq!(
373 Point::new(0., 0.) - Point::new(-5., 101.),
374 Vec2::new(5., -101.)
375 );
376 }
377
378 #[test]
379 #[allow(clippy::float_cmp)]
380 fn distance() {
381 let p1 = Point::new(0., 10.);
382 let p2 = Point::new(0., 5.);
383 assert_eq!(p1.distance(p2), 5.);
384
385 let p1 = Point::new(-11., 1.);
386 let p2 = Point::new(-7., -2.);
387 assert_eq!(p1.distance(p2), 5.);
388 }
389
390 #[test]
391 fn display() {
392 let p = Point::new(0.12345, 9.87654);
393 assert_eq!(format!("{p}"), "(0.12345, 9.87654)");
394
395 let p = Point::new(0.12345, 9.87654);
396 assert_eq!(format!("{p:.2}"), "(0.12, 9.88)");
397 }
398}