Skip to main content

oxihuman_export/
deform_stack_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// Type of deformer in the stack.
6#[allow(dead_code)]
7#[derive(Clone, PartialEq)]
8pub enum DeformerType {
9    Lattice,
10    BlendShape,
11    Skinning,
12    Wrap,
13    Custom(String),
14}
15
16impl DeformerType {
17    #[allow(dead_code)]
18    pub fn name(&self) -> String {
19        match self {
20            DeformerType::Lattice => "lattice".to_string(),
21            DeformerType::BlendShape => "blend_shape".to_string(),
22            DeformerType::Skinning => "skinning".to_string(),
23            DeformerType::Wrap => "wrap".to_string(),
24            DeformerType::Custom(s) => s.clone(),
25        }
26    }
27}
28
29/// A deformer stack entry.
30#[allow(dead_code)]
31pub struct DeformerEntry {
32    pub name: String,
33    pub deformer_type: DeformerType,
34    pub enabled: bool,
35    pub order: usize,
36}
37
38/// A deform stack export.
39#[allow(dead_code)]
40#[derive(Default)]
41pub struct DeformStackExport {
42    pub entries: Vec<DeformerEntry>,
43}
44
45/// Create a new deform stack export.
46#[allow(dead_code)]
47pub fn new_deform_stack() -> DeformStackExport {
48    DeformStackExport::default()
49}
50
51/// Add a deformer to the stack.
52#[allow(dead_code)]
53pub fn add_deformer(stack: &mut DeformStackExport, name: &str, dt: DeformerType, enabled: bool) {
54    let order = stack.entries.len();
55    stack.entries.push(DeformerEntry {
56        name: name.to_string(),
57        deformer_type: dt,
58        enabled,
59        order,
60    });
61}
62
63/// Count deformers in the stack.
64#[allow(dead_code)]
65pub fn deformer_count(stack: &DeformStackExport) -> usize {
66    stack.entries.len()
67}
68
69/// Count enabled deformers.
70#[allow(dead_code)]
71pub fn enabled_deformer_count(stack: &DeformStackExport) -> usize {
72    stack.entries.iter().filter(|e| e.enabled).count()
73}
74
75/// Find deformer by name.
76#[allow(dead_code)]
77pub fn find_deformer<'a>(stack: &'a DeformStackExport, name: &str) -> Option<&'a DeformerEntry> {
78    stack.entries.iter().find(|e| e.name == name)
79}
80
81/// Get deformers of a given type.
82#[allow(dead_code)]
83pub fn deformers_of_type<'a>(
84    stack: &'a DeformStackExport,
85    dt: &DeformerType,
86) -> Vec<&'a DeformerEntry> {
87    stack
88        .entries
89        .iter()
90        .filter(|e| &e.deformer_type == dt)
91        .collect()
92}
93
94/// Toggle enabled state of a deformer.
95#[allow(dead_code)]
96pub fn toggle_deformer(stack: &mut DeformStackExport, name: &str) {
97    for e in &mut stack.entries {
98        if e.name == name {
99            e.enabled = !e.enabled;
100        }
101    }
102}
103
104/// Serialize to JSON.
105#[allow(dead_code)]
106pub fn deform_stack_to_json(stack: &DeformStackExport) -> String {
107    format!(
108        r#"{{"deformers":{},"enabled":{}}}"#,
109        stack.entries.len(),
110        enabled_deformer_count(stack)
111    )
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn add_and_count() {
120        let mut s = new_deform_stack();
121        add_deformer(&mut s, "skin", DeformerType::Skinning, true);
122        assert_eq!(deformer_count(&s), 1);
123    }
124
125    #[test]
126    fn enabled_count() {
127        let mut s = new_deform_stack();
128        add_deformer(&mut s, "a", DeformerType::Lattice, true);
129        add_deformer(&mut s, "b", DeformerType::Wrap, false);
130        assert_eq!(enabled_deformer_count(&s), 1);
131    }
132
133    #[test]
134    fn find_deformer_found() {
135        let mut s = new_deform_stack();
136        add_deformer(&mut s, "skin", DeformerType::Skinning, true);
137        assert!(find_deformer(&s, "skin").is_some());
138    }
139
140    #[test]
141    fn find_deformer_missing() {
142        let s = new_deform_stack();
143        assert!(find_deformer(&s, "x").is_none());
144    }
145
146    #[test]
147    fn deformers_of_type_filter() {
148        let mut s = new_deform_stack();
149        add_deformer(&mut s, "a", DeformerType::Skinning, true);
150        add_deformer(&mut s, "b", DeformerType::Lattice, true);
151        assert_eq!(deformers_of_type(&s, &DeformerType::Skinning).len(), 1);
152    }
153
154    #[test]
155    fn toggle() {
156        let mut s = new_deform_stack();
157        add_deformer(&mut s, "x", DeformerType::Wrap, true);
158        toggle_deformer(&mut s, "x");
159        assert_eq!(enabled_deformer_count(&s), 0);
160    }
161
162    #[test]
163    fn json_has_deformers() {
164        let s = new_deform_stack();
165        let j = deform_stack_to_json(&s);
166        assert!(j.contains("\"deformers\":0"));
167    }
168
169    #[test]
170    fn type_name() {
171        assert_eq!(DeformerType::Skinning.name(), "skinning");
172    }
173
174    #[test]
175    fn custom_type_name() {
176        let t = DeformerType::Custom("ffd".to_string());
177        assert_eq!(t.name(), "ffd");
178    }
179
180    #[test]
181    fn order_increments() {
182        let mut s = new_deform_stack();
183        add_deformer(&mut s, "a", DeformerType::Lattice, true);
184        add_deformer(&mut s, "b", DeformerType::Skinning, true);
185        assert_eq!(s.entries[1].order, 1);
186    }
187}