use crate::common::VertexIndex;
#[allow(unused_imports)]
use crate::common::macros::{integrity_print, integrity_println};
use crate::corner_table::{CornerIndex, CornerTable};
use crate::prelude::RemeshError;
use rustc_hash::FxHashMap;
pub(super) fn validate_num_corners(num_corners: usize) -> Result<u32, RemeshError> {
let num_corners: u32 = num_corners.try_into().map_err(|e| {
RemeshError(format!("Triangle list too large for u32 corners. {e:?}").to_string())
})?;
if !num_corners.is_multiple_of(3) {
return Err(RemeshError(
"Triangle list must have length divisible by 3".to_string(),
));
}
if num_corners >= u32::MAX / 2 {
return Err(RemeshError(
"Triangle list too large for u32 corners".to_string(),
));
}
Ok(num_corners)
}
impl<const ENABLE_UNSAFE: bool> CornerTable<ENABLE_UNSAFE> {
pub(super) fn check_edge_winding(
c0: CornerIndex,
c1: CornerIndex,
vertex_of_corner: &[VertexIndex],
) -> Result<(), RemeshError> {
let (c0_next, c0_prev) = c0.next_prev();
let (c1_next, c1_prev) = c1.next_prev();
let c0_edge = (
vertex_of_corner[c0_next.usize()],
vertex_of_corner[c0_prev.usize()],
);
let c1_edge = (
vertex_of_corner[c1_next.usize()],
vertex_of_corner[c1_prev.usize()],
);
if !(c0_edge.0 == c1_edge.1 && c0_edge.1 == c1_edge.0) {
return Err(RemeshError(format!(
"Inconsistent triangle winding at edge V{:?}-V{:?}: \
triangle T{:?} has edge {:?}→{:?}, \
triangle T{:?} has edge {:?}→{:?} (should be reversed)",
c0_edge.0,
c0_edge.1,
c0.triangle(),
c0_edge.0,
c0_edge.1,
c1.triangle(),
c1_edge.0,
c1_edge.1
)));
}
Ok(())
}
pub(crate) fn from_manifold_triangles(
vertex_of_corner: Vec<VertexIndex>,
max_vertex_index: u32,
) -> Result<Self, RemeshError> {
let num_corners = validate_num_corners(vertex_of_corner.len())?;
let mut opposite_corner = vec![CornerIndex::INVALID; num_corners as usize];
let mut corner_of_vertex = vec![CornerIndex::INVALID; max_vertex_index as usize + 1];
for (corner, &vertex) in vertex_of_corner.iter().enumerate() {
corner_of_vertex[vertex.usize()] = CornerIndex(corner as u32);
}
let mut edge_map: FxHashMap<(VertexIndex, VertexIndex), CornerIndex> = {
let capacity = (num_corners / 2)
.checked_mul(3)
.ok_or_else(|| RemeshError("Capacity calculation overflow".to_string()))?;
FxHashMap::with_capacity_and_hasher(capacity as usize, Default::default())
};
for corner_id in (0..num_corners).map(CornerIndex) {
let (next_corner_id, prev_corner_id) = corner_id.next_prev();
let v_i = vertex_of_corner[next_corner_id.usize()]; let v_j = vertex_of_corner[prev_corner_id.usize()];
let key = VertexIndex::canonical_pair(v_i, v_j);
if let Some(&stored_corner) = edge_map.get(&key) {
Self::check_edge_winding(stored_corner, corner_id, &vertex_of_corner)?;
opposite_corner[corner_id.usize()] = stored_corner;
opposite_corner[stored_corner.usize()] = corner_id;
let _ = edge_map.remove(&key);
} else {
let _ = edge_map.insert(key, corner_id);
}
}
if !edge_map.is_empty() {
Err(RemeshError(format!(
"{} boundary edges found",
edge_map.len()
)))?
}
Ok(Self::new(
vertex_of_corner,
opposite_corner,
corner_of_vertex,
))
}
}