oxihuman_morph/
face_depth_control.rs1#![allow(dead_code)]
4
5use std::f32::consts::FRAC_PI_4;
8
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct FaceDepthConfig {
12 pub max_depth: f32,
13 pub min_depth: f32,
14}
15
16impl Default for FaceDepthConfig {
17 fn default() -> Self {
18 Self {
19 max_depth: 1.0,
20 min_depth: -1.0,
21 }
22 }
23}
24
25#[allow(dead_code)]
26#[derive(Debug, Clone)]
27pub struct FaceDepthState {
28 pub upper: f32,
29 pub middle: f32,
30 pub lower: f32,
31 pub config: FaceDepthConfig,
32}
33
34#[allow(dead_code)]
35pub fn default_face_depth_config() -> FaceDepthConfig {
36 FaceDepthConfig::default()
37}
38
39#[allow(dead_code)]
40pub fn new_face_depth_state(config: FaceDepthConfig) -> FaceDepthState {
41 FaceDepthState {
42 upper: 0.0,
43 middle: 0.0,
44 lower: 0.0,
45 config,
46 }
47}
48
49#[allow(dead_code)]
50pub fn fdc_set_upper(state: &mut FaceDepthState, v: f32) {
51 state.upper = v.clamp(state.config.min_depth, state.config.max_depth);
52}
53
54#[allow(dead_code)]
55pub fn fdc_set_middle(state: &mut FaceDepthState, v: f32) {
56 state.middle = v.clamp(state.config.min_depth, state.config.max_depth);
57}
58
59#[allow(dead_code)]
60pub fn fdc_set_lower(state: &mut FaceDepthState, v: f32) {
61 state.lower = v.clamp(state.config.min_depth, state.config.max_depth);
62}
63
64#[allow(dead_code)]
65pub fn fdc_set_all(state: &mut FaceDepthState, v: f32) {
66 let v = v.clamp(state.config.min_depth, state.config.max_depth);
67 state.upper = v;
68 state.middle = v;
69 state.lower = v;
70}
71
72#[allow(dead_code)]
73pub fn fdc_reset(state: &mut FaceDepthState) {
74 state.upper = 0.0;
75 state.middle = 0.0;
76 state.lower = 0.0;
77}
78
79#[allow(dead_code)]
80pub fn fdc_is_neutral(state: &FaceDepthState) -> bool {
81 state.upper.abs() < 1e-6 && state.middle.abs() < 1e-6 && state.lower.abs() < 1e-6
82}
83
84#[allow(dead_code)]
85pub fn fdc_average(state: &FaceDepthState) -> f32 {
86 (state.upper + state.middle + state.lower) / 3.0
87}
88
89#[allow(dead_code)]
90pub fn fdc_range(state: &FaceDepthState) -> f32 {
91 let max = state.upper.max(state.middle).max(state.lower);
92 let min = state.upper.min(state.middle).min(state.lower);
93 max - min
94}
95
96#[allow(dead_code)]
97pub fn fdc_profile_angle_rad(state: &FaceDepthState) -> f32 {
98 fdc_average(state) * FRAC_PI_4
99}
100
101#[allow(dead_code)]
102pub fn fdc_to_weights(state: &FaceDepthState) -> [f32; 3] {
103 let m = state.config.max_depth;
104 let n = |v: f32| {
105 if m > 1e-9 {
106 (v / m).clamp(-1.0, 1.0)
107 } else {
108 0.0
109 }
110 };
111 [n(state.upper), n(state.middle), n(state.lower)]
112}
113
114#[allow(dead_code)]
115pub fn fdc_blend(a: &FaceDepthState, b: &FaceDepthState, t: f32) -> [f32; 3] {
116 let t = t.clamp(0.0, 1.0);
117 [
118 a.upper * (1.0 - t) + b.upper * t,
119 a.middle * (1.0 - t) + b.middle * t,
120 a.lower * (1.0 - t) + b.lower * t,
121 ]
122}
123
124#[allow(dead_code)]
125pub fn fdc_to_json(state: &FaceDepthState) -> String {
126 format!(
127 "{{\"upper\":{:.4},\"middle\":{:.4},\"lower\":{:.4}}}",
128 state.upper, state.middle, state.lower
129 )
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 #[test]
136 fn default_neutral() {
137 assert!(fdc_is_neutral(&new_face_depth_state(
138 default_face_depth_config()
139 )));
140 }
141 #[test]
142 fn set_upper_clamps() {
143 let mut s = new_face_depth_state(default_face_depth_config());
144 fdc_set_upper(&mut s, 5.0);
145 assert!((0.0..=1.0).contains(&s.upper));
146 }
147 #[test]
148 fn set_all_applies() {
149 let mut s = new_face_depth_state(default_face_depth_config());
150 fdc_set_all(&mut s, 0.5);
151 assert!((s.upper - 0.5).abs() < 1e-5 && (s.lower - 0.5).abs() < 1e-5);
152 }
153 #[test]
154 fn reset_zeroes() {
155 let mut s = new_face_depth_state(default_face_depth_config());
156 fdc_set_all(&mut s, 0.5);
157 fdc_reset(&mut s);
158 assert!(fdc_is_neutral(&s));
159 }
160 #[test]
161 fn average_three_vals() {
162 let mut s = new_face_depth_state(default_face_depth_config());
163 fdc_set_upper(&mut s, 0.3);
164 fdc_set_middle(&mut s, 0.6);
165 fdc_set_lower(&mut s, 0.9);
166 assert!((fdc_average(&s) - 0.6).abs() < 1e-5);
167 }
168 #[test]
169 fn range_max_minus_min() {
170 let mut s = new_face_depth_state(default_face_depth_config());
171 fdc_set_upper(&mut s, 0.2);
172 fdc_set_middle(&mut s, 0.2);
173 fdc_set_lower(&mut s, 0.8);
174 assert!((fdc_range(&s) - 0.6).abs() < 1e-5);
175 }
176 #[test]
177 fn profile_angle_nonneg() {
178 let s = new_face_depth_state(default_face_depth_config());
179 assert!(fdc_profile_angle_rad(&s) >= -1.0);
180 }
181 #[test]
182 fn to_weights_at_max() {
183 let mut s = new_face_depth_state(default_face_depth_config());
184 fdc_set_upper(&mut s, 1.0);
185 assert!((fdc_to_weights(&s)[0] - 1.0).abs() < 1e-5);
186 }
187 #[test]
188 fn blend_mid() {
189 let mut a = new_face_depth_state(default_face_depth_config());
190 let b = new_face_depth_state(default_face_depth_config());
191 fdc_set_upper(&mut a, 0.4);
192 let w = fdc_blend(&a, &b, 0.5);
193 assert!((w[0] - 0.2).abs() < 1e-5);
194 }
195 #[test]
196 fn to_json_has_middle() {
197 assert!(
198 fdc_to_json(&new_face_depth_state(default_face_depth_config())).contains("\"middle\"")
199 );
200 }
201}