use std::collections::VecDeque;
use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, NULL_DART_ID, OrbitPolicy};
use honeycomb_core::geometry::{CoordsFloat, Vertex2};
use rustc_hash::FxHashSet as HashSet;
use crate::grisubal::GrisubalError;
use crate::grisubal::model::Boundary;
use crate::utils::VertexAnchor;
pub fn clip_left<T: CoordsFloat>(cmap: &mut CMap2<T>) -> Result<(), GrisubalError> {
let marked = mark_faces(cmap, Boundary::Left, Boundary::Right)?;
delete_darts(cmap, marked, Boundary::Right);
Ok(())
}
pub fn clip_right<T: CoordsFloat>(cmap: &mut CMap2<T>) -> Result<(), GrisubalError> {
let marked = mark_faces(cmap, Boundary::Right, Boundary::Left)?;
delete_darts(cmap, marked, Boundary::Left);
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
fn mark_faces<T: CoordsFloat>(
cmap: &CMap2<T>,
mark: Boundary,
other: Boundary,
) -> Result<HashSet<FaceIdType>, GrisubalError> {
let mut marked: HashSet<FaceIdType> = HashSet::default();
marked.insert(0);
let mut queue: VecDeque<FaceIdType> = (1..cmap.n_darts() as DartIdType)
.filter_map(|dart_id| {
if cmap.read_attribute::<Boundary>(dart_id) == Some(mark) && !cmap.is_free(dart_id) {
return Some(cmap.face_id(dart_id));
}
None
})
.collect();
while let Some(face_id) = queue.pop_front() {
if marked.insert(face_id) {
let mut darts = cmap.orbit(OrbitPolicy::Face, face_id as DartIdType);
if darts.any(|did| cmap.read_attribute::<Boundary>(did) == Some(other)) {
return Err(GrisubalError::InconsistentOrientation(
"between-boundary inconsistency",
));
}
let darts = cmap.orbit(OrbitPolicy::Face, face_id as DartIdType);
queue.extend(darts.filter_map(|dart_id| {
if matches!(
cmap.read_attribute::<Boundary>(cmap.beta::<2>(dart_id)),
Some(Boundary::None) | None
) {
return Some(cmap.face_id(cmap.beta::<2>(dart_id)));
}
None
}));
}
}
marked.remove(&0);
Ok(marked)
}
#[allow(clippy::cast_possible_truncation)]
fn delete_darts<T: CoordsFloat>(
cmap: &mut CMap2<T>,
marked: HashSet<FaceIdType>,
kept_boundary: Boundary,
) {
let kept_boundary_components: Vec<(DartIdType, Vertex2<T>, Option<VertexAnchor>)> =
(1..cmap.n_darts() as DartIdType)
.filter_map(|dart_id| {
if cmap.read_attribute::<Boundary>(dart_id) == Some(kept_boundary) {
return Some((
dart_id,
cmap.read_vertex(cmap.vertex_id(dart_id))
.expect("E: found a topological vertex with no associated coordinates"),
if cmap.contains_attribute::<VertexAnchor>() {
cmap.read_attribute(cmap.vertex_id(dart_id)) } else {
None
},
));
}
None
})
.collect();
for face_id in marked {
let darts: Vec<DartIdType> = cmap
.orbit(OrbitPolicy::Face, face_id as DartIdType)
.collect();
for &dart in &darts {
let _ = cmap.remove_vertex(cmap.vertex_id(dart));
cmap.set_betas(dart, [NULL_DART_ID; 3]);
cmap.release_dart(dart).expect("E: unreachable");
}
}
for (dart, vertex, anchor) in kept_boundary_components {
cmap.set_beta::<2>(dart, NULL_DART_ID); let vid = cmap.vertex_id(dart);
cmap.write_vertex(vid, vertex);
if let Some(a) = anchor {
cmap.write_attribute(vid, a);
}
}
}