aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlotte Pabst <charlotte.pabst@stud.tu-darmstadt.de>2024-03-25 02:25:43 +0100
committerCharlotte Pabst <charlotte.pabst@stud.tu-darmstadt.de>2024-03-25 02:25:43 +0100
commit4cc4f8d5e07a4aa85ac52ce3205574e37ffdf780 (patch)
tree4a4a6c864e25ab8df5f6547b490428b23c6c5d14
parent1f92f06d21289019ca8a955b9162da66d98badfe (diff)
downloaddcel-4cc4f8d5e07a4aa85ac52ce3205574e37ffdf780.tar.xz
generational references & documentation
-rw-r--r--examples/subdivision.rs2
-rw-r--r--src/entity.rs13
-rw-r--r--src/entity_iterator.rs3
-rw-r--r--src/lib.rs890
-rw-r--r--src/mekh.rs8
-rw-r--r--src/melf.rs8
-rw-r--r--src/mev.rs8
-rw-r--r--src/mevvlfs.rs7
-rw-r--r--src/mpkh.rs8
-rw-r--r--src/msev.rs8
-rw-r--r--src/mve.rs7
11 files changed, 887 insertions, 75 deletions
diff --git a/examples/subdivision.rs b/examples/subdivision.rs
index 883b121..46ce604 100644
--- a/examples/subdivision.rs
+++ b/examples/subdivision.rs
@@ -1,4 +1,4 @@
-use dcel::{ptr_t, Dcel, ObjExport, ObjImport, Ptr, Shell};
+use dcel::{ptr_t, Dcel, ObjExport, ObjImport, Shell};
use std::array::from_fn;
use std::collections::HashMap;
diff --git a/src/entity.rs b/src/entity.rs
index 1c2eb7a..f6003c1 100644
--- a/src/entity.rs
+++ b/src/entity.rs
@@ -73,8 +73,8 @@ macro_rules! entity {
($name:ident : $T:ident $(($init_name:ident : $init_ty:ty))?
$(, $($custom_field:ident : $custom_ty:ty = $custom_expr:expr),* )?
$(;
- $( $field_vis:vis $field:ident
- $([ $list_singular:ident : $list_name:ident $(($list_init:ty))? $($list_back:ident)? ])?
+ $( $(#[$field_attr:meta])* $field_vis:vis $field:ident
+ $([ $(#[$list_attr:meta])* $list_singular:ident : $list_name:ident $(($list_init:ty))? $($list_back:ident)? ])?
: $field_ty:ident ),*
)?
) => { paste! {
@@ -138,6 +138,7 @@ macro_rules! entity {
#[allow(unused)]
impl<'brand, 'arena, V> ptr!($T) {
$($(
+ $(#[$field_attr])*
$field_vis fn $field(self, token: &impl ReflAsRef<GhostToken<'brand>>) -> ptr!($field_ty) {
self.[<maybe_ $field>](token).unwrap()
}
@@ -155,6 +156,7 @@ macro_rules! entity {
}
$(
+ $(#[$list_attr])*
pub fn [<iter_ $field>]<'tok>(
self,
token: &'tok impl ReflAsRef<GhostToken<'brand>>,
@@ -163,7 +165,7 @@ macro_rules! entity {
EntityIterator::new(self.[<maybe_ $field>](token), token)
}
- pub fn [<iter_mut_ $field>]<T: ReflAsMut<GhostToken<'brand>>>(
+ fn [<iter_mut_ $field>]<T: ReflAsMut<GhostToken<'brand>>>(
self,
token: &mut T,
mut f: impl FnMut(ptr!($field_ty), &mut T),
@@ -177,7 +179,10 @@ macro_rules! entity {
let next_item = item.next(token);
f(item, token);
item = next_item;
- matches!(item.maybe_id(token), Some(x) if x != last)
+
+ // hack to ensure killing the item is allowed
+ // bypassing generational references
+ matches!(item.cell.borrow(ReflAsRef::as_ref(token)).maybe_id(), Some(x) if x != last)
} {}
}
diff --git a/src/entity_iterator.rs b/src/entity_iterator.rs
index 2e8c906..f293dae 100644
--- a/src/entity_iterator.rs
+++ b/src/entity_iterator.rs
@@ -1,5 +1,8 @@
use crate::*;
+/// An iterator over a linked list of entities.
+///
+/// See the various `.iter_*()` methods on [`Ptr`] for details.
pub struct EntityIterator<'tok, 'brand, 'arena, T>(Option<(lens_t!(T), lens_t!(T))>);
impl<'tok, 'brand, 'arena, T> Clone for EntityIterator<'tok, 'brand, 'arena, T> {
diff --git a/src/lib.rs b/src/lib.rs
index 08af947..b42993c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,6 +25,8 @@ macro_rules! try_check {
};
}
+use crate as dcel;
+
#[macro_use]
mod entity;
use entity::*;
@@ -71,6 +73,7 @@ pub use msev::*;
mod mpkh;
pub use mpkh::*;
+/// A trait equivalent to [`AsRef`], except with a built-in [reflexivity](https://doc.rust-lang.org/stable/std/convert/trait.AsRef.html#reflexivity) rule.
pub trait ReflAsRef<T> {
fn as_ref(&self) -> &T;
}
@@ -81,6 +84,7 @@ impl<T> ReflAsRef<T> for T {
}
}
+/// A trait equivalent to [`AsMut`], except with a built-in [reflexivity](https://doc.rust-lang.org/stable/std/convert/trait.AsMut.html#reflexivity) rule.
pub trait ReflAsMut<T>: ReflAsRef<T> {
fn as_mut(&mut self) -> &mut T;
}
@@ -103,48 +107,186 @@ impl<F: Fn(&mut Formatter) -> fmt::Result> Debug for DisplayFn<F> {
}
}
+/// Returns a [`Ptr`] type for a given type, using `'brand` and `'arena` lifetimes from the scope.
+///
+/// `ptr_!(T)` expands to `dcel::Ptr<'brand, 'arena, T>`
+///
+/// This macro is useful versus [`ptr!`] if you don't want to populate the vertex data `V`
+/// parameter from the environment.
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{ptr_t, Dcel, Vertex};
+///
+/// fn vertex_len<'brand, 'arena>(
+/// vert: ptr_t!(Vertex<'brand, 'arena, [f32; 2]>), //
+/// dcel: &Dcel<'brand, 'arena, [f32; 2]>,
+/// ) -> f32 {
+/// let [x, y] = vert.data(dcel);
+/// (x * x + y * y).sqrt()
+/// }
+/// ```
#[macro_export]
macro_rules! ptr_t {
($T:ty) => {
- Ptr<'brand, 'arena, $T>
+ dcel::Ptr<'brand, 'arena, $T>
}
}
+/// Returns a [`Ptr`] type for a given type, using `'brand` and `'arena` lifetimes and the vertex data parameter `V` from the scope.
+///
+/// `ptr!(T)` expands to `dcel::Ptr<'brand, 'arena, T<'brand, 'arena, V>>`
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{ptr, Dcel, Vertex};
+/// use std::ops::Add;
+///
+/// fn vertex_sum<'brand, 'arena, O, V>(verts: [ptr!(Vertex); 2], dcel: &Dcel<'brand, 'arena, V>) -> O
+/// where
+/// for<'a> &'a V: Add<Output = O>
+/// {
+/// verts[0].data(dcel) + verts[1].data(dcel)
+/// }
+/// ```
#[macro_export]
macro_rules! ptr {
($T:ident) => {
- Ptr<'brand, 'arena, $T<'brand, 'arena, V>>
+ dcel::Ptr<'brand, 'arena, $T<'brand, 'arena, V>>
};
}
+/// Returns a [`Own`] type for a given type, using `'brand` and `'arena` lifetimes from the scope.
+///
+/// `own_t!(T)` expands to `dcel::Own<'brand, 'arena, T>`
+///
+/// This macro is useful versus [`own!`] if you don't want to populate the vertex data `V`
+/// parameter from the environment.
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{own_t, Dcel, Vertex, Edge};
+///
+/// fn sum_kve<'brand, 'arena>(
+/// ops: Vec<(own_t!(Vertex<'brand, 'arena, u32>), own_t!(Edge<'brand, 'arena, u32>))>,
+/// dcel: &mut Dcel<'brand, 'arena, u32>,
+/// ) -> u32 {
+/// ops
+/// .into_iter()
+/// .map(|(vertex, edge)| dcel.kve(edge, vertex).unwrap().data)
+/// .sum()
+/// }
+/// ```
#[macro_export]
macro_rules! own_t {
($T:ty) => {
- Own<'brand, 'arena, $T>
+ dcel::Own<'brand, 'arena, $T>
}
}
+/// Returns a [`Own`] type for a given type, using `'brand` and `'arena` lifetimes and the vertex data parameter `V` from the scope.
+///
+/// `own!(T)` expands to `dcel::Own<'brand, 'arena, T<'brand, 'arena, V>>`
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{own, Dcel, Vertex, Edge};
+///
+/// fn sum_kve<'brand, 'arena, V: std::iter::Sum>(
+/// ops: Vec<(own!(Vertex), own!(Edge))>,
+/// dcel: &mut Dcel<'brand, 'arena, V>,
+/// ) -> V {
+/// ops
+/// .into_iter()
+/// .map(|(vertex, edge)| dcel.kve(edge, vertex).unwrap().data)
+/// .sum()
+/// }
+/// ```
#[macro_export]
macro_rules! own {
($T:ident) => {
- Own<'brand, 'arena, $T<'brand, 'arena, V>>
+ dcel::Own<'brand, 'arena, $T<'brand, 'arena, V>>
};
}
+/// Returns a [`Lens`] type for a given type, using `'tok`, `'brand` and `'arena` lifetimes from the scope.
+///
+/// `lens_t!(T)` expands to `dcel::Lens<'tok, 'brand, 'arena, T>`
+///
+/// This macro is useful versus [`lens!`] if you don't want to populate the vertex data `V`
+/// parameter from the environment.
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{lens_t, Dcel, Vertex};
+///
+/// fn vertex_len<'tok, 'brand, 'arena>(
+/// vert: lens_t!(Vertex<'brand, 'arena, [f32; 2]>),
+/// ) -> f32 {
+/// let [x, y] = vert.data();
+/// (x * x + y * y).sqrt()
+/// }
+/// ```
#[macro_export]
macro_rules! lens_t {
($T:ty) => {
- Lens<'tok, 'brand, 'arena, $T>
+ dcel::Lens<'tok, 'brand, 'arena, $T>
}
}
+/// Returns a [`Lens`] type for a given type, using `'tok`, `'brand` and `'arena` lifetimes and the vertex data parameter `V` from the scope.
+///
+/// `lens!(T)` expands to `dcel::Lens<'tok, 'brand, 'arena, T<'brand, 'arena, V>>`
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{lens, Dcel, Vertex};
+/// use std::ops::Add;
+///
+/// fn vertex_sum<'tok, 'brand, 'arena, O, V>(verts: [lens!(Vertex); 2]) -> O
+/// where
+/// for<'a> &'a V: Add<Output = O>
+/// {
+/// verts[0].data() + verts[1].data()
+/// }
+/// ```
#[macro_export]
macro_rules! lens {
($T:ident) => {
- Lens<'tok, 'brand, 'arena, $T<'brand, 'arena, V>>
+ dcel::Lens<'tok, 'brand, 'arena, $T<'brand, 'arena, V>>
};
}
+/// For each identifier passed in, introduces a new binding shadowing the old one
+/// containing a lens to what was previously a pointer. Takes a token as first argument.
+///
+/// # Examples
+/// ```
+/// use dcel::{mklens, Dcel, Kevvlfs};
+///
+/// Dcel::new(|mut dcel| {
+/// let body = dcel.new_body();
+/// let Kevvlfs {
+/// edge,
+/// face,
+/// loop_,
+/// shell,
+/// ..
+/// } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+///
+/// mklens!(&dcel, edge, face, loop_, shell);
+///
+/// assert_eq!(face.outer_loop(), loop_);
+/// assert_eq!(face.shell(), shell);
+/// assert_eq!(edge.half_edges()[0].loop_(), loop_);
+/// assert_eq!(edge.half_edges()[1].loop_(), loop_);
+/// })
#[macro_export]
macro_rules! mklens {
($token: expr, $($name:ident),*) => {
@@ -187,7 +329,22 @@ fn or_err<T>(cond: bool, err: T) -> Result<(), T> {
}
}
-pub struct Ptr<'brand, 'arena, T>(pub &'arena GhostCell<'brand, T>);
+/// A non-owning pointer to a topological entity like a Vertex or a HalfEdge.
+///
+/// Being a wrapper around [`GhostCell`], it requires a reference to a [`GhostToken`] to access or mutate its contents.
+///
+/// [`Ptr`]s to different kinds of entities have different methods available to access their
+/// contents and this is the primary way to read out their data. [`Ptr`] is also the primary way
+/// to pass around entities.
+///
+/// # Safety
+/// Entities can be killed while pointers to them are held, but the integrity of [`Ptr`]s
+/// is ensured using generational references. Trying to access a [`Ptr`] pointing to an item that has been killed
+/// results in a use after free panic, even when its memory is being reused.
+pub struct Ptr<'brand, 'arena, T> {
+ cell: &'arena GhostCell<'brand, T>,
+ id: usize,
+}
impl<'brand, 'arena, T> Clone for ptr_t!(T) {
fn clone(&self) -> Self {
@@ -196,13 +353,25 @@ impl<'brand, 'arena, T> Clone for ptr_t!(T) {
}
impl<'brand, 'arena, T> Copy for ptr_t!(T) {}
-impl<'brand, 'arena, T> ptr_t!(T) {
+#[allow(unused)]
+impl<'brand, 'arena, T: Entity<'brand, 'arena>> ptr_t!(T) {
+ fn new(cell: &'arena GhostCell<'brand, T>, token: &impl ReflAsRef<GhostToken<'brand>>) -> Self {
+ let id = cell.borrow(token.as_ref()).id();
+ Self { id, cell }
+ }
+
+ fn clear(self, token: &mut impl ReflAsMut<GhostToken<'brand>>) {
+ self.borrow_mut(token).clear()
+ }
+
pub fn borrow<'tok, 'out>(self, token: &'tok impl ReflAsRef<GhostToken<'brand>>) -> &'out T
where
'arena: 'out,
'tok: 'out,
{
- self.0.borrow(token.as_ref())
+ let borrow = self.cell.borrow(token.as_ref());
+ assert_eq!(borrow.maybe_id(), Some(self.id), "use after free");
+ borrow
}
pub fn borrow_mut<'tok, 'out>(
@@ -213,20 +382,48 @@ impl<'brand, 'arena, T> ptr_t!(T) {
'arena: 'out,
'tok: 'out,
{
- self.0.borrow_mut(token.as_mut())
- }
-
+ let borrow = self.cell.borrow_mut(token.as_mut());
+ assert_eq!(borrow.maybe_id(), Some(self.id), "use after free");
+ borrow
+ }
+
+ /// Creates a Lens holding this pointer and the given token.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, face, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ ///
+ /// // without .lens()
+ /// assert!(edge.half_edges(&dcel)[0].loop_(&dcel).face(&dcel).eq(*face, &dcel));
+ ///
+ /// // with .lens()
+ /// assert!(edge.lens(&dcel).half_edges()[0].loop_().face().eq(*face));
+ /// })
pub fn lens<'tok>(self, token: &'tok impl ReflAsRef<GhostToken<'brand>>) -> lens_t!(T) {
Lens::new(self, token)
}
-}
-
-#[allow(unused)]
-impl<'brand, 'arena, T: Entity<'brand, 'arena>> ptr_t!(T) {
- fn clear(self, token: &mut impl ReflAsMut<GhostToken<'brand>>) {
- self.borrow_mut(token).clear()
- }
+ /// A unique id for the item referenced by this pointer.
+ /// The id is unique *only* for items of the same type.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, vertices: [a, b], .. } = dcel.mevvlfs(*body, [4, 4]).unwrap();
+ ///
+ /// assert_ne!(a.id(&dcel), b.id(&dcel)); // IDs of two distinct vertices must be different
+ /// // assert_ne!(a.id(&dcel), edge.id(&dcel)); // WRONG! may panic
+ /// })
+ /// ```
pub fn id(self, token: &impl ReflAsRef<GhostToken<'brand>>) -> usize {
self.borrow(token).id()
}
@@ -237,6 +434,21 @@ impl<'brand, 'arena, T: Entity<'brand, 'arena>> ptr_t!(T) {
self.borrow(token).alive()
}
+ /// Compares the pointer to `other` by-reference.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let [a, b] = dcel.mevvlfs(*body, [4, 4]).unwrap().vertices;
+ ///
+ /// assert!(a.eq(*a, &dcel));
+ /// assert!(!a.eq(*b, &dcel));
+ /// })
+ /// ```
pub fn eq(self, other: Self, token: &impl ReflAsRef<GhostToken<'brand>>) -> bool {
self.borrow(token) == other.borrow(token)
}
@@ -268,10 +480,27 @@ impl<'brand, 'arena, T: Entity<'brand, 'arena>> ptr_t!(T) {
}
}
+/// A owning pointer to a topological entity like a Vertex or a HalfEdge.
+///
+/// Operators that create entities return [`Own`]ing pointers to them. Unlike [`Ptr`], [`Own`]
+/// does not implement [`Copy`]/[`Clone`], meaning that only one instance to it exists for a given
+/// item. Similarly, operators that kill entities require [`Own`]ing pointers, preventing
+/// conceptual double-free bugs at compile time.
+///
+/// [`Own`] dereferences to [`Ptr`].
+///
+/// # Safety
+/// Since it is not feasible to keep around [`Own`] pointers for every application (for example,
+/// traversing a mesh and killing parts of it based on read data in the form of [`Ptr`]s),
+/// [`Own::unsafe_make_owned`] is provided, which turns a [`Ptr`] into an `[Own]`. Trying to free
+/// the same entity twice results in an use after free panic.
pub struct Own<'brand, 'arena, T>(ptr_t!(T));
impl<'brand, 'arena, T> Own<'brand, 'arena, T> {
- // avoid this
+ /// Turns an [`Own`] into an [`Ptr`]. This is fully sound, but may cause runtime crashes
+ /// if an entity is killed twice.
+ ///
+ /// Avoid this unless it really makes sense in your application to use it.
pub fn unsafe_make_owned(this: ptr_t!(T)) -> Self {
Self(this)
}
@@ -285,6 +514,37 @@ impl<'brand, 'arena, T> Deref for own_t!(T) {
}
}
+/// A wrapper around a [`Ptr`] that also contains an immutable GhostToken reference.
+///
+/// A [`Lens`] has the same methods as a [`Ptr`] (minus methods that require mutability)
+/// except it does not require a [`GhostToken`] as argument since that is already stored.
+/// [`Lens`] methods also return [`Lens`]es instead of [`Ptr`]s, which allows convenient traversal.
+///
+/// This also allows [`Lens`] to implement [`PartialEq`], [`Hash`] and [`Debug`], which [`Ptr`]
+/// can't.
+///
+/// For documentation of [`Lens`] methods, see the [`Ptr`] equivalents.
+///
+/// Since holding an immutable borrow to the GhostToken prevents mutation of the [`Dcel`], lenses
+/// are best used for read-only (parts of) tasks.
+///
+/// A [`Lens`] can be created either by using the [`Lens::new`] method or the [`Ptr::lens`] method.
+///
+/// # Examples
+///
+/// ```
+/// use dcel::{Dcel, Kevvlfs};
+///
+/// Dcel::new(|mut dcel| {
+/// let body = dcel.new_body();
+/// let Kevvlfs { edge, face, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+///
+/// // without using lens
+/// assert!(edge.half_edges(&dcel)[0].loop_(&dcel).face(&dcel).eq(*face, &dcel));
+///
+/// // using lens
+/// assert!(edge.lens(&dcel).half_edges()[0].loop_().face().eq(*face));
+/// })
pub struct Lens<'tok, 'brand, 'arena, T> {
pub item: ptr_t!(T),
pub token: &'tok GhostToken<'brand>,
@@ -297,14 +557,14 @@ impl<'tok, 'brand, 'arena, T> Clone for lens_t!(T) {
}
impl<'tok, 'brand, 'arena, T> Copy for lens_t!(T) {}
-impl<'tok, 'brand, 'arena, T: PartialEq> PartialEq for lens_t!(T) {
+impl<'tok, 'brand, 'arena, T: Entity<'brand, 'arena>> PartialEq for lens_t!(T) {
fn eq(&self, other: &Self) -> bool {
self.item.borrow(self.token) == other.item.borrow(other.token)
}
}
-impl<'tok, 'brand, 'arena, T: PartialEq> Eq for lens_t!(T) {}
+impl<'tok, 'brand, 'arena, T: Entity<'brand, 'arena>> Eq for lens_t!(T) {}
-impl<'tok, 'brand, 'arena, T: Hash> Hash for lens_t!(T) {
+impl<'tok, 'brand, 'arena, T: Entity<'brand, 'arena> + Hash> Hash for lens_t!(T) {
fn hash<H: Hasher>(&self, state: &mut H) {
self.item.borrow(self.token).hash(state);
}
@@ -322,17 +582,15 @@ impl<'tok, 'brand, 'arena, T> From<lens_t!(T)> for ptr_t!(T) {
}
}
-impl<'tok, 'brand, 'arena, T> lens_t!(T) {
+#[allow(unused)]
+impl<'tok, 'brand, 'arena, T: Entity<'brand, 'arena>> lens_t!(T) {
pub fn new(item: ptr_t!(T), token: &'tok impl ReflAsRef<GhostToken<'brand>>) -> Self {
Self {
item,
token: token.as_ref(),
}
}
-}
-#[allow(unused)]
-impl<'tok, 'brand, 'arena, T: Entity<'brand, 'arena>> lens_t!(T) {
pub fn id(self) -> usize {
self.item.id(&self)
}
@@ -367,6 +625,11 @@ entity!(vertex: Vertex (init: V),
outgoing: HalfEdge
);
+/// An iterator over the outgoing HalfEdges from a vertex.
+///
+/// See [ptr!(Vertex)::iter_outgoing] for details.
+///
+/// [ptr!(Vertex)::iter_outgoing]: dcel::Ptr#method.iter_outgoing
pub struct OutgoingIterator<'tok, 'brand, 'arena, V>(Option<(lens!(HalfEdge), lens!(HalfEdge))>);
impl<'tok, 'brand, 'arena, V> Clone for OutgoingIterator<'tok, 'brand, 'arena, V> {
@@ -411,6 +674,19 @@ impl<'brand, 'arena, V> own!(Vertex) {
}
impl<'brand, 'arena, V> ptr!(Vertex) {
+ /// Returns an immutable borrow to the user-data associated with the vertex.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let [_, vertex] = dcel.mevvlfs(*body, [4, 5]).unwrap().vertices;
+ ///
+ /// assert_eq!(*vertex.data(&dcel), 5);
+ /// })
pub fn data<'tok, 'out>(self, token: &'tok impl ReflAsRef<GhostToken<'brand>>) -> &'out V
where
'arena: 'out,
@@ -419,6 +695,20 @@ impl<'brand, 'arena, V> ptr!(Vertex) {
self.borrow(token).data.as_ref().unwrap()
}
+ /// Returns a mutable borrow to the user-data associated with the vertex.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let [_, vertex] = dcel.mevvlfs(*body, [4, 5]).unwrap().vertices;
+ ///
+ /// *vertex.mut_data(&mut dcel) = 10;
+ /// assert_eq!(*vertex.data(&dcel), 10);
+ /// })
pub fn mut_data<'tok, 'out>(
self,
token: &'tok mut impl ReflAsMut<GhostToken<'brand>>,
@@ -430,15 +720,46 @@ impl<'brand, 'arena, V> ptr!(Vertex) {
self.borrow_mut(token).data.as_mut().unwrap()
}
+ /// Returns an iterator over lenses to all half-edges going out of this vertex
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ /// use std::iter::once;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, vertices: [vertex, _], .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let [a, b] = edge.lens(&dcel).half_edges();
+ /// let outgoing = vertex.iter_outgoing(&dcel);
+ ///
+ /// assert!(outgoing.eq(once(a)) || outgoing.eq(once(b)));
+ /// })
pub fn iter_outgoing<'tok>(
self,
token: &'tok impl ReflAsRef<GhostToken<'brand>>,
) -> OutgoingIterator<'tok, 'brand, 'arena, V> {
- // FIXME: maybe enforce at least one item by using .outgoing()
+ // FIXME: maybe enforce at least one item by using Some(self.outgoing())
// there should be no "standalone" vertices (?)
OutgoingIterator::new(self.maybe_outgoing(token), token)
}
+ /// Returns a half-edge going out of this vertex belonging to a given loop, if one exists.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, vertices: [vertex, _], loop_, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let [a, b] = edge.lens(&dcel).half_edges();
+ /// let outgoing = vertex.find_outgoing(*loop_, &dcel).unwrap();
+ ///
+ /// assert!(a.eq(outgoing) || b.eq(outgoing));
+ /// })
pub fn find_outgoing(
self,
loop_: ptr!(Loop),
@@ -469,15 +790,95 @@ impl<'tok, 'brand, 'arena, V> lens!(Vertex) {
}
}
-// TODO: target
entity!(half_edge: HalfEdge;
+ /// Returns the Vertex this half-edge is anchored on (dual of target).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{mklens, Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, vertices: [v1, v2], .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let [x1, x2] = edge.half_edges(&dcel).map(|x| x.origin(&dcel));
+ /// mklens!(&dcel, v1, v2, x1, x2);
+ ///
+ /// assert!([x1, x2] == [v1, v2] || [x1, x2] == [v2, v1]);
+ /// })
pub origin: Vertex,
+ /// Returns the twin of this HalfEdge.
+ ///
+ /// Twins have their target/origin swapped and face in opposite directions.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let edge = dcel.mevvlfs(*body, [4, 5]).unwrap().edge;
+ /// let [a, b] = edge.half_edges(&dcel);
+ ///
+ /// assert!(a.twin(&dcel).eq(b, &dcel));
+ /// assert!(b.twin(&dcel).eq(a, &dcel));
+ /// })
pub twin: HalfEdge,
+ /// Returns a back pointer to the Loop this HalfEdge belongs is part of.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, loop_, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let [a, b] = edge.half_edges(&dcel);
+ ///
+ /// assert!(a.loop_(&dcel).eq(*loop_, &dcel));
+ /// assert!(b.loop_(&dcel).eq(*loop_, &dcel));
+ /// })
pub loop_: Loop,
+ /// Returns a back pointer to the Edge (combination of both twins) this HalfEdge belongs to.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let edge = dcel.mevvlfs(*body, [4, 5]).unwrap().edge;
+ /// let [a, b] = edge.half_edges(&dcel);
+ ///
+ /// assert!(a.edge(&dcel).eq(*edge, &dcel));
+ /// assert!(b.edge(&dcel).eq(*edge, &dcel));
+ /// })
pub edge: Edge
);
impl<'brand, 'arena, V> ptr!(HalfEdge) {
+ /// Returns the vertex this half-edge points to (dual of origin).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, vertices: [mut v1, mut v2], .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let [a, b] = edge.lens(&dcel).half_edges();
+ ///
+ /// if a.origin().eq(*v2) {
+ /// [v1, v2] = [v2, v1];
+ /// }
+ ///
+ /// assert!(b.target().eq(*v1));
+ /// assert!(a.target().eq(*v2));
+ /// })
pub fn target(self, token: &impl ReflAsRef<GhostToken<'brand>>) -> ptr!(Vertex) {
self.twin(token).origin(token)
}
@@ -495,11 +896,53 @@ impl<'tok, 'brand, 'arena, V> lens!(HalfEdge) {
}
entity!(loop_: Loop;
- half_edges[half_edge: half_edge back]: HalfEdge,
+ half_edges[
+ /// Returns an iterator over the half-edges this loop is made of, starting with an arbitrary one.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, loop_, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let [a, b] = edge.lens(&dcel).half_edges();
+ /// let half_edges = loop_.iter_half_edges(&dcel);
+ ///
+ /// assert!(half_edges.eq([a, b]) || half_edges.eq([b, a]));
+ /// })
+ half_edge: half_edge back]: HalfEdge,
+ /// Returns a back pointer to the Face this Loop belongs to.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { loop_, face, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ ///
+ /// assert!(loop_.face(&dcel).eq(*face, &dcel));
+ /// })
pub face: Face
);
impl<'brand, 'arena, V> ptr!(Loop) {
+ /// Returns true if this loop is the outer loop of its face.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let loop_ = dcel.mevvlfs(*body, [4, 5]).unwrap().loop_;
+ ///
+ /// assert!(loop_.is_outer(&dcel));
+ /// })
pub fn is_outer(self, token: &impl ReflAsRef<GhostToken<'brand>>) -> bool {
self.lens(token).is_outer()
}
@@ -561,6 +1004,23 @@ impl<'brand, 'arena, V> own!(Edge) {
}
impl<'brand, 'arena, V> ptr!(Edge) {
+ /// Returns the two half-edges associated with this edge.
+ ///
+ /// The half edges are twins and facing in opposite directions.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let edge = dcel.mevvlfs(*body, [4, 5]).unwrap().edge;
+ /// let [a, b] = edge.half_edges(&dcel);
+ ///
+ /// assert!(a.twin(&dcel).eq(b, &dcel));
+ /// assert!(b.twin(&dcel).eq(a, &dcel));
+ /// })
pub fn half_edges(self, token: &impl ReflAsRef<GhostToken<'brand>>) -> [ptr!(HalfEdge); 2] {
self.borrow(token)
.half_edges
@@ -568,18 +1028,24 @@ impl<'brand, 'arena, V> ptr!(Edge) {
.map(|x| *x.as_deref().unwrap())
}
+ /// Returns the origin vertices of the two half-edges associated with this edge.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{mklens, Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, vertices: [v1, v2], .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// let verts = edge.vertices(&dcel).map(|x| x.lens(&dcel));
+ /// mklens!(&dcel, v1, v2);
+ ///
+ /// assert!(verts == [v1, v2] || verts == [v2, v1]);
+ /// })
pub fn vertices(self, token: &impl ReflAsRef<GhostToken<'brand>>) -> [ptr!(Vertex); 2] {
self.half_edges(token).map(|x| x.origin(token))
}
-
- /*
- fn set_half_edges(
- self,
- x: [own!(HalfEdge); 2],
- token: &mut impl ReflAsMut<GhostToken<'brand>>,
- ) {
- self.borrow_mut(token).half_edges = Some(x);
- }*/
}
impl<'tok, 'brand, 'arena, V> lens!(Edge) {
@@ -600,8 +1066,48 @@ impl<'tok, 'brand, 'arena, V> lens!(Edge) {
}
entity!(face: Face;
+ /// Returns the outer (peripheral) Loop of this face.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { loop_, face, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ ///
+ /// assert!(face.outer_loop(&dcel).eq(*loop_, &dcel));
+ /// })
pub outer_loop: Loop,
- inner_loops[inner_loop: loop_ back]: Loop,
+ inner_loops[
+ /// Returns an iterator over the inner (hole) Loops of this face.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let face = dcel.mevvlfs(*body, [4, 5]).unwrap().face;
+ ///
+ /// assert!(face.iter_inner_loops(&dcel).next().is_none());
+ /// })
+ inner_loop: loop_ back]: Loop,
+ /// Returns a back pointer to the Shell this Face belongs to.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { face, shell, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ ///
+ /// assert!(face.shell(&dcel).eq(*shell, &dcel));
+ /// })
pub shell: Shell
);
@@ -616,9 +1122,68 @@ impl<'brand, 'arena, V> own!(Face) {
}
entity!(shell: Shell;
- faces[face: face back]: Face,
- edges[edge: edge]: Edge,
- vertices[vertex: vertex (V)]: Vertex,
+ faces[
+ /// Returns an iterator over all faces in this shell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ /// use std::iter::once;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { face, shell, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ ///
+ /// assert!(shell.iter_faces(&dcel).eq(once(face.lens(&dcel))));
+ /// })
+ face: face back]: Face,
+ edges[
+ /// Returns an iterator over all faces in this shell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{Dcel, Kevvlfs};
+ /// use std::iter::once;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { edge, shell, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ ///
+ /// assert!(shell.iter_edges(&dcel).eq(once(edge.lens(&dcel))));
+ /// })
+ edge: edge]: Edge,
+ vertices[
+ /// Returns an iterator over all faces in this shell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::{mklens, Dcel, Kevvlfs};
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let Kevvlfs { vertices: [v1, v2], shell, .. } = dcel.mevvlfs(*body, [4, 5]).unwrap();
+ /// mklens!(&dcel, v1, v2);
+ /// let verts = shell.iter_vertices(&dcel);
+ ///
+ /// assert!(verts.eq([v1, v2]) || verts.eq([v2, v1]));
+ /// })
+ vertex: vertex (V)]: Vertex,
+ /// Returns a back pointer to the Body this Shell belongs to.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let shell = dcel.mevvlfs(*body, [4, 5]).unwrap().shell;
+ ///
+ /// assert!(shell.body(&dcel).eq(*body, &dcel));
+ /// })
pub body: Body
);
@@ -638,7 +1203,22 @@ impl<'brand, 'arena, V> own!(Shell) {
}
entity!(body: Body;
- shells[shell: shell back]: Shell
+ shells[
+ /// Returns an iterator over all shells in this body.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dcel::Dcel;
+ /// use std::iter::once;
+ ///
+ /// Dcel::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// let shell = dcel.mevvlfs(*body, [4, 5]).unwrap().shell;
+ ///
+ /// assert!(body.iter_shells(&dcel).eq(once(shell.lens(&dcel))));
+ /// })
+ shell: shell back]: Shell
);
impl<'brand, 'arena, V> own!(Body) {
@@ -646,14 +1226,14 @@ impl<'brand, 'arena, V> own!(Body) {
self.iter_mut_shells(dcel, |x, dcel| {
Own::unsafe_make_owned(x).destroy(dcel);
});
- dcel.delete_body(self);
+ dcel.delete_body(self).unwrap();
}
}
struct Allocator<'brand, 'arena, T: Entity<'brand, 'arena>> {
next_id: usize,
arena: &'arena Arena<T>,
- freelist: Vec<own_t!(T)>,
+ freelist: Vec<&'arena GhostCell<'brand, T>>,
}
impl<'brand, 'arena, T: Entity<'brand, 'arena>> Allocator<'brand, 'arena, T> {
@@ -672,18 +1252,21 @@ impl<'brand, 'arena, T: Entity<'brand, 'arena>> Allocator<'brand, 'arena, T> {
}
fn alloc(&mut self, x: T, token: &mut impl ReflAsMut<GhostToken<'brand>>) -> own_t!(T) {
- if let Some(ptr) = self.freelist.pop() {
- *ptr.borrow_mut(token) = x;
- ptr
- } else {
- Own::unsafe_make_owned(Ptr(GhostCell::from_mut(self.arena.alloc(x))))
- }
+ Own::unsafe_make_owned(Ptr::new(
+ if let Some(cell) = self.freelist.pop() {
+ *cell.borrow_mut(token.as_mut()) = x;
+ cell
+ } else {
+ GhostCell::from_mut(self.arena.alloc(x))
+ },
+ token,
+ ))
}
fn free(&mut self, token: &mut impl ReflAsMut<GhostToken<'brand>>, ptr: own_t!(T)) {
- debug_assert!(ptr.alive(token), "double free");
+ let _ = ptr.id(token); // to prevent double free
ptr.clear(token);
- self.freelist.push(ptr);
+ self.freelist.push(ptr.cell);
}
}
@@ -710,19 +1293,44 @@ impl<T, E: Display> Display for OperatorErr<T, E> {
}
}
+/// A trait for Euler-Operators with preconditions and inverse operations.
pub trait Operator<'brand, 'arena, V>: Sized {
+ /// The inverse of the operation, created by applying it.
+ /// Applying the inverse should revert the operation.
+ /// This may not work if other operators have been used to modify the DCEL in the meantime.
type Inverse: Operator<'brand, 'arena, V>;
+
+ /// Error returned on precondition failure.
type Error: std::error::Error;
+
+ /// Info returned by precondition success.
type Check;
+ /// Check if preconditions are met without making any modifications to the DCEL.
+ ///
+ /// On precondition failure, an error indicating the reason for the failure is returned.
+ ///
+ /// On precondition success, this method may return additional info computed during
+ /// precondition checking and used by the [`Operator::apply`] method.
+ ///
+ /// [`Operator::apply`] calls this method to check for preconditions.
fn check(&self, dcel: &Dcel<'brand, 'arena, V>) -> Result<Self::Check, Self::Error>;
+ /// Check for preconditions and run the operation, taking ownership of it.
+ ///
+ /// On precondition failure, an [`OperatorErr`] is returned, which contains both an error
+ /// as well as the original operation passed in, for recovery purposes.
+ ///
+ /// On precondition success, the DCEL is modified and the inverse operation is returned.
fn apply(
self,
dcel: &mut Dcel<'brand, 'arena, V>,
) -> Result<Self::Inverse, OperatorErr<Self, Self::Error>>;
}
+/// A struct containing typed arena allocators for entities used by [`Dcel`].
+///
+/// Use [`Default::default()`] to construct this, or create [`Arena`] structs individually.
pub struct DcelArena<'brand, 'arena, V> {
pub vertex: Arena<Vertex<'brand, 'arena, V>>,
pub half_edge: Arena<HalfEdge<'brand, 'arena, V>>,
@@ -747,6 +1355,9 @@ impl<'brand, 'arena, V> Default for DcelArena<'brand, 'arena, V> {
}
}
+/// A doubly-connected edge-list.
+///
+/// Use [`Dcel::new`] to create a DCEL with a new [`GhostToken`] or [`Dcel::from_token`] to create one for an existing [`GhostToken`].
pub struct Dcel<'brand, 'arena, V> {
pub token: GhostToken<'brand>,
vertex: Allocator<'brand, 'arena, Vertex<'brand, 'arena, V>>,
@@ -772,6 +1383,21 @@ impl<'brand, 'arena, V> ReflAsMut<GhostToken<'brand>> for Dcel<'brand, 'arena, V
}
impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
+ /// Creates a new DCEL with a new [`GhostToken`] and default arena allocators.
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new<R, F>(fun: F) -> R
+ where
+ for<'new_brand, 'new_arena> F: FnOnce(Dcel<'new_brand, 'new_arena, V>) -> R,
+ {
+ GhostToken::new(|token| {
+ let arena = DcelArena::default();
+ let dcel = Dcel::from_token(token, &arena);
+
+ fun(dcel)
+ })
+ }
+
+ /// Creates a DCEL associated with the given [`GhostToken`] and using the arena allocators passed in.
pub fn from_token(token: GhostToken<'brand>, ar: &'arena DcelArena<'brand, 'arena, V>) -> Self {
Self {
token,
@@ -786,43 +1412,75 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
}
}
- #[allow(clippy::new_ret_no_self)]
- pub fn new<R, F>(fun: F) -> R
- where
- for<'new_brand, 'new_arena> F: FnOnce(Dcel<'new_brand, 'new_arena, V>) -> R,
- {
- GhostToken::new(|token| {
- let arena = DcelArena::default();
- let dcel = Dcel::from_token(token, &arena);
-
- fun(dcel)
- })
- }
-
+ /// Adds a new, empty body to the DCEL.
+ ///
+ /// # Examples
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::<()>::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// /* ... */
+ /// })
+ /// ```
pub fn new_body(&mut self) -> own!(Body) {
let body = Body::new(self);
self.bodies = Some(Entity::list_add(*body, self.bodies, self));
body
}
- pub fn delete_body(&mut self, body: own!(Body)) {
+ /// Removes a body from the DCEL.
+ ///
+ /// Requires the body to be empty. Ok is returned on success, if the body is non-empty (has shells), Err is returned
+ ///
+ /// # Examples
+ /// ```
+ /// use dcel::Dcel;
+ ///
+ /// Dcel::<()>::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// dcel.delete_body(body).unwrap();
+ /// })
+ /// ```
+ pub fn delete_body(&mut self, body: own!(Body)) -> Result<(), ()> {
+ if body.maybe_shells(self).is_some() {
+ return Err(());
+ }
self.bodies = Entity::list_remove(*body, self);
body.free(self);
+ Ok(())
}
+ /// Returns an iterator over all bodies in the DCEL.
+ ///
+ /// # Examples
+ /// ```
+ /// use dcel::Dcel;
+ /// use std::iter::once;
+ ///
+ /// Dcel::<()>::new(|mut dcel| {
+ /// let body = dcel.new_body();
+ /// assert!(dcel.iter_bodies().eq(once(body.lens(&dcel))));
+ /// })
+ /// ```
pub fn iter_bodies<'tok>(
&'tok self,
) -> EntityIterator<'tok, 'brand, 'arena, Body<'brand, 'arena, V>> {
EntityIterator::new(self.bodies, self)
}
- // fn new_edge(&mut self, shell: ptr!(Shell)) -> (own!(Edge), [ptr!(HalfEdge); 2]) {}
-
fn follow(&mut self, prev: ptr!(HalfEdge), next: ptr!(HalfEdge)) {
next.set_prev(prev, self);
prev.set_next(next, self);
}
+ /// Make-Edge-Vertex-Vertex-Loop-Face-Shell
+ ///
+ /// This corresponds to MEVVLS in SNUMOD and is the inverse of kevvlfs. Adds a new shell with two vertices connected by an
+ /// edge. Both half edges in the edge form a loop that is the outer loop of a new face.
+ /// No preconditions.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn mevvlfs(
&mut self,
body: ptr!(Body),
@@ -832,6 +1490,14 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Mevvlfs::new(body, data).apply(self)
}
+ /// Kill-Edge-Vertex-Vertex-Loop-Face-Shell
+ ///
+ /// This corresponds to KEVVLS in SNUMOD and is the inverse of mevvlfs. Removes a shell and two vertices connected by an
+ /// edge, as well as a face and it's outer loop. The relationships between the entities
+ /// must be as described in [`Dcel::mevvlfs`], and the vertices, edge, and face must be
+ /// the only ones in the shell.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn kevvlfs(
&mut self,
edge: own!(Edge),
@@ -844,6 +1510,13 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Kevvlfs::new(edge, vertices, loop_, face, shell).apply(self)
}
+ /// Make-Edge-Vertex
+ ///
+ /// This corresponds to MEV in SNUMOD and is the inverse of kev. Adds a vertex connected to an existing vertex via an
+ /// edge going back and forth. Both half-edges of the new edge will be added to the given loop.
+ /// The given vertex must be part of the given loop.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn mev(
&mut self,
loop_: ptr!(Loop),
@@ -853,6 +1526,12 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Mev::new(loop_, vertex, data).apply(self)
}
+ /// Kill-Edge-Vertex
+ ///
+ /// This corresponds to KEV in SNUMOD and is the inverse of mev. Removes a vertex connected to an existing vertex via an
+ /// edge going back and forth. The vertex to be removed must only be connected to one other vertex, using the given edge only.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn kev(
&mut self,
edge: own!(Edge),
@@ -861,6 +1540,12 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Kev::new(edge, vertex).apply(self)
}
+ /// Make-Edge-Loop-Face
+ ///
+ /// This corresponds to MEL in SNUMOD and is the inverse of kelf. Connects two existing vertices both part of the same
+ /// loop, creating a new face (and outer loop)
+ ///
+ /// For examples, see `src/tests.rs`
pub fn melf(
&mut self,
vertices: [ptr!(Vertex); 2],
@@ -869,6 +1554,14 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Melf::new(vertices, loop_).apply(self)
}
+ /// Make-Edge-Loop-Face
+ ///
+ /// This corresponds to KEL in SNUMOD and is the inverse of melf. Removes an existing edge
+ /// between two vertices, joining two loops in the process, removing one of them (must be
+ /// passed in as an owned pointer). The given loop must be the outer loop of the given face,
+ /// which must not have inner loops.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn kelf(
&mut self,
edge: own!(Edge),
@@ -878,6 +1571,12 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Kelf::new(edge, loop_, face).apply(self)
}
+ /// Make-Vertex-Edge
+ ///
+ /// This corresponds to MVE in SNUMOD and is the inverse of kve. Adds a new vertex in the
+ /// middle of an existing edge, splitting the edge in two. No preconditions.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn mve(
&mut self,
edge: ptr!(Edge),
@@ -886,6 +1585,14 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Mve::new(edge, data).apply(self)
}
+ /// Kill-Vertex-Edge
+ ///
+ /// This corresponds to KVE in SNUMOD and is the inverse of mve. Removes a vertex
+ /// that exists between two other vertices using two edges, connected to one of the by the given edge, which
+ /// is removed (joined with the other edge). The vertex must not be connected to any
+ /// other edges.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn kve(
&mut self,
edge: own!(Edge),
@@ -894,6 +1601,13 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Kve::new(edge, vertex).apply(self)
}
+ /// Make-Edge-Kill-Hole
+ ///
+ /// This corresponds to MEKH in SNUMOD and is the inverse of kemh. Two loops that are part of
+ /// the same face are joined by making an edge between two given vertices part of each loop respectively.
+ /// The loop being removed must now be an outer loop, and a loop cannot be joined with itself.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn mekh(
&mut self,
shell: ptr!(Shell),
@@ -905,6 +1619,12 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Mekh::new(shell, into_loop, into_vertex, hole_loop, hole_vertex).apply(self)
}
+ /// Make-Edge-Kill-Hole
+ ///
+ /// This corresponds to KEMH in SNUMOD and is the inverse of mekh. An edge going back and forth
+ /// between two vertices is removed, splitting a loop into two loops, adding a new hole loop.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn kemh(
&mut self,
shell: ptr!(Shell),
@@ -915,6 +1635,12 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Kemh::new(shell, edge, loop_, hole_vertex).apply(self)
}
+ /// Make-Split-Edge-Vertex
+ ///
+ /// This corresponds to MZEV in SNUMOD with the exception of not (necessarily) creating a zero-length edge and is the inverse of ksev. It is similar to MVE, except the involved vertices can be
+ /// part of more than one loop.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn msev(
&mut self,
shell: ptr!(Shell),
@@ -925,6 +1651,12 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Msev::new(shell, vertex, loops, data).apply(self)
}
+ /// Kill-Split-Edge-Vertex
+ ///
+ /// This corresponds to KZEV in SNUMOD with the exception of not (necessarily) killing a zero-length edge and is the inverse of msev. It is similar to KVE, except the involved vertices can be
+ /// part of more than one loop.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn ksev(
&mut self,
shell: ptr!(Shell),
@@ -936,6 +1668,13 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Ksev::new(shell, loops, old_vertex, new_vertex, edge).apply(self)
}
+ /// Make-Peripheral-Kill-Hole
+ ///
+ /// This corresponds to MPKH in SNUMOD and is the inverse of kpmh. An inner (hole) loop is
+ /// converted to the outer loop of a new face. This may potentially result in two disconnected
+ /// manifolds, in which case the shells are split and the newly created shell is returned.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn mpkh(
&mut self,
loop_: ptr!(Loop),
@@ -943,6 +1682,17 @@ impl<'brand, 'arena, V> Dcel<'brand, 'arena, V> {
Mpkh::new(loop_).apply(self)
}
+ /// Kill-Peripheral-Make-Hole
+ ///
+ /// This corresponds to KPMH in SNUMOD and is the inverse of kpmh. An outer (peripheral) loop is
+ /// converted to the inner loop of an existing face, removing its original face.
+ /// This may cause two previously disconnected shells to now be connected, in which case
+ /// they will be merged into one. The shell
+ /// to be removed (which, if applicable, is the shell of the removed face) must be passed
+ /// in if and only if a merge needs to happen. The removed face must not have any inner loops
+ /// and it is not possible to merge shells belonging to different bodies.
+ ///
+ /// For examples, see `src/tests.rs`
pub fn kpmh(
&mut self,
new_shell: Option<own!(Shell)>,
diff --git a/src/mekh.rs b/src/mekh.rs
index 9b23115..8f73845 100644
--- a/src/mekh.rs
+++ b/src/mekh.rs
@@ -1,5 +1,8 @@
use crate::*;
+/// Operator corresponding to MEKH in SNUMOD.
+///
+/// See [`Dcel::mekh`] for details.
pub struct Mekh<'brand, 'arena, V> {
pub shell: ptr!(Shell),
pub into_loop: ptr!(Loop),
@@ -26,6 +29,7 @@ impl<'brand, 'arena, V> Mekh<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Mekh`].
#[derive(Debug, Error)]
pub enum MekhError {
#[error("cannot join loop with itself")]
@@ -107,6 +111,9 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Mekh<'brand, 'arena, V>
}
}
+/// Operator corresponding to KEMH in SNUMOD.
+///
+/// See [`Dcel::kemh`] for details.
pub struct Kemh<'brand, 'arena, V> {
pub shell: ptr!(Shell),
pub edge: own!(Edge),
@@ -114,6 +121,7 @@ pub struct Kemh<'brand, 'arena, V> {
pub hole_vertex: ptr!(Vertex),
}
+/// Precondition Error for [`Kemh`].
#[derive(Error, Debug)]
pub enum KemhError {
#[error("vertex is not part of edge")]
diff --git a/src/melf.rs b/src/melf.rs
index 2b8df3f..0fd818a 100644
--- a/src/melf.rs
+++ b/src/melf.rs
@@ -2,6 +2,9 @@ use crate::*;
// Make Edge-Vertex
+/// Operator corresponding to MEL in SNUMOD.
+///
+/// See [`Dcel::melf`] for details.
pub struct Melf<'brand, 'arena, V> {
pub vertices: [ptr!(Vertex); 2],
pub loop_: ptr!(Loop),
@@ -13,6 +16,7 @@ impl<'brand, 'arena, V> Melf<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Melf`].
#[derive(Debug, Error)]
pub enum MelfError {
#[error("vertex is not part of loop")]
@@ -88,12 +92,16 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Melf<'brand, 'arena, V>
}
}
+/// Operator corresponding to KEL in SNUMOD.
+///
+/// See [`Dcel::kelf`] for details.
pub struct Kelf<'brand, 'arena, V> {
pub edge: own!(Edge),
pub loop_: own!(Loop),
pub face: own!(Face),
}
+/// Precondition Error for [`Kelf`].
impl<'brand, 'arena, V> Kelf<'brand, 'arena, V> {
pub fn new(edge: own!(Edge), loop_: own!(Loop), face: own!(Face)) -> Self {
Self { edge, loop_, face }
diff --git a/src/mev.rs b/src/mev.rs
index 5de1ce8..a0eeac7 100644
--- a/src/mev.rs
+++ b/src/mev.rs
@@ -2,6 +2,9 @@ use crate::*;
// Make Edge-Vertex
+/// Operator corresponding to MEV in SNUMOD.
+///
+/// See [`Dcel::mev`] for details.
pub struct Mev<'brand, 'arena, V> {
pub loop_: ptr!(Loop),
pub vertex: ptr!(Vertex),
@@ -18,6 +21,7 @@ impl<'brand, 'arena, V> Mev<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Mev`].
#[derive(Debug, Error)]
pub enum MevError {
#[error("vertex is not part of loop")]
@@ -72,6 +76,9 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Mev<'brand, 'arena, V> {
}
}
+/// Operator corresponding to KEV in SNUMOD.
+///
+/// See [`Dcel::kev`] for details.
pub struct Kev<'brand, 'arena, V> {
pub edge: own!(Edge),
pub vertex: own!(Vertex),
@@ -83,6 +90,7 @@ impl<'brand, 'arena, V> Kev<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Kev`].
#[derive(Debug, Error)]
pub enum KevError {
#[error("half edges don't go back and forth between new and old vertex")]
diff --git a/src/mevvlfs.rs b/src/mevvlfs.rs
index 318a91b..92a11f1 100644
--- a/src/mevvlfs.rs
+++ b/src/mevvlfs.rs
@@ -2,6 +2,9 @@ use crate::*;
// Make Edge-Vertex-Vertex-Loop-Face-Shell
+/// Operator corresponding to MEVVLS in SNUMOD.
+///
+/// See [`Dcel::mevvlfs`] for details.
pub struct Mevvlfs<'brand, 'arena, V> {
pub body: ptr!(Body),
pub data: [V; 2],
@@ -59,6 +62,9 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Mevvlfs<'brand, 'arena,
}
}
+/// Operator corresponding to KEVVLS in SNUMOD.
+///
+/// See [`Dcel::kevvlfs`] for details.
pub struct Kevvlfs<'brand, 'arena, V> {
pub edge: own!(Edge),
pub vertices: [own!(Vertex); 2],
@@ -67,6 +73,7 @@ pub struct Kevvlfs<'brand, 'arena, V> {
pub shell: own!(Shell),
}
+/// Precondition Error for [`Kevvlfs`].
#[derive(Debug, Error)]
pub enum KevvlfsError {
#[error("edge vertices do not equal vertices")]
diff --git a/src/mpkh.rs b/src/mpkh.rs
index e19bd42..b55f298 100644
--- a/src/mpkh.rs
+++ b/src/mpkh.rs
@@ -72,6 +72,9 @@ fn maybe_split<'brand, 'arena, V>(
Some(new_shell)
}
+/// Operator corresponding to MPKH in SNUMOD.
+///
+/// See [`Dcel::mpkh`] for details.
pub struct Mpkh<'brand, 'arena, V> {
pub loop_: ptr!(Loop),
}
@@ -82,6 +85,7 @@ impl<'brand, 'arena, V> Mpkh<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Mpkh`].
#[derive(Debug, Error)]
pub enum MpkhError {
#[error("loop is not an inner loop")]
@@ -131,12 +135,16 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Mpkh<'brand, 'arena, V>
}
}
+/// Operator corresponding to KPMH in SNUMOD.
+///
+/// See [`Dcel::kpmh`] for details.
pub struct Kpmh<'brand, 'arena, V> {
pub new_shell: Option<own!(Shell)>,
pub old_face: ptr!(Face),
pub new_face: own!(Face),
}
+/// Precondition Error for [`Kpmh`].
#[derive(Error, Debug)]
pub enum KpmhError {
#[error("face does not match shell")]
diff --git a/src/msev.rs b/src/msev.rs
index 16d18e4..945b729 100644
--- a/src/msev.rs
+++ b/src/msev.rs
@@ -2,6 +2,9 @@
use crate::*;
+/// Operator corresponding to MZEV in SNUMOD.
+///
+/// See [`Dcel::msev`] for details.
pub struct Msev<'brand, 'arena, V> {
pub shell: ptr!(Shell),
pub vertex: ptr!(Vertex),
@@ -20,6 +23,7 @@ impl<'brand, 'arena, V> Msev<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Msev`].
#[derive(Debug, Error)]
pub enum MsevError {
#[error("vertex is not part of loop")]
@@ -81,6 +85,9 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Msev<'brand, 'arena, V>
}
}
+/// Operator corresponding to KZEV in SNUMOD.
+///
+/// See [`Dcel::ksev`] for details.
pub struct Ksev<'brand, 'arena, V> {
pub shell: ptr!(Shell),
pub loops: [ptr!(Loop); 2],
@@ -107,6 +114,7 @@ impl<'brand, 'arena, V> Ksev<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Ksev`].
#[derive(Debug, Error)]
pub enum KsevError {
#[error("edge does not match vertices")]
diff --git a/src/mve.rs b/src/mve.rs
index 19785d9..6fc326c 100644
--- a/src/mve.rs
+++ b/src/mve.rs
@@ -2,6 +2,9 @@ use crate::*;
// Make Vertex-Edge
+/// Operator corresponding to MVE in SNUMOD.
+///
+/// See [`Dcel::mve`] for details.
pub struct Mve<'brand, 'arena, V> {
pub edge: ptr!(Edge),
pub data: V,
@@ -87,6 +90,9 @@ impl<'brand, 'arena, V> Operator<'brand, 'arena, V> for Mve<'brand, 'arena, V> {
}
}
+/// Operator corresponding to KVE in SNUMOD.
+///
+/// See [`Dcel::kve`] for details.
pub struct Kve<'brand, 'arena, V> {
pub edge: own!(Edge),
pub vertex: own!(Vertex),
@@ -98,6 +104,7 @@ impl<'brand, 'arena, V> Kve<'brand, 'arena, V> {
}
}
+/// Precondition Error for [`Kve`].
#[derive(Debug, Error)]
pub enum KveError {
#[error("vertex is not part of edge")]