honeycomb_kernels/cell_insertion/
vertices.rs

1//! Vertex isnertion routines
2
3use honeycomb_core::{
4    attributes::AttributeError,
5    cmap::{CMap2, DartIdType, EdgeIdType, LinkError, NULL_DART_ID, SewError},
6    geometry::{CoordsFloat, Vertex2},
7    stm::{Transaction, TransactionClosureResult, abort, try_or_coerce},
8};
9
10// -- error type
11
12/// Error-modeling enum for vertex insertion routines.
13#[derive(thiserror::Error, Debug, PartialEq, Eq)]
14pub enum VertexInsertionError {
15    /// A core operation failed.
16    #[error("core operation failed: {0}")]
17    FailedCoreOp(#[from] SewError),
18    /// Relative position of the new vertex isn't located on the edge.
19    #[error("vertex placement for split is not in ]0;1[")]
20    VertexBound,
21    /// One or both vertices of the edge are undefined.
22    #[error("edge isn't defined correctly")]
23    UndefinedEdge,
24    /// Darts passed to the function do not match requirements.
25    #[error("passed darts should be free & non-null - {0}")]
26    InvalidDarts(&'static str),
27    /// The number of darts passed to create the new segments is too low. The `usize` value
28    /// is the number of missing darts.
29    #[error("wrong # of darts - expected `{0}`, got {1}")]
30    WrongAmountDarts(usize, usize),
31}
32
33impl From<LinkError> for VertexInsertionError {
34    fn from(value: LinkError) -> Self {
35        Self::FailedCoreOp(value.into())
36    }
37}
38
39impl From<AttributeError> for VertexInsertionError {
40    fn from(value: AttributeError) -> Self {
41        Self::FailedCoreOp(value.into())
42    }
43}
44
45// -- routines
46
47/// Insert a vertex in an edge, cutting it into two segments.
48///
49/// <div class="warning">
50/// This implementation is 2D specific.
51/// </div>
52///
53/// # Arguments
54///
55/// - `cmap: &mut CMap2<T>` -- Reference to the modified map.
56/// - `t: &mut Transaction` -- Associated transaction.
57/// - `edge_id: EdgeIdentifier` -- Target edge.
58/// - `new_darts: (DartIdentifier, DartIdentifier)` -- Dart IDs used to build the new vertex/segments.
59/// - `midpoint_vertex: Option<T>` -- Relative position of the new vertex, starting from the
60///   vertex of the dart sharing `edge_id` as its identifier.
61///
62/// ## Dart IDs Requirements & Usage
63///
64/// Because of the dimension, the number of dart needed to perform this operation is at most
65/// two. These are the requirements for these two darts:
66/// - identifiers are passed as a tuple.
67/// - the first dart of the tuple will always be used if the operation is successful.
68/// - the second dart of the tuple will only be used if the original edge is made of two darts;
69///   if that is not the case, the second dart ID can be `NULL_DART_ID`.
70/// - both of these darts should be free
71///
72/// # Errors
73///
74/// This function will abort and raise an error if:
75/// - the transaction cannot be completed,
76/// - a hypothesis over input isn't verified (see [`VertexInsertionError`]),
77/// - an internal operation failed.
78///
79/// The returned error can be used in conjunction with transaction control to avoid any
80/// modifications in case of failure at attribute level. The user can then choose to retry or
81/// abort as he wishes using `Transaction::with_control_and_err`.
82#[allow(clippy::too_many_lines)]
83pub fn insert_vertex_on_edge<T: CoordsFloat>(
84    cmap: &CMap2<T>,
85    t: &mut Transaction,
86    edge_id: EdgeIdType,
87    new_darts: (DartIdType, DartIdType), // 2D => statically known number of darts
88    midpoint_vertex: Option<T>,
89) -> TransactionClosureResult<(), VertexInsertionError> {
90    // midpoint check
91    if midpoint_vertex.is_some_and(|p| (p >= T::one()) | (p <= T::zero())) {
92        abort(VertexInsertionError::VertexBound)?;
93    }
94
95    // base darts making up the edge
96    let base_dart1 = edge_id as DartIdType;
97    let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
98
99    if new_darts.0 == NULL_DART_ID || !cmap.is_free_tx(t, new_darts.0)? {
100        abort(VertexInsertionError::InvalidDarts(
101            "first dart is null or not free",
102        ))?;
103    }
104    if base_dart2 != NULL_DART_ID
105        && (new_darts.1 == NULL_DART_ID || !cmap.is_free_tx(t, new_darts.1)?)
106    {
107        abort(VertexInsertionError::InvalidDarts(
108            "second dart is null or not free",
109        ))?;
110    }
111
112    // base darts making up the edge
113    let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
114    if base_dart2 == NULL_DART_ID {
115        let b1d1_old = cmap.beta_tx::<1>(t, base_dart1)?;
116        let b1d1_new = new_darts.0;
117        let (vid1, vid2) = (
118            cmap.vertex_id_tx(t, base_dart1)?,
119            cmap.vertex_id_tx(t, b1d1_old)?,
120        );
121        let (Some(v1), Some(v2)) = (cmap.read_vertex(t, vid1)?, cmap.read_vertex(t, vid2)?) else {
122            abort(VertexInsertionError::UndefinedEdge)?
123        };
124        // unsew current dart
125        if b1d1_old != NULL_DART_ID {
126            try_or_coerce!(cmap.unlink::<1>(t, base_dart1), VertexInsertionError);
127        }
128        // rebuild the edge
129        try_or_coerce!(
130            cmap.link::<1>(t, base_dart1, b1d1_new),
131            VertexInsertionError
132        );
133        try_or_coerce!(cmap.link::<1>(t, b1d1_new, b1d1_old), VertexInsertionError);
134        // insert the new vertex
135        let seg = v2 - v1;
136        let vnew = cmap.vertex_id_tx(t, b1d1_new)?;
137        cmap.write_vertex(
138            t,
139            vnew,
140            midpoint_vertex.map_or(Vertex2::average(&v1, &v2), |t| v1 + seg * t),
141        )?;
142        Ok(())
143    } else {
144        let b1d1_old = cmap.beta_tx::<1>(t, base_dart1)?;
145        let b1d2_old = cmap.beta_tx::<1>(t, base_dart2)?;
146        let (b1d1_new, b1d2_new) = new_darts;
147        let (vid1, vid2) = (
148            cmap.vertex_id_tx(t, base_dart1)?,
149            cmap.vertex_id_tx(t, base_dart2)?,
150        );
151        let (Some(v1), Some(v2)) = (cmap.read_vertex(t, vid1)?, cmap.read_vertex(t, vid2)?) else {
152            abort(VertexInsertionError::UndefinedEdge)?
153        };
154        // unsew current darts
155        if b1d1_old != NULL_DART_ID {
156            try_or_coerce!(cmap.unlink::<1>(t, base_dart1), VertexInsertionError);
157        }
158        if b1d2_old != NULL_DART_ID {
159            try_or_coerce!(cmap.unlink::<1>(t, base_dart2), VertexInsertionError);
160        }
161        // cmap.set_beta::<1>(base_dart1, 0);
162        // cmap.set_beta::<0>(b1d1_old, 0);
163        // cmap.set_beta::<1>(base_dart2, 0);
164        // cmap.set_beta::<0>(b1d2_old, 0);
165        try_or_coerce!(cmap.unlink::<2>(t, base_dart1), VertexInsertionError);
166        // rebuild the edge
167        try_or_coerce!(
168            cmap.link::<1>(t, base_dart1, b1d1_new),
169            VertexInsertionError
170        );
171        if b1d1_old != NULL_DART_ID {
172            try_or_coerce!(cmap.link::<1>(t, b1d1_new, b1d1_old), VertexInsertionError);
173        }
174        try_or_coerce!(
175            cmap.link::<1>(t, base_dart2, b1d2_new),
176            VertexInsertionError
177        );
178        if b1d2_old != NULL_DART_ID {
179            try_or_coerce!(cmap.link::<1>(t, b1d2_new, b1d2_old), VertexInsertionError);
180        }
181        try_or_coerce!(
182            cmap.link::<2>(t, base_dart1, b1d2_new),
183            VertexInsertionError
184        );
185        try_or_coerce!(
186            cmap.link::<2>(t, base_dart2, b1d1_new),
187            VertexInsertionError
188        );
189        // insert the new vertex
190        let seg = v2 - v1;
191        let vnew = cmap.vertex_id_tx(t, b1d1_new)?;
192        cmap.write_vertex(
193            t,
194            vnew,
195            midpoint_vertex.map_or(Vertex2::average(&v1, &v2), |t| v1 + seg * t),
196        )?;
197        Ok(())
198    }
199}
200
201#[allow(clippy::missing_errors_doc)]
202/// Insert `n` vertices in an edge, cutting it into `n+1` segments.
203///
204/// <div class="warning">
205/// This implementation is 2D specific.
206/// </div>
207///
208/// # Arguments
209///
210/// - `cmap: &mut CMap2<T>` -- Reference to the modified map.
211/// - `t: &mut Transaction` -- Associated transaction.
212/// - `edge_id: EdgeIdentifier` -- Target edge.
213/// - `new_darts: &[DartIdentifier]` -- Dart IDs used to build the new vertices/segments.
214/// - `midpoint_vertices: &[T]` -- Relative positions of new vertices, starting from the
215///   vertex of the dart sharing `edge_id` as its identifier.
216///
217/// ## Dart IDs Requirements & Usage
218///
219/// Because of the dimension, we can easily compute the number of dart needed to perform this
220/// operation. These are the requirements for the darts:
221/// - identifiers are passed as a slice:
222///   - slice length should verify `new_darts.len() == 2 * midpoint_vertices.len()`
223/// - the first half of the slice will always be used if the operation is successful.
224/// - the second half of the slice will only be used if the original edge is made of two darts;
225///   if that is not the case, the second half IDs can all be `NULL_DART_ID`s.
226/// - all of these darts should be free
227///
228/// # Errors
229///
230/// This function will abort and raise an error if:
231/// - the transaction cannot be completed,
232/// - a hypothesis over input isn't verified (see [`VertexInsertionError`]),
233/// - an internal operation failed.
234///
235/// The returned error can be used in conjunction with transaction control to avoid any
236/// modifications in case of failure at attribute level. The user can then choose to retry or
237/// abort as he wishes using `Transaction::with_control_and_err`.
238///
239/// # Example
240///
241/// ```
242/// # use honeycomb_core::cmap::{CMap2, CMapBuilder, NULL_DART_ID};
243/// # use honeycomb_core::geometry::Vertex2;
244/// # use honeycomb_core::stm::atomically_with_err;
245/// # use honeycomb_kernels::cell_insertion::insert_vertices_on_edge;
246/// // before
247/// //    <--2---
248/// //  1         2
249/// //    ---1-->
250///
251/// let mut map: CMap2<_> = CMapBuilder::<2, _>::from_n_darts(2)
252///                             .build()
253///                             .unwrap();
254/// map.force_link::<2>(1, 2);
255/// map.force_write_vertex(1, (0.0, 0.0));
256/// map.force_write_vertex(2, (1.0, 0.0));
257///
258/// let nd = map.allocate_unused_darts(6);
259///
260/// // split
261/// assert!(
262///     atomically_with_err(|t| insert_vertices_on_edge(
263///         &map,
264///         t,
265///         1,
266///         &[nd, nd + 1, nd + 2, nd + 3, nd + 4, nd + 5],
267///         &[0.25, 0.50, 0.75],
268///     )).is_ok()
269/// );
270///
271/// // after
272/// //    <-<-<-<
273/// //  1 -3-4-5- 2
274/// //    >->->->
275///
276/// // checks
277/// let new_darts = [
278///     map.beta::<1>(1),
279///     map.beta::<1>(map.beta::<1>(1)),
280///     map.beta::<1>(map.beta::<1>(map.beta::<1>(1))),
281/// ];
282/// assert_eq!(&new_darts, &[3, 4, 5]);
283/// assert_eq!(map.force_read_vertex(3), Some(Vertex2(0.25, 0.0)));
284/// assert_eq!(map.force_read_vertex(4), Some(Vertex2(0.50, 0.0)));
285/// assert_eq!(map.force_read_vertex(5), Some(Vertex2(0.75, 0.0)));
286///
287/// assert_eq!(map.beta::<1>(1), 3);
288/// assert_eq!(map.beta::<1>(3), 4);
289/// assert_eq!(map.beta::<1>(4), 5);
290/// assert_eq!(map.beta::<1>(5), NULL_DART_ID);
291///
292/// assert_eq!(map.beta::<1>(2), 6);
293/// assert_eq!(map.beta::<1>(6), 7);
294/// assert_eq!(map.beta::<1>(7), 8);
295/// assert_eq!(map.beta::<1>(8), NULL_DART_ID);
296///
297/// assert_eq!(map.beta::<2>(1), 8);
298/// assert_eq!(map.beta::<2>(3), 7);
299/// assert_eq!(map.beta::<2>(4), 6);
300/// assert_eq!(map.beta::<2>(5), 2);
301/// ```
302pub fn insert_vertices_on_edge<T: CoordsFloat>(
303    cmap: &CMap2<T>,
304    t: &mut Transaction,
305    edge_id: EdgeIdType,
306    new_darts: &[DartIdType],
307    midpoint_vertices: &[T],
308) -> TransactionClosureResult<(), VertexInsertionError> {
309    // check pre-allocated darts reqs
310    let n_t = midpoint_vertices.len();
311    let n_d = new_darts.len();
312    if n_d != 2 * n_t {
313        abort(VertexInsertionError::WrongAmountDarts(2 * n_t, n_d))?;
314    }
315    for d in new_darts {
316        if !cmap.is_free_tx(t, *d)? {
317            abort(VertexInsertionError::InvalidDarts("one dart is not free"))?;
318        }
319    }
320    // get the first and second halves
321    let darts_fh = &new_darts[..n_t];
322    let darts_sh = &new_darts[n_t..];
323
324    // base darts making up the edge
325    let base_dart1 = edge_id as DartIdType;
326    let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
327
328    if darts_fh.contains(&NULL_DART_ID) {
329        abort(VertexInsertionError::InvalidDarts(
330            "one dart of the first half is null",
331        ))?;
332    }
333    if base_dart2 != NULL_DART_ID && darts_sh.contains(&NULL_DART_ID) {
334        abort(VertexInsertionError::InvalidDarts(
335            "one dart of the second half is null",
336        ))?;
337    }
338
339    if midpoint_vertices
340        .iter()
341        .any(|p| (*p >= T::one()) | (*p <= T::zero()))
342    {
343        abort(VertexInsertionError::VertexBound)?;
344    }
345
346    let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
347    let b1d1_old = cmap.beta_tx::<1>(t, base_dart1)?;
348
349    let (vid1, vid2) = (
350        cmap.vertex_id_tx(t, base_dart1)?,
351        cmap.vertex_id_tx(
352            t,
353            if b1d1_old != NULL_DART_ID {
354                b1d1_old
355            } else if base_dart2 != NULL_DART_ID {
356                base_dart2
357            } else {
358                abort(VertexInsertionError::UndefinedEdge)?
359            },
360        )?,
361    );
362    let (Some(v1), Some(v2)) = (cmap.read_vertex(t, vid1)?, cmap.read_vertex(t, vid2)?) else {
363        abort(VertexInsertionError::UndefinedEdge)?
364    };
365    let seg = v2 - v1;
366
367    // unsew current dart
368    if b1d1_old != NULL_DART_ID {
369        try_or_coerce!(cmap.unlink::<1>(t, base_dart1), VertexInsertionError);
370    }
371    //
372    if base_dart2 != NULL_DART_ID {
373        try_or_coerce!(cmap.unlink::<2>(t, base_dart1), VertexInsertionError);
374    }
375    // insert new vertices / darts on base_dart1's side
376    let mut prev_d = base_dart1;
377    for (&p, &new_d) in midpoint_vertices.iter().zip(darts_fh.iter()) {
378        let new_v = v1 + seg * p;
379        try_or_coerce!(cmap.link::<1>(t, prev_d, new_d), VertexInsertionError);
380        cmap.write_vertex(t, new_d, new_v)?;
381        prev_d = new_d;
382    }
383    try_or_coerce!(cmap.link::<1>(t, prev_d, b1d1_old), VertexInsertionError);
384
385    // if b2(base_dart1) is defined, insert vertices / darts on its side too
386    if base_dart2 != NULL_DART_ID {
387        let b1d2_old = cmap.beta_tx::<1>(t, base_dart2)?;
388        if b1d2_old != NULL_DART_ID {
389            try_or_coerce!(cmap.unlink::<1>(t, base_dart2), VertexInsertionError);
390        }
391        let mut prev_d = base_dart2;
392        for (d, new_d) in darts_fh.iter().rev().zip(darts_sh.iter()) {
393            try_or_coerce!(cmap.link::<2>(t, prev_d, *d), VertexInsertionError);
394            try_or_coerce!(cmap.link::<1>(t, prev_d, *new_d), VertexInsertionError);
395            prev_d = *new_d;
396        }
397        if b1d2_old != NULL_DART_ID {
398            try_or_coerce!(cmap.link::<1>(t, prev_d, b1d2_old), VertexInsertionError);
399        }
400        try_or_coerce!(cmap.link::<2>(t, prev_d, base_dart1), VertexInsertionError);
401    }
402
403    Ok(())
404}