oxihuman_morph/
ear_concha_control.rs1#![allow(dead_code)]
5
6use std::f32::consts::PI;
9
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct EarConchaConfig {
13 pub min_depth: f32,
14 pub max_depth: f32,
15}
16
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct EarConchaState {
20 pub depth: f32,
21 pub width: f32,
22 pub symmetry: f32,
23}
24
25#[allow(dead_code)]
26pub fn default_ear_concha_config() -> EarConchaConfig {
27 EarConchaConfig {
28 min_depth: 0.0,
29 max_depth: 1.0,
30 }
31}
32
33#[allow(dead_code)]
34pub fn new_ear_concha_state() -> EarConchaState {
35 EarConchaState {
36 depth: 0.5,
37 width: 0.5,
38 symmetry: 1.0,
39 }
40}
41
42#[allow(dead_code)]
43pub fn ec_set_depth(state: &mut EarConchaState, cfg: &EarConchaConfig, v: f32) {
44 state.depth = v.clamp(cfg.min_depth, cfg.max_depth);
45}
46
47#[allow(dead_code)]
48pub fn ec_set_width(state: &mut EarConchaState, v: f32) {
49 state.width = v.clamp(0.0, 1.0);
50}
51
52#[allow(dead_code)]
53pub fn ec_set_symmetry(state: &mut EarConchaState, v: f32) {
54 state.symmetry = v.clamp(0.0, 1.0);
55}
56
57#[allow(dead_code)]
58pub fn ec_reset(state: &mut EarConchaState) {
59 *state = new_ear_concha_state();
60}
61
62#[allow(dead_code)]
63pub fn ec_cavity_volume(state: &EarConchaState) -> f32 {
64 PI * state.width * state.width * state.depth * 0.25
65}
66
67#[allow(dead_code)]
68pub fn ec_to_weights(state: &EarConchaState) -> Vec<(String, f32)> {
69 vec![
70 ("ear_concha_depth".to_string(), state.depth),
71 ("ear_concha_width".to_string(), state.width),
72 ("ear_concha_symmetry".to_string(), state.symmetry),
73 ]
74}
75
76#[allow(dead_code)]
77pub fn ec_to_json(state: &EarConchaState) -> String {
78 format!(
79 r#"{{"depth":{:.4},"width":{:.4},"symmetry":{:.4}}}"#,
80 state.depth, state.width, state.symmetry
81 )
82}
83
84#[allow(dead_code)]
85pub fn ec_blend(a: &EarConchaState, b: &EarConchaState, t: f32) -> EarConchaState {
86 let t = t.clamp(0.0, 1.0);
87 EarConchaState {
88 depth: a.depth + (b.depth - a.depth) * t,
89 width: a.width + (b.width - a.width) * t,
90 symmetry: a.symmetry + (b.symmetry - a.symmetry) * t,
91 }
92}
93
94#[allow(dead_code)]
95pub fn ec_effective_left(state: &EarConchaState) -> f32 {
96 state.depth * state.symmetry
97}
98
99#[allow(dead_code)]
100pub fn ec_effective_right(state: &EarConchaState) -> f32 {
101 state.depth * (2.0 - state.symmetry)
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_default_config() {
110 let cfg = default_ear_concha_config();
111 assert!(cfg.min_depth.abs() < 1e-6);
112 }
113
114 #[test]
115 fn test_new_state() {
116 let s = new_ear_concha_state();
117 assert!((s.depth - 0.5).abs() < 1e-6);
118 }
119
120 #[test]
121 fn test_set_depth_clamps() {
122 let cfg = default_ear_concha_config();
123 let mut s = new_ear_concha_state();
124 ec_set_depth(&mut s, &cfg, 5.0);
125 assert!((s.depth - 1.0).abs() < 1e-6);
126 }
127
128 #[test]
129 fn test_set_width() {
130 let mut s = new_ear_concha_state();
131 ec_set_width(&mut s, 0.8);
132 assert!((s.width - 0.8).abs() < 1e-6);
133 }
134
135 #[test]
136 fn test_set_symmetry() {
137 let mut s = new_ear_concha_state();
138 ec_set_symmetry(&mut s, 0.7);
139 assert!((s.symmetry - 0.7).abs() < 1e-6);
140 }
141
142 #[test]
143 fn test_reset() {
144 let cfg = default_ear_concha_config();
145 let mut s = new_ear_concha_state();
146 ec_set_depth(&mut s, &cfg, 0.9);
147 ec_reset(&mut s);
148 assert!((s.depth - 0.5).abs() < 1e-6);
149 }
150
151 #[test]
152 fn test_cavity_volume() {
153 let s = new_ear_concha_state();
154 assert!(ec_cavity_volume(&s) > 0.0);
155 }
156
157 #[test]
158 fn test_to_weights() {
159 let s = new_ear_concha_state();
160 assert_eq!(ec_to_weights(&s).len(), 3);
161 }
162
163 #[test]
164 fn test_blend() {
165 let a = new_ear_concha_state();
166 let mut b = new_ear_concha_state();
167 b.depth = 1.0;
168 let mid = ec_blend(&a, &b, 0.5);
169 assert!((mid.depth - 0.75).abs() < 1e-6);
170 }
171
172 #[test]
173 fn test_effective_sides() {
174 let s = new_ear_concha_state();
175 assert!((ec_effective_left(&s) - ec_effective_right(&s)).abs() < 1e-6);
176 }
177}