oxihuman_morph/
finger_length_control.rs1#![allow(dead_code)]
3
4use std::f32::consts::PI;
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct FingerLengthConfig {
11 pub overall_length: f32,
12 pub thickness: f32,
13 pub taper: f32,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct FingerLengthState {
19 pub overall_length: f32,
20 pub thickness: f32,
21 pub taper: f32,
22 pub knuckle_size: f32,
23}
24
25#[allow(dead_code)]
26#[derive(Debug, Clone)]
27pub struct FingerLengthWeights {
28 pub long: f32,
29 pub short: f32,
30 pub thick: f32,
31 pub thin: f32,
32 pub tapered: f32,
33 pub knobby: f32,
34}
35
36#[allow(dead_code)]
37pub fn default_finger_length_config() -> FingerLengthConfig {
38 FingerLengthConfig {
39 overall_length: 0.5,
40 thickness: 0.5,
41 taper: 0.5,
42 }
43}
44
45#[allow(dead_code)]
46pub fn new_finger_length_state() -> FingerLengthState {
47 FingerLengthState {
48 overall_length: 0.5,
49 thickness: 0.5,
50 taper: 0.5,
51 knuckle_size: 0.3,
52 }
53}
54
55#[allow(dead_code)]
56pub fn set_finger_overall_length(state: &mut FingerLengthState, value: f32) {
57 state.overall_length = value.clamp(0.0, 1.0);
58}
59
60#[allow(dead_code)]
61pub fn set_finger_thickness(state: &mut FingerLengthState, value: f32) {
62 state.thickness = value.clamp(0.0, 1.0);
63}
64
65#[allow(dead_code)]
66pub fn set_finger_taper(state: &mut FingerLengthState, value: f32) {
67 state.taper = value.clamp(0.0, 1.0);
68}
69
70#[allow(dead_code)]
71pub fn set_finger_knuckle_size(state: &mut FingerLengthState, value: f32) {
72 state.knuckle_size = value.clamp(0.0, 1.0);
73}
74
75#[allow(dead_code)]
76pub fn compute_finger_length_weights(
77 state: &FingerLengthState,
78 cfg: &FingerLengthConfig,
79) -> FingerLengthWeights {
80 let l = state.overall_length * cfg.overall_length;
81 let long = (l * (PI * 0.25).sin()).clamp(0.0, 1.0);
82 let short = (1.0 - l).clamp(0.0, 1.0);
83 let thick = (state.thickness * cfg.thickness).clamp(0.0, 1.0);
84 let thin = (1.0 - thick).clamp(0.0, 1.0);
85 let tapered = (state.taper * cfg.taper).clamp(0.0, 1.0);
86 let knobby = state.knuckle_size.clamp(0.0, 1.0);
87 FingerLengthWeights {
88 long,
89 short,
90 thick,
91 thin,
92 tapered,
93 knobby,
94 }
95}
96
97#[allow(dead_code)]
98pub fn finger_length_to_json(state: &FingerLengthState) -> String {
99 format!(
100 r#"{{"overall_length":{},"thickness":{},"taper":{},"knuckle_size":{}}}"#,
101 state.overall_length, state.thickness, state.taper, state.knuckle_size
102 )
103}
104
105#[allow(dead_code)]
106pub fn blend_finger_lengths(
107 a: &FingerLengthState,
108 b: &FingerLengthState,
109 t: f32,
110) -> FingerLengthState {
111 let t = t.clamp(0.0, 1.0);
112 FingerLengthState {
113 overall_length: a.overall_length + (b.overall_length - a.overall_length) * t,
114 thickness: a.thickness + (b.thickness - a.thickness) * t,
115 taper: a.taper + (b.taper - a.taper) * t,
116 knuckle_size: a.knuckle_size + (b.knuckle_size - a.knuckle_size) * t,
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_default_config() {
126 let cfg = default_finger_length_config();
127 assert!((0.0..=1.0).contains(&cfg.overall_length));
128 }
129
130 #[test]
131 fn test_new_state() {
132 let s = new_finger_length_state();
133 assert!((s.overall_length - 0.5).abs() < 1e-6);
134 }
135
136 #[test]
137 fn test_set_length_clamp() {
138 let mut s = new_finger_length_state();
139 set_finger_overall_length(&mut s, 1.5);
140 assert!((s.overall_length - 1.0).abs() < 1e-6);
141 }
142
143 #[test]
144 fn test_set_thickness() {
145 let mut s = new_finger_length_state();
146 set_finger_thickness(&mut s, 0.8);
147 assert!((s.thickness - 0.8).abs() < 1e-6);
148 }
149
150 #[test]
151 fn test_set_taper() {
152 let mut s = new_finger_length_state();
153 set_finger_taper(&mut s, 0.7);
154 assert!((s.taper - 0.7).abs() < 1e-6);
155 }
156
157 #[test]
158 fn test_set_knuckle_size() {
159 let mut s = new_finger_length_state();
160 set_finger_knuckle_size(&mut s, 0.6);
161 assert!((s.knuckle_size - 0.6).abs() < 1e-6);
162 }
163
164 #[test]
165 fn test_compute_weights() {
166 let s = new_finger_length_state();
167 let cfg = default_finger_length_config();
168 let w = compute_finger_length_weights(&s, &cfg);
169 assert!((0.0..=1.0).contains(&w.long));
170 assert!((0.0..=1.0).contains(&w.thick));
171 }
172
173 #[test]
174 fn test_to_json() {
175 let s = new_finger_length_state();
176 let json = finger_length_to_json(&s);
177 assert!(json.contains("overall_length"));
178 assert!(json.contains("knuckle_size"));
179 }
180
181 #[test]
182 fn test_blend() {
183 let a = new_finger_length_state();
184 let mut b = new_finger_length_state();
185 b.overall_length = 1.0;
186 let mid = blend_finger_lengths(&a, &b, 0.5);
187 assert!((mid.overall_length - 0.75).abs() < 1e-6);
188 }
189
190 #[test]
191 fn test_blend_identity() {
192 let a = new_finger_length_state();
193 let r = blend_finger_lengths(&a, &a, 0.5);
194 assert!((r.overall_length - a.overall_length).abs() < 1e-6);
195 }
196}