#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HalfEdgeMeshConfig {
pub keep_isolated_verts: bool,
}
#[allow(dead_code)]
pub fn default_halfedge_config() -> HalfEdgeMeshConfig {
HalfEdgeMeshConfig { keep_isolated_verts: false }
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HalfEdge {
pub vertex: usize,
pub face: usize,
pub next: usize,
pub twin: usize,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HalfEdgeMesh {
pub half_edges: Vec<HalfEdge>,
pub vertex_edge: Vec<usize>,
pub face_edge: Vec<usize>,
pub vertex_count: usize,
pub face_count: usize,
pub config: HalfEdgeMeshConfig,
}
#[allow(dead_code)]
pub fn build_halfedge_mesh(
vertex_count: usize,
indices: &[u32],
config: HalfEdgeMeshConfig,
) -> HalfEdgeMesh {
let face_count = indices.len() / 3;
let he_count = face_count * 3;
let mut half_edges: Vec<HalfEdge> = Vec::with_capacity(he_count);
let mut vertex_edge = vec![usize::MAX; vertex_count];
let mut face_edge = vec![usize::MAX; face_count];
#[allow(clippy::needless_range_loop)]
for f in 0..face_count {
let base = f * 3;
let he_base = f * 3;
for k in 0..3 {
let src = indices[base + k] as usize;
let dst = indices[base + (k + 1) % 3] as usize;
let next = he_base + (k + 1) % 3;
half_edges.push(HalfEdge { vertex: dst, face: f, next, twin: usize::MAX });
if vertex_edge[src] == usize::MAX { vertex_edge[src] = he_base + k; }
}
face_edge[f] = he_base;
}
use std::collections::HashMap;
let mut edge_map: HashMap<(usize, usize), usize> = HashMap::with_capacity(he_count);
for f in 0..face_count {
let base = f * 3;
for k in 0..3 {
let src = indices[base + k] as usize;
let dst = indices[base + (k + 1) % 3] as usize;
let he_idx = f * 3 + k;
edge_map.insert((src, dst), he_idx);
}
}
#[allow(clippy::needless_range_loop)]
for i in 0..half_edges.len() {
if half_edges[i].twin == usize::MAX {
let f = half_edges[i].face;
let base = f * 3;
let k = i - f * 3;
let src = indices[base + k] as usize;
let dst = half_edges[i].vertex;
if let Some(&twin_idx) = edge_map.get(&(dst, src)) {
half_edges[i].twin = twin_idx;
}
}
}
HalfEdgeMesh { half_edges, vertex_edge, face_edge, vertex_count, face_count, config }
}
#[allow(dead_code)]
pub fn halfedge_twin(mesh: &HalfEdgeMesh, he: usize) -> usize {
mesh.half_edges[he].twin
}
#[allow(dead_code)]
pub fn halfedge_next(mesh: &HalfEdgeMesh, he: usize) -> usize {
mesh.half_edges[he].next
}
#[allow(dead_code)]
pub fn halfedge_vertex(mesh: &HalfEdgeMesh, he: usize) -> usize {
mesh.half_edges[he].vertex
}
#[allow(dead_code)]
pub fn halfedge_face(mesh: &HalfEdgeMesh, he: usize) -> usize {
mesh.half_edges[he].face
}
#[allow(dead_code)]
pub fn halfedge_count(mesh: &HalfEdgeMesh) -> usize {
mesh.half_edges.len()
}
#[allow(dead_code)]
pub fn halfedge_is_boundary(mesh: &HalfEdgeMesh, he: usize) -> bool {
mesh.half_edges[he].twin == usize::MAX
}
#[allow(dead_code)]
pub fn halfedge_vertex_one_ring(mesh: &HalfEdgeMesh, v: usize) -> Vec<usize> {
let start = mesh.vertex_edge[v];
if start == usize::MAX { return vec![]; }
let mut result = Vec::new();
let mut he = start;
loop {
result.push(mesh.half_edges[he].vertex);
let next1 = mesh.half_edges[he].next;
let next2 = mesh.half_edges[next1].next;
let twin = mesh.half_edges[next2].twin;
if twin == usize::MAX || twin == start { break; }
he = twin;
if he == start { break; }
}
result
}
#[allow(dead_code)]
pub fn halfedge_mesh_boundary_loops(mesh: &HalfEdgeMesh) -> Vec<Vec<usize>> {
let mut visited = vec![false; mesh.half_edges.len()];
let mut loops = Vec::new();
for start in 0..mesh.half_edges.len() {
if !visited[start] && mesh.half_edges[start].twin == usize::MAX {
let mut loop_verts = Vec::new();
let mut he = start;
loop {
visited[he] = true;
loop_verts.push(mesh.half_edges[he].vertex);
let mut cur = mesh.half_edges[he].next;
while mesh.half_edges[cur].twin != usize::MAX {
cur = mesh.half_edges[mesh.half_edges[cur].twin].next;
}
he = cur;
if he == start || visited[he] { break; }
}
if !loop_verts.is_empty() {
loops.push(loop_verts);
}
}
}
loops
}
#[cfg(test)]
mod tests {
use super::*;
fn single_tri() -> (usize, Vec<u32>) {
(3, vec![0u32, 1, 2])
}
fn quad_mesh() -> (usize, Vec<u32>) {
(4, vec![0u32, 1, 2, 0, 2, 3])
}
#[test]
fn test_single_tri_he_count() {
let (vc, idx) = single_tri();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
assert_eq!(halfedge_count(&mesh), 3);
}
#[test]
fn test_single_tri_all_boundary() {
let (vc, idx) = single_tri();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
for he in 0..3 {
assert!(halfedge_is_boundary(&mesh, he), "he {he} should be boundary");
}
}
#[test]
fn test_quad_twin_linking() {
let (vc, idx) = quad_mesh();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
let mut found_linked = false;
for he in 0..mesh.half_edges.len() {
let twin = mesh.half_edges[he].twin;
if twin != usize::MAX {
found_linked = true;
assert_eq!(mesh.half_edges[twin].twin, he);
}
}
assert!(found_linked, "expected at least one twin pair");
}
#[test]
fn test_face_count() {
let (vc, idx) = quad_mesh();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
assert_eq!(mesh.face_count, 2);
}
#[test]
fn test_vertex_count() {
let (vc, idx) = quad_mesh();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
assert_eq!(mesh.vertex_count, 4);
}
#[test]
fn test_halfedge_next_cycles_face() {
let (vc, idx) = single_tri();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
let start = 0;
let n1 = halfedge_next(&mesh, start);
let n2 = halfedge_next(&mesh, n1);
let n3 = halfedge_next(&mesh, n2);
assert_eq!(n3, start);
}
#[test]
fn test_halfedge_face_accessor() {
let (vc, idx) = quad_mesh();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
for he in 0..6 {
assert!(halfedge_face(&mesh, he) < 2);
}
}
#[test]
fn test_boundary_loops_single_tri() {
let (vc, idx) = single_tri();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
let loops = halfedge_mesh_boundary_loops(&mesh);
assert_eq!(loops.len(), 1);
assert_eq!(loops[0].len(), 3);
}
#[test]
fn test_empty_mesh() {
let mesh = build_halfedge_mesh(0, &[], default_halfedge_config());
assert_eq!(halfedge_count(&mesh), 0);
assert_eq!(halfedge_mesh_boundary_loops(&mesh).len(), 0);
}
#[test]
fn test_halfedge_vertex_accessor() {
let (vc, idx) = single_tri();
let mesh = build_halfedge_mesh(vc, &idx, default_halfedge_config());
assert_eq!(halfedge_vertex(&mesh, 0), 1);
assert_eq!(halfedge_vertex(&mesh, 1), 2);
assert_eq!(halfedge_vertex(&mesh, 2), 0);
}
}