Skip to main content

oxihuman_morph/
age_progression_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Age progression morph set stub.
6
7/// An age stage entry in the progression sequence.
8#[derive(Debug, Clone)]
9pub struct AgeStage {
10    pub age_years: f32,
11    pub morph_weights: Vec<f32>,
12    pub label: String,
13}
14
15/// Age progression morph controller.
16#[derive(Debug, Clone)]
17pub struct AgeProgressionMorph {
18    pub stages: Vec<AgeStage>,
19    pub current_age: f32,
20    pub morph_count: usize,
21    pub enabled: bool,
22}
23
24impl AgeProgressionMorph {
25    pub fn new(morph_count: usize) -> Self {
26        AgeProgressionMorph {
27            stages: Vec::new(),
28            current_age: 25.0,
29            morph_count,
30            enabled: true,
31        }
32    }
33}
34
35/// Create a new age progression morph controller.
36pub fn new_age_progression_morph(morph_count: usize) -> AgeProgressionMorph {
37    AgeProgressionMorph::new(morph_count)
38}
39
40/// Add an age stage.
41pub fn apm_add_stage(apm: &mut AgeProgressionMorph, stage: AgeStage) {
42    apm.stages.push(stage);
43}
44
45/// Set current age for interpolation.
46pub fn apm_set_age(apm: &mut AgeProgressionMorph, age: f32) {
47    apm.current_age = age.max(0.0);
48}
49
50/// Evaluate morph weights for current age (stub: zeroed).
51pub fn apm_evaluate(apm: &AgeProgressionMorph) -> Vec<f32> {
52    /* Stub: returns zeroed weights */
53    vec![0.0; apm.morph_count]
54}
55
56/// Return stage count.
57pub fn apm_stage_count(apm: &AgeProgressionMorph) -> usize {
58    apm.stages.len()
59}
60
61/// Enable or disable.
62pub fn apm_set_enabled(apm: &mut AgeProgressionMorph, enabled: bool) {
63    apm.enabled = enabled;
64}
65
66/// Serialize to JSON-like string.
67pub fn apm_to_json(apm: &AgeProgressionMorph) -> String {
68    format!(
69        r#"{{"morph_count":{},"stage_count":{},"current_age":{:.1},"enabled":{}}}"#,
70        apm.morph_count,
71        apm.stages.len(),
72        apm.current_age,
73        apm.enabled
74    )
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_new_morph_count() {
83        let a = new_age_progression_morph(12);
84        assert_eq!(a.morph_count, 12 /* morph count must match */,);
85    }
86
87    #[test]
88    fn test_default_age() {
89        let a = new_age_progression_morph(4);
90        assert!((a.current_age - 25.0).abs() < 1e-5, /* default age must be 25.0 */);
91    }
92
93    #[test]
94    fn test_no_stages_initially() {
95        let a = new_age_progression_morph(4);
96        assert_eq!(apm_stage_count(&a), 0 /* no stages initially */,);
97    }
98
99    #[test]
100    fn test_add_stage() {
101        let mut a = new_age_progression_morph(4);
102        apm_add_stage(
103            &mut a,
104            AgeStage {
105                age_years: 30.0,
106                morph_weights: vec![0.0; 4],
107                label: "adult".into(),
108            },
109        );
110        assert_eq!(apm_stage_count(&a), 1 /* one stage after add */,);
111    }
112
113    #[test]
114    fn test_set_age() {
115        let mut a = new_age_progression_morph(4);
116        apm_set_age(&mut a, 60.0);
117        assert!((a.current_age - 60.0).abs() < 1e-5, /* age must be set */);
118    }
119
120    #[test]
121    fn test_age_clamped_negative() {
122        let mut a = new_age_progression_morph(4);
123        apm_set_age(&mut a, -5.0);
124        assert!((a.current_age).abs() < 1e-6, /* negative age clamped to 0 */);
125    }
126
127    #[test]
128    fn test_evaluate_length() {
129        let a = new_age_progression_morph(8);
130        let out = apm_evaluate(&a);
131        assert_eq!(out.len(), 8 /* output length must match morph_count */,);
132    }
133
134    #[test]
135    fn test_set_enabled() {
136        let mut a = new_age_progression_morph(2);
137        apm_set_enabled(&mut a, false);
138        assert!(!a.enabled /* must be disabled */,);
139    }
140
141    #[test]
142    fn test_to_json_contains_current_age() {
143        let a = new_age_progression_morph(4);
144        let j = apm_to_json(&a);
145        assert!(j.contains("\"current_age\""), /* json must contain current_age */);
146    }
147
148    #[test]
149    fn test_enabled_default() {
150        let a = new_age_progression_morph(1);
151        assert!(a.enabled /* must be enabled by default */,);
152    }
153}