oxihuman_morph/
cheek_tighten_control.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct CheekTightenConfig {
11 pub scale: f32,
13}
14
15impl Default for CheekTightenConfig {
16 fn default() -> Self {
17 CheekTightenConfig { scale: 1.0 }
18 }
19}
20
21#[allow(dead_code)]
23#[derive(Debug, Clone)]
24pub struct CheekTightenState {
25 left: f32,
26 right: f32,
27 vertical_bias: f32,
29 config: CheekTightenConfig,
30}
31
32pub fn default_cheek_tighten_config() -> CheekTightenConfig {
34 CheekTightenConfig::default()
35}
36
37pub fn new_cheek_tighten_state(config: CheekTightenConfig) -> CheekTightenState {
39 CheekTightenState {
40 left: 0.0,
41 right: 0.0,
42 vertical_bias: 0.0,
43 config,
44 }
45}
46
47pub fn ct_set_left(state: &mut CheekTightenState, v: f32) {
49 state.left = v.clamp(0.0, 1.0);
50}
51
52pub fn ct_set_right(state: &mut CheekTightenState, v: f32) {
54 state.right = v.clamp(0.0, 1.0);
55}
56
57pub fn ct_set_both(state: &mut CheekTightenState, v: f32) {
59 let v = v.clamp(0.0, 1.0);
60 state.left = v;
61 state.right = v;
62}
63
64pub fn ct_set_vertical_bias(state: &mut CheekTightenState, v: f32) {
66 state.vertical_bias = v.clamp(-1.0, 1.0);
67}
68
69pub fn ct_reset(state: &mut CheekTightenState) {
71 state.left = 0.0;
72 state.right = 0.0;
73 state.vertical_bias = 0.0;
74}
75
76pub fn ct_is_neutral(state: &CheekTightenState) -> bool {
78 state.left < 1e-5 && state.right < 1e-5 && state.vertical_bias.abs() < 1e-5
79}
80
81pub fn ct_asymmetry(state: &CheekTightenState) -> f32 {
83 (state.left - state.right).abs()
84}
85
86pub fn ct_average(state: &CheekTightenState) -> f32 {
88 (state.left + state.right) * 0.5
89}
90
91pub fn ct_to_weights(state: &CheekTightenState) -> [f32; 3] {
93 let s = state.config.scale;
94 [
95 (state.left * s).clamp(0.0, 1.0),
96 (state.right * s).clamp(0.0, 1.0),
97 ((state.vertical_bias * 0.5 + 0.5) * s).clamp(0.0, 1.0),
98 ]
99}
100
101pub fn ct_blend(a: &CheekTightenState, b: &CheekTightenState, t: f32) -> CheekTightenState {
103 let t = t.clamp(0.0, 1.0);
104 CheekTightenState {
105 left: a.left + (b.left - a.left) * t,
106 right: a.right + (b.right - a.right) * t,
107 vertical_bias: a.vertical_bias + (b.vertical_bias - a.vertical_bias) * t,
108 config: a.config.clone(),
109 }
110}
111
112pub fn ct_to_json(state: &CheekTightenState) -> String {
114 format!(
115 r#"{{"left":{:.4},"right":{:.4},"vertical_bias":{:.4}}}"#,
116 state.left, state.right, state.vertical_bias
117 )
118}
119
120#[cfg(test)]
124mod tests {
125 use super::*;
126
127 fn make() -> CheekTightenState {
128 new_cheek_tighten_state(default_cheek_tighten_config())
129 }
130
131 #[test]
132 fn neutral_on_creation() {
133 assert!(ct_is_neutral(&make()));
134 }
135
136 #[test]
137 fn set_left_clamps() {
138 let mut s = make();
139 ct_set_left(&mut s, 5.0);
140 assert!((s.left - 1.0).abs() < 1e-5);
141 }
142
143 #[test]
144 fn set_both_equal() {
145 let mut s = make();
146 ct_set_both(&mut s, 0.6);
147 assert!((s.left - s.right).abs() < 1e-5);
148 }
149
150 #[test]
151 fn reset_clears_all() {
152 let mut s = make();
153 ct_set_both(&mut s, 0.9);
154 ct_reset(&mut s);
155 assert!(ct_is_neutral(&s));
156 }
157
158 #[test]
159 fn asymmetry_zero_when_equal() {
160 let mut s = make();
161 ct_set_both(&mut s, 0.4);
162 assert!(ct_asymmetry(&s) < 1e-5);
163 }
164
165 #[test]
166 fn weights_in_unit_range() {
167 let mut s = make();
168 ct_set_both(&mut s, 0.7);
169 for v in ct_to_weights(&s) {
170 assert!((0.0..=1.0).contains(&v));
171 }
172 }
173
174 #[test]
175 fn blend_at_one_is_b() {
176 let mut b = make();
177 ct_set_both(&mut b, 0.8);
178 let r = ct_blend(&make(), &b, 1.0);
179 assert!((r.left - 0.8).abs() < 1e-5);
180 }
181
182 #[test]
183 fn json_contains_left_key() {
184 assert!(ct_to_json(&make()).contains("left"));
185 }
186
187 #[test]
188 fn vertical_bias_clamped() {
189 let mut s = make();
190 ct_set_vertical_bias(&mut s, 10.0);
191 assert!((s.vertical_bias - 1.0).abs() < 1e-5);
192 }
193
194 #[test]
195 fn average_is_mean_of_sides() {
196 let mut s = make();
197 ct_set_left(&mut s, 0.2);
198 ct_set_right(&mut s, 0.8);
199 assert!((ct_average(&s) - 0.5).abs() < 1e-5);
200 }
201}