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
97// Building trait
98
99impl<T: CoordsFloat> From<(T, T)> for Vertex2<T> {
100    fn from((x, y): (T, T)) -> Self {
101        Self(x, y)
102    }
103}
104
105// Basic operations
106
107// -- add flavors
108
109impl<T: CoordsFloat> std::ops::Add<Vector2<T>> for Vertex2<T> {
110    // Vertex + Vector = Vertex
111    type Output = Self;
112
113    fn add(self, rhs: Vector2<T>) -> Self::Output {
114        Self(self.0 + rhs.0, self.1 + rhs.1)
115    }
116}
117
118impl<T: CoordsFloat> std::ops::AddAssign<Vector2<T>> for Vertex2<T> {
119    fn add_assign(&mut self, rhs: Vector2<T>) {
120        self.0 += rhs.0;
121        self.1 += rhs.1;
122    }
123}
124
125impl<T: CoordsFloat> std::ops::Add<&Vector2<T>> for Vertex2<T> {
126    // Vertex + Vector = Vertex
127    type Output = Self;
128
129    fn add(self, rhs: &Vector2<T>) -> Self::Output {
130        Self(self.0 + rhs.0, self.1 + rhs.1)
131    }
132}
133
134impl<T: CoordsFloat> std::ops::AddAssign<&Vector2<T>> for Vertex2<T> {
135    fn add_assign(&mut self, rhs: &Vector2<T>) {
136        self.0 += rhs.0;
137        self.1 += rhs.1;
138    }
139}
140
141// -- sub flavors
142
143impl<T: CoordsFloat> std::ops::Sub<Vector2<T>> for Vertex2<T> {
144    // Vertex - Vector = Vertex
145    type Output = Self;
146
147    fn sub(self, rhs: Vector2<T>) -> Self::Output {
148        Self(self.0 - rhs.0, self.1 - rhs.1)
149    }
150}
151
152impl<T: CoordsFloat> std::ops::SubAssign<Vector2<T>> for Vertex2<T> {
153    fn sub_assign(&mut self, rhs: Vector2<T>) {
154        self.0 -= rhs.0;
155        self.1 -= rhs.1;
156    }
157}
158
159impl<T: CoordsFloat> std::ops::Sub<&Vector2<T>> for Vertex2<T> {
160    // Vertex - Vector = Vertex
161    type Output = Self;
162
163    fn sub(self, rhs: &Vector2<T>) -> Self::Output {
164        Self(self.0 - rhs.0, self.1 - rhs.1)
165    }
166}
167
168impl<T: CoordsFloat> std::ops::SubAssign<&Vector2<T>> for Vertex2<T> {
169    fn sub_assign(&mut self, rhs: &Vector2<T>) {
170        self.0 -= rhs.0;
171        self.1 -= rhs.1;
172    }
173}
174
175impl<T: CoordsFloat> std::ops::Sub<Vertex2<T>> for Vertex2<T> {
176    type Output = Vector2<T>;
177
178    fn sub(self, rhs: Vertex2<T>) -> Self::Output {
179        Vector2(self.0 - rhs.0, self.1 - rhs.1)
180    }
181}
182
183impl<T: CoordsFloat> AttributeUpdate for Vertex2<T> {
184    fn merge(attr1: Self, attr2: Self) -> Result<Self, AttributeError> {
185        Ok(Self::average(&attr1, &attr2))
186    }
187
188    fn split(attr: Self) -> Result<(Self, Self), AttributeError> {
189        Ok((attr, attr))
190    }
191
192    // override default impl
193    fn merge_incomplete(attr: Self) -> Result<Self, AttributeError> {
194        Ok(attr)
195    }
196}
197
198impl<T: CoordsFloat> AttributeBind for Vertex2<T> {
199    type StorageType = AttrSparseVec<Self>;
200    type IdentifierType = VertexIdType;
201    const BIND_POLICY: OrbitPolicy = OrbitPolicy::Vertex;
202}