Skip to main content

oxihuman_morph/
hand_palm_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 palm control: adjusts width, thickness and arch of the palm.
7
8use std::f32::consts::PI;
9
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct HandPalmConfig {
13    pub min_width: f32,
14    pub max_width: f32,
15}
16
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct HandPalmState {
20    pub width: f32,
21    pub thickness: f32,
22    pub arch: f32,
23}
24
25#[allow(dead_code)]
26pub fn default_hand_palm_config() -> HandPalmConfig {
27    HandPalmConfig {
28        min_width: 0.0,
29        max_width: 1.0,
30    }
31}
32
33#[allow(dead_code)]
34pub fn new_hand_palm_state() -> HandPalmState {
35    HandPalmState {
36        width: 0.5,
37        thickness: 0.4,
38        arch: 0.3,
39    }
40}
41
42#[allow(dead_code)]
43pub fn hp_set_width(state: &mut HandPalmState, cfg: &HandPalmConfig, v: f32) {
44    state.width = v.clamp(cfg.min_width, cfg.max_width);
45}
46
47#[allow(dead_code)]
48pub fn hp_set_thickness(state: &mut HandPalmState, v: f32) {
49    state.thickness = v.clamp(0.0, 1.0);
50}
51
52#[allow(dead_code)]
53pub fn hp_set_arch(state: &mut HandPalmState, v: f32) {
54    state.arch = v.clamp(0.0, 1.0);
55}
56
57#[allow(dead_code)]
58pub fn hp_reset(state: &mut HandPalmState) {
59    *state = new_hand_palm_state();
60}
61
62/// Approximate cross-section area of the palm.
63#[allow(dead_code)]
64pub fn hp_cross_section(state: &HandPalmState) -> f32 {
65    PI * state.width * state.thickness * 0.25
66}
67
68#[allow(dead_code)]
69pub fn hp_to_weights(state: &HandPalmState) -> Vec<(String, f32)> {
70    vec![
71        ("hand_palm_width".to_string(), state.width),
72        ("hand_palm_thickness".to_string(), state.thickness),
73        ("hand_palm_arch".to_string(), state.arch),
74    ]
75}
76
77#[allow(dead_code)]
78pub fn hp_to_json(state: &HandPalmState) -> String {
79    format!(
80        r#"{{"width":{:.4},"thickness":{:.4},"arch":{:.4}}}"#,
81        state.width, state.thickness, state.arch
82    )
83}
84
85#[allow(dead_code)]
86pub fn hp_blend(a: &HandPalmState, b: &HandPalmState, t: f32) -> HandPalmState {
87    let t = t.clamp(0.0, 1.0);
88    HandPalmState {
89        width: a.width + (b.width - a.width) * t,
90        thickness: a.thickness + (b.thickness - a.thickness) * t,
91        arch: a.arch + (b.arch - a.arch) * t,
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_default_config() {
101        let cfg = default_hand_palm_config();
102        assert!(cfg.min_width.abs() < 1e-6);
103    }
104
105    #[test]
106    fn test_new_state() {
107        let s = new_hand_palm_state();
108        assert!((s.width - 0.5).abs() < 1e-6);
109    }
110
111    #[test]
112    fn test_set_width_clamps() {
113        let cfg = default_hand_palm_config();
114        let mut s = new_hand_palm_state();
115        hp_set_width(&mut s, &cfg, 5.0);
116        assert!((s.width - 1.0).abs() < 1e-6);
117    }
118
119    #[test]
120    fn test_set_thickness() {
121        let mut s = new_hand_palm_state();
122        hp_set_thickness(&mut s, 0.7);
123        assert!((s.thickness - 0.7).abs() < 1e-6);
124    }
125
126    #[test]
127    fn test_set_arch() {
128        let mut s = new_hand_palm_state();
129        hp_set_arch(&mut s, 0.6);
130        assert!((s.arch - 0.6).abs() < 1e-6);
131    }
132
133    #[test]
134    fn test_reset() {
135        let cfg = default_hand_palm_config();
136        let mut s = new_hand_palm_state();
137        hp_set_width(&mut s, &cfg, 0.9);
138        hp_reset(&mut s);
139        assert!((s.width - 0.5).abs() < 1e-6);
140    }
141
142    #[test]
143    fn test_cross_section() {
144        let s = new_hand_palm_state();
145        assert!(hp_cross_section(&s) > 0.0);
146    }
147
148    #[test]
149    fn test_to_weights() {
150        let s = new_hand_palm_state();
151        assert_eq!(hp_to_weights(&s).len(), 3);
152    }
153
154    #[test]
155    fn test_blend() {
156        let a = new_hand_palm_state();
157        let mut b = new_hand_palm_state();
158        b.width = 1.0;
159        let mid = hp_blend(&a, &b, 0.5);
160        assert!((mid.width - 0.75).abs() < 1e-6);
161    }
162
163    #[test]
164    fn test_to_json() {
165        let s = new_hand_palm_state();
166        let j = hp_to_json(&s);
167        assert!(j.contains("width"));
168    }
169}