use std::collections::{HashMap, HashSet};
use crate::ids::{FaceId, VertexId};
use crate::io::build_mesh_from_vertices_and_faces;
use crate::storage::MeshStorage;
use crate::traversal::{FaceHalfEdges, VertexRing, is_boundary_vertex};
pub fn sqrt3_subdivide(mesh: &MeshStorage) -> MeshStorage {
let orig_v_ids: Vec<VertexId> = mesh.vertex_ids().collect();
let n_orig = orig_v_ids.len();
if n_orig == 0 {
return MeshStorage::new();
}
let mut v_index: HashMap<VertexId, u32> = HashMap::new();
for (i, &v) in orig_v_ids.iter().enumerate() {
v_index.insert(v, i as u32);
}
let mut orig_faces: Vec<[u32; 3]> = Vec::new();
let mut face_id_to_idx: HashMap<FaceId, usize> = HashMap::new();
let mut skipped_non_triangle: u32 = 0;
for f_id in mesh.face_ids() {
let verts: Vec<u32> = FaceHalfEdges::new(mesh, f_id)
.filter_map(|he| mesh.get_halfedge(he))
.map(|h| h.vertex)
.filter_map(|v| v_index.get(&v).copied())
.collect();
if verts.len() == 3 {
face_id_to_idx.insert(f_id, orig_faces.len());
orig_faces.push([verts[0], verts[1], verts[2]]);
} else {
skipped_non_triangle += 1;
}
}
if skipped_non_triangle > 0 {
log::warn!(
"[halfedge::sqrt3_subdivide] 警告:输入网格含 {} 个非三角面,已跳过(√3 细分仅支持三角形)。\
若需处理任意多边形,请使用 catmull_clark_subdivide。",
skipped_non_triangle
);
}
let n_faces = orig_faces.len();
if n_faces == 0 {
let positions: Vec<[f64; 3]> = orig_v_ids
.iter()
.filter_map(|&v| mesh.get_vertex(v))
.map(|vt| vt.position)
.collect();
return build_mesh_from_vertices_and_faces(&positions, &[])
.expect("empty faces is always valid");
}
let face_points: Vec<[f64; 3]> = orig_faces
.iter()
.map(|face| {
let [a, b, c] = *face;
let pa = mesh
.get_vertex(orig_v_ids[a as usize])
.map(|v| v.position)
.unwrap_or([0.0; 3]);
let pb = mesh
.get_vertex(orig_v_ids[b as usize])
.map(|v| v.position)
.unwrap_or([0.0; 3]);
let pc = mesh
.get_vertex(orig_v_ids[c as usize])
.map(|v| v.position)
.unwrap_or([0.0; 3]);
[
(pa[0] + pb[0] + pc[0]) / 3.0,
(pa[1] + pb[1] + pc[1]) / 3.0,
(pa[2] + pb[2] + pc[2]) / 3.0,
]
})
.collect();
let face_point_start = n_orig;
let mut new_positions: Vec<[f64; 3]> = Vec::with_capacity(n_orig + n_faces);
for &v in &orig_v_ids {
new_positions.push(mesh.get_vertex(v).map(|vt| vt.position).unwrap_or([0.0; 3]));
}
for fp in &face_points {
new_positions.push(*fp);
}
for (i, &v) in orig_v_ids.iter().enumerate() {
let p_old = new_positions[i];
let new_pos = if is_boundary_vertex(mesh, v) {
p_old
} else {
let neighbors: Vec<[f64; 3]> = VertexRing::new(mesh, v)
.filter_map(|he| mesh.get_halfedge(he))
.map(|h| h.vertex)
.filter_map(|nb| mesh.get_vertex(nb))
.map(|vt| vt.position)
.collect();
let n = neighbors.len();
if n == 0 {
p_old
} else {
let alpha = sqrt3_alpha(n);
let n_f = n as f64;
let mut sum = [0.0; 3];
for nb in &neighbors {
sum[0] += nb[0];
sum[1] += nb[1];
sum[2] += nb[2];
}
[
(1.0 - alpha) * p_old[0] + (alpha / n_f) * sum[0],
(1.0 - alpha) * p_old[1] + (alpha / n_f) * sum[1],
(1.0 - alpha) * p_old[2] + (alpha / n_f) * sum[2],
]
}
};
new_positions[i] = new_pos;
}
let mut new_faces: Vec<[u32; 3]> = Vec::with_capacity(n_faces * 3);
let mut processed: HashSet<(u32, u32)> = HashSet::new();
for he_id in mesh.halfedge_ids() {
let h = match mesh.get_halfedge(he_id) {
Some(h) => h,
None => continue,
};
let v_tip = h.vertex;
let twin_id = match h.twin {
Some(t) => t,
None => continue, };
let twin = match mesh.get_halfedge(twin_id) {
Some(t) => t,
None => continue,
};
let v_origin = twin.vertex;
let i_o = match v_index.get(&v_origin) {
Some(&i) => i,
None => continue,
};
let i_t = match v_index.get(&v_tip) {
Some(&i) => i,
None => continue,
};
if i_o == i_t {
continue; }
let key = edge_key(i_o, i_t);
if !processed.insert(key) {
continue; }
let face_h = h.face;
let face_twin = twin.face;
let fp_h = face_h.and_then(|f| face_id_to_idx.get(&f).copied());
let fp_twin = face_twin.and_then(|f| face_id_to_idx.get(&f).copied());
match (fp_h, fp_twin) {
(Some(f1_idx), Some(f2_idx)) => {
let fp1 = (face_point_start + f1_idx) as u32; let fp2 = (face_point_start + f2_idx) as u32; new_faces.push([i_o, fp2, fp1]);
new_faces.push([i_t, fp1, fp2]);
}
(Some(f1_idx), None) => {
let fp1 = (face_point_start + f1_idx) as u32;
new_faces.push([i_o, i_t, fp1]);
}
(None, Some(f2_idx)) => {
let fp2 = (face_point_start + f2_idx) as u32;
new_faces.push([i_t, i_o, fp2]);
}
(None, None) => {
}
}
}
build_mesh_from_vertices_and_faces(&new_positions, &new_faces)
.expect("sqrt3 subdivision output is always valid")
}
fn sqrt3_alpha(n: usize) -> f64 {
if n == 0 {
return 0.0; }
let n_f = n as f64;
(4.0 - 2.0 * (2.0 * std::f64::consts::PI / n_f).cos()) / 9.0
}
#[inline]
fn edge_key(a: u32, b: u32) -> (u32, u32) {
if a < b { (a, b) } else { (b, a) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::{Face, HalfEdge, Vertex};
use crate::test_util::build_icosphere;
use crate::validate::check_topology;
fn build_single_quad_mesh() -> MeshStorage {
let mut mesh = MeshStorage::new();
let v0 = mesh.add_vertex(Vertex::new([0.0, 0.0, 0.0]));
let v1 = mesh.add_vertex(Vertex::new([1.0, 0.0, 0.0]));
let v2 = mesh.add_vertex(Vertex::new([1.0, 1.0, 0.0]));
let v3 = mesh.add_vertex(Vertex::new([0.0, 1.0, 0.0]));
let h0 = mesh.add_halfedge(HalfEdge::new(v1));
let h1 = mesh.add_halfedge(HalfEdge::new(v2));
let h2 = mesh.add_halfedge(HalfEdge::new(v3));
let h3 = mesh.add_halfedge(HalfEdge::new(v0));
for (he, next, prev) in [(h0, h1, h3), (h1, h2, h0), (h2, h3, h1), (h3, h0, h2)] {
let h = mesh.get_halfedge_mut(he).unwrap();
h.next = Some(next);
h.prev = Some(prev);
}
let face = mesh.add_face(Face::new());
mesh.get_face_mut(face).unwrap().halfedge = Some(h0);
for he in [h0, h1, h2, h3] {
mesh.get_halfedge_mut(he).unwrap().face = Some(face);
}
mesh.get_vertex_mut(v0).unwrap().halfedge = Some(h0);
mesh.get_vertex_mut(v1).unwrap().halfedge = Some(h1);
mesh.get_vertex_mut(v2).unwrap().halfedge = Some(h2);
mesh.get_vertex_mut(v3).unwrap().halfedge = Some(h3);
mesh
}
#[test]
fn sqrt3_skips_non_triangle_with_warning() {
let mesh = build_single_quad_mesh();
assert_eq!(mesh.face_count(), 1);
let refined = sqrt3_subdivide(&mesh);
assert_eq!(refined.face_count(), 0, "非三角面应被跳过");
assert_eq!(refined.vertex_count(), 4, "原始 4 个顶点应保留");
}
#[test]
fn sqrt3_icosphere0_vertex_face_count() {
let mesh = build_icosphere(0);
assert_eq!(mesh.vertex_count(), 12);
assert_eq!(mesh.face_count(), 20);
let refined = sqrt3_subdivide(&mesh);
assert_eq!(refined.vertex_count(), 32, "顶点数应为 V+F = 12+20 = 32");
assert_eq!(refined.face_count(), 60, "面数应为 3*F = 3*20 = 60");
}
#[test]
fn sqrt3_icosphere1_vertex_face_count() {
let mesh = build_icosphere(1);
let refined = sqrt3_subdivide(&mesh);
assert_eq!(refined.vertex_count(), 122, "顶点数应为 42+80=122");
assert_eq!(refined.face_count(), 240, "面数应为 3*80=240");
}
#[test]
fn sqrt3_icosphere0_passes_validation() {
let mesh = build_icosphere(0);
let refined = sqrt3_subdivide(&mesh);
assert!(
check_topology(&refined).is_ok(),
"细分后的网格应通过完整拓扑校验: {:?}",
check_topology(&refined)
);
}
#[test]
fn sqrt3_icosphere2_passes_validation() {
let mesh = build_icosphere(2);
let refined = sqrt3_subdivide(&mesh);
assert!(check_topology(&refined).is_ok());
}
#[test]
fn sqrt3_preserves_euler_characteristic() {
for n in 0..=2 {
let mesh = build_icosphere(n);
let v = mesh.vertex_count() as i64;
let e = (mesh.halfedge_count() / 2) as i64;
let f = mesh.face_count() as i64;
assert_eq!(v - e + f, 2, "细分前 icosphere({}) Euler 示性数应为 2", n);
let refined = sqrt3_subdivide(&mesh);
let v2 = refined.vertex_count() as i64;
let e2 = (refined.halfedge_count() / 2) as i64;
let f2 = refined.face_count() as i64;
assert_eq!(
v2 - e2 + f2,
2,
"细分后 icosphere({}) Euler 示性数应保持 2,实际 {}-{}+{}={}",
n,
v2,
e2,
f2,
v2 - e2 + f2
);
}
}
#[test]
fn sqrt3_vs_loop_face_growth() {
let mesh = build_icosphere(1);
let loop_refined = crate::subdiv::loop_subdivide(&mesh);
let sqrt3_refined = sqrt3_subdivide(&mesh);
assert_eq!(loop_refined.face_count(), 320, "Loop 应为 4*80=320");
assert_eq!(sqrt3_refined.face_count(), 240, "√3 应为 3*80=240");
}
#[test]
fn sqrt3_multiple_iterations_stay_valid() {
let mut mesh = build_icosphere(0);
for i in 0..2 {
mesh = sqrt3_subdivide(&mesh);
assert!(
check_topology(&mesh).is_ok(),
"第 {} 次细分后拓扑校验失败",
i + 1
);
}
assert_eq!(mesh.vertex_count(), 92);
assert_eq!(mesh.face_count(), 180);
}
#[test]
fn sqrt3_single_triangle() {
let vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let faces = [[0, 1, 2]];
let mesh = build_mesh_from_vertices_and_faces(&vertices, &faces).unwrap();
let refined = sqrt3_subdivide(&mesh);
assert_eq!(refined.vertex_count(), 4, "单三角形 V'=3+1=4");
assert_eq!(refined.face_count(), 3, "单三角形 F'=3");
assert!(check_topology(&refined).is_ok());
}
#[test]
fn sqrt3_open_quad() {
let vertices = [
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
];
let faces = [[0, 1, 2], [0, 2, 3]];
let mesh = build_mesh_from_vertices_and_faces(&vertices, &faces).unwrap();
let refined = sqrt3_subdivide(&mesh);
assert_eq!(refined.vertex_count(), 6, "开四边形 V'=4+2=6");
assert_eq!(refined.face_count(), 6, "开四边形 F'=2+4=6");
assert!(check_topology(&refined).is_ok());
}
#[test]
fn sqrt3_face_point_at_centroid() {
let vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let faces = [[0, 1, 2]];
let mesh = build_mesh_from_vertices_and_faces(&vertices, &faces).unwrap();
let refined = sqrt3_subdivide(&mesh);
let target = [1.0 / 3.0, 1.0 / 3.0, 0.0];
let eps = 1e-9;
let found = refined
.vertex_ids()
.filter_map(|v| refined.get_vertex(v))
.any(|vt| {
let p = vt.position;
(p[0] - target[0]).abs() < eps
&& (p[1] - target[1]).abs() < eps
&& (p[2] - target[2]).abs() < eps
});
assert!(found, "面点应在重心 (1/3, 1/3, 0)");
}
#[test]
fn sqrt3_alpha_regular_valence_6() {
let alpha = sqrt3_alpha(6);
assert!(
(alpha - 1.0 / 3.0).abs() < 1e-12,
"α(6) 应为 1/3,实际 {}",
alpha
);
}
#[test]
fn sqrt3_alpha_valence_3() {
let alpha = sqrt3_alpha(3);
assert!(
(alpha - 5.0 / 9.0).abs() < 1e-12,
"α(3) 应为 5/9,实际 {}",
alpha
);
}
#[test]
fn sqrt3_alpha_zero_valence_returns_zero() {
assert_eq!(sqrt3_alpha(0), 0.0);
}
#[test]
fn sqrt3_empty_mesh() {
let mesh = MeshStorage::new();
let refined = sqrt3_subdivide(&mesh);
assert_eq!(refined.vertex_count(), 0);
assert_eq!(refined.face_count(), 0);
}
#[test]
fn sqrt3_internal_vertex_relaxation() {
let vertices = [
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
];
let faces = [[0, 2, 1], [0, 1, 3], [0, 3, 2], [1, 2, 3]];
let mesh = build_mesh_from_vertices_and_faces(&vertices, &faces).unwrap();
let refined = sqrt3_subdivide(&mesh);
let eps = 1e-9;
let target = [5.0 / 27.0; 3];
let found = refined
.vertex_ids()
.filter_map(|v| refined.get_vertex(v))
.any(|vt| {
let p = vt.position;
(p[0] - target[0]).abs() < eps
&& (p[1] - target[1]).abs() < eps
&& (p[2] - target[2]).abs() < eps
});
assert!(found, "v0 松弛后应在 (5/27, 5/27, 5/27),未找到该位置");
}
}