Skip to main content

oxihuman_morph/
hand_thickness_control.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3
4#![allow(dead_code)]
5
6//! Hand thickness morph control: adjusts the palm/back thickness and finger girth.
7
8/// Configuration for hand thickness morphing.
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct HandThicknessConfig {
12    pub min_thickness: f32,
13    pub max_thickness: f32,
14}
15
16/// Runtime state for hand thickness morph.
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct HandThicknessState {
20    pub left_thickness: f32,
21    pub right_thickness: f32,
22    pub finger_girth: f32,
23    pub palm_width: f32,
24}
25
26#[allow(dead_code)]
27pub fn default_hand_thickness_config() -> HandThicknessConfig {
28    HandThicknessConfig {
29        min_thickness: 0.0,
30        max_thickness: 1.0,
31    }
32}
33
34#[allow(dead_code)]
35pub fn new_hand_thickness_state() -> HandThicknessState {
36    HandThicknessState {
37        left_thickness: 0.5,
38        right_thickness: 0.5,
39        finger_girth: 0.5,
40        palm_width: 0.5,
41    }
42}
43
44#[allow(dead_code)]
45pub fn ht_set_left(state: &mut HandThicknessState, cfg: &HandThicknessConfig, v: f32) {
46    state.left_thickness = v.clamp(cfg.min_thickness, cfg.max_thickness);
47}
48
49#[allow(dead_code)]
50pub fn ht_set_right(state: &mut HandThicknessState, cfg: &HandThicknessConfig, v: f32) {
51    state.right_thickness = v.clamp(cfg.min_thickness, cfg.max_thickness);
52}
53
54#[allow(dead_code)]
55pub fn ht_set_finger_girth(state: &mut HandThicknessState, v: f32) {
56    state.finger_girth = v.clamp(0.0, 1.0);
57}
58
59#[allow(dead_code)]
60pub fn ht_set_palm_width(state: &mut HandThicknessState, v: f32) {
61    state.palm_width = v.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn ht_reset(state: &mut HandThicknessState) {
66    *state = new_hand_thickness_state();
67}
68
69#[allow(dead_code)]
70pub fn ht_to_weights(state: &HandThicknessState) -> Vec<(String, f32)> {
71    vec![
72        ("hand_thickness_left".to_string(), state.left_thickness),
73        ("hand_thickness_right".to_string(), state.right_thickness),
74        ("hand_finger_girth".to_string(), state.finger_girth),
75        ("hand_palm_width".to_string(), state.palm_width),
76    ]
77}
78
79#[allow(dead_code)]
80pub fn ht_to_json(state: &HandThicknessState) -> String {
81    format!(
82        r#"{{"left_thickness":{:.4},"right_thickness":{:.4},"finger_girth":{:.4},"palm_width":{:.4}}}"#,
83        state.left_thickness, state.right_thickness, state.finger_girth, state.palm_width
84    )
85}
86
87#[allow(dead_code)]
88pub fn ht_blend(a: &HandThicknessState, b: &HandThicknessState, t: f32) -> HandThicknessState {
89    let t = t.clamp(0.0, 1.0);
90    HandThicknessState {
91        left_thickness: a.left_thickness + (b.left_thickness - a.left_thickness) * t,
92        right_thickness: a.right_thickness + (b.right_thickness - a.right_thickness) * t,
93        finger_girth: a.finger_girth + (b.finger_girth - a.finger_girth) * t,
94        palm_width: a.palm_width + (b.palm_width - a.palm_width) * t,
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_default_config() {
104        let cfg = default_hand_thickness_config();
105        assert!(cfg.min_thickness.abs() < 1e-6);
106        assert!((cfg.max_thickness - 1.0).abs() < 1e-6);
107    }
108
109    #[test]
110    fn test_new_state() {
111        let s = new_hand_thickness_state();
112        assert!((s.left_thickness - 0.5).abs() < 1e-6);
113        assert!((s.finger_girth - 0.5).abs() < 1e-6);
114    }
115
116    #[test]
117    fn test_set_left_clamps() {
118        let cfg = default_hand_thickness_config();
119        let mut s = new_hand_thickness_state();
120        ht_set_left(&mut s, &cfg, 5.0);
121        assert!((s.left_thickness - 1.0).abs() < 1e-6);
122    }
123
124    #[test]
125    fn test_set_right() {
126        let cfg = default_hand_thickness_config();
127        let mut s = new_hand_thickness_state();
128        ht_set_right(&mut s, &cfg, 0.3);
129        assert!((s.right_thickness - 0.3).abs() < 1e-6);
130    }
131
132    #[test]
133    fn test_set_finger_girth() {
134        let mut s = new_hand_thickness_state();
135        ht_set_finger_girth(&mut s, 0.8);
136        assert!((s.finger_girth - 0.8).abs() < 1e-6);
137    }
138
139    #[test]
140    fn test_set_palm_width() {
141        let mut s = new_hand_thickness_state();
142        ht_set_palm_width(&mut s, 0.2);
143        assert!((s.palm_width - 0.2).abs() < 1e-6);
144    }
145
146    #[test]
147    fn test_reset() {
148        let cfg = default_hand_thickness_config();
149        let mut s = new_hand_thickness_state();
150        ht_set_left(&mut s, &cfg, 0.9);
151        ht_reset(&mut s);
152        assert!((s.left_thickness - 0.5).abs() < 1e-6);
153    }
154
155    #[test]
156    fn test_to_weights() {
157        let s = new_hand_thickness_state();
158        assert_eq!(ht_to_weights(&s).len(), 4);
159    }
160
161    #[test]
162    fn test_to_json() {
163        let s = new_hand_thickness_state();
164        let j = ht_to_json(&s);
165        assert!(j.contains("left_thickness"));
166    }
167
168    #[test]
169    fn test_blend() {
170        let a = new_hand_thickness_state();
171        let mut b = new_hand_thickness_state();
172        b.left_thickness = 1.0;
173        let mid = ht_blend(&a, &b, 0.5);
174        assert!((mid.left_thickness - 0.75).abs() < 1e-6);
175    }
176}