spade/
point.rs

1use num_traits::{Num, Signed};
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6/// A coordinate type that can be used with a triangulation.
7///
8/// Internally, most calculations are performed after converting the type into a `f64`.
9/// However, changing this to `f32` will reduce the required storage space slightly.
10///
11/// This type should usually be either `f32` or `f64`.
12pub trait SpadeNum:
13    Num + PartialOrd + Into<f64> + From<f32> + Copy + Signed + core::fmt::Debug
14{
15}
16
17impl<T> SpadeNum for T where
18    T: Num + PartialOrd + Into<f64> + From<f32> + Copy + Signed + core::fmt::Debug
19{
20}
21
22/// A two-dimensional point.
23///
24/// This is the basic type used for defining positions.
25#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy, Default, Hash)]
26#[cfg_attr(
27    feature = "serde",
28    derive(Serialize, Deserialize),
29    serde(crate = "serde")
30)]
31pub struct Point2<S> {
32    /// The point's x coordinate
33    pub x: S,
34    /// The point's y coordinate
35    pub y: S,
36}
37
38impl<S> Point2<S> {
39    /// Creates a new point.
40    #[inline]
41    pub const fn new(x: S, y: S) -> Self {
42        Point2 { x, y }
43    }
44}
45
46impl<S: SpadeNum> Point2<S> {
47    /// Returns the squared distance of this point and another point.
48    #[inline]
49    pub fn distance_2(&self, other: Self) -> S {
50        self.sub(other).length2()
51    }
52
53    pub(crate) fn to_f64(self) -> Point2<f64> {
54        Point2::new(self.x.into(), self.y.into())
55    }
56
57    pub(crate) fn mul(&self, factor: S) -> Self {
58        Point2 {
59            x: self.x * factor,
60            y: self.y * factor,
61        }
62    }
63
64    pub(crate) fn add(&self, other: Self) -> Self {
65        Point2 {
66            x: self.x + other.x,
67            y: self.y + other.y,
68        }
69    }
70
71    pub(crate) fn length2(&self) -> S {
72        self.x * self.x + self.y * self.y
73    }
74
75    pub(crate) fn sub(&self, other: Self) -> Self {
76        Point2 {
77            x: self.x - other.x,
78            y: self.y - other.y,
79        }
80    }
81
82    pub(crate) fn dot(&self, other: Self) -> S {
83        self.x * other.x + self.y * other.y
84    }
85
86    pub(crate) fn all_component_wise(&self, other: Self, f: impl Fn(S, S) -> bool) -> bool {
87        f(self.x, other.x) && f(self.y, other.y)
88    }
89}
90
91impl<S: SpadeNum> From<Point2<S>> for [S; 2] {
92    #[inline]
93    fn from(point: Point2<S>) -> Self {
94        [point.x, point.y]
95    }
96}
97
98impl<S: SpadeNum> From<Point2<S>> for (S, S) {
99    #[inline]
100    fn from(point: Point2<S>) -> (S, S) {
101        (point.x, point.y)
102    }
103}
104
105impl<S: SpadeNum> From<[S; 2]> for Point2<S> {
106    #[inline]
107    fn from(source: [S; 2]) -> Self {
108        Self::new(source[0], source[1])
109    }
110}
111
112impl<S: SpadeNum> From<(S, S)> for Point2<S> {
113    #[inline]
114    fn from(source: (S, S)) -> Self {
115        Self::new(source.0, source.1)
116    }
117}
118
119/// An object with a position.
120///
121/// Vertices need to implement this trait to allow being inserted into triangulations.
122pub trait HasPosition {
123    /// The number type used by this coordinate type.
124    type Scalar: SpadeNum;
125
126    /// Returns the position of this object.
127    ///
128    /// **Note**: It is assumed that the position doesn't change once it has been
129    /// inserted into a triangulation. Failing this requirement can lead to crashes,
130    /// invalid results or endless loops.
131    fn position(&self) -> Point2<Self::Scalar>;
132}
133
134impl<S: SpadeNum> HasPosition for Point2<S> {
135    type Scalar = S;
136
137    fn position(&self) -> Point2<S> {
138        *self
139    }
140}