Skip to main content

oxihuman_mesh/
mesh_edge_loop_select.rs

1#![allow(dead_code)]
2
3//! Edge loop selection utilities.
4
5use std::collections::HashMap;
6
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct EdgeLoopSelect {
10    pub edges: Vec<(u32, u32)>,
11    pub closed: bool,
12}
13
14fn canonical(a: u32, b: u32) -> (u32, u32) {
15    if a < b {
16        (a, b)
17    } else {
18        (b, a)
19    }
20}
21
22#[allow(dead_code)]
23pub fn select_edge_loop(indices: &[u32], start_a: u32, start_b: u32) -> EdgeLoopSelect {
24    let mut adj: HashMap<u32, Vec<u32>> = HashMap::new();
25    for tri in indices.chunks(3) {
26        if tri.len() == 3 {
27            for &(a, b) in &[(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])] {
28                adj.entry(a).or_default().push(b);
29                adj.entry(b).or_default().push(a);
30            }
31        }
32    }
33    let mut edges = vec![canonical(start_a, start_b)];
34    let mut current = start_b;
35    let mut prev = start_a;
36    for _ in 0..indices.len() {
37        if let Some(neighbors) = adj.get(&current) {
38            let next = neighbors.iter().find(|&&n| n != prev && n != current);
39            if let Some(&n) = next {
40                let e = canonical(current, n);
41                if edges.contains(&e) {
42                    break;
43                }
44                edges.push(e);
45                prev = current;
46                current = n;
47            } else {
48                break;
49            }
50        } else {
51            break;
52        }
53    }
54    let closed = edges.len() > 2 && current == start_a;
55    EdgeLoopSelect { edges, closed }
56}
57
58#[allow(dead_code)]
59pub fn loop_edge_count(sel: &EdgeLoopSelect) -> usize {
60    sel.edges.len()
61}
62
63#[allow(dead_code)]
64pub fn loop_is_closed_els(sel: &EdgeLoopSelect) -> bool {
65    sel.closed
66}
67
68#[allow(dead_code)]
69pub fn loop_vertices_els(sel: &EdgeLoopSelect) -> Vec<u32> {
70    let mut verts = Vec::new();
71    for &(a, b) in &sel.edges {
72        if !verts.contains(&a) {
73            verts.push(a);
74        }
75        if !verts.contains(&b) {
76            verts.push(b);
77        }
78    }
79    verts
80}
81
82#[allow(dead_code)]
83pub fn grow_loop_selection(sel: &mut EdgeLoopSelect, indices: &[u32]) {
84    let verts = loop_vertices_els(sel);
85    for tri in indices.chunks(3) {
86        if tri.len() == 3 {
87            for &(a, b) in &[(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])] {
88                if verts.contains(&a) || verts.contains(&b) {
89                    let e = canonical(a, b);
90                    if !sel.edges.contains(&e) {
91                        sel.edges.push(e);
92                    }
93                }
94            }
95        }
96    }
97}
98
99#[allow(dead_code)]
100pub fn shrink_loop_selection(sel: &mut EdgeLoopSelect) {
101    if sel.edges.len() > 1 {
102        sel.edges.pop();
103    }
104}
105
106#[allow(dead_code)]
107pub fn loop_to_json(sel: &EdgeLoopSelect) -> String {
108    let es: Vec<String> = sel
109        .edges
110        .iter()
111        .map(|(a, b)| format!("[{},{}]", a, b))
112        .collect();
113    format!(
114        "{{\"edge_count\":{},\"closed\":{},\"edges\":[{}]}}",
115        sel.edges.len(),
116        sel.closed,
117        es.join(",")
118    )
119}
120
121#[allow(dead_code)]
122pub fn clear_loop_selection(sel: &mut EdgeLoopSelect) {
123    sel.edges.clear();
124    sel.closed = false;
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_select() {
133        let s = select_edge_loop(&[0, 1, 2], 0, 1);
134        assert!(!s.edges.is_empty());
135    }
136    #[test]
137    fn test_edge_count() {
138        let s = select_edge_loop(&[0, 1, 2], 0, 1);
139        assert!(loop_edge_count(&s) >= 1);
140    }
141    #[test]
142    fn test_vertices() {
143        let s = select_edge_loop(&[0, 1, 2], 0, 1);
144        let v = loop_vertices_els(&s);
145        assert!(v.contains(&0));
146    }
147    #[test]
148    fn test_grow() {
149        let mut s = EdgeLoopSelect {
150            edges: vec![(0, 1)],
151            closed: false,
152        };
153        grow_loop_selection(&mut s, &[0, 1, 2]);
154        assert!(s.edges.len() > 1);
155    }
156    #[test]
157    fn test_shrink() {
158        let mut s = EdgeLoopSelect {
159            edges: vec![(0, 1), (1, 2)],
160            closed: false,
161        };
162        shrink_loop_selection(&mut s);
163        assert_eq!(s.edges.len(), 1);
164    }
165    #[test]
166    fn test_to_json() {
167        let s = EdgeLoopSelect {
168            edges: vec![(0, 1)],
169            closed: false,
170        };
171        assert!(loop_to_json(&s).contains("\"edge_count\":1"));
172    }
173    #[test]
174    fn test_clear() {
175        let mut s = EdgeLoopSelect {
176            edges: vec![(0, 1)],
177            closed: true,
178        };
179        clear_loop_selection(&mut s);
180        assert!(s.edges.is_empty());
181        assert!(!s.closed);
182    }
183    #[test]
184    fn test_closed_flag() {
185        let s = EdgeLoopSelect {
186            edges: vec![],
187            closed: true,
188        };
189        assert!(loop_is_closed_els(&s));
190    }
191    #[test]
192    fn test_empty_select() {
193        let s = select_edge_loop(&[], 0, 1);
194        assert_eq!(loop_edge_count(&s), 1);
195    }
196    #[test]
197    fn test_shrink_min() {
198        let mut s = EdgeLoopSelect {
199            edges: vec![(0, 1)],
200            closed: false,
201        };
202        shrink_loop_selection(&mut s);
203        assert_eq!(s.edges.len(), 1);
204    }
205}