honeycomb_core/cmap/dim2/sews/one.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
//! 1D sew implementations
use stm::{atomically, StmResult, Transaction};
use crate::{
attributes::UnknownAttributeStorage,
cmap::{CMap2, CMapResult, DartIdType, NULL_DART_ID},
prelude::CoordsFloat,
};
/// 1-sews
impl<T: CoordsFloat> CMap2<T> {
/// 1-sew operation.
///
/// This operation corresponds to *coherently linking* two darts via the *β<sub>1</sub>*
/// function. For a thorough explanation of this operation (and implied hypothesis &
/// consequences), refer to the [user guide][UG].
///
/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/
///
/// # Arguments
///
/// - `lhs_dart_id: DartIdentifier` -- ID of the first dart to be linked.
/// - `rhs_dart_id: DartIdentifier` -- ID of the second dart to be linked.
/// - `policy: SewPolicy` -- Geometrical sewing policy to follow.
///
/// After the sewing operation, these darts will verify
/// *β<sub>1</sub>(`lhs_dart`) = `rhs_dart`*. The *β<sub>0</sub>* function is also updated.
///
/// # Errors
///
/// This method is meant to be called in a context where the returned `Result` is used to
/// validate the transaction passed as argument. The result should not be processed manually.
///
/// The policy in case of failure can be defined through the transaction, using
/// `Transaction::with_control` for construction.
///
/// # Panics
///
/// The method may panic if the two darts are not 1-sewable.
pub fn one_sew(
&self,
trans: &mut Transaction,
lhs_dart_id: DartIdType,
rhs_dart_id: DartIdType,
) -> StmResult<()> {
let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?;
if b2lhs_dart_id == NULL_DART_ID {
self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id)
} else {
let b2lhs_vid_old = self.vertex_id_transac(trans, b2lhs_dart_id)?;
let rhs_vid_old = self.vertex_id_transac(trans, rhs_dart_id)?;
self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id)?;
let new_vid = self.vertex_id_transac(trans, rhs_dart_id)?;
// FIXME: VertexIdentifier should be cast to DartIdentifier
self.vertices
.merge(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?;
self.attributes
.merge_vertex_attributes(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?;
Ok(())
}
}
/// 1-sew two darts.
///
/// This variant is equivalent to `one_sew`, but internally uses a transaction that will be
/// retried until validated.
pub fn force_one_sew(&self, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType) {
atomically(|trans| self.one_sew(trans, lhs_dart_id, rhs_dart_id));
}
/// Attempt to 1-sew two darts.
///
/// # Errors
///
/// This method will fail, returning an error, if:
/// - the transaction cannot be completed
/// - one (or more) attribute merge fails
///
/// The returned error can be used in conjunction with transaction control to avoid any
/// modifications in case of failure at attribute level. The user can then choose, through its
/// transaction control policy, to retry or abort as he wishes.
pub fn try_one_sew(
&self,
trans: &mut Transaction,
lhs_dart_id: DartIdType,
rhs_dart_id: DartIdType,
) -> CMapResult<()> {
let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?;
if b2lhs_dart_id == NULL_DART_ID {
self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id)?;
} else {
let b2lhs_vid_old = self.vertex_id_transac(trans, b2lhs_dart_id)?;
let rhs_vid_old = self.vertex_id_transac(trans, rhs_dart_id)?;
self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id)?;
let new_vid = self.vertex_id_transac(trans, rhs_dart_id)?;
// TODO: these should be attempts, only succeding if it's a full merge
self.vertices
.try_merge(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?;
self.attributes.try_merge_vertex_attributes(
trans,
new_vid,
b2lhs_vid_old,
rhs_vid_old,
)?;
}
Ok(())
}
}
/// 1-unsews
impl<T: CoordsFloat> CMap2<T> {
/// 1-unsew operation.
///
/// This operation corresponds to *coherently separating* two darts linked via the
/// *β<sub>1</sub>* function. For a thorough explanation of this operation (and implied
/// hypothesis & consequences), refer to the [user guide][UG].
///
/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/
///
/// # Arguments
///
/// - `lhs_dart_id: DartIdentifier` -- ID of the dart to separate.
/// - `policy: UnsewPolicy` -- Geometrical unsewing policy to follow.
///
/// Note that we do not need to take two darts as arguments since the second dart can be
/// obtained through the *β<sub>1</sub>* function. The *β<sub>0</sub>* function is also updated.
///
/// # Errors
///
/// This method is meant to be called in a context where the returned `Result` is used to
/// validate the transaction passed as argument. The result should not be processed manually.
///
/// The policy in case of failure can be defined through the transaction, using
/// `Transaction::with_control` for construction.
///
/// # Panics
///
/// The method may panic if there's a missing attribute at the splitting step. While the
/// implementation could fall back to a simple unlink operation, it probably should have been
/// called by the user, instead of unsew, in the first place.
pub fn one_unsew(&self, trans: &mut Transaction, lhs_dart_id: DartIdType) -> StmResult<()> {
let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?;
if b2lhs_dart_id == NULL_DART_ID {
self.betas.one_unlink_core(trans, lhs_dart_id)?;
} else {
// fetch IDs before topology update
let rhs_dart_id = self.betas[(1, lhs_dart_id)].read(trans)?;
let vid_old = self.vertex_id_transac(trans, rhs_dart_id)?;
// update the topology
self.betas.one_unlink_core(trans, lhs_dart_id)?;
// split vertices & attributes from the old ID to the new ones
// FIXME: VertexIdentifier should be cast to DartIdentifier
let (new_lhs, new_rhs) = (
self.vertex_id_transac(trans, b2lhs_dart_id)?,
self.vertex_id_transac(trans, rhs_dart_id)?,
);
self.vertices.split(trans, new_lhs, new_rhs, vid_old)?;
self.attributes
.split_vertex_attributes(trans, new_lhs, new_rhs, vid_old)?;
}
Ok(())
}
/// 1-unsew two darts.
///
/// This variant is equivalent to `one_unsew`, but internally uses a transaction that will
/// be retried until validated.
pub fn force_one_unsew(&self, lhs_dart_id: DartIdType) {
atomically(|trans| self.one_unsew(trans, lhs_dart_id));
}
/// Attempt to 1-unsew two darts.
///
/// # Errors
///
/// This method will fail, returning an error, if:
/// - the transaction cannot be completed
/// - one (or more) attribute merge fails
///
/// The returned error can be used in conjunction with transaction control to avoid any
/// modifications in case of failure at attribute level. The user can then choose, through its
/// transaction control policy, to retry or abort as he wishes.
pub fn try_one_unsew(
&self,
trans: &mut Transaction,
lhs_dart_id: DartIdType,
) -> CMapResult<()> {
let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?;
if b2lhs_dart_id == NULL_DART_ID {
self.betas.one_unlink_core(trans, lhs_dart_id)?;
} else {
// fetch IDs before topology update
let rhs_dart_id = self.betas[(1, lhs_dart_id)].read(trans)?;
let vid_old = self.vertex_id_transac(trans, rhs_dart_id)?;
// update the topology
self.betas.one_unlink_core(trans, lhs_dart_id)?;
// split vertices & attributes from the old ID to the new ones
// TODO: these should be attempts, only succeding if splitting a value
let (new_lhs, new_rhs) = (
self.vertex_id_transac(trans, b2lhs_dart_id)?,
self.vertex_id_transac(trans, rhs_dart_id)?,
);
self.vertices.try_split(trans, new_lhs, new_rhs, vid_old)?;
self.attributes
.try_split_vertex_attributes(trans, new_lhs, new_rhs, vid_old)?;
}
Ok(())
}
}