oxihuman_morph/
body_weight_control.rs1#![allow(dead_code)]
5
6use std::f32::consts::PI;
9
10#[allow(dead_code)]
12#[derive(Debug, Clone)]
13pub struct BodyWeightConfig {
14 pub min_weight: f32,
15 pub max_weight: f32,
16 pub default_weight: f32,
17}
18
19#[allow(dead_code)]
21#[derive(Debug, Clone)]
22pub struct BodyWeightState {
23 pub overall: f32,
24 pub upper_body: f32,
25 pub lower_body: f32,
26 pub belly: f32,
27}
28
29#[allow(dead_code)]
30pub fn default_body_weight_config() -> BodyWeightConfig {
31 BodyWeightConfig {
32 min_weight: 0.0,
33 max_weight: 1.0,
34 default_weight: 0.5,
35 }
36}
37
38#[allow(dead_code)]
39pub fn new_body_weight_state() -> BodyWeightState {
40 BodyWeightState {
41 overall: 0.5,
42 upper_body: 0.5,
43 lower_body: 0.5,
44 belly: 0.3,
45 }
46}
47
48#[allow(dead_code)]
49pub fn bw_set_overall(state: &mut BodyWeightState, cfg: &BodyWeightConfig, v: f32) {
50 state.overall = v.clamp(cfg.min_weight, cfg.max_weight);
51}
52
53#[allow(dead_code)]
54pub fn bw_set_upper(state: &mut BodyWeightState, v: f32) {
55 state.upper_body = v.clamp(0.0, 1.0);
56}
57
58#[allow(dead_code)]
59pub fn bw_set_lower(state: &mut BodyWeightState, v: f32) {
60 state.lower_body = v.clamp(0.0, 1.0);
61}
62
63#[allow(dead_code)]
64pub fn bw_set_belly(state: &mut BodyWeightState, v: f32) {
65 state.belly = v.clamp(0.0, 1.0);
66}
67
68#[allow(dead_code)]
69pub fn bw_reset(state: &mut BodyWeightState) {
70 *state = new_body_weight_state();
71}
72
73#[allow(dead_code)]
74pub fn bw_to_weights(state: &BodyWeightState) -> Vec<(String, f32)> {
75 vec![
76 ("body_weight_overall".to_string(), state.overall),
77 ("body_weight_upper".to_string(), state.upper_body),
78 ("body_weight_lower".to_string(), state.lower_body),
79 ("body_weight_belly".to_string(), state.belly),
80 ]
81}
82
83#[allow(dead_code)]
84pub fn bw_to_json(state: &BodyWeightState) -> String {
85 format!(
86 r#"{{"overall":{:.4},"upper_body":{:.4},"lower_body":{:.4},"belly":{:.4}}}"#,
87 state.overall, state.upper_body, state.lower_body, state.belly
88 )
89}
90
91#[allow(dead_code)]
92pub fn bw_blend(a: &BodyWeightState, b: &BodyWeightState, t: f32) -> BodyWeightState {
93 let t = t.clamp(0.0, 1.0);
94 BodyWeightState {
95 overall: a.overall + (b.overall - a.overall) * t,
96 upper_body: a.upper_body + (b.upper_body - a.upper_body) * t,
97 lower_body: a.lower_body + (b.lower_body - a.lower_body) * t,
98 belly: a.belly + (b.belly - a.belly) * t,
99 }
100}
101
102#[allow(dead_code)]
104pub fn bw_distribution_curve(state: &BodyWeightState, t: f32) -> f32 {
105 let t = t.clamp(0.0, 1.0);
106 state.overall * (PI * t).sin() * (state.upper_body + state.lower_body) * 0.5
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_default_config() {
115 let cfg = default_body_weight_config();
116 assert!((cfg.min_weight).abs() < 1e-6);
117 assert!((cfg.max_weight - 1.0).abs() < 1e-6);
118 }
119
120 #[test]
121 fn test_new_state() {
122 let s = new_body_weight_state();
123 assert!((s.overall - 0.5).abs() < 1e-6);
124 assert!((s.belly - 0.3).abs() < 1e-6);
125 }
126
127 #[test]
128 fn test_set_overall_clamps() {
129 let cfg = default_body_weight_config();
130 let mut s = new_body_weight_state();
131 bw_set_overall(&mut s, &cfg, 5.0);
132 assert!((s.overall - 1.0).abs() < 1e-6);
133 bw_set_overall(&mut s, &cfg, -1.0);
134 assert!(s.overall.abs() < 1e-6);
135 }
136
137 #[test]
138 fn test_set_upper() {
139 let mut s = new_body_weight_state();
140 bw_set_upper(&mut s, 0.8);
141 assert!((s.upper_body - 0.8).abs() < 1e-6);
142 }
143
144 #[test]
145 fn test_set_lower() {
146 let mut s = new_body_weight_state();
147 bw_set_lower(&mut s, 0.2);
148 assert!((s.lower_body - 0.2).abs() < 1e-6);
149 }
150
151 #[test]
152 fn test_set_belly() {
153 let mut s = new_body_weight_state();
154 bw_set_belly(&mut s, 0.9);
155 assert!((s.belly - 0.9).abs() < 1e-6);
156 }
157
158 #[test]
159 fn test_reset() {
160 let cfg = default_body_weight_config();
161 let mut s = new_body_weight_state();
162 bw_set_overall(&mut s, &cfg, 0.9);
163 bw_reset(&mut s);
164 assert!((s.overall - 0.5).abs() < 1e-6);
165 }
166
167 #[test]
168 fn test_to_weights_count() {
169 let s = new_body_weight_state();
170 assert_eq!(bw_to_weights(&s).len(), 4);
171 }
172
173 #[test]
174 fn test_blend_midpoint() {
175 let a = new_body_weight_state();
176 let mut b = new_body_weight_state();
177 b.overall = 1.0;
178 let mid = bw_blend(&a, &b, 0.5);
179 assert!((mid.overall - 0.75).abs() < 1e-6);
180 }
181
182 #[test]
183 fn test_distribution_curve() {
184 let s = new_body_weight_state();
185 let v = bw_distribution_curve(&s, 0.5);
186 assert!(v > 0.0);
187 }
188}