honeycomb_core/cmap/dim2/structure.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
//! Main definitions
//!
//! This module contains the main structure definition ([`CMap2`]) as well as its constructor
//! implementation.
// ------ IMPORTS
use super::CMAP2_BETA;
use crate::cmap::components::betas::BetaFunctions;
use crate::cmap::components::unused::UnusedDarts;
use crate::prelude::Vertex2;
use crate::{
attributes::{AttrSparseVec, AttrStorageManager, UnknownAttributeStorage},
geometry::CoordsFloat,
};
// ------ CONTENT
/// Main map object.
///
/// Structure used to model 2D combinatorial map. The structure implements basic operations as
/// well as higher level abstractions that are useful to write meshing applications.
///
/// Definition of the structure and its logic can be found in the [user guide][UG].
/// This documentation focuses on the implementation and its API.
///
/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/definitions/cmaps
///
/// <div class="warning">
///
/// **This structure only implements `Clone` if the `utils` feature is enabled.**
///
/// </div>
///
/// # Fields
///
/// Fields are kept private in order to better define interfaces. The structure
/// contains the following data:
///
/// - an array-based representation of the beta functions
/// - a storage structure for vertices making up the represented mesh
/// - a generic storage manager for user-defined attributes
///
/// Note that:
/// - we encode *β<sub>0</sub>* as the inverse function of *β<sub>1</sub>*. This is extremely
/// useful (read *required*) to implement correct and efficient i-cell computation. Additionally,
/// while *β<sub>0</sub>* can be accessed using the [`Self::beta`] method, we do not define
/// the 0-sew / 0-unsew operations.
/// - we chose a boundary-less representation of meshes (i.e. darts on the boundary are 2-free).
///
/// # Generics
///
/// - `T: CoordsFloat` -- Generic type for coordinates representation.
///
/// # Example
///
/// The following example goes over multiple operations on the mesh in order
/// to demonstrate general usage of the structure and its methods.
///
/// ![`CMAP2_EXAMPLE`](https://lihpc-computational-geometry.github.io/honeycomb/images/bg_hcmap_example.svg)
///
/// Note that:
/// - we create the map using its builder structure: [`CMapBuilder`][crate::prelude::CMapBuilder].
/// - the map we operate on has no boundaries. In addition to the different
/// operations realized at each step, we insert a few assertions to demonstrate the
/// progressive changes applied to the structure.
///
/// ```
/// # fn main() {
///
/// use honeycomb_core::prelude::{CMap2, CMapBuilder, Orbit2, OrbitPolicy, Vertex2};
///
/// // build a triangle
/// let mut map: CMap2<f64> = CMapBuilder::default().n_darts(3).build().unwrap(); // three darts
/// map.force_one_link(1, 2); // beta1(1) = 2 & beta0(2) = 1
/// map.force_one_link(2, 3); // beta1(2) = 3 & beta0(3) = 2
/// map.force_one_link(3, 1); // beta1(3) = 1 & beta0(1) = 3
/// map.force_write_vertex(1, (0.0, 0.0));
/// map.force_write_vertex(2, (1.0, 0.0));
/// map.force_write_vertex(3, (0.0, 1.0));
///
/// // we can go through the face using an orbit
/// let mut face = Orbit2::new(&map, OrbitPolicy::Face, 1);
/// assert_eq!(face.next(), Some(1));
/// assert_eq!(face.next(), Some(2));
/// assert_eq!(face.next(), Some(3));
/// assert_eq!(face.next(), None);
///
/// // build a second triangle
/// let first_added_dart_id = map.add_free_darts(3);
/// assert_eq!(first_added_dart_id, 4);
/// map.force_one_link(4, 5);
/// map.force_one_link(5, 6);
/// map.force_one_link(6, 4);
/// map.force_write_vertex(4, (0.0, 2.0));
/// map.force_write_vertex(5, (2.0, 0.0));
/// map.force_write_vertex(6, (1.0, 1.0));
///
/// // there should be two faces now
/// let faces = map.fetch_faces();
/// assert_eq!(&faces.identifiers, &[1, 4]);
///
/// // sew both triangles
/// map.force_two_sew(2, 4);
///
/// // there are 5 edges now, making up a square & its diagonal
/// let edges = map.fetch_edges();
/// assert_eq!(&edges.identifiers, &[1, 2, 3, 5, 6]);
///
/// // adjust bottom-right & top-left vertex position
/// // the returned values were the average of the sewn vertices
/// assert_eq!(
/// map.force_write_vertex(2, Vertex2::from((1.0, 0.0))),
/// Some(Vertex2(1.5, 0.0))
/// );
/// assert_eq!(
/// map.force_write_vertex(3, Vertex2::from((0.0, 1.0))),
/// Some(Vertex2(0.0, 1.5))
/// );
///
/// // separate the diagonal from the rest
/// map.force_one_unsew(1);
/// map.force_one_unsew(2);
/// map.force_one_unsew(6);
/// map.force_one_unsew(4);
/// // break up & remove the diagonal
/// map.force_two_unsew(2); // this makes dart 2 and 4 free
/// map.remove_free_dart(2);
/// map.remove_free_dart(4);
/// // sew the square back up
/// map.force_one_sew(1, 5);
/// map.force_one_sew(6, 3);
///
/// // there's only the square face left
/// let faces = map.fetch_faces();
/// assert_eq!(&faces.identifiers, &[1]);
/// // we can check the vertices
/// let vertices = map.fetch_vertices();
/// let mut value_iterator = vertices.identifiers.iter().map(|vertex_id| map.force_read_vertex(*vertex_id).unwrap());
/// assert_eq!(value_iterator.next(), Some(Vertex2::from((0.0, 0.0)))); // vertex ID 1
/// assert_eq!(value_iterator.next(), Some(Vertex2::from((0.0, 1.0)))); // vertex ID 3
/// assert_eq!(value_iterator.next(), Some(Vertex2::from((1.0, 0.0)))); // vertex ID 5
/// assert_eq!(value_iterator.next(), Some(Vertex2::from((1.0, 1.0)))); // vertex ID 6
///
/// # }
/// ```
pub struct CMap2<T: CoordsFloat> {
/// List of vertices making up the represented mesh
pub(super) attributes: AttrStorageManager,
/// List of vertices making up the represented mesh
pub(super) vertices: AttrSparseVec<Vertex2<T>>,
/// List of free darts identifiers, i.e. empty spots
/// in the current dart list
pub(super) unused_darts: UnusedDarts,
/// Array representation of the beta functions
pub(super) betas: BetaFunctions<CMAP2_BETA>,
/// Current number of darts
pub(super) n_darts: usize,
}
unsafe impl<T: CoordsFloat> Send for CMap2<T> {}
unsafe impl<T: CoordsFloat> Sync for CMap2<T> {}
#[doc(hidden)]
/// **Constructor convenience implementations**
impl<T: CoordsFloat> CMap2<T> {
/// Creates a new 2D combinatorial map.
///
/// # Arguments
///
/// - `n_darts: usize` -- Number of darts composing the new combinatorial map.
///
/// # Return
///
/// Returns a combinatorial map containing `n_darts + 1` darts, the amount of darts wanted plus
/// the null dart (at index `NULL_DART_ID` i.e. `0`).
///
/// # Example
///
/// See [`CMap2`] example.
#[allow(unused)]
#[must_use = "constructed object is not used, consider removing this function call"]
pub(crate) fn new(n_darts: usize) -> Self {
Self {
attributes: AttrStorageManager::default(),
vertices: AttrSparseVec::new(n_darts + 1),
unused_darts: UnusedDarts::new(n_darts + 1),
betas: BetaFunctions::new(n_darts + 1),
n_darts: n_darts + 1,
}
}
/// Creates a new 2D combinatorial map with user-defined attributes
///
/// # Arguments
///
/// - `n_darts: usize` -- Number of darts composing the new combinatorial map.
/// - `attr_storage_manager: AttrStorageManager` -- Manager structure holding
/// the user-defined attributes. The containers held by the manager should
/// all be empty.
///
/// # Return
///
/// Returns a combinatorial map containing `n_darts + 1` darts, the amount of darts wanted plus
/// the null dart (at index `NULL_DART_ID` i.e. `0`).
///
/// # Example
///
/// See [`CMap2`] example.
#[must_use = "constructed object is not used, consider removing this function call"]
pub(crate) fn new_with_undefined_attributes(
n_darts: usize,
mut attr_storage_manager: AttrStorageManager,
) -> Self {
// extend all storages to the expected length: n_darts + 1 (for the null dart)
// the passed manager should be containing defined, empty storage i.e. attributes
// are known, but no space has been used/allocated yet
attr_storage_manager.extend_storages(n_darts + 1);
Self {
attributes: attr_storage_manager,
vertices: AttrSparseVec::new(n_darts + 1),
unused_darts: UnusedDarts::new(n_darts + 1),
betas: BetaFunctions::new(n_darts + 1),
n_darts: n_darts + 1,
}
}
}