use crate::common::VertexIndex;
use crate::common::macros::{
integrity_assert, integrity_assert_eq, integrity_assert_unique, integrity_println,
};
use crate::{
corner_table::{CornerIndex, TriangleIndex},
isotropic_remesh::IsotropicRemeshAlgo,
};
use std::fmt::Debug;
use vector_traits::num_traits::{AsPrimitive, Float};
use vector_traits::prelude::{GenericVector3, SimdUpgradable};
impl<S, V, const ENABLE_UNSAFE: bool> IsotropicRemeshAlgo<S, V, ENABLE_UNSAFE>
where
S: crate::common::sealed::ScalarType,
f64: AsPrimitive<S>,
V: Debug + Copy + From<[S; 3]> + Into<[S; 3]> + Sync + 'static,
{
const SUBSCRIPT_CHARS: [char; 10] = ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'];
pub(crate) fn subscript(n: usize) -> String {
n.to_string()
.chars()
.map(|c| Self::SUBSCRIPT_CHARS[c.to_digit(10).unwrap() as usize])
.collect()
}
#[inline(always)]
pub(crate) fn triangle_raw_normal(
a: S::Vec3Simd,
b: S::Vec3Simd,
c: S::Vec3Simd,
) -> S::Vec3Simd {
assert!(a.is_finite());
assert!(b.is_finite());
assert!(c.is_finite());
(b - a).cross(c - a)
}
pub(crate) fn iter_valid_vertices(&self) -> impl Iterator<Item = VertexIndex> {
(0..self.vertices.len() as u32)
.map(VertexIndex)
.filter(|&v| self.corner_table.corner(v).is_valid())
}
fn check_integrity(&self) -> Result<(), String> {
self.corner_table
.check_integrity(self.vertices.len() as u32)?;
for v in (0..self.vertices.len() as u32).map(VertexIndex) {
if !self.corner_table.corner(v).is_valid() {
assert!(!self.vertex_pool.is_used(v));
} else {
assert!(self.vertex_pool.is_used(v));
}
}
for v in self.iter_valid_vertices() {
self.check_vertex_fan(v)?;
}
let triangle_count = self.corner_table.vertex_of_corners().len() as u32 / 3;
for triangle in (0..triangle_count).map(TriangleIndex) {
{
let [c, cn, cp] = triangle.corners();
if self.corner_table.is_triangle_deleted(triangle) {
self.corner_table.check_deleted_triangle(triangle);
continue;
}
let vic = self.corner_table.vertex(c);
let vc = self.vertex_of_corner(c).to_simd();
let vn = self.vertex_of_corner(cn).to_simd();
let vin = self.corner_table.vertex(cn);
let vp = self.vertex_of_corner(cp).to_simd();
let vip = self.corner_table.vertex(cp);
let l = Self::triangle_raw_normal(vc, vn, vp).magnitude();
if (!Float::is_finite(l)) || Float::abs(l) < S::EPSILON {
return Err(format!(
"Zero area triangle: {c:?}{vic:?},{cn:?}{vin:?},{cp:?}{vip:?} normal length:{l}",
));
}
}
for i in 0..3 {
let c = CornerIndex(triangle.0 * 3 + i);
let (cn, cp) = self.corner_table.next_prev(c); let vic = self.corner_table.vertex(c);
let vin = self.corner_table.vertex(cn);
let co = self.corner_table.opposite(cp);
debug_assert!(co.is_valid());
debug_assert!(vic.is_valid());
debug_assert!(vin.is_valid());
let vio = self.corner_table.vertex(co);
debug_assert!(vio.is_valid());
let vip = self.corner_table.vertex(cp);
debug_assert!(vip.is_valid());
integrity_assert_unique!(vic, vio, vin, vip);
if vic.0 < vin.0 {
let coplanarity = self.coplanarity(c);
assert!(Float::is_finite(coplanarity));
if coplanarity < self.params.inversion_validation_threshold {
let co = self.corner_table.opposite(cp);
let (con, cop) = self.corner_table.next_prev(co);
return Err(format!(
"adjacent triangles with opposite normals: {},{},{} & {},{},{} cop:{coplanarity}",
self.corner_table.data.dbg_corner(c),
self.corner_table.data.dbg_corner(cn),
self.corner_table.data.dbg_corner(cp),
self.corner_table.data.dbg_corner(con),
self.corner_table.data.dbg_corner(cop),
self.corner_table.data.dbg_corner(co),
));
}
}
}
}
Ok(())
}
pub fn check_mesh_integrity(&self, context: &str) -> Result<(), String> {
match self.check_integrity() {
Ok(()) => {
integrity_println!("✓ Mesh integrity OK at: {context}");
Ok(())
}
Err(e) => {
eprintln!("✗ Mesh integrity FAILED at {context}: {e}");
self.print_debug_stats();
Err(e)
}
}
}
pub fn check_mesh_integrity_fast(&self, context: &str) -> Result<(), String> {
match self
.corner_table
.check_basic_integrity(self.vertices.len() as u32)
{
Ok(()) => Ok(()),
Err(e) => {
eprintln!("✗ Basic mesh integrity FAILED at {context}: {e}");
Err(e)
}
}
}
pub(crate) fn dbg_edge(&self, c: CornerIndex) -> String {
let cn = self.corner_table.next(c);
format!(
"{}->{}",
self.corner_table.data.dbg_corner(c),
self.corner_table.data.dbg_corner(cn)
)
}
pub(crate) fn dbg_corner(&self, c: CornerIndex) -> String {
self.corner_table.data.dbg_corner(c)
}
pub(crate) fn dbg_vertex(&self, vi: VertexIndex) -> String {
let v = self.vertex(vi);
let c = self.corner_table.corner(vi);
let fan = self.corner_table.ccw_vertex_fan(c);
format!("{vi:?}{:?} fan:{:?}", v.into(), fan)
}
pub(crate) fn dbg_vertex_fan(&self, fan: &[CornerIndex]) -> String {
let parts: Vec<String> = fan
.iter()
.enumerate()
.map(|(i, &c)| {
let (n, p) = self.corner_table.next_prev(c);
let o = self.corner_table.opposite(c);
format!(
"t{}({}(O:{}),{},{})",
Self::subscript(i),
self.corner_table.data.dbg_corner(c),
self.corner_table.data.dbg_corner(o),
self.corner_table.data.dbg_corner(n),
self.corner_table.data.dbg_corner(p)
)
})
.collect();
format!("{}: valence:{}", parts.join(","), fan.len())
}
pub(crate) fn dbg_corner_table(&self, count: usize) {
let num_corners = self.corner_table.len() as u32;
let num_triangles = num_corners / 3;
println!(
"First {count} triangles (of {num_triangles}): [vertex_id:corner_id,opposite vertex_id:corner_id+1,opposite vertex_id:corner_id+2, opposite]"
);
for triangle in (0..num_triangles)
.map(TriangleIndex)
.filter(|t| !self.corner_table.is_triangle_deleted(*t))
.take(count)
{
let c0 = CornerIndex(triangle.0 * 3);
let c1 = CornerIndex(triangle.0 * 3 + 1);
let c2 = CornerIndex(triangle.0 * 3 + 2);
if c2.0 < num_corners {
let vi0 = self.corner_table.vertex(c0);
let vi1 = self.corner_table.vertex(c1);
let vi2 = self.corner_table.vertex(c2);
if !self.corner_table.is_triangle_deleted(triangle) {
let v0 = self.vertex(vi0).to_simd();
let v1 = self.vertex(vi1).to_simd();
let v2 = self.vertex(vi2).to_simd();
let n = Self::triangle_raw_normal(v0, v1, v2);
let v0: [S; 3] = v0.into();
let v1: [S; 3] = v1.into();
let v2: [S; 3] = v2.into();
if n.magnitude() < S::EPSILON {
println!(
" {triangle:?}: ({}, {}, {}) ({v0:?},{v1:?},{v2:?}) zero area triangle",
self.corner_table.data.dbg_corner(c0),
self.corner_table.data.dbg_corner(c0),
self.corner_table.data.dbg_corner(c0)
);
} else {
println!(
" {triangle:?}: ({}, {}, {}) ({v0:?},{v1:?},{v2:?})",
self.corner_table.data.dbg_corner(c0),
self.corner_table.data.dbg_corner(c0),
self.corner_table.data.dbg_corner(c0)
);
}
} else {
println!(
" T{triangle:?}: (C{} C{} C{}) deleted",
triangle.0 * 3,
triangle.0 * 3 + 1,
triangle.0 * 3 + 2
);
}
}
}
}
pub fn print_debug_stats(&self) {
let num_vertices = self.vertices.len() as u32;
let num_corners = self.corner_table.len() as u32;
let num_triangles = num_corners / 3;
println!("=== Corner Table Debug Stats ===");
println!("Vertices: {num_vertices}, Triangles: {num_triangles}, Corners: {num_corners}");
println!(
" free_triangles: {}",
self.corner_table.deleted_triangle_count()
);
let boundary_edges = self
.corner_table
.opposite_corners()
.iter()
.enumerate()
.map(|(c, &o)| (CornerIndex(c as u32), o))
.filter(|(c, o)| {
!self.corner_table.is_triangle_deleted((*c).into())
&& self.corner_table.is_triangle_deleted((*o).into())
})
.count();
println!("Boundary edges: {boundary_edges}");
let mut vertex_degrees = vec![0_u32; num_vertices as usize];
for &vertex in self.corner_table.vertex_of_corners().iter() {
if vertex.0 < num_vertices {
vertex_degrees[vertex.0 as usize] += 1;
}
}
let isolated_vertices = vertex_degrees
.iter()
.enumerate()
.filter(|(v, degree)| !self.is_vertex_deleted(VertexIndex(*v as u32)) && **degree == 0)
.count();
println!("Isolated vertices: {isolated_vertices}");
if false {
self.dbg_corner_table(20);
println!();
print!(" ");
for v in self
.vertices
.iter()
.enumerate()
.map(|(v, _)| VertexIndex(v as u32))
{
if self.corner_table.corner_of_vertices()[v.0 as usize].is_valid() {
print!(
"{v:?}:{} ",
self.corner_table
.data
.dbg_corner(self.corner_table.corner_of_vertices()[v.0 as usize])
);
} else {
print!("{v:?}:deleted ");
}
}
println!();
}
println!("==============================");
}
pub(crate) fn coplanarity(&self, c: CornerIndex) -> S {
let (cn, cp) = self.corner_table.next_prev(c);
let vic = self.corner_table.vertex(c);
integrity_assert!(vic.is_valid());
let vicn = self.corner_table.vertex(cn);
integrity_assert!(vicn.is_valid());
let vicp = self.corner_table.vertex(cp);
integrity_assert!(vicp.is_valid());
let co = self.corner_table.opposite(c);
let (con, cop) = self.corner_table.next_prev(co);
integrity_assert_eq!(self.corner_table.vertex(con), vicp);
integrity_assert_eq!(self.corner_table.vertex(cop), vicn);
let vico = self.corner_table.vertex(co);
integrity_assert!(vico.is_valid());
integrity_assert_unique!(vic, vicn, vicp, vico);
let vc = self.vertex(vic).to_simd();
let vn = self.vertex(vicn).to_simd();
let vp = self.vertex(vicp).to_simd();
let vo = self.vertex(vico).to_simd();
assert_eq!(
Self::normal_similarity_crease_angle(
vc,
vn,
vp,
vo,
self.params.crease_limit_threshold_sq
),
Some(true),
"{:?},{:?},{:?},{:?} were too opposite",
vc.into(),
vn.into(),
vp.into(),
vo.into()
);
Self::normal_similarity_opposite_winding_slow(vc, vn, vp, vo)
}
#[inline(always)]
pub(crate) fn dihedral_angle_cosine_check(
a: S::Vec3Simd,
b: S::Vec3Simd,
c: S::Vec3Simd,
d: S::Vec3Simd,
threshold: S,
) -> bool {
Self::normal_similarity_opposite_winding_slow_wrong_order(a, b, c, d) >= threshold
}
pub(crate) fn check_vertex_fan(&self, v: VertexIndex) -> Result<(), String> {
if !v.is_valid() {
return Err(format!("Vertex {v:?} was not valid"));
}
if self.is_vertex_deleted(v) {
return Err(format!("Vertex {v:?} was valid but deleted"));
}
let c0 = self.corner_table.corner(v);
if !c0.is_valid() {
return Err(format!("Corner of Vertex {v:?} was invalid"));
}
if self.corner_table.is_triangle_deleted(c0.into()) {
return Err(format!("Corner {c0:?} was already deleted"));
}
let c0_fan = self.corner_table.ccw_vertex_fan(c0);
for &c in &c0_fan {
if self.corner_table.is_triangle_deleted(c.into()) {
return Err(format!("Corner {c:?} was already deleted"));
}
let o = self.corner_table.opposite(c);
if self.corner_table.is_triangle_deleted(o.into()) {
return Err(format!("opposite corner {o:?} was already deleted"));
}
let (cn, cp) = self.corner_table.next_prev(c);
let vio = self.corner_table.vertex(o);
let vic = self.corner_table.vertex(c);
let vicn = self.corner_table.vertex(cn);
let vicp = self.corner_table.vertex(cp);
integrity_assert_unique!(vio, vic, vicn, vicp);
let vo = self.vertex_of_corner(o).to_simd();
let vc = self.vertex_of_corner(c).to_simd();
let vcn = self.vertex_of_corner(cn).to_simd();
let vcp = self.vertex_of_corner(cp).to_simd();
match Self::normal_similarity_crease_angle(
vc,
vcn,
vcp,
vo,
self.params.inversion_validation_threshold_sq,
) {
Some(true) => {}
v => {
integrity_println!("ci triangle:{}", self.corner_table.dbg_triangle(c));
integrity_println!("c.fan.prev: {}", self.dbg_fan_prev(&c0_fan));
return Err(format!(
"fail: dihedral_angle_cosine_test_negative_threshold()->{:?} {:?},{:?},{:?},{:?} {:?},{:?},{:?},{:?} {}",
v,
vic,
vicn,
vicp,
vio,
vc.into(),
vcn.into(),
vcp.into(),
vo.into(),
self.params.inversion_validation_threshold
));
}
}
}
Ok(())
}
pub(crate) fn check_vertex_fan_dbg(&self, v: VertexIndex) -> Result<(), String> {
let c0 = self.corner_table.corner(v);
if self.corner_table.is_triangle_deleted(c0.into()) {
return Err(format!("Corner {c0:?} was already deleted"));
}
for c in self.corner_table.iter_ccw_swing(c0) {
if self.corner_table.is_triangle_deleted(c.into()) {
return Err(format!("Corner {c:?} was already deleted"));
}
let o = self.corner_table.opposite(c);
if self.corner_table.is_triangle_deleted(o.into()) {
return Err(format!("opposite corner {o:?} was already deleted"));
}
let (cn, cp) = self.corner_table.next_prev(c);
let vio = self.corner_table.vertex(o);
let vic = self.corner_table.vertex(c);
let vicn = self.corner_table.vertex(cn);
let vicp = self.corner_table.vertex(cp);
integrity_assert_unique!(vio, vic, vicn, vicp);
let vo = self.vertex_of_corner(o).to_simd();
let vc = self.vertex_of_corner(c).to_simd();
let vcn = self.vertex_of_corner(cn).to_simd();
let vcp = self.vertex_of_corner(cp).to_simd();
match Self::normal_similarity_crease_angle(
vc,
vcn,
vcp,
vo,
self.params.inversion_validation_threshold_sq,
) {
Some(true) => {}
Some(false) => {
return Err(format!(
"fail: dihedral_angle_cosine_test_negative_threshold()->Some(false) {:?},{:?},{:?},{:?} {:?},{:?},{:?},{:?} {}",
vic,
vicn,
vicp,
vio,
vc.into(),
vcn.into(),
vcp.into(),
vo.into(),
self.params.inversion_validation_threshold,
));
}
None => {
return Err(format!(
"fail:dihedral_angle_cosine_test_negative_threshold()->None {:?},{:?},{:?},{:?} {:?},{:?},{:?},{:?} {}",
vic,
vicn,
vicp,
vio,
vc.into(),
vcn.into(),
vcp.into(),
vo.into(),
self.params.inversion_validation_threshold,
));
}
}
}
Ok(())
}
pub(crate) fn dbg_fan_prev(&self, v_fan: &[CornerIndex]) -> String {
v_fan
.iter()
.enumerate()
.map(|(i, c)| {
let p = self.corner_table.prev(*c);
let vertex = self.corner_table.vertex(p);
format!("V{}:{:?}", Self::subscript(i), vertex)
})
.collect::<Vec<_>>()
.join(",")
}
pub(crate) fn dbg_fan_next(&self, v_fan: &[CornerIndex]) -> String {
v_fan
.iter()
.enumerate()
.map(|(i, c)| {
let n = self.corner_table.next(*c);
let vertex = self.corner_table.vertex(n);
format!("V{}:{:?}", Self::subscript(i), vertex)
})
.collect::<Vec<_>>()
.join(",")
}
}