Skip to main content

oxihuman_export/
edge_select_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5use std::collections::HashMap;
6
7/// Export edge selection data (seams, creases, sharp edges).
8#[allow(dead_code)]
9pub struct SelectedEdge {
10    pub v0: u32,
11    pub v1: u32,
12    pub flags: EdgeFlags,
13}
14
15#[allow(dead_code)]
16pub struct EdgeFlags {
17    pub is_seam: bool,
18    pub is_sharp: bool,
19    pub is_crease: bool,
20    pub crease_value: f32,
21}
22
23#[allow(dead_code)]
24pub struct EdgeSelectExport {
25    pub edges: Vec<SelectedEdge>,
26}
27
28#[allow(dead_code)]
29pub fn new_edge_select_export() -> EdgeSelectExport {
30    EdgeSelectExport { edges: vec![] }
31}
32
33#[allow(dead_code)]
34pub fn add_selected_edge(export: &mut EdgeSelectExport, edge: SelectedEdge) {
35    export.edges.push(edge);
36}
37
38#[allow(dead_code)]
39pub fn selected_edge_count(export: &EdgeSelectExport) -> usize {
40    export.edges.len()
41}
42
43#[allow(dead_code)]
44pub fn seam_count(export: &EdgeSelectExport) -> usize {
45    export.edges.iter().filter(|e| e.flags.is_seam).count()
46}
47
48#[allow(dead_code)]
49pub fn sharp_count(export: &EdgeSelectExport) -> usize {
50    export.edges.iter().filter(|e| e.flags.is_sharp).count()
51}
52
53#[allow(dead_code)]
54pub fn crease_count(export: &EdgeSelectExport) -> usize {
55    export.edges.iter().filter(|e| e.flags.is_crease).count()
56}
57
58#[allow(dead_code)]
59pub fn average_crease_value(export: &EdgeSelectExport) -> f32 {
60    let crease_edges: Vec<f32> = export
61        .edges
62        .iter()
63        .filter(|e| e.flags.is_crease)
64        .map(|e| e.flags.crease_value)
65        .collect();
66    if crease_edges.is_empty() {
67        return 0.0;
68    }
69    crease_edges.iter().sum::<f32>() / crease_edges.len() as f32
70}
71
72#[allow(dead_code)]
73pub fn edge_select_to_json(export: &EdgeSelectExport) -> String {
74    format!(
75        "{{\"total\":{},\"seams\":{},\"sharp\":{},\"crease\":{}}}",
76        export.edges.len(),
77        seam_count(export),
78        sharp_count(export),
79        crease_count(export)
80    )
81}
82
83#[allow(dead_code)]
84pub fn build_edge_index(export: &EdgeSelectExport) -> HashMap<(u32, u32), usize> {
85    let mut map = HashMap::new();
86    for (i, e) in export.edges.iter().enumerate() {
87        let key = if e.v0 < e.v1 {
88            (e.v0, e.v1)
89        } else {
90            (e.v1, e.v0)
91        };
92        map.insert(key, i);
93    }
94    map
95}
96
97#[allow(dead_code)]
98pub fn has_edge(export: &EdgeSelectExport, v0: u32, v1: u32) -> bool {
99    let key = if v0 < v1 { (v0, v1) } else { (v1, v0) };
100    export.edges.iter().any(|e| {
101        let ek = if e.v0 < e.v1 {
102            (e.v0, e.v1)
103        } else {
104            (e.v1, e.v0)
105        };
106        ek == key
107    })
108}
109
110#[allow(dead_code)]
111pub fn validate_edge_export(export: &EdgeSelectExport) -> bool {
112    export
113        .edges
114        .iter()
115        .all(|e| e.v0 != e.v1 && (0.0..=1.0).contains(&e.flags.crease_value))
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    fn sample_export() -> EdgeSelectExport {
123        let mut e = new_edge_select_export();
124        add_selected_edge(
125            &mut e,
126            SelectedEdge {
127                v0: 0,
128                v1: 1,
129                flags: EdgeFlags {
130                    is_seam: true,
131                    is_sharp: false,
132                    is_crease: false,
133                    crease_value: 0.0,
134                },
135            },
136        );
137        add_selected_edge(
138            &mut e,
139            SelectedEdge {
140                v0: 1,
141                v1: 2,
142                flags: EdgeFlags {
143                    is_seam: false,
144                    is_sharp: true,
145                    is_crease: true,
146                    crease_value: 0.8,
147                },
148            },
149        );
150        e
151    }
152
153    #[test]
154    fn test_selected_edge_count() {
155        let e = sample_export();
156        assert_eq!(selected_edge_count(&e), 2);
157    }
158
159    #[test]
160    fn test_seam_count() {
161        let e = sample_export();
162        assert_eq!(seam_count(&e), 1);
163    }
164
165    #[test]
166    fn test_sharp_count() {
167        let e = sample_export();
168        assert_eq!(sharp_count(&e), 1);
169    }
170
171    #[test]
172    fn test_crease_count() {
173        let e = sample_export();
174        assert_eq!(crease_count(&e), 1);
175    }
176
177    #[test]
178    fn test_average_crease_value() {
179        let e = sample_export();
180        assert!((average_crease_value(&e) - 0.8).abs() < 1e-5);
181    }
182
183    #[test]
184    fn test_has_edge() {
185        let e = sample_export();
186        assert!(has_edge(&e, 0, 1));
187        assert!(!has_edge(&e, 3, 4));
188    }
189
190    #[test]
191    fn test_validate_export() {
192        let e = sample_export();
193        assert!(validate_edge_export(&e));
194    }
195
196    #[test]
197    fn test_to_json() {
198        let e = sample_export();
199        let j = edge_select_to_json(&e);
200        assert!(j.contains("seams"));
201    }
202
203    #[test]
204    fn test_build_edge_index() {
205        let e = sample_export();
206        let idx = build_edge_index(&e);
207        assert!(idx.contains_key(&(0, 1)));
208    }
209
210    #[test]
211    fn test_empty_average_crease() {
212        let e = new_edge_select_export();
213        assert_eq!(average_crease_value(&e), 0.0);
214    }
215}