Skip to main content

oxihuman_export/
geo_modifier_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// Geometry modifier type.
6#[allow(dead_code)]
7#[derive(Clone, PartialEq)]
8pub enum GeoModType {
9    Subdivide,
10    Decimate,
11    Mirror,
12    Solidify,
13    Bevel,
14    Custom(String),
15}
16
17impl GeoModType {
18    #[allow(dead_code)]
19    pub fn type_name(&self) -> String {
20        match self {
21            GeoModType::Subdivide => "subdivide".to_string(),
22            GeoModType::Decimate => "decimate".to_string(),
23            GeoModType::Mirror => "mirror".to_string(),
24            GeoModType::Solidify => "solidify".to_string(),
25            GeoModType::Bevel => "bevel".to_string(),
26            GeoModType::Custom(s) => s.clone(),
27        }
28    }
29}
30
31/// A geometry modifier entry.
32#[allow(dead_code)]
33pub struct GeoModEntry {
34    pub name: String,
35    pub mod_type: GeoModType,
36    pub enabled: bool,
37    pub realtime: bool,
38}
39
40/// Geo modifier stack export.
41#[allow(dead_code)]
42#[derive(Default)]
43pub struct GeoModifierExport {
44    pub modifiers: Vec<GeoModEntry>,
45}
46
47/// Create a new geo modifier export.
48#[allow(dead_code)]
49pub fn new_geo_modifier_export() -> GeoModifierExport {
50    GeoModifierExport::default()
51}
52
53/// Add a modifier.
54#[allow(dead_code)]
55pub fn add_geo_modifier(
56    export: &mut GeoModifierExport,
57    name: &str,
58    mt: GeoModType,
59    enabled: bool,
60    realtime: bool,
61) {
62    export.modifiers.push(GeoModEntry {
63        name: name.to_string(),
64        mod_type: mt,
65        enabled,
66        realtime,
67    });
68}
69
70/// Count modifiers.
71#[allow(dead_code)]
72pub fn geo_mod_count(export: &GeoModifierExport) -> usize {
73    export.modifiers.len()
74}
75
76/// Count enabled modifiers.
77#[allow(dead_code)]
78pub fn geo_mod_enabled_count(export: &GeoModifierExport) -> usize {
79    export.modifiers.iter().filter(|m| m.enabled).count()
80}
81
82/// Find modifier by name.
83#[allow(dead_code)]
84pub fn find_geo_modifier<'a>(export: &'a GeoModifierExport, name: &str) -> Option<&'a GeoModEntry> {
85    export.modifiers.iter().find(|m| m.name == name)
86}
87
88/// Get modifiers of a given type.
89#[allow(dead_code)]
90pub fn mods_of_type<'a>(export: &'a GeoModifierExport, mt: &GeoModType) -> Vec<&'a GeoModEntry> {
91    export
92        .modifiers
93        .iter()
94        .filter(|m| &m.mod_type == mt)
95        .collect()
96}
97
98/// Realtime modifier count.
99#[allow(dead_code)]
100pub fn realtime_mod_count(export: &GeoModifierExport) -> usize {
101    export.modifiers.iter().filter(|m| m.realtime).count()
102}
103
104/// Serialize to JSON.
105#[allow(dead_code)]
106pub fn geo_modifier_to_json(export: &GeoModifierExport) -> String {
107    format!(
108        r#"{{"modifiers":{},"enabled":{}}}"#,
109        export.modifiers.len(),
110        geo_mod_enabled_count(export)
111    )
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn add_and_count() {
120        let mut e = new_geo_modifier_export();
121        add_geo_modifier(&mut e, "subdiv", GeoModType::Subdivide, true, true);
122        assert_eq!(geo_mod_count(&e), 1);
123    }
124
125    #[test]
126    fn enabled_count() {
127        let mut e = new_geo_modifier_export();
128        add_geo_modifier(&mut e, "a", GeoModType::Decimate, true, true);
129        add_geo_modifier(&mut e, "b", GeoModType::Mirror, false, false);
130        assert_eq!(geo_mod_enabled_count(&e), 1);
131    }
132
133    #[test]
134    fn find_modifier() {
135        let mut e = new_geo_modifier_export();
136        add_geo_modifier(&mut e, "bev", GeoModType::Bevel, true, true);
137        assert!(find_geo_modifier(&e, "bev").is_some());
138    }
139
140    #[test]
141    fn find_missing() {
142        let e = new_geo_modifier_export();
143        assert!(find_geo_modifier(&e, "x").is_none());
144    }
145
146    #[test]
147    fn type_filter() {
148        let mut e = new_geo_modifier_export();
149        add_geo_modifier(&mut e, "a", GeoModType::Subdivide, true, true);
150        add_geo_modifier(&mut e, "b", GeoModType::Decimate, true, true);
151        assert_eq!(mods_of_type(&e, &GeoModType::Subdivide).len(), 1);
152    }
153
154    #[test]
155    fn realtime_count() {
156        let mut e = new_geo_modifier_export();
157        add_geo_modifier(&mut e, "a", GeoModType::Solidify, true, true);
158        add_geo_modifier(&mut e, "b", GeoModType::Bevel, true, false);
159        assert_eq!(realtime_mod_count(&e), 1);
160    }
161
162    #[test]
163    fn json_has_modifiers() {
164        let e = new_geo_modifier_export();
165        let j = geo_modifier_to_json(&e);
166        assert!(j.contains("\"modifiers\":0"));
167    }
168
169    #[test]
170    fn type_name() {
171        assert_eq!(GeoModType::Subdivide.type_name(), "subdivide");
172    }
173
174    #[test]
175    fn custom_type_name() {
176        let t = GeoModType::Custom("shrinkwrap".to_string());
177        assert_eq!(t.type_name(), "shrinkwrap");
178    }
179
180    #[test]
181    fn empty_export() {
182        let e = new_geo_modifier_export();
183        assert_eq!(geo_mod_count(&e), 0);
184    }
185}