use crate::linalg::{Vec2, Vec3, dot, dot2, length2, length2_2};
use crate::types::{next_halfedge, Halfedge, TriRef};
use crate::impl_mesh::ManifoldImpl;
use crate::face_op::{get_axis_aligned_projection, calculate_vert_normals};
use crate::polygon::ccw;
fn tri_of(edge: usize) -> [usize; 3] {
let e1 = next_halfedge(edge as i32) as usize;
let e2 = next_halfedge(e1 as i32) as usize;
[edge, e1, e2]
}
fn is_01_longest(v0: Vec2, v1: Vec2, v2: Vec2) -> bool {
let e = [v1 - v0, v2 - v1, v0 - v2];
let l = [dot2(e[0], e[0]), dot2(e[1], e[1]), dot2(e[2], e[2])];
l[0] > l[1] && l[0] > l[2]
}
pub fn pair_up(halfedge: &mut Vec<Halfedge>, e0: usize, e1: usize) {
halfedge[e0].paired_halfedge = e1 as i32;
halfedge[e1].paired_halfedge = e0 as i32;
}
pub fn update_vert(mesh: &mut ManifoldImpl, vert: i32, start_edge: usize, end_edge: usize) {
let mut current = start_edge;
while current != end_edge {
mesh.halfedge[current].end_vert = vert;
let next = next_halfedge(current as i32) as usize;
mesh.halfedge[next].start_vert = vert;
current = mesh.halfedge[next].paired_halfedge as usize;
debug_assert_ne!(current, start_edge, "infinite loop in update_vert!");
}
}
pub fn form_loop(mesh: &mut ManifoldImpl, current: usize, end: usize) {
let start_vert = mesh.vert_pos.len() as i32;
mesh.vert_pos.push(mesh.vert_pos[mesh.halfedge[current].start_vert as usize]);
let end_vert = mesh.vert_pos.len() as i32;
mesh.vert_pos.push(mesh.vert_pos[mesh.halfedge[current].end_vert as usize]);
let old_match = mesh.halfedge[current].paired_halfedge as usize;
let new_match = mesh.halfedge[end].paired_halfedge as usize;
update_vert(mesh, start_vert, old_match, new_match);
update_vert(mesh, end_vert, end, current);
mesh.halfedge[current].paired_halfedge = new_match as i32;
mesh.halfedge[new_match].paired_halfedge = current as i32;
mesh.halfedge[end].paired_halfedge = old_match as i32;
mesh.halfedge[old_match].paired_halfedge = end as i32;
remove_if_folded(mesh, end);
}
pub fn collapse_tri(halfedge: &mut Vec<Halfedge>, tri_edge: [usize; 3]) {
if halfedge[tri_edge[1]].paired_halfedge == -1 {
return;
}
let pair1 = halfedge[tri_edge[1]].paired_halfedge as usize;
let pair2 = halfedge[tri_edge[2]].paired_halfedge as usize;
halfedge[pair1].paired_halfedge = pair2 as i32;
halfedge[pair2].paired_halfedge = pair1 as i32;
for i in 0..3 {
let prop_vert = halfedge[tri_edge[i]].prop_vert;
halfedge[tri_edge[i]] = Halfedge { start_vert: -1, end_vert: -1, paired_halfedge: -1, prop_vert };
}
}
pub fn remove_if_folded(mesh: &mut ManifoldImpl, edge: usize) {
let tri0edge = tri_of(edge);
let pair0 = mesh.halfedge[edge].paired_halfedge;
if pair0 < 0 {
return;
}
let tri1edge = tri_of(pair0 as usize);
if mesh.halfedge[tri0edge[1]].paired_halfedge == -1 {
return;
}
if mesh.halfedge[tri0edge[1]].end_vert == mesh.halfedge[tri1edge[1]].end_vert {
if mesh.halfedge[tri0edge[1]].paired_halfedge == tri1edge[2] as i32 {
if mesh.halfedge[tri0edge[2]].paired_halfedge == tri1edge[1] as i32 {
for i in 0..3 {
let sv = mesh.halfedge[tri0edge[i]].start_vert;
if sv >= 0 {
mesh.vert_pos[sv as usize] = Vec3::new(f64::NAN, f64::NAN, f64::NAN);
}
}
} else {
let sv = mesh.halfedge[tri0edge[1]].start_vert;
if sv >= 0 {
mesh.vert_pos[sv as usize] = Vec3::new(f64::NAN, f64::NAN, f64::NAN);
}
}
} else if mesh.halfedge[tri0edge[2]].paired_halfedge == tri1edge[1] as i32 {
let sv = mesh.halfedge[tri1edge[1]].start_vert;
if sv >= 0 {
mesh.vert_pos[sv as usize] = Vec3::new(f64::NAN, f64::NAN, f64::NAN);
}
} else {
return;
}
let p01 = mesh.halfedge[tri0edge[1]].paired_halfedge as usize;
let p02 = mesh.halfedge[tri0edge[2]].paired_halfedge as usize;
let p11 = mesh.halfedge[tri1edge[1]].paired_halfedge as usize;
let p12 = mesh.halfedge[tri1edge[2]].paired_halfedge as usize;
pair_up(&mut mesh.halfedge, p01, p12);
pair_up(&mut mesh.halfedge, p02, p11);
for i in 0..3 {
mesh.halfedge[tri0edge[i]] = Halfedge { start_vert: -1, end_vert: -1, paired_halfedge: -1, prop_vert: -1 };
mesh.halfedge[tri1edge[i]] = Halfedge { start_vert: -1, end_vert: -1, paired_halfedge: -1, prop_vert: -1 };
}
}
}
pub fn collapse_edge(mesh: &mut ManifoldImpl, edge: usize, scratch: &mut Vec<usize>, tol: f64, first_new_vert: i32) -> bool {
let tol = if tol < 0.0 { mesh.epsilon } else { tol };
let to_remove = mesh.halfedge[edge];
if to_remove.paired_halfedge < 0 {
return false;
}
let end_vert = to_remove.end_vert as usize;
let tri0edge = tri_of(edge);
let paired = to_remove.paired_halfedge as usize;
let tri1edge = tri_of(paired);
let p_new = mesh.vert_pos[end_vert];
let p_old = mesh.vert_pos[to_remove.start_vert as usize];
let delta = p_new - p_old;
let max_len = if (end_vert as i32) < first_new_vert {
tol * tol
} else {
mesh.epsilon * mesh.epsilon
};
let short_edge = dot(delta, delta) < max_len;
let start_orb = mesh.halfedge[tri1edge[1]].paired_halfedge;
if start_orb < 0 {
return false;
}
let mut start = start_orb as usize;
let mut current = tri1edge[2];
if !short_edge {
current = start;
let mut ref_check = mesh.mesh_relation.tri_ref[paired / 3];
let mut p_last = mesh.vert_pos[mesh.halfedge[tri1edge[1]].end_vert as usize];
while current != tri1edge[0] {
current = next_halfedge(current as i32) as usize;
let p_next = mesh.vert_pos[mesh.halfedge[current].end_vert as usize];
let tri = current / 3;
let ref_tri = mesh.mesh_relation.tri_ref[tri];
let projection = get_axis_aligned_projection(mesh.face_normal[tri]);
if !ref_tri.same_face(&ref_check) {
let old_ref = ref_check;
ref_check = mesh.mesh_relation.tri_ref[edge / 3];
if !ref_tri.same_face(&ref_check) {
return false;
}
if ref_tri.mesh_id != old_ref.mesh_id
|| ref_tri.face_id != old_ref.face_id
|| dot(mesh.face_normal[paired / 3], mesh.face_normal[tri]) < -0.5
{
let p_p_last = projection.apply(p_last);
let p_p_old = projection.apply(p_old);
let p_p_new = projection.apply(p_new);
if ccw(p_p_last, p_p_old, p_p_new, tol) != 0 {
return false;
}
}
}
if ccw(projection.apply(p_next), projection.apply(p_last),
projection.apply(p_new), mesh.epsilon) < 0 {
return false;
}
p_last = p_next;
let pair = mesh.halfedge[current].paired_halfedge;
if pair < 0 {
break;
}
current = pair as usize;
}
}
{
let mut cur = mesh.halfedge[tri0edge[1]].paired_halfedge as usize;
while cur != tri1edge[2] {
cur = next_halfedge(cur as i32) as usize;
scratch.push(cur);
let pair = mesh.halfedge[cur].paired_halfedge;
if pair < 0 {
break;
}
cur = pair as usize;
}
}
mesh.vert_pos[to_remove.start_vert as usize] = Vec3::new(f64::NAN, f64::NAN, f64::NAN);
collapse_tri(&mut mesh.halfedge, tri1edge);
current = start;
while current != tri0edge[2] {
current = next_halfedge(current as i32) as usize;
if mesh.num_prop > 0 && !mesh.mesh_relation.tri_ref.is_empty() {
let tri = current / 3;
if tri < mesh.mesh_relation.tri_ref.len() {
let ref_tri = mesh.mesh_relation.tri_ref[tri];
if ref_tri.same_face(&mesh.mesh_relation.tri_ref[edge / 3]) {
let prop_v = mesh.halfedge[next_halfedge(edge as i32) as usize].prop_vert;
mesh.halfedge[current].prop_vert = prop_v;
} else if ref_tri.same_face(&mesh.mesh_relation.tri_ref[paired / 3]) {
let prop_v = mesh.halfedge[paired].prop_vert;
mesh.halfedge[current].prop_vert = prop_v;
}
}
}
let vert = mesh.halfedge[current].end_vert;
let next_pair = mesh.halfedge[current].paired_halfedge;
let next_edge = if next_pair >= 0 { next_pair as usize } else { break };
let mut formed_loop = false;
for k in 0..scratch.len() {
if vert == mesh.halfedge[scratch[k]].end_vert {
form_loop(mesh, scratch[k], current);
start = next_edge;
scratch.truncate(k);
current = next_edge;
formed_loop = true;
break;
}
}
if formed_loop {
continue;
}
current = next_edge;
}
update_vert(mesh, end_vert as i32, start, tri0edge[2]);
collapse_tri(&mut mesh.halfedge, tri0edge);
remove_if_folded(mesh, start);
true
}
pub fn recursive_edge_swap(
mesh: &mut ManifoldImpl,
edge: i32,
tag: &mut i32,
visited: &mut Vec<i32>,
edge_swap_stack: &mut Vec<i32>,
scratch: &mut Vec<usize>,
) {
if edge < 0 {
return;
}
let edge = edge as usize;
if mesh.halfedge[edge].paired_halfedge < 0 {
return;
}
let pair = mesh.halfedge[edge].paired_halfedge as usize;
if visited.get(edge) == Some(&*tag) && visited.get(pair) == Some(&*tag) {
return;
}
let tri0edge = tri_of(edge);
let tri1edge = tri_of(pair);
if edge / 3 >= mesh.face_normal.len() || pair / 3 >= mesh.face_normal.len() {
return;
}
let proj0 = get_axis_aligned_projection(mesh.face_normal[edge / 3]);
let mut v = [Vec2::new(0.0, 0.0); 4];
for i in 0..3 {
let sv = mesh.halfedge[tri0edge[i]].start_vert as usize;
if sv < mesh.vert_pos.len() {
v[i] = proj0.apply(mesh.vert_pos[sv]);
}
}
if ccw(v[0], v[1], v[2], mesh.tolerance) > 0 || !is_01_longest(v[0], v[1], v[2]) {
return;
}
let proj1 = get_axis_aligned_projection(mesh.face_normal[pair / 3]);
for i in 0..3 {
let sv = mesh.halfedge[tri0edge[i]].start_vert as usize;
if sv < mesh.vert_pos.len() {
v[i] = proj1.apply(mesh.vert_pos[sv]);
}
}
let sv3 = mesh.halfedge[tri1edge[2]].start_vert as usize;
if sv3 < mesh.vert_pos.len() {
v[3] = proj1.apply(mesh.vert_pos[sv3]);
}
let do_swap = |mesh: &mut ManifoldImpl| {
let v0 = mesh.halfedge[tri0edge[2]].start_vert;
let v1 = mesh.halfedge[tri1edge[2]].start_vert;
mesh.halfedge[tri0edge[0]].start_vert = v1;
mesh.halfedge[tri0edge[2]].end_vert = v1;
mesh.halfedge[tri1edge[0]].start_vert = v0;
mesh.halfedge[tri1edge[2]].end_vert = v0;
let tri1e2_paired = mesh.halfedge[tri1edge[2]].paired_halfedge as usize;
let tri0e2_paired = mesh.halfedge[tri0edge[2]].paired_halfedge as usize;
pair_up(&mut mesh.halfedge, tri0edge[0], tri1e2_paired);
pair_up(&mut mesh.halfedge, tri1edge[0], tri0e2_paired);
pair_up(&mut mesh.halfedge, tri0edge[2], tri1edge[2]);
let tri0 = tri0edge[0] / 3;
let tri1 = tri1edge[0] / 3;
if tri1 < mesh.face_normal.len() && tri0 < mesh.face_normal.len() {
mesh.face_normal[tri0] = mesh.face_normal[tri1];
}
if tri1 < mesh.mesh_relation.tri_ref.len() && tri0 < mesh.mesh_relation.tri_ref.len() {
mesh.mesh_relation.tri_ref[tri0] = mesh.mesh_relation.tri_ref[tri1];
}
if !mesh.properties.is_empty() {
let num_prop = mesh.num_prop;
let l01 = length2_2(v[1] - v[0]).sqrt();
let l02 = length2_2(v[2] - v[0]).sqrt();
let a = (l02 / l01).clamp(0.0, 1.0);
mesh.halfedge[tri0edge[1]].prop_vert = mesh.halfedge[tri1edge[0]].prop_vert;
mesh.halfedge[tri0edge[0]].prop_vert = mesh.halfedge[tri1edge[2]].prop_vert;
mesh.halfedge[tri0edge[2]].prop_vert = mesh.halfedge[tri1edge[2]].prop_vert;
let new_prop = (mesh.properties.len() / num_prop) as i32;
let idx0 = mesh.halfedge[tri1edge[0]].prop_vert as usize;
let idx1 = mesh.halfedge[tri1edge[1]].prop_vert as usize;
for p in 0..num_prop {
let val = a * mesh.properties[num_prop * idx0 + p]
+ (1.0 - a) * mesh.properties[num_prop * idx1 + p];
mesh.properties.push(val);
}
mesh.halfedge[tri1edge[0]].prop_vert = new_prop;
mesh.halfedge[tri0edge[2]].prop_vert = new_prop;
}
let end_vert = mesh.halfedge[tri1edge[1]].end_vert;
let mut current = mesh.halfedge[tri1edge[0]].paired_halfedge;
while current != tri0edge[1] as i32 {
current = next_halfedge(current);
if mesh.halfedge[current as usize].end_vert == end_vert {
form_loop(mesh, tri0edge[2], current as usize);
remove_if_folded(mesh, tri0edge[2]);
return;
}
current = mesh.halfedge[current as usize].paired_halfedge;
}
};
if ccw(v[1], v[0], v[3], mesh.tolerance) <= 0 {
if !is_01_longest(v[1], v[0], v[3]) {
return;
}
do_swap(mesh);
let e23 = v[3] - v[2];
if dot2(e23, e23) < mesh.tolerance * mesh.tolerance {
*tag += 1;
let _ = collapse_edge(mesh, tri0edge[2], scratch, -1.0, 0);
scratch.clear();
} else {
if edge < visited.len() { visited[edge] = *tag; }
if pair < visited.len() { visited[pair] = *tag; }
for &e in &[tri1edge[1], tri1edge[0], tri0edge[1], tri0edge[0]] {
edge_swap_stack.push(e as i32);
}
}
} else if ccw(v[0], v[3], v[2], mesh.tolerance) <= 0
|| ccw(v[1], v[2], v[3], mesh.tolerance) <= 0
{
return;
} else {
do_swap(mesh);
if edge < visited.len() { visited[edge] = *tag; }
if pair < visited.len() { visited[pair] = *tag; }
let p1 = mesh.halfedge[tri1edge[0]].paired_halfedge;
let p2 = mesh.halfedge[tri0edge[1]].paired_halfedge;
edge_swap_stack.push(p1);
edge_swap_stack.push(p2);
}
}
pub fn split_pinched_verts(mesh: &mut ManifoldImpl) {
let n_edges = mesh.halfedge.len();
let mut vert_processed = vec![false; mesh.vert_pos.len()];
let mut halfedge_processed = vec![false; n_edges];
let mut i = 0;
while i < n_edges {
if halfedge_processed[i] {
i += 1;
continue;
}
let vert = mesh.halfedge[i].start_vert;
if vert < 0 {
i += 1;
continue;
}
let vert = vert as usize;
if vert_processed.get(vert) == Some(&true) {
let new_pos = mesh.vert_pos[vert];
mesh.vert_pos.push(new_pos);
let new_vert = (mesh.vert_pos.len() - 1) as i32;
let mut current = i;
loop {
let paired = mesh.halfedge[current].paired_halfedge;
if paired < 0 { break; }
current = next_halfedge(paired) as usize;
if current >= halfedge_processed.len() { break; }
halfedge_processed[current] = true;
mesh.halfedge[current].start_vert = new_vert;
let curr_paired = mesh.halfedge[current].paired_halfedge;
if curr_paired >= 0 {
mesh.halfedge[curr_paired as usize].end_vert = new_vert;
}
if current == i { break; }
}
} else {
if vert < vert_processed.len() {
vert_processed[vert] = true;
}
let mut current = i;
loop {
let paired = mesh.halfedge[current].paired_halfedge;
if paired < 0 { break; }
current = next_halfedge(paired) as usize;
if current >= halfedge_processed.len() { break; }
halfedge_processed[current] = true;
if current == i { break; }
}
}
i += 1;
}
}
pub fn dedupe_edge(mesh: &mut ManifoldImpl, edge: usize) {
let start_vert = mesh.halfedge[edge].start_vert;
let end_vert = mesh.halfedge[edge].end_vert;
let end_prop = mesh.halfedge[next_halfedge(edge as i32) as usize].prop_vert;
let paired = mesh.halfedge[edge].paired_halfedge;
if paired < 0 {
return;
}
let next_paired = mesh.halfedge[next_halfedge(edge as i32) as usize].paired_halfedge;
if next_paired < 0 {
return;
}
let mut current = next_paired as usize;
while current != edge {
let vert = mesh.halfedge[current].start_vert;
if vert == start_vert {
let new_vert = mesh.vert_pos.len() as i32;
mesh.vert_pos.push(mesh.vert_pos[end_vert as usize]);
let next_p = mesh.halfedge[next_halfedge(current as i32) as usize].paired_halfedge;
if next_p < 0 { break; }
current = next_p as usize;
let opp_p = mesh.halfedge[next_halfedge(edge as i32) as usize].paired_halfedge;
if opp_p < 0 { break; }
let opposite = opp_p as usize;
update_vert(mesh, new_vert, current, opposite);
let new_he = mesh.halfedge.len();
let old_face = current / 3;
let outside_vert = mesh.halfedge[current].start_vert;
mesh.halfedge.push(Halfedge { start_vert: end_vert, end_vert: new_vert, paired_halfedge: -1, prop_vert: end_prop });
mesh.halfedge.push(Halfedge { start_vert: new_vert, end_vert: outside_vert, paired_halfedge: -1, prop_vert: end_prop });
let curr_prop_vert = mesh.halfedge[current].prop_vert;
let curr_paired = mesh.halfedge[current].paired_halfedge as usize;
mesh.halfedge.push(Halfedge { start_vert: outside_vert, end_vert: end_vert, paired_halfedge: -1, prop_vert: curr_prop_vert });
pair_up(&mut mesh.halfedge, new_he + 2, curr_paired);
pair_up(&mut mesh.halfedge, new_he + 1, current);
if !mesh.mesh_relation.tri_ref.is_empty() {
let tri_ref_copy = mesh.mesh_relation.tri_ref[old_face];
mesh.mesh_relation.tri_ref.push(tri_ref_copy);
}
if !mesh.face_normal.is_empty() {
let fn_copy = mesh.face_normal[old_face];
mesh.face_normal.push(fn_copy);
}
let new_he2 = new_he + 3;
let old_face2 = opposite / 3;
let outside_vert2 = mesh.halfedge[opposite].start_vert;
mesh.halfedge.push(Halfedge { start_vert: new_vert, end_vert: end_vert, paired_halfedge: -1, prop_vert: end_prop });
mesh.halfedge.push(Halfedge { start_vert: end_vert, end_vert: outside_vert2, paired_halfedge: -1, prop_vert: end_prop });
let opp_prop_vert = mesh.halfedge[opposite].prop_vert;
let opp_paired = mesh.halfedge[opposite].paired_halfedge as usize;
mesh.halfedge.push(Halfedge { start_vert: outside_vert2, end_vert: new_vert, paired_halfedge: -1, prop_vert: opp_prop_vert });
pair_up(&mut mesh.halfedge, new_he2 + 2, opp_paired);
pair_up(&mut mesh.halfedge, new_he2 + 1, opposite);
pair_up(&mut mesh.halfedge, new_he2, new_he);
if !mesh.mesh_relation.tri_ref.is_empty() {
let tri_ref_copy = mesh.mesh_relation.tri_ref[old_face2];
mesh.mesh_relation.tri_ref.push(tri_ref_copy);
}
if !mesh.face_normal.is_empty() {
let fn_copy = mesh.face_normal[old_face2];
mesh.face_normal.push(fn_copy);
}
break;
}
let orbit_p = mesh.halfedge[next_halfedge(current as i32) as usize].paired_halfedge;
if orbit_p < 0 { break; }
current = orbit_p as usize;
}
if current == edge {
let new_vert = mesh.vert_pos.len() as i32;
mesh.vert_pos.push(mesh.vert_pos[end_vert as usize]);
let start = next_halfedge(current as i32) as usize;
let mut cur = start;
loop {
mesh.halfedge[cur].start_vert = new_vert;
let paired_cur = mesh.halfedge[cur].paired_halfedge;
if paired_cur < 0 { break; }
mesh.halfedge[paired_cur as usize].end_vert = new_vert;
cur = next_halfedge(paired_cur) as usize;
if cur == start { break; }
}
}
let pair = mesh.halfedge[edge].paired_halfedge;
if pair < 0 { return; }
let pair = pair as usize;
let mut cur = mesh.halfedge[next_halfedge(pair as i32) as usize].paired_halfedge;
if cur < 0 { return; }
let mut cur = cur as usize;
while cur != pair {
let v = mesh.halfedge[cur].start_vert;
if v == end_vert {
return; }
let p = mesh.halfedge[next_halfedge(cur as i32) as usize].paired_halfedge;
if p < 0 { break; }
cur = p as usize;
}
if cur == pair {
let new_vert2 = mesh.vert_pos.len() as i32;
mesh.vert_pos.push(mesh.vert_pos[end_vert as usize]);
let s2 = next_halfedge(cur as i32) as usize;
let mut c2 = s2;
loop {
mesh.halfedge[c2].start_vert = new_vert2;
let paired_c2 = mesh.halfedge[c2].paired_halfedge;
if paired_c2 < 0 { break; }
mesh.halfedge[paired_c2 as usize].end_vert = new_vert2;
c2 = next_halfedge(paired_c2) as usize;
if c2 == s2 { break; }
}
}
}
pub fn dedupe_edges(mesh: &mut ManifoldImpl) {
let max_iterations = mesh.halfedge.len(); for iteration in 0..max_iterations {
let n_edges = mesh.halfedge.len();
let mut processed = vec![false; n_edges];
let mut duplicates: Vec<usize> = Vec::new();
for i in 0..n_edges {
if processed[i] { continue; }
let sv = mesh.halfedge[i].start_vert;
let ev = mesh.halfedge[i].end_vert;
if sv < 0 || ev < 0 { continue; }
let mut end_verts: Vec<(i32, usize)> = Vec::new(); processed[i] = true;
let c_ev0 = mesh.halfedge[i].end_vert;
if c_ev0 >= 0 { end_verts.push((c_ev0, i)); }
let mut current = i;
let mut orbit_steps = 0;
loop {
let pair = mesh.halfedge[current].paired_halfedge;
if pair < 0 { break; }
current = next_halfedge(pair) as usize;
if current == i { break; }
orbit_steps += 1;
if orbit_steps > n_edges { break; } processed[current] = true;
let c_sv = mesh.halfedge[current].start_vert;
let c_ev = mesh.halfedge[current].end_vert;
if c_sv >= 0 && c_ev >= 0 {
if let Some(entry) = end_verts.iter_mut().find(|(v, _)| *v == c_ev) {
if current < entry.1 { entry.1 = current; }
} else {
end_verts.push((c_ev, current));
}
}
}
let c_ev0 = mesh.halfedge[i].end_vert;
if c_ev0 >= 0 {
if let Some(&(_, min_edge)) = end_verts.iter().find(|(v, _)| *v == c_ev0) {
if min_edge != i { duplicates.push(i); }
}
}
current = i;
orbit_steps = 0;
loop {
let pair = mesh.halfedge[current].paired_halfedge;
if pair < 0 { break; }
current = next_halfedge(pair) as usize;
if current == i { break; }
orbit_steps += 1;
if orbit_steps > n_edges { break; } let c_ev = mesh.halfedge[current].end_vert;
if c_ev >= 0 {
if let Some(&(_, min_edge)) = end_verts.iter().find(|(v, _)| *v == c_ev) {
if min_edge != current {
duplicates.push(current);
}
}
}
}
}
if duplicates.is_empty() { break; }
for &dup in &duplicates {
dedupe_edge(mesh, dup);
}
}
}
pub fn cleanup_topology(mesh: &mut ManifoldImpl) {
if mesh.halfedge.is_empty() { return; }
split_pinched_verts(mesh);
dedupe_edges(mesh);
}
pub fn simplify_topology(mesh: &mut ManifoldImpl, first_new_vert: i32) {
if mesh.halfedge.is_empty() { return; }
cleanup_topology(mesh);
collapse_short_edges(mesh, first_new_vert);
collapse_colinear_edges(mesh, first_new_vert);
swap_degenerates(mesh, first_new_vert);
calculate_vert_normals(mesh);
}
pub fn remove_degenerates(mesh: &mut ManifoldImpl, first_new_vert: i32) {
if mesh.halfedge.is_empty() { return; }
cleanup_topology(mesh);
collapse_short_edges(mesh, first_new_vert);
swap_degenerates(mesh, first_new_vert);
calculate_vert_normals(mesh);
}
pub fn collapse_short_edges(mesh: &mut ManifoldImpl, first_new_vert: i32) {
let mut scratch = Vec::with_capacity(10);
let n = mesh.halfedge.len();
let mut flagged: Vec<usize> = Vec::new();
let tol = if first_new_vert == 0 { mesh.epsilon } else { mesh.tolerance };
for i in 0..n {
let h = &mesh.halfedge[i];
if h.paired_halfedge < 0 { continue; }
if h.start_vert < first_new_vert && h.end_vert < first_new_vert { continue; }
if h.start_vert < 0 || h.end_vert < 0 { continue; }
let delta = mesh.vert_pos[h.end_vert as usize] - mesh.vert_pos[h.start_vert as usize];
let len_sq = dot(delta, delta);
let max_len = if h.end_vert < first_new_vert {
tol * tol
} else {
mesh.epsilon * mesh.epsilon
};
if len_sq < max_len {
flagged.push(i);
}
}
for &i in &flagged {
scratch.clear();
collapse_edge(mesh, i, &mut scratch, tol, first_new_vert);
}
}
pub fn collapse_colinear_edges(mesh: &mut ManifoldImpl, first_new_vert: i32) {
let mut scratch = Vec::with_capacity(10);
loop {
let n = mesh.halfedge.len();
let mut flagged: Vec<usize> = Vec::new();
for i in 0..n {
let h = &mesh.halfedge[i];
if h.paired_halfedge < 0 || h.start_vert < first_new_vert { continue; }
if h.start_vert < 0 { continue; }
if mesh.mesh_relation.tri_ref.is_empty() { continue; }
if i / 3 >= mesh.mesh_relation.tri_ref.len() { continue; }
let ref0 = mesh.mesh_relation.tri_ref[i / 3];
let mut current = next_halfedge(mesh.halfedge[i].paired_halfedge) as usize;
if current >= mesh.halfedge.len() { continue; }
let mut ref1 = if current / 3 < mesh.mesh_relation.tri_ref.len() {
mesh.mesh_relation.tri_ref[current / 3]
} else {
continue
};
let mut ref1_updated = !ref0.same_face(&ref1);
let mut is_redundant = true;
let max_orbit = mesh.halfedge.len(); let mut orbit_count = 0;
while current != i {
orbit_count += 1;
if orbit_count > max_orbit {
is_redundant = false;
break;
}
let pair = mesh.halfedge[current].paired_halfedge;
if pair < 0 { is_redundant = false; break; }
current = next_halfedge(pair) as usize;
if current >= mesh.halfedge.len() { is_redundant = false; break; }
let tri = current / 3;
if tri >= mesh.mesh_relation.tri_ref.len() { is_redundant = false; break; }
let ref_cur = mesh.mesh_relation.tri_ref[tri];
if !ref_cur.same_face(&ref0) && !ref_cur.same_face(&ref1) {
if !ref1_updated {
ref1 = ref_cur;
ref1_updated = true;
} else {
is_redundant = false;
break;
}
}
}
if is_redundant {
flagged.push(i);
}
}
if flagged.is_empty() { break; }
let mut num_collapsed = 0;
for &i in &flagged {
scratch.clear();
if collapse_edge(mesh, i, &mut scratch, -1.0, 0) {
num_collapsed += 1;
}
}
if num_collapsed == 0 { break; }
}
}
pub fn swap_degenerates(mesh: &mut ManifoldImpl, first_new_vert: i32) {
if mesh.face_normal.is_empty() { return; }
let n = mesh.halfedge.len();
let mut flagged: Vec<usize> = Vec::new();
for i in 0..n {
let h = &mesh.halfedge[i];
if h.paired_halfedge < 0 { continue; }
let tri0edge = tri_of(i);
let pair = h.paired_halfedge as usize;
let tri1edge = tri_of(pair);
if mesh.halfedge[i].start_vert < first_new_vert
&& mesh.halfedge[i].end_vert < first_new_vert
&& mesh.halfedge[next_halfedge(i as i32) as usize].end_vert < first_new_vert
&& mesh.halfedge[next_halfedge(pair as i32) as usize].end_vert < first_new_vert
{
continue;
}
let tri = i / 3;
if tri >= mesh.face_normal.len() { continue; }
let proj = get_axis_aligned_projection(mesh.face_normal[tri]);
let mut v = [Vec2::new(0.0, 0.0); 3];
for j in 0..3 {
let sv = mesh.halfedge[tri0edge[j]].start_vert;
if sv >= 0 && (sv as usize) < mesh.vert_pos.len() {
v[j] = proj.apply(mesh.vert_pos[sv as usize]);
}
}
if ccw(v[0], v[1], v[2], mesh.tolerance) > 0 || !is_01_longest(v[0], v[1], v[2]) {
continue;
}
let tri_p = pair / 3;
if tri_p >= mesh.face_normal.len() { continue; }
let proj_p = get_axis_aligned_projection(mesh.face_normal[tri_p]);
for j in 0..3 {
let sv = mesh.halfedge[tri1edge[j]].start_vert;
if sv >= 0 && (sv as usize) < mesh.vert_pos.len() {
v[j] = proj_p.apply(mesh.vert_pos[sv as usize]);
}
}
if ccw(v[0], v[1], v[2], mesh.tolerance) > 0 || is_01_longest(v[0], v[1], v[2]) {
flagged.push(i);
}
}
let mut visited = vec![-1i32; n];
let mut edge_swap_stack: Vec<i32> = Vec::new();
let mut scratch: Vec<usize> = Vec::new();
let mut tag = 0i32;
for &i in &flagged {
tag += 1;
recursive_edge_swap(mesh, i as i32, &mut tag, &mut visited, &mut edge_swap_stack, &mut scratch);
while let Some(e) = edge_swap_stack.pop() {
recursive_edge_swap(mesh, e, &mut tag, &mut visited, &mut edge_swap_stack, &mut scratch);
}
}
}
#[cfg(test)]
#[path = "edge_op_tests.rs"]
mod tests;