oxihuman_morph/
face_vertical_control.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct FaceVerticalConfig {
11 pub max_scale: f32,
12}
13
14#[allow(dead_code)]
16#[derive(Debug, Clone)]
17pub struct FaceVerticalState {
18 pub upper_third: f32,
19 pub middle_third: f32,
20 pub lower_third: f32,
21}
22
23#[allow(dead_code)]
24pub fn default_face_vertical_config() -> FaceVerticalConfig {
25 FaceVerticalConfig { max_scale: 1.5 }
26}
27
28#[allow(dead_code)]
29pub fn new_face_vertical_state() -> FaceVerticalState {
30 FaceVerticalState {
31 upper_third: 0.0,
32 middle_third: 0.0,
33 lower_third: 0.0,
34 }
35}
36
37#[allow(dead_code)]
38pub fn fv_set_upper(state: &mut FaceVerticalState, cfg: &FaceVerticalConfig, v: f32) {
39 state.upper_third = v.clamp(-cfg.max_scale, cfg.max_scale);
40}
41
42#[allow(dead_code)]
43pub fn fv_set_middle(state: &mut FaceVerticalState, cfg: &FaceVerticalConfig, v: f32) {
44 state.middle_third = v.clamp(-cfg.max_scale, cfg.max_scale);
45}
46
47#[allow(dead_code)]
48pub fn fv_set_lower(state: &mut FaceVerticalState, cfg: &FaceVerticalConfig, v: f32) {
49 state.lower_third = v.clamp(-cfg.max_scale, cfg.max_scale);
50}
51
52#[allow(dead_code)]
53pub fn fv_set_all(state: &mut FaceVerticalState, cfg: &FaceVerticalConfig, v: f32) {
54 let clamped = v.clamp(-cfg.max_scale, cfg.max_scale);
55 state.upper_third = clamped;
56 state.middle_third = clamped;
57 state.lower_third = clamped;
58}
59
60#[allow(dead_code)]
61pub fn fv_reset(state: &mut FaceVerticalState) {
62 *state = new_face_vertical_state();
63}
64
65#[allow(dead_code)]
66pub fn fv_is_neutral(state: &FaceVerticalState) -> bool {
67 state.upper_third.abs() < 1e-6
68 && state.middle_third.abs() < 1e-6
69 && state.lower_third.abs() < 1e-6
70}
71
72#[allow(dead_code)]
73pub fn fv_total_scale(state: &FaceVerticalState) -> f32 {
74 state.upper_third + state.middle_third + state.lower_third
75}
76
77#[allow(dead_code)]
78pub fn fv_blend(a: &FaceVerticalState, b: &FaceVerticalState, t: f32) -> FaceVerticalState {
79 let t = t.clamp(0.0, 1.0);
80 FaceVerticalState {
81 upper_third: a.upper_third + (b.upper_third - a.upper_third) * t,
82 middle_third: a.middle_third + (b.middle_third - a.middle_third) * t,
83 lower_third: a.lower_third + (b.lower_third - a.lower_third) * t,
84 }
85}
86
87#[allow(dead_code)]
88pub fn fv_to_weights(state: &FaceVerticalState) -> Vec<(String, f32)> {
89 vec![
90 ("face_upper_third".to_string(), state.upper_third),
91 ("face_middle_third".to_string(), state.middle_third),
92 ("face_lower_third".to_string(), state.lower_third),
93 ]
94}
95
96#[allow(dead_code)]
97pub fn fv_to_json(state: &FaceVerticalState) -> String {
98 format!(
99 r#"{{"upper_third":{:.4},"middle_third":{:.4},"lower_third":{:.4}}}"#,
100 state.upper_third, state.middle_third, state.lower_third
101 )
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn default_config() {
110 let cfg = default_face_vertical_config();
111 assert!((cfg.max_scale - 1.5).abs() < 1e-6);
112 }
113
114 #[test]
115 fn new_state_neutral() {
116 let s = new_face_vertical_state();
117 assert!(fv_is_neutral(&s));
118 }
119
120 #[test]
121 fn set_upper_clamps() {
122 let cfg = default_face_vertical_config();
123 let mut s = new_face_vertical_state();
124 fv_set_upper(&mut s, &cfg, 5.0);
125 assert!((s.upper_third - 1.5).abs() < 1e-6);
126 }
127
128 #[test]
129 fn set_middle() {
130 let cfg = default_face_vertical_config();
131 let mut s = new_face_vertical_state();
132 fv_set_middle(&mut s, &cfg, 0.3);
133 assert!((s.middle_third - 0.3).abs() < 1e-6);
134 }
135
136 #[test]
137 fn set_all() {
138 let cfg = default_face_vertical_config();
139 let mut s = new_face_vertical_state();
140 fv_set_all(&mut s, &cfg, 0.5);
141 assert!((fv_total_scale(&s) - 1.5).abs() < 1e-6);
142 }
143
144 #[test]
145 fn reset_clears() {
146 let cfg = default_face_vertical_config();
147 let mut s = new_face_vertical_state();
148 fv_set_lower(&mut s, &cfg, 0.9);
149 fv_reset(&mut s);
150 assert!(fv_is_neutral(&s));
151 }
152
153 #[test]
154 fn blend_midpoint() {
155 let a = new_face_vertical_state();
156 let cfg = default_face_vertical_config();
157 let mut b = new_face_vertical_state();
158 fv_set_upper(&mut b, &cfg, 1.0);
159 let m = fv_blend(&a, &b, 0.5);
160 assert!((m.upper_third - 0.5).abs() < 1e-6);
161 }
162
163 #[test]
164 fn to_weights_count() {
165 let s = new_face_vertical_state();
166 assert_eq!(fv_to_weights(&s).len(), 3);
167 }
168
169 #[test]
170 fn total_scale_sum() {
171 let cfg = default_face_vertical_config();
172 let mut s = new_face_vertical_state();
173 fv_set_upper(&mut s, &cfg, 0.1);
174 fv_set_middle(&mut s, &cfg, 0.2);
175 fv_set_lower(&mut s, &cfg, 0.3);
176 assert!((fv_total_scale(&s) - 0.6).abs() < 1e-6);
177 }
178
179 #[test]
180 fn to_json_fields() {
181 let s = new_face_vertical_state();
182 let j = fv_to_json(&s);
183 assert!(j.contains("upper_third"));
184 }
185}