oxihuman_morph/
chin_shape_control.rs1#![allow(dead_code)]
5
6use std::f32::consts::FRAC_PI_2;
9
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct ChinShapeConfig {
13 pub min_val: f32,
14 pub max_val: f32,
15}
16
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct ChinShapeState {
20 pub projection: f32,
21 pub width: f32,
22 pub vertical: f32,
23 pub cleft_depth: f32,
24}
25
26#[allow(dead_code)]
27pub fn default_chin_shape_config() -> ChinShapeConfig {
28 ChinShapeConfig {
29 min_val: 0.0,
30 max_val: 1.0,
31 }
32}
33
34#[allow(dead_code)]
35pub fn new_chin_shape_state() -> ChinShapeState {
36 ChinShapeState {
37 projection: 0.5,
38 width: 0.5,
39 vertical: 0.5,
40 cleft_depth: 0.0,
41 }
42}
43
44#[allow(dead_code)]
45pub fn cs_set_projection(state: &mut ChinShapeState, cfg: &ChinShapeConfig, v: f32) {
46 state.projection = v.clamp(cfg.min_val, cfg.max_val);
47}
48
49#[allow(dead_code)]
50pub fn cs_set_width(state: &mut ChinShapeState, cfg: &ChinShapeConfig, v: f32) {
51 state.width = v.clamp(cfg.min_val, cfg.max_val);
52}
53
54#[allow(dead_code)]
55pub fn cs_set_vertical(state: &mut ChinShapeState, cfg: &ChinShapeConfig, v: f32) {
56 state.vertical = v.clamp(cfg.min_val, cfg.max_val);
57}
58
59#[allow(dead_code)]
60pub fn cs_set_cleft(state: &mut ChinShapeState, v: f32) {
61 state.cleft_depth = v.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn cs_reset(state: &mut ChinShapeState) {
66 *state = new_chin_shape_state();
67}
68
69#[allow(dead_code)]
70pub fn cs_profile_angle(state: &ChinShapeState) -> f32 {
71 state.projection * FRAC_PI_2
72}
73
74#[allow(dead_code)]
75pub fn cs_to_weights(state: &ChinShapeState) -> Vec<(String, f32)> {
76 vec![
77 ("chin_projection".to_string(), state.projection),
78 ("chin_width".to_string(), state.width),
79 ("chin_vertical".to_string(), state.vertical),
80 ("chin_cleft_depth".to_string(), state.cleft_depth),
81 ]
82}
83
84#[allow(dead_code)]
85pub fn cs_to_json(state: &ChinShapeState) -> String {
86 format!(
87 r#"{{"projection":{:.4},"width":{:.4},"vertical":{:.4},"cleft_depth":{:.4}}}"#,
88 state.projection, state.width, state.vertical, state.cleft_depth
89 )
90}
91
92#[allow(dead_code)]
93pub fn cs_blend(a: &ChinShapeState, b: &ChinShapeState, t: f32) -> ChinShapeState {
94 let t = t.clamp(0.0, 1.0);
95 ChinShapeState {
96 projection: a.projection + (b.projection - a.projection) * t,
97 width: a.width + (b.width - a.width) * t,
98 vertical: a.vertical + (b.vertical - a.vertical) * t,
99 cleft_depth: a.cleft_depth + (b.cleft_depth - a.cleft_depth) * t,
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_default_config() {
109 let cfg = default_chin_shape_config();
110 assert!(cfg.min_val.abs() < 1e-6);
111 }
112
113 #[test]
114 fn test_new_state() {
115 let s = new_chin_shape_state();
116 assert!((s.projection - 0.5).abs() < 1e-6);
117 }
118
119 #[test]
120 fn test_set_projection_clamps() {
121 let cfg = default_chin_shape_config();
122 let mut s = new_chin_shape_state();
123 cs_set_projection(&mut s, &cfg, 5.0);
124 assert!((s.projection - 1.0).abs() < 1e-6);
125 }
126
127 #[test]
128 fn test_set_width() {
129 let cfg = default_chin_shape_config();
130 let mut s = new_chin_shape_state();
131 cs_set_width(&mut s, &cfg, 0.7);
132 assert!((s.width - 0.7).abs() < 1e-6);
133 }
134
135 #[test]
136 fn test_set_vertical() {
137 let cfg = default_chin_shape_config();
138 let mut s = new_chin_shape_state();
139 cs_set_vertical(&mut s, &cfg, 0.3);
140 assert!((s.vertical - 0.3).abs() < 1e-6);
141 }
142
143 #[test]
144 fn test_set_cleft() {
145 let mut s = new_chin_shape_state();
146 cs_set_cleft(&mut s, 0.6);
147 assert!((s.cleft_depth - 0.6).abs() < 1e-6);
148 }
149
150 #[test]
151 fn test_reset() {
152 let cfg = default_chin_shape_config();
153 let mut s = new_chin_shape_state();
154 cs_set_projection(&mut s, &cfg, 0.9);
155 cs_reset(&mut s);
156 assert!((s.projection - 0.5).abs() < 1e-6);
157 }
158
159 #[test]
160 fn test_profile_angle() {
161 let s = new_chin_shape_state();
162 let a = cs_profile_angle(&s);
163 assert!((a - 0.5 * FRAC_PI_2).abs() < 1e-6);
164 }
165
166 #[test]
167 fn test_to_weights() {
168 let s = new_chin_shape_state();
169 assert_eq!(cs_to_weights(&s).len(), 4);
170 }
171
172 #[test]
173 fn test_blend() {
174 let a = new_chin_shape_state();
175 let mut b = new_chin_shape_state();
176 b.projection = 1.0;
177 let mid = cs_blend(&a, &b, 0.5);
178 assert!((mid.projection - 0.75).abs() < 1e-6);
179 }
180}