oxihuman_morph/
neck_thickness_control.rs1#![allow(dead_code)]
7
8use std::f32::consts::PI;
9
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct NeckThicknessConfig {
13 pub min_radius: f32,
14 pub max_radius: f32,
15}
16
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct NeckThicknessState {
20 pub width: f32,
21 pub depth: f32,
22 pub length: f32,
23}
24
25#[allow(dead_code)]
26pub fn default_neck_thickness_config() -> NeckThicknessConfig {
27 NeckThicknessConfig {
28 min_radius: 0.1,
29 max_radius: 1.0,
30 }
31}
32
33#[allow(dead_code)]
34pub fn new_neck_thickness_state() -> NeckThicknessState {
35 NeckThicknessState {
36 width: 0.4,
37 depth: 0.4,
38 length: 0.5,
39 }
40}
41
42#[allow(dead_code)]
43pub fn neck_set_width(state: &mut NeckThicknessState, cfg: &NeckThicknessConfig, value: f32) {
44 state.width = value.clamp(cfg.min_radius, cfg.max_radius);
45}
46
47#[allow(dead_code)]
48pub fn neck_set_depth(state: &mut NeckThicknessState, cfg: &NeckThicknessConfig, value: f32) {
49 state.depth = value.clamp(cfg.min_radius, cfg.max_radius);
50}
51
52#[allow(dead_code)]
53pub fn neck_set_length(state: &mut NeckThicknessState, value: f32) {
54 state.length = value.clamp(0.0, 1.0);
55}
56
57#[allow(dead_code)]
58pub fn neck_reset(state: &mut NeckThicknessState) {
59 *state = new_neck_thickness_state();
60}
61
62#[allow(dead_code)]
63pub fn neck_to_weights(state: &NeckThicknessState) -> Vec<(String, f32)> {
64 vec![
65 ("neck_width".to_string(), state.width),
66 ("neck_depth".to_string(), state.depth),
67 ("neck_length".to_string(), state.length),
68 ]
69}
70
71#[allow(dead_code)]
72pub fn neck_to_json(state: &NeckThicknessState) -> String {
73 format!(
74 r#"{{"width":{:.4},"depth":{:.4},"length":{:.4}}}"#,
75 state.width, state.depth, state.length
76 )
77}
78
79#[allow(dead_code)]
80pub fn neck_clamp(state: &mut NeckThicknessState, cfg: &NeckThicknessConfig) {
81 state.width = state.width.clamp(cfg.min_radius, cfg.max_radius);
82 state.depth = state.depth.clamp(cfg.min_radius, cfg.max_radius);
83 state.length = state.length.clamp(0.0, 1.0);
84}
85
86#[allow(dead_code)]
88pub fn neck_compute_volume(state: &NeckThicknessState, height_m: f32) -> f32 {
89 PI * state.width * state.depth * (state.length * height_m)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_default_config() {
98 let cfg = default_neck_thickness_config();
99 assert!((cfg.min_radius - 0.1).abs() < 1e-6);
100 assert_eq!(cfg.max_radius, 1.0);
101 }
102
103 #[test]
104 fn test_new_state_defaults() {
105 let s = new_neck_thickness_state();
106 assert!((s.width - 0.4).abs() < 1e-6);
107 assert!((s.length - 0.5).abs() < 1e-6);
108 }
109
110 #[test]
111 fn test_set_width_clamps() {
112 let cfg = default_neck_thickness_config();
113 let mut s = new_neck_thickness_state();
114 neck_set_width(&mut s, &cfg, 0.0);
115 assert!((s.width - cfg.min_radius).abs() < 1e-6);
116 neck_set_width(&mut s, &cfg, 5.0);
117 assert_eq!(s.width, cfg.max_radius);
118 }
119
120 #[test]
121 fn test_set_depth_clamps() {
122 let cfg = default_neck_thickness_config();
123 let mut s = new_neck_thickness_state();
124 neck_set_depth(&mut s, &cfg, 0.6);
125 assert!((s.depth - 0.6).abs() < 1e-6);
126 }
127
128 #[test]
129 fn test_set_length_clamps() {
130 let mut s = new_neck_thickness_state();
131 neck_set_length(&mut s, 2.0);
132 assert_eq!(s.length, 1.0);
133 neck_set_length(&mut s, -1.0);
134 assert_eq!(s.length, 0.0);
135 }
136
137 #[test]
138 fn test_reset() {
139 let cfg = default_neck_thickness_config();
140 let mut s = new_neck_thickness_state();
141 neck_set_width(&mut s, &cfg, 0.9);
142 neck_reset(&mut s);
143 assert!((s.width - 0.4).abs() < 1e-6);
144 }
145
146 #[test]
147 fn test_to_weights_count() {
148 let s = new_neck_thickness_state();
149 assert_eq!(neck_to_weights(&s).len(), 3);
150 }
151
152 #[test]
153 fn test_to_json_has_keys() {
154 let s = new_neck_thickness_state();
155 let j = neck_to_json(&s);
156 assert!(j.contains("width"));
157 assert!(j.contains("length"));
158 }
159
160 #[test]
161 fn test_compute_volume_positive() {
162 let s = new_neck_thickness_state();
163 let v = neck_compute_volume(&s, 0.15);
164 assert!(v > 0.0);
165 }
166
167 #[test]
168 fn test_clamp_enforces_bounds() {
169 let cfg = default_neck_thickness_config();
170 let mut s = NeckThicknessState {
171 width: 0.0,
172 depth: 5.0,
173 length: -1.0,
174 };
175 neck_clamp(&mut s, &cfg);
176 assert!((s.width - cfg.min_radius).abs() < 1e-6);
177 assert_eq!(s.depth, cfg.max_radius);
178 assert_eq!(s.length, 0.0);
179 }
180}