use cartan_core::Manifold;
use cartan_dec::Mesh;
use cartan_manifolds::euclidean::Euclidean;
use crate::error::RemeshError;
use crate::log::{EdgeCollapse, EdgeFlip, EdgeSplit, RemeshLog, VertexShift};
pub fn split_edge<M: Manifold>(mesh: &mut Mesh<M, 3, 2>, manifold: &M, edge: usize) -> RemeshLog {
assert!(edge < mesh.n_boundaries(), "edge index out of bounds");
let [v_a, v_b] = mesh.boundaries[edge];
let midpoint = mesh.boundary_midpoint(manifold, edge);
let v_m = mesh.vertices.len();
mesh.vertices.push(midpoint);
let adjacent_faces: Vec<usize> = mesh.boundary_simplices[edge].clone();
let mut new_triangles: Vec<[usize; 3]> = Vec::new();
let mut faces_to_remove: Vec<usize> = Vec::new();
for &face_idx in &adjacent_faces {
let tri = mesh.simplices[face_idx];
let v_opp = tri
.iter()
.copied()
.find(|&v| v != v_a && v != v_b)
.expect("triangle must have an opposite vertex");
let pos_a = tri.iter().position(|&v| v == v_a).unwrap();
let pos_b = tri.iter().position(|&v| v == v_b).unwrap();
if (pos_a + 1) % 3 == pos_b {
new_triangles.push([v_a, v_m, v_opp]);
new_triangles.push([v_m, v_b, v_opp]);
} else {
new_triangles.push([v_b, v_m, v_opp]);
new_triangles.push([v_m, v_a, v_opp]);
}
faces_to_remove.push(face_idx);
}
faces_to_remove.sort_unstable();
for &fi in faces_to_remove.iter().rev() {
mesh.simplices.swap_remove(fi);
}
for tri in &new_triangles {
mesh.simplices.push(*tri);
}
mesh.rebuild_topology();
let new_edges: Vec<usize> = mesh.vertex_boundaries[v_m].clone();
let mut log = RemeshLog::new();
log.splits.push(EdgeSplit {
old_edge: edge,
v_a,
v_b,
new_vertex: v_m,
new_edges,
});
log
}
pub fn collapse_edge(
mesh: &mut Mesh<Euclidean<2>, 3, 2>,
_manifold: &Euclidean<2>,
edge: usize,
foldover_threshold: f64,
) -> Result<RemeshLog, RemeshError> {
assert!(edge < mesh.n_boundaries(), "edge index out of bounds");
let [v_a, v_b] = mesh.boundaries[edge];
let (survivor, removed) = if v_a < v_b { (v_a, v_b) } else { (v_b, v_a) };
let pa = &mesh.vertices[v_a];
let pb = &mesh.vertices[v_b];
let midpoint = (pa + pb) * 0.5;
let faces_with_both: Vec<usize> = mesh.boundary_simplices[edge].clone();
let mut faces_to_check: Vec<usize> = Vec::new();
for &f in mesh.vertex_simplices[removed]
.iter()
.chain(mesh.vertex_simplices[survivor].iter())
{
if !faces_with_both.contains(&f) && !faces_to_check.contains(&f) {
faces_to_check.push(f);
}
}
for &face_idx in &faces_to_check {
let tri = mesh.simplices[face_idx];
let area_before = signed_area_flat(mesh, &tri);
let mut tri_after = tri;
for v in tri_after.iter_mut() {
if *v == removed {
*v = survivor;
}
}
let old_pos = mesh.vertices[survivor];
mesh.vertices[survivor] = midpoint;
let area_after = signed_area_flat(mesh, &tri_after);
mesh.vertices[survivor] = old_pos;
if area_before.abs() > 1e-30 && area_after.abs() > 1e-30 {
let cos_angle: f64 = if area_before.signum() == area_after.signum() {
1.0
} else {
-1.0
};
let angle = cos_angle.acos();
if angle > foldover_threshold {
return Err(RemeshError::Foldover {
face: face_idx,
angle_rad: angle,
threshold: foldover_threshold,
});
}
}
}
mesh.vertices[survivor] = midpoint;
let removed_faces = faces_with_both.clone();
let mut to_remove_sorted = faces_with_both;
to_remove_sorted.sort_unstable();
for &fi in to_remove_sorted.iter().rev() {
mesh.simplices.swap_remove(fi);
}
for tri in mesh.simplices.iter_mut() {
for v in tri.iter_mut() {
if *v == removed {
*v = survivor;
}
}
}
let last_vertex = mesh.vertices.len() - 1;
mesh.vertices.swap_remove(removed);
if removed != last_vertex {
for tri in mesh.simplices.iter_mut() {
for v in tri.iter_mut() {
if *v == last_vertex {
*v = removed;
}
}
}
}
mesh.rebuild_topology();
let mut log = RemeshLog::new();
log.collapses.push(EdgeCollapse {
old_edge: edge,
surviving_vertex: survivor,
removed_vertex: removed,
removed_faces,
});
Ok(log)
}
fn signed_area_flat(mesh: &Mesh<Euclidean<2>, 3, 2>, tri: &[usize; 3]) -> f64 {
let [i, j, k] = *tri;
let a = &mesh.vertices[i];
let b = &mesh.vertices[j];
let c = &mesh.vertices[k];
0.5 * ((b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]))
}
pub fn flip_edge<M: Manifold>(
mesh: &mut Mesh<M, 3, 2>,
manifold: &M,
edge: usize,
) -> Result<RemeshLog, RemeshError> {
assert!(edge < mesh.n_boundaries(), "edge index out of bounds");
let adj = &mesh.boundary_simplices[edge];
let adj_count = adj.len();
if adj_count == 1 {
return Err(RemeshError::BoundaryEdge { edge });
}
if adj_count != 2 {
return Err(RemeshError::NotInteriorEdge {
edge,
count: adj_count,
});
}
let [v_a, v_b] = mesh.boundaries[edge];
let face_0 = adj[0];
let face_1 = adj[1];
let opp_0 = mesh.simplices[face_0]
.iter()
.copied()
.find(|&v| v != v_a && v != v_b)
.expect("triangle must have a vertex not on the edge");
let opp_1 = mesh.simplices[face_1]
.iter()
.copied()
.find(|&v| v != v_a && v != v_b)
.expect("triangle must have a vertex not on the edge");
let angle_0 = opposite_angle(manifold, &mesh.vertices, opp_0, v_a, v_b);
let angle_1 = opposite_angle(manifold, &mesh.vertices, opp_1, v_a, v_b);
let angle_sum = angle_0 + angle_1;
if angle_sum <= std::f64::consts::PI {
return Err(RemeshError::AlreadyDelaunay { edge, angle_sum });
}
let tri_0 = mesh.simplices[face_0];
let pos_a_in_0 = tri_0.iter().position(|&v| v == v_a).unwrap();
let next_in_0 = tri_0[(pos_a_in_0 + 1) % 3];
if next_in_0 == v_b {
mesh.simplices[face_0] = [opp_0, opp_1, v_a];
mesh.simplices[face_1] = [opp_1, opp_0, v_b];
} else {
mesh.simplices[face_0] = [opp_1, opp_0, v_a];
mesh.simplices[face_1] = [opp_0, opp_1, v_b];
}
mesh.rebuild_topology();
let mut log = RemeshLog::new();
log.flips.push(EdgeFlip {
old_edge: edge,
new_edge: [opp_0, opp_1],
affected_faces: [face_0, face_1],
});
Ok(log)
}
fn opposite_angle<M: Manifold>(
manifold: &M,
vertices: &[M::Point],
apex: usize,
p: usize,
q: usize,
) -> f64 {
let v_ap = manifold
.log(&vertices[apex], &vertices[p])
.expect("log map failed for angle computation");
let v_aq = manifold
.log(&vertices[apex], &vertices[q])
.expect("log map failed for angle computation");
let dot = manifold.inner(&vertices[apex], &v_ap, &v_aq);
let norm_ap = manifold.norm(&vertices[apex], &v_ap);
let norm_aq = manifold.norm(&vertices[apex], &v_aq);
let denom = norm_ap * norm_aq;
if denom < 1e-30 {
return 0.0;
}
let cos_val = (dot / denom).clamp(-1.0, 1.0);
cos_val.acos()
}
pub fn shift_vertex<M: Manifold>(
mesh: &mut Mesh<M, 3, 2>,
manifold: &M,
vertex: usize,
) -> RemeshLog {
assert!(vertex < mesh.n_vertices(), "vertex index out of bounds");
let mut neighbors: Vec<usize> = Vec::new();
for &b in &mesh.vertex_boundaries[vertex] {
let [e0, e1] = mesh.boundaries[b];
let other = if e0 == vertex { e1 } else { e0 };
if !neighbors.contains(&other) {
neighbors.push(other);
}
}
if neighbors.is_empty() {
let mut log = RemeshLog::new();
log.shifts.push(VertexShift {
vertex,
old_pos_tangent: Vec::new(),
});
return log;
}
let n = neighbors.len() as f64;
let base = mesh.vertices[vertex].clone();
let first_log = manifold
.log(&base, &mesh.vertices[neighbors[0]])
.expect("log map failed in shift_vertex");
let mut displacement = first_log;
for &nb in &neighbors[1..] {
let v_log = manifold
.log(&base, &mesh.vertices[nb])
.expect("log map failed in shift_vertex");
displacement = displacement + v_log;
}
displacement = displacement * (1.0 / n);
let new_pos = manifold.exp(&base, &displacement);
mesh.vertices[vertex] = new_pos;
let mut log = RemeshLog::new();
log.shifts.push(VertexShift {
vertex,
old_pos_tangent: Vec::new(),
});
log
}