oxihuman_morph/
forehead_raise_control.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct ForeheadRaiseConfig {
11 pub max_raise: f32,
12}
13
14#[allow(dead_code)]
16#[derive(Debug, Clone)]
17pub struct ForeheadRaiseState {
18 pub center_raise: f32,
19 pub left_raise: f32,
20 pub right_raise: f32,
21 pub skin_tension: f32,
22}
23
24#[allow(dead_code)]
25pub fn default_forehead_raise_config() -> ForeheadRaiseConfig {
26 ForeheadRaiseConfig { max_raise: 1.0 }
27}
28
29#[allow(dead_code)]
30pub fn new_forehead_raise_state() -> ForeheadRaiseState {
31 ForeheadRaiseState {
32 center_raise: 0.0,
33 left_raise: 0.0,
34 right_raise: 0.0,
35 skin_tension: 0.0,
36 }
37}
38
39#[allow(dead_code)]
40pub fn fhr_set_center(state: &mut ForeheadRaiseState, cfg: &ForeheadRaiseConfig, v: f32) {
41 state.center_raise = v.clamp(0.0, cfg.max_raise);
42}
43
44#[allow(dead_code)]
45pub fn fhr_set_sides(
46 state: &mut ForeheadRaiseState,
47 cfg: &ForeheadRaiseConfig,
48 left: f32,
49 right: f32,
50) {
51 state.left_raise = left.clamp(0.0, cfg.max_raise);
52 state.right_raise = right.clamp(0.0, cfg.max_raise);
53}
54
55#[allow(dead_code)]
56pub fn fhr_set_all(state: &mut ForeheadRaiseState, cfg: &ForeheadRaiseConfig, v: f32) {
57 let clamped = v.clamp(0.0, cfg.max_raise);
58 state.center_raise = clamped;
59 state.left_raise = clamped;
60 state.right_raise = clamped;
61}
62
63#[allow(dead_code)]
64pub fn fhr_set_tension(state: &mut ForeheadRaiseState, v: f32) {
65 state.skin_tension = v.clamp(0.0, 1.0);
66}
67
68#[allow(dead_code)]
69pub fn fhr_reset(state: &mut ForeheadRaiseState) {
70 *state = new_forehead_raise_state();
71}
72
73#[allow(dead_code)]
74pub fn fhr_is_neutral(state: &ForeheadRaiseState) -> bool {
75 let vals = [
76 state.center_raise,
77 state.left_raise,
78 state.right_raise,
79 state.skin_tension,
80 ];
81 !vals.is_empty() && vals.iter().all(|v| v.abs() < 1e-6)
82}
83
84#[allow(dead_code)]
85pub fn fhr_average_raise(state: &ForeheadRaiseState) -> f32 {
86 (state.center_raise + state.left_raise + state.right_raise) / 3.0
87}
88
89#[allow(dead_code)]
90pub fn fhr_symmetry(state: &ForeheadRaiseState) -> f32 {
91 (state.left_raise - state.right_raise).abs()
92}
93
94#[allow(dead_code)]
95pub fn fhr_blend(a: &ForeheadRaiseState, b: &ForeheadRaiseState, t: f32) -> ForeheadRaiseState {
96 let t = t.clamp(0.0, 1.0);
97 ForeheadRaiseState {
98 center_raise: a.center_raise + (b.center_raise - a.center_raise) * t,
99 left_raise: a.left_raise + (b.left_raise - a.left_raise) * t,
100 right_raise: a.right_raise + (b.right_raise - a.right_raise) * t,
101 skin_tension: a.skin_tension + (b.skin_tension - a.skin_tension) * t,
102 }
103}
104
105#[allow(dead_code)]
106pub fn fhr_to_weights(state: &ForeheadRaiseState) -> Vec<(String, f32)> {
107 vec![
108 ("forehead_raise_center".to_string(), state.center_raise),
109 ("forehead_raise_left".to_string(), state.left_raise),
110 ("forehead_raise_right".to_string(), state.right_raise),
111 ("forehead_skin_tension".to_string(), state.skin_tension),
112 ]
113}
114
115#[allow(dead_code)]
116pub fn fhr_to_json(state: &ForeheadRaiseState) -> String {
117 format!(
118 r#"{{"center_raise":{:.4},"left_raise":{:.4},"right_raise":{:.4},"skin_tension":{:.4}}}"#,
119 state.center_raise, state.left_raise, state.right_raise, state.skin_tension
120 )
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn default_config() {
129 let cfg = default_forehead_raise_config();
130 assert!((cfg.max_raise - 1.0).abs() < 1e-6);
131 }
132
133 #[test]
134 fn new_state_neutral() {
135 let s = new_forehead_raise_state();
136 assert!(fhr_is_neutral(&s));
137 }
138
139 #[test]
140 fn set_center_clamps() {
141 let cfg = default_forehead_raise_config();
142 let mut s = new_forehead_raise_state();
143 fhr_set_center(&mut s, &cfg, 5.0);
144 assert!((s.center_raise - 1.0).abs() < 1e-6);
145 }
146
147 #[test]
148 fn set_sides() {
149 let cfg = default_forehead_raise_config();
150 let mut s = new_forehead_raise_state();
151 fhr_set_sides(&mut s, &cfg, 0.3, 0.7);
152 assert!((s.left_raise - 0.3).abs() < 1e-6);
153 assert!((s.right_raise - 0.7).abs() < 1e-6);
154 }
155
156 #[test]
157 fn set_all_equal() {
158 let cfg = default_forehead_raise_config();
159 let mut s = new_forehead_raise_state();
160 fhr_set_all(&mut s, &cfg, 0.6);
161 assert!((s.center_raise - 0.6).abs() < 1e-6);
162 assert!((s.left_raise - 0.6).abs() < 1e-6);
163 assert!((s.right_raise - 0.6).abs() < 1e-6);
164 }
165
166 #[test]
167 fn symmetry_zero_when_equal() {
168 let cfg = default_forehead_raise_config();
169 let mut s = new_forehead_raise_state();
170 fhr_set_sides(&mut s, &cfg, 0.5, 0.5);
171 assert!(fhr_symmetry(&s) < 1e-6);
172 }
173
174 #[test]
175 fn average_raise() {
176 let cfg = default_forehead_raise_config();
177 let mut s = new_forehead_raise_state();
178 fhr_set_all(&mut s, &cfg, 0.9);
179 assert!((fhr_average_raise(&s) - 0.9).abs() < 1e-6);
180 }
181
182 #[test]
183 fn reset_clears() {
184 let cfg = default_forehead_raise_config();
185 let mut s = new_forehead_raise_state();
186 fhr_set_all(&mut s, &cfg, 0.5);
187 fhr_reset(&mut s);
188 assert!(fhr_is_neutral(&s));
189 }
190
191 #[test]
192 fn blend_midpoint() {
193 let a = new_forehead_raise_state();
194 let cfg = default_forehead_raise_config();
195 let mut b = new_forehead_raise_state();
196 fhr_set_all(&mut b, &cfg, 1.0);
197 let mid = fhr_blend(&a, &b, 0.5);
198 assert!((mid.center_raise - 0.5).abs() < 1e-6);
199 }
200
201 #[test]
202 fn to_weights_count() {
203 let s = new_forehead_raise_state();
204 assert_eq!(fhr_to_weights(&s).len(), 4);
205 }
206
207 #[test]
208 fn to_json_fields() {
209 let s = new_forehead_raise_state();
210 let j = fhr_to_json(&s);
211 assert!(j.contains("center_raise"));
212 assert!(j.contains("skin_tension"));
213 }
214}