oxihuman_morph/
clavicle_control.rs1#![allow(dead_code)]
3
4#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct ClavicleConfig {
9 pub prominence: f32,
10 pub length_ratio: f32,
11 pub angle_range: f32,
12}
13
14#[allow(dead_code)]
15#[derive(Debug, Clone)]
16pub struct ClavicleState {
17 pub prominence: f32,
18 pub angle: f32,
19 pub length: f32,
20 pub left_offset: f32,
21 pub right_offset: f32,
22}
23
24#[allow(dead_code)]
25#[derive(Debug, Clone)]
26pub struct ClavicleMorphWeights {
27 pub prominent: f32,
28 pub flat: f32,
29 pub angled_up: f32,
30 pub angled_down: f32,
31 pub wide: f32,
32}
33
34#[allow(dead_code)]
35pub fn default_clavicle_config() -> ClavicleConfig {
36 ClavicleConfig {
37 prominence: 0.5,
38 length_ratio: 0.5,
39 angle_range: 0.3,
40 }
41}
42
43#[allow(dead_code)]
44pub fn new_clavicle_state() -> ClavicleState {
45 ClavicleState {
46 prominence: 0.5,
47 angle: 0.5,
48 length: 0.5,
49 left_offset: 0.0,
50 right_offset: 0.0,
51 }
52}
53
54#[allow(dead_code)]
55pub fn set_clavicle_prominence(state: &mut ClavicleState, value: f32) {
56 state.prominence = value.clamp(0.0, 1.0);
57}
58
59#[allow(dead_code)]
60pub fn set_clavicle_angle(state: &mut ClavicleState, value: f32) {
61 state.angle = value.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn set_clavicle_length(state: &mut ClavicleState, value: f32) {
66 state.length = value.clamp(0.0, 1.0);
67}
68
69#[allow(dead_code)]
70pub fn set_clavicle_offset(state: &mut ClavicleState, left: f32, right: f32) {
71 state.left_offset = left.clamp(-1.0, 1.0);
72 state.right_offset = right.clamp(-1.0, 1.0);
73}
74
75#[allow(dead_code)]
76pub fn compute_clavicle_weights(
77 state: &ClavicleState,
78 cfg: &ClavicleConfig,
79) -> ClavicleMorphWeights {
80 let p = state.prominence * cfg.prominence;
81 let prominent = p.clamp(0.0, 1.0);
82 let flat = (1.0 - p).clamp(0.0, 1.0);
83 let angle_bias = (state.angle - 0.5) * 2.0 * cfg.angle_range;
84 let angled_up = angle_bias.max(0.0).clamp(0.0, 1.0);
85 let angled_down = (-angle_bias).max(0.0).clamp(0.0, 1.0);
86 let wide = (state.length * cfg.length_ratio).clamp(0.0, 1.0);
87 ClavicleMorphWeights {
88 prominent,
89 flat,
90 angled_up,
91 angled_down,
92 wide,
93 }
94}
95
96#[allow(dead_code)]
97pub fn clavicle_to_json(state: &ClavicleState) -> String {
98 format!(
99 r#"{{"prominence":{},"angle":{},"length":{},"left_offset":{},"right_offset":{}}}"#,
100 state.prominence, state.angle, state.length, state.left_offset, state.right_offset
101 )
102}
103
104#[allow(dead_code)]
105pub fn blend_clavicle_states(a: &ClavicleState, b: &ClavicleState, t: f32) -> ClavicleState {
106 let t = t.clamp(0.0, 1.0);
107 ClavicleState {
108 prominence: a.prominence + (b.prominence - a.prominence) * t,
109 angle: a.angle + (b.angle - a.angle) * t,
110 length: a.length + (b.length - a.length) * t,
111 left_offset: a.left_offset + (b.left_offset - a.left_offset) * t,
112 right_offset: a.right_offset + (b.right_offset - a.right_offset) * t,
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_default_config() {
122 let c = default_clavicle_config();
123 assert!((0.0..=1.0).contains(&c.prominence));
124 }
125
126 #[test]
127 fn test_new_state() {
128 let s = new_clavicle_state();
129 assert!((s.prominence - 0.5).abs() < 1e-6);
130 }
131
132 #[test]
133 fn test_set_prominence() {
134 let mut s = new_clavicle_state();
135 set_clavicle_prominence(&mut s, 0.9);
136 assert!((s.prominence - 0.9).abs() < 1e-6);
137 }
138
139 #[test]
140 fn test_set_angle_clamp() {
141 let mut s = new_clavicle_state();
142 set_clavicle_angle(&mut s, 1.5);
143 assert!((s.angle - 1.0).abs() < 1e-6);
144 }
145
146 #[test]
147 fn test_set_length() {
148 let mut s = new_clavicle_state();
149 set_clavicle_length(&mut s, 0.7);
150 assert!((s.length - 0.7).abs() < 1e-6);
151 }
152
153 #[test]
154 fn test_offset() {
155 let mut s = new_clavicle_state();
156 set_clavicle_offset(&mut s, 0.3, -0.2);
157 assert!((s.left_offset - 0.3).abs() < 1e-6);
158 assert!((s.right_offset - (-0.2)).abs() < 1e-6);
159 }
160
161 #[test]
162 fn test_compute_weights() {
163 let s = new_clavicle_state();
164 let cfg = default_clavicle_config();
165 let w = compute_clavicle_weights(&s, &cfg);
166 assert!((0.0..=1.0).contains(&w.prominent));
167 assert!((0.0..=1.0).contains(&w.flat));
168 }
169
170 #[test]
171 fn test_to_json() {
172 let s = new_clavicle_state();
173 let j = clavicle_to_json(&s);
174 assert!(j.contains("prominence"));
175 }
176
177 #[test]
178 fn test_blend() {
179 let a = new_clavicle_state();
180 let mut b = new_clavicle_state();
181 b.prominence = 1.0;
182 let mid = blend_clavicle_states(&a, &b, 0.5);
183 assert!((mid.prominence - 0.75).abs() < 1e-6);
184 }
185
186 #[test]
187 fn test_blend_identity() {
188 let a = new_clavicle_state();
189 let r = blend_clavicle_states(&a, &a, 0.3);
190 assert!((r.angle - a.angle).abs() < 1e-6);
191 }
192}