honeycomb_core/cmap/dim2/structure.rs
1//! Main definitions
2//!
3//! This module contains the main structure definition ([`CMap2`]) as well as its constructor
4//! implementation.
5
6use crate::attributes::{AttrSparseVec, AttrStorageManager, UnknownAttributeStorage};
7use crate::cmap::components::{betas::BetaFunctions, unused::UnusedDarts};
8use crate::geometry::{CoordsFloat, Vertex2};
9
10use super::CMAP2_BETA;
11
12/// # 2D combinatorial map implementation
13///
14/// Information regarding maps can be found in the [user guide][UG].
15/// This documentation focuses on the implementation and its API.
16///
17/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/user-guide/definitions/cmaps
18///
19/// Notes on implementation:
20/// - We encode *β<sub>0</sub>* as the inverse function of *β<sub>1</sub>*. This is extremely
21/// useful (read *required*) to implement correct and efficient i-cell computation. Additionally,
22/// while *β<sub>0</sub>* can be accessed using the [`beta`][Self::beta] method, we do not define
23/// the 0-sew / 0-unsew operations.
24/// - We chose a boundary-less representation of meshes (i.e. darts on the boundary are 2-free).
25/// - The null dart will always be encoded as `0`.
26///
27/// ## Generics
28///
29/// - `T: CoordsFloat` -- Generic FP type for coordinates representation
30///
31/// ## Example
32///
33/// The following code corresponds to this flow of operations:
34///
35/// 
36///
37/// Note that:
38/// - we create the map using its builder structure: [`CMapBuilder`][crate::prelude::CMapBuilder]
39/// - we insert a few assertions to demonstrate the progressive changes applied to the structure
40/// - even though the faces are represented in the figure, they are not stored in the structure
41/// - we use a lot of methods with the `force_` prefix; these are convenience methods when
42/// synchronization isn't needed
43///
44/// ```
45/// # fn main() {
46/// use honeycomb_core::{
47/// cmap::{CMap2, CMapBuilder, Orbit2, OrbitPolicy},
48/// geometry::Vertex2
49/// };
50///
51/// // build a triangle (A)
52/// let mut map: CMap2<f64> = CMapBuilder::default().n_darts(3).build().unwrap(); // three darts
53/// map.force_link::<1>(1, 2); // beta1(1) = 2 & beta0(2) = 1
54/// map.force_link::<1>(2, 3); // beta1(2) = 3 & beta0(3) = 2
55/// map.force_link::<1>(3, 1); // beta1(3) = 1 & beta0(1) = 3
56/// map.force_write_vertex(1, (0.0, 0.0));
57/// map.force_write_vertex(2, (1.0, 0.0));
58/// map.force_write_vertex(3, (0.0, 1.0));
59///
60/// // we can go through the face using an orbit
61/// let mut face = Orbit2::new(&map, OrbitPolicy::Face, 1);
62/// assert_eq!(face.next(), Some(1));
63/// assert_eq!(face.next(), Some(2));
64/// assert_eq!(face.next(), Some(3));
65/// assert_eq!(face.next(), None);
66///
67/// // build a second triangle (B)
68/// let first_added_dart_id = map.add_free_darts(3);
69/// assert_eq!(first_added_dart_id, 4);
70/// map.force_link::<1>(4, 5);
71/// map.force_link::<1>(5, 6);
72/// map.force_link::<1>(6, 4);
73/// map.force_write_vertex(4, (0.0, 2.0));
74/// map.force_write_vertex(5, (2.0, 0.0));
75/// map.force_write_vertex(6, (1.0, 1.0));
76///
77/// // there should be two faces now
78/// let faces: Vec<_> = map.iter_faces().collect();
79/// assert_eq!(&faces, &[1, 4]);
80///
81/// // sew both triangles (C)
82/// map.force_sew::<2>(2, 4);
83///
84/// // there are 5 edges now, making up a square & its diagonal
85/// let edges: Vec<_> = map.iter_edges().collect();
86/// assert_eq!(&edges, &[1, 2, 3, 5, 6]);
87///
88/// // adjust bottom-right & top-left vertex position (D)
89/// assert_eq!(
90/// map.force_write_vertex(2, Vertex2::from((1.0, 0.0))),
91/// Some(Vertex2(1.5, 0.0)) // `write` act as a `replace`
92/// );
93/// assert_eq!(
94/// map.force_write_vertex(3, Vertex2::from((0.0, 1.0))),
95/// Some(Vertex2(0.0, 1.5)) // these values were the average of sewn vertices
96/// );
97///
98/// // separate the diagonal from the rest (E)
99/// map.force_unsew::<1>(1);
100/// map.force_unsew::<1>(2);
101/// map.force_unsew::<1>(6);
102/// map.force_unsew::<1>(4);
103/// // break up & remove the diagonal
104/// map.force_unsew::<2>(2); // this makes dart 2 and 4 free
105/// map.remove_free_dart(2);
106/// map.remove_free_dart(4);
107/// // sew the square back up
108/// map.force_sew::<1>(1, 5);
109/// map.force_sew::<1>(6, 3);
110///
111/// // there's only the square face left
112/// let faces: Vec<_> = map.iter_faces().collect();
113/// assert_eq!(&faces, &[1]);
114/// // we can check the vertices
115/// let vertices = map.iter_vertices();
116/// let mut value_iterator = vertices.map(|vertex_id| map.force_read_vertex(vertex_id).unwrap());
117/// assert_eq!(value_iterator.next(), Some(Vertex2::from((0.0, 0.0)))); // vertex ID 1
118/// assert_eq!(value_iterator.next(), Some(Vertex2::from((0.0, 1.0)))); // vertex ID 3
119/// assert_eq!(value_iterator.next(), Some(Vertex2::from((1.0, 0.0)))); // vertex ID 5
120/// assert_eq!(value_iterator.next(), Some(Vertex2::from((1.0, 1.0)))); // vertex ID 6
121/// # }
122/// ```
123pub struct CMap2<T: CoordsFloat> {
124 /// List of vertices making up the represented mesh
125 pub(super) attributes: AttrStorageManager,
126 /// List of vertices making up the represented mesh
127 pub(super) vertices: AttrSparseVec<Vertex2<T>>,
128 /// List of free darts identifiers, i.e. empty spots
129 /// in the current dart list
130 pub(super) unused_darts: UnusedDarts,
131 /// Array representation of the beta functions
132 pub(super) betas: BetaFunctions<CMAP2_BETA>,
133 /// Current number of darts
134 pub(super) n_darts: usize,
135}
136
137unsafe impl<T: CoordsFloat> Send for CMap2<T> {}
138unsafe impl<T: CoordsFloat> Sync for CMap2<T> {}
139
140#[doc(hidden)]
141/// **Constructor convenience implementations**
142impl<T: CoordsFloat> CMap2<T> {
143 /// Creates a new 2D combinatorial map.
144 #[allow(unused)]
145 #[must_use = "unused return value"]
146 pub(crate) fn new(n_darts: usize) -> Self {
147 Self {
148 attributes: AttrStorageManager::default(),
149 vertices: AttrSparseVec::new(n_darts + 1),
150 unused_darts: UnusedDarts::new(n_darts + 1),
151 betas: BetaFunctions::new(n_darts + 1),
152 n_darts: n_darts + 1,
153 }
154 }
155
156 /// Creates a new 2D combinatorial map with user-defined attributes
157 ///
158 /// We expect the passed storages to be defined but empty, i.e. attributes are known,
159 /// but no space has been used/ allocated yet.
160 #[must_use = "unused return value"]
161 pub(crate) fn new_with_undefined_attributes(
162 n_darts: usize,
163 mut attr_storage_manager: AttrStorageManager,
164 ) -> Self {
165 // extend all storages to the expected length: n_darts + 1 (for the null dart)
166 attr_storage_manager.extend_storages(n_darts + 1);
167 Self {
168 attributes: attr_storage_manager,
169 vertices: AttrSparseVec::new(n_darts + 1),
170 unused_darts: UnusedDarts::new(n_darts + 1),
171 betas: BetaFunctions::new(n_darts + 1),
172 n_darts: n_darts + 1,
173 }
174 }
175}