#![allow(dead_code)]
use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct EdgeValenceResult {
pub valences: HashMap<(u32, u32), usize>,
}
#[allow(dead_code)]
pub fn compute_edge_valences(indices: &[u32]) -> EdgeValenceResult {
let mut valences: HashMap<(u32, u32), usize> = HashMap::new();
let tri_count = indices.len() / 3;
for t in 0..tri_count {
let vs = [indices[t * 3], indices[t * 3 + 1], indices[t * 3 + 2]];
for k in 0..3 {
let a = vs[k];
let b = vs[(k + 1) % 3];
let key = if a < b { (a, b) } else { (b, a) };
*valences.entry(key).or_insert(0) += 1;
}
}
EdgeValenceResult { valences }
}
#[allow(dead_code)]
pub fn total_edge_count(result: &EdgeValenceResult) -> usize {
result.valences.len()
}
#[allow(dead_code)]
pub fn edge_valence(result: &EdgeValenceResult, a: u32, b: u32) -> usize {
let key = if a < b { (a, b) } else { (b, a) };
result.valences.get(&key).copied().unwrap_or(0)
}
#[allow(dead_code)]
pub fn boundary_edge_count(result: &EdgeValenceResult) -> usize {
result.valences.values().filter(|&&v| v == 1).count()
}
#[allow(dead_code)]
pub fn manifold_edge_count(result: &EdgeValenceResult) -> usize {
result.valences.values().filter(|&&v| v == 2).count()
}
#[allow(dead_code)]
pub fn non_manifold_edge_count(result: &EdgeValenceResult) -> usize {
result.valences.values().filter(|&&v| v > 2).count()
}
#[allow(dead_code)]
pub fn max_valence(result: &EdgeValenceResult) -> usize {
result.valences.values().copied().max().unwrap_or(0)
}
#[allow(dead_code)]
pub fn avg_valence(result: &EdgeValenceResult) -> f32 {
if result.valences.is_empty() {
return 0.0;
}
let sum: usize = result.valences.values().sum();
sum as f32 / result.valences.len() as f32
}
#[allow(dead_code)]
pub fn edge_valence_to_json(result: &EdgeValenceResult) -> String {
format!(
"{{\"edges\":{},\"boundary\":{},\"manifold\":{},\"non_manifold\":{}}}",
total_edge_count(result),
boundary_edge_count(result),
manifold_edge_count(result),
non_manifold_edge_count(result),
)
}
#[cfg(test)]
mod tests {
use super::*;
fn single_tri() -> Vec<u32> {
vec![0, 1, 2]
}
fn two_tris() -> Vec<u32> {
vec![0, 1, 2, 1, 3, 2]
}
#[test]
fn test_single_tri_edges() {
let r = compute_edge_valences(&single_tri());
assert_eq!(total_edge_count(&r), 3);
}
#[test]
fn test_single_tri_all_boundary() {
let r = compute_edge_valences(&single_tri());
assert_eq!(boundary_edge_count(&r), 3);
}
#[test]
fn test_two_tris_shared_edge() {
let r = compute_edge_valences(&two_tris());
assert_eq!(edge_valence(&r, 1, 2), 2);
}
#[test]
fn test_manifold_count() {
let r = compute_edge_valences(&two_tris());
assert_eq!(manifold_edge_count(&r), 1);
}
#[test]
fn test_non_manifold_empty() {
let r = compute_edge_valences(&two_tris());
assert_eq!(non_manifold_edge_count(&r), 0);
}
#[test]
fn test_max_valence() {
let r = compute_edge_valences(&two_tris());
assert_eq!(max_valence(&r), 2);
}
#[test]
fn test_avg_valence() {
let r = compute_edge_valences(&single_tri());
assert!((avg_valence(&r) - 1.0).abs() < 1e-6);
}
#[test]
fn test_empty() {
let r = compute_edge_valences(&[]);
assert_eq!(total_edge_count(&r), 0);
assert_eq!(max_valence(&r), 0);
}
#[test]
fn test_edge_valence_nonexistent() {
let r = compute_edge_valences(&single_tri());
assert_eq!(edge_valence(&r, 10, 20), 0);
}
#[test]
fn test_edge_valence_to_json() {
let r = compute_edge_valences(&single_tri());
let json = edge_valence_to_json(&r);
assert!(json.contains("\"edges\":3"));
}
}