iron_shapes/
point.rs

1// Copyright (c) 2018-2020 Thomas Kramer.
2// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Points represent a location in the two dimensional plane by an `x` and `y` coordinate.
7
8use crate::traits::{BoundingBox, MapPointwise, TryBoundingBox, TryCastCoord};
9use crate::vector::Vector;
10
11use std::cmp::{Ord, Ordering};
12use std::fmt;
13
14use crate::prelude::{Orientation2D, Rect};
15pub use num_traits::Zero;
16use num_traits::{Float, NumCast};
17pub use std::ops::Deref;
18use std::ops::{Add, AddAssign, DerefMut, Div, Mul, MulAssign, Neg, Sub, SubAssign};
19
20/// Shorthand notation for creating a point.
21///
22/// # Example
23/// ```
24/// # #[macro_use]
25/// # extern crate iron_shapes;
26/// # fn main() {
27/// use iron_shapes::prelude::*;
28/// let p = point!(1, 2);
29/// assert_eq!(p, Point::new(1, 2));
30/// # }
31/// ```
32#[macro_export]
33macro_rules! point {
34    ($x:expr, $y:expr) => {
35        Point::new($x, $y)
36    };
37}
38
39/// A point is defined by a x and y coordinate in the euclidean plane.
40#[derive(Copy, Clone, Default, Hash, PartialEq, Eq)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42pub struct Point<T> {
43    /// Store the location as a translation from the origin.
44    location: Vector<T>,
45}
46
47impl<T> Deref for Point<T> {
48    type Target = Vector<T>;
49
50    #[inline]
51    fn deref(&self) -> &Self::Target {
52        &self.location
53    }
54}
55
56impl<T> DerefMut for Point<T> {
57    #[inline]
58    fn deref_mut(&mut self) -> &mut Self::Target {
59        &mut self.location
60    }
61}
62
63impl<T> From<Vector<T>> for Point<T> {
64    #[inline]
65    fn from(v: Vector<T>) -> Self {
66        Point { location: v }
67    }
68}
69
70impl<T: Copy> From<&Vector<T>> for Point<T> {
71    #[inline]
72    fn from(v: &Vector<T>) -> Self {
73        Self::from(*v)
74    }
75}
76
77impl<T> From<Point<T>> for Vector<T> {
78    #[inline]
79    fn from(p: Point<T>) -> Self {
80        p.location
81    }
82}
83
84impl<T: Copy> From<&Point<T>> for Vector<T> {
85    #[inline]
86    fn from(p: &Point<T>) -> Self {
87        p.location
88    }
89}
90
91impl<T> Point<T> {
92    /// Create a new point with `x` and `y` coordinates.
93    #[inline]
94    pub fn new(x: T, y: T) -> Self {
95        Point {
96            location: Vector::new(x, y),
97        }
98    }
99}
100
101impl<T: Copy> Point<T> {
102    /// Return the location of this point as a vector.
103    #[inline]
104    pub fn v(&self) -> Vector<T> {
105        self.location
106    }
107
108    /// Get a specific coordinate of the point.
109    #[inline]
110    pub fn get(&self, coord: Orientation2D) -> T {
111        match coord {
112            Orientation2D::Horizontal => self.x,
113            Orientation2D::Vertical => self.y,
114        }
115    }
116
117    /// Set a specific coordinate of the point.
118    #[inline]
119    pub fn set(&mut self, coord: Orientation2D, value: T) {
120        match coord {
121            Orientation2D::Horizontal => self.x = value,
122            Orientation2D::Vertical => self.y = value,
123        }
124    }
125}
126
127impl<T: Zero> Point<T> {
128    /// Get zero-Point.
129    ///
130    /// # Examples
131    /// ```
132    /// use iron_shapes::point::Point;
133    ///
134    /// let a = Point::zero();
135    /// let b = Point::new(0, 0);
136    ///
137    /// assert_eq!(a, b);
138    /// ```
139    #[inline]
140    pub fn zero() -> Self {
141        Vector::zero().into()
142    }
143
144    /// Check if this is the zero-Point.
145    ///
146    /// # Examples
147    /// ```
148    /// use iron_shapes::point::*;
149    ///
150    /// assert!(Point::<usize>::zero().is_zero());
151    /// ```
152    #[inline]
153    pub fn is_zero(&self) -> bool {
154        self.x.is_zero() && self.y.is_zero()
155    }
156}
157
158impl<T: Copy + Mul<Output = T> + Add<Output = T> + Sub<Output = T>> Point<T> {
159    /// Compute the squared distance to the `other` point.
160    ///
161    /// # Examples
162    /// ```
163    /// use iron_shapes::point::*;
164    ///
165    /// let a = Point::new(0, 0);
166    /// let b = Point::new(2, 0);
167    /// assert_eq!(a.distance_sq(&b), 2*2);
168    /// ```
169    #[inline]
170    pub fn distance_sq(self, other: &Point<T>) -> T {
171        let diff = self - *other;
172        diff.norm2_squared()
173    }
174
175    /// Calculate the cross product of the two vectors defined by three points.
176    ///
177    /// A positive value implies that `self` → `a` → `b` is counter-clockwise, negative implies
178    /// clockwise.
179    ///
180    /// (`b` - `self`) x (`c` - `b`)
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use iron_shapes::point::Point;
186    ///
187    /// let a = Point::new(1,0);
188    /// let b = Point::new(1,1);
189    /// let c = Point::new(0,1);
190    ///
191    /// let p = a.cross_prod3(b, c);
192    ///
193    /// assert_eq!(p, (b-a).cross_prod(c - b));
194    /// ```
195    #[inline]
196    pub fn cross_prod3(&self, b: Point<T>, c: Point<T>) -> T {
197        (b.x - self.x) * (c.y - b.y) - (b.y - self.y) * (c.x - b.x)
198    }
199}
200
201impl<T: Copy + Sub<Output = T> + NumCast> Point<T> {
202    /// Compute the Euclidean distance betwen two points.
203    #[inline]
204    pub fn distance<F: Float>(self, other: &Point<T>) -> F {
205        let diff = self - *other;
206        diff.length()
207    }
208}
209
210/// Compare points.
211///
212/// The ordering is determined by the x-coordinates. If it is the same
213/// for both points the y-coordinate is used.
214///
215/// Point `a` > Point `b` iff `a.x > b.x || (a.x == b.x && a.y > b.y)`.
216impl<T: PartialOrd> PartialOrd for Point<T> {
217    #[inline]
218    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
219        match self.x.partial_cmp(&rhs.x) {
220            Some(Ordering::Equal) => self.y.partial_cmp(&rhs.y),
221            maybe_ordering => maybe_ordering,
222        }
223    }
224}
225
226/// Compare points.
227///
228/// The ordering is determined by the x-coordinates. If it is the same
229/// for both points the y-coordinate is used.
230///
231/// Point `a` > Point `b` iff `a.x > b.x || (a.x == b.x && a.y > b.y)`.
232impl<T: Ord> Ord for Point<T> {
233    #[inline]
234    fn cmp(&self, rhs: &Self) -> Ordering {
235        match self.x.cmp(&rhs.x) {
236            Ordering::Equal => self.y.cmp(&rhs.y),
237            ordering => ordering,
238        }
239    }
240}
241
242/// Point wise transformation for a single point.
243impl<T: Copy> MapPointwise<T> for Point<T> {
244    /// Point wise transformation.
245    #[inline]
246    fn transform<F>(&self, transformation: F) -> Self
247    where
248        F: Fn(Point<T>) -> Point<T>,
249    {
250        transformation(*self)
251    }
252}
253
254impl<T: Copy> From<Point<T>> for (T, T) {
255    #[inline]
256    fn from(p: Point<T>) -> Self {
257        (p.x, p.y)
258    }
259}
260
261impl<T: Copy> From<&Point<T>> for (T, T) {
262    #[inline]
263    fn from(p: &Point<T>) -> Self {
264        (p.x, p.y)
265    }
266}
267
268impl<T: Copy> From<(T, T)> for Point<T> {
269    #[inline]
270    fn from(coords: (T, T)) -> Self {
271        Point::new(coords.0, coords.1)
272    }
273}
274
275impl<'a, T: Copy> From<&'a (T, T)> for Point<T> {
276    #[inline]
277    fn from(coords: &'a (T, T)) -> Self {
278        Point::new(coords.0, coords.1)
279    }
280}
281
282impl<'a, T: Copy> From<&'a Point<T>> for Point<T> {
283    #[inline]
284    fn from(v: &'a Point<T>) -> Self {
285        Point::new(v.x, v.y)
286    }
287}
288
289impl<T: Copy> From<[T; 2]> for Point<T> {
290    #[inline]
291    fn from(coords: [T; 2]) -> Self {
292        Point::new(coords[0], coords[1])
293    }
294}
295
296impl<T: Copy> From<Point<T>> for [T; 2] {
297    #[inline]
298    fn from(p: Point<T>) -> Self {
299        [p.x, p.y]
300    }
301}
302
303impl<T> fmt::Debug for Point<T>
304where
305    T: fmt::Debug,
306{
307    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308        write!(f, "Point({:?}, {:?})", self.x, self.y)
309    }
310}
311
312impl<T> fmt::Display for Point<T>
313where
314    T: fmt::Display,
315{
316    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317        write!(f, "({}, {})", self.x, self.y)
318    }
319}
320
321impl<T: Copy + NumCast> Point<T> {
322    /// Convert Point into a Point with floating point data type.
323    #[inline]
324    pub fn cast_to_float<F: Float + NumCast>(&self) -> Point<F> {
325        // TODO: find conversion that does not panic for sure.
326        Point::new(F::from(self.x).unwrap(), F::from(self.y).unwrap())
327    }
328}
329
330impl<T: Copy + NumCast, Dst: Copy + NumCast> TryCastCoord<T, Dst> for Point<T> {
331    type Output = Point<Dst>;
332
333    #[inline]
334    fn try_cast(&self) -> Option<Self::Output> {
335        match (Dst::from(self.x), Dst::from(self.y)) {
336            (Some(x), Some(y)) => Some(Point::new(x, y)),
337            _ => None,
338        }
339    }
340}
341
342/// Point addition.
343impl<T, V> Add<V> for Point<T>
344where
345    T: Copy + Add<Output = T>,
346    V: Into<Point<T>>,
347{
348    type Output = Self;
349
350    #[inline]
351    fn add(self, rhs: V) -> Self {
352        let rhs = rhs.into();
353        Point::new(self.x + rhs.x, self.y + rhs.y)
354    }
355}
356
357impl<T, V> AddAssign<V> for Point<T>
358where
359    T: Copy + AddAssign<T>,
360    V: Into<Vector<T>>,
361{
362    #[inline]
363    fn add_assign(&mut self, rhs: V) {
364        let rhs = rhs.into();
365        self.location += rhs;
366    }
367}
368
369/// Subtract a point.
370impl<T> Sub<Point<T>> for Point<T>
371where
372    T: Copy + Sub<Output = T>,
373{
374    type Output = Vector<T>;
375
376    #[inline]
377    fn sub(self, rhs: Point<T>) -> Self::Output {
378        Vector::new(self.x - rhs.x, self.y - rhs.y)
379    }
380}
381
382/// Subtract a vector.
383impl<T> Sub<Vector<T>> for Point<T>
384where
385    T: Copy + Sub<Output = T>,
386{
387    type Output = Point<T>;
388
389    #[inline]
390    fn sub(self, rhs: Vector<T>) -> Self::Output {
391        Point::new(self.x - rhs.x, self.y - rhs.y)
392    }
393}
394
395impl<T, V> SubAssign<V> for Point<T>
396where
397    T: Copy + SubAssign<T>,
398    V: Into<Vector<T>>,
399{
400    #[inline]
401    fn sub_assign(&mut self, rhs: V) {
402        let rhs = rhs.into();
403        self.location -= rhs;
404    }
405}
406
407impl<T> Neg for Point<T>
408where
409    T: Copy + Neg<Output = T>,
410{
411    type Output = Self;
412
413    #[inline]
414    fn neg(self) -> Self {
415        Point::new(-self.x, -self.y)
416    }
417}
418
419/// Scalar multiplication.
420impl<T> Mul<T> for Point<T>
421where
422    T: Copy + Mul<Output = T>,
423{
424    type Output = Self;
425
426    #[inline]
427    fn mul(self, rhs: T) -> Self {
428        Point::new(self.x * rhs, self.y * rhs)
429    }
430}
431
432/// In-place scalar multiplication.
433impl<T> MulAssign<T> for Point<T>
434where
435    T: Copy + MulAssign<T>,
436{
437    #[inline]
438    fn mul_assign(&mut self, rhs: T) {
439        self.location *= rhs;
440    }
441}
442
443/// Scalar division.
444impl<T> Div<T> for Point<T>
445where
446    T: Copy + Div<Output = T>,
447{
448    type Output = Self;
449
450    #[inline]
451    fn div(self, rhs: T) -> Self {
452        Point::new(self.x / rhs, self.y / rhs)
453    }
454}
455
456impl<T: Copy + Zero + Add<Output = T>> std::iter::Sum for Point<T> {
457    /// Compute the sum of all points in the iterator.
458    /// If the iterator is empty, (0, 0) is returned.
459    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
460        iter.fold(Self::zero(), |acc, p| acc + p)
461    }
462}
463
464impl<T: Copy> BoundingBox<T> for Point<T> {
465    fn bounding_box(&self) -> Rect<T> {
466        Rect {
467            lower_left: *self,
468            upper_right: *self,
469        }
470    }
471}
472
473impl<T: Copy> TryBoundingBox<T> for Point<T> {
474    fn try_bounding_box(&self) -> Option<Rect<T>> {
475        Some(self.bounding_box())
476    }
477}