use cartan_dec::Mesh;
use cartan_manifolds::euclidean::Euclidean;
use nalgebra::Vector3;
use std::collections::HashMap;
use crate::error::RemeshError;
pub fn barycentric_refine_tets(
mesh: &mut Mesh<Euclidean<3>, 4, 3>,
flags: &[bool],
) -> Result<usize, RemeshError> {
let nt = mesh.n_simplices();
if flags.len() != nt {
return Err(RemeshError::InvalidInput(format!(
"flags length {} != n_simplices {nt}", flags.len())));
}
let mut new_simplices: Vec<[usize; 4]> = Vec::new();
let mut flagged_indices: Vec<usize> = Vec::new();
let mut refined = 0;
for (s, &flag) in flags.iter().enumerate() {
if !flag {
new_simplices.push(mesh.simplices[s]);
continue;
}
let tet = mesh.simplices[s];
let v: [Vector3<f64>; 4] = [
mesh.vertices[tet[0]], mesh.vertices[tet[1]],
mesh.vertices[tet[2]], mesh.vertices[tet[3]],
];
let bary = (v[0] + v[1] + v[2] + v[3]) / 4.0;
let v_b = mesh.vertices.len();
mesh.vertices.push(bary);
for omit in 0..4 {
let mut sub = [0usize; 4];
let mut j = 0;
for (i, &vert) in tet.iter().enumerate() {
if i == omit { continue; }
sub[j] = vert;
j += 1;
}
sub[3] = v_b;
new_simplices.push(sub);
}
flagged_indices.push(s);
refined += 1;
}
mesh.simplices = new_simplices;
mesh.rebuild_topology();
Ok(refined)
}
pub fn indicator_flags<F: Fn(Vector3<f64>) -> f64>(
mesh: &Mesh<Euclidean<3>, 4, 3>,
indicator_fn: F,
threshold: f64,
) -> Vec<bool> {
mesh.simplices.iter().map(|tet| {
let bary = (mesh.vertices[tet[0]] + mesh.vertices[tet[1]]
+ mesh.vertices[tet[2]] + mesh.vertices[tet[3]]) / 4.0;
indicator_fn(bary).abs() > threshold
}).collect()
}
pub fn red_refine_tets_uniform(
mesh: &mut Mesh<Euclidean<3>, 4, 3>,
) -> Result<(), RemeshError> {
let old_verts = mesh.vertices.clone();
let old_simplices = mesh.simplices.clone();
let mut verts = old_verts.clone();
let mut mid_cache: HashMap<(usize, usize), usize> = HashMap::new();
let mut midpoint = |a: usize, b: usize, verts: &mut Vec<Vector3<f64>>| -> usize {
let key = (a.min(b), a.max(b));
if let Some(&idx) = mid_cache.get(&key) { return idx; }
let m = (old_verts[a] + old_verts[b]) * 0.5;
let new_idx = verts.len();
verts.push(m);
mid_cache.insert(key, new_idx);
new_idx
};
let mut new_simplices = Vec::with_capacity(old_simplices.len() * 8);
for tet in &old_simplices {
let v = *tet;
let m01 = midpoint(v[0], v[1], &mut verts);
let m02 = midpoint(v[0], v[2], &mut verts);
let m03 = midpoint(v[0], v[3], &mut verts);
let m12 = midpoint(v[1], v[2], &mut verts);
let m13 = midpoint(v[1], v[3], &mut verts);
let m23 = midpoint(v[2], v[3], &mut verts);
new_simplices.push([v[0], m01, m02, m03]);
new_simplices.push([v[1], m01, m12, m13]);
new_simplices.push([v[2], m02, m12, m23]);
new_simplices.push([v[3], m03, m13, m23]);
new_simplices.push([m01, m02, m03, m23]);
new_simplices.push([m01, m02, m12, m23]);
new_simplices.push([m01, m03, m13, m23]);
new_simplices.push([m01, m12, m13, m23]);
}
mesh.vertices = verts;
mesh.simplices = new_simplices;
mesh.rebuild_topology();
Ok(())
}
pub fn refine_to_depth<F: Fn(Vector3<f64>) -> f64>(
mesh: &mut Mesh<Euclidean<3>, 4, 3>,
depth: usize,
indicator_fn: F,
threshold: f64,
) -> Result<usize, RemeshError> {
let mut total = 0;
for _ in 0..depth {
let flags = indicator_flags(mesh, &indicator_fn, threshold);
let n = barycentric_refine_tets(mesh, &flags)?;
total += n;
if n == 0 { break; }
}
Ok(total)
}
#[cfg(test)]
mod tests {
use super::*;
use cartan_manifolds::euclidean::Euclidean;
fn unit_tet() -> Mesh<Euclidean<3>, 4, 3> {
let vertices = vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0),
Vector3::new(0.0, 0.0, 1.0),
];
let simplices = vec![[0usize, 1, 2, 3]];
let manifold = Euclidean::<3>;
Mesh::<Euclidean<3>, 4, 3>::from_simplices_generic(&manifold, vertices, simplices)
}
#[test]
fn refine_one_tet_produces_four_subtets_and_one_new_vertex() {
let mut mesh = unit_tet();
assert_eq!(mesh.n_simplices(), 1);
assert_eq!(mesh.n_vertices(), 4);
let n = barycentric_refine_tets(&mut mesh, &[true]).unwrap();
assert_eq!(n, 1);
assert_eq!(mesh.n_simplices(), 4);
assert_eq!(mesh.n_vertices(), 5);
}
#[test]
fn zero_flags_leaves_mesh_unchanged() {
let mut mesh = unit_tet();
let orig_tets = mesh.n_simplices();
let orig_verts = mesh.n_vertices();
let n = barycentric_refine_tets(&mut mesh, &[false]).unwrap();
assert_eq!(n, 0);
assert_eq!(mesh.n_simplices(), orig_tets);
assert_eq!(mesh.n_vertices(), orig_verts);
}
#[test]
fn indicator_flags_match_barycentre_test() {
let mesh = unit_tet();
let flags = indicator_flags(&mesh, |p| p.x + p.y + p.z, 0.1);
assert_eq!(flags.len(), 1);
assert!(flags[0]);
}
#[test]
fn refine_to_depth_terminates_when_no_more_flags() {
let mut mesh = unit_tet();
let n = refine_to_depth(&mut mesh, 5, |_| 0.0, 1.0).unwrap();
assert_eq!(n, 0); assert_eq!(mesh.n_simplices(), 1);
}
#[test]
fn refine_to_depth_two_levels() {
let mut mesh = unit_tet();
let n = refine_to_depth(&mut mesh, 2, |_| 1.0, 0.5).unwrap();
assert_eq!(n, 1 + 4);
assert_eq!(mesh.n_simplices(), 16);
}
#[test]
fn red_refine_preserves_total_volume() {
let mut mesh = unit_tet();
let manifold = Euclidean::<3>;
let v_orig: f64 = (0..mesh.n_simplices())
.map(|s| mesh.simplex_volume(&manifold, s))
.sum();
red_refine_tets_uniform(&mut mesh).unwrap();
let v_new: f64 = (0..mesh.n_simplices())
.map(|s| mesh.simplex_volume(&manifold, s))
.sum();
assert_eq!(mesh.n_simplices(), 8);
assert!((v_new - v_orig).abs() / v_orig.max(1e-30) < 1e-10,
"total volume changed: {v_orig} -> {v_new}");
}
#[test]
fn red_refine_produces_10_vertices_from_one_tet() {
let mut mesh = unit_tet();
red_refine_tets_uniform(&mut mesh).unwrap();
assert_eq!(mesh.n_vertices(), 10);
}
}