honeycomb_core/geometry/dim3/
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, Vector3, Vertex2};
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::{Vector3, Vertex3};
28///
29/// let v1 = Vertex3(1.0, 0.0, 0.0);
30/// let v2 = Vertex3(1.0, 1.0, 1.0);
31///
32/// assert_eq!(v1.x(), 1.0);
33/// assert_eq!(v1.y(), 0.0);
34/// assert_eq!(v1.z(), 0.0);
35///
36/// let two: f64 = 2.0;
37/// // vectorAB = vertexB - vertexA
38/// let v2_minus_v1: Vector3<f64> = v2 - v1;
39///
40/// assert_eq!(v2_minus_v1.norm(), two.sqrt());
41/// assert_eq!(v2_minus_v1.unit_dir()?, Vector3(0.0, 1.0 / two.sqrt(), 1.0 / two.sqrt()));
42///
43/// let mut v3 = Vertex3(0.0, 1.0, 1.0);
44/// // vertexA + vectorB = vertexA'
45/// v3 += v2_minus_v1;
46///
47/// assert_eq!(v3.x(), 0.0);
48/// assert_eq!(v3.y(), 2.0);
49/// assert_eq!(v3.z(), 2.0);
50///
51/// # Ok(())
52/// # }
53/// ```
54#[derive(Debug, Clone, Copy, Default, PartialEq)]
55pub struct Vertex3<T: CoordsFloat>(pub T, pub T, pub T);
56
57unsafe impl<T: CoordsFloat> Send for Vertex3<T> {}
58unsafe impl<T: CoordsFloat> Sync for Vertex3<T> {}
59
60impl<T: CoordsFloat> Vertex3<T> {
61    /// Consume `self` to return inner values.
62    pub fn into_inner(self) -> (T, T, T) {
63        (self.0, self.1, self.2)
64    }
65
66    /// Cast `self` to `f32` coordinates.
67    pub fn to_f32(self) -> Option<Vertex3<f32>> {
68        match (self.0.to_f32(), self.1.to_f32(), self.2.to_f32()) {
69            (Some(x), Some(y), Some(z)) => Some(Vertex3(x, y, z)),
70            _ => None,
71        }
72    }
73
74    /// Cast `self` to `f64` coordinates.
75    pub fn to_f64(self) -> Option<Vertex3<f64>> {
76        match (self.0.to_f64(), self.1.to_f64(), self.2.to_f64()) {
77            (Some(x), Some(y), Some(z)) => Some(Vertex3(x, y, z)),
78            _ => None,
79        }
80    }
81
82    /// Return the value of the `x` coordinate of the vertex.
83    pub fn x(&self) -> T {
84        self.0
85    }
86
87    /// Return the value of the `y` coordinate of the ver, Otex.
88    pub fn y(&self) -> T {
89        self.1
90    }
91
92    /// Return the value of the `z` coordinate of the vertex.
93    pub fn z(&self) -> T {
94        self.2
95    }
96
97    /// Compute the mid-point between two vertices.
98    ///
99    /// # Panics
100    ///
101    /// This function may panic if it cannot initialize an object `T: CoordsFloat` from the value
102    /// `2.0`. The chance of this happening when using `T = f64` or `T = f32` is most likely zero.
103    ///
104    /// # Example
105    ///
106    /// ```rust
107    /// use honeycomb_core::geometry::Vertex3;
108    ///
109    /// let far_far_away: Vertex3<f64> = Vertex3(2.0, 2.0, 2.0);
110    /// let origin: Vertex3<f64> = Vertex3::default();
111    ///
112    /// assert_eq!(Vertex3::average(&origin, &far_far_away), Vertex3(1.0, 1.0, 1.0));
113    /// ```
114    pub fn average(lhs: &Vertex3<T>, rhs: &Vertex3<T>) -> Vertex3<T> {
115        let two = T::from(2.0).unwrap();
116        Vertex3(
117            (lhs.0 + rhs.0) / two,
118            (lhs.1 + rhs.1) / two,
119            (lhs.2 + rhs.2) / two,
120        )
121    }
122}
123
124// Building trait
125
126impl<T: CoordsFloat> From<(T, T, T)> for Vertex3<T> {
127    fn from((x, y, z): (T, T, T)) -> Self {
128        Self(x, y, z)
129    }
130}
131
132impl<T: CoordsFloat> From<Vertex2<T>> for Vertex3<T> {
133    fn from(v: Vertex2<T>) -> Self {
134        Self(v.0, v.1, T::zero())
135    }
136}
137
138// Basic operations
139
140// -- add flavors
141
142impl<T: CoordsFloat> std::ops::Add<Vector3<T>> for Vertex3<T> {
143    // Vertex + Vector = Vertex
144    type Output = Self;
145    fn add(self, rhs: Vector3<T>) -> Self::Output {
146        Self(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
147    }
148}
149
150impl<T: CoordsFloat> std::ops::AddAssign<Vector3<T>> for Vertex3<T> {
151    fn add_assign(&mut self, rhs: Vector3<T>) {
152        self.0 += rhs.0;
153        self.1 += rhs.1;
154        self.2 += rhs.2;
155    }
156}
157
158impl<T: CoordsFloat> std::ops::Add<&Vector3<T>> for Vertex3<T> {
159    // Vertex + Vector = Vertex
160    type Output = Self;
161    fn add(self, rhs: &Vector3<T>) -> Self::Output {
162        Self(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
163    }
164}
165
166impl<T: CoordsFloat> std::ops::AddAssign<&Vector3<T>> for Vertex3<T> {
167    fn add_assign(&mut self, rhs: &Vector3<T>) {
168        self.0 += rhs.0;
169        self.1 += rhs.1;
170        self.2 += rhs.2;
171    }
172}
173
174// -- sub flavors
175impl<T: CoordsFloat> std::ops::Sub<Vector3<T>> for Vertex3<T> {
176    // Vertex - Vector = Vertex
177    type Output = Self;
178    fn sub(self, rhs: Vector3<T>) -> Self::Output {
179        Self(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
180    }
181}
182
183impl<T: CoordsFloat> std::ops::SubAssign<Vector3<T>> for Vertex3<T> {
184    fn sub_assign(&mut self, rhs: Vector3<T>) {
185        self.0 -= rhs.0;
186        self.1 -= rhs.1;
187        self.2 -= rhs.2;
188    }
189}
190
191impl<T: CoordsFloat> std::ops::Sub<&Vector3<T>> for Vertex3<T> {
192    // Vertex - Vector = Vertex
193    type Output = Self;
194    fn sub(self, rhs: &Vector3<T>) -> Self::Output {
195        Self(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
196    }
197}
198
199impl<T: CoordsFloat> std::ops::SubAssign<&Vector3<T>> for Vertex3<T> {
200    fn sub_assign(&mut self, rhs: &Vector3<T>) {
201        self.0 -= rhs.0;
202        self.1 -= rhs.1;
203        self.2 -= rhs.2;
204    }
205}
206
207impl<T: CoordsFloat> std::ops::Sub<Vertex3<T>> for Vertex3<T> {
208    type Output = Vector3<T>;
209    fn sub(self, rhs: Vertex3<T>) -> Self::Output {
210        Vector3(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
211    }
212}
213
214impl<T: CoordsFloat> AttributeUpdate for Vertex3<T> {
215    fn merge(attr1: Self, attr2: Self) -> Result<Self, AttributeError> {
216        Ok(Self::average(&attr1, &attr2))
217    }
218
219    fn split(attr: Self) -> Result<(Self, Self), AttributeError> {
220        Ok((attr, attr))
221    }
222
223    fn merge_incomplete(attr: Self) -> Result<Self, AttributeError> {
224        Ok(attr)
225    }
226}
227
228impl<T: CoordsFloat> AttributeBind for Vertex3<T> {
229    type StorageType = AttrSparseVec<Self>;
230    type IdentifierType = VertexIdType;
231    const BIND_POLICY: OrbitPolicy = OrbitPolicy::Vertex;
232}