oxihuman_viewer/
edge_normal.rs1#![allow(dead_code)]
4
5use std::f32::consts::FRAC_PI_2;
8
9#[allow(dead_code)]
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub struct MeshEdge {
13 pub v0: u32,
14 pub v1: u32,
15}
16
17#[allow(dead_code)]
19#[derive(Clone, Debug)]
20pub struct EdgeNormalEntry {
21 pub edge: MeshEdge,
22 pub normal: [f32; 3],
23 pub dihedral_rad: f32,
25}
26
27#[allow(dead_code)]
29#[derive(Clone, Debug, Default)]
30pub struct EdgeNormalMap {
31 pub entries: Vec<EdgeNormalEntry>,
32}
33
34#[allow(dead_code)]
35pub fn new_edge_normal_map() -> EdgeNormalMap {
36 EdgeNormalMap::default()
37}
38
39#[allow(dead_code)]
40pub fn en_add(map: &mut EdgeNormalMap, edge: MeshEdge, normal: [f32; 3], dihedral_rad: f32) {
41 map.entries.push(EdgeNormalEntry {
42 edge,
43 normal,
44 dihedral_rad,
45 });
46}
47
48#[allow(dead_code)]
49pub fn en_count(map: &EdgeNormalMap) -> usize {
50 map.entries.len()
51}
52
53#[allow(dead_code)]
54pub fn en_clear(map: &mut EdgeNormalMap) {
55 map.entries.clear();
56}
57
58#[allow(dead_code)]
60pub fn en_normalize(v: [f32; 3]) -> [f32; 3] {
61 let l = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
62 if l < 1e-9 {
63 return [0.0, 1.0, 0.0];
64 }
65 [v[0] / l, v[1] / l, v[2] / l]
66}
67
68#[allow(dead_code)]
70pub fn en_crease_edges(map: &EdgeNormalMap, threshold_rad: f32) -> Vec<&EdgeNormalEntry> {
71 let _ = FRAC_PI_2; map.entries
73 .iter()
74 .filter(|e| e.dihedral_rad >= threshold_rad)
75 .collect()
76}
77
78#[allow(dead_code)]
79pub fn en_average_dihedral(map: &EdgeNormalMap) -> f32 {
80 if map.entries.is_empty() {
81 return 0.0;
82 }
83 map.entries.iter().map(|e| e.dihedral_rad).sum::<f32>() / map.entries.len() as f32
84}
85
86#[allow(dead_code)]
87pub fn en_max_dihedral(map: &EdgeNormalMap) -> f32 {
88 map.entries
89 .iter()
90 .map(|e| e.dihedral_rad)
91 .fold(0.0f32, f32::max)
92}
93
94#[allow(dead_code)]
95pub fn en_to_json(map: &EdgeNormalMap) -> String {
96 format!(
97 "{{\"count\":{},\"avg_dihedral\":{:.4}}}",
98 en_count(map),
99 en_average_dihedral(map)
100 )
101}
102
103#[allow(dead_code)]
104pub fn en_find_by_vertices(map: &EdgeNormalMap, v0: u32, v1: u32) -> Option<&EdgeNormalEntry> {
105 map.entries
106 .iter()
107 .find(|e| (e.edge.v0 == v0 && e.edge.v1 == v1) || (e.edge.v0 == v1 && e.edge.v1 == v0))
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn new_empty() {
116 assert_eq!(en_count(&new_edge_normal_map()), 0);
117 }
118
119 #[test]
120 fn add_entry() {
121 let mut m = new_edge_normal_map();
122 en_add(&mut m, MeshEdge { v0: 0, v1: 1 }, [0.0, 1.0, 0.0], 0.5);
123 assert_eq!(en_count(&m), 1);
124 }
125
126 #[test]
127 fn clear() {
128 let mut m = new_edge_normal_map();
129 en_add(&mut m, MeshEdge { v0: 0, v1: 1 }, [0.0, 1.0, 0.0], 0.5);
130 en_clear(&mut m);
131 assert_eq!(en_count(&m), 0);
132 }
133
134 #[test]
135 fn normalize_unit_vector() {
136 let n = en_normalize([3.0, 0.0, 0.0]);
137 assert!((n[0] - 1.0).abs() < 1e-5);
138 }
139
140 #[test]
141 fn normalize_zero_safe() {
142 let n = en_normalize([0.0, 0.0, 0.0]);
143 assert!((n[1] - 1.0).abs() < 1e-5);
144 }
145
146 #[test]
147 fn crease_filter() {
148 let mut m = new_edge_normal_map();
149 en_add(&mut m, MeshEdge { v0: 0, v1: 1 }, [0.0, 1.0, 0.0], 1.5);
150 en_add(&mut m, MeshEdge { v0: 1, v1: 2 }, [0.0, 1.0, 0.0], 0.2);
151 let c = en_crease_edges(&m, 1.0);
152 assert_eq!(c.len(), 1);
153 }
154
155 #[test]
156 fn average_dihedral_empty() {
157 assert!((en_average_dihedral(&new_edge_normal_map())).abs() < 1e-5);
158 }
159
160 #[test]
161 fn max_dihedral() {
162 let mut m = new_edge_normal_map();
163 en_add(&mut m, MeshEdge { v0: 0, v1: 1 }, [1.0, 0.0, 0.0], 2.5);
164 assert!((en_max_dihedral(&m) - 2.5).abs() < 1e-5);
165 }
166
167 #[test]
168 fn find_by_vertices() {
169 let mut m = new_edge_normal_map();
170 en_add(&mut m, MeshEdge { v0: 3, v1: 7 }, [0.0, 1.0, 0.0], 1.0);
171 assert!(en_find_by_vertices(&m, 3, 7).is_some());
172 assert!(en_find_by_vertices(&m, 7, 3).is_some());
173 }
174
175 #[test]
176 fn json_has_count() {
177 assert!(en_to_json(&new_edge_normal_map()).contains("count"));
178 }
179}