Skip to main content

oxihuman_morph/
face_length_control.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3
4//! Face vertical length morph.
5
6#![allow(dead_code)]
7
8/// Configuration for face length morphing.
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct FaceLengthConfig {
12    pub min_scale: f32,
13    pub max_scale: f32,
14}
15
16/// Runtime state for face length morph.
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct FaceLengthState {
20    pub scale: f32,
21    pub upper_contribution: f32,
22    pub lower_contribution: f32,
23}
24
25#[allow(dead_code)]
26pub fn default_face_length_config() -> FaceLengthConfig {
27    FaceLengthConfig {
28        min_scale: 0.5,
29        max_scale: 2.0,
30    }
31}
32
33#[allow(dead_code)]
34pub fn new_face_length_state() -> FaceLengthState {
35    FaceLengthState {
36        scale: 1.0,
37        upper_contribution: 0.5,
38        lower_contribution: 0.5,
39    }
40}
41
42#[allow(dead_code)]
43pub fn facel_set_scale(state: &mut FaceLengthState, cfg: &FaceLengthConfig, v: f32) {
44    state.scale = v.clamp(cfg.min_scale, cfg.max_scale);
45}
46
47#[allow(dead_code)]
48pub fn facel_set_upper_contrib(state: &mut FaceLengthState, v: f32) {
49    state.upper_contribution = v.clamp(0.0, 1.0);
50}
51
52#[allow(dead_code)]
53pub fn facel_set_lower_contrib(state: &mut FaceLengthState, v: f32) {
54    state.lower_contribution = v.clamp(0.0, 1.0);
55}
56
57#[allow(dead_code)]
58pub fn facel_reset(state: &mut FaceLengthState) {
59    *state = new_face_length_state();
60}
61
62#[allow(dead_code)]
63pub fn facel_to_weights(state: &FaceLengthState) -> Vec<(String, f32)> {
64    vec![
65        ("face_length_scale".to_string(), state.scale),
66        (
67            "face_length_upper_contrib".to_string(),
68            state.upper_contribution,
69        ),
70        (
71            "face_length_lower_contrib".to_string(),
72            state.lower_contribution,
73        ),
74    ]
75}
76
77#[allow(dead_code)]
78pub fn facel_to_json(state: &FaceLengthState) -> String {
79    format!(
80        r#"{{"scale":{:.4},"upper_contribution":{:.4},"lower_contribution":{:.4}}}"#,
81        state.scale, state.upper_contribution, state.lower_contribution
82    )
83}
84
85#[allow(dead_code)]
86pub fn facel_clamp(state: &mut FaceLengthState, cfg: &FaceLengthConfig) {
87    state.scale = state.scale.clamp(cfg.min_scale, cfg.max_scale);
88    state.upper_contribution = state.upper_contribution.clamp(0.0, 1.0);
89    state.lower_contribution = state.lower_contribution.clamp(0.0, 1.0);
90}
91
92#[allow(dead_code)]
93pub fn facel_effective_scale(state: &FaceLengthState) -> f32 {
94    state.scale * (1.0 + (state.upper_contribution + state.lower_contribution) * 0.05)
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_default_config() {
103        let cfg = default_face_length_config();
104        assert!((cfg.min_scale - 0.5).abs() < 1e-6);
105        assert!((cfg.max_scale - 2.0).abs() < 1e-6);
106    }
107
108    #[test]
109    fn test_new_state_defaults() {
110        let s = new_face_length_state();
111        assert!((s.scale - 1.0).abs() < 1e-6);
112        assert!((s.upper_contribution - 0.5).abs() < 1e-6);
113    }
114
115    #[test]
116    fn test_set_scale_clamps() {
117        let cfg = default_face_length_config();
118        let mut s = new_face_length_state();
119        facel_set_scale(&mut s, &cfg, 10.0);
120        assert!((s.scale - 2.0).abs() < 1e-6);
121        facel_set_scale(&mut s, &cfg, 0.1);
122        assert!((s.scale - 0.5).abs() < 1e-6);
123    }
124
125    #[test]
126    fn test_set_upper_contrib() {
127        let mut s = new_face_length_state();
128        facel_set_upper_contrib(&mut s, 0.8);
129        assert!((s.upper_contribution - 0.8).abs() < 1e-6);
130    }
131
132    #[test]
133    fn test_set_lower_contrib() {
134        let mut s = new_face_length_state();
135        facel_set_lower_contrib(&mut s, 0.2);
136        assert!((s.lower_contribution - 0.2).abs() < 1e-6);
137    }
138
139    #[test]
140    fn test_reset() {
141        let cfg = default_face_length_config();
142        let mut s = new_face_length_state();
143        facel_set_scale(&mut s, &cfg, 1.5);
144        facel_reset(&mut s);
145        assert!((s.scale - 1.0).abs() < 1e-6);
146    }
147
148    #[test]
149    fn test_to_weights_count() {
150        let s = new_face_length_state();
151        assert_eq!(facel_to_weights(&s).len(), 3);
152    }
153
154    #[test]
155    fn test_to_json_contains_fields() {
156        let s = new_face_length_state();
157        let j = facel_to_json(&s);
158        assert!(j.contains("scale"));
159        assert!(j.contains("upper_contribution"));
160    }
161
162    #[test]
163    fn test_effective_scale() {
164        let s = new_face_length_state();
165        let eff = facel_effective_scale(&s);
166        assert!(eff >= 1.0);
167    }
168}