oxihuman_morph/
forehead_vein_control.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Clone, Debug)]
10pub struct ForeheadVeinState {
11 pub temple_left: f32,
13 pub temple_right: f32,
14 pub central: f32,
16}
17
18#[allow(dead_code)]
20#[derive(Clone, Debug)]
21pub struct ForeheadVeinConfig {
22 pub max_prominence: f32,
23}
24
25impl Default for ForeheadVeinConfig {
26 fn default() -> Self {
27 Self {
28 max_prominence: 1.0,
29 }
30 }
31}
32impl Default for ForeheadVeinState {
33 fn default() -> Self {
34 Self {
35 temple_left: 0.0,
36 temple_right: 0.0,
37 central: 0.0,
38 }
39 }
40}
41
42#[allow(dead_code)]
43pub fn new_forehead_vein_state() -> ForeheadVeinState {
44 ForeheadVeinState::default()
45}
46
47#[allow(dead_code)]
48pub fn default_forehead_vein_config() -> ForeheadVeinConfig {
49 ForeheadVeinConfig::default()
50}
51
52#[allow(dead_code)]
53pub fn fv_set_temple(
54 state: &mut ForeheadVeinState,
55 cfg: &ForeheadVeinConfig,
56 left: f32,
57 right: f32,
58) {
59 state.temple_left = left.clamp(0.0, cfg.max_prominence);
60 state.temple_right = right.clamp(0.0, cfg.max_prominence);
61}
62
63#[allow(dead_code)]
64pub fn fv_set_central(state: &mut ForeheadVeinState, cfg: &ForeheadVeinConfig, v: f32) {
65 state.central = v.clamp(0.0, cfg.max_prominence);
66}
67
68#[allow(dead_code)]
69pub fn fv_reset(state: &mut ForeheadVeinState) {
70 *state = ForeheadVeinState::default();
71}
72
73#[allow(dead_code)]
74pub fn fv_is_neutral(state: &ForeheadVeinState) -> bool {
75 state.temple_left < 1e-4 && state.temple_right < 1e-4 && state.central < 1e-4
76}
77
78#[allow(dead_code)]
79pub fn fv_blend(a: &ForeheadVeinState, b: &ForeheadVeinState, t: f32) -> ForeheadVeinState {
80 let t = t.clamp(0.0, 1.0);
81 ForeheadVeinState {
82 temple_left: a.temple_left + (b.temple_left - a.temple_left) * t,
83 temple_right: a.temple_right + (b.temple_right - a.temple_right) * t,
84 central: a.central + (b.central - a.central) * t,
85 }
86}
87
88#[allow(dead_code)]
89pub fn fv_total_prominence(state: &ForeheadVeinState) -> f32 {
90 (state.temple_left + state.temple_right + state.central) / 3.0
91}
92
93#[allow(dead_code)]
94pub fn fv_to_weights(state: &ForeheadVeinState) -> [f32; 3] {
95 [state.temple_left, state.temple_right, state.central]
96}
97
98#[allow(dead_code)]
99pub fn fv_to_json(state: &ForeheadVeinState) -> String {
100 format!(
101 "{{\"temple_l\":{:.4},\"temple_r\":{:.4},\"central\":{:.4}}}",
102 state.temple_left, state.temple_right, state.central
103 )
104}
105
106#[allow(dead_code)]
107pub fn fv_symmetry(state: &ForeheadVeinState) -> f32 {
108 1.0 - (state.temple_left - state.temple_right).abs().min(1.0)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn default_neutral() {
117 assert!(fv_is_neutral(&new_forehead_vein_state()));
118 }
119
120 #[test]
121 fn set_temple_clamps() {
122 let mut s = new_forehead_vein_state();
123 let cfg = default_forehead_vein_config();
124 fv_set_temple(&mut s, &cfg, 2.0, -1.0);
125 assert!(s.temple_left <= cfg.max_prominence);
126 assert!(s.temple_right >= 0.0);
127 }
128
129 #[test]
130 fn central_clamp() {
131 let mut s = new_forehead_vein_state();
132 let cfg = default_forehead_vein_config();
133 fv_set_central(&mut s, &cfg, 5.0);
134 assert!(s.central <= cfg.max_prominence);
135 }
136
137 #[test]
138 fn reset_neutral() {
139 let mut s = new_forehead_vein_state();
140 let cfg = default_forehead_vein_config();
141 fv_set_temple(&mut s, &cfg, 0.5, 0.5);
142 fv_reset(&mut s);
143 assert!(fv_is_neutral(&s));
144 }
145
146 #[test]
147 fn blend_midpoint() {
148 let cfg = default_forehead_vein_config();
149 let mut a = new_forehead_vein_state();
150 let mut b = new_forehead_vein_state();
151 fv_set_central(&mut a, &cfg, 0.0);
152 fv_set_central(&mut b, &cfg, 1.0);
153 let m = fv_blend(&a, &b, 0.5);
154 assert!((m.central - 0.5).abs() < 1e-4);
155 }
156
157 #[test]
158 fn total_prominence_zero() {
159 assert!((fv_total_prominence(&new_forehead_vein_state())).abs() < 1e-5);
160 }
161
162 #[test]
163 fn symmetry_one_equal() {
164 let s = new_forehead_vein_state();
165 assert!((fv_symmetry(&s) - 1.0).abs() < 1e-5);
166 }
167
168 #[test]
169 fn weights_len() {
170 assert_eq!(fv_to_weights(&new_forehead_vein_state()).len(), 3);
171 }
172
173 #[test]
174 fn json_has_central() {
175 assert!(fv_to_json(&new_forehead_vein_state()).contains("central"));
176 }
177
178 #[test]
179 fn not_neutral_after_set() {
180 let mut s = new_forehead_vein_state();
181 let cfg = default_forehead_vein_config();
182 fv_set_central(&mut s, &cfg, 0.5);
183 assert!(!fv_is_neutral(&s));
184 }
185}