oxihuman_morph/
philtrum_control.rs1#![allow(dead_code)]
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct PhiltrumConfig {
11 pub max_depth: f32,
12 pub max_width: f32,
13}
14
15#[allow(dead_code)]
16#[derive(Debug, Clone)]
17pub struct PhiltrumState {
18 pub depth: f32,
19 pub width: f32,
20 pub length: f32,
21}
22
23#[allow(dead_code)]
24pub fn default_philtrum_config() -> PhiltrumConfig {
25 PhiltrumConfig {
26 max_depth: 1.0,
27 max_width: 1.0,
28 }
29}
30
31#[allow(dead_code)]
32pub fn new_philtrum_state() -> PhiltrumState {
33 PhiltrumState {
34 depth: 0.0,
35 width: 0.5,
36 length: 0.5,
37 }
38}
39
40#[allow(dead_code)]
41pub fn philtrum_set_depth(state: &mut PhiltrumState, cfg: &PhiltrumConfig, value: f32) {
42 state.depth = value.clamp(0.0, cfg.max_depth);
43}
44
45#[allow(dead_code)]
46pub fn philtrum_set_width(state: &mut PhiltrumState, cfg: &PhiltrumConfig, value: f32) {
47 state.width = value.clamp(0.0, cfg.max_width);
48}
49
50#[allow(dead_code)]
51pub fn philtrum_set_length(state: &mut PhiltrumState, value: f32) {
52 state.length = value.clamp(0.0, 1.0);
53}
54
55#[allow(dead_code)]
56pub fn philtrum_reset(state: &mut PhiltrumState) {
57 *state = new_philtrum_state();
58}
59
60#[allow(dead_code)]
61pub fn philtrum_to_weights(state: &PhiltrumState) -> Vec<(String, f32)> {
62 vec![
63 ("philtrum_depth".to_string(), state.depth),
64 ("philtrum_width".to_string(), state.width),
65 ("philtrum_length".to_string(), state.length),
66 ]
67}
68
69#[allow(dead_code)]
70pub fn philtrum_to_json(state: &PhiltrumState) -> String {
71 format!(
72 r#"{{"depth":{:.4},"width":{:.4},"length":{:.4}}}"#,
73 state.depth, state.width, state.length
74 )
75}
76
77#[allow(dead_code)]
78pub fn philtrum_clamp(state: &mut PhiltrumState, cfg: &PhiltrumConfig) {
79 state.depth = state.depth.clamp(0.0, cfg.max_depth);
80 state.width = state.width.clamp(0.0, cfg.max_width);
81 state.length = state.length.clamp(0.0, 1.0);
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_default_config() {
90 let cfg = default_philtrum_config();
91 assert_eq!(cfg.max_depth, 1.0);
92 assert_eq!(cfg.max_width, 1.0);
93 }
94
95 #[test]
96 fn test_new_state_defaults() {
97 let s = new_philtrum_state();
98 assert_eq!(s.depth, 0.0);
99 assert!((s.width - 0.5).abs() < 1e-6);
100 assert!((s.length - 0.5).abs() < 1e-6);
101 }
102
103 #[test]
104 fn test_set_depth_clamps() {
105 let cfg = default_philtrum_config();
106 let mut s = new_philtrum_state();
107 philtrum_set_depth(&mut s, &cfg, 2.0);
108 assert_eq!(s.depth, 1.0);
109 philtrum_set_depth(&mut s, &cfg, -0.5);
110 assert_eq!(s.depth, 0.0);
111 }
112
113 #[test]
114 fn test_set_width_clamps() {
115 let cfg = default_philtrum_config();
116 let mut s = new_philtrum_state();
117 philtrum_set_width(&mut s, &cfg, 0.6);
118 assert!((s.width - 0.6).abs() < 1e-6);
119 }
120
121 #[test]
122 fn test_set_length_clamps() {
123 let mut s = new_philtrum_state();
124 philtrum_set_length(&mut s, 5.0);
125 assert_eq!(s.length, 1.0);
126 philtrum_set_length(&mut s, -1.0);
127 assert_eq!(s.length, 0.0);
128 }
129
130 #[test]
131 fn test_reset() {
132 let cfg = default_philtrum_config();
133 let mut s = new_philtrum_state();
134 philtrum_set_depth(&mut s, &cfg, 0.9);
135 philtrum_reset(&mut s);
136 assert_eq!(s.depth, 0.0);
137 }
138
139 #[test]
140 fn test_to_weights_count() {
141 let s = new_philtrum_state();
142 let w = philtrum_to_weights(&s);
143 assert_eq!(w.len(), 3);
144 }
145
146 #[test]
147 fn test_to_json_contains_keys() {
148 let s = new_philtrum_state();
149 let j = philtrum_to_json(&s);
150 assert!(j.contains("depth"));
151 assert!(j.contains("length"));
152 }
153
154 #[test]
155 fn test_clamp_enforces_bounds() {
156 let cfg = default_philtrum_config();
157 let mut s = PhiltrumState {
158 depth: 3.0,
159 width: -1.0,
160 length: 2.0,
161 };
162 philtrum_clamp(&mut s, &cfg);
163 assert_eq!(s.depth, 1.0);
164 assert_eq!(s.width, 0.0);
165 assert_eq!(s.length, 1.0);
166 }
167}
168
169#[allow(dead_code)]
173#[derive(Debug, Clone, PartialEq)]
174pub struct PhiltrumControl {
175 pub depth: f32,
177 pub width: f32,
179 pub length: f32,
181}
182
183#[allow(dead_code)]
185pub fn default_philtrum_control() -> PhiltrumControl {
186 PhiltrumControl {
187 depth: 0.5,
188 width: 0.5,
189 length: 0.5,
190 }
191}
192
193#[allow(dead_code)]
195pub fn apply_philtrum_control(weights: &mut [f32], pc: &PhiltrumControl) {
196 if !weights.is_empty() {
197 weights[0] = pc.depth;
198 }
199 if weights.len() > 1 {
200 weights[1] = pc.width;
201 }
202 if weights.len() > 2 {
203 weights[2] = pc.length;
204 }
205}
206
207#[allow(dead_code)]
209pub fn philtrum_blend(a: &PhiltrumControl, b: &PhiltrumControl, t: f32) -> PhiltrumControl {
210 let t = t.clamp(0.0, 1.0);
211 PhiltrumControl {
212 depth: a.depth + (b.depth - a.depth) * t,
213 width: a.width + (b.width - a.width) * t,
214 length: a.length + (b.length - a.length) * t,
215 }
216}