#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ApexResult {
pub apex_indices: Vec<usize>,
pub avg_apex_angle: f32,
}
#[allow(dead_code)]
pub fn dist_sq(a: [f32; 3], b: [f32; 3]) -> f32 {
let dx = a[0] - b[0];
let dy = a[1] - b[1];
let dz = a[2] - b[2];
dx * dx + dy * dy + dz * dz
}
#[allow(dead_code)]
pub fn triangle_apex(v0: [f32; 3], v1: [f32; 3], v2: [f32; 3]) -> usize {
let d01 = dist_sq(v0, v1);
let d12 = dist_sq(v1, v2);
let d20 = dist_sq(v2, v0);
if d01 >= d12 && d01 >= d20 {
2 } else if d12 >= d01 && d12 >= d20 {
0 } else {
1 }
}
#[allow(dead_code)]
pub fn vertex_angle(v: [f32; 3], a: [f32; 3], b: [f32; 3]) -> f32 {
let va = [a[0] - v[0], a[1] - v[1], a[2] - v[2]];
let vb = [b[0] - v[0], b[1] - v[1], b[2] - v[2]];
let dot = va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2];
let la = (va[0] * va[0] + va[1] * va[1] + va[2] * va[2]).sqrt();
let lb = (vb[0] * vb[0] + vb[1] * vb[1] + vb[2] * vb[2]).sqrt();
let denom = la * lb;
if denom < 1e-12 {
return 0.0;
}
(dot / denom).clamp(-1.0, 1.0).acos()
}
#[allow(dead_code)]
pub fn apex_angle(v0: [f32; 3], v1: [f32; 3], v2: [f32; 3]) -> f32 {
let idx = triangle_apex(v0, v1, v2);
match idx {
0 => vertex_angle(v0, v1, v2),
1 => vertex_angle(v1, v0, v2),
_ => vertex_angle(v2, v0, v1),
}
}
#[allow(dead_code)]
pub fn find_apex_vertices(positions: &[[f32; 3]], indices: &[u32]) -> ApexResult {
let tri_count = indices.len() / 3;
let mut apex_indices = Vec::with_capacity(tri_count);
let mut angle_sum = 0.0f32;
for t in 0..tri_count {
let i0 = indices[t * 3] as usize;
let i1 = indices[t * 3 + 1] as usize;
let i2 = indices[t * 3 + 2] as usize;
let local = triangle_apex(positions[i0], positions[i1], positions[i2]);
let global = match local {
0 => i0,
1 => i1,
_ => i2,
};
apex_indices.push(global);
angle_sum += apex_angle(positions[i0], positions[i1], positions[i2]);
}
let avg = if tri_count > 0 {
angle_sum / tri_count as f32
} else {
0.0
};
ApexResult {
apex_indices,
avg_apex_angle: avg,
}
}
#[allow(dead_code)]
pub fn apex_count(result: &ApexResult) -> usize {
result.apex_indices.len()
}
#[allow(dead_code)]
pub fn is_apex_vertex(result: &ApexResult, vertex: usize) -> bool {
result.apex_indices.contains(&vertex)
}
#[allow(dead_code)]
pub fn apex_frequency(result: &ApexResult, vertex: usize) -> usize {
result.apex_indices.iter().filter(|&&v| v == vertex).count()
}
#[allow(dead_code)]
pub fn apex_result_to_json(result: &ApexResult) -> String {
format!(
"{{\"apex_count\":{},\"avg_apex_angle\":{:.6}}}",
result.apex_indices.len(),
result.avg_apex_angle
)
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::PI;
fn equilateral_triangle() -> ([f32; 3], [f32; 3], [f32; 3]) {
let v0 = [0.0, 0.0, 0.0];
let v1 = [1.0, 0.0, 0.0];
let v2 = [0.5, 0.866_025_4, 0.0];
(v0, v1, v2)
}
#[test]
fn test_dist_sq() {
let d = dist_sq([0.0, 0.0, 0.0], [3.0, 4.0, 0.0]);
assert!((d - 25.0).abs() < 1e-6);
}
#[test]
fn test_triangle_apex_right_angle() {
let v0 = [0.0, 0.0, 0.0];
let v1 = [3.0, 0.0, 0.0];
let v2 = [0.0, 4.0, 0.0];
assert_eq!(triangle_apex(v0, v1, v2), 0);
}
#[test]
fn test_vertex_angle_right() {
let v = [0.0, 0.0, 0.0];
let a = [1.0, 0.0, 0.0];
let b = [0.0, 1.0, 0.0];
let angle = vertex_angle(v, a, b);
assert!((angle - PI / 2.0).abs() < 1e-5);
}
#[test]
fn test_apex_angle_equilateral() {
let (v0, v1, v2) = equilateral_triangle();
let angle = apex_angle(v0, v1, v2);
assert!((angle - PI / 3.0).abs() < 0.01);
}
#[test]
fn test_find_apex_vertices() {
let positions = vec![[0.0, 0.0, 0.0], [3.0, 0.0, 0.0], [0.0, 4.0, 0.0]];
let indices = vec![0, 1, 2];
let result = find_apex_vertices(&positions, &indices);
assert_eq!(result.apex_indices.len(), 1);
assert_eq!(result.apex_indices[0], 0);
}
#[test]
fn test_apex_count() {
let result = ApexResult {
apex_indices: vec![0, 1, 2],
avg_apex_angle: 1.0,
};
assert_eq!(apex_count(&result), 3);
}
#[test]
fn test_is_apex_vertex() {
let result = ApexResult {
apex_indices: vec![0, 2],
avg_apex_angle: 1.0,
};
assert!(is_apex_vertex(&result, 0));
assert!(!is_apex_vertex(&result, 1));
}
#[test]
fn test_apex_frequency() {
let result = ApexResult {
apex_indices: vec![0, 0, 1],
avg_apex_angle: 1.0,
};
assert_eq!(apex_frequency(&result, 0), 2);
assert_eq!(apex_frequency(&result, 1), 1);
}
#[test]
fn test_apex_result_to_json() {
let result = ApexResult {
apex_indices: vec![0],
avg_apex_angle: 1.047,
};
let json = apex_result_to_json(&result);
assert!(json.contains("\"apex_count\":1"));
}
#[test]
fn test_empty_mesh() {
let result = find_apex_vertices(&[], &[]);
assert_eq!(apex_count(&result), 0);
assert!((result.avg_apex_angle - 0.0).abs() < 1e-9);
}
}