oxihuman_morph/
forearm_control.rs1#![allow(dead_code)]
3
4#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct ForearmConfig {
9 pub muscle_range: f32,
10 pub taper_range: f32,
11 pub rotation_range: f32,
12}
13
14#[allow(dead_code)]
15#[derive(Debug, Clone)]
16pub struct ForearmState {
17 pub muscle_mass: f32,
18 pub taper: f32,
19 pub pronation: f32,
20 pub vein_visibility: f32,
21 pub length_ratio: f32,
22}
23
24#[allow(dead_code)]
25#[derive(Debug, Clone)]
26pub struct ForearmMorphWeights {
27 pub muscular: f32,
28 pub thin: f32,
29 pub tapered: f32,
30 pub pronated: f32,
31 pub supinated: f32,
32}
33
34#[allow(dead_code)]
35pub fn default_forearm_config() -> ForearmConfig {
36 ForearmConfig {
37 muscle_range: 0.8,
38 taper_range: 0.5,
39 rotation_range: 0.6,
40 }
41}
42
43#[allow(dead_code)]
44pub fn new_forearm_state() -> ForearmState {
45 ForearmState {
46 muscle_mass: 0.5,
47 taper: 0.5,
48 pronation: 0.5,
49 vein_visibility: 0.0,
50 length_ratio: 0.5,
51 }
52}
53
54#[allow(dead_code)]
55pub fn set_forearm_muscle(state: &mut ForearmState, value: f32) {
56 state.muscle_mass = value.clamp(0.0, 1.0);
57}
58
59#[allow(dead_code)]
60pub fn set_forearm_taper(state: &mut ForearmState, value: f32) {
61 state.taper = value.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn set_forearm_pronation(state: &mut ForearmState, value: f32) {
66 state.pronation = value.clamp(0.0, 1.0);
67}
68
69#[allow(dead_code)]
70pub fn set_vein_visibility(state: &mut ForearmState, value: f32) {
71 state.vein_visibility = value.clamp(0.0, 1.0);
72}
73
74#[allow(dead_code)]
75pub fn compute_forearm_weights(state: &ForearmState, cfg: &ForearmConfig) -> ForearmMorphWeights {
76 let m = state.muscle_mass * cfg.muscle_range;
77 let muscular = m.clamp(0.0, 1.0);
78 let thin = (1.0 - m).clamp(0.0, 1.0);
79 let tapered = (state.taper * cfg.taper_range).clamp(0.0, 1.0);
80 let rot = (state.pronation - 0.5) * 2.0 * cfg.rotation_range;
81 let pronated = rot.max(0.0).clamp(0.0, 1.0);
82 let supinated = (-rot).max(0.0).clamp(0.0, 1.0);
83 ForearmMorphWeights {
84 muscular,
85 thin,
86 tapered,
87 pronated,
88 supinated,
89 }
90}
91
92#[allow(dead_code)]
93pub fn forearm_to_json(state: &ForearmState) -> String {
94 format!(
95 r#"{{"muscle_mass":{},"taper":{},"pronation":{},"veins":{},"length":{}}}"#,
96 state.muscle_mass, state.taper, state.pronation, state.vein_visibility, state.length_ratio
97 )
98}
99
100#[allow(dead_code)]
101pub fn blend_forearm_states(a: &ForearmState, b: &ForearmState, t: f32) -> ForearmState {
102 let t = t.clamp(0.0, 1.0);
103 ForearmState {
104 muscle_mass: a.muscle_mass + (b.muscle_mass - a.muscle_mass) * t,
105 taper: a.taper + (b.taper - a.taper) * t,
106 pronation: a.pronation + (b.pronation - a.pronation) * t,
107 vein_visibility: a.vein_visibility + (b.vein_visibility - a.vein_visibility) * t,
108 length_ratio: a.length_ratio + (b.length_ratio - a.length_ratio) * t,
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_default_config() {
118 let c = default_forearm_config();
119 assert!((0.0..=1.0).contains(&c.muscle_range));
120 }
121
122 #[test]
123 fn test_new_state() {
124 let s = new_forearm_state();
125 assert!((s.muscle_mass - 0.5).abs() < 1e-6);
126 }
127
128 #[test]
129 fn test_set_muscle() {
130 let mut s = new_forearm_state();
131 set_forearm_muscle(&mut s, 0.9);
132 assert!((s.muscle_mass - 0.9).abs() < 1e-6);
133 }
134
135 #[test]
136 fn test_set_taper() {
137 let mut s = new_forearm_state();
138 set_forearm_taper(&mut s, 0.3);
139 assert!((s.taper - 0.3).abs() < 1e-6);
140 }
141
142 #[test]
143 fn test_set_pronation_clamp() {
144 let mut s = new_forearm_state();
145 set_forearm_pronation(&mut s, 5.0);
146 assert!((s.pronation - 1.0).abs() < 1e-6);
147 }
148
149 #[test]
150 fn test_compute_weights_range() {
151 let s = new_forearm_state();
152 let cfg = default_forearm_config();
153 let w = compute_forearm_weights(&s, &cfg);
154 assert!((0.0..=1.0).contains(&w.muscular));
155 assert!((0.0..=1.0).contains(&w.thin));
156 }
157
158 #[test]
159 fn test_to_json() {
160 let s = new_forearm_state();
161 let j = forearm_to_json(&s);
162 assert!(j.contains("muscle_mass"));
163 }
164
165 #[test]
166 fn test_blend() {
167 let a = new_forearm_state();
168 let mut b = new_forearm_state();
169 b.muscle_mass = 1.0;
170 let mid = blend_forearm_states(&a, &b, 0.5);
171 assert!((mid.muscle_mass - 0.75).abs() < 1e-6);
172 }
173
174 #[test]
175 fn test_set_veins() {
176 let mut s = new_forearm_state();
177 set_vein_visibility(&mut s, 0.6);
178 assert!((s.vein_visibility - 0.6).abs() < 1e-6);
179 }
180
181 #[test]
182 fn test_blend_identity() {
183 let a = new_forearm_state();
184 let r = blend_forearm_states(&a, &a, 0.5);
185 assert!((r.taper - a.taper).abs() < 1e-6);
186 }
187}