diff --git a/honeycomb-benches/benches/core/cmap2/editing.rs b/honeycomb-benches/benches/core/cmap2/editing.rs index abdfdb1e..0e70b338 100644 --- a/honeycomb-benches/benches/core/cmap2/editing.rs +++ b/honeycomb-benches/benches/core/cmap2/editing.rs @@ -14,10 +14,7 @@ // ------ IMPORTS -use honeycomb_core::{ - utils::square_cmap2, CMap2, DartIdentifier, FaceIdentifier, FloatType, Vertex2, - VertexIdentifier, -}; +use honeycomb_core::{utils::square_cmap2, CMap2, DartIdentifier, FloatType, Vertex2}; use iai_callgrind::{ library_benchmark, library_benchmark_group, main, FlamegraphConfig, LibraryBenchmarkConfig, }; @@ -33,18 +30,20 @@ fn get_sparse_map(n_square: usize) -> CMap2 { let mut map = square_cmap2::(n_square); map.set_betas(5, [0; 3]); // free dart 5 map.remove_free_dart(5); - map.remove_vertex(1); + // because of the way we built the map in the square_cmap2 function & the ID computation + // policy, we can safely remove a vertex we know is defined + assert_eq!(map.remove_vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); map } -fn compute_dims(n_square: usize) -> (usize, usize) { - (n_square.pow(2) * 4, (n_square + 1).pow(2)) +fn compute_dims(n_square: usize) -> usize { + n_square.pow(2) * 4 } #[library_benchmark] #[benches::with_setup(args = [16, 32, 64, 128, 256, 512], setup = compute_dims)] -fn constructor((n_darts, n_vertices): (usize, usize)) -> CMap2 { - black_box(CMap2::new(n_darts, n_vertices)) +fn constructor(n_darts: usize) -> CMap2 { + black_box(CMap2::new(n_darts)) } #[library_benchmark] @@ -63,39 +62,12 @@ fn add_ten_darts(map: &mut CMap2) -> DartIdentifier { black_box(map.add_free_darts(10)) } -#[library_benchmark] -#[bench::small(&mut get_map(5))] -#[bench::medium(&mut get_map(50))] -#[bench::large(&mut get_map(500))] -fn add_default_vertex(map: &mut CMap2) -> VertexIdentifier { - black_box(map.add_vertex(None)) -} - -#[library_benchmark] -#[bench::small(&mut get_map(5))] -#[bench::medium(&mut get_map(50))] -#[bench::large(&mut get_map(500))] -fn add_vertex(map: &mut CMap2) -> VertexIdentifier { - black_box(map.add_vertex(Some(Vertex2::from((12.0, 6.0))))) -} - -#[library_benchmark] -#[bench::small(&mut get_map(5))] -#[bench::medium(&mut get_map(50))] -#[bench::large(&mut get_map(500))] -fn add_10_vertices(map: &mut CMap2) -> VertexIdentifier { - black_box(map.add_vertices(10)) -} - library_benchmark_group!( name = bench_new; benchmarks = constructor, add_single_dart, add_ten_darts, - add_default_vertex, - add_vertex, - add_10_vertices, ); #[library_benchmark] @@ -118,16 +90,8 @@ fn insert_dart_full(map: &mut CMap2) -> DartIdentifier { #[bench::small(&mut get_sparse_map(5))] #[bench::medium(&mut get_sparse_map(50))] #[bench::large(&mut get_sparse_map(500))] -fn insert_vertex(map: &mut CMap2) -> VertexIdentifier { - black_box(map.insert_vertex(None)) -} - -#[library_benchmark] -#[bench::small(&mut get_map(5))] -#[bench::medium(&mut get_map(50))] -#[bench::large(&mut get_map(500))] -fn insert_vertex_full(map: &mut CMap2) -> VertexIdentifier { - black_box(map.insert_vertex(None)) +fn insert_vertex(map: &mut CMap2) { + black_box(map.insert_vertex(1, (0.0, 0.0))); } library_benchmark_group!( @@ -136,29 +100,19 @@ library_benchmark_group!( insert_dart, insert_dart_full, insert_vertex, - insert_vertex_full, ); #[library_benchmark] #[bench::small(&mut get_map(5))] #[bench::medium(&mut get_map(50))] #[bench::large(&mut get_map(500))] -fn build_face(map: &mut CMap2) -> FaceIdentifier { - black_box(map.build_face(5)) -} - -#[library_benchmark] -#[bench::small(&mut get_map(5))] -#[bench::medium(&mut get_map(50))] -#[bench::large(&mut get_map(500))] -fn build_faces(map: &mut CMap2) -> usize { - black_box(map.build_all_faces()) +fn build_faces(map: &mut CMap2) { + black_box(map.fetch_faces()); } library_benchmark_group!( name = bench_face_building; benchmarks = - build_face, build_faces, ); diff --git a/honeycomb-benches/benches/core/cmap2/reading.rs b/honeycomb-benches/benches/core/cmap2/reading.rs index 035dbb80..f62e8bbb 100644 --- a/honeycomb-benches/benches/core/cmap2/reading.rs +++ b/honeycomb-benches/benches/core/cmap2/reading.rs @@ -112,24 +112,24 @@ library_benchmark_group!( #[bench::small(&get_map(5))] #[bench::medium(&get_map(50))] #[bench::large(&get_map(500))] -fn zero_cell(map: &CMap2) -> Vec { - black_box(map.i_cell::<0>(5)) +fn zero_cell(map: &CMap2) { + black_box(map.i_cell::<0>(5)); } #[library_benchmark] #[bench::small(&get_map(5))] #[bench::medium(&get_map(50))] #[bench::large(&get_map(500))] -fn one_cell(map: &CMap2) -> Vec { - black_box(map.i_cell::<0>(5)) +fn one_cell(map: &CMap2) { + black_box(map.i_cell::<1>(5)); } #[library_benchmark] #[bench::small(&get_map(5))] #[bench::medium(&get_map(50))] #[bench::large(&get_map(500))] -fn two_cell(map: &CMap2) -> Vec { - black_box(map.i_cell::<2>(5)) +fn two_cell(map: &CMap2) { + black_box(map.i_cell::<2>(5)); } library_benchmark_group!( diff --git a/honeycomb-benches/benches/core/cmap2/sewing.rs b/honeycomb-benches/benches/core/cmap2/sewing.rs index fcf6f96c..0dd20069 100644 --- a/honeycomb-benches/benches/core/cmap2/sewing.rs +++ b/honeycomb-benches/benches/core/cmap2/sewing.rs @@ -15,7 +15,7 @@ // ------ IMPORTS -use honeycomb_core::{utils::square_cmap2, CMap2, FloatType, SewPolicy, UnsewPolicy}; +use honeycomb_core::{utils::square_cmap2, CMap2, FloatType}; use iai_callgrind::{ library_benchmark, library_benchmark_group, main, FlamegraphConfig, LibraryBenchmarkConfig, }; @@ -23,8 +23,8 @@ use std::hint::black_box; // ------ CONTENT -fn compute_dims(n_square: usize) -> (usize, usize) { - (n_square.pow(2) * 4, (n_square + 1).pow(2)) +fn compute_dims(n_square: usize) -> usize { + n_square.pow(2) * 4 } fn get_map(n_square: usize) -> CMap2 { @@ -32,8 +32,8 @@ fn get_map(n_square: usize) -> CMap2 { } fn get_unstructured_map(n_square: usize) -> CMap2 { - let (n_darts, n_vertices) = compute_dims(n_square); - CMap2::new(n_darts, n_vertices) + let n_darts = compute_dims(n_square); + CMap2::new(n_darts) } #[library_benchmark] @@ -41,7 +41,7 @@ fn get_unstructured_map(n_square: usize) -> CMap2 { #[bench::medium(&mut get_unstructured_map(50))] #[bench::large(&mut get_unstructured_map(500))] fn one_sew_left(map: &mut CMap2) -> &mut CMap2 { - map.one_sew(4, 6, SewPolicy::StretchLeft); + map.one_sew(4, 6); black_box(map) } @@ -50,7 +50,7 @@ fn one_sew_left(map: &mut CMap2) -> &mut CMap2 { #[bench::medium(&mut get_unstructured_map(50))] #[bench::large(&mut get_unstructured_map(500))] fn one_sew_right(map: &mut CMap2) -> &mut CMap2 { - map.one_sew(4, 6, SewPolicy::StretchRight); + map.one_sew(4, 6); black_box(map) } @@ -59,7 +59,7 @@ fn one_sew_right(map: &mut CMap2) -> &mut CMap2 { #[bench::medium(&mut get_unstructured_map(50))] #[bench::large(&mut get_unstructured_map(500))] fn one_sew_avg(map: &mut CMap2) -> &mut CMap2 { - map.one_sew(4, 6, SewPolicy::StretchAverage); + map.one_sew(4, 6); black_box(map) } @@ -76,7 +76,7 @@ library_benchmark_group!( #[bench::medium(&mut get_unstructured_map(50))] #[bench::large(&mut get_unstructured_map(500))] fn two_sew_left(map: &mut CMap2) -> &mut CMap2 { - map.two_sew(4, 6, SewPolicy::StretchLeft); + map.two_sew(4, 6); black_box(map) } @@ -85,7 +85,7 @@ fn two_sew_left(map: &mut CMap2) -> &mut CMap2 { #[bench::medium(&mut get_unstructured_map(50))] #[bench::large(&mut get_unstructured_map(500))] fn two_sew_right(map: &mut CMap2) -> &mut CMap2 { - map.two_sew(4, 6, SewPolicy::StretchRight); + map.two_sew(4, 6); black_box(map) } @@ -94,7 +94,7 @@ fn two_sew_right(map: &mut CMap2) -> &mut CMap2 { #[bench::medium(&mut get_unstructured_map(50))] #[bench::large(&mut get_unstructured_map(500))] fn two_sew_avg(map: &mut CMap2) -> &mut CMap2 { - map.two_sew(4, 6, SewPolicy::StretchAverage); + map.two_sew(4, 6); black_box(map) } @@ -111,7 +111,7 @@ library_benchmark_group!( #[bench::medium(&mut get_map(50))] #[bench::large(&mut get_map(500))] fn one_unsew_nothing(map: &mut CMap2) -> &mut CMap2 { - map.one_unsew(4, UnsewPolicy::DoNothing); + map.one_unsew(4); black_box(map) } @@ -120,7 +120,7 @@ fn one_unsew_nothing(map: &mut CMap2) -> &mut CMap2 { #[bench::medium(&mut get_map(50))] #[bench::large(&mut get_map(500))] fn one_unsew_duplicate(map: &mut CMap2) -> &mut CMap2 { - map.one_unsew(4, UnsewPolicy::Duplicate); + map.one_unsew(4); black_box(map) } @@ -136,7 +136,7 @@ library_benchmark_group!( #[bench::medium(&mut get_map(50))] #[bench::large(&mut get_map(500))] fn two_unsew_nothing(map: &mut CMap2) -> &mut CMap2 { - map.two_unsew(4, UnsewPolicy::DoNothing); + map.two_unsew(4); black_box(map) } @@ -145,7 +145,7 @@ fn two_unsew_nothing(map: &mut CMap2) -> &mut CMap2 { #[bench::medium(&mut get_map(50))] #[bench::large(&mut get_map(500))] fn two_unsew_duplicate(map: &mut CMap2) -> &mut CMap2 { - map.two_unsew(4, UnsewPolicy::Duplicate); + map.two_unsew(4); black_box(map) } diff --git a/honeycomb-benches/benches/splitsquaremap/shift.rs b/honeycomb-benches/benches/splitsquaremap/shift.rs index 6fc10c7f..1e07e0a0 100644 --- a/honeycomb-benches/benches/splitsquaremap/shift.rs +++ b/honeycomb-benches/benches/splitsquaremap/shift.rs @@ -31,32 +31,38 @@ use honeycomb_core::{ // ------ CONTENT fn offset(mut map: CMap2, offsets: &[Vector2]) { - (0..map.n_vertices().0).for_each(|vertex_id| { - let current_value = map.vertex(vertex_id as DartIdentifier); - let _ = map.set_vertex( - vertex_id as VertexIdentifier, - *current_value + offsets[vertex_id], + let n_offsets = offsets.len(); + let vertices = map.fetch_vertices(); + vertices.identifiers.iter().for_each(|vertex_id| { + let current_value = map.vertex(*vertex_id as DartIdentifier); + let _ = map.replace_vertex( + *vertex_id as VertexIdentifier, + current_value + offsets[*vertex_id as usize % n_offsets], ); }); black_box(&mut map); } fn offset_if_inner(mut map: CMap2, offsets: &[Vector2]) { + let n_offsets = offsets.len(); let mut inner: BTreeSet = BTreeSet::new(); + let vertices = map.fetch_vertices(); // collect inner vertex IDs - (0..map.n_darts().0 as DartIdentifier).for_each(|dart_id| { + vertices.identifiers.iter().for_each(|vertex_id| { let neighbors_vertex_cell: Vec = map - .i_cell::<0>(dart_id) - .iter() - .map(|d_id| map.beta::<2>(*d_id)) + .i_cell::<0>(*vertex_id as DartIdentifier) + .map(|d_id| map.beta::<2>(d_id)) .collect(); if !neighbors_vertex_cell.contains(&NULL_DART_ID) { - inner.insert(map.vertexid(dart_id)); + inner.insert(*vertex_id); } }); inner.iter().for_each(|vertex_id| { let current_value = map.vertex(*vertex_id); - let _ = map.set_vertex(*vertex_id, *current_value + offsets[*vertex_id as usize]); + let _ = map.replace_vertex( + *vertex_id, + current_value + offsets[*vertex_id as usize % n_offsets], + ); }); black_box(&mut map); } diff --git a/honeycomb-benches/benches/squaremap/shift.rs b/honeycomb-benches/benches/squaremap/shift.rs index 8784f161..1bf301e2 100644 --- a/honeycomb-benches/benches/squaremap/shift.rs +++ b/honeycomb-benches/benches/squaremap/shift.rs @@ -30,32 +30,38 @@ use honeycomb_core::{ // ------ CONTENT fn offset(mut map: CMap2, offsets: &[Vector2]) { - (0..map.n_vertices().0).for_each(|vertex_id| { - let current_value = map.vertex(vertex_id as DartIdentifier); - let _ = map.set_vertex( - vertex_id as VertexIdentifier, - *current_value + offsets[vertex_id], + let n_offsets = offsets.len(); + let vertices = map.fetch_vertices(); + vertices.identifiers.iter().for_each(|vertex_id| { + let current_value = map.vertex(*vertex_id as DartIdentifier); + let _ = map.replace_vertex( + *vertex_id, + current_value + offsets[*vertex_id as usize % n_offsets], ); }); black_box(&mut map); } fn offset_if_inner(mut map: CMap2, offsets: &[Vector2]) { + let n_offsets = offsets.len(); + let vertices = map.fetch_vertices(); let mut inner: BTreeSet = BTreeSet::new(); // collect inner vertex IDs - (0..map.n_darts().0 as DartIdentifier).for_each(|dart_id| { + vertices.identifiers.iter().for_each(|vertex_id| { let neighbors_vertex_cell: Vec = map - .i_cell::<0>(dart_id) - .iter() - .map(|d_id| map.beta::<2>(*d_id)) + .i_cell::<0>(*vertex_id as DartIdentifier) + .map(|d_id| map.beta::<2>(d_id)) .collect(); if !neighbors_vertex_cell.contains(&NULL_DART_ID) { - inner.insert(map.vertexid(dart_id)); + inner.insert(*vertex_id); } }); inner.iter().for_each(|vertex_id| { let current_value = map.vertex(*vertex_id); - let _ = map.set_vertex(*vertex_id, *current_value + offsets[*vertex_id as usize]); + let _ = map.replace_vertex( + *vertex_id, + current_value + offsets[*vertex_id as usize % n_offsets], + ); }); black_box(&mut map); } diff --git a/honeycomb-benches/benches/squaremap/split.rs b/honeycomb-benches/benches/squaremap/split.rs index bc361dbf..1fd17238 100644 --- a/honeycomb-benches/benches/squaremap/split.rs +++ b/honeycomb-benches/benches/squaremap/split.rs @@ -1,7 +1,7 @@ //! This benchmarks handle measurements for a given operation on CMap2 //! of a given topology (see `generation::splitsquare_two_map` doc). //! -//! The operations applied here affect only topology, geometry is left unchanged +//! The operations applied here affect only topology, cells is left unchanged //! aside from the new faces divisiosns. //! //! All splitting operations consist in splitting diagonally a square face to create @@ -25,9 +25,7 @@ use rand::{ SeedableRng, }; -use honeycomb_core::{ - utils::square_cmap2, CMap2, DartIdentifier, FloatType, SewPolicy, UnsewPolicy, -}; +use honeycomb_core::{utils::square_cmap2, CMap2, DartIdentifier, FloatType}; // ------ CONTENT @@ -35,92 +33,111 @@ const N_SQUARE: usize = 2_usize.pow(10); const P_BERNOULLI: f64 = 0.6; fn split(mut map: CMap2) { - (0..N_SQUARE.pow(2)).for_each(|square| { - let d1 = (1 + square * 4) as DartIdentifier; - let (d2, d3, d4) = (d1 + 1, d1 + 2, d1 + 3); - // in a parallel impl, we would create all new darts before-hand - let dsplit1 = map.add_free_darts(2); - let dsplit2 = dsplit1 + 1; - map.two_sew(dsplit1, dsplit2, SewPolicy::StretchLeft); - map.one_unsew(d1, UnsewPolicy::DoNothing); - map.one_unsew(d3, UnsewPolicy::DoNothing); - map.one_sew(d1, dsplit1, SewPolicy::StretchLeft); - map.one_sew(d3, dsplit2, SewPolicy::StretchLeft); - map.one_sew(dsplit1, d4, SewPolicy::StretchRight); - map.one_sew(dsplit2, d2, SewPolicy::StretchRight); - }); + map.fetch_faces() + .identifiers + .iter() + .copied() + .for_each(|square| { + let square = square as DartIdentifier; + let (d1, d2, d3, d4) = (square, square + 1, square + 2, square + 3); + // in a parallel impl, we would create all new darts before-hand + let dsplit1 = map.add_free_darts(2); + let dsplit2 = dsplit1 + 1; + // unsew the square & duplicate vertices to avoid data loss + // this duplication effectively means that there are two existing vertices + // for a short time, before being merged back by the sewing ops + map.one_unsew(d1); + map.one_unsew(d3); + // link the two new dart in order to + map.two_link(dsplit1, dsplit2); + // define beta1 of the new darts, i.e. tell them where they point to + map.one_sew(dsplit1, d4); + map.one_sew(dsplit2, d2); + + // sew the original darts to the new darts + map.one_sew(d1, dsplit1); + map.one_sew(d3, dsplit2); + // fuse the edges; this is where duplicated vertices are merged back together + }); // rebuild faces - assert_eq!(map.build_all_faces(), N_SQUARE.pow(2) * 2); + assert_eq!(map.fetch_faces().identifiers.len(), N_SQUARE.pow(2) * 2); black_box(&mut map); } fn split_some(mut map: CMap2, split: &[bool]) { - (0..N_SQUARE.pow(2)) - .filter(|square| split[*square]) // split only if true + let n_split = split.len(); + map.fetch_faces() + .identifiers + .iter() + .copied() + .filter(|square| split[*square as usize % n_split]) .for_each(|square| { - let d1 = (1 + square * 4) as DartIdentifier; - let (d2, d3, d4) = (d1 + 1, d1 + 2, d1 + 3); + let square = square as DartIdentifier; + let (d1, d2, d3, d4) = (square, square + 1, square + 2, square + 3); // in a parallel impl, we would create all new darts before-hand let dsplit1 = map.add_free_darts(2); let dsplit2 = dsplit1 + 1; - map.two_sew(dsplit1, dsplit2, SewPolicy::StretchLeft); - map.one_unsew(d1, UnsewPolicy::DoNothing); - map.one_unsew(d3, UnsewPolicy::DoNothing); - map.one_sew(d1, dsplit1, SewPolicy::StretchLeft); - map.one_sew(d3, dsplit2, SewPolicy::StretchLeft); - map.one_sew(dsplit1, d4, SewPolicy::StretchRight); - map.one_sew(dsplit2, d2, SewPolicy::StretchRight); + // unsew the square & duplicate vertices to avoid data loss + // this duplication effectively means that there are two existing vertices + // for a short time, before being merged back by the sewing ops + map.one_unsew(d1); + map.one_unsew(d3); + // link the two new dart in order to + map.two_link(dsplit1, dsplit2); + // define beta1 of the new darts, i.e. tell them where they point to + map.one_sew(dsplit1, d4); + map.one_sew(dsplit2, d2); + + // sew the original darts to the new darts + map.one_sew(d1, dsplit1); + map.one_sew(d3, dsplit2); + // fuse the edges; this is where duplicated vertices are merged back together }); - // rebuild faces - map.build_all_faces(); - + black_box(map.fetch_faces()); black_box(&mut map); } fn split_diff(mut map: CMap2, split: &[bool]) { - (0..N_SQUARE.pow(2)).for_each(|square| { - let ddown = (1 + square * 4) as DartIdentifier; - let (dright, dup, dleft) = (ddown + 1, ddown + 2, ddown + 3); - // in a parallel impl, we would create all new darts before-hand - let dsplit1 = map.add_free_darts(2); - let dsplit2 = dsplit1 + 1; - // this leads to the same result as the commented code below; there doesn't seem - // to be any change in performances - let (dbefore1, dbefore2, dafter1, dafter2) = if split[square] { - (ddown, dup, dleft, dright) - } else { - (dright, dleft, ddown, dup) - }; - map.two_sew(dsplit1, dsplit2, SewPolicy::StretchLeft); - map.one_unsew(dbefore1, UnsewPolicy::DoNothing); - map.one_unsew(dbefore2, UnsewPolicy::DoNothing); - map.one_sew(dbefore1, dsplit1, SewPolicy::StretchLeft); - map.one_sew(dbefore2, dsplit2, SewPolicy::StretchLeft); - map.one_sew(dsplit1, dafter1, SewPolicy::StretchRight); - map.one_sew(dsplit2, dafter2, SewPolicy::StretchRight); - /* - if split[square] { - map.one_unsew(ddown, UnsewPolicy::DoNothing); - map.one_unsew(dup, UnsewPolicy::DoNothing); - map.one_sew(ddown, dsplit1, SewPolicy::StretchLeft); - map.one_sew(dup, dsplit2, SewPolicy::StretchLeft); - map.one_sew(dsplit1, dleft, SewPolicy::StretchRight); - map.one_sew(dsplit2, dright, SewPolicy::StretchRight); - } else { - map.one_unsew(dright, UnsewPolicy::DoNothing); - map.one_unsew(dleft, UnsewPolicy::DoNothing); - map.one_sew(dright, dsplit1, SewPolicy::StretchLeft); - map.one_sew(dleft, dsplit2, SewPolicy::StretchLeft); - map.one_sew(dsplit1, ddown, SewPolicy::StretchRight); - map.one_sew(dsplit2, dup, SewPolicy::StretchRight); - }*/ - }); + let n_split = split.len(); + map.fetch_faces() + .identifiers + .iter() + .copied() + .for_each(|square| { + let square = square as DartIdentifier; + let (ddown, dright, dup, dleft) = (square, square + 1, square + 2, square + 3); + // in a parallel impl, we would create all new darts before-hand + let dsplit1 = map.add_free_darts(2); + let dsplit2 = dsplit1 + 1; + + let (dbefore1, dbefore2, dafter1, dafter2) = if split[square as usize % n_split] { + (ddown, dup, dleft, dright) + } else { + (dright, dleft, ddown, dup) + }; + + // unsew the square & duplicate vertices to avoid data loss + // this duplication effectively means that there are two existing vertices + // for a short time, before being merged back by the sewing ops + map.one_unsew(dbefore1); + map.one_unsew(dbefore2); + // link the two new dart in order to + map.two_link(dsplit1, dsplit2); + // define beta1 of the new darts, i.e. tell them where they point to + map.one_sew(dsplit1, dafter1); + map.one_sew(dsplit2, dafter2); + + // sew the original darts to the new darts + map.one_sew(dbefore1, dsplit1); + map.one_sew(dbefore2, dsplit2); + // fuse the edges; this is where duplicated vertices are merged back together + }); // rebuild faces - assert_eq!(map.build_all_faces(), N_SQUARE.pow(2) * 2); + assert_eq!(map.fetch_faces().identifiers.len(), N_SQUARE.pow(2) * 2); black_box(&mut map); } diff --git a/honeycomb-core/src/cells/attribute_collections.rs b/honeycomb-core/src/cells/attribute_collections.rs index 51fdc8e9..08cd119d 100644 --- a/honeycomb-core/src/cells/attribute_collections.rs +++ b/honeycomb-core/src/cells/attribute_collections.rs @@ -25,6 +25,7 @@ use num::ToPrimitive; /// /// todo /// +#[cfg_attr(feature = "utils", derive(Clone))] pub struct AttrSparseVec { data: Vec>, } @@ -47,6 +48,14 @@ impl AttrSparseVec { } } + pub fn extend(&mut self, length: usize) { + self.data.extend((0..length).map(|_| None)); + } + + pub fn n_vertices(&self) -> usize { + self.data.iter().filter(|val| val.is_some()).count() + } + /// Getter /// /// # Arguments @@ -133,17 +142,17 @@ impl AttrSparseVec { /// - `index: T::IdentifierType` -- Cell index. /// - `val: T` -- Attribute value. /// - /// # Panic + /// # Return / Panic + /// + /// Return an option containing the old value if it existed. /// /// The method may panic if: - /// - **there is no value associated to the specified index** /// - the index lands out of bounds /// - the index cannot be converted to `usize` /// - pub fn replace(&mut self, index: T::IdentifierType, val: T) { - let tmp = &mut self.data[index.to_usize().unwrap()]; - assert!(tmp.is_some()); - *tmp = Some(val); + pub fn replace(&mut self, index: T::IdentifierType, val: T) -> Option { + self.data.push(Some(val)); + self.data.swap_remove(index.to_usize().unwrap()) } /// Remove an item from the storage and return it @@ -167,6 +176,21 @@ impl AttrSparseVec { } } +#[cfg(feature = "utils")] +impl AttrSparseVec { + pub fn allocated_size(&self) -> usize { + self.data.capacity() * std::mem::size_of::>() + } + + pub fn effective_size(&self) -> usize { + self.data.len() * std::mem::size_of::>() + } + + pub fn used_size(&self) -> usize { + self.data.iter().filter(|val| val.is_some()).count() * std::mem::size_of::>() + } +} + /// Custom storage structure for attributes /// /// This structured is used to store user-defined attributes using two internal collections: @@ -184,13 +208,14 @@ impl AttrSparseVec { /// /// todo /// -pub struct AttrCompactVec { +#[cfg_attr(feature = "utils", derive(Clone))] +pub struct AttrCompactVec { unused_data_slots: Vec, index_map: Vec>, data: Vec, } -impl AttrCompactVec { +impl AttrCompactVec { pub fn new(n_ids: usize) -> Self { Self { unused_data_slots: Vec::new(), @@ -199,6 +224,14 @@ impl AttrCompactVec { } } + pub fn extend(&mut self, length: usize) { + self.index_map.extend((0..length).map(|_| None)); + } + + pub fn n_vertices(&self) -> usize { + self.data.len() + } + pub fn get(&self, index: T::IdentifierType) -> Option<&T> { self.index_map[index.to_usize().unwrap()].map(|idx| &self.data[idx]) } @@ -234,23 +267,45 @@ impl AttrCompactVec { }; } - pub fn replace(&mut self, index: T::IdentifierType, val: T) { + pub fn replace(&mut self, index: T::IdentifierType, val: T) -> Option { let idx = &self.index_map[index.to_usize().unwrap()]; assert!(idx.is_some()); - self.data[idx.unwrap()] = val; + self.data.push(val); + Some(self.data.swap_remove(idx.unwrap())) } pub fn remove(&mut self, index: T::IdentifierType) -> Option { self.index_map.push(None); if let Some(tmp) = self.index_map.swap_remove(index.to_usize().unwrap()) { self.unused_data_slots.push(tmp); - self.data.push(T::default()); - return Some(self.data.swap_remove(tmp)); + return Some(self.data[tmp].clone()); }; None } } +#[cfg(feature = "utils")] +impl AttrCompactVec { + pub fn allocated_size(&self) -> usize { + self.unused_data_slots.capacity() * std::mem::size_of::() + + self.index_map.capacity() * std::mem::size_of::>() + + self.data.capacity() * std::mem::size_of::() + } + + pub fn effective_size(&self) -> usize { + self.unused_data_slots.len() * std::mem::size_of::() + + self.index_map.len() * std::mem::size_of::>() + + self.data.len() * std::mem::size_of::() + } + + pub fn used_size(&self) -> usize { + self.unused_data_slots.len() * std::mem::size_of::() + + self.index_map.iter().filter(|val| val.is_some()).count() + * std::mem::size_of::>() + + self.data.len() * std::mem::size_of::() + } +} + // ------ TESTS #[cfg(test)] @@ -373,7 +428,7 @@ mod tests { fn sparse_vec_remove_replace() { generate_sparse!(storage); assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - storage.replace(3, Temperature::from(280.0)); // panic + storage.replace(3, Temperature::from(280.0)).unwrap(); // panic } macro_rules! generate_compact { diff --git a/honeycomb-core/src/cells/collections.rs b/honeycomb-core/src/cells/collections.rs new file mode 100644 index 00000000..1ae195a3 --- /dev/null +++ b/honeycomb-core/src/cells/collections.rs @@ -0,0 +1,43 @@ +//! Module short description +//! +//! Should you interact with this module directly? +//! +//! Content description if needed + +// ------ IMPORTS + +use crate::{CMap2, CoordsFloat, EdgeIdentifier, FaceIdentifier, VertexIdentifier}; + +// ------ CONTENT + +pub struct VertexCollection<'a, T: CoordsFloat> { + lifetime_indicator: std::marker::PhantomData<&'a CMap2>, + pub identifiers: Vec, +} + +pub struct EdgeCollection<'a, T: CoordsFloat> { + lifetime_indicator: std::marker::PhantomData<&'a CMap2>, + pub identifiers: Vec, +} + +pub struct FaceCollection<'a, T: CoordsFloat> { + lifetime_indicator: std::marker::PhantomData<&'a CMap2>, + pub identifiers: Vec, +} + +macro_rules! collection_constructor { + ($coll: ident, $idty: ty) => { + impl<'a, T: CoordsFloat> $coll<'a, T> { + pub fn new(_: &'a CMap2, ids: impl IntoIterator) -> Self { + Self { + lifetime_indicator: std::marker::PhantomData::default(), + identifiers: ids.into_iter().collect(), + } + } + } + }; +} + +collection_constructor!(VertexCollection, VertexIdentifier); +collection_constructor!(EdgeCollection, EdgeIdentifier); +collection_constructor!(FaceCollection, FaceIdentifier); diff --git a/honeycomb-core/src/cells/mod.rs b/honeycomb-core/src/cells/mod.rs index bdf72389..8cb9306a 100644 --- a/honeycomb-core/src/cells/mod.rs +++ b/honeycomb-core/src/cells/mod.rs @@ -6,5 +6,6 @@ pub mod attribute_collections; pub mod attributes; +pub mod collections; pub mod identifiers; pub mod orbits; diff --git a/honeycomb-core/src/cells/orbits.rs b/honeycomb-core/src/cells/orbits.rs index a270535b..4ff92517 100644 --- a/honeycomb-core/src/cells/orbits.rs +++ b/honeycomb-core/src/cells/orbits.rs @@ -6,7 +6,6 @@ // ------ IMPORTS use crate::{CMap2, CoordsFloat, DartIdentifier, NULL_DART_ID}; - use num::Zero; use std::collections::{BTreeSet, VecDeque}; @@ -116,6 +115,40 @@ impl<'a, T: CoordsFloat> Orbit2<'a, T> { pending, } } + + pub fn is_isolated(&self) -> bool { + // this is boolean tells us if the orbit is either: + // a) unaltered (pending.len() == 1) + // b) iterated upon, but the dart is + let marked_unaltered = self.marked.len() == 2; + let pending_unaltered = self.pending.len() == 1; + match (marked_unaltered, pending_unaltered) { + // marked is altered, i.e. we found a dart that isn't null or the starting one + (false, _) => false, + // marked is unaltered but pending is altered + // we checked for neighbors but no new mark appeared + // => the dart is isolated or self-indident + (true, false) => true, + // both are unaltered, we need to check if neighbors exist + // we check manually to not invalidate an iterator call later + (true, true) => { + let dart = self.pending[0]; + let image = match self.orbit_policy { + OrbitPolicy::Vertex => { + self.map_handle.beta::<1>(self.map_handle.beta::<2>(dart)) + } + OrbitPolicy::Edge => self.map_handle.beta::<2>(dart), + OrbitPolicy::Face => self.map_handle.beta::<1>(dart), + OrbitPolicy::Custom(beta_slice) => beta_slice + .iter() + .map(|beta_id| self.map_handle.beta_runtime(*beta_id, dart)) + .find(|d| *d != NULL_DART_ID) + .unwrap_or(NULL_DART_ID), + }; + image == NULL_DART_ID + } + } + } } impl<'a, T: CoordsFloat> Iterator for Orbit2<'a, T> { @@ -126,13 +159,20 @@ impl<'a, T: CoordsFloat> Iterator for Orbit2<'a, T> { match self.orbit_policy { OrbitPolicy::Vertex => { // THIS CODE IS ONLY VALID IN 2D - // WE ASSUME THAT THE EDGE IS COMPLETE - let image = self.map_handle.beta::<1>(self.map_handle.beta::<2>(d)); - if self.marked.insert(image) { + + 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(image); + 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); } + Some(d) } OrbitPolicy::Edge => { @@ -182,23 +222,17 @@ mod tests { use crate::{CMap2, DartIdentifier, FloatType, Orbit2, OrbitPolicy}; fn simple_map() -> CMap2 { - let mut map: CMap2 = CMap2::new(6, 4); + let mut map: CMap2 = CMap2::new(6); map.set_betas(1, [3, 2, 0]); map.set_betas(2, [1, 3, 4]); map.set_betas(3, [2, 1, 0]); map.set_betas(4, [6, 5, 2]); map.set_betas(5, [4, 6, 0]); map.set_betas(6, [5, 4, 0]); - map.set_vertex(0, (0.0, 0.0)).unwrap(); - map.set_vertex(1, (1.0, 0.0)).unwrap(); - map.set_vertex(2, (1.0, 1.0)).unwrap(); - map.set_vertex(3, (0.0, 1.0)).unwrap(); - map.set_vertexid(1, 0); - map.set_vertexid(2, 1); - map.set_vertexid(3, 3); - map.set_vertexid(4, 3); - map.set_vertexid(5, 1); - map.set_vertexid(6, 2); + assert!(map.replace_vertex(1, (0.0, 0.0)).is_err()); + assert!(map.replace_vertex(2, (1.0, 0.0)).is_err()); + assert!(map.replace_vertex(6, (1.0, 1.0)).is_err()); + assert!(map.replace_vertex(3, (0.0, 1.0)).is_err()); map } diff --git a/honeycomb-core/src/lib.rs b/honeycomb-core/src/lib.rs index 78aae922..2a4ea58a 100644 --- a/honeycomb-core/src/lib.rs +++ b/honeycomb-core/src/lib.rs @@ -30,6 +30,7 @@ pub mod utils; pub use cells::{ attribute_collections::{AttrCompactVec, AttrSparseVec}, attributes::{AttributeBind, AttributeUpdate}, + collections::{EdgeCollection, FaceCollection, VertexCollection}, identifiers::*, orbits::{Orbit2, OrbitPolicy}, }; diff --git a/honeycomb-core/src/twomap.rs b/honeycomb-core/src/twomap.rs index 0eddc765..a4f0b5b7 100644 --- a/honeycomb-core/src/twomap.rs +++ b/honeycomb-core/src/twomap.rs @@ -10,9 +10,9 @@ // ------ IMPORTS -use super::dart::CellIdentifiers; use crate::{ - CoordsFloat, DartData, DartIdentifier, Face, FaceIdentifier, SewPolicy, UnsewPolicy, Vertex2, + AttrSparseVec, AttributeUpdate, CoordsFloat, DartIdentifier, EdgeCollection, EdgeIdentifier, + FaceCollection, FaceIdentifier, Orbit2, OrbitPolicy, Vertex2, VertexCollection, VertexIdentifier, NULL_DART_ID, }; @@ -22,9 +22,14 @@ use std::{fs::File, io::Write}; // ------ CONTENT +/// Error-modeling enum +/// +/// This enum is used to describe all non-panic errors that can occur when operating on a map. #[derive(Debug)] pub enum CMapError { - VertexOOB, + /// Variant used when requesting a vertex using an ID that has no associated vertex + /// in storage. + UndefinedVertex, } // --- 2-MAP @@ -56,15 +61,10 @@ const CMAP2_BETA: usize = 3; /// contains the following data: /// /// - `vertices: Vec` -- List of vertices making up the represented mesh -/// - `free_vertices: BTreeSet` -- Set of free vertex identifiers, -/// i.e. empty spots in the current vertex list -/// - `faces: Vec` -- List of faces making up the represented mesh -/// - `dart_data: DartData` -- Structure holding embedded data associated with darts /// - `free_darts: BTreeSet` -- Set of free darts identifiers, i.e. empty /// spots in the current dart list /// - `betas: Vec<[DartIdentifier; 3]>` -- Array representation of the beta functions /// - `n_darts: usize` -- Current number of darts (including the null dart) -/// - `n_vertices: usize` -- Current number of vertices /// /// Note that we encode *β0* as the inverse function of *β1*. /// This is extremely useful (read *required*) to implement correct and efficient @@ -89,165 +89,8 @@ const CMAP2_BETA: usize = 3; /// ``` /// # use honeycomb_core::CMapError; /// # fn main() -> Result<(), CMapError> { -/// use honeycomb_core::{ DartIdentifier, SewPolicy, CMap2, UnsewPolicy, VertexIdentifier, NULL_DART_ID, Orbit2, OrbitPolicy}; -/// -/// // --- Map creation -/// -/// // create a map with 3 non-null darts & 3 vertices -/// let mut map: CMap2 = CMap2::new(3, 3); -/// -/// // the two following lines are not strictly necessary, you may use integers directly -/// let (d1, d2, d3): (DartIdentifier, DartIdentifier, DartIdentifier) = (1, 2, 3); -/// let (v1, v2, v3): (VertexIdentifier, VertexIdentifier, VertexIdentifier) = (0, 1, 2); -/// -/// // place the vertices in space -/// map.set_vertex(v1, [0.0, 0.0])?; -/// map.set_vertex(v2, [0.0, 10.0])?; -/// map.set_vertex(v3, [10.0, 0.0])?; -/// // associate dart to vertices -/// map.set_vertexid(d1, v1); -/// map.set_vertexid(d2, v2); -/// map.set_vertexid(d3, v3); -/// // define beta values to form a face -/// map.set_betas(d1, [d3, d2, NULL_DART_ID]); // beta 0 / 1 / 2 (d1) = d3 / d2 / null -/// map.set_betas(d2, [d1, d3, NULL_DART_ID]); // beta 0 / 1 / 2 (d2) = d1 / d3 / null -/// map.set_betas(d3, [d2, d1, NULL_DART_ID]); // beta 0 / 1 / 2 (d3) = d2 / d1 / null -/// -/// // build the face we just linked & fetch the id for checks -/// let fface_id = map.build_face(d1); -/// -/// // --- checks -/// -/// // fetch cells associated to each dart -/// let d1_cells = map.cells(d1); -/// let d2_cells = map.cells(d2); -/// let d3_cells = map.cells(d3); -/// -/// // check dart-vertex association -/// assert_eq!(d1_cells.vertex_id, v1); -/// assert_eq!(d2_cells.vertex_id, v2); -/// assert_eq!(d3_cells.vertex_id, v3); -/// -/// // check dart-face association -/// assert_eq!(d1_cells.face_id, fface_id); -/// assert_eq!(d2_cells.face_id, fface_id); -/// assert_eq!(d3_cells.face_id, fface_id); -/// -/// // fetch all darts of the two-cell d2 belongs to -/// // i.e. the face -/// let two_cell = map.i_cell::<2>(d2); // directly -/// let orbit = Orbit2::new(&map, OrbitPolicy::Face, d2); // using an orbit -/// let two_cell_from_orbit: Vec = orbit.collect(); -/// -/// // check topology of the face -/// // we make no assumption on the ordering of the result when using the i_cell method -/// assert!(two_cell.contains(&d1)); -/// assert!(two_cell.contains(&d2)); -/// assert!(two_cell.contains(&d3)); -/// assert_eq!(two_cell.len(), 3); -/// assert!(two_cell_from_orbit.contains(&d1)); -/// assert!(two_cell_from_orbit.contains(&d2)); -/// assert!(two_cell_from_orbit.contains(&d3)); -/// assert_eq!(two_cell_from_orbit.len(), 3); -/// -/// // --- (a) -/// -/// // add three new darts -/// let d4 = map.add_free_dart(); // 4 -/// let d5 = map.add_free_dart(); // 5 -/// let d6 = map.add_free_dart(); // 6 -/// -/// assert!(map.is_free(d4)); -/// assert!(map.is_free(d5)); -/// assert!(map.is_free(d6)); -/// -/// // create the corresponding three vertices -/// let v4 = map.add_vertex(Some([15.0, 0.0].into())); // v4 -/// let v5 = map.add_vertices(2); // v5, v6 -/// let v6 = v5 + 1; -/// map.set_vertex(v5, [5.0, 10.0])?; // v5 -/// map.set_vertex(v6, [15.0, 10.0])?; // v6 -/// // associate dart to vertices -/// map.set_vertexid(d4, v4); -/// map.set_vertexid(d5, v5); -/// map.set_vertexid(d6, v6); -/// // define beta values to form a second face -/// map.set_betas(d4, [d6, d5, NULL_DART_ID]); // beta 0 / 1 / 2 (d4) = d6 / d5 / null -/// map.set_betas(d5, [d4, d6, NULL_DART_ID]); // beta 0 / 1 / 2 (d5) = d4 / d6 / null -/// map.set_betas(d6, [d5, d4, NULL_DART_ID]); // beta 0 / 1 / 2 (d6) = d5 / d4 / null -/// -/// let sface_id = map.build_face(d6); // build the second face -/// -/// // --- checks -/// -/// // d4 & d2 are 2-free, hence can be 2-sewn together -/// assert!(map.is_i_free::<2>(d4)); -/// assert!(map.is_i_free::<2>(d2)); -/// -/// // --- (b) -/// -/// // 2-sew d2 & d4, stretching d4 to d2's spatial position -/// // this invalidates the face built before since vertex are overwritten -/// // if we used a StretchRight policy, the invalidated face would have been the first one -/// map.two_sew(d2, d4, SewPolicy::StretchLeft); -/// -/// // --- checks -/// -/// // check topological result -/// assert_eq!(map.beta::<2>(d2), d4); -/// assert_eq!(map.beta::<2>(d4), d2); -/// // check geometrical result -/// assert_eq!(map.vertexid(d2), map.vertexid(d5)); -/// assert_eq!(map.vertexid(d3), map.vertexid(d4)); -/// -/// // --- (c) -/// -/// // shift the position of d6 to build a square using the two faces -/// map.set_vertex(map.vertexid(d6), [10.0, 10.0])?; -/// -/// // --- (d) -/// -/// // disconnect d2 & d4 for removal -/// map.one_unsew(d2, UnsewPolicy::Duplicate); // using unsew here allow proper vertices -/// map.one_unsew(d4, UnsewPolicy::Duplicate); // modifications -/// map.set_beta::<0>(d2, NULL_DART_ID); -/// map.set_beta::<0>(d4, NULL_DART_ID); -/// map.set_beta::<2>(d2, NULL_DART_ID); -/// map.set_beta::<2>(d4, NULL_DART_ID); -/// map.remove_free_dart(d2); // this checks if d2 is free for all i -/// map.remove_free_dart(d4); // this checks if d4 is free for all i -/// -/// // reconnect d1/d5 & d6/d3 to form the new face -/// map.set_beta::<1>(d1, d5); -/// map.set_beta::<0>(d5, d1); -/// map.set_beta::<1>(d6, d3); -/// map.set_beta::<0>(d3, d6); -/// -/// // rebuild the face -/// -/// let new_face_id = map.build_face(d6); -/// -/// // --- checks -/// -/// // check associated face -/// assert_eq!(map.faceid(d1), new_face_id); -/// assert_eq!(map.faceid(d5), new_face_id); -/// assert_eq!(map.faceid(d6), new_face_id); -/// assert_eq!(map.faceid(d3), new_face_id); /// -/// // check dart positions -/// assert_eq!(*map.vertex(map.vertexid(d1)), [0.0, 0.0].into()); -/// assert_eq!(*map.vertex(map.vertexid(d5)), [0.0, 10.0].into()); -/// assert_eq!(*map.vertex(map.vertexid(d6)), [10.0, 10.0].into()); -/// assert_eq!(*map.vertex(map.vertexid(d3)), [10.0, 0.0].into()); -/// -/// // check topology of the new face -/// let new_two_cell = map.i_cell::<2>(d3); -/// assert!(new_two_cell.contains(&d1)); -/// assert!(new_two_cell.contains(&d5)); -/// assert!(new_two_cell.contains(&d6)); -/// assert!(new_two_cell.contains(&d3)); -/// assert_eq!(new_two_cell.len(), 4); +/// // todo: write a new example /// /// # Ok(()) /// # } @@ -255,14 +98,7 @@ const CMAP2_BETA: usize = 3; #[cfg_attr(feature = "utils", derive(Clone))] pub struct CMap2 { /// List of vertices making up the represented mesh - vertices: Vec>, - /// List of free vertex identifiers, i.e. empty spots - /// in the current vertex list - unused_vertices: BTreeSet, - /// List of faces making up the represented mesh - faces: Vec, - /// Structure holding data related to darts (marks, associated cells) - dart_data: DartData, + vertices: AttrSparseVec>, /// List of free darts identifiers, i.e. empty spots /// in the current dart list unused_darts: BTreeSet, @@ -270,17 +106,9 @@ pub struct CMap2 { betas: Vec<[DartIdentifier; CMAP2_BETA]>, /// Current number of darts n_darts: usize, - /// Current number of vertices - n_vertices: usize, -} - -macro_rules! stretch { - ($slf: ident, $replaced: expr, $replacer: expr) => { - $slf.dart_data.associated_cells[$replaced as usize].vertex_id = - $slf.dart_data.associated_cells[$replacer as usize].vertex_id - }; } +// --- constructor impl CMap2 { /// Creates a new 2D combinatorial map. /// @@ -302,60 +130,131 @@ impl CMap2 { /// /// See [CMap2] example. /// - pub fn new(n_darts: usize, n_vertices: usize) -> Self { - let vertices = vec![Vertex2::default(); n_vertices]; - let betas = vec![[0; CMAP2_BETA]; n_darts + 1]; - + pub fn new(n_darts: usize) -> Self { Self { - vertices, - unused_vertices: BTreeSet::new(), - faces: Vec::with_capacity(n_darts / 3), - dart_data: DartData::new(n_darts), + vertices: AttrSparseVec::new(n_darts + 1), unused_darts: BTreeSet::new(), - betas, + betas: vec![[0; CMAP2_BETA]; n_darts + 1], n_darts: n_darts + 1, - n_vertices, } } +} - // --- reading interfaces +// --- dart-related code +impl CMap2 { + // --- read - /// Return information about the current number of vertices. + /// Return information about the current number of darts. /// /// # Return / Panic /// /// Return a tuple of two elements: /// - /// - the number of vertices - /// - a boolean indicating whether there are free vertices or not + /// - the number of darts + /// - a boolean indicating whether there are unused darts or not /// - /// The boolean essentially indicates if it is safe to access all - /// vertex IDs in the `0..n_vertices` range. + /// The boolean essentially indicates if it is safe to access & use all dart IDs in the + /// `1..n_darts+1` range. /// - pub fn n_vertices(&self) -> (usize, bool) { - (self.n_vertices, !self.unused_vertices.is_empty()) + pub fn n_darts(&self) -> (usize, bool) { + (self.n_darts, !self.unused_darts.is_empty()) } - /// Return the current number of faces. - pub fn n_faces(&self) -> usize { - self.faces.len() + // --- edit + + /// Add a new free dart to the combinatorial map. + /// + /// The dart is i-free for all i and is pushed to the list of existing darts, effectively + /// making its identifier equal to the total number of darts (post-push). + /// + /// # Return / Panic + /// + /// Return the ID of the created dart to allow for direct operations. + /// + pub fn add_free_dart(&mut self) -> DartIdentifier { + let new_id = self.n_darts as DartIdentifier; + self.n_darts += 1; + self.betas.push([0; CMAP2_BETA]); + self.vertices.extend(1); + new_id } - /// Return information about the current number of darts. + /// Add multiple new free darts to the combinatorial map. + /// + /// All darts are i-free for all i and are pushed to the end of the list of existing darts. + /// + /// # Arguments + /// + /// - `n_darts: usize` -- Number of darts to have. /// /// # Return / Panic /// - /// Return a tuple of two elements: + /// Return the ID of the first created dart to allow for direct operations. Darts are + /// positioned on range `ID..ID+n_darts`. /// - /// - the number of darts - /// - a boolean indicating whether there are free darts or not + pub fn add_free_darts(&mut self, n_darts: usize) -> DartIdentifier { + let new_id = self.n_darts as DartIdentifier; + self.n_darts += n_darts; + self.betas.extend((0..n_darts).map(|_| [0; CMAP2_BETA])); + self.vertices.extend(n_darts); + new_id + } + + /// Insert a new free dart to the combinatorial map. /// - /// The boolean essentially indicates if it is safe to access all - /// dart IDs in the `0..n_darts` range. + /// The dart is i-free for all i and may be inserted into an unused spot in the existing dart + /// list. If no free spots exist, it will be pushed to the end of the list. /// - pub fn n_darts(&self) -> (usize, bool) { - (self.n_darts, !self.unused_darts.is_empty()) + /// # Return / Panic + /// + /// Return the ID of the created dart to allow for direct operations. + /// + pub fn insert_free_dart(&mut self) -> DartIdentifier { + if let Some(new_id) = self.unused_darts.pop_first() { + self.betas[new_id as usize] = [0; CMAP2_BETA]; + new_id + } else { + self.add_free_dart() + } + } + + /// Remove a free dart from the combinatorial map. + /// + /// The removed dart identifier is added to the list of free dart. This way of proceeding is + /// necessary as the structure relies on darts indexing for encoding data, making reordering of + /// any sort extremely costly. + /// + /// By keeping track of free spots in the dart arrays, we can prevent too much memory waste, + /// although at the cost of locality of reference. + /// + /// # Arguments + /// + /// - `dart_id: DartIdentifier` -- Identifier of the dart to remove. + /// + /// # Panic + /// + /// This method may panic if: + /// + /// - The dart is not *i*-free for all *i*. + /// - The dart is already marked as unused (Refer to [Self::remove_vertex] documentation for + /// a detailed breakdown of this choice). + /// + pub fn remove_free_dart(&mut self, dart_id: DartIdentifier) { + assert!(self.is_free(dart_id)); + assert!(self.unused_darts.insert(dart_id)); + let b0d = self.beta::<0>(dart_id); + let b1d = self.beta::<1>(dart_id); + let b2d = self.beta::<2>(dart_id); + self.betas[dart_id as usize] = [0; CMAP2_BETA]; + self.betas[b0d as usize][1] = 0 as DartIdentifier; + self.betas[b1d as usize][0] = 0 as DartIdentifier; + self.betas[b2d as usize][2] = 0 as DartIdentifier; } +} + +// --- beta-related code +impl CMap2 { + // --- read /// Compute the value of the i-th beta function of a given dart. /// @@ -370,16 +269,11 @@ impl CMap2 { /// /// # Return / Panic /// - /// Return the identifier of the dart *d* such that *d = βi(dart)*. If - /// the returned value is the null dart (i.e. a dart identifier equal to 0), this - /// means that *dart* is i-free . + /// Return the identifier of the dart *d* such that *d = βi(dart)*. If the returned + /// value is the null dart (i.e. a dart ID equal to 0), this means that *dart* is i-free. /// /// The method will panic if *I* is not 0, 1 or 2. /// - /// # Example - /// - /// See [CMap2] example. - /// pub fn beta(&self, dart_id: DartIdentifier) -> DartIdentifier { assert!(I < 3); self.betas[dart_id as usize][I as usize] @@ -390,22 +284,15 @@ impl CMap2 { /// # Arguments /// /// - `dart_id: DartIdentifier` -- Identifier of *dart*. - /// - /// - `i: u8` -- Index of the beta function. *i* should - /// be 0, 1 or 2 for a 2D map. + /// - `i: u8` -- Index of the beta function. *i* should be 0, 1 or 2 for a 2D map. /// /// # Return / Panic /// - /// Return the identifier of the dart *d* such that *d = βi(dart)*. If - /// the returned value is the null dart (i.e. a dart identifier equal to 0), this - /// means that *dart* is i-free . + /// Return the identifier of the dart *d* such that *d = βi(dart)*. If the returned + /// value is the null dart (i.e. a dart ID equal to 0), this means that *dart* is i-free. /// /// The method will panic if *i* is not 0, 1 or 2. /// - /// # Example - /// - /// See [CMap2] example. - /// pub fn beta_runtime(&self, i: u8, dart_id: DartIdentifier) -> DartIdentifier { assert!(i < 3); match i { @@ -416,80 +303,81 @@ impl CMap2 { } } - /// Fetch cells associated to a given dart. + /// Check if a given dart is i-free. /// /// # Arguments /// /// - `dart_id: DartIdentifier` -- Identifier of *dart*. /// - /// # Return / Panic + /// ## Generics /// - /// Return a reference to a structure that contain identifiers to the different - /// **geometrical** i-cells *dart* models. + /// - `const I: u8` -- Index of the beta function. *I* should be 0, 1 or 2 for a 2D map. /// - /// # Example + /// # Return / Panic /// - /// See [CMap2] example. + /// Return a boolean indicating if *dart* is i-free, i.e. *βi(dart) = NullDart*. /// - pub fn cells(&self, dart_id: DartIdentifier) -> &CellIdentifiers { - &self.dart_data.associated_cells[dart_id as usize] + /// The function will panic if *I* is not 0, 1 or 2. + /// + pub fn is_i_free(&self, dart_id: DartIdentifier) -> bool { + self.beta::(dart_id) == NULL_DART_ID } - /// Fetch vertex value associated to a given identifier. + /// Check if a given dart is i-free, for all i. /// /// # Arguments /// - /// - `vertex_id: VertexIdentifier` -- Identifier of the given vertex. + /// - `dart_id: DartIdentifier` -- Identifier of *dart*. /// /// # Return / Panic /// - /// Return a reference to the [Vertex2] associated to the ID. - /// - /// # Example - /// - /// See [CMap2] example. + /// Return a boolean indicating if *dart* is 0-free, 1-free and 2-free. /// - pub fn vertex(&self, vertex_id: VertexIdentifier) -> &Vertex2 { - &self.vertices[vertex_id as usize] + pub fn is_free(&self, dart_id: DartIdentifier) -> bool { + self.beta::<0>(dart_id) == NULL_DART_ID + && self.beta::<1>(dart_id) == NULL_DART_ID + && self.beta::<2>(dart_id) == NULL_DART_ID } - /// Fetch face structure associated to a given identifier. + // --- edit + + /// Set the values of the *βi* function of a dart. /// /// # Arguments /// - /// - `face_id: FaceIdentifier` -- Identifier of the given face. + /// - `dart_id: DartIdentifier` -- ID of the dart of interest. + /// - `beta: DartIdentifier` -- Value of *βI(dart)* /// - /// # Return / Panic + /// ## Generics /// - /// Return a reference to the [Face] associated to the ID. + /// - `const I: u8` -- Dimension of the cell of interest. *I* should be 0 (vertex), 1 (edge) or + /// 2 (face) for a 2D map. /// - /// # Example + /// # Return / Panic /// - /// See [CMap2] example. + /// The method will panic if *I* is not 0, 1 or 2. /// - pub fn face(&self, face_id: FaceIdentifier) -> &Face { - &self.faces[face_id as usize] + pub fn set_beta(&mut self, dart_id: DartIdentifier, beta: DartIdentifier) { + assert!(I < 3); + self.betas[dart_id as usize][I as usize] = beta; } - /// Fetch vertex identifier associated to a given dart. + /// Set the values of the beta functions of a dart. /// /// # Arguments /// - /// - `dart_id: DartIdentifier` -- Identifier of *dart*. - /// - /// # Return / Panic - /// - /// Return the identifier of the associated vertex. - /// - /// # Example - /// - /// See [CMap2] example. + /// - `dart_id: DartIdentifier` -- ID of the dart of interest. + /// - `betas: [DartIdentifier; 3]` -- Value of the images as + /// *[β0(dart), β1(dart), β2(dart)]* /// - pub fn vertexid(&self, dart_id: DartIdentifier) -> VertexIdentifier { - self.dart_data.associated_cells[dart_id as usize].vertex_id + pub fn set_betas(&mut self, dart_id: DartIdentifier, betas: [DartIdentifier; CMAP2_BETA]) { + self.betas[dart_id as usize] = betas; } +} - /// Fetch face associated to a given dart. +// --- icell-related code +impl CMap2 { + /// Fetch vertex identifier associated to a given dart. /// /// # Arguments /// @@ -497,43 +385,57 @@ impl CMap2 { /// /// # Return / Panic /// - /// Return the identifier of the associated face. + /// Return the identifier of the associated vertex. /// - /// # Example + /// Cells identifiers are defined as the smallest identifier among the darts that make up the + /// cell. This definition has three interesting properties: /// - /// See [CMap2] example. + /// - A given cell ID can be computed from any dart of the cell, i.e. all darts have an + /// associated cell ID. + /// - Cell IDs are not affected by the order of traversal of the map. + /// - Because the ID is computed in real time, there is no need to store cell IDs and ensure + /// that the storage is consistent / up to date. + /// + /// These properties come at the literal cost of the computation routine, which is: + /// 1. a BFS to compute a given orbit + /// 2. a minimum computation on the IDs composing the orbit /// - pub fn faceid(&self, dart_id: DartIdentifier) -> FaceIdentifier { - self.dart_data.associated_cells[dart_id as usize].face_id + pub fn vertex_id(&self, dart_id: DartIdentifier) -> VertexIdentifier { + // unwraping the result is safe because the orbit is always non empty + Orbit2::new(self, OrbitPolicy::Vertex, dart_id) + .min() + .unwrap() as VertexIdentifier } - /// Check if a given dart is i-free. + /// Fetch edge associated to a given dart. /// /// # Arguments /// /// - `dart_id: DartIdentifier` -- Identifier of *dart*. /// - /// ## Generics - /// - /// - `const I: u8` -- Index of the beta function. *I* should - /// be 0, 1 or 2 for a 2D map. - /// /// # Return / Panic /// - /// Return a boolean indicating if *dart* is i-free, i.e. - /// *βi(dart) = NullDart*. + /// Return the identifier of the associated edge. /// - /// The function will panic if *I* is not 0, 1 or 2. + /// Cells identifiers are defined as the smallest identifier among the darts that make up the + /// cell. This definition has three interesting properties: /// - /// # Example + /// - A given cell ID can be computed from any dart of the cell, i.e. all darts have an + /// associated cell ID. + /// - Cell IDs are not affected by the order of traversal of the map. + /// - Because the ID is computed in real time, there is no need to store cell IDs and ensure + /// that the storage is consistent / up to date. /// - /// See [CMap2] example. + /// These properties come at the literal cost of the computation routine, which is: + /// 1. a BFS to compute a given orbit + /// 2. a minimum computation on the IDs composing the orbit /// - pub fn is_i_free(&self, dart_id: DartIdentifier) -> bool { - self.beta::(dart_id) == NULL_DART_ID + pub fn edge_id(&self, dart_id: DartIdentifier) -> EdgeIdentifier { + // unwraping the result is safe because the orbit is always non empty + Orbit2::new(self, OrbitPolicy::Edge, dart_id).min().unwrap() as EdgeIdentifier } - /// Check if a given dart is i-free, for all i. + /// Fetch face associated to a given dart. /// /// # Arguments /// @@ -541,21 +443,27 @@ impl CMap2 { /// /// # Return / Panic /// - /// Return a boolean indicating if *dart* is 0-free, 1-free and 2-free. + /// Return the identifier of the associated face. /// - /// # Example + /// Cells identifiers are defined as the smallest identifier among the darts that make up the + /// cell. This definition has three interesting properties: /// - /// See [CMap2] example. + /// - A given cell ID can be computed from any dart of the cell, i.e. all darts have an + /// associated cell ID. + /// - Cell IDs are not affected by the order of traversal of the map. + /// - Because the ID is computed in real time, there is no need to store cell IDs and ensure + /// that the storage is consistent / up to date. /// - pub fn is_free(&self, dart_id: DartIdentifier) -> bool { - self.beta::<0>(dart_id) == NULL_DART_ID - && self.beta::<1>(dart_id) == NULL_DART_ID - && self.beta::<2>(dart_id) == NULL_DART_ID + /// These properties come at the literal cost of the computation routine, which is: + /// 1. a BFS to compute a given orbit + /// 2. a minimum computation on the IDs composing the orbit + /// + pub fn face_id(&self, dart_id: DartIdentifier) -> FaceIdentifier { + // unwraping the result is safe because the orbit is always non empty + Orbit2::new(self, OrbitPolicy::Face, dart_id).min().unwrap() as FaceIdentifier } - // orbits / i-cells - - /// Return the identifiers of all dart composing an i-cell. + /// Return an [Orbit2] object that can be used to iterate over darts of an i-cell. /// /// # Arguments /// @@ -563,404 +471,117 @@ impl CMap2 { /// /// ## Generics /// - /// - `const I: u8` -- Dimension of the cell of interest. *I* should - /// be 0 (vertex), 1 (edge) or 2 (face) for a 2D map. + /// - `const I: u8` -- Dimension of the cell of interest. *I* should be 0 (vertex), 1 (edge) or + /// 2 (face) for a 2D map. /// /// # Return / Panic /// - /// Returns a vector of IDs of the darts of the i-cell of *dart* (including - /// *dart* at index 0). - /// - /// KNOWN ISSUE: - /// - /// - returning a vector is highly inefficient; a few alternatives to consider: - /// ArrayVec or heap-less Vec (requires a hard cap on the number of elements), - /// an iterator... - /// - /// # Example - /// - /// See [CMap2] example. + /// Returns an [Orbit2] that 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**. /// - pub fn i_cell(&self, dart_id: DartIdentifier) -> Vec { - let mut cell: Vec = vec![dart_id]; - let mut curr_dart = dart_id; + pub fn i_cell(&self, dart_id: DartIdentifier) -> Orbit2 { + assert!(I < 3); match I { - 0 => { - let mut completeness = true; - // rotate around the vertex until we get back to the first dart - while self.beta::<1>(self.beta::<2>(curr_dart)) != dart_id { - curr_dart = self.beta::<1>(self.beta::<2>(curr_dart)); - cell.push(curr_dart); - if curr_dart == NULL_DART_ID { - completeness = false; - break; // stop if we land on the null dart - } - } - // if not complete, we need to rotate in the other direction to make sure - // no dart is missing - if !completeness { - curr_dart = self.beta::<2>(self.beta::<0>(dart_id)); - while curr_dart != NULL_DART_ID { - cell.push(curr_dart); - curr_dart = self.beta::<2>(self.beta::<0>(curr_dart)); - } - } - } - 1 => { - // in the case of a 2-map, the 1-cell corresponds to [dart, beta_2(dart)] - cell.push(self.beta::<2>(dart_id)) - } - 2 => { - let mut completeness = true; - // travel along the edges of the face until we get back to the first dart - while self.beta::<1>(curr_dart) != dart_id { - curr_dart = self.beta::<1>(curr_dart); - cell.push(curr_dart); - if curr_dart == NULL_DART_ID { - completeness = false; - break; // stop if we land on the null dart - } - } - if !completeness { - curr_dart = self.beta::<0>(dart_id); - while curr_dart != NULL_DART_ID { - cell.push(curr_dart); - curr_dart = self.beta::<0>(curr_dart); - } - } - } - _ => panic!(), + 0 => Orbit2::new(self, OrbitPolicy::Vertex, dart_id), + 1 => Orbit2::new(self, OrbitPolicy::Edge, dart_id), + 2 => Orbit2::new(self, OrbitPolicy::Face, dart_id), + _ => unreachable!(), } - cell } - // --- editing interfaces - - /// Add a new free dart to the combinatorial map. - /// - /// The dart is i-free for all i and is pushed to the list of existing - /// darts, effectively making its identifier equal to the total number - /// of darts (post-push). + /// Return a collection of all the map's vertices. /// /// # Return / Panic /// - /// Return the ID of the created dart to allow for direct operations. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn add_free_dart(&mut self) -> DartIdentifier { - let new_id = self.n_darts as DartIdentifier; - self.n_darts += 1; - self.dart_data.add_entry(); - self.betas.push([0; CMAP2_BETA]); - new_id + /// Return a [VertexCollection] object containing a list of vertex identifiers, whose validity + /// is ensured through an implicit lifetime condition on the structure and original map. + /// + pub fn fetch_vertices(&self) -> VertexCollection { + let mut marked: BTreeSet = BTreeSet::new(); + // using a set for cells & converting it later to avoid duplicated values + // from incomplete cells until they are correctly supported by Orbit2 + let mut vertex_ids: BTreeSet = BTreeSet::new(); + (1..self.n_darts as DartIdentifier) + .filter(|dart_id| !self.unused_darts.contains(dart_id)) // only used darts + .for_each(|dart_id| { + // if we haven't seen this dart yet + if marked.insert(dart_id) { + // because we iterate from dart 1 to n_darts, + // the first dart we encounter is the min of its orbit + vertex_ids.insert(dart_id as VertexIdentifier); + // mark its orbit + Orbit2::new(self, OrbitPolicy::Vertex, dart_id).for_each(|did| { + marked.insert(did); + }); + } + }); + VertexCollection::new(self, vertex_ids) } - /// Add multiple new free darts to the combinatorial map. - /// - /// All darts are i-free for all i and are pushed to the end of the list - /// of existing darts. - /// - /// # Arguments - /// - /// - `n_darts: usize` -- Number of darts to have. + /// Return a collection of all the map's edges. /// /// # Return / Panic /// - /// Return the ID of the first created dart to allow for direct operations. - /// Darts are positioned on range `ID..ID+n_darts`. - /// - /// # Example + /// Return an [EdgeCollection] object containing a list of edge identifiers, whose validity + /// is ensured through an implicit lifetime condition on the structure and original map. + /// + pub fn fetch_edges(&self) -> EdgeCollection { + let mut marked: BTreeSet = BTreeSet::new(); + marked.insert(NULL_DART_ID); + // using a set for cells & converting it later to avoid duplicated values + // from incomplete cells until they are correctly supported by Orbit2 + let mut edge_ids: BTreeSet = BTreeSet::new(); + (1..self.n_darts as DartIdentifier) + .filter(|dart_id| !self.unused_darts.contains(dart_id)) // only used darts + .for_each(|dart_id| { + // if we haven't seen this dart yet + if marked.insert(dart_id) { + // because we iterate from dart 1 to n_darts, + // the first dart we encounter is the min of its orbit + edge_ids.insert(dart_id as EdgeIdentifier); + // mark its orbit + Orbit2::new(self, OrbitPolicy::Edge, dart_id).for_each(|did| { + marked.insert(did); + }); + } + }); + EdgeCollection::new(self, edge_ids) + } + + /// Return a collection of all the map's faces. /// - /// See [CMap2] example. + /// # Return / Panic /// - pub fn add_free_darts(&mut self, n_darts: usize) -> DartIdentifier { - let new_id = self.n_darts as DartIdentifier; - self.n_darts += n_darts; - self.dart_data.add_entries(n_darts); - self.betas.extend((0..n_darts).map(|_| [0; CMAP2_BETA])); - new_id - } - - /// Insert a new free dart to the combinatorial map. - /// - /// The dart is i-free for all i and may be inserted into a free spot in - /// the existing dart list. If no free spots exist, it will be pushed to - /// the end of the list. - /// - /// # Return / Panic - /// - /// Return the ID of the created dart to allow for direct operations. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn insert_free_dart(&mut self) -> DartIdentifier { - if let Some(new_id) = self.unused_darts.pop_first() { - self.dart_data.reset_entry(new_id); - self.betas[new_id as usize] = [0; CMAP2_BETA]; - new_id - } else { - self.add_free_dart() - } - } - - /// Remove a free dart from the combinatorial map. - /// - /// The removed dart identifier is added to the list of free dart. - /// This way of proceeding is necessary as the structure relies on - /// darts indexing for encoding data, making reordering of any sort - /// extremely costly. - /// - /// By keeping track of free spots in the dart arrays, we can prevent too - /// much memory waste, although at the cost of locality of reference. - /// - /// # Arguments - /// - /// - `dart_id: DartIdentifier` -- Identifier of the dart to remove. - /// - /// # Panic - /// - /// This method may panic if: - /// - /// - The dart is not *i*-free for all *i*. - /// - The dart is already marked as unused (Refer to [Self::remove_vertex] documentation for - /// a detailed breakdown of this choice). - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn remove_free_dart(&mut self, dart_id: DartIdentifier) { - assert!(self.is_free(dart_id)); - assert!(self.unused_darts.insert(dart_id)); - let b0d = self.beta::<0>(dart_id); - let b1d = self.beta::<1>(dart_id); - let b2d = self.beta::<2>(dart_id); - self.betas[dart_id as usize] = [0; CMAP2_BETA]; - self.betas[b0d as usize][1] = 0 as DartIdentifier; - self.betas[b1d as usize][0] = 0 as DartIdentifier; - self.betas[b2d as usize][2] = 0 as DartIdentifier; - // the following two lines are more safety than anything else - // this prevents having to deal w/ artifacts in case of re-insertion - self.dart_data.reset_entry(dart_id); - } - - /// Add a vertex to the combinatorial map. - /// - /// The user can provide a [Vertex2] to use as the initial value of the - /// added vertex. - /// - /// # Arguments - /// - /// - `vertex: Option` -- Optional vertex value. - /// - /// # Return / Panic - /// - /// Return the ID of the created vertex to allow for direct operations. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn add_vertex(&mut self, vertex: Option>) -> VertexIdentifier { - let new_id = self.n_vertices as VertexIdentifier; - self.n_vertices += 1; - self.vertices.push(vertex.unwrap_or_default()); - new_id - } - - /// Add multiple vertices to the combinatorial map. - /// - /// # Arguments - /// - /// - `n_vertices: usize` -- Number of vertices to create. - /// - /// # Return / Panic - /// - /// Return the ID of the first created vertex to allow for direct operations. - /// Vertices are positioned on range `ID..ID+n_darts`. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn add_vertices(&mut self, n_vertices: usize) -> VertexIdentifier { - let new_id = self.n_vertices as VertexIdentifier; - self.n_vertices += n_vertices; - self.vertices - .extend((0..n_vertices).map(|_| Vertex2::default())); - new_id - } - - /// Insert a vertex in the combinatorial map. - /// - /// The vertex may be inserted into a free spot in the existing list. If no free - /// spots exist, it will be pushed to the end of the list. The user can provide a - /// [Vertex2] to use as the initial value of the added vertex. - /// - /// # Arguments - /// - /// - `vertex: Option` -- Optional vertex value. - /// - /// # Return / Panic - /// - /// Return the ID of the created dart to allow for direct operations. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn insert_vertex(&mut self, vertex: Option>) -> VertexIdentifier { - if let Some(new_id) = self.unused_vertices.pop_first() { - self.set_vertex(new_id, vertex.unwrap_or_default()).unwrap(); - new_id - } else { - self.add_vertex(vertex) - } - } - - /// Remove a vertex from the combinatorial map. - /// - /// The removed vertex identifier is added to the list of free vertex. - /// This way of proceeding is necessary as the structure relies on - /// vertices indexing for encoding data, making reordering of any sort - /// extremely costly. - /// - /// By keeping track of free spots in the vertices array, we can prevent too - /// much memory waste, although at the cost of locality of reference. - /// - /// # Arguments - /// - /// - `vertex_id: VertexIdentifier` -- Identifier of the vertex to remove. - /// - /// # Panic - /// - /// This method may panic if the user tries to remove a vertex that is already - /// unused. This is a strongly motivated choice as: - /// - By definition, vertices are unique (through their IDs) and so are unused vertices/slots - /// - Duplicated unused slots will only lead to errors when reusing the slots (e.g. implicit - /// overwrites). - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn remove_vertex(&mut self, vertex_id: VertexIdentifier) { - // the insert method returns true if the value was inserted into the set, - // i.e. it wasn't already in before. This assertions guarantees that a - // single vertex won't be removed twice, leading to it being re-used - // multiple times. - assert!(self.unused_vertices.insert(vertex_id)); - // the following line is more safety than anything else - // this prevents having to deal w/ artifacts in case of re-insertion - // it also panics on OOB - self.set_vertex(vertex_id, Vertex2::default()).unwrap(); - } - - /// Try to overwrite the given vertex with a new value. - /// - /// # Arguments - /// - /// - `vertex_id: VertexIdentifier` -- Identifier of the vertex to replace. - /// - `vertex: Vertex2` -- New value for the vertex. - /// - /// # Return / Panic - /// - /// Return a result indicating if the vertex could be overwritten. The main reason - /// of failure would be an out-of-bounds access. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn set_vertex( - &mut self, - vertex_id: VertexIdentifier, - vertex: impl Into>, - ) -> Result<(), CMapError> { - if let Some(val) = self.vertices.get_mut(vertex_id as usize) { - *val = vertex.into(); - return Ok(()); - } - Err(CMapError::VertexOOB) - } - - /// Set the values of the *βi* function of a dart. - /// - /// # Arguments - /// - /// - `dart_id: DartIdentifier` -- ID of the dart of interest. - /// - `beta: DartIdentifier` -- Value of *βI(dart)* - /// - /// ## Generics - /// - /// - `const I: u8` -- Dimension of the cell of interest. *I* should - /// be 0 (vertex), 1 (edge) or 2 (face) for a 2D map. - /// - /// # Return / Panic - /// - /// The method will panic if *I* is not 0, 1 or 2. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn set_beta(&mut self, dart_id: DartIdentifier, beta: DartIdentifier) { - assert!(I < 3); - self.betas[dart_id as usize][I as usize] = beta; - } - - /// Set the values of the beta functions of a dart. - /// - /// # Arguments - /// - /// - `dart_id: DartIdentifier` -- ID of the dart of interest. - /// - `betas: [DartIdentifier; 3]` -- Value of the images as - /// *[β0(dart), β1(dart), β2(dart)]* - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn set_betas(&mut self, dart_id: DartIdentifier, betas: [DartIdentifier; CMAP2_BETA]) { - self.betas[dart_id as usize] = betas; - } - - /// Set the vertex ID associated to a dart. - /// - /// # Arguments - /// - /// - `dart_id: DartIdentifier` -- ID of the dart of interest. - /// - `vertex_id: VertexIdentifier` -- Unique vertex identifier. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn set_vertexid(&mut self, dart_id: DartIdentifier, vertex_id: VertexIdentifier) { - self.dart_data.associated_cells[dart_id as usize].vertex_id = vertex_id; - } - - /// Set the face ID associated to a dart. - /// - /// # Arguments - /// - /// - `dart_id: DartIdentifier` -- ID of the dart of interest. - /// - `face_id: FaceIdentifier` -- Unique face identifier. - /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn set_faceid(&mut self, dart_id: DartIdentifier, face_id: FaceIdentifier) { - self.dart_data.associated_cells[dart_id as usize].face_id = face_id; + /// Return a [FaceCollection] object containing a list of face identifiers, whose validity + /// is ensured through an implicit lifetime condition on the structure and original map. + /// + pub fn fetch_faces(&self) -> FaceCollection { + let mut marked: BTreeSet = BTreeSet::new(); + // using a set for cells & converting it later to avoid duplicated values + // from incomplete cells until they are correctly supported by Orbit2 + let mut face_ids: BTreeSet = BTreeSet::new(); + (1..self.n_darts as DartIdentifier) + .filter(|dart_id| !self.unused_darts.contains(dart_id)) // only used darts + .for_each(|dart_id| { + // if we haven't seen this dart yet + if marked.insert(dart_id) { + // because we iterate from dart 1 to n_darts, + // the first dart we encounter is the min of its orbit + face_ids.insert(dart_id as FaceIdentifier); + // mark its orbit + Orbit2::new(self, OrbitPolicy::Face, dart_id).for_each(|did| { + marked.insert(did); + }); + } + }); + FaceCollection::new(self, face_ids) } +} - /// 1-sewing operation. +// --- (un)sew operations +impl CMap2 { + /// 1-sew operation. /// /// This operation corresponds to *coherently linking* two darts via /// the *β1* function. For a thorough explanation of this operation @@ -982,59 +603,34 @@ impl CMap2 { /// /// The method may panic if the two darts are not 1-sewable. /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn one_sew( - &mut self, - lhs_dart_id: DartIdentifier, - rhs_dart_id: DartIdentifier, - policy: SewPolicy, - ) { - // --- topological update - - // we could technically overwrite the value, but these assertions - // makes it easier to assert algorithm correctness - assert!(self.is_i_free::<1>(lhs_dart_id)); - assert!(self.is_i_free::<0>(rhs_dart_id)); - self.betas[lhs_dart_id as usize][1] = rhs_dart_id; // set beta_1(lhs_dart) to rhs_dart - self.betas[rhs_dart_id as usize][0] = lhs_dart_id; // set beta_0(rhs_dart) to lhs_dart - - // --- geometrical update - - // in case of a 1-sew, we need to update the 0-cell geometry - // of rhs_dart to ensure no vertex is duplicated - - // this operation only makes sense if lhs_dart is associated - // to a fully defined edge, i.e. its image through beta2 is defined - // & has a valid associated vertex (we assume the second condition - // is valid if the first one is). - let lid = self.beta::<2>(lhs_dart_id); - if lid != NULL_DART_ID { - match policy { - SewPolicy::StretchLeft => { - stretch!(self, rhs_dart_id, lid); - } - SewPolicy::StretchRight => { - stretch!(self, lid, rhs_dart_id); - } - SewPolicy::StretchAverage => { - // this works under the assumption that a valid vertex is - // associated to rhs_dart - let lid_vertex = self.vertices[self.cells(lid).vertex_id as usize]; - let rhs_vertex = self.vertices[self.cells(rhs_dart_id).vertex_id as usize]; - self.vertices - .push(Vertex2::average(&lid_vertex, &rhs_vertex)); - let new_id = (self.vertices.len() - 1) as VertexIdentifier; - stretch!(self, lid, new_id); - stretch!(self, rhs_dart_id, new_id); - } - } + pub fn one_sew(&mut self, lhs_dart_id: DartIdentifier, rhs_dart_id: DartIdentifier) { + // this operation only makes sense if lhs_dart is associated to a fully defined edge, i.e. + // its image through beta2 is defined & has a valid associated vertex (we assume the second + // condition is valid if the first one is) + // if that is not the case, the sewing operation becomes a linking operation + let b2lhs_dart_id = self.beta::<2>(lhs_dart_id); + if b2lhs_dart_id != NULL_DART_ID { + let b2lhs_vid_old = self.vertex_id(b2lhs_dart_id); + let rhs_vid_old = self.vertex_id(rhs_dart_id); + let tmp = ( + self.vertices.remove(b2lhs_vid_old), + self.vertices.remove(rhs_vid_old), + ); + let new_vertex = match tmp { + (Some(val1), Some(val2)) => Vertex2::merge(val1, val2), + (Some(val), None) => Vertex2::merge_undefined(Some(val)), + (None, Some(val)) => Vertex2::merge_undefined(Some(val)), + (None, None) => Vertex2::merge_undefined(None), + }; + // use b2lhs_vid as the index for the new vertex + self.one_link(lhs_dart_id, rhs_dart_id); + self.insert_vertex(self.vertex_id(rhs_dart_id), new_vertex); + } else { + self.one_link(lhs_dart_id, rhs_dart_id); } } - /// 2-sewing operation. + /// 2-sew operation. /// /// This operation corresponds to *coherently linking* two darts via /// the *β2* function. For a thorough explanation of this operation @@ -1057,127 +653,116 @@ impl CMap2 { /// - the two darts are not 2-sewable, /// - the method cannot resolve orientation issues. /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn two_sew( - &mut self, - lhs_dart_id: DartIdentifier, - rhs_dart_id: DartIdentifier, - policy: SewPolicy, - ) { - // --- topological update - - // we could technically overwrite the value, but these assertions - // make it easier to assert algorithm correctness - assert!(self.is_i_free::<2>(lhs_dart_id)); - assert!(self.is_i_free::<2>(rhs_dart_id)); - self.betas[lhs_dart_id as usize][2] = rhs_dart_id; // set beta_2(lhs_dart) to rhs_dart - self.betas[rhs_dart_id as usize][2] = lhs_dart_id; // set beta_2(rhs_dart) to lhs_dart - - // --- geometrical update - - // depending on existing connections, different things are required - let l_is1free = self.is_i_free::<1>(lhs_dart_id); - let r_is1free = self.is_i_free::<1>(rhs_dart_id); - match (l_is1free, r_is1free) { - (true, true) => {} // do nothing + pub fn two_sew(&mut self, lhs_dart_id: DartIdentifier, rhs_dart_id: DartIdentifier) { + let b1lhs_dart_id = self.beta::<1>(lhs_dart_id); + let b1rhs_dart_id = self.beta::<1>(rhs_dart_id); + // match (is lhs 1-free, is rhs 1-free) + match (b1lhs_dart_id == NULL_DART_ID, b1rhs_dart_id == NULL_DART_ID) { + // trivial case, no update needed + (true, true) => self.two_link(lhs_dart_id, rhs_dart_id), + // update vertex associated to b1rhs/lhs (true, false) => { - let b1rid = self.beta::<1>(rhs_dart_id); - match policy { - SewPolicy::StretchLeft => { - stretch!(self, lhs_dart_id, b1rid) - } - SewPolicy::StretchRight => { - stretch!(self, b1rid, lhs_dart_id) - } - SewPolicy::StretchAverage => { - let vertex1 = self.vertices[self.cells(b1rid).vertex_id as usize]; - let vertex2 = self.vertices[self.cells(lhs_dart_id).vertex_id as usize]; - - self.vertices.push(Vertex2::average(&vertex1, &vertex2)); - let new_id = (self.vertices.len() - 1) as VertexIdentifier; - - stretch!(self, b1rid, new_id); - stretch!(self, lhs_dart_id, new_id); - } - } + // read current values / remove old ones + let lhs_vid_old = self.vertex_id(lhs_dart_id); + let b1rhs_vid_old = self.vertex_id(b1rhs_dart_id); + let tmp = ( + self.vertices.remove(lhs_vid_old), + self.vertices.remove(b1rhs_vid_old), + ); + let new_vertex = match tmp { + (Some(val1), Some(val2)) => Vertex2::merge(val1, val2), + (Some(val), None) => Vertex2::merge_undefined(Some(val)), + (None, Some(val)) => Vertex2::merge_undefined(Some(val)), + (None, None) => Vertex2::merge_undefined(None), + }; + // update the topology (this is why we need the above lines) + self.two_link(lhs_dart_id, rhs_dart_id); + // reinsert correct value + self.insert_vertex(self.vertex_id(lhs_dart_id), new_vertex); } + // update vertex associated to b1lhs/rhs (false, true) => { - let b1lid = self.beta::<1>(lhs_dart_id); - match policy { - SewPolicy::StretchLeft => { - stretch!(self, rhs_dart_id, b1lid) - } - SewPolicy::StretchRight => { - stretch!(self, b1lid, rhs_dart_id) - } - SewPolicy::StretchAverage => { - let vertex1 = self.vertices[self.cells(b1lid).vertex_id as usize]; - let vertex2 = self.vertices[self.cells(rhs_dart_id).vertex_id as usize]; - - self.vertices.push(Vertex2::average(&vertex1, &vertex2)); - let new_id = (self.vertices.len() - 1) as VertexIdentifier; - - stretch!(self, b1lid, new_id); - stretch!(self, rhs_dart_id, new_id); - } - } + // read current values / remove old ones + let b1lhs_vid_old = self.vertex_id(b1lhs_dart_id); + let rhs_vid_old = self.vertex_id(rhs_dart_id); + let tmp = ( + self.vertices.remove(b1lhs_vid_old), + self.vertices.remove(rhs_vid_old), + ); + let new_vertex = match tmp { + (Some(val1), Some(val2)) => Vertex2::merge(val1, val2), + (Some(val), None) => Vertex2::merge_undefined(Some(val)), + (None, Some(val)) => Vertex2::merge_undefined(Some(val)), + (None, None) => Vertex2::merge_undefined(None), + }; + // update the topology (this is why we need the above lines) + self.two_link(lhs_dart_id, rhs_dart_id); + // reinsert correct value + self.insert_vertex(self.vertex_id(rhs_dart_id), new_vertex); } + // update both vertices making up the edge (false, false) => { - // ensure orientation consistency - - let b1lid = self.beta::<1>(lhs_dart_id); - let b1rid = self.beta::<1>(rhs_dart_id); - - let b1_lvertex = self.vertices[self.cells(b1lid).vertex_id as usize]; - let lvertex = self.vertices[self.cells(lhs_dart_id).vertex_id as usize]; - let b1_rvertex = self.vertices[self.cells(b1rid).vertex_id as usize]; - let rvertex = self.vertices[self.cells(rhs_dart_id).vertex_id as usize]; - - let lhs_vec = b1_lvertex - lvertex; - let rhs_vec = b1_rvertex - rvertex; - - // dot product should be negative if the two darts have opposite direction - // we could also put restriction on the angle made by the two darts to prevent - // drastic deformation - assert!( - lhs_vec.dot(&rhs_vec) < T::zero(), - "Dart {} and {} do not have consistent orientation for 2-sewing", - lhs_dart_id, - rhs_dart_id + // read current values / remove old ones + // (lhs/b1rhs) vertex + let lhs_vid_old = self.vertex_id(lhs_dart_id); + let b1rhs_vid_old = self.vertex_id(b1rhs_dart_id); + let tmpa = ( + self.vertices.remove(lhs_vid_old), + self.vertices.remove(b1rhs_vid_old), + ); + // (b1lhs/rhs) vertex + let b1lhs_vid_old = self.vertex_id(b1lhs_dart_id); + let rhs_vid_old = self.vertex_id(rhs_dart_id); + let tmpb = ( + self.vertices.remove(b1lhs_vid_old), + self.vertices.remove(rhs_vid_old), ); - match policy { - SewPolicy::StretchLeft => { - stretch!(self, rhs_dart_id, b1lid); - stretch!(self, b1rid, lhs_dart_id); - } - SewPolicy::StretchRight => { - stretch!(self, b1lid, rhs_dart_id); - stretch!(self, lhs_dart_id, b1rid); - } - SewPolicy::StretchAverage => { - let new_lvertex = Vertex2::average(&lvertex, &b1_rvertex); - let new_rvertex = Vertex2::average(&rvertex, &b1_lvertex); - self.vertices.push(new_lvertex); - self.vertices.push(new_rvertex); - let new_lid = self.vertices.len() - 2; - let new_rid = self.vertices.len() - 1; - - stretch!(self, lhs_dart_id, new_lid); - stretch!(self, b1rid, new_lid); - - stretch!(self, rhs_dart_id, new_rid); - stretch!(self, b1lid, new_rid); - } - } + // check orientation + #[rustfmt::skip] + if let ( + (Some(l_vertex), Some(b1r_vertex)), + (Some(b1l_vertex), Some(r_vertex)), + ) = (tmpa, tmpb) { + let lhs_vector = b1l_vertex - l_vertex; + let rhs_vector = b1r_vertex - r_vertex; + // dot product should be negative if the two darts have opposite direction + // we could also put restriction on the angle made by the two darts to prevent + // drastic deformation + assert!( + lhs_vector.dot(&rhs_vector) < T::zero(), + "Dart {} and {} do not have consistent orientation for 2-sewing", + lhs_dart_id, + rhs_dart_id + ); + }; + + // proceed with new vertices creation & insertion + let new_vertexa = match tmpa { + (Some(val1), Some(val2)) => Vertex2::merge(val1, val2), + (Some(val), None) => Vertex2::merge_undefined(Some(val)), + (None, Some(val)) => Vertex2::merge_undefined(Some(val)), + (None, None) => Vertex2::merge_undefined(None), + }; + + let new_vertexb = match tmpb { + (Some(val1), Some(val2)) => Vertex2::merge(val1, val2), + (Some(val), None) => Vertex2::merge_undefined(Some(val)), + (None, Some(val)) => Vertex2::merge_undefined(Some(val)), + (None, None) => Vertex2::merge_undefined(None), + }; + // update the topology + self.two_link(lhs_dart_id, rhs_dart_id); + self.two_link(lhs_dart_id, rhs_dart_id); + + // reinsert correct values + self.insert_vertex(self.vertex_id(lhs_dart_id), new_vertexa); + self.insert_vertex(self.vertex_id(rhs_dart_id), new_vertexb); } } } - /// 1-unsewing operation. + /// 1-unsew operation. /// /// This operation corresponds to *coherently separating* two darts linked /// via the *β1* function. For a thorough explanation of this operation @@ -1194,33 +779,25 @@ impl CMap2 { /// second dart can be obtained through the *β1* function. The /// *β0* function is also updated. /// - /// # Example - /// - /// See [CMap2] example. - /// - pub fn one_unsew(&mut self, lhs_dart_id: DartIdentifier, policy: UnsewPolicy) { - // --- topological update - - // fetch id of beta_1(lhs_dart) - let rhs_dart_id = self.beta::<1>(lhs_dart_id); - self.betas[lhs_dart_id as usize][1] = 0; // set beta_1(lhs_dart) to NullDart - self.betas[rhs_dart_id as usize][0] = 0; // set beta_0(rhs_dart) to NullDart - - // --- geometrical update - match policy { - UnsewPolicy::Duplicate => { - // if the vertex was shared, duplicate it - if self.i_cell::<0>(rhs_dart_id).len() > 1 { - let old_vertex = self.vertices[self.vertexid(rhs_dart_id) as usize]; - self.vertices.push(old_vertex); - self.set_vertexid(rhs_dart_id, (self.vertices.len() - 1) as VertexIdentifier); - } - } - UnsewPolicy::DoNothing => {} + pub fn one_unsew(&mut self, lhs_dart_id: DartIdentifier) { + let b2lhs_dart_id = self.beta::<2>(lhs_dart_id); + if b2lhs_dart_id != NULL_DART_ID { + // read current values / remove old ones + let rhs_dart_id = self.beta::<1>(lhs_dart_id); + // we only need to remove a single vertex since we're unlinking + let vertex = self.remove_vertex(self.vertex_id(rhs_dart_id)).unwrap(); + let (v1, v2) = Vertex2::split(vertex); + // update the topology + self.one_unlink(lhs_dart_id); + // reinsert correct values + let _ = self.replace_vertex(self.vertex_id(b2lhs_dart_id), v1); + let _ = self.replace_vertex(self.vertex_id(rhs_dart_id), v2); + } else { + self.one_unlink(lhs_dart_id) } } - /// 2-unsewing operation. + /// 2-unsew operation. /// /// This operation corresponds to *coherently separating* two darts linked /// via the *β2* function. For a thorough explanation of this operation @@ -1236,132 +813,207 @@ impl CMap2 { /// Note that we do not need to take two darts as arguments since the /// second dart can be obtained through the *β2* function. /// - /// # Example + pub fn two_unsew(&mut self, lhs_dart_id: DartIdentifier) { + let rhs_dart_id = self.beta::<2>(lhs_dart_id); + let b1lhs_dart_id = self.beta::<1>(lhs_dart_id); + let b1rhs_dart_id = self.beta::<1>(rhs_dart_id); + // match (is lhs 1-free, is rhs 1-free) + match (b1lhs_dart_id == NULL_DART_ID, b1rhs_dart_id == NULL_DART_ID) { + (true, true) => self.two_unlink(lhs_dart_id), + (true, false) => { + let rhs_vid_old = self.vertex_id(rhs_dart_id); + let rhs_vertex = self.remove_vertex(rhs_vid_old).unwrap(); + let (v1, v2) = Vertex2::split(rhs_vertex); + self.two_unlink(lhs_dart_id); + self.insert_vertex(self.vertex_id(b1lhs_dart_id), v1); + self.insert_vertex(self.vertex_id(rhs_dart_id), v2); + } + (false, true) => { + let lhs_vid_old = self.vertex_id(lhs_dart_id); + let lhs_vertex = self.remove_vertex(lhs_vid_old).unwrap(); + let (v1, v2) = Vertex2::split(lhs_vertex); + self.two_unlink(lhs_dart_id); + self.insert_vertex(self.vertex_id(lhs_dart_id), v1); + self.insert_vertex(self.vertex_id(b1rhs_dart_id), v2); + } + (false, false) => { + let lhs_vid_old = self.vertex_id(lhs_dart_id); + let rhs_vid_old = self.vertex_id(rhs_dart_id); + let lhs_vertex = self.remove_vertex(lhs_vid_old).unwrap(); + let rhs_vertex = self.remove_vertex(rhs_vid_old).unwrap(); + self.two_unlink(lhs_dart_id); + let (rhs_v1, rhs_v2) = Vertex2::split(rhs_vertex); + let (lhs_v1, lhs_v2) = Vertex2::split(lhs_vertex); + + self.insert_vertex(self.vertex_id(b1lhs_dart_id), rhs_v1); + self.insert_vertex(self.vertex_id(rhs_dart_id), rhs_v2); + self.insert_vertex(self.vertex_id(lhs_dart_id), lhs_v1); + self.insert_vertex(self.vertex_id(b1rhs_dart_id), lhs_v2); + } + } + } +} + +// --- (un)link operations +impl CMap2 { + /// 1-link operation. /// - /// See [CMap2] example. + /// This operation corresponds to linking two darts via the *β1* function. Unlike + /// its sewing counterpart, this method does not contain any code to update the attributes or + /// geometrical data of the affected cell(s). The *β0* function is also updated. + /// + /// # Arguments + /// + /// - `lhs_dart_id: DartIdentifier` -- ID of the first dart to be linked. + /// - `rhs_dart_id: DartIdentifier` -- ID of the second dart to be linked. + /// + pub fn one_link(&mut self, lhs_dart_id: DartIdentifier, rhs_dart_id: DartIdentifier) { + // we could technically overwrite the value, but these assertions + // makes it easier to assert algorithm correctness + assert!(self.is_i_free::<1>(lhs_dart_id)); + assert!(self.is_i_free::<0>(rhs_dart_id)); + self.betas[lhs_dart_id as usize][1] = rhs_dart_id; // set beta_1(lhs_dart) to rhs_dart + self.betas[rhs_dart_id as usize][0] = lhs_dart_id; // set beta_0(rhs_dart) to lhs_dart + } + + /// 2-link operation. + /// + /// This operation corresponds to linking two darts via the *β2* function. Unlike + /// its sewing counterpart, this method does not contain any code to update the attributes or + /// geometrical data of the affected cell(s). + /// + /// # Arguments + /// + /// - `lhs_dart_id: DartIdentifier` -- ID of the first dart to be linked. + /// - `rhs_dart_id: DartIdentifier` -- ID of the second dart to be linked. /// - pub fn two_unsew(&mut self, lhs_dart_id: DartIdentifier, policy: UnsewPolicy) { - // --- topological update + pub fn two_link(&mut self, lhs_dart_id: DartIdentifier, rhs_dart_id: DartIdentifier) { + // we could technically overwrite the value, but these assertions + // make it easier to assert algorithm correctness + assert!(self.is_i_free::<2>(lhs_dart_id)); + assert!(self.is_i_free::<2>(rhs_dart_id)); + self.betas[lhs_dart_id as usize][2] = rhs_dart_id; // set beta_2(lhs_dart) to rhs_dart + self.betas[rhs_dart_id as usize][2] = lhs_dart_id; // set beta_2(rhs_dart) to lhs_dart + } - let rhs_dart_id = self.beta::<2>(lhs_dart_id); + /// 1-unlink operation. + /// + /// This operation corresponds to unlinking two darts that are linked via the *β1* + /// function. Unlike its sewing counterpart, this method does not contain any code to update + /// the attributes or geometrical data of the affected cell(s). The *β0* function is + /// also updated. + /// + /// # Arguments + /// + /// - `lhs_dart_id: DartIdentifier` -- ID of the dart to unlink. + /// + pub fn one_unlink(&mut self, lhs_dart_id: DartIdentifier) { + let rhs_dart_id = self.beta::<1>(lhs_dart_id); // fetch id of beta_1(lhs_dart) + self.betas[lhs_dart_id as usize][1] = 0; // set beta_1(lhs_dart) to NullDart + self.betas[rhs_dart_id as usize][0] = 0; // set beta_0(rhs_dart) to NullDart + } + + /// 2-unlink operation. + /// + /// This operation corresponds to unlinking two darts that are linked via the *β2* + /// function. Unlike its sewing counterpart, this method does not contain any code to update + /// the attributes or geometrical data of the affected cell(s). + /// + /// # Arguments + /// + /// - `lhs_dart_id: DartIdentifier` -- ID of the dart to unlink. + /// + pub fn two_unlink(&mut self, lhs_dart_id: DartIdentifier) { + let rhs_dart_id = self.beta::<2>(lhs_dart_id); // fetch id of beta_2(lhs_dart) self.betas[lhs_dart_id as usize][2] = 0; // set beta_2(dart) to NullDart self.betas[rhs_dart_id as usize][2] = 0; // set beta_2(beta_2(dart)) to NullDart + } +} - // --- geometrical update - match policy { - UnsewPolicy::Duplicate => { - // if the vertex was shared, duplicate it - // repeat on both ends of the edge - let b1lid = self.beta::<1>(lhs_dart_id); - if b1lid != NULL_DART_ID { - self.set_vertexid(rhs_dart_id, self.vertexid(b1lid)); - } - let b1rid = self.beta::<1>(rhs_dart_id); - if b1rid != NULL_DART_ID { - self.set_vertexid(lhs_dart_id, self.vertexid(b1rid)); - } - } - UnsewPolicy::DoNothing => {} - } +// --- vertex attributes +// this should eventually be replaced by a generalized structure to handle +// different kind of attributes for all the i-cells. +impl CMap2 { + /// Return the current number of vertices. + pub fn n_vertices(&self) -> usize { + self.vertices.n_vertices() } - /// Clear and rebuild the face list defined by the map. - /// - /// # Return / Panic + /// Fetch vertex value associated to a given identifier. /// - /// Returns the number of faces built by the operation. + /// # Arguments /// - /// # Example + /// - `vertex_id: VertexIdentifier` -- Identifier of the given vertex. /// - /// ```text + /// # Return / Panic /// - /// ``` + /// Return a reference to the [Vertex2] associated to the ID. /// - pub fn build_all_faces(&mut self) -> usize { - self.faces.clear(); - let mut marked = BTreeSet::::new(); - let mut n_faces = 0; - // go through all darts ? update - (1..self.n_darts as DartIdentifier).for_each(|id| { - if marked.insert(id) { - let tmp = self.i_cell::<2>(id); - if tmp.len() > 1 { - tmp.iter().for_each(|member| { - let _ = marked.insert(*member); - }); - self.build_face(id); - n_faces += 1 - } - } - }); - n_faces + pub fn vertex(&self, vertex_id: VertexIdentifier) -> Vertex2 { + self.vertices.get(vertex_id).unwrap() } - /// Build the geometrical face associated with a given dart + /// Insert a vertex in the combinatorial map. + /// + /// This method can be interpreted as giving a value to the vertex of a specific ID. Vertices + /// implicitly exist through topology, but their spatial representation is not automatically + /// created at first. /// /// # Arguments /// - /// - `dart_id: DartIdentifier` -- Identifier of the dart + /// - `vertex_id: VertexIdentifier` -- Vertex identifier to attribute a value to. + /// - `vertex: impl Into` -- Value used to create a [Vertex2] value. /// - /// # Return / Panic + /// # Return /// - /// Return the ID of the created face to allow for direct operations. + /// Return an option which may contain the previous value associated to the specified vertex ID. /// - /// # Example + pub fn insert_vertex(&mut self, vertex_id: VertexIdentifier, vertex: impl Into>) { + self.vertices.insert(vertex_id, vertex.into()) + } + + /// Remove a vertex from the combinatorial map. /// - /// See [CMap2] example. + /// # Arguments /// - pub fn build_face(&mut self, dart_id: DartIdentifier) -> FaceIdentifier { - let new_faceid = self.faces.len() as FaceIdentifier; - self.set_faceid(dart_id, new_faceid); - let mut part_one = vec![dart_id]; - let mut closed = true; - let mut curr_dart = self.beta::<1>(dart_id); - // search the face using beta1 - while curr_dart != dart_id { - // if we encounter the null dart, it means the face is open - if curr_dart == NULL_DART_ID { - closed = false; - break; - } - part_one.push(curr_dart); - self.set_faceid(curr_dart, new_faceid); - curr_dart = self.beta::<1>(curr_dart); + /// - `vertex_id: VertexIdentifier` -- Identifier of the vertex to remove. + /// + /// # Return + /// + /// This method return a `Result` taking the following values: + /// - `Ok(v: Vertex2)` -- The vertex was successfully removed & its value was returned + /// - `Err(CMapError::UndefinedVertexID)` -- The vertex was not found in the internal storage + /// + pub fn remove_vertex(&mut self, vertex_id: VertexIdentifier) -> Result, CMapError> { + if let Some(val) = self.vertices.remove(vertex_id) { + return Ok(val); } + Err(CMapError::UndefinedVertex) + } - let res = if !closed { - // if the face is open, we might have missed some darts - // that were before the starting dart. - curr_dart = self.beta::<0>(dart_id); - let mut part_two = Vec::new(); - // search the face in the other direction using beta0 - while curr_dart != NULL_DART_ID { - part_two.push(curr_dart); - self.set_faceid(curr_dart, new_faceid); - curr_dart = self.beta::<0>(curr_dart); - } - // to have the ordered face, we need to reverse the beta 0 part and - // add the beta 1 part to its end - part_two.reverse(); - part_two.extend(part_one); - part_two - } else { - // if the face was closed - // => we looped around its edges - // => the list is already complete & ordered - part_one - }; - - let face = Face { - corners: res - .iter() - .map(|d_id| self.dart_data.associated_cells[*d_id as usize].vertex_id) - .collect(), - closed, + /// Try to overwrite the given vertex with a new value. + /// + /// # Arguments + /// + /// - `vertex_id: VertexIdentifier` -- Identifier of the vertex to replace. + /// - `vertex: impl>` -- New value for the vertex. + /// + /// # Return / Panic + /// + /// This method return a `Result` taking the following values: + /// - `Ok(v: Vertex2)` -- The vertex was successfully overwritten & its previous value was + /// returned + /// - `Err(CMapError::UnknownVertexID)` -- The vertex was not found in the internal storage + /// + pub fn replace_vertex( + &mut self, + vertex_id: VertexIdentifier, + vertex: impl Into>, + ) -> Result, CMapError> { + if let Some(val) = self.vertices.replace(vertex_id, vertex.into()) { + return Ok(val); }; - - self.faces.push(face); - new_faceid + Err(CMapError::UndefinedVertex) } } @@ -1397,14 +1049,14 @@ impl CMap2 { /// /// # Example /// - /// An example going over all three `size` methods is provided in the `honeycomb-benches` + /// An example going over all three `size` methods is provided in the `honeycomb-utils` /// crate. You can run it using the following command: /// /// ```shell /// cargo run --example memory_usage /// ``` /// - /// The output data can be visualized using the `plot.py` script. + /// The output data can be visualized using the `memory_usage.py` script. /// pub fn allocated_size(&self, rootname: &str) { let mut file = File::create(rootname.to_owned() + "_allocated.csv").unwrap(); @@ -1419,32 +1071,18 @@ impl CMap2 { }); writeln!(file, "beta_total, {beta_total}").unwrap(); - // embed - let embed_vertex = - self.dart_data.associated_cells.capacity() * std::mem::size_of::(); - let embed_face = - self.dart_data.associated_cells.capacity() * std::mem::size_of::(); - let embed_total = embed_vertex + embed_face; - writeln!(file, "embed_vertex, {embed_vertex}").unwrap(); - writeln!(file, "embed_face, {embed_face}").unwrap(); - writeln!(file, "embed_total, {embed_total}").unwrap(); - - // geometry + // cells // using 2 * sizeof(f64) bc sizeof(array) always is the size of a pointer - let geometry_vertex = self.n_vertices * 2 * std::mem::size_of::(); - let geometry_face = self.faces.capacity() * std::mem::size_of::(); - let geometry_total = geometry_vertex + geometry_face; + let geometry_vertex = self.vertices.allocated_size(); + let geometry_total = geometry_vertex; writeln!(file, "geometry_vertex, {geometry_vertex}").unwrap(); - writeln!(file, "geometry_face, {geometry_face}").unwrap(); writeln!(file, "geometry_total, {geometry_total}").unwrap(); // others let others_freedarts = self.unused_darts.len(); - let others_freevertices = self.unused_vertices.len(); let others_counters = 2 * std::mem::size_of::(); - let others_total = others_freedarts + others_freevertices + others_counters; + let others_total = others_freedarts + others_counters; writeln!(file, "others_freedarts, {others_freedarts}").unwrap(); - writeln!(file, "others_freevertices, {others_freevertices}").unwrap(); writeln!(file, "others_counters, {others_counters}").unwrap(); writeln!(file, "others_total, {others_total}").unwrap(); } @@ -1479,14 +1117,14 @@ impl CMap2 { /// /// # Example /// - /// An example going over all three `size` methods is provided in the `honeycomb-benches` + /// An example going over all three `size` methods is provided in the `honeycomb-utils` /// crate. You can run it using the following command: /// /// ```shell /// cargo run --example memory_usage /// ``` /// - /// The output data can be visualized using the `plot.py` script. + /// The output data can be visualized using the `memory_usage.py` script. /// pub fn effective_size(&self, rootname: &str) { let mut file = File::create(rootname.to_owned() + "_effective.csv").unwrap(); @@ -1509,22 +1147,18 @@ impl CMap2 { writeln!(file, "embed_face, {embed_face}").unwrap(); writeln!(file, "embed_total, {embed_total}").unwrap(); - // geometry + // cells // using 2 * sizeof(f64) bc sizeof(array) always is the size of a pointer - let geometry_vertex = self.n_vertices * 2 * std::mem::size_of::(); - let geometry_face = self.faces.len() * std::mem::size_of::(); - let geometry_total = geometry_vertex + geometry_face; + let geometry_vertex = self.vertices.effective_size(); + let geometry_total = geometry_vertex; writeln!(file, "geometry_vertex, {geometry_vertex}").unwrap(); - writeln!(file, "geometry_face, {geometry_face}").unwrap(); writeln!(file, "geometry_total, {geometry_total}").unwrap(); // others let others_freedarts = self.unused_darts.len(); - let others_freevertices = self.unused_vertices.len(); let others_counters = 2 * std::mem::size_of::(); - let others_total = others_freedarts + others_freevertices + others_counters; + let others_total = others_freedarts + others_counters; writeln!(file, "others_freedarts, {others_freedarts}").unwrap(); - writeln!(file, "others_freevertices, {others_freevertices}").unwrap(); writeln!(file, "others_counters, {others_counters}").unwrap(); writeln!(file, "others_total, {others_total}").unwrap(); } @@ -1562,21 +1196,20 @@ impl CMap2 { /// /// # Example /// - /// An example going over all three `size` methods is provided in the `honeycomb-benches` + /// An example going over all three `size` methods is provided in the `honeycomb-utils` /// crate. You can run it using the following command: /// /// ```shell /// cargo run --example memory_usage /// ``` /// - /// The output data can be visualized using the `plot.py` script. + /// The output data can be visualized using the `memory_usage.py` script. /// pub fn used_size(&self, rootname: &str) { let mut file = File::create(rootname.to_owned() + "_used.csv").unwrap(); writeln!(file, "key, memory (bytes)").unwrap(); let n_used_darts = self.n_darts - self.unused_darts.len(); - let n_used_vertices = self.n_vertices - self.unused_vertices.len(); // beta let mut beta_total = 0; @@ -1595,22 +1228,18 @@ impl CMap2 { writeln!(file, "embed_face, {embed_face}").unwrap(); writeln!(file, "embed_total, {embed_total}").unwrap(); - // geometry + // cells // using 2 * sizeof(f64) bc sizeof(array) always is the size of a pointer - let geometry_vertex = n_used_vertices * 2 * std::mem::size_of::(); - let geometry_face = self.faces.len() * std::mem::size_of::(); - let geometry_total = geometry_vertex + geometry_face; + let geometry_vertex = self.vertices.used_size(); + let geometry_total = geometry_vertex; writeln!(file, "geometry_vertex, {geometry_vertex}").unwrap(); - writeln!(file, "geometry_face, {geometry_face}").unwrap(); writeln!(file, "geometry_total, {geometry_total}").unwrap(); // others let others_freedarts = self.unused_darts.len(); - let others_freevertices = self.unused_vertices.len(); let others_counters = 2 * std::mem::size_of::(); - let others_total = others_freedarts + others_freevertices + others_counters; + let others_total = others_freedarts + others_counters; writeln!(file, "others_freedarts, {others_freedarts}").unwrap(); - writeln!(file, "others_freevertices, {others_freevertices}").unwrap(); writeln!(file, "others_counters, {others_counters}").unwrap(); writeln!(file, "others_total, {others_total}").unwrap(); } @@ -1626,11 +1255,11 @@ mod tests { #[should_panic] fn remove_vertex_twice() { // in its default state, all darts/vertices of a map are considered to be used - let mut map: CMap2 = CMap2::new(4, 4); + let mut map: CMap2 = CMap2::new(4); // set vertex 1 as unused - map.remove_vertex(1); + map.remove_vertex(1).unwrap(); // set vertex 1 as unused, again - map.remove_vertex(1); // this should panic + map.remove_vertex(1).unwrap(); // this should panic } #[test] @@ -1638,7 +1267,7 @@ mod tests { fn remove_dart_twice() { // in its default state, all darts/vertices of a map are considered to be used // darts are also free - let mut map: CMap2 = CMap2::new(4, 4); + let mut map: CMap2 = CMap2::new(4); // set dart 1 as unused map.remove_free_dart(1); // set dart 1 as unused, again diff --git a/honeycomb-core/src/utils/generation.rs b/honeycomb-core/src/utils/generation.rs index b6b97de7..f60f13f8 100644 --- a/honeycomb-core/src/utils/generation.rs +++ b/honeycomb-core/src/utils/generation.rs @@ -12,9 +12,7 @@ // ------ IMPORTS -use crate::{ - CMap2, CoordsFloat, DartIdentifier, SewPolicy, UnsewPolicy, Vertex2, VertexIdentifier, -}; +use crate::{CMap2, CoordsFloat, DartIdentifier}; // ------ CONTENT @@ -60,73 +58,74 @@ use crate::{ /// applies for face IDs. /// pub fn square_cmap2(n_square: usize) -> CMap2 { - let mut map: CMap2 = CMap2::new(4 * n_square.pow(2), (n_square + 1).pow(2)); + let mut map: CMap2 = CMap2::new(4 * n_square.pow(2)); // first, topology (0..n_square).for_each(|y_idx| { (0..n_square).for_each(|x_idx| { let d1 = (1 + 4 * x_idx + n_square * 4 * y_idx) as DartIdentifier; let (d2, d3, d4) = (d1 + 1, d1 + 2, d1 + 3); - map.one_sew(d1, d2, SewPolicy::StretchLeft); - map.one_sew(d2, d3, SewPolicy::StretchLeft); - map.one_sew(d3, d4, SewPolicy::StretchLeft); - map.one_sew(d4, d1, SewPolicy::StretchLeft); + map.one_link(d1, d2); + map.one_link(d2, d3); + map.one_link(d3, d4); + map.one_link(d4, d1); // if there is a right neighbor, sew sew if x_idx != n_square - 1 { let right_neighbor = d2 + 6; - map.two_sew(d2, right_neighbor, SewPolicy::StretchLeft); + map.two_link(d2, right_neighbor); } // if there is an up neighbor, sew sew if y_idx != n_square - 1 { let up_neighbor = d1 + (4 * n_square) as DartIdentifier; - map.two_sew(d3, up_neighbor, SewPolicy::StretchLeft) + map.two_link(d3, up_neighbor) } }) }); - // then geometry + // then cells (0..n_square + 1).for_each(|y_idx| { (0..n_square + 1).for_each(|x_idx| { - // first position the vertex - let vertex_id = (y_idx * (n_square + 1) + x_idx) as VertexIdentifier; - map.set_vertex( - vertex_id, - Vertex2::from((T::from(x_idx).unwrap(), T::from(y_idx).unwrap())), - ) - .unwrap(); // update the associated 0-cell if (y_idx < n_square) & (x_idx < n_square) { let base_dart = (1 + 4 * x_idx + n_square * 4 * y_idx) as DartIdentifier; - map.i_cell::<0>(base_dart) - .iter() - .for_each(|dart_id| map.set_vertexid(*dart_id, vertex_id)); + let vertex_id = map.vertex_id(base_dart); + map.insert_vertex( + vertex_id, + (T::from(x_idx).unwrap(), T::from(y_idx).unwrap()), + ); let last_column = x_idx == n_square - 1; let last_row = y_idx == n_square - 1; if last_column { // that last column of 0-cell needs special treatment // bc there are no "horizontal" associated dart - map.i_cell::<0>(base_dart + 1) - .iter() - .for_each(|dart_id| map.set_vertexid(*dart_id, vertex_id + 1)); + let vertex_id = map.vertex_id(base_dart + 1); + map.insert_vertex( + vertex_id, + (T::from(x_idx + 1).unwrap(), T::from(y_idx).unwrap()), + ); } if last_row { // same as the case on x - map.i_cell::<0>(base_dart + 3).iter().for_each(|dart_id| { - map.set_vertexid(*dart_id, vertex_id + (n_square + 1) as VertexIdentifier) - }); + let vertex_id = map.vertex_id(base_dart + 3); + map.insert_vertex( + vertex_id, + (T::from(x_idx).unwrap(), T::from(y_idx + 1).unwrap()), + ); } if last_row & last_column { // need to do the upper right corner - map.i_cell::<0>(base_dart + 2).iter().for_each(|dart_id| { - map.set_vertexid(*dart_id, vertex_id + (n_square + 2) as VertexIdentifier) - }); + let vertex_id = map.vertex_id(base_dart + 2); + map.insert_vertex( + vertex_id, + (T::from(x_idx + 1).unwrap(), T::from(y_idx + 1).unwrap()), + ); } } }) }); // and then build faces - assert_eq!(map.build_all_faces(), n_square.pow(2)); + assert_eq!(map.fetch_faces().identifiers.len(), n_square.pow(2)); map } @@ -173,25 +172,81 @@ pub fn square_cmap2(n_square: usize) -> CMap2 { /// applies for face IDs. /// pub fn splitsquare_cmap2(n_square: usize) -> CMap2 { - let mut map: CMap2 = square_cmap2(n_square); - - (0..n_square.pow(2)).for_each(|square| { - let d1 = (1 + square * 4) as DartIdentifier; - let (d2, d3, d4) = (d1 + 1, d1 + 2, d1 + 3); - // in a parallel impl, we would create all new darts before-hand - let dsplit1 = map.add_free_darts(2); - let dsplit2 = dsplit1 + 1; - map.two_sew(dsplit1, dsplit2, SewPolicy::StretchLeft); - map.one_unsew(d1, UnsewPolicy::DoNothing); - map.one_unsew(d3, UnsewPolicy::DoNothing); - map.one_sew(d1, dsplit1, SewPolicy::StretchLeft); - map.one_sew(d3, dsplit2, SewPolicy::StretchLeft); - map.one_sew(dsplit1, d4, SewPolicy::StretchRight); - map.one_sew(dsplit2, d2, SewPolicy::StretchRight); + let mut map: CMap2 = CMap2::new(6 * n_square.pow(2)); + + // first, topology + (0..n_square).for_each(|y_idx| { + (0..n_square).for_each(|x_idx| { + let d1 = (1 + 6 * (x_idx + n_square * y_idx)) as DartIdentifier; + let (d2, d3, d4, d5, d6) = (d1 + 1, d1 + 2, d1 + 3, d1 + 4, d1 + 5); + // bottom left triangle + map.one_link(d1, d2); + map.one_link(d2, d3); + map.one_link(d3, d1); + // top right triangle + map.one_link(d4, d5); + map.one_link(d5, d6); + map.one_link(d6, d4); + // diagonal + map.two_link(d2, d4); + + // if there is a right neighbor, sew sew + if x_idx != n_square - 1 { + let right_neighbor = d1 + 8; + map.two_link(d5, right_neighbor); + } + // if there is an up neighbor, sew sew + if y_idx != n_square - 1 { + let up_neighbor = d1 + (6 * n_square) as DartIdentifier; + map.two_link(d6, up_neighbor) + } + }) + }); + + // then cells + (0..n_square + 1).for_each(|y_idx| { + (0..n_square + 1).for_each(|x_idx| { + // update the associated 0-cell + if (y_idx < n_square) & (x_idx < n_square) { + let base_dart = (1 + 6 * (x_idx + n_square * y_idx)) as DartIdentifier; + let vertex_id = map.vertex_id(base_dart); + map.insert_vertex( + vertex_id, + (T::from(x_idx).unwrap(), T::from(y_idx).unwrap()), + ); + let last_column = x_idx == n_square - 1; + let last_row = y_idx == n_square - 1; + if last_column { + // that last column of 0-cell needs special treatment + // bc there are no "horizontal" associated dart + let vertex_id = map.vertex_id(base_dart + 4); + map.insert_vertex( + vertex_id, + (T::from(x_idx + 1).unwrap(), T::from(y_idx).unwrap()), + ); + } + if last_row { + // same as the case on x + let vertex_id = map.vertex_id(base_dart + 2); + map.insert_vertex( + vertex_id, + (T::from(x_idx).unwrap(), T::from(y_idx + 1).unwrap()), + ); + } + if last_row & last_column { + // need to do the upper right corner + let vertex_id = map.vertex_id(base_dart + 5); + map.insert_vertex( + vertex_id, + (T::from(x_idx + 1).unwrap(), T::from(y_idx + 1).unwrap()), + ); + } + } + }) }); // rebuild faces - assert_eq!(map.build_all_faces(), n_square.pow(2) * 2); + assert_eq!(map.fetch_faces().identifiers.len(), n_square.pow(2) * 2); map } @@ -210,16 +265,17 @@ mod tests { // reusing the same pattern as the one used during construction // face 0 - assert_eq!(cmap.faceid(1), 0); - assert_eq!(cmap.faceid(2), 0); - assert_eq!(cmap.faceid(3), 0); - assert_eq!(cmap.faceid(4), 0); - assert_eq!(cmap.face(0).corners.len(), 4); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(1))); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(2))); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(3))); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(4))); - assert!(cmap.face(0).closed); + assert_eq!(cmap.face_id(1), 1); + assert_eq!(cmap.face_id(2), 1); + assert_eq!(cmap.face_id(3), 1); + assert_eq!(cmap.face_id(4), 1); + + let mut face = cmap.i_cell::<2>(1); + assert_eq!(face.next(), Some(1)); + assert_eq!(face.next(), Some(2)); + assert_eq!(face.next(), Some(3)); + assert_eq!(face.next(), Some(4)); + assert_eq!(face.next(), None); assert_eq!(cmap.beta::<1>(1), 2); assert_eq!(cmap.beta::<1>(2), 3); @@ -232,16 +288,17 @@ mod tests { assert_eq!(cmap.beta::<2>(4), 0); // face 1 - assert_eq!(cmap.faceid(5), 1); - assert_eq!(cmap.faceid(6), 1); - assert_eq!(cmap.faceid(7), 1); - assert_eq!(cmap.faceid(8), 1); - assert_eq!(cmap.face(1).corners.len(), 4); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(5))); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(6))); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(7))); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(8))); - assert!(cmap.face(1).closed); + assert_eq!(cmap.face_id(5), 5); + assert_eq!(cmap.face_id(6), 5); + assert_eq!(cmap.face_id(7), 5); + assert_eq!(cmap.face_id(8), 5); + + let mut face = cmap.i_cell::<2>(5); + assert_eq!(face.next(), Some(5)); + assert_eq!(face.next(), Some(6)); + assert_eq!(face.next(), Some(7)); + assert_eq!(face.next(), Some(8)); + assert_eq!(face.next(), None); assert_eq!(cmap.beta::<1>(5), 6); assert_eq!(cmap.beta::<1>(6), 7); @@ -254,16 +311,17 @@ mod tests { assert_eq!(cmap.beta::<2>(8), 2); // face 2 - assert_eq!(cmap.faceid(9), 2); - assert_eq!(cmap.faceid(10), 2); - assert_eq!(cmap.faceid(11), 2); - assert_eq!(cmap.faceid(12), 2); - assert_eq!(cmap.face(2).corners.len(), 4); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(9))); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(10))); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(11))); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(12))); - assert!(cmap.face(2).closed); + assert_eq!(cmap.face_id(9), 9); + assert_eq!(cmap.face_id(10), 9); + assert_eq!(cmap.face_id(11), 9); + assert_eq!(cmap.face_id(12), 9); + + let mut face = cmap.i_cell::<2>(9); + assert_eq!(face.next(), Some(9)); + assert_eq!(face.next(), Some(10)); + assert_eq!(face.next(), Some(11)); + assert_eq!(face.next(), Some(12)); + assert_eq!(face.next(), None); assert_eq!(cmap.beta::<1>(9), 10); assert_eq!(cmap.beta::<1>(10), 11); @@ -276,16 +334,17 @@ mod tests { assert_eq!(cmap.beta::<2>(12), 0); // face 3 - assert_eq!(cmap.faceid(13), 3); - assert_eq!(cmap.faceid(14), 3); - assert_eq!(cmap.faceid(15), 3); - assert_eq!(cmap.faceid(16), 3); - assert_eq!(cmap.face(3).corners.len(), 4); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(13))); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(14))); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(15))); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(16))); - assert!(cmap.face(3).closed); + assert_eq!(cmap.face_id(13), 13); + assert_eq!(cmap.face_id(14), 13); + assert_eq!(cmap.face_id(15), 13); + assert_eq!(cmap.face_id(16), 13); + + let mut face = cmap.i_cell::<2>(13); + assert_eq!(face.next(), Some(13)); + assert_eq!(face.next(), Some(14)); + assert_eq!(face.next(), Some(15)); + assert_eq!(face.next(), Some(16)); + assert_eq!(face.next(), None); assert_eq!(cmap.beta::<1>(13), 14); assert_eq!(cmap.beta::<1>(14), 15); @@ -305,148 +364,148 @@ mod tests { // hardcoded because using a generic loop & dim would just mean // reusing the same pattern as the one used during construction - // face 0 - assert_eq!(cmap.faceid(1), 0); - assert_eq!(cmap.faceid(17), 0); - assert_eq!(cmap.faceid(4), 0); - assert_eq!(cmap.face(0).corners.len(), 3); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(1))); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(17))); - assert!(cmap.face(0).corners.contains(&cmap.vertexid(4))); - assert!(cmap.face(0).closed); - - assert_eq!(cmap.beta::<1>(1), 17); - assert_eq!(cmap.beta::<1>(17), 4); - assert_eq!(cmap.beta::<1>(4), 1); - - assert_eq!(cmap.beta::<2>(1), 0); - assert_eq!(cmap.beta::<2>(17), 18); - assert_eq!(cmap.beta::<2>(4), 0); - // face 1 - assert_eq!(cmap.faceid(2), 1); - assert_eq!(cmap.faceid(3), 1); - assert_eq!(cmap.faceid(18), 1); - assert_eq!(cmap.face(1).corners.len(), 3); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(2))); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(3))); - assert!(cmap.face(1).corners.contains(&cmap.vertexid(18))); - assert!(cmap.face(1).closed); + assert_eq!(cmap.face_id(1), 1); + assert_eq!(cmap.face_id(2), 1); + assert_eq!(cmap.face_id(3), 1); + let mut face = cmap.i_cell::<2>(1); + assert_eq!(face.next(), Some(1)); + assert_eq!(face.next(), Some(2)); + assert_eq!(face.next(), Some(3)); + + assert_eq!(cmap.beta::<1>(1), 2); assert_eq!(cmap.beta::<1>(2), 3); - assert_eq!(cmap.beta::<1>(3), 18); - assert_eq!(cmap.beta::<1>(18), 2); + assert_eq!(cmap.beta::<1>(3), 1); - assert_eq!(cmap.beta::<2>(2), 8); - assert_eq!(cmap.beta::<2>(3), 9); - assert_eq!(cmap.beta::<2>(18), 17); + assert_eq!(cmap.beta::<2>(1), 0); + assert_eq!(cmap.beta::<2>(2), 4); + assert_eq!(cmap.beta::<2>(3), 0); - // face 2 - assert_eq!(cmap.faceid(5), 2); - assert_eq!(cmap.faceid(19), 2); - assert_eq!(cmap.faceid(8), 2); - assert_eq!(cmap.face(2).corners.len(), 3); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(5))); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(19))); - assert!(cmap.face(2).corners.contains(&cmap.vertexid(8))); - assert!(cmap.face(2).closed); - - assert_eq!(cmap.beta::<1>(5), 19); - assert_eq!(cmap.beta::<1>(19), 8); - assert_eq!(cmap.beta::<1>(8), 5); + // face 4 + assert_eq!(cmap.face_id(4), 4); + assert_eq!(cmap.face_id(5), 4); + assert_eq!(cmap.face_id(6), 4); - assert_eq!(cmap.beta::<2>(5), 0); - assert_eq!(cmap.beta::<2>(19), 20); - assert_eq!(cmap.beta::<2>(8), 2); + let mut face = cmap.i_cell::<2>(4); + assert_eq!(face.next(), Some(4)); + assert_eq!(face.next(), Some(5)); + assert_eq!(face.next(), Some(6)); - // face 3 - assert_eq!(cmap.faceid(6), 3); - assert_eq!(cmap.faceid(7), 3); - assert_eq!(cmap.faceid(20), 3); - assert_eq!(cmap.face(3).corners.len(), 3); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(6))); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(7))); - assert!(cmap.face(3).corners.contains(&cmap.vertexid(20))); - assert!(cmap.face(3).closed); + assert_eq!(cmap.beta::<1>(4), 5); + assert_eq!(cmap.beta::<1>(5), 6); + assert_eq!(cmap.beta::<1>(6), 4); - assert_eq!(cmap.beta::<1>(6), 7); - assert_eq!(cmap.beta::<1>(7), 20); - assert_eq!(cmap.beta::<1>(20), 6); + assert_eq!(cmap.beta::<2>(4), 2); + assert_eq!(cmap.beta::<2>(5), 9); + assert_eq!(cmap.beta::<2>(6), 13); - assert_eq!(cmap.beta::<2>(6), 0); - assert_eq!(cmap.beta::<2>(7), 13); - assert_eq!(cmap.beta::<2>(20), 19); + // face 7 + assert_eq!(cmap.face_id(7), 7); + assert_eq!(cmap.face_id(8), 7); + assert_eq!(cmap.face_id(9), 7); - // face 4 - assert_eq!(cmap.faceid(9), 4); - assert_eq!(cmap.faceid(21), 4); - assert_eq!(cmap.faceid(12), 4); - assert_eq!(cmap.face(4).corners.len(), 3); - assert!(cmap.face(4).corners.contains(&cmap.vertexid(9))); - assert!(cmap.face(4).corners.contains(&cmap.vertexid(21))); - assert!(cmap.face(4).corners.contains(&cmap.vertexid(12))); - assert!(cmap.face(4).closed); - - assert_eq!(cmap.beta::<1>(9), 21); - assert_eq!(cmap.beta::<1>(21), 12); - assert_eq!(cmap.beta::<1>(12), 9); + let mut face = cmap.i_cell::<2>(7); + assert_eq!(face.next(), Some(7)); + assert_eq!(face.next(), Some(8)); + assert_eq!(face.next(), Some(9)); - assert_eq!(cmap.beta::<2>(9), 3); - assert_eq!(cmap.beta::<2>(21), 22); - assert_eq!(cmap.beta::<2>(12), 0); + assert_eq!(cmap.beta::<1>(7), 8); + assert_eq!(cmap.beta::<1>(8), 9); + assert_eq!(cmap.beta::<1>(9), 7); + + assert_eq!(cmap.beta::<2>(7), 0); + assert_eq!(cmap.beta::<2>(8), 10); + assert_eq!(cmap.beta::<2>(9), 5); - // face 5 - assert_eq!(cmap.faceid(10), 5); - assert_eq!(cmap.faceid(11), 5); - assert_eq!(cmap.faceid(22), 5); - assert_eq!(cmap.face(5).corners.len(), 3); - assert!(cmap.face(5).corners.contains(&cmap.vertexid(10))); - assert!(cmap.face(5).corners.contains(&cmap.vertexid(11))); - assert!(cmap.face(5).corners.contains(&cmap.vertexid(22))); - assert!(cmap.face(5).closed); + // face 10 + assert_eq!(cmap.face_id(10), 10); + assert_eq!(cmap.face_id(11), 10); + assert_eq!(cmap.face_id(12), 10); + + let mut face = cmap.i_cell::<2>(10); + assert_eq!(face.next(), Some(10)); + assert_eq!(face.next(), Some(11)); + assert_eq!(face.next(), Some(12)); assert_eq!(cmap.beta::<1>(10), 11); - assert_eq!(cmap.beta::<1>(11), 22); - assert_eq!(cmap.beta::<1>(22), 10); + assert_eq!(cmap.beta::<1>(11), 12); + assert_eq!(cmap.beta::<1>(12), 10); - assert_eq!(cmap.beta::<2>(10), 16); + assert_eq!(cmap.beta::<2>(10), 8); assert_eq!(cmap.beta::<2>(11), 0); - assert_eq!(cmap.beta::<2>(22), 21); - - // face 6 - assert_eq!(cmap.faceid(13), 6); - assert_eq!(cmap.faceid(23), 6); - assert_eq!(cmap.faceid(16), 6); - assert_eq!(cmap.face(6).corners.len(), 3); - assert!(cmap.face(6).corners.contains(&cmap.vertexid(13))); - assert!(cmap.face(6).corners.contains(&cmap.vertexid(23))); - assert!(cmap.face(6).corners.contains(&cmap.vertexid(16))); - assert!(cmap.face(6).closed); - - assert_eq!(cmap.beta::<1>(13), 23); - assert_eq!(cmap.beta::<1>(23), 16); - assert_eq!(cmap.beta::<1>(16), 13); + assert_eq!(cmap.beta::<2>(12), 19); - assert_eq!(cmap.beta::<2>(13), 7); - assert_eq!(cmap.beta::<2>(23), 24); - assert_eq!(cmap.beta::<2>(16), 10); + // face 13 + assert_eq!(cmap.face_id(13), 13); + assert_eq!(cmap.face_id(14), 13); + assert_eq!(cmap.face_id(15), 13); - // face 7 - assert_eq!(cmap.faceid(14), 7); - assert_eq!(cmap.faceid(15), 7); - assert_eq!(cmap.faceid(24), 7); - assert_eq!(cmap.face(7).corners.len(), 3); - assert!(cmap.face(7).corners.contains(&cmap.vertexid(14))); - assert!(cmap.face(7).corners.contains(&cmap.vertexid(15))); - assert!(cmap.face(7).corners.contains(&cmap.vertexid(24))); - assert!(cmap.face(7).closed); + let mut face = cmap.i_cell::<2>(13); + assert_eq!(face.next(), Some(13)); + assert_eq!(face.next(), Some(14)); + assert_eq!(face.next(), Some(15)); + assert_eq!(cmap.beta::<1>(13), 14); assert_eq!(cmap.beta::<1>(14), 15); - assert_eq!(cmap.beta::<1>(15), 24); - assert_eq!(cmap.beta::<1>(24), 14); + assert_eq!(cmap.beta::<1>(15), 13); - assert_eq!(cmap.beta::<2>(14), 0); + assert_eq!(cmap.beta::<2>(13), 6); + assert_eq!(cmap.beta::<2>(14), 16); assert_eq!(cmap.beta::<2>(15), 0); - assert_eq!(cmap.beta::<2>(24), 23); + + // face 16 + assert_eq!(cmap.face_id(16), 16); + assert_eq!(cmap.face_id(17), 16); + assert_eq!(cmap.face_id(18), 16); + + let mut face = cmap.i_cell::<2>(16); + assert_eq!(face.next(), Some(16)); + assert_eq!(face.next(), Some(17)); + assert_eq!(face.next(), Some(18)); + + assert_eq!(cmap.beta::<1>(16), 17); + assert_eq!(cmap.beta::<1>(17), 18); + assert_eq!(cmap.beta::<1>(18), 16); + + assert_eq!(cmap.beta::<2>(16), 14); + assert_eq!(cmap.beta::<2>(17), 21); + assert_eq!(cmap.beta::<2>(18), 0); + + // face 19 + assert_eq!(cmap.face_id(19), 19); + assert_eq!(cmap.face_id(20), 19); + assert_eq!(cmap.face_id(21), 19); + + let mut face = cmap.i_cell::<2>(19); + assert_eq!(face.next(), Some(19)); + assert_eq!(face.next(), Some(20)); + assert_eq!(face.next(), Some(21)); + + assert_eq!(cmap.beta::<1>(19), 20); + assert_eq!(cmap.beta::<1>(20), 21); + assert_eq!(cmap.beta::<1>(21), 19); + + assert_eq!(cmap.beta::<2>(19), 12); + assert_eq!(cmap.beta::<2>(20), 22); + assert_eq!(cmap.beta::<2>(21), 17); + + // face 22 + assert_eq!(cmap.face_id(22), 22); + assert_eq!(cmap.face_id(23), 22); + assert_eq!(cmap.face_id(24), 22); + + let mut face = cmap.i_cell::<2>(22); + assert_eq!(face.next(), Some(22)); + assert_eq!(face.next(), Some(23)); + assert_eq!(face.next(), Some(24)); + + assert_eq!(cmap.beta::<1>(22), 23); + assert_eq!(cmap.beta::<1>(23), 24); + assert_eq!(cmap.beta::<1>(24), 22); + + assert_eq!(cmap.beta::<2>(22), 20); + assert_eq!(cmap.beta::<2>(23), 0); + assert_eq!(cmap.beta::<2>(24), 0); } } diff --git a/honeycomb-examples/examples/memory_usage/compute.rs b/honeycomb-examples/examples/memory_usage/compute.rs index ffd13e5e..e9a92d99 100644 --- a/honeycomb-examples/examples/memory_usage/compute.rs +++ b/honeycomb-examples/examples/memory_usage/compute.rs @@ -1,4 +1,4 @@ -use honeycomb_core::{utils::square_cmap2, CMap2, DartIdentifier, FloatType, UnsewPolicy}; +use honeycomb_core::{utils::square_cmap2, CMap2, DartIdentifier, FloatType}; pub fn main() { // create a 3x3 grid & remove the central square @@ -11,15 +11,15 @@ pub fn main() { DartIdentifier, ) = (17, 18, 19, 20); // separate the square from the rest - cmap.two_unsew(d1, UnsewPolicy::DoNothing); - cmap.two_unsew(d2, UnsewPolicy::DoNothing); - cmap.two_unsew(d3, UnsewPolicy::DoNothing); - cmap.two_unsew(d4, UnsewPolicy::DoNothing); + cmap.two_unsew(d1); + cmap.two_unsew(d2); + cmap.two_unsew(d3); + cmap.two_unsew(d4); // separate dart individually - cmap.one_unsew(d1, UnsewPolicy::DoNothing); - cmap.one_unsew(d2, UnsewPolicy::DoNothing); - cmap.one_unsew(d3, UnsewPolicy::DoNothing); - cmap.one_unsew(d4, UnsewPolicy::DoNothing); + cmap.one_unsew(d1); + cmap.one_unsew(d2); + cmap.one_unsew(d3); + cmap.one_unsew(d4); // remove darts cmap.remove_free_dart(d1); cmap.remove_free_dart(d2); diff --git a/honeycomb-render/src/handle.rs b/honeycomb-render/src/handle.rs index 238409fc..d594b2ab 100644 --- a/honeycomb-render/src/handle.rs +++ b/honeycomb-render/src/handle.rs @@ -7,7 +7,7 @@ use crate::shader_data::Coords2Shader; use crate::SmaaMode; -use honeycomb_core::{CMap2, Coords2, CoordsFloat, FaceIdentifier, Vertex2}; +use honeycomb_core::{CMap2, CoordsFloat, DartIdentifier, Vertex2}; // ------ CONTENT @@ -63,48 +63,52 @@ impl<'a, T: CoordsFloat> CMap2RenderHandle<'a, T> { } pub fn build_darts(&mut self) { - let n_face = self.handle.n_faces() as FaceIdentifier; - let face_iter = (0..n_face).map(|face_id| { - let cell = self.handle.face(face_id); - // compute face center for shrink operation - let tmp = cell - .corners - .iter() - .map(|vid| self.handle.vertex(*vid).into_inner()) - .sum::>() - / T::from(cell.corners.len()).unwrap(); - let center = Vertex2::from(tmp); - (cell, center, face_id) + // let n_face = self.handle.n_faces() as FaceIdentifier; + // get all faces + let faces = self.handle.fetch_faces(); + let face_indices = faces.identifiers.iter(); + let face_vertices = face_indices.clone().map(|face_id| { + ( + self.handle.i_cell::<2>(*face_id as DartIdentifier).count(), + self.handle + .i_cell::<2>(*face_id as DartIdentifier) + .map(|dart_id| { + let vertex_id = self.handle.vertex_id(dart_id); + self.handle.vertex(vertex_id) + }), + ) + }); + let centers = face_vertices.clone().map(|(n_vertices, vertices)| { + vertices + .map(|vertex2: Vertex2| vertex2.into_inner()) + .reduce(|coords1, coords2| coords1 + coords2) + .unwrap() + / T::from(n_vertices).unwrap() }); self.dart_construction_buffer.extend( - face_iter - .flat_map(|(cell, center, face_id)| { - let n_vertices = cell.corners.len(); - let fids = (0..n_vertices).map(move |_| face_id); - let centers = (0..n_vertices).map(move |_| center); - (0..n_vertices) - .zip(centers) - .map(|(vertex_id, center)| { - let v1id = vertex_id; - let v2id = if vertex_id == cell.corners.len() - 1 { - 0 - } else { - vertex_id + 1 - }; - // fetch dart vetices - let (v1, v2) = ( - self.handle.vertex(cell.corners[v1id]), - self.handle.vertex(cell.corners[v2id]), - ); - + face_indices + .copied() + .zip(face_vertices) + .zip(centers) + .flat_map(|((face_id, (n_vertices, vertices)), center)| { + let vertices: Vec> = vertices.collect(); + let vertices_pair = (0..n_vertices).map(move |vid| { + let v1id = vid; + let v2id = if vid == n_vertices - 1 { 0 } else { vid + 1 }; + (vertices[v1id], vertices[v2id]) + }); + let metadata = (0..n_vertices).map(move |_| (face_id, Vertex2::from(center))); + vertices_pair + .zip(metadata) + .map(|((v1, v2), (fid, centerv))| { // shrink towards center - let v1_shrink_dir = (center - *v1).unit_dir().unwrap(); - let v2_shrink_dir = (center - *v2).unit_dir().unwrap(); + let v1_shrink_dir = (centerv - v1).unit_dir().unwrap(); + let v2_shrink_dir = (centerv - v2).unit_dir().unwrap(); let mut v1_intermediate = - *v1 + v1_shrink_dir * T::from(self.params.shrink_factor).unwrap(); + v1 + v1_shrink_dir * T::from(self.params.shrink_factor).unwrap(); let mut v2_intermediate = - *v2 + v2_shrink_dir * T::from(self.params.shrink_factor).unwrap(); + v2 + v2_shrink_dir * T::from(self.params.shrink_factor).unwrap(); // truncate length let seg_dir = (v2_intermediate - v1_intermediate).unit_dir().unwrap(); @@ -114,11 +118,10 @@ impl<'a, T: CoordsFloat> CMap2RenderHandle<'a, T> { seg_dir * T::from(self.params.shrink_factor).unwrap(); // return a coordinate pair - (v1_intermediate, v2_intermediate) + (v1_intermediate, v2_intermediate, fid) }) - .zip(fids) }) - .flat_map(|((v1, v6), face_id)| { + .flat_map(|(v1, v6, face_id)| { // transform the dart coordinates into triangles for the shader to render let seg = v6 - v1; let seg_length = seg.norm();