oxihuman_morph/
hand_palm_control.rs1#![allow(dead_code)]
5
6use 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#[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}