honeycomb_core/geometry/dim2/
vertex.rs

1//! Custom spatial representation
2//!
3//! This module contains all code used to model vertices.
4
5use crate::attributes::{AttrSparseVec, AttributeBind, AttributeError, AttributeUpdate};
6use crate::cmap::{OrbitPolicy, VertexIdType};
7use crate::geometry::{CoordsFloat, Vector2};
8
9/// # 2D vertex structure
10///
11/// ## Attribute behavior
12///
13/// - binds to 0-cells,
14/// - merge policy: the new vertex is placed at the midpoint between the two existing ones,
15/// - split policy: the current vertex is duplicated,
16/// - fallback policies: default implementations are used.
17///
18/// ## Generics
19///
20/// - `T: CoordsFloat` -- Generic FP type for coordinates.
21///
22/// ## Example
23///
24/// ```
25/// # use honeycomb_core::geometry::CoordsError;
26/// # fn main() -> Result<(), CoordsError> {
27/// use honeycomb_core::geometry::{Vector2, Vertex2};
28///
29/// let v1 = Vertex2(1.0, 0.0);
30/// let v2 = Vertex2(1.0, 1.0);
31///
32/// assert_eq!(v1.x(), 1.0);
33/// assert_eq!(v1.y(), 0.0);
34///
35/// let two: f64 = 2.0;
36/// // vectorAB = vertexB - vertexA
37/// let v2_minus_v1: Vector2<f64> = v2 - v1;
38///
39/// assert_eq!(v2_minus_v1.norm(), 1.0);
40/// assert_eq!(v2_minus_v1.unit_dir()?, Vector2::unit_y());
41///
42/// let mut v3 = Vertex2(0.0, 1.0);
43/// // vertexA + vectorB = vertexA'
44/// v3 += v2_minus_v1;
45///
46/// assert_eq!(v3.x(), 0.0);
47/// assert_eq!(v3.y(), 2.0);
48///
49/// # Ok(())
50/// # }
51/// ```
52#[derive(Debug, Clone, Copy, Default, PartialEq)]
53pub struct Vertex2<T: CoordsFloat>(pub T, pub T);
54
55unsafe impl<T: CoordsFloat> Send for Vertex2<T> {}
56unsafe impl<T: CoordsFloat> Sync for Vertex2<T> {}
57
58impl<T: CoordsFloat> Vertex2<T> {
59    /// Consume `self` to return inner values.
60    pub fn into_inner(self) -> (T, T) {
61        (self.0, self.1)
62    }
63
64    /// Return the value of the `x` coordinate of the vertex.
65    pub fn x(&self) -> T {
66        self.0
67    }
68
69    /// Return the value of the `y` coordinate of the vertex.
70    pub fn y(&self) -> T {
71        self.1
72    }
73
74    /// Compute the mid-point between two vertices.
75    ///
76    /// # Panics
77    ///
78    /// This function may panic if it cannot initialize an object `T: CoordsFloat` from the value
79    /// `2.0`. The chance of this happening when using `T = f64` or `T = f32` is most likely zero.
80    ///
81    /// # Example
82    ///
83    /// ```rust
84    /// use honeycomb_core::geometry::Vertex2;
85    ///
86    /// let far_far_away: Vertex2<f64> = Vertex2(2.0, 2.0);
87    /// let origin: Vertex2<f64> = Vertex2::default();
88    ///
89    /// assert_eq!(Vertex2::average(&origin, &far_far_away), Vertex2(1.0, 1.0));
90    /// ```
91    pub fn average(lhs: &Vertex2<T>, rhs: &Vertex2<T>) -> Vertex2<T> {
92        let two = T::from(2.0).unwrap();
93        Vertex2((lhs.0 + rhs.0) / two, (lhs.1 + rhs.1) / two)
94    }
95
96    /// Compute the scalar component of the `v1v2 x v2v3` cross product.
97    pub fn cross_product_from_vertices(v1: &Self, v2: &Self, v3: &Self) -> T {
98        (v2.x() - v1.x()) * (v3.y() - v2.y()) - (v2.y() - v1.y()) * (v3.x() - v2.x())
99    }
100}
101
102// Building trait
103
104impl<T: CoordsFloat> From<(T, T)> for Vertex2<T> {
105    fn from((x, y): (T, T)) -> Self {
106        Self(x, y)
107    }
108}
109
110// Basic operations
111
112// -- add flavors
113
114impl<T: CoordsFloat> std::ops::Add<Vector2<T>> for Vertex2<T> {
115    // Vertex + Vector = Vertex
116    type Output = Self;
117
118    fn add(self, rhs: Vector2<T>) -> Self::Output {
119        Self(self.0 + rhs.0, self.1 + rhs.1)
120    }
121}
122
123impl<T: CoordsFloat> std::ops::AddAssign<Vector2<T>> for Vertex2<T> {
124    fn add_assign(&mut self, rhs: Vector2<T>) {
125        self.0 += rhs.0;
126        self.1 += rhs.1;
127    }
128}
129
130impl<T: CoordsFloat> std::ops::Add<&Vector2<T>> for Vertex2<T> {
131    // Vertex + Vector = Vertex
132    type Output = Self;
133
134    fn add(self, rhs: &Vector2<T>) -> Self::Output {
135        Self(self.0 + rhs.0, self.1 + rhs.1)
136    }
137}
138
139impl<T: CoordsFloat> std::ops::AddAssign<&Vector2<T>> for Vertex2<T> {
140    fn add_assign(&mut self, rhs: &Vector2<T>) {
141        self.0 += rhs.0;
142        self.1 += rhs.1;
143    }
144}
145
146// -- sub flavors
147
148impl<T: CoordsFloat> std::ops::Sub<Vector2<T>> for Vertex2<T> {
149    // Vertex - Vector = Vertex
150    type Output = Self;
151
152    fn sub(self, rhs: Vector2<T>) -> Self::Output {
153        Self(self.0 - rhs.0, self.1 - rhs.1)
154    }
155}
156
157impl<T: CoordsFloat> std::ops::SubAssign<Vector2<T>> for Vertex2<T> {
158    fn sub_assign(&mut self, rhs: Vector2<T>) {
159        self.0 -= rhs.0;
160        self.1 -= rhs.1;
161    }
162}
163
164impl<T: CoordsFloat> std::ops::Sub<&Vector2<T>> for Vertex2<T> {
165    // Vertex - Vector = Vertex
166    type Output = Self;
167
168    fn sub(self, rhs: &Vector2<T>) -> Self::Output {
169        Self(self.0 - rhs.0, self.1 - rhs.1)
170    }
171}
172
173impl<T: CoordsFloat> std::ops::SubAssign<&Vector2<T>> for Vertex2<T> {
174    fn sub_assign(&mut self, rhs: &Vector2<T>) {
175        self.0 -= rhs.0;
176        self.1 -= rhs.1;
177    }
178}
179
180impl<T: CoordsFloat> std::ops::Sub<Vertex2<T>> for Vertex2<T> {
181    type Output = Vector2<T>;
182
183    fn sub(self, rhs: Vertex2<T>) -> Self::Output {
184        Vector2(self.0 - rhs.0, self.1 - rhs.1)
185    }
186}
187
188impl<T: CoordsFloat> AttributeUpdate for Vertex2<T> {
189    fn merge(attr1: Self, attr2: Self) -> Result<Self, AttributeError> {
190        Ok(Self::average(&attr1, &attr2))
191    }
192
193    fn split(attr: Self) -> Result<(Self, Self), AttributeError> {
194        Ok((attr, attr))
195    }
196
197    // override default impl
198    fn merge_incomplete(attr: Self) -> Result<Self, AttributeError> {
199        Ok(attr)
200    }
201}
202
203impl<T: CoordsFloat> AttributeBind for Vertex2<T> {
204    type StorageType = AttrSparseVec<Self>;
205    type IdentifierType = VertexIdType;
206    const BIND_POLICY: OrbitPolicy = OrbitPolicy::Vertex;
207}