use crate::common::macros::{integrity_assert, integrity_assert_eq, integrity_assert_unique};
use crate::corner_table::TriangleIndex;
use crate::corner_table::common::CornerTableData;
use crate::{
common::VertexIndex,
corner_table::{CornerIndex, CornerTable},
};
impl<const ENABLE_UNSAFE: bool> CornerTable<ENABLE_UNSAFE> {
#[inline(always)]
pub(crate) fn opposite_corners(&self) -> &[CornerIndex] {
&self.data.opposite_corner_vec
}
#[inline(always)]
pub(crate) fn corner_of_vertices(&self) -> &[CornerIndex] {
&self.data.corner_of_vertex_vec
}
pub(crate) fn iter_valid_corners(&self) -> impl Iterator<Item = CornerIndex> {
self.data
.vertex_of_corner_vec
.iter()
.enumerate()
.filter_map(|(c, &v)| v.is_valid().then_some(CornerIndex(c as u32)))
}
pub(crate) fn assert_corner_fan_vertices(&self, c0: CornerIndex) {
let v0 = self.vertex(c0);
for c in self.iter_ccw_swing(c0).skip(1) {
integrity_assert_eq!(
self.vertex(c),
v0,
"Corner {:?} has vertex {:?} but expected {:?} (same as corner {:?})",
c,
self.vertex(c),
v0,
c0
);
}
}
pub(crate) fn assert_valid_corner(&self, c0: CornerIndex) {
assert!(!self.is_triangle_deleted(c0.into()));
let o = self.data.opposite(c0);
assert!(o.is_valid());
assert!(!self.is_triangle_deleted(o.into()));
assert!(self.next(c0).is_valid());
assert!(self.prev(c0).is_valid());
let v = self.vertex(c0);
assert!(
v.is_valid(),
"Corner has no Vertex:{}",
self.data.dbg_corner(c0)
);
let vc = self.corner(v);
assert!(vc.is_valid());
for (i, c) in self.iter_ccw_swing(c0).enumerate() {
assert!(i < 100); assert!(c.is_valid());
assert!(!self.is_triangle_deleted(c.into()));
assert_eq!(v, self.vertex(c));
}
}
pub(crate) fn assert_triangle_vertices_distinct(&self, c: CornerIndex) {
use crate::common::macros::integrity_assert_ne;
let v0 = self.vertex(c);
let v1 = self.vertex(self.next(c));
let v2 = self.vertex(self.prev(c));
integrity_assert_ne!(v0, v1, "Triangle {:?}: corners share vertex", c.triangle());
integrity_assert_ne!(v0, v2, "Triangle {:?}: corners share vertex", c.triangle());
integrity_assert_ne!(v1, v2, "Triangle {:?}: corners share vertex", c.triangle());
}
pub(crate) fn dbg_triangle_context(&self, context: impl AsRef<str>, c0: CornerIndex) {
let context = context.as_ref();
let (c1, c2) = c0.next_prev();
let v0 = self.data.vertex_of_corner(c0);
let o0 = self.data.opposite(c0);
let v1 = self.data.vertex_of_corner(c1);
let o1 = self.data.opposite(c1);
let v2 = self.data.vertex_of_corner(c2);
let o2 = self.data.opposite(c2);
println!(
"triangle: {context} {c0:?}:v{v0:?}o{o0:?} {c1:?}:v{v1:?}o{o1:?} {c2:?}:v{v2:?}o{o2:?}"
);
}
pub(crate) fn dbg_triangle(&self, c: CornerIndex) -> String {
let (cn, cp) = c.next_prev();
format!(
"{},{},{}",
self.data.dbg_corner(c),
self.data.dbg_corner(cn),
self.data.dbg_corner(cp)
)
}
pub(crate) fn dbg_vertex_fan(&self, vertex: VertexIndex) -> String {
let corner = self.corner(vertex);
self.iter_ccw_swing(corner)
.map(|c| self.dbg_corner(c))
.collect::<Vec<_>>()
.join(",")
}
pub(crate) fn assert_dual_opposite(&self, ca: CornerIndex, cb: CornerIndex) {
integrity_assert!(ca.is_valid());
integrity_assert!(cb.is_valid());
integrity_assert_eq!(
self.data.opposite_corner_vec[ca.0 as usize],
cb,
"ca:{}.opposite != cb:{}",
self.data.dbg_corner(ca),
self.dbg_triangle(cb)
);
integrity_assert_eq!(
self.data.opposite_corner_vec[cb.0 as usize],
ca,
"cb:{}.opposite != ca:{}",
self.data.dbg_corner(cb),
self.dbg_triangle(ca)
);
}
pub(crate) fn get_vertex_valence(&self, vertex: VertexIndex) -> i16 {
integrity_assert!(vertex.is_valid());
let start_corner = self.corner(vertex);
integrity_assert!(start_corner.is_valid());
self.valence(start_corner)
}
pub(crate) fn check_deleted_triangle(&self, triangle: TriangleIndex) {
assert!(self.is_triangle_deleted(triangle));
let c = triangle.base_corner();
let (cn, cp) = c.next_prev();
assert!(
!self.data.vertex_of_corner(c).is_valid(),
"Triangle {} is deleted, but still has a valid vertex",
self.data.dbg_corner(c)
);
assert!(
!self.data.vertex_of_corner(cn).is_valid(),
"Triangle {} is deleted, but still has a valid vertex",
self.data.dbg_corner(cn)
);
assert!(
!self.data.vertex_of_corner(cp).is_valid(),
"Triangle {} is deleted, but still has a valid vertex",
self.data.dbg_corner(cp)
);
assert!(
!self.data.opposite(c).is_valid(),
"Triangle {} is deleted, but still has a valid opposite",
self.data.dbg_corner(c)
);
assert!(
!self.data.opposite(cn).is_valid(),
"Triangle {} is deleted, but still has a valid opposite",
self.data.dbg_corner(cn)
);
assert!(
!self.data.opposite(cp).is_valid(),
"Triangle {} is deleted, but still has a valid opposite",
self.data.dbg_corner(cp)
);
}
pub fn check_integrity(&self, num_vertices: u32) -> Result<(), String> {
let num_corners = self.len() as u32;
let num_triangles = num_corners / 3;
for c in self.iter_valid_corners() {
let o = self.opposite(c);
{
if !o.is_valid() {
return Err(
format!("{c:?} pointed to an invalid opposite corner: {o:?}").to_string(),
);
}
if self.is_triangle_deleted(o.triangle()) {
return Err(format!(
"{c:?} pointed to a deleted, but valid, opposite corner: {}",
self.dbg_triangle(o)
)
.to_string());
}
let triangle = o.into();
if self.is_triangle_deleted(triangle) {
return Err(format!(
"{c:?} pointed to a deleted opposite triangle {triangle:?} {o:?}"
)
.to_string());
}
}
{
let c0 = o;
let (c1, c2) = self.next_prev(c0);
let v0 = self.vertex(c0);
let v1 = self.vertex(c1);
let v2 = self.vertex(c2);
integrity_assert!(v0.is_valid());
integrity_assert!(v1.is_valid());
integrity_assert!(v2.is_valid());
integrity_assert_unique!(v0, v1, v2);
}
{
let v_c = self.vertex(c);
let v_o = self.vertex(o);
if v_c == v_o {
return Err(format!(
"V({c:?})=V{v_c:?} == V(O({c:?})={o:?})=V{v_o:?} (check V(c)!=V(O(c)))"
)
.to_string());
}
}
{
let o_c = self.opposite(c);
let o_o_c = self.opposite(o_c);
if o_o_c != c {
return Err(format!("O(O({c:?}))={o_o_c:?} != c={c:?}").to_string());
}
}
{
let o_p = self.prev(o);
let c_n = self.next(c);
let v_c_n = self.vertex(c_n);
let v_o_p = self.vertex(o_p);
if v_c_n != v_o_p {
return Err(format!(
"V({c:?}.next()={c_n:?})={v_c_n:?} != V(O({o:?}).prev())={o_p:?})={v_o_p:?}"
)
.to_string());
}
}
{
let o_n = self.next(o);
let c_p = self.prev(c);
let v_o_n = self.vertex(c_p);
let v_c_p = self.vertex(o_n);
if v_o_n != v_c_p {
return Err(format!(
"V({c:?}.prev():{c_p:?}){v_o_n:?} != V({o:?}.next():{o_n:?})={v_c_p:?} "
)
.to_string());
}
}
{
let c_n = self.next(c);
let c_n_n = self.next(c_n);
let c_n_n_n = self.next(c_n_n);
if c_n_n_n != c {
return Err(
format!("c.next().next().next()={c_n_n_n:?} != c={c:?}").to_string()
);
}
}
{
let c_p = self.next(c);
let c_p_p = self.next(c_p);
let c_p_p_p = self.next(c_p_p);
if c_p_p_p != c {
return Err(
format!("c.prev().prev().prev()={c_p_p_p:?} != c={c:?}").to_string()
);
}
}
{
if self.is_corner_deleted(c) {
let twin = self.data.twin_unchecked(c);
if twin.is_valid() {
assert_eq!(twin, self.twin(c));
return Err(format!(
"{c:?}.twin()={twin:?} was valid when corner {c:?} was deleted"
)
.to_string());
}
} else {
let twin = self.data.twin_unchecked(c);
if self.is_corner_deleted(twin) {
return Err(format!(
"{c:?}.twin()={twin:?} was deleted when corner={c:?} was not"
)
.to_string());
} else {
assert_eq!(
twin,
self.twin(c),
"twin_unchecked():{twin:?} != twin():{:?}",
self.twin(c)
);
}
let ctt = self.data.twin_unchecked(twin);
if ctt != c {
return Err(format!("c.twin().twin()={ctt:?} != c={c:?}").to_string());
}
}
}
{
let css = self.swing_cw(self.swing_ccw(c));
if css != c {
return Err(format!("c.swing_ccw().swing_cw()={css:?} != c={c:?}").to_string());
}
}
}
if num_corners == 0 {
return Err("Empty corner table".to_string());
}
if !num_corners.is_multiple_of(3) {
return Err(format!("Corner count {num_corners} is not divisible by 3",));
}
if self.opposite_corners().len() as u32 != num_corners {
return Err(format!(
"Opposite corner array size {} doesn't match vertex_of_corner size {num_corners}",
self.opposite_corners().len(),
));
}
for (corner, &vertex) in self.vertex_of_corners().iter().enumerate() {
if vertex.0 >= num_vertices && vertex.is_valid() {
return Err(format!(
"Corner {corner} references invalid vertex {} (max: {})",
vertex.0,
num_vertices - 1
));
}
}
if self.corner_of_vertices().len() as u32 != num_vertices {
return Err(format!(
"corner_of_vertex size {} doesn't match vertex count {num_vertices}",
self.corner_of_vertices().len(),
));
}
for (vertex, &corner) in self.corner_of_vertices().iter().enumerate() {
let vertex = VertexIndex(vertex as u32);
if corner.is_valid() {
if corner.0 >= num_corners {
return Err(format!(
"Vertex {vertex:?} references invalid corner {corner:?} (max: {})",
num_corners - 1
));
}
if self.vertex(corner) != vertex {
return Err(format!(
"Vertex {vertex:?} references corner {} but that corner points to vertex {:?}",
self.dbg_corner(corner),
self.vertex(corner)
));
}
}
}
for (corner, &opposite) in self.opposite_corners().iter().enumerate() {
if self.is_triangle_deleted(CornerIndex(corner as u32).triangle()) {
continue;
}
if !self.vertex_of_corners()[corner].is_valid() {
continue;
}
let corner = CornerIndex(corner as u32);
if !opposite.is_valid() {
return Err(format!(
"Corner {corner:?} has invalid opposite corner (max: {})",
num_corners - 1
));
}
if opposite.is_valid() {
if opposite.0 >= num_corners {
return Err(format!(
"Corner {corner:?} has invalid opposite corner {opposite:?} (max: {})",
num_corners - 1
));
}
if self.opposite_corners()[opposite.0 as usize] != corner {
return Err(format!(
"Corner {} points to opposite {}, but {} points to {} (not symmetric)",
self.data.dbg_corner(corner),
self.data.dbg_corner(opposite),
self.data.dbg_corner(opposite),
self.data
.dbg_corner(self.opposite_corners()[opposite.0 as usize]),
));
}
let next_corner = self.next(corner);
let edge_v1 = self.vertex(next_corner);
let prev_corner = self.prev(corner);
let edge_v2 = self.vertex(prev_corner);
let opp_next = self.next(opposite);
let opp_v1 = self.vertex(opp_next);
let opp_prev = self.prev(opposite);
let opp_v2 = self.vertex(opp_prev);
if !(edge_v1 == opp_v2 && edge_v2 == opp_v1) {
return Err(format!(
"Corner {corner:?} (edge {edge_v1:?}->{edge_v2:?}) and opposite {opposite:?} (edge {opp_v1:?}->{opp_v2:?}) don't form a valid shared edge",
));
}
let corner_next = self.next(corner);
let corner_prev = self.prev(corner);
let opposite_next = self.next(opposite);
let opposite_prev = self.prev(opposite);
if self.vertex(corner_next) != self.vertex(opposite_prev) {
return Err(format!(
"Corner {corner:?}.next().vertex() ({:?}) != opposite {opposite:?}.prev().vertex() ({:?})",
self.vertex(corner_next),
self.vertex(opposite_prev)
));
}
if self.vertex(opposite_next) != self.vertex(corner_prev) {
return Err(format!(
"Corner {opposite:?}.next().vertex() ({:?}) != corner {corner:?}.prev().vertex() ({:?})",
self.vertex(opposite_next),
self.vertex(corner_prev)
));
}
}
}
for triangle in 0..num_triangles {
let c0 = CornerIndex(triangle * 3);
let c1 = CornerIndex(triangle * 3 + 1);
let c2 = CornerIndex(triangle * 3 + 2);
if !self.vertex_of_corners()[c0.0 as usize].is_valid() {
continue;
}
let v0 = self.vertex(c0);
let v1 = self.vertex(c1);
let v2 = self.vertex(c2);
if v0 == v1 || v1 == v2 || v2 == v0 {
return Err(format!(
"Triangle {triangle} is degenerate: vertices ({v0:?}, {v1:?}, {v2:?})",
));
}
}
let mut vertex_referenced = vec![false; num_vertices as usize];
for &vertex in self.vertex_of_corners().iter() {
if vertex.is_valid() {
vertex_referenced[vertex.0 as usize] = true;
}
}
for (vertex, &corner) in self.corner_of_vertices().iter().enumerate() {
if corner.is_valid() && !vertex_referenced[vertex] {
return Err(format!(
"Vertex {vertex} has corner_of_vertex entry but is not referenced by any corner",
));
}
}
for (vertex, &referenced) in vertex_referenced.iter().enumerate() {
if referenced && !self.corner_of_vertices()[vertex].is_valid() {
return Err(format!(
"Vertex {vertex} is referenced by some corner(s) but has INVALID_INDEX in corner_of_vertex",
));
}
}
Ok(())
}
pub fn check_basic_integrity(&self, num_vertices: u32) -> Result<(), String> {
let num_corners = self.vertex_of_corners().len() as u32;
if !num_corners.is_multiple_of(3) {
return Err("Corner count not divisible by 3".to_string());
}
if self.opposite_corners().len() as u32 != num_corners {
return Err("Array size mismatch".to_string());
}
let num_triangles = num_corners / 3;
for triangle in 0..num_triangles {
let c0 = CornerIndex(triangle * 3);
let c1 = CornerIndex(triangle * 3 + 1);
let c2 = CornerIndex(triangle * 3 + 2);
if c2.0 >= num_corners {
continue;
}
let v0 = self.vertex(c0);
let v1 = self.vertex(c1);
let v2 = self.vertex(c2);
if v0.0 >= num_vertices || v1.0 >= num_vertices || v2.0 >= num_vertices {
return Err(format!("Triangle {triangle} has invalid vertex indices"));
}
if v0 == v1 || v1 == v2 || v2 == v0 {
return Err(format!(
"Triangle {triangle} is degenerate: ({v0:?}, {v1:?}, {v2:?})",
));
}
}
Ok(())
}
#[inline(always)]
pub(crate) fn len(&self) -> usize {
integrity_assert_eq!(
self.data.vertex_of_corner_vec.len(),
self.data.opposite_corner_vec.len()
);
integrity_assert_eq!(
self.data.vertex_of_corner_vec.len() as u32 / 3,
self.triangle_pool.total_count()
);
self.data.vertex_of_corner_vec.len()
}
pub fn validate_partial(&self) -> Result<(), String> {
for corner in (0..self.vertex_of_corners().len() as u32).map(CornerIndex) {
if let Some(opp) = self.try_opposite(corner) {
if let Some(opp_opp) = self.try_opposite(opp) {
if opp_opp != corner {
return Err(format!(
"Opposite symmetry broken: opposite(opposite(C{})) = C{}, expected C{}",
self.dbg_corner(corner),
self.dbg_corner(opp_opp),
corner.0
));
}
} else {
return Err(format!(
"Opposite asymmetry: C{} points to C{}, but C{} has no opposite",
self.dbg_corner(corner),
self.dbg_corner(opp),
opp.0
));
}
let c_next_v = self.vertex(self.next(corner));
let c_prev_v = self.vertex(self.prev(corner));
let opp_next_v = self.vertex(self.next(opp));
let opp_prev_v = self.vertex(self.prev(opp));
if c_next_v != opp_prev_v || c_prev_v != opp_next_v {
return Err(format!(
"Edge mismatch: {} edge ({:?}→{:?}) vs {} edge ({:?}→{:?})",
self.dbg_corner(corner),
c_next_v,
c_prev_v,
self.dbg_corner(opp),
opp_next_v,
opp_prev_v
));
}
}
}
for c in (0..self.vertex_of_corners().len() as u32).map(CornerIndex) {
let v_at_c = self.vertex(c);
if let Some(swung) = self.try_swing_ccw(c) {
let v_at_swung = self.vertex(swung);
if v_at_c != v_at_swung {
return Err(format!(
"Swing broken: C{} at V{} swung to C{} at V{}",
c.0, v_at_c.0, swung.0, v_at_swung.0
));
}
}
}
Ok(())
}
}
impl<const ENABLE_UNSAFE: bool> CornerTableData<ENABLE_UNSAFE> {
pub(crate) fn twin_unchecked(&self, corner: CornerIndex) -> CornerIndex {
self.opposite(corner.prev()).next()
}
}