oxihuman_morph/
adolescent_morph.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum AdolSex {
10 Male,
11 Female,
12}
13
14#[derive(Debug, Clone)]
16pub struct AdolescentMorphConfig {
17 pub sex: AdolSex,
18 pub growth_spurt_peak_years: f32,
19}
20
21impl Default for AdolescentMorphConfig {
22 fn default() -> Self {
23 AdolescentMorphConfig {
24 sex: AdolSex::Female,
25 growth_spurt_peak_years: 14.0,
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct AdolescentMorph {
33 pub age_years: f32,
35 pub config: AdolescentMorphConfig,
36 pub enabled: bool,
37}
38
39pub fn new_adolescent_morph() -> AdolescentMorph {
41 AdolescentMorph {
42 age_years: 12.0,
43 config: AdolescentMorphConfig::default(),
44 enabled: true,
45 }
46}
47
48pub fn adol_set_age(m: &mut AdolescentMorph, years: f32) {
50 m.age_years = years.clamp(12.0, 18.0);
51}
52
53pub fn adol_progress(m: &AdolescentMorph) -> f32 {
55 (m.age_years - 12.0) / 6.0
56}
57
58pub fn adol_hip_delta(m: &AdolescentMorph) -> f32 {
60 let t = adol_progress(m);
61 match m.config.sex {
62 AdolSex::Female => 0.2 * t,
63 AdolSex::Male => 0.05 * t,
64 }
65}
66
67pub fn adol_shoulder_delta(m: &AdolescentMorph) -> f32 {
69 let t = adol_progress(m);
70 match m.config.sex {
71 AdolSex::Male => 0.2 * t,
72 AdolSex::Female => 0.08 * t,
73 }
74}
75
76pub fn adol_to_json(m: &AdolescentMorph) -> String {
78 format!(
79 r#"{{"age_years":{:.1},"enabled":{},"hip_delta":{:.3}}}"#,
80 m.age_years,
81 m.enabled,
82 adol_hip_delta(m)
83 )
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn default_age_is_twelve() {
92 let m = new_adolescent_morph();
93 assert!((m.age_years - 12.0).abs() < 1e-6 );
94 }
95
96 #[test]
97 fn clamp_upper() {
98 let mut m = new_adolescent_morph();
99 adol_set_age(&mut m, 99.0);
100 assert!((m.age_years - 18.0).abs() < 1e-6 );
101 }
102
103 #[test]
104 fn progress_at_18() {
105 let mut m = new_adolescent_morph();
106 adol_set_age(&mut m, 18.0);
107 assert!((adol_progress(&m) - 1.0).abs() < 1e-6 );
108 }
109
110 #[test]
111 fn female_hip_delta_larger_than_male() {
112 let mut mf = new_adolescent_morph();
113 adol_set_age(&mut mf, 18.0);
114 let mut mm = new_adolescent_morph();
115 mm.config.sex = AdolSex::Male;
116 adol_set_age(&mut mm, 18.0);
117 assert!(adol_hip_delta(&mf) > adol_hip_delta(&mm) );
118 }
119
120 #[test]
121 fn male_shoulder_delta_larger() {
122 let mut mm = new_adolescent_morph();
123 mm.config.sex = AdolSex::Male;
124 adol_set_age(&mut mm, 18.0);
125 let mut mf = new_adolescent_morph();
126 adol_set_age(&mut mf, 18.0);
127 assert!(adol_shoulder_delta(&mm) > adol_shoulder_delta(&mf) );
128 }
129
130 #[test]
131 fn json_contains_age() {
132 let mut m = new_adolescent_morph();
133 adol_set_age(&mut m, 15.0);
134 assert!(adol_to_json(&m).contains("15.0") );
135 }
136
137 #[test]
138 fn enabled_default_true() {
139 let m = new_adolescent_morph();
140 assert!(m.enabled );
141 }
142
143 #[test]
144 fn hip_delta_zero_at_start() {
145 let mut m = new_adolescent_morph();
146 adol_set_age(&mut m, 12.0);
147 assert!((adol_hip_delta(&m) - 0.0).abs() < 1e-6 );
148 }
149}