oxihuman_morph/
finger_joint_control.rs1#![allow(dead_code)]
5
6use std::f32::consts::PI;
9
10#[allow(dead_code)]
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum Finger {
14 Thumb,
15 Index,
16 Middle,
17 Ring,
18 Pinky,
19}
20
21#[allow(dead_code)]
23#[derive(Debug, Clone)]
24pub struct FingerJointState {
25 pub curl: [f32; 5],
26 pub spread: [f32; 5],
27 pub stiffness: f32,
28}
29
30#[allow(dead_code)]
31pub fn new_finger_joint_state() -> FingerJointState {
32 FingerJointState {
33 curl: [0.0; 5],
34 spread: [0.0; 5],
35 stiffness: 0.5,
36 }
37}
38
39#[allow(dead_code)]
40pub fn fj_finger_index(finger: Finger) -> usize {
41 match finger {
42 Finger::Thumb => 0,
43 Finger::Index => 1,
44 Finger::Middle => 2,
45 Finger::Ring => 3,
46 Finger::Pinky => 4,
47 }
48}
49
50#[allow(dead_code)]
51pub fn fj_set_curl(state: &mut FingerJointState, finger: Finger, v: f32) {
52 let idx = fj_finger_index(finger);
53 state.curl[idx] = v.clamp(0.0, 1.0);
54}
55
56#[allow(dead_code)]
57pub fn fj_set_spread(state: &mut FingerJointState, finger: Finger, v: f32) {
58 let idx = fj_finger_index(finger);
59 state.spread[idx] = v.clamp(-1.0, 1.0);
60}
61
62#[allow(dead_code)]
63pub fn fj_set_all_curl(state: &mut FingerJointState, v: f32) {
64 let clamped = v.clamp(0.0, 1.0);
65 #[allow(clippy::needless_range_loop)]
66 for i in 0..5 {
67 state.curl[i] = clamped;
68 }
69}
70
71#[allow(dead_code)]
72pub fn fj_set_stiffness(state: &mut FingerJointState, v: f32) {
73 state.stiffness = v.clamp(0.0, 1.0);
74}
75
76#[allow(dead_code)]
77pub fn fj_reset(state: &mut FingerJointState) {
78 *state = new_finger_joint_state();
79}
80
81#[allow(dead_code)]
82pub fn fj_curl_angle(state: &FingerJointState, finger: Finger) -> f32 {
83 let idx = fj_finger_index(finger);
84 state.curl[idx] * PI * 0.5
85}
86
87#[allow(dead_code)]
88pub fn fj_to_json(state: &FingerJointState) -> String {
89 format!(
90 r#"{{"curl":[{:.4},{:.4},{:.4},{:.4},{:.4}],"stiffness":{:.4}}}"#,
91 state.curl[0], state.curl[1], state.curl[2], state.curl[3], state.curl[4], state.stiffness
92 )
93}
94
95#[allow(dead_code)]
96pub fn fj_blend(a: &FingerJointState, b: &FingerJointState, t: f32) -> FingerJointState {
97 let t = t.clamp(0.0, 1.0);
98 let mut result = new_finger_joint_state();
99 #[allow(clippy::needless_range_loop)]
100 for i in 0..5 {
101 result.curl[i] = a.curl[i] + (b.curl[i] - a.curl[i]) * t;
102 result.spread[i] = a.spread[i] + (b.spread[i] - a.spread[i]) * t;
103 }
104 result.stiffness = a.stiffness + (b.stiffness - a.stiffness) * t;
105 result
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_new_state() {
114 let s = new_finger_joint_state();
115 assert!(s.curl[0].abs() < 1e-6);
116 assert!((s.stiffness - 0.5).abs() < 1e-6);
117 }
118
119 #[test]
120 fn test_finger_index() {
121 assert_eq!(fj_finger_index(Finger::Thumb), 0);
122 assert_eq!(fj_finger_index(Finger::Pinky), 4);
123 }
124
125 #[test]
126 fn test_set_curl() {
127 let mut s = new_finger_joint_state();
128 fj_set_curl(&mut s, Finger::Index, 0.8);
129 assert!((s.curl[1] - 0.8).abs() < 1e-6);
130 }
131
132 #[test]
133 fn test_set_curl_clamps() {
134 let mut s = new_finger_joint_state();
135 fj_set_curl(&mut s, Finger::Middle, 5.0);
136 assert!((s.curl[2] - 1.0).abs() < 1e-6);
137 }
138
139 #[test]
140 fn test_set_spread() {
141 let mut s = new_finger_joint_state();
142 fj_set_spread(&mut s, Finger::Ring, -0.5);
143 assert!((s.spread[3] + 0.5).abs() < 1e-6);
144 }
145
146 #[test]
147 fn test_set_all_curl() {
148 let mut s = new_finger_joint_state();
149 fj_set_all_curl(&mut s, 0.7);
150 for c in &s.curl {
151 assert!((*c - 0.7).abs() < 1e-6);
152 }
153 }
154
155 #[test]
156 fn test_reset() {
157 let mut s = new_finger_joint_state();
158 fj_set_curl(&mut s, Finger::Thumb, 0.9);
159 fj_reset(&mut s);
160 assert!(s.curl[0].abs() < 1e-6);
161 }
162
163 #[test]
164 fn test_curl_angle() {
165 let mut s = new_finger_joint_state();
166 fj_set_curl(&mut s, Finger::Index, 1.0);
167 let angle = fj_curl_angle(&s, Finger::Index);
168 assert!((angle - PI * 0.5).abs() < 1e-6);
169 }
170
171 #[test]
172 fn test_to_json() {
173 let s = new_finger_joint_state();
174 let j = fj_to_json(&s);
175 assert!(j.contains("curl"));
176 assert!(j.contains("stiffness"));
177 }
178
179 #[test]
180 fn test_blend() {
181 let a = new_finger_joint_state();
182 let mut b = new_finger_joint_state();
183 b.curl[0] = 1.0;
184 let mid = fj_blend(&a, &b, 0.5);
185 assert!((mid.curl[0] - 0.5).abs() < 1e-6);
186 }
187}