diff --git a/benches/benches/triangulate/quads.rs b/benches/benches/triangulate/quads.rs index 5af75963..156774d3 100644 --- a/benches/benches/triangulate/quads.rs +++ b/benches/benches/triangulate/quads.rs @@ -1,6 +1,6 @@ use criterion::{Criterion, black_box, criterion_group, criterion_main}; use honeycomb::prelude::{ - CMap2, CMapBuilder, DartIdType, Orbit2, OrbitPolicy, + CMap2, CMapBuilder, DartIdType, OrbitPolicy, triangulation::{TriangulateError, earclip_cell, fan_cell}, }; @@ -15,7 +15,7 @@ fn fan_bench() -> Result<(), TriangulateError> { let faces: Vec<_> = map.iter_faces().collect(); let n_darts_per_face: Vec<_> = faces .iter() - .map(|id| (Orbit2::new(&map, OrbitPolicy::Face, *id as DartIdType).count() - 3) * 2) + .map(|id| (map.orbit(OrbitPolicy::Face, *id as DartIdType).count() - 3) * 2) .collect(); let n_tot: usize = n_darts_per_face.iter().sum(); let tmp = map.add_free_darts(n_tot) as usize; @@ -49,7 +49,7 @@ fn earclip_bench() -> Result<(), TriangulateError> { let faces: Vec<_> = map.iter_faces().collect(); let n_darts_per_face: Vec<_> = faces .iter() - .map(|id| (Orbit2::new(&map, OrbitPolicy::Face, *id as DartIdType).count() - 3) * 2) + .map(|id| (map.orbit(OrbitPolicy::Face, *id as DartIdType).count() - 3) * 2) .collect(); let n_tot: usize = n_darts_per_face.iter().sum(); let tmp = map.add_free_darts(n_tot) as usize; diff --git a/benches/src/cut_edges.rs b/benches/src/cut_edges.rs index 656d3868..aa544adf 100644 --- a/benches/src/cut_edges.rs +++ b/benches/src/cut_edges.rs @@ -38,10 +38,10 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { }; #[cfg(debug_assertions)] // check input { - use honeycomb::prelude::{Orbit2, OrbitPolicy}; + use honeycomb::prelude::OrbitPolicy; assert!( map.iter_faces() - .all(|f| { Orbit2::new(&map, OrbitPolicy::Face, f as DartIdType).count() == 3 }), + .all(|f| { map.orbit(OrbitPolicy::Face, f as DartIdType).count() == 3 }), "Input mesh isn't a triangle mesh" ); } diff --git a/benches/src/grid_gen.rs b/benches/src/grid_gen.rs index c91def88..9dd10a65 100644 --- a/benches/src/grid_gen.rs +++ b/benches/src/grid_gen.rs @@ -2,7 +2,7 @@ use std::time::{Duration, Instant}; use honeycomb::{ core::stm::atomically_with_err, - prelude::{CMap2, CMapBuilder, CoordsFloat, DartIdType, GridDescriptor, Orbit2, OrbitPolicy}, + prelude::{CMap2, CMapBuilder, CoordsFloat, DartIdType, GridDescriptor, OrbitPolicy}, }; use rand::{ SeedableRng, @@ -57,7 +57,7 @@ fn split_faces_randomly( .par_bridge() .for_each(|((df, sl), split)| { let square = df as DartIdType; - assert_eq!(Orbit2::new(map, OrbitPolicy::FaceLinear, df).count(), 4); + assert_eq!(map.orbit(OrbitPolicy::FaceLinear, df).count(), 4); let (ddown, dright, dup, dleft) = (square, square + 1, square + 2, square + 3); let &[dsplit1, dsplit2] = sl else { diff --git a/benches/src/shift.rs b/benches/src/shift.rs index bc842154..cb4a4373 100644 --- a/benches/src/shift.rs +++ b/benches/src/shift.rs @@ -20,7 +20,7 @@ use rayon::prelude::*; use honeycomb::core::stm::{Transaction, TransactionControl}; use honeycomb::prelude::{ - CMap2, CMapBuilder, CoordsFloat, DartIdType, NULL_DART_ID, Orbit2, OrbitPolicy, VertexIdType, + CMap2, CMapBuilder, CoordsFloat, DartIdType, NULL_DART_ID, OrbitPolicy, VertexIdType, }; use crate::cli::ShiftArgs; @@ -49,14 +49,15 @@ pub fn bench_shift(args: ShiftArgs) -> CMap2 { let tmp: Vec<(VertexIdType, Vec)> = map .iter_vertices() .filter_map(|v| { - if Orbit2::new(&map, OrbitPolicy::Vertex, v as DartIdType) + if map + .orbit(OrbitPolicy::Vertex, v as DartIdType) .any(|d| map.beta::<2>(d) == NULL_DART_ID) { None } else { Some(( v, - Orbit2::new(&map, OrbitPolicy::Vertex, v as DartIdType) + map.orbit(OrbitPolicy::Vertex, v as DartIdType) .map(|d| map.vertex_id(map.beta::<2>(d))) .collect(), )) diff --git a/examples/examples/parallel_shift.rs b/examples/examples/parallel_shift.rs index 3c8e743c..3b32516f 100644 --- a/examples/examples/parallel_shift.rs +++ b/examples/examples/parallel_shift.rs @@ -22,7 +22,7 @@ //! use honeycomb_core::cmap::{ - CMap2, CMapBuilder, DartIdType, NULL_DART_ID, Orbit2, OrbitPolicy, VertexIdType, + CMap2, CMapBuilder, DartIdType, NULL_DART_ID, OrbitPolicy, VertexIdType, }; use honeycomb_core::geometry::Vertex2; use honeycomb_core::stm::atomically; @@ -51,7 +51,8 @@ fn main() { .iter_vertices() .filter_map(|v| { // the condition detects if we're on the boundary - if Orbit2::new(&map, OrbitPolicy::Vertex, v as DartIdType) + if map + .orbit(OrbitPolicy::Vertex, v as DartIdType) .any(|d| map.beta::<2>(d) == NULL_DART_ID) { None @@ -59,7 +60,7 @@ fn main() { // the orbit transformation yields neighbor IDs Some(( v, - Orbit2::new(&map, OrbitPolicy::Vertex, v as DartIdType) + map.orbit(OrbitPolicy::Vertex, v as DartIdType) .map(|d| map.vertex_id(map.beta::<2>(d))) .collect(), )) diff --git a/honeycomb-core/src/cmap/builder/tests.rs b/honeycomb-core/src/cmap/builder/tests.rs index 556ed6aa..c1c6ef4e 100644 --- a/honeycomb-core/src/cmap/builder/tests.rs +++ b/honeycomb-core/src/cmap/builder/tests.rs @@ -1,7 +1,7 @@ use vtkio::Vtk; use crate::attributes::AttrStorageManager; -use crate::cmap::{CMap2, CMapBuilder, DartIdType, GridDescriptor, Orbit2, OrbitPolicy}; +use crate::cmap::{CMap2, CMapBuilder, DartIdType, GridDescriptor, OrbitPolicy}; // --- basic @@ -499,7 +499,7 @@ mod vtk { let mut n_vertices_per_face: Vec = faces .iter() - .map(|id| Orbit2::new(&cmap, OrbitPolicy::Face, *id as DartIdType).count()) + .map(|id| cmap.orbit(OrbitPolicy::Face, *id as DartIdType).count()) .collect(); let (mut three_count, mut four_count, mut six_count): (usize, usize, usize) = (0, 0, 0); while let Some(n) = n_vertices_per_face.pop() { diff --git a/honeycomb-core/src/cmap/dim2/basic_ops.rs b/honeycomb-core/src/cmap/dim2/basic_ops.rs index b0a39f77..ed8b2a75 100644 --- a/honeycomb-core/src/cmap/dim2/basic_ops.rs +++ b/honeycomb-core/src/cmap/dim2/basic_ops.rs @@ -11,9 +11,7 @@ use std::cell::RefCell; use std::collections::{HashSet, VecDeque}; use crate::attributes::UnknownAttributeStorage; -use crate::cmap::{ - CMap2, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, Orbit2, OrbitPolicy, VertexIdType, -}; +use crate::cmap::{CMap2, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, VertexIdType}; use crate::geometry::CoordsFloat; use crate::stm::{StmClosureResult, Transaction, atomically}; @@ -396,27 +394,6 @@ impl CMap2 { }) } - /// Return the orbit defined by a dart and its `I`-cell. - /// - /// # Usage - /// - /// The [`Orbit2`] can be iterated upon to retrieve all dart member of the cell. Note that - /// **the dart passed as an argument is included as the first element of the returned orbit**. - /// - /// # Panics - /// - /// The method will panic if *I* is not 0, 1 or 2. - #[must_use = "unused return value"] - pub fn i_cell(&self, dart_id: DartIdType) -> Orbit2 { - assert!(I < 3); - match I { - 0 => Orbit2::<'_, T>::new(self, OrbitPolicy::Vertex, dart_id), - 1 => Orbit2::<'_, T>::new(self, OrbitPolicy::Edge, dart_id), - 2 => Orbit2::<'_, T>::new(self, OrbitPolicy::Face, dart_id), - _ => unreachable!(), - } - } - /// Return an iterator over IDs of all the map's vertices. #[must_use = "unused return value"] pub fn iter_vertices(&self) -> impl Iterator + '_ { diff --git a/honeycomb-core/src/cmap/dim2/orbits.rs b/honeycomb-core/src/cmap/dim2/orbits.rs index a51179f7..af2aeb04 100644 --- a/honeycomb-core/src/cmap/dim2/orbits.rs +++ b/honeycomb-core/src/cmap/dim2/orbits.rs @@ -3,284 +3,137 @@ //! This module contains all code used to model orbits, a notion defined //! along the structure of combinatorial maps. -use std::collections::{BTreeSet, VecDeque}; +use std::collections::{HashSet, VecDeque}; use crate::cmap::{CMap2, DartIdType, NULL_DART_ID, OrbitPolicy}; use crate::geometry::CoordsFloat; -/// # Generic 2D orbit implementation -/// -/// This structure only contains meta-data about the orbit in its initial state. All the darts -/// making up the orbit are computed when using the methods that come with the [Iterator] -/// implementation. -/// -/// It is not currently possible to iterate over references, the orbit has to be consumed for its -/// result to be used. This is most likely the best behavior since orbits should be consumed upon -/// traversal to avoid inconsistencies created by a later mutable operation on the map. -/// -/// ## Generics -/// -/// - `'a` -- Lifetime of the reference to the map -/// - `T: CoordsFloat` -- Generic parameter of the referenced map. -/// -/// ## The search algorithm -/// -/// The search algorithm used to establish the list of dart included in the orbit is a -/// [Breadth-First Search algorithm][WIKIBFS]. This means that: -/// -/// - we look at the images of the current dart through all beta functions, -/// adding those to a queue, before moving on to the next dart. -/// - we apply the beta functions in their specified order; This guarantees a consistent and -/// predictable result. -/// -/// [WIKIBFS]: https://en.wikipedia.org/wiki/Breadth-first_search -pub struct Orbit2<'a, T: CoordsFloat> { - /// Reference to the map containing the beta functions used in the BFS. - map_handle: &'a CMap2, - /// Policy used by the orbit for the BFS. It can be predetermined or custom. - orbit_policy: OrbitPolicy, - /// Set used to identify which dart is marked during the BFS. - marked: BTreeSet, - /// Queue used to store which dart must be visited next during the BFS. - pending: VecDeque, -} - -impl<'a, T: CoordsFloat> Orbit2<'a, T> { - /// Constructor +/// **Orbits** +impl CMap2 { + /// Generic orbit implementation. /// /// # Arguments + /// - `opolicy: OrbitPolicy` -- Policy used by the orbit for the BFS. + /// - `dart_id: DartIdentifier` -- Dart of which the structure will compute the orbit. + /// + /// # The search algorithm + /// + /// The search algorithm used to establish the list of dart included in the orbit is a + /// [Breadth-First Search algorithm][WIKIBFS]. This means that: /// - /// - `map_handle: &'a CMap2` -- Reference to the map containing the beta - /// functions used in the BFS. - /// - `orbit_policy: OrbitPolicy` -- Policy used by the orbit for the BFS. - /// - `dart: DartIdentifier` -- Dart of which the structure will compute the orbit. + /// - we look at the images of the current dart through all beta functions, + /// adding those to a queue, before moving on to the next dart. + /// - we apply the beta functions in their specified order; This guarantees a consistent and + /// predictable result. /// /// # Performance /// /// Currently, orbits use two dynamically allocated structures for computation: a `VecDeque`, - /// and a `BTreeSet`. Allocations are made by the constructor since these structures aren't - /// empty initially. - #[must_use = "unused return value"] - pub fn new(map_handle: &'a CMap2, orbit_policy: OrbitPolicy, dart: DartIdType) -> Self { - let mut marked = BTreeSet::::new(); - marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit - marked.insert(dart); // we're starting here, so we mark it beforehand - let pending = VecDeque::from([dart]); - /* - if let OrbitPolicy::Custom(slice) = orbit_policy { - assert!(!slice.len().is_zero()); - } - */ - Self { - map_handle, - orbit_policy, - marked, - pending, - } - } -} - -impl Iterator for Orbit2<'_, T> { - type Item = DartIdType; - - fn next(&mut self) -> Option { - if let Some(d) = self.pending.pop_front() { - match self.orbit_policy { - OrbitPolicy::Vertex => { - // THIS CODE IS ONLY VALID IN 2D - let image1 = self.map_handle.beta::<1>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image1) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<2>(self.map_handle.beta::<0>(d)); - if self.marked.insert(image2) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image2); - } - } - OrbitPolicy::VertexLinear => { - // THIS CODE IS ONLY VALID IN 2D - let image = self.map_handle.beta::<1>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image); + /// and a `HashSet`. There is a possibility to use static thread-local instances to avoid + /// ephemeral allocations, but [it would require a guard mechanism][PR]. + /// + /// [PR]: https://github.com/LIHPC-Computational-Geometry/honeycomb/pull/293 + #[allow(clippy::needless_for_each)] + #[rustfmt::skip] + pub fn orbit( + &self, + opolicy: OrbitPolicy, + dart_id: DartIdType, + ) -> impl Iterator { + let mut pending = VecDeque::new(); + let mut marked: HashSet = HashSet::new(); + pending.push_back(dart_id); + marked.insert(NULL_DART_ID); + marked.insert(dart_id); // we're starting here, so we mark it beforehand + + // FIXME: move the match block out of the iterator + std::iter::from_fn(move || { + if let Some(d) = pending.pop_front() { + // compute the next images + match opolicy { + OrbitPolicy::Vertex => { + [ + self.beta::<1>(self.beta::<2>(d)), + self.beta::<2>(self.beta::<0>(d)), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + // if true, we did not see this dart yet + // i.e. we need to visit it later + pending.push_back(im); + } + }); } - } - OrbitPolicy::Edge => { - // THIS CODE IS ONLY VALID IN 2D - let image = self.map_handle.beta::<2>(d); - if self.marked.insert(image) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image); + OrbitPolicy::VertexLinear => { + let im = self.beta::<1>(self.beta::<2>(d)); + if marked.insert(im) { + pending.push_back(im); + } } - } - OrbitPolicy::Face => { - // THIS CODE IS ONLY VALID IN 2D - let image1 = self.map_handle.beta::<1>(d); - if self.marked.insert(image1) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image1); + OrbitPolicy::Edge => { + let im = self.beta::<2>(d); + if marked.insert(im) { + pending.push_back(im); + } } - let image2 = self.map_handle.beta::<0>(d); - if self.marked.insert(image2) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image2); + OrbitPolicy::Face => { + [ + self.beta::<1>(d), + self.beta::<0>(d), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + // if true, we did not see this dart yet + // i.e. we need to visit it later + pending.push_back(im); + } + }); } - } - OrbitPolicy::FaceLinear => { - // THIS CODE IS ONLY VALID IN 2D - let image = self.map_handle.beta::<1>(d); - if self.marked.insert(image) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image); + OrbitPolicy::FaceLinear => { + let im = self.beta::<1>(d); + if marked.insert(im) { + pending.push_back(im); + } } - } - OrbitPolicy::Custom(beta_slice) => { - for beta_id in beta_slice { - let image = self.map_handle.beta_rt(*beta_id, d); - if self.marked.insert(image) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image); + OrbitPolicy::Custom(beta_slice) => { + for beta_id in beta_slice { + let image = self.beta_rt(*beta_id, d); + if marked.insert(image) { + pending.push_back(image); + } } } + OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => { + unimplemented!("3-cells aren't defined for 2-maps") + } } - OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => { - unimplemented!("3-cells aren't defined for 2-maps") - } - } - Some(d) - } else { - None - } - } -} - -// ------ TESTS - -#[allow(unused_mut)] -#[cfg(test)] -mod tests { - use super::*; - - fn simple_map() -> CMap2 { - let mut map: CMap2 = CMap2::new(11); - // tri1 - map.force_link::<1>(1, 2).unwrap(); - map.force_link::<1>(2, 3).unwrap(); - map.force_link::<1>(3, 1).unwrap(); - // tri2 - map.force_link::<1>(4, 5).unwrap(); - map.force_link::<1>(5, 6).unwrap(); - map.force_link::<1>(6, 4).unwrap(); - // pent on top - map.force_link::<1>(7, 8).unwrap(); - map.force_link::<1>(8, 9).unwrap(); - map.force_link::<1>(9, 10).unwrap(); - map.force_link::<1>(10, 11).unwrap(); - map.force_link::<1>(11, 7).unwrap(); - - // link all - map.force_link::<2>(2, 4).unwrap(); - map.force_link::<2>(6, 7).unwrap(); - - assert!(map.force_write_vertex(1, (0.0, 0.0)).is_none()); - assert!(map.force_write_vertex(2, (1.0, 0.0)).is_none()); - assert!(map.force_write_vertex(6, (1.0, 1.0)).is_none()); - assert!(map.force_write_vertex(3, (0.0, 1.0)).is_none()); - assert!(map.force_write_vertex(9, (1.5, 1.5)).is_none()); - assert!(map.force_write_vertex(10, (0.5, 2.0)).is_none()); - assert!(map.force_write_vertex(11, (-0.5, 1.5)).is_none()); - - map - } - - #[test] - fn full_map_from_orbit() { - let map = simple_map(); - let orbit = Orbit2::new(&map, OrbitPolicy::Custom(&[1, 2]), 3); - let darts: Vec = orbit.collect(); - assert_eq!(darts.len(), 11); - // because the algorithm is consistent, we can predict the exact layout - assert_eq!(&darts, &[3, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11]); - } - - #[test] - fn orbit_variants() { - let map = simple_map(); - - // face is complete, so everything works - let face: Vec = Orbit2::new(&map, OrbitPolicy::Face, 7).collect(); - let face_linear: Vec = Orbit2::new(&map, OrbitPolicy::FaceLinear, 7).collect(); - let face_custom: Vec = - Orbit2::new(&map, OrbitPolicy::Custom(&[0, 1]), 7).collect(); - assert_eq!(&face, &[7, 8, 11, 9, 10]); - assert_eq!(&face_linear, &[7, 8, 9, 10, 11]); - assert_eq!(&face_custom, &[7, 11, 8, 10, 9]); - - // vertex is incomplete, so using the linear variant will yield an incomplete orbit - let vertex: Vec = Orbit2::new(&map, OrbitPolicy::Vertex, 4).collect(); - let vertex_linear: Vec = - Orbit2::new(&map, OrbitPolicy::VertexLinear, 4).collect(); - assert_eq!(&vertex, &[4, 3, 7]); - assert_eq!(&vertex_linear, &[4, 3]); - } - #[test] - fn face_from_orbit() { - let map = simple_map(); - let face_orbit = Orbit2::new(&map, OrbitPolicy::Face, 1); - let darts: Vec = face_orbit.collect(); - assert_eq!(darts.len(), 3); - assert_eq!(&darts, &[1, 2, 3]); - let other_face_orbit = Orbit2::new(&map, OrbitPolicy::Custom(&[1]), 5); - let other_darts: Vec = other_face_orbit.collect(); - assert_eq!(other_darts.len(), 3); - assert_eq!(&other_darts, &[5, 6, 4]); - } - - #[test] - fn edge_from_orbit() { - let map = simple_map(); - let face_orbit = Orbit2::new(&map, OrbitPolicy::Edge, 1); - let darts: Vec = face_orbit.collect(); - assert_eq!(darts.len(), 1); - assert_eq!(&darts, &[1]); // dart 1 is on the boundary - let other_face_orbit = Orbit2::new(&map, OrbitPolicy::Custom(&[2]), 4); - let other_darts: Vec = other_face_orbit.collect(); - assert_eq!(other_darts.len(), 2); - assert_eq!(&other_darts, &[4, 2]); - } - - #[test] - fn vertex_from_orbit() { - let map = simple_map(); - let orbit = Orbit2::new(&map, OrbitPolicy::Vertex, 4); - let darts: Vec = orbit.collect(); - assert_eq!(darts.len(), 3); - assert_eq!(&darts, &[4, 3, 7]); - } - - #[test] - fn empty_orbit_policy() { - let map = simple_map(); - let darts: Vec = Orbit2::new(&map, OrbitPolicy::Custom(&[]), 3).collect(); - assert_eq!(&darts, &[3]); + return Some(d); + } + None // queue is empty, we're done + }) } - #[test] - #[should_panic(expected = "assertion failed: i < 3")] - fn invalid_orbit_policy() { - let map = simple_map(); - let orbit = Orbit2::new(&map, OrbitPolicy::Custom(&[6]), 3); - let _: Vec = orbit.collect(); + /// Return the orbit defined by a dart and its `I`-cell. + /// + /// # Usage + /// + /// The [`Orbit2`] can be iterated upon to retrieve all dart member of the cell. Note that + /// **the dart passed as an argument is included as the first element of the returned orbit**. + /// + /// # Panics + /// + /// The method will panic if *I* is not 0, 1 or 2. + #[must_use = "unused return value"] + pub fn i_cell(&self, dart_id: DartIdType) -> impl Iterator { + assert!(I < 3); + match I { + 0 => self.orbit(OrbitPolicy::Vertex, dart_id), + 1 => self.orbit(OrbitPolicy::Edge, dart_id), + 2 => self.orbit(OrbitPolicy::Face, dart_id), + _ => unreachable!(), + } } } diff --git a/honeycomb-core/src/cmap/dim2/serialize.rs b/honeycomb-core/src/cmap/dim2/serialize.rs index c48f000f..b22ad94c 100644 --- a/honeycomb-core/src/cmap/dim2/serialize.rs +++ b/honeycomb-core/src/cmap/dim2/serialize.rs @@ -8,7 +8,7 @@ use vtkio::{ }; use crate::cmap::{ - CMap2, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, Orbit2, OrbitPolicy, VertexIdType, + CMap2, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, OrbitPolicy, VertexIdType, }; use crate::geometry::CoordsFloat; @@ -187,7 +187,8 @@ where let face_data = face_ids.into_iter().map(|id| { let mut count: u32 = 0; // VecDeque will be useful later - let orbit: Vec = Orbit2::new(map, OrbitPolicy::Custom(&[1]), id as DartIdType) + let orbit: Vec = map + .orbit(OrbitPolicy::Custom(&[1]), id as DartIdType) .map(|dart_id| { count += 1; id_map[&map.vertex_id(dart_id)] as u32 diff --git a/honeycomb-core/src/cmap/dim2/structure.rs b/honeycomb-core/src/cmap/dim2/structure.rs index ce6b5141..24185e41 100644 --- a/honeycomb-core/src/cmap/dim2/structure.rs +++ b/honeycomb-core/src/cmap/dim2/structure.rs @@ -44,7 +44,7 @@ use super::CMAP2_BETA; /// ``` /// # fn main() { /// use honeycomb_core::{ -/// cmap::{CMap2, CMapBuilder, Orbit2, OrbitPolicy}, +/// cmap::{CMap2, CMapBuilder, OrbitPolicy}, /// geometry::Vertex2 /// }; /// @@ -58,11 +58,13 @@ use super::CMAP2_BETA; /// 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); +/// { +/// let mut face = map.orbit(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 (B) /// let first_added_dart_id = map.add_free_darts(3); diff --git a/honeycomb-core/src/cmap/dim2/tests.rs b/honeycomb-core/src/cmap/dim2/tests.rs index d1338d53..cfdbd358 100644 --- a/honeycomb-core/src/cmap/dim2/tests.rs +++ b/honeycomb-core/src/cmap/dim2/tests.rs @@ -1,6 +1,6 @@ use crate::{ attributes::{AttrSparseVec, AttributeBind, AttributeError, AttributeUpdate}, - cmap::{CMap2, CMapBuilder, LinkError, Orbit2, OrbitPolicy, SewError, VertexIdType}, + cmap::{CMap2, CMapBuilder, DartIdType, LinkError, OrbitPolicy, SewError, VertexIdType}, geometry::Vertex2, stm::{StmError, TransactionError, atomically, atomically_with_err}, }; @@ -22,11 +22,13 @@ fn example_test() { let faces: Vec<_> = map.iter_faces().collect(); assert_eq!(faces.len(), 1); assert_eq!(faces[0], 1); - 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); + { + let mut face = map.orbit(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 map.add_free_darts(3); @@ -40,11 +42,13 @@ fn example_test() { // checks let faces: Vec<_> = map.iter_faces().collect(); assert_eq!(&faces, &[1, 4]); - let mut face = Orbit2::new(&map, OrbitPolicy::Face, 4); - assert_eq!(face.next(), Some(4)); - assert_eq!(face.next(), Some(5)); - assert_eq!(face.next(), Some(6)); - assert_eq!(face.next(), None); + { + let mut face = map.orbit(OrbitPolicy::Face, 4); + assert_eq!(face.next(), Some(4)); + assert_eq!(face.next(), Some(5)); + assert_eq!(face.next(), Some(6)); + assert_eq!(face.next(), None); + } // sew both triangles map.force_sew::<2>(2, 4).unwrap(); @@ -123,11 +127,13 @@ fn example_test_transactional() { let faces: Vec<_> = map.iter_faces().collect(); assert_eq!(faces.len(), 1); assert_eq!(faces[0], 1); - 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); + { + let mut face = map.orbit(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 map.add_free_darts(3); @@ -145,11 +151,13 @@ fn example_test_transactional() { // checks let faces: Vec<_> = map.iter_faces().collect(); assert_eq!(&faces, &[1, 4]); - let mut face = Orbit2::new(&map, OrbitPolicy::Face, 4); - assert_eq!(face.next(), Some(4)); - assert_eq!(face.next(), Some(5)); - assert_eq!(face.next(), Some(6)); - assert_eq!(face.next(), None); + { + let mut face = map.orbit(OrbitPolicy::Face, 4); + assert_eq!(face.next(), Some(4)); + assert_eq!(face.next(), Some(5)); + assert_eq!(face.next(), Some(6)); + assert_eq!(face.next(), None); + } // sew both triangles atomically(|trans| { @@ -421,6 +429,119 @@ fn one_sew_no_attributes() { )))); } +// --- ORBITS + +fn simple_map() -> CMap2 { + let mut map: CMap2 = CMap2::new(11); + // tri1 + map.force_link::<1>(1, 2).unwrap(); + map.force_link::<1>(2, 3).unwrap(); + map.force_link::<1>(3, 1).unwrap(); + // tri2 + map.force_link::<1>(4, 5).unwrap(); + map.force_link::<1>(5, 6).unwrap(); + map.force_link::<1>(6, 4).unwrap(); + // pent on top + map.force_link::<1>(7, 8).unwrap(); + map.force_link::<1>(8, 9).unwrap(); + map.force_link::<1>(9, 10).unwrap(); + map.force_link::<1>(10, 11).unwrap(); + map.force_link::<1>(11, 7).unwrap(); + + // link all + map.force_link::<2>(2, 4).unwrap(); + map.force_link::<2>(6, 7).unwrap(); + + assert!(map.force_write_vertex(1, (0.0, 0.0)).is_none()); + assert!(map.force_write_vertex(2, (1.0, 0.0)).is_none()); + assert!(map.force_write_vertex(6, (1.0, 1.0)).is_none()); + assert!(map.force_write_vertex(3, (0.0, 1.0)).is_none()); + assert!(map.force_write_vertex(9, (1.5, 1.5)).is_none()); + assert!(map.force_write_vertex(10, (0.5, 2.0)).is_none()); + assert!(map.force_write_vertex(11, (-0.5, 1.5)).is_none()); + + map +} + +#[test] +fn full_map_from_orbit() { + let map = simple_map(); + let orbit = map.orbit(OrbitPolicy::Custom(&[1, 2]), 3); + let darts: Vec = orbit.collect(); + assert_eq!(darts.len(), 11); + // because the algorithm is consistent, we can predict the exact layout + assert_eq!(&darts, &[3, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11]); +} + +#[test] +fn orbit_variants() { + let map = simple_map(); + + // face is complete, so everything works + let face: Vec = map.orbit(OrbitPolicy::Face, 7).collect(); + let face_linear: Vec = map.orbit(OrbitPolicy::FaceLinear, 7).collect(); + let face_custom: Vec = map.orbit(OrbitPolicy::Custom(&[0, 1]), 7).collect(); + assert_eq!(&face, &[7, 8, 11, 9, 10]); + assert_eq!(&face_linear, &[7, 8, 9, 10, 11]); + assert_eq!(&face_custom, &[7, 11, 8, 10, 9]); + + // vertex is incomplete, so using the linear variant will yield an incomplete orbit + let vertex: Vec = map.orbit(OrbitPolicy::Vertex, 4).collect(); + let vertex_linear: Vec = map.orbit(OrbitPolicy::VertexLinear, 4).collect(); + assert_eq!(&vertex, &[4, 3, 7]); + assert_eq!(&vertex_linear, &[4, 3]); +} + +#[test] +fn face_from_orbit() { + let map = simple_map(); + let face_orbit = map.orbit(OrbitPolicy::Face, 1); + let darts: Vec = face_orbit.collect(); + assert_eq!(darts.len(), 3); + assert_eq!(&darts, &[1, 2, 3]); + let other_face_orbit = map.orbit(OrbitPolicy::Custom(&[1]), 5); + let other_darts: Vec = other_face_orbit.collect(); + assert_eq!(other_darts.len(), 3); + assert_eq!(&other_darts, &[5, 6, 4]); +} + +#[test] +fn edge_from_orbit() { + let map = simple_map(); + let face_orbit = map.orbit(OrbitPolicy::Edge, 1); + let darts: Vec = face_orbit.collect(); + assert_eq!(darts.len(), 1); + assert_eq!(&darts, &[1]); // dart 1 is on the boundary + let other_face_orbit = map.orbit(OrbitPolicy::Custom(&[2]), 4); + let other_darts: Vec = other_face_orbit.collect(); + assert_eq!(other_darts.len(), 2); + assert_eq!(&other_darts, &[4, 2]); +} + +#[test] +fn vertex_from_orbit() { + let map = simple_map(); + let orbit = map.orbit(OrbitPolicy::Vertex, 4); + let darts: Vec = orbit.collect(); + assert_eq!(darts.len(), 3); + assert_eq!(&darts, &[4, 3, 7]); +} + +#[test] +fn empty_orbit_policy() { + let map = simple_map(); + let darts: Vec = map.orbit(OrbitPolicy::Custom(&[]), 3).collect(); + assert_eq!(&darts, &[3]); +} + +#[test] +#[should_panic(expected = "assertion failed: i < 3")] +fn invalid_orbit_policy() { + let map = simple_map(); + let orbit = map.orbit(OrbitPolicy::Custom(&[6]), 3); + let _: Vec = orbit.collect(); +} + // --- IO #[test] @@ -560,7 +681,7 @@ fn sew_ordering() { assert!(v2.is_some()); assert!(v3.is_none()); assert!(v5.is_none()); - assert_eq!(Orbit2::new(arc.as_ref(), OrbitPolicy::Vertex, 2).count(), 3); + assert_eq!(arc.orbit(OrbitPolicy::Vertex, 2).count(), 3); assert_eq!(arc.force_read_vertex(2), None); assert_eq!(arc.force_read_vertex(3), None); assert_eq!(arc.force_read_vertex(5), None); @@ -638,7 +759,7 @@ fn sew_ordering_with_transactions() { assert!(v2.is_some()); assert!(v3.is_none()); assert!(v5.is_none()); - assert_eq!(Orbit2::new(arc.as_ref(), OrbitPolicy::Vertex, 2).count(), 3); + assert_eq!(arc.orbit(OrbitPolicy::Vertex, 2).count(), 3); atomically(|trans| { assert_eq!(arc.read_vertex(trans, 2)?, None); assert_eq!(arc.read_vertex(trans, 3)?, None); diff --git a/honeycomb-core/src/cmap/dim3/basic_ops.rs b/honeycomb-core/src/cmap/dim3/basic_ops.rs index 986b3edf..bcee1402 100644 --- a/honeycomb-core/src/cmap/dim3/basic_ops.rs +++ b/honeycomb-core/src/cmap/dim3/basic_ops.rs @@ -12,8 +12,7 @@ use std::collections::{HashSet, VecDeque}; use crate::attributes::UnknownAttributeStorage; use crate::cmap::{ - CMap3, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, Orbit3, OrbitPolicy, VertexIdType, - VolumeIdType, + CMap3, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, VertexIdType, VolumeIdType, }; use crate::geometry::CoordsFloat; use crate::stm::{StmClosureResult, StmError, Transaction, atomically}; @@ -453,28 +452,6 @@ impl CMap3 { }) } - /// Return the orbit defined by a dart and its `I`-cell. - /// - /// # Usage - /// - /// The [`Orbit3`] can be iterated upon to retrieve all dart members of the cell. Note that - /// **the dart passed as an argument is included as the first element of the returned orbit**. - /// - /// # Panics - /// - /// The method will panic if *I* is not 0, 1, 2, or 3. - #[must_use = "unused return value"] - pub fn i_cell(&self, dart_id: DartIdType) -> Orbit3<'_, T> { - assert!(I < 4); - match I { - 0 => Orbit3::new(self, OrbitPolicy::Vertex, dart_id), - 1 => Orbit3::new(self, OrbitPolicy::Edge, dart_id), - 2 => Orbit3::new(self, OrbitPolicy::Face, dart_id), - 3 => todo!(), - _ => unreachable!(), - } - } - /// Return an iterator over IDs of all the map's vertices. pub fn iter_vertices(&self) -> impl Iterator + '_ { (1..self.n_darts() as DartIdType) diff --git a/honeycomb-core/src/cmap/dim3/orbits.rs b/honeycomb-core/src/cmap/dim3/orbits.rs index e6fa329e..77b86dbd 100644 --- a/honeycomb-core/src/cmap/dim3/orbits.rs +++ b/honeycomb-core/src/cmap/dim3/orbits.rs @@ -8,169 +8,180 @@ use std::collections::{HashSet, VecDeque}; use crate::cmap::{CMap3, DartIdType, NULL_DART_ID, OrbitPolicy}; use crate::geometry::CoordsFloat; -#[derive(Clone)] -pub struct Orbit3<'a, T: CoordsFloat> { - /// Reference to the map containing the beta functions used in the BFS. - map_handle: &'a CMap3, - /// Policy used by the orbit for the BFS. It can be predetermined or custom. - orbit_policy: OrbitPolicy, - /// Set used to identify which dart is marked during the BFS. - marked: HashSet, - /// Queue used to store which dart must be visited next during the BFS. - pending: VecDeque, -} - -impl<'a, T: CoordsFloat> Orbit3<'a, T> { - #[must_use = "unused return value"] - pub fn new(map_handle: &'a CMap3, orbit_policy: OrbitPolicy, dart: DartIdType) -> Self { - let mut marked = HashSet::::new(); - marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit - marked.insert(dart); // we're starting here, so we mark it beforehand - let pending = VecDeque::from([dart]); +impl CMap3 { + /// Generic orbit implementation. + /// + /// # Arguments + /// - `opolicy: OrbitPolicy` -- Policy used by the orbit for the BFS. + /// - `dart_id: DartIdentifier` -- Dart of which the structure will compute the orbit. + /// + /// # The search algorithm + /// + /// The search algorithm used to establish the list of dart included in the orbit is a + /// [Breadth-First Search algorithm][WIKIBFS]. This means that: + /// + /// - we look at the images of the current dart through all beta functions, + /// adding those to a queue, before moving on to the next dart. + /// - we apply the beta functions in their specified order; This guarantees a consistent and + /// predictable result. + /// + /// # Performance + /// + /// Currently, orbits use two dynamically allocated structures for computation: a `VecDeque`, + /// and a `HashSet`. There is a possibility to use static thread-local instances to avoid + /// ephemeral allocations, but [it would require a guard mechanism][PR]. + /// + /// [PR]: https://github.com/LIHPC-Computational-Geometry/honeycomb/pull/293 + #[allow(clippy::needless_for_each,clippy::too_many_lines)] + #[rustfmt::skip] + pub fn orbit( + &self, + opolicy: OrbitPolicy, + dart_id: DartIdType, + ) -> impl Iterator { + let mut pending = VecDeque::new(); + let mut marked: HashSet = HashSet::new(); + pending.push_back(dart_id); + marked.insert(NULL_DART_ID); + marked.insert(dart_id); // we're starting here, so we mark it beforehand - Self { - map_handle, - orbit_policy, - marked, - pending, - } - } -} - -impl Iterator for Orbit3<'_, T> { - type Item = DartIdType; - - #[allow(clippy::too_many_lines)] - fn next(&mut self) -> Option { - if let Some(d) = self.pending.pop_front() { - match self.orbit_policy { - // B3oB2, B1oB3, B1oB2, B3oB0, B2oB0 - OrbitPolicy::Vertex => { - // b3(b2(d)) - let image1 = self.map_handle.beta::<3>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image1) { - // if true, we did not see this dart yet - // i.e. we need to visit it later - self.pending.push_back(image1); - } - // b1(b3(d)) - let image2 = self.map_handle.beta::<1>(self.map_handle.beta::<3>(d)); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - // b1(b2(d)) - let image3 = self.map_handle.beta::<1>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image3) { - self.pending.push_back(image3); - } - // b3(b0(d)) - let image4 = self.map_handle.beta::<3>(self.map_handle.beta::<0>(d)); - if self.marked.insert(image4) { - self.pending.push_back(image4); - } - // b2(b0(d)) - let image5 = self.map_handle.beta::<2>(self.map_handle.beta::<0>(d)); - if self.marked.insert(image5) { - self.pending.push_back(image5); - } - } - // B3oB2, B1oB3, B1oB2 - OrbitPolicy::VertexLinear => { - let image1 = self.map_handle.beta::<3>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image1) { - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<1>(self.map_handle.beta::<3>(d)); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - // b1(b2(d)) - let image3 = self.map_handle.beta::<1>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image3) { - self.pending.push_back(image3); - } - } - // B2, B3 - OrbitPolicy::Edge => { - let image1 = self.map_handle.beta::<2>(d); - if self.marked.insert(image1) { - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<3>(d); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - } - // B1, B0, B3 - OrbitPolicy::Face => { - let image1 = self.map_handle.beta::<1>(d); - if self.marked.insert(image1) { - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<0>(d); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - let image3 = self.map_handle.beta::<3>(d); - if self.marked.insert(image3) { - self.pending.push_back(image3); - } - } - // B1, B3 - OrbitPolicy::FaceLinear => { - let image1 = self.map_handle.beta::<1>(d); - if self.marked.insert(image1) { - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<3>(d); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - } - // B1, B0, B2 - OrbitPolicy::Volume => { - let image1 = self.map_handle.beta::<1>(d); - if self.marked.insert(image1) { - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<0>(d); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - let image3 = self.map_handle.beta::<2>(d); - if self.marked.insert(image3) { - self.pending.push_back(image3); - } - } - // B1, B2 - OrbitPolicy::VolumeLinear => { - let image1 = self.map_handle.beta::<1>(d); - if self.marked.insert(image1) { - self.pending.push_back(image1); - } - let image2 = self.map_handle.beta::<2>(d); - if self.marked.insert(image2) { - self.pending.push_back(image2); - } - } - OrbitPolicy::Custom(beta_slice) => { - for beta_id in beta_slice { - let image = self.map_handle.beta_rt(*beta_id, d); - if self.marked.insert(image) { - self.pending.push_back(image); + // FIXME: move the match block out of the iterator + std::iter::from_fn(move || { + if let Some(d) = pending.pop_front() { + // compute the next images + match opolicy { + // B3oB2, B1oB3, B1oB2, B3oB0, B2oB0 + OrbitPolicy::Vertex => { + [ + self.beta::<3>(self.beta::<2>(d)), // b3(b2(d)) + self.beta::<1>(self.beta::<3>(d)), // b1(b3(d)) + self.beta::<1>(self.beta::<2>(d)), // b1(b2(d)) + self.beta::<3>(self.beta::<0>(d)), // b3(b0(d)) + self.beta::<2>(self.beta::<0>(d)), // b2(b0(d)) + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + // B3oB2, B1oB3, B1oB2 + OrbitPolicy::VertexLinear => { + [ + self.beta::<3>(self.beta::<2>(d)), // b3(b2(d)) + self.beta::<1>(self.beta::<3>(d)), // b1(b3(d)) + self.beta::<1>(self.beta::<2>(d)), // b1(b2(d)) + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + // B2, B3 + OrbitPolicy::Edge => { + [ + self.beta::<2>(d), + self.beta::<3>(d), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + // B1, B0, B3 + OrbitPolicy::Face => { + [ + self.beta::<1>(d), + self.beta::<0>(d), + self.beta::<3>(d), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + // B1, B3 + OrbitPolicy::FaceLinear => { + [ + self.beta::<1>(d), + self.beta::<3>(d), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + // B1, B0, B2 + OrbitPolicy::Volume => { + [ + self.beta::<1>(d), + self.beta::<0>(d), + self.beta::<2>(d), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + // B1, B2 + OrbitPolicy::VolumeLinear => { + [ + self.beta::<1>(d), + self.beta::<2>(d), + ] + .into_iter() + .for_each(|im| { + if marked.insert(im) { + pending.push_back(im); + } + }); + } + OrbitPolicy::Custom(beta_slice) => { + for beta_id in beta_slice { + let image = self.beta_rt(*beta_id, d); + if marked.insert(image) { + pending.push_back(image); + } } } } + + return Some(d); } - Some(d) - } else { - None + None // queue is empty, we're done + }) + } + + /// Return the orbit defined by a dart and its `I`-cell. + /// + /// # Usage + /// + /// The [`Orbit3`] can be iterated upon to retrieve all dart members of the cell. Note that + /// **the dart passed as an argument is included as the first element of the returned orbit**. + /// + /// # Panics + /// + /// The method will panic if *I* is not 0, 1, 2, or 3. + #[must_use = "unused return value"] + pub fn i_cell(&self, dart_id: DartIdType) -> impl Iterator { + assert!(I < 4); + match I { + 0 => self.orbit(OrbitPolicy::Vertex, dart_id), + 1 => self.orbit(OrbitPolicy::Edge, dart_id), + 2 => self.orbit(OrbitPolicy::Face, dart_id), + 3 => self.orbit(OrbitPolicy::Volume, dart_id), + _ => unreachable!(), } } } - -// -- - -#[allow(unused_mut)] -#[cfg(test)] -mod tests {} diff --git a/honeycomb-core/src/cmap/dim3/sews/three.rs b/honeycomb-core/src/cmap/dim3/sews/three.rs index 59cd87ff..e9ff8729 100644 --- a/honeycomb-core/src/cmap/dim3/sews/three.rs +++ b/honeycomb-core/src/cmap/dim3/sews/three.rs @@ -2,9 +2,7 @@ use crate::{ attributes::{AttributeStorage, UnknownAttributeStorage}, - cmap::{ - CMap3, DartIdType, EdgeIdType, NULL_DART_ID, Orbit3, OrbitPolicy, SewError, VertexIdType, - }, + cmap::{CMap3, DartIdType, EdgeIdType, NULL_DART_ID, OrbitPolicy, SewError, VertexIdType}, geometry::CoordsFloat, stm::{Transaction, TransactionClosureResult, abort, try_or_coerce}, }; @@ -20,15 +18,22 @@ impl CMap3 { ) -> TransactionClosureResult<(), SewError> { // using these custom orbits, I can get both dart of all sides, directly ordered // for the merges - let l_side = Orbit3::new(self, OrbitPolicy::Custom(&[1, 0]), ld); - let r_side = Orbit3::new(self, OrbitPolicy::Custom(&[0, 1]), rd); - let l_face = l_side.clone().min().expect("E: unreachable"); - let r_face = r_side.clone().min().expect("E: unreachable"); + let l_face = self + .orbit(OrbitPolicy::Custom(&[1, 0]), ld) + .min() + .expect("E: unreachable"); + let r_face = self + .orbit(OrbitPolicy::Custom(&[0, 1]), rd) + .min() + .expect("E: unreachable"); let mut edges: Vec<(EdgeIdType, EdgeIdType)> = Vec::with_capacity(10); let mut vertices: Vec<(VertexIdType, VertexIdType)> = Vec::with_capacity(10); // read edge + vertex on the b1ld side. if b0ld == NULL, we need to read the left vertex - for (l, r) in l_side.zip(r_side) { + for (l, r) in self + .orbit(OrbitPolicy::Custom(&[1, 0]), ld) + .zip(self.orbit(OrbitPolicy::Custom(&[0, 1]), rd)) + { edges.push(( self.edge_id_transac(trans, l)?, self.edge_id_transac(trans, r)?, @@ -142,19 +147,25 @@ impl CMap3 { try_or_coerce!(self.unlink::<3>(trans, ld), SewError); - let l_side = Orbit3::new(self, OrbitPolicy::Custom(&[1, 0]), ld); - let r_side = Orbit3::new(self, OrbitPolicy::Custom(&[0, 1]), rd); - // faces - let l_face = l_side.clone().min().expect("E: unreachable"); - let r_face = r_side.clone().min().expect("E: unreachable"); + let l_face = self + .orbit(OrbitPolicy::Custom(&[1, 0]), ld) + .min() + .expect("E: unreachable"); + let r_face = self + .orbit(OrbitPolicy::Custom(&[0, 1]), rd) + .min() + .expect("E: unreachable"); try_or_coerce!( self.attributes .split_face_attributes(trans, l_face, r_face, l_face.max(r_face)), SewError ); - for (l, r) in l_side.zip(r_side) { + for (l, r) in self + .orbit(OrbitPolicy::Custom(&[1, 0]), ld) + .zip(self.orbit(OrbitPolicy::Custom(&[0, 1]), rd)) + { // edge let (eid_l, eid_r) = ( self.edge_id_transac(trans, l)?, diff --git a/honeycomb-core/src/cmap/dim3/tests.rs b/honeycomb-core/src/cmap/dim3/tests.rs index ee4dd9f1..305e048e 100644 --- a/honeycomb-core/src/cmap/dim3/tests.rs +++ b/honeycomb-core/src/cmap/dim3/tests.rs @@ -1,6 +1,6 @@ use crate::{ attributes::{AttrSparseVec, AttributeBind, AttributeError, AttributeUpdate}, - cmap::{CMap3, DartIdType, Orbit3, OrbitPolicy, SewError, VertexIdType}, + cmap::{CMap3, DartIdType, OrbitPolicy, SewError, VertexIdType}, geometry::Vertex3, stm::{StmError, TVar, TransactionError, atomically, atomically_with_err}, }; @@ -566,7 +566,7 @@ fn sew_ordering() { assert!(v2.is_some()); assert!(v3.is_none()); assert!(v5.is_none()); - assert_eq!(Orbit3::new(arc.as_ref(), OrbitPolicy::Vertex, 2).count(), 3); + assert_eq!(arc.orbit(OrbitPolicy::Vertex, 2).count(), 3); assert!(arc.force_read_vertex(2).is_none()); assert!(arc.force_read_vertex(3).is_none()); assert!(arc.force_read_vertex(5).is_none()); @@ -646,7 +646,7 @@ fn sew_ordering_with_transactions() { assert!(v2.is_some()); assert!(v3.is_none()); assert!(v5.is_none()); - assert_eq!(Orbit3::new(arc.as_ref(), OrbitPolicy::Vertex, 2).count(), 3); + assert_eq!(arc.orbit(OrbitPolicy::Vertex, 2).count(), 3); atomically(|trans| { assert!(arc.read_vertex(trans, 2)?.is_none()); assert!(arc.read_vertex(trans, 3)?.is_none()); diff --git a/honeycomb-core/src/cmap/mod.rs b/honeycomb-core/src/cmap/mod.rs index ea25dead..57cf75f3 100644 --- a/honeycomb-core/src/cmap/mod.rs +++ b/honeycomb-core/src/cmap/mod.rs @@ -15,6 +15,6 @@ pub use components::{ }, orbits::OrbitPolicy, }; -pub use dim2::{orbits::Orbit2, structure::CMap2}; -pub use dim3::{orbits::Orbit3, structure::CMap3}; +pub use dim2::structure::CMap2; +pub use dim3::structure::CMap3; pub use error::{LinkError, SewError}; diff --git a/honeycomb-kernels/src/grisubal/routines/clip.rs b/honeycomb-kernels/src/grisubal/routines/clip.rs index 0492c2ba..0ebcf869 100644 --- a/honeycomb-kernels/src/grisubal/routines/clip.rs +++ b/honeycomb-kernels/src/grisubal/routines/clip.rs @@ -2,7 +2,7 @@ use std::collections::{HashSet, VecDeque}; -use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, NULL_DART_ID, Orbit2, OrbitPolicy}; +use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, NULL_DART_ID, OrbitPolicy}; use honeycomb_core::geometry::{CoordsFloat, Vertex2}; use crate::grisubal::GrisubalError; @@ -54,14 +54,14 @@ fn mark_faces( // mark faces if marked.insert(face_id) { // detect orientation issues / open geometries - let mut darts = Orbit2::new(cmap, OrbitPolicy::Face, face_id as DartIdType); + let mut darts = cmap.orbit(OrbitPolicy::Face, face_id as DartIdType); if darts.any(|did| cmap.force_read_attribute::(did) == Some(other)) { return Err(GrisubalError::InconsistentOrientation( "between-boundary inconsistency", )); } // find neighbor faces where entry darts aren't tagged - let darts = Orbit2::new(cmap, OrbitPolicy::Face, face_id as DartIdType); + let darts = cmap.orbit(OrbitPolicy::Face, face_id as DartIdType); queue.extend(darts.filter_map(|dart_id| { if matches!( cmap.force_read_attribute::(cmap.beta::<2>(dart_id)), @@ -97,8 +97,9 @@ fn delete_darts( .collect(); for face_id in marked { - let darts: Vec = - Orbit2::new(cmap, OrbitPolicy::Face, face_id as DartIdType).collect(); + let darts: Vec = cmap + .orbit(OrbitPolicy::Face, face_id as DartIdType) + .collect(); for &dart in &darts { let _ = cmap.force_remove_vertex(cmap.vertex_id(dart)); cmap.set_betas(dart, [NULL_DART_ID; 3]); diff --git a/honeycomb-kernels/src/grisubal/tests.rs b/honeycomb-kernels/src/grisubal/tests.rs index a0553ad9..f6c47810 100644 --- a/honeycomb-kernels/src/grisubal/tests.rs +++ b/honeycomb-kernels/src/grisubal/tests.rs @@ -1,4 +1,4 @@ -use honeycomb_core::cmap::{CMapBuilder, GridDescriptor, Orbit2, OrbitPolicy}; +use honeycomb_core::cmap::{CMapBuilder, GridDescriptor, OrbitPolicy}; use honeycomb_core::geometry::Vertex2; use vtkio::Vtk; @@ -237,24 +237,24 @@ fn regular_intersections() { assert_eq!(faces.len(), 8); // bottom left assert!(faces.contains(&1)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 1).count(), 6); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 1).count(), 6); assert!(faces.contains(&3)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 3).count(), 4); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 3).count(), 4); // bottom right assert!(faces.contains(&5)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 5).count(), 6); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 5).count(), 6); assert!(faces.contains(&8)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 8).count(), 4); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 8).count(), 4); // top right assert!(faces.contains(&9)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 9).count(), 6); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 9).count(), 6); assert!(faces.contains(&10)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 10).count(), 4); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 10).count(), 4); // top left assert!(faces.contains(&14)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 14).count(), 6); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 14).count(), 6); assert!(faces.contains(&13)); - assert_eq!(Orbit2::new(&cmap, OrbitPolicy::Face, 13).count(), 4); + assert_eq!(cmap.orbit(OrbitPolicy::Face, 13).count(), 4); } #[allow(clippy::too_many_lines)] @@ -362,7 +362,8 @@ fn corner_intersection() { let edges = cmap.iter_edges(); assert_eq!(edges.count(), 20); - let face1_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 1) + let face1_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 1) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -376,7 +377,8 @@ fn corner_intersection() { assert!(face1_vertices.contains(&Vertex2(1.0, 1.0))); assert!(face1_vertices.contains(&Vertex2(0.0, 1.0))); - let face9_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 9) + let face9_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 9) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -388,7 +390,8 @@ fn corner_intersection() { assert!(face9_vertices.contains(&Vertex2(1.0, 2.0))); assert!(face9_vertices.contains(&Vertex2(0.0, 2.0))); - let face13_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 13) + let face13_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 13) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -448,7 +451,8 @@ pub fn successive_straight_intersections() { // bottom row - let face1_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 1) + let face1_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 1) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -462,7 +466,8 @@ pub fn successive_straight_intersections() { assert!(face1_vertices.contains(&Vertex2(0.5, 1.0))); assert!(face1_vertices.contains(&Vertex2(0.0, 1.0))); - let face3_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 3) + let face3_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 3) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -474,7 +479,8 @@ pub fn successive_straight_intersections() { assert!(face3_vertices.contains(&Vertex2(1.0, 1.0))); assert!(face3_vertices.contains(&Vertex2(0.5, 1.0))); - let face5_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 5) + let face5_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 5) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -486,7 +492,8 @@ pub fn successive_straight_intersections() { assert!(face5_vertices.contains(&Vertex2(2.0, 0.5))); assert!(face5_vertices.contains(&Vertex2(1.0, 0.5))); - let face7_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 7) + let face7_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 7) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -498,7 +505,8 @@ pub fn successive_straight_intersections() { assert!(face7_vertices.contains(&Vertex2(2.0, 0.5))); assert!(face7_vertices.contains(&Vertex2(1.0, 0.5))); - let face9_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 9) + let face9_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 9) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -512,7 +520,8 @@ pub fn successive_straight_intersections() { assert!(face9_vertices.contains(&Vertex2(2.5, 0.5))); assert!(face9_vertices.contains(&Vertex2(2.0, 0.5))); - let face12_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 12) + let face12_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 12) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -526,7 +535,8 @@ pub fn successive_straight_intersections() { // middle row - let face13_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 13) + let face13_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 13) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -538,7 +548,8 @@ pub fn successive_straight_intersections() { assert!(face13_vertices.contains(&Vertex2(0.5, 1.0))); assert!(face13_vertices.contains(&Vertex2(0.5, 2.0))); - let face14_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 14) + let face14_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 14) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -550,7 +561,8 @@ pub fn successive_straight_intersections() { assert!(face14_vertices.contains(&Vertex2(1.0, 2.0))); assert!(face14_vertices.contains(&Vertex2(1.0, 1.0))); - let face17_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 17) + let face17_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 17) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -562,7 +574,8 @@ pub fn successive_straight_intersections() { assert!(face17_vertices.contains(&Vertex2(2.0, 2.0))); assert!(face17_vertices.contains(&Vertex2(2.0, 1.0))); - let face21_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 21) + let face21_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 21) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -574,7 +587,8 @@ pub fn successive_straight_intersections() { assert!(face21_vertices.contains(&Vertex2(2.5, 2.0))); assert!(face21_vertices.contains(&Vertex2(2.0, 2.0))); - let face22_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 22) + let face22_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 22) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -588,7 +602,8 @@ pub fn successive_straight_intersections() { // top row - let face25_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 25) + let face25_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 25) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -602,7 +617,8 @@ pub fn successive_straight_intersections() { assert!(face25_vertices.contains(&Vertex2(1.0, 3.0))); assert!(face25_vertices.contains(&Vertex2(0.0, 3.0))); - let face26_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 26) + let face26_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 26) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -614,7 +630,8 @@ pub fn successive_straight_intersections() { assert!(face26_vertices.contains(&Vertex2(1.0, 2.5))); assert!(face26_vertices.contains(&Vertex2(0.5, 2.5))); - let face29_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 29) + let face29_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 29) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -626,7 +643,8 @@ pub fn successive_straight_intersections() { assert!(face29_vertices.contains(&Vertex2(2.0, 2.5))); assert!(face29_vertices.contains(&Vertex2(1.0, 2.5))); - let face31_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 31) + let face31_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 31) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -638,7 +656,8 @@ pub fn successive_straight_intersections() { assert!(face31_vertices.contains(&Vertex2(1.0, 3.0))); assert!(face31_vertices.contains(&Vertex2(2.0, 3.0))); - let face33_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 33) + let face33_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 33) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -650,7 +669,8 @@ pub fn successive_straight_intersections() { assert!(face33_vertices.contains(&Vertex2(2.5, 2.5))); assert!(face33_vertices.contains(&Vertex2(2.5, 2.0))); - let face34_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 34) + let face34_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 34) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -726,7 +746,8 @@ pub fn successive_diag_intersections() { // bottom row - let face1_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 1) + let face1_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 1) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -734,7 +755,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face1_vertices.len(), 5); - let face3_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 3) + let face3_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 3) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -742,7 +764,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face3_vertices.len(), 3); - let face5_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 5) + let face5_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 5) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -750,7 +773,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face5_vertices.len(), 6); - let face7_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 7) + let face7_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 7) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -758,7 +782,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face7_vertices.len(), 6); - let face9_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 9) + let face9_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 9) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -766,7 +791,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face9_vertices.len(), 5); - let face12_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 12) + let face12_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 12) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -776,7 +802,8 @@ pub fn successive_diag_intersections() { // middle row - let face13_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 13) + let face13_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 13) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -784,7 +811,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face13_vertices.len(), 6); - let face14_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 14) + let face14_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 14) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -792,7 +820,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face14_vertices.len(), 6); - let face17_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 17) + let face17_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 17) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -800,7 +829,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face17_vertices.len(), 4); - let face21_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 21) + let face21_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 21) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -808,7 +838,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face21_vertices.len(), 6); - let face22_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 22) + let face22_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 22) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -818,7 +849,8 @@ pub fn successive_diag_intersections() { // top row - let face25_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 25) + let face25_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 25) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -826,7 +858,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face25_vertices.len(), 5); - let face26_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 26) + let face26_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 26) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -834,7 +867,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face26_vertices.len(), 3); - let face29_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 29) + let face29_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 29) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -842,7 +876,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face29_vertices.len(), 6); - let face31_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 31) + let face31_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 31) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -850,7 +885,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face31_vertices.len(), 6); - let face33_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 33) + let face33_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 33) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") @@ -858,7 +894,8 @@ pub fn successive_diag_intersections() { .collect(); assert_eq!(face33_vertices.len(), 3); - let face34_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 34) + let face34_vertices: Vec> = cmap + .orbit(OrbitPolicy::Face, 34) .map(|d| { cmap.force_read_vertex(cmap.vertex_id(d)) .expect("E: unreachable") diff --git a/honeycomb-kernels/src/triangulation/ear_clipping.rs b/honeycomb-kernels/src/triangulation/ear_clipping.rs index 34cc27dc..3b0d0328 100644 --- a/honeycomb-kernels/src/triangulation/ear_clipping.rs +++ b/honeycomb-kernels/src/triangulation/ear_clipping.rs @@ -1,4 +1,4 @@ -use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, Orbit2, OrbitPolicy}; +use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, OrbitPolicy}; use honeycomb_core::geometry::CoordsFloat; use crate::triangulation::{ @@ -60,8 +60,10 @@ pub fn process_cell( new_darts: &[DartIdType], ) -> Result<(), TriangulateError> { // fetch darts using a custom orbit so that they're ordered - let mut darts: Vec<_> = - Orbit2::new(cmap, OrbitPolicy::Custom(&[1]), face_id as DartIdType).collect(); + let mut darts: Vec<_> = cmap + .orbit(OrbitPolicy::Custom(&[1]), face_id as DartIdType) + .collect(); + let mut n = darts.len(); // early checks - check # of darts & face size @@ -131,7 +133,7 @@ pub fn process_cell( vertices.remove((ear + 1) % n); // update n - n = Orbit2::new(cmap, OrbitPolicy::Custom(&[1]), nd2).count(); + n = cmap.orbit(OrbitPolicy::Custom(&[1]), nd2).count(); } Ok(()) diff --git a/honeycomb-kernels/src/triangulation/fan.rs b/honeycomb-kernels/src/triangulation/fan.rs index 943e18f3..53d1c23a 100644 --- a/honeycomb-kernels/src/triangulation/fan.rs +++ b/honeycomb-kernels/src/triangulation/fan.rs @@ -1,4 +1,4 @@ -use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, Orbit2, OrbitPolicy}; +use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, OrbitPolicy}; use honeycomb_core::geometry::CoordsFloat; use crate::triangulation::{ @@ -48,8 +48,9 @@ pub fn process_cell( new_darts: &[DartIdType], ) -> Result<(), TriangulateError> { // fetch darts using a custom orbit so that they're ordered - let darts: Vec<_> = - Orbit2::new(cmap, OrbitPolicy::Custom(&[1]), face_id as DartIdType).collect(); + let darts: Vec<_> = cmap + .orbit(OrbitPolicy::Custom(&[1]), face_id as DartIdType) + .collect(); let n = darts.len(); // early checks - check # of darts & face size @@ -151,7 +152,9 @@ pub fn process_convex_cell( face_id: FaceIdType, new_darts: &[DartIdType], ) -> Result<(), TriangulateError> { - let n = Orbit2::new(cmap, OrbitPolicy::Custom(&[1]), face_id as DartIdType).count(); + let n = cmap + .orbit(OrbitPolicy::Custom(&[1]), face_id as DartIdType) + .count(); // early rets check_requirements(n, new_darts.len())?; diff --git a/honeycomb-render/src/capture/mod.rs b/honeycomb-render/src/capture/mod.rs index a7d1e5a4..78649698 100644 --- a/honeycomb-render/src/capture/mod.rs +++ b/honeycomb-render/src/capture/mod.rs @@ -3,7 +3,7 @@ pub mod system; use bevy::prelude::*; use bevy::utils::HashMap; -use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, Orbit2, OrbitPolicy, VertexIdType}; +use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, OrbitPolicy, VertexIdType}; use honeycomb_core::geometry::CoordsFloat; use crate::bundles::{DartBodyBundle, DartHeadBundle, EdgeBundle, FaceBundle, VertexBundle}; @@ -102,10 +102,10 @@ impl Capture { let faces: Vec = map_faces .iter() .map(|id| { - let vertex_ids: Vec = - Orbit2::new(cmap, OrbitPolicy::Custom(&[1]), *id as DartIdType) - .map(|dart_id| index_map[&cmap.vertex_id(dart_id)]) - .collect(); + let vertex_ids: Vec = cmap + .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType) + .map(|dart_id| index_map[&cmap.vertex_id(dart_id)]) + .collect(); let n_v = vertex_ids.len(); let mut loc_normals = vec![{ let (ver_in, ver, ver_out) = @@ -146,7 +146,8 @@ impl Capture { normals.insert(*id, loc_normals); // common dart iterator - let mut tmp = Orbit2::new(cmap, OrbitPolicy::Custom(&[1]), *id as DartIdType) + let mut tmp = cmap + .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType) .enumerate() .map(|(idx, dart_id)| (dart_id, index_map[&cmap.vertex_id(dart_id)], idx)) .collect::>(); diff --git a/honeycomb/src/lib.rs b/honeycomb/src/lib.rs index afde96f2..892aabff 100644 --- a/honeycomb/src/lib.rs +++ b/honeycomb/src/lib.rs @@ -59,8 +59,8 @@ pub mod prelude { pub use honeycomb_core::attributes::{AttributeBind, AttributeUpdate}; pub use honeycomb_core::cmap::{ BuilderError, CMap2, CMapBuilder, DartIdType, EdgeIdType, FaceIdType, GridDescriptor, - NULL_DART_ID, NULL_EDGE_ID, NULL_FACE_ID, NULL_VERTEX_ID, NULL_VOLUME_ID, Orbit2, - OrbitPolicy, VertexIdType, VolumeIdType, + NULL_DART_ID, NULL_EDGE_ID, NULL_FACE_ID, NULL_VERTEX_ID, NULL_VOLUME_ID, OrbitPolicy, + VertexIdType, VolumeIdType, }; pub use honeycomb_core::geometry::{CoordsError, CoordsFloat, Vector2, Vertex2};