Skip to main content

oxihuman_export/
edge_loop_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Edge loop export for mesh topology information.
6
7/// An edge loop (ordered sequence of vertex indices).
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct EdgeLoopExport {
11    pub name: String,
12    pub vertices: Vec<u32>,
13}
14
15/// Collection of edge loops.
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct EdgeLoopBundle {
19    pub loops: Vec<EdgeLoopExport>,
20}
21
22/// Create new bundle.
23#[allow(dead_code)]
24pub fn new_edge_loop_bundle() -> EdgeLoopBundle {
25    EdgeLoopBundle { loops: vec![] }
26}
27
28/// Add an edge loop.
29#[allow(dead_code)]
30pub fn add_edge_loop(b: &mut EdgeLoopBundle, name: &str, verts: &[u32]) {
31    b.loops.push(EdgeLoopExport {
32        name: name.to_string(),
33        vertices: verts.to_vec(),
34    });
35}
36
37/// Loop count.
38#[allow(dead_code)]
39pub fn el_loop_count(b: &EdgeLoopBundle) -> usize {
40    b.loops.len()
41}
42
43/// Total vertex count across all loops.
44#[allow(dead_code)]
45pub fn el_total_vertices(b: &EdgeLoopBundle) -> usize {
46    b.loops.iter().map(|l| l.vertices.len()).sum()
47}
48
49/// Get loop by name.
50#[allow(dead_code)]
51pub fn get_edge_loop<'a>(b: &'a EdgeLoopBundle, name: &str) -> Option<&'a EdgeLoopExport> {
52    b.loops.iter().find(|l| l.name == name)
53}
54
55/// Check if a loop is closed (first == last).
56#[allow(dead_code)]
57pub fn is_closed_loop(l: &EdgeLoopExport) -> bool {
58    l.vertices.len() >= 2 && l.vertices.first() == l.vertices.last()
59}
60
61/// Largest loop vertex count.
62#[allow(dead_code)]
63pub fn largest_loop_size(b: &EdgeLoopBundle) -> usize {
64    b.loops.iter().map(|l| l.vertices.len()).max().unwrap_or(0)
65}
66
67/// Validate.
68#[allow(dead_code)]
69pub fn el_validate(b: &EdgeLoopBundle) -> bool {
70    b.loops.iter().all(|l| !l.vertices.is_empty())
71}
72
73/// Export to JSON.
74#[allow(dead_code)]
75pub fn edge_loop_bundle_to_json(b: &EdgeLoopBundle) -> String {
76    format!(
77        "{{\"loop_count\":{},\"total_vertices\":{}}}",
78        el_loop_count(b),
79        el_total_vertices(b)
80    )
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    #[test]
87    fn test_new() {
88        let b = new_edge_loop_bundle();
89        assert_eq!(el_loop_count(&b), 0);
90    }
91    #[test]
92    fn test_add() {
93        let mut b = new_edge_loop_bundle();
94        add_edge_loop(&mut b, "waist", &[0, 1, 2]);
95        assert_eq!(el_loop_count(&b), 1);
96    }
97    #[test]
98    fn test_total_verts() {
99        let mut b = new_edge_loop_bundle();
100        add_edge_loop(&mut b, "a", &[0, 1]);
101        add_edge_loop(&mut b, "b", &[2, 3, 4]);
102        assert_eq!(el_total_vertices(&b), 5);
103    }
104    #[test]
105    fn test_get() {
106        let mut b = new_edge_loop_bundle();
107        add_edge_loop(&mut b, "neck", &[0]);
108        assert!(get_edge_loop(&b, "neck").is_some());
109    }
110    #[test]
111    fn test_get_missing() {
112        let b = new_edge_loop_bundle();
113        assert!(get_edge_loop(&b, "x").is_none());
114    }
115    #[test]
116    fn test_is_closed() {
117        let l = EdgeLoopExport {
118            name: "c".to_string(),
119            vertices: vec![0, 1, 2, 0],
120        };
121        assert!(is_closed_loop(&l));
122    }
123    #[test]
124    fn test_not_closed() {
125        let l = EdgeLoopExport {
126            name: "c".to_string(),
127            vertices: vec![0, 1, 2],
128        };
129        assert!(!is_closed_loop(&l));
130    }
131    #[test]
132    fn test_largest() {
133        let mut b = new_edge_loop_bundle();
134        add_edge_loop(&mut b, "a", &[0, 1]);
135        add_edge_loop(&mut b, "b", &[0, 1, 2, 3]);
136        assert_eq!(largest_loop_size(&b), 4);
137    }
138    #[test]
139    fn test_validate() {
140        let mut b = new_edge_loop_bundle();
141        add_edge_loop(&mut b, "a", &[0]);
142        assert!(el_validate(&b));
143    }
144    #[test]
145    fn test_to_json() {
146        let b = new_edge_loop_bundle();
147        assert!(edge_loop_bundle_to_json(&b).contains("\"loop_count\":0"));
148    }
149}