honeycomb_core/cmap/dim3/
structure.rs

1//! Main definitions
2//!
3//! This module contains the main structure definition ([`CMap3`]) as well as its constructor
4//! implementation.
5
6use crate::{
7    attributes::{AttrSparseVec, AttrStorageManager, UnknownAttributeStorage},
8    cmap::{
9        DartIdType, DartReleaseError, DartReservationError,
10        components::{betas::BetaFunctions, unused::UnusedDarts},
11    },
12    geometry::{CoordsFloat, Vertex3},
13    stm::{StmClosureResult, Transaction, TransactionClosureResult, abort, atomically_with_err},
14};
15
16use super::CMAP3_BETA;
17
18/// Main map object.
19pub struct CMap3<T: CoordsFloat> {
20    /// List of vertices making up the represented mesh
21    pub(super) attributes: AttrStorageManager,
22    /// List of vertices making up the represented mesh
23    pub(super) vertices: AttrSparseVec<Vertex3<T>>,
24    /// List of free darts identifiers, i.e. empty spots
25    /// in the current dart list
26    pub(super) unused_darts: UnusedDarts,
27    /// Array representation of the beta functions
28    pub(super) betas: BetaFunctions<CMAP3_BETA>,
29}
30
31unsafe impl<T: CoordsFloat> Send for CMap3<T> {}
32unsafe impl<T: CoordsFloat> Sync for CMap3<T> {}
33#[doc(hidden)]
34/// # 3D combinatorial map implementation
35///
36/// Information regarding maps can be found in the [user guide][UG].
37/// This documentation focuses on the implementation and its API.
38///
39/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/user-guide/definitions/cmaps
40///
41/// Notes on implementation:
42/// - We encode *β<sub>0</sub>* as the inverse function of *β<sub>1</sub>*. This is extremely
43///   useful (read *required*) to implement correct and efficient i-cell computation. Additionally,
44///   while *β<sub>0</sub>* can be accessed using the [`beta`][Self::beta] method, we do not define
45///   the 0-sew / 0-unsew operations.
46/// - We chose a boundary-less representation of meshes (i.e. darts on the boundary are 3-free).
47/// - The null dart will always be encoded as `0`.
48///
49/// ## Generics
50///
51/// - `T: CoordsFloat` -- Generic FP type for coordinates representation
52///
53/// ## Example
54///
55/// The following example corresponds to this flow of operations:
56///
57/// - Building a tetrahedron (A)
58/// - Building another tetrahedron (B)
59/// - Sewing both tetrahedrons along a face (C)
60/// - Adjusting shared vertices (D)
61/// - Separating and removing the shared face (E)
62///
63/// ```rust
64/// # fn main() {
65/// // TODO: complete with test example once the structure is integrated to the builder
66/// # }
67/// ```
68///
69/// Note that:
70/// - We use the builder structure: [`CMapBuilder`][crate::prelude::CMapBuilder]
71/// - We insert a few assertions to demonstrate the progressive changes applied to the structure
72/// - Even though volumes are represented in the figure, they are not stored in the structure
73/// - We use a lot of methods with the `force_` prefix; these are convenience methods when
74///   synchronization isn't needed
75impl<T: CoordsFloat> CMap3<T> {
76    /// Creates a new 3D combinatorial map.
77    #[allow(unused)]
78    #[must_use = "unused return value"]
79    pub(crate) fn new(n_darts: usize) -> Self {
80        Self {
81            attributes: AttrStorageManager::default(),
82            vertices: AttrSparseVec::new(n_darts + 1),
83            unused_darts: UnusedDarts::new(n_darts + 1),
84            betas: BetaFunctions::new(n_darts + 1),
85        }
86    }
87
88    /// Creates a new 3D combinatorial map with user-defined attributes
89    ///
90    /// We expect the passed storages to be defined but empty, i.e. attributes are known,
91    /// but no space has been used/ allocated yet.
92    #[must_use = "unused return value"]
93    pub(crate) fn new_with_undefined_attributes(
94        n_darts: usize,
95        mut attr_storage_manager: AttrStorageManager,
96    ) -> Self {
97        // extend all storages to the expected length: n_darts + 1 (for the null dart)
98        attr_storage_manager.extend_storages(n_darts + 1);
99        Self {
100            attributes: attr_storage_manager,
101            vertices: AttrSparseVec::new(n_darts + 1),
102            unused_darts: UnusedDarts::new(n_darts + 1),
103            betas: BetaFunctions::new(n_darts + 1),
104        }
105    }
106}
107
108/// **Dart-related methods**
109impl<T: CoordsFloat> CMap3<T> {
110    // --- read
111
112    /// Return the current number of darts.
113    #[must_use = "unused return value"]
114    pub fn n_darts(&self) -> usize {
115        self.unused_darts.len()
116    }
117
118    /// Return the current number of unused darts.
119    #[must_use = "unused return value"]
120    pub fn n_unused_darts(&self) -> usize {
121        self.unused_darts.iter().filter(|v| v.read_atomic()).count()
122    }
123
124    /// Return whether a given dart is unused or not.
125    ///
126    /// # Errors
127    ///
128    /// This method is meant to be called in a context where the returned `Result` is used to
129    /// validate the transaction passed as argument. Errors should not be processed manually,
130    /// only processed via the `?` operator.
131    #[must_use = "unused return value"]
132    pub fn is_unused_tx(&self, t: &mut Transaction, d: DartIdType) -> StmClosureResult<bool> {
133        self.unused_darts[d].read(t)
134    }
135
136    // --- edit
137
138    /// Add `n_darts` new free darts to the map.
139    fn allocate_darts_core(&mut self, n_darts: usize, unused: bool) -> DartIdType {
140        let new_id = self.n_darts() as DartIdType;
141        self.betas.extend(n_darts);
142        self.unused_darts.extend_with(n_darts, unused);
143        self.vertices.extend(n_darts);
144        self.attributes.extend_storages(n_darts);
145        new_id
146    }
147
148    /// Add `n_darts` new free darts to the map.
149    ///
150    /// Added darts are marked as used.
151    ///
152    /// # Return
153    ///
154    /// Return the ID of the first new dart. Other IDs are in the range `ID..ID+n_darts`.
155    pub fn allocate_used_darts(&mut self, n_darts: usize) -> DartIdType {
156        self.allocate_darts_core(n_darts, false)
157    }
158
159    /// Add `n_darts` new free darts to the map.
160    ///
161    /// Added dart are marked as unused.
162    ///
163    /// # Return
164    ///
165    /// Return the ID of the first new dart. Other IDs are in the range `ID..ID+n_darts`.
166    pub fn allocate_unused_darts(&mut self, n_darts: usize) -> DartIdType {
167        self.allocate_darts_core(n_darts, true)
168    }
169
170    // --- reservation / removal
171
172    #[allow(clippy::missing_errors_doc)]
173    /// Mark `n_darts` free darts as used and return them for usage.
174    ///
175    /// # Return / Errors
176    ///
177    /// This function returns a vector containing IDs of the darts marked as used. It will fail if
178    /// there are not enough unused darts to return; darts will then be left as unused.
179    pub fn reserve_darts(&self, n_darts: usize) -> Result<Vec<DartIdType>, DartReservationError> {
180        atomically_with_err(|t| self.reserve_darts_tx(t, n_darts))
181    }
182
183    #[allow(clippy::missing_errors_doc)]
184    /// Mark `n_darts` free darts as used and return them for usage.
185    ///
186    /// # Return / Errors
187    ///
188    /// This function returns a vector containing IDs of the darts marked as used. It will fail if
189    /// there are not enough unused darts to return; darts will then be left as unused.
190    ///
191    /// This method is meant to be called in a context where the returned `Result` is used to
192    /// validate the transaction passed as argument. Errors should not be processed manually,
193    /// only processed via the `?` operator.
194    pub fn reserve_darts_tx(
195        &self,
196        t: &mut Transaction,
197        n_darts: usize,
198    ) -> TransactionClosureResult<Vec<DartIdType>, DartReservationError> {
199        let mut res = Vec::with_capacity(n_darts);
200
201        for d in 1..self.n_darts() as DartIdType {
202            if self.is_unused_tx(t, d)? {
203                self.claim_dart_tx(t, d)?;
204                res.push(d);
205                if res.len() == n_darts {
206                    return Ok(res);
207                }
208            }
209        }
210
211        abort(DartReservationError(n_darts))
212    }
213
214    /// Set a given dart as used.
215    ///
216    /// # Errors
217    ///
218    /// This method is meant to be called in a context where the returned `Result` is used to
219    /// validate the transaction passed as argument. Errors should not be processed manually,
220    /// only processed via the `?` operator.
221    pub fn claim_dart_tx(&self, t: &mut Transaction, dart_id: DartIdType) -> StmClosureResult<()> {
222        self.unused_darts[dart_id].write(t, false)
223    }
224
225    #[allow(clippy::missing_errors_doc)]
226    /// Mark a free dart from the map as unused.
227    ///
228    /// # Return / Errors
229    ///
230    /// This method return a boolean indicating whether the art was already unused or not. It will
231    /// fail if the dart is not free, i.e. if one of its beta images isn't null.
232    pub fn release_dart(&mut self, dart_id: DartIdType) -> Result<bool, DartReleaseError> {
233        atomically_with_err(|t| self.release_dart_tx(t, dart_id))
234    }
235
236    #[allow(clippy::missing_errors_doc)]
237    /// Mark a free dart from the map as unused.
238    ///
239    /// # Return / Errors
240    ///
241    /// This method return a boolean indicating whether the art was already unused or not. It will
242    /// fail if the dart is not free, i.e. if one of its beta images isn't null.
243    ///
244    /// This method is meant to be called in a context where the returned `Result` is used to
245    /// validate the transaction passed as argument. Errors should not be processed manually,
246    /// only processed via the `?` operator.
247    pub fn release_dart_tx(
248        &self,
249        t: &mut Transaction,
250        dart_id: DartIdType,
251    ) -> TransactionClosureResult<bool, DartReleaseError> {
252        if !self.is_free_tx(t, dart_id)? {
253            abort(DartReleaseError(dart_id))?;
254        }
255        self.attributes.clear_attribute_values(t, dart_id)?;
256        self.vertices.clear_slot(t, dart_id)?;
257        Ok(self.unused_darts[dart_id].replace(t, true)?) // Ok(_?) necessary for err type coercion
258    }
259}