Skip to main content

oxihuman_morph/
crease_depth_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Wrinkle/crease depth morph stub.
6
7/// Crease region.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum CreaseRegion {
10    Forehead,
11    GlabellaLines,
12    NasolabialFold,
13    Marionette,
14    CrowsFeet,
15    NeckLines,
16}
17
18/// A crease entry with depth.
19#[derive(Debug, Clone)]
20pub struct CreaseEntry {
21    pub region: CreaseRegion,
22    pub depth: f32,
23    pub sharpness: f32,
24}
25
26/// Crease depth morph controller.
27#[derive(Debug, Clone)]
28pub struct CreaseDepthMorph {
29    pub creases: Vec<CreaseEntry>,
30    pub global_scale: f32,
31    pub morph_count: usize,
32    pub enabled: bool,
33}
34
35impl CreaseDepthMorph {
36    pub fn new(morph_count: usize) -> Self {
37        CreaseDepthMorph {
38            creases: Vec::new(),
39            global_scale: 1.0,
40            morph_count,
41            enabled: true,
42        }
43    }
44}
45
46/// Create a new crease depth morph.
47pub fn new_crease_depth_morph(morph_count: usize) -> CreaseDepthMorph {
48    CreaseDepthMorph::new(morph_count)
49}
50
51/// Add a crease entry.
52pub fn cdm_add_crease(morph: &mut CreaseDepthMorph, entry: CreaseEntry) {
53    morph.creases.push(entry);
54}
55
56/// Set global scale for all creases.
57pub fn cdm_set_global_scale(morph: &mut CreaseDepthMorph, scale: f32) {
58    morph.global_scale = scale.clamp(0.0, 2.0);
59}
60
61/// Clear all creases.
62pub fn cdm_clear(morph: &mut CreaseDepthMorph) {
63    morph.creases.clear();
64}
65
66/// Evaluate morph weights (stub: avg depth × global_scale).
67pub fn cdm_evaluate(morph: &CreaseDepthMorph) -> Vec<f32> {
68    /* Stub: average crease depth scaled by global_scale */
69    if !morph.enabled || morph.morph_count == 0 {
70        return vec![];
71    }
72    let avg = if morph.creases.is_empty() {
73        0.0
74    } else {
75        morph.creases.iter().map(|c| c.depth).sum::<f32>() / morph.creases.len() as f32
76    };
77    let w = (avg * morph.global_scale).clamp(0.0, 1.0);
78    vec![w; morph.morph_count]
79}
80
81/// Enable or disable.
82pub fn cdm_set_enabled(morph: &mut CreaseDepthMorph, enabled: bool) {
83    morph.enabled = enabled;
84}
85
86/// Return crease count.
87pub fn cdm_crease_count(morph: &CreaseDepthMorph) -> usize {
88    morph.creases.len()
89}
90
91/// Serialize to JSON-like string.
92pub fn cdm_to_json(morph: &CreaseDepthMorph) -> String {
93    format!(
94        r#"{{"crease_count":{},"global_scale":{},"morph_count":{},"enabled":{}}}"#,
95        morph.creases.len(),
96        morph.global_scale,
97        morph.morph_count,
98        morph.enabled
99    )
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    fn make_crease(region: CreaseRegion, depth: f32) -> CreaseEntry {
107        CreaseEntry {
108            region,
109            depth,
110            sharpness: 0.5,
111        }
112    }
113
114    #[test]
115    fn test_initial_empty() {
116        let m = new_crease_depth_morph(4);
117        assert_eq!(cdm_crease_count(&m), 0 /* no creases initially */);
118    }
119
120    #[test]
121    fn test_add_crease() {
122        let mut m = new_crease_depth_morph(4);
123        cdm_add_crease(&mut m, make_crease(CreaseRegion::Forehead, 0.5));
124        assert_eq!(cdm_crease_count(&m), 1 /* one crease added */);
125    }
126
127    #[test]
128    fn test_clear() {
129        let mut m = new_crease_depth_morph(4);
130        cdm_add_crease(&mut m, make_crease(CreaseRegion::CrowsFeet, 0.8));
131        cdm_clear(&mut m);
132        assert_eq!(cdm_crease_count(&m), 0 /* cleared */);
133    }
134
135    #[test]
136    fn test_global_scale_clamped() {
137        let mut m = new_crease_depth_morph(4);
138        cdm_set_global_scale(&mut m, 5.0);
139        assert!((m.global_scale - 2.0).abs() < 1e-6 /* global_scale clamped to 2.0 */);
140    }
141
142    #[test]
143    fn test_evaluate_length() {
144        let m = new_crease_depth_morph(5);
145        assert_eq!(
146            cdm_evaluate(&m).len(),
147            5 /* output must match morph_count */
148        );
149    }
150
151    #[test]
152    fn test_evaluate_empty_creases() {
153        let m = new_crease_depth_morph(4);
154        let out = cdm_evaluate(&m);
155        assert!((out[0]).abs() < 1e-6 /* empty creases must give zero weight */);
156    }
157
158    #[test]
159    fn test_evaluate_disabled() {
160        let mut m = new_crease_depth_morph(4);
161        cdm_set_enabled(&mut m, false);
162        assert!(cdm_evaluate(&m).is_empty() /* disabled must return empty */);
163    }
164
165    #[test]
166    fn test_to_json_has_crease_count() {
167        let m = new_crease_depth_morph(4);
168        let j = cdm_to_json(&m);
169        assert!(j.contains("\"crease_count\"") /* JSON must have crease_count */);
170    }
171
172    #[test]
173    fn test_enabled_default() {
174        let m = new_crease_depth_morph(4);
175        assert!(m.enabled /* must be enabled by default */);
176    }
177
178    #[test]
179    fn test_evaluate_avg_depth() {
180        let mut m = new_crease_depth_morph(2);
181        cdm_add_crease(&mut m, make_crease(CreaseRegion::Marionette, 0.4));
182        cdm_add_crease(&mut m, make_crease(CreaseRegion::NeckLines, 0.6));
183        let out = cdm_evaluate(&m);
184        assert!((out[0] - 0.5).abs() < 1e-5 /* avg depth must be 0.5 */);
185    }
186}