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