oxihuman_morph/
android_proportion.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct AndroidProportionConfig {
10 pub upper_body_mass: f32,
11 pub belly_protrusion: f32,
12 pub hip_narrowness: f32,
13}
14
15impl Default for AndroidProportionConfig {
16 fn default() -> Self {
17 AndroidProportionConfig {
18 upper_body_mass: 0.75,
19 belly_protrusion: 0.65,
20 hip_narrowness: 0.55,
21 }
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct AndroidProportion {
28 pub intensity: f32,
29 pub config: AndroidProportionConfig,
30 pub enabled: bool,
31}
32
33pub fn new_android_proportion() -> AndroidProportion {
35 AndroidProportion {
36 intensity: 0.0,
37 config: AndroidProportionConfig::default(),
38 enabled: true,
39 }
40}
41
42pub fn andr_set_intensity(m: &mut AndroidProportion, v: f32) {
44 m.intensity = v.clamp(0.0, 1.0);
45}
46
47pub fn andr_upper_mass(m: &AndroidProportion) -> f32 {
49 m.intensity * m.config.upper_body_mass
50}
51
52pub fn andr_belly(m: &AndroidProportion) -> f32 {
54 m.intensity * m.config.belly_protrusion
55}
56
57pub fn andr_hip_narrow(m: &AndroidProportion) -> f32 {
59 m.intensity * m.config.hip_narrowness
60}
61
62pub fn andr_to_json(m: &AndroidProportion) -> String {
64 format!(
65 r#"{{"intensity":{:.3},"belly":{:.3},"enabled":{}}}"#,
66 m.intensity,
67 andr_belly(m),
68 m.enabled
69 )
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn default_zero_intensity() {
78 let m = new_android_proportion();
79 assert!((m.intensity - 0.0).abs() < 1e-6 );
80 }
81
82 #[test]
83 fn clamp_intensity() {
84 let mut m = new_android_proportion();
85 andr_set_intensity(&mut m, -1.0);
86 assert!((m.intensity - 0.0).abs() < 1e-6 );
87 }
88
89 #[test]
90 fn upper_mass_proportional() {
91 let mut m = new_android_proportion();
92 andr_set_intensity(&mut m, 1.0);
93 assert!((andr_upper_mass(&m) - m.config.upper_body_mass).abs() < 1e-6 );
94 }
95
96 #[test]
97 fn belly_increases_with_intensity() {
98 let mut m = new_android_proportion();
99 andr_set_intensity(&mut m, 0.5);
100 let b5 = andr_belly(&m);
101 andr_set_intensity(&mut m, 1.0);
102 let b10 = andr_belly(&m);
103 assert!(b10 > b5 );
104 }
105
106 #[test]
107 fn hip_narrow_zero_at_zero() {
108 let m = new_android_proportion();
109 assert!((andr_hip_narrow(&m) - 0.0).abs() < 1e-6 );
110 }
111
112 #[test]
113 fn json_has_belly() {
114 let mut m = new_android_proportion();
115 andr_set_intensity(&mut m, 0.8);
116 assert!(andr_to_json(&m).contains("belly") );
117 }
118
119 #[test]
120 fn enabled_default() {
121 let m = new_android_proportion();
122 assert!(m.enabled );
123 }
124
125 #[test]
126 fn config_upper_body_mass_positive() {
127 let m = new_android_proportion();
128 assert!(m.config.upper_body_mass > 0.0 );
129 }
130}