Skip to main content

oxihuman_export/
edge_mark_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Marked edge export: seam, sharp, crease and freestyle markings.
6
7#[allow(dead_code)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub struct EdgeFlags {
10    pub is_seam: bool,
11    pub is_sharp: bool,
12    pub is_crease: bool,
13    pub is_freestyle: bool,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct MarkedEdge {
19    pub v0: u32,
20    pub v1: u32,
21    pub flags: EdgeFlags,
22    pub crease_value: f32,
23}
24
25#[allow(dead_code)]
26#[derive(Debug, Clone)]
27pub struct EdgeMarkExport {
28    pub edges: Vec<MarkedEdge>,
29}
30
31#[allow(dead_code)]
32pub fn new_edge_mark_export() -> EdgeMarkExport {
33    EdgeMarkExport { edges: Vec::new() }
34}
35
36#[allow(dead_code)]
37pub fn add_marked_edge(exp: &mut EdgeMarkExport, v0: u32, v1: u32, flags: EdgeFlags) {
38    exp.edges.push(MarkedEdge {
39        v0,
40        v1,
41        flags,
42        crease_value: 0.0,
43    });
44}
45
46#[allow(dead_code)]
47pub fn marked_edge_count(exp: &EdgeMarkExport) -> usize {
48    exp.edges.len()
49}
50
51#[allow(dead_code)]
52pub fn seam_edge_count_em(exp: &EdgeMarkExport) -> usize {
53    exp.edges.iter().filter(|e| e.flags.is_seam).count()
54}
55
56#[allow(dead_code)]
57pub fn sharp_edge_count_em(exp: &EdgeMarkExport) -> usize {
58    exp.edges.iter().filter(|e| e.flags.is_sharp).count()
59}
60
61#[allow(dead_code)]
62pub fn crease_edge_count_em(exp: &EdgeMarkExport) -> usize {
63    exp.edges.iter().filter(|e| e.flags.is_crease).count()
64}
65
66#[allow(dead_code)]
67pub fn set_crease_value(exp: &mut EdgeMarkExport, v0: u32, v1: u32, value: f32) {
68    for e in &mut exp.edges {
69        let (ea, eb) = if e.v0 < e.v1 {
70            (e.v0, e.v1)
71        } else {
72            (e.v1, e.v0)
73        };
74        let (qa, qb) = if v0 < v1 { (v0, v1) } else { (v1, v0) };
75        if ea == qa && eb == qb {
76            e.crease_value = value.clamp(0.0, 1.0);
77        }
78    }
79}
80
81#[allow(dead_code)]
82pub fn edge_mark_to_json(exp: &EdgeMarkExport) -> String {
83    format!(
84        "{{\"edge_count\":{},\"seams\":{},\"sharp\":{}}}",
85        marked_edge_count(exp),
86        seam_edge_count_em(exp),
87        sharp_edge_count_em(exp),
88    )
89}
90
91#[allow(dead_code)]
92pub fn avg_crease_value(exp: &EdgeMarkExport) -> f32 {
93    let creased: Vec<f32> = exp
94        .edges
95        .iter()
96        .filter(|e| e.flags.is_crease)
97        .map(|e| e.crease_value)
98        .collect();
99    if creased.is_empty() {
100        return 0.0;
101    }
102    creased.iter().sum::<f32>() / creased.len() as f32
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_empty() {
111        let exp = new_edge_mark_export();
112        assert_eq!(marked_edge_count(&exp), 0);
113    }
114
115    #[test]
116    fn test_add_edge() {
117        let mut exp = new_edge_mark_export();
118        add_marked_edge(
119            &mut exp,
120            0,
121            1,
122            EdgeFlags {
123                is_seam: true,
124                ..Default::default()
125            },
126        );
127        assert_eq!(marked_edge_count(&exp), 1);
128    }
129
130    #[test]
131    fn test_seam_count() {
132        let mut exp = new_edge_mark_export();
133        add_marked_edge(
134            &mut exp,
135            0,
136            1,
137            EdgeFlags {
138                is_seam: true,
139                ..Default::default()
140            },
141        );
142        add_marked_edge(&mut exp, 1, 2, EdgeFlags::default());
143        assert_eq!(seam_edge_count_em(&exp), 1);
144    }
145
146    #[test]
147    fn test_sharp_count() {
148        let mut exp = new_edge_mark_export();
149        add_marked_edge(
150            &mut exp,
151            0,
152            1,
153            EdgeFlags {
154                is_sharp: true,
155                ..Default::default()
156            },
157        );
158        assert_eq!(sharp_edge_count_em(&exp), 1);
159    }
160
161    #[test]
162    fn test_crease_count() {
163        let mut exp = new_edge_mark_export();
164        add_marked_edge(
165            &mut exp,
166            0,
167            1,
168            EdgeFlags {
169                is_crease: true,
170                ..Default::default()
171            },
172        );
173        assert_eq!(crease_edge_count_em(&exp), 1);
174    }
175
176    #[test]
177    fn test_set_crease_value() {
178        let mut exp = new_edge_mark_export();
179        add_marked_edge(
180            &mut exp,
181            0,
182            1,
183            EdgeFlags {
184                is_crease: true,
185                ..Default::default()
186            },
187        );
188        set_crease_value(&mut exp, 0, 1, 0.8);
189        assert!((exp.edges[0].crease_value - 0.8).abs() < 1e-5);
190    }
191
192    #[test]
193    fn test_json_output() {
194        let exp = new_edge_mark_export();
195        let j = edge_mark_to_json(&exp);
196        assert!(j.contains("edge_count"));
197    }
198
199    #[test]
200    fn test_avg_crease_empty() {
201        let exp = new_edge_mark_export();
202        assert!((avg_crease_value(&exp)).abs() < 1e-6);
203    }
204
205    #[test]
206    fn test_avg_crease_value() {
207        let mut exp = new_edge_mark_export();
208        add_marked_edge(
209            &mut exp,
210            0,
211            1,
212            EdgeFlags {
213                is_crease: true,
214                ..Default::default()
215            },
216        );
217        set_crease_value(&mut exp, 0, 1, 1.0);
218        assert!((avg_crease_value(&exp) - 1.0).abs() < 1e-5);
219    }
220
221    #[test]
222    fn test_default_crease_zero() {
223        let mut exp = new_edge_mark_export();
224        add_marked_edge(&mut exp, 5, 6, EdgeFlags::default());
225        assert!((exp.edges[0].crease_value).abs() < 1e-6);
226    }
227}