oxihuman_morph/
subcutaneous_fat_morph.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum FatPattern {
10 Android,
11 Gynoid,
12 Uniform,
13}
14
15#[derive(Debug, Clone)]
17pub struct SubcutaneousFatMorph {
18 pub total_fat: f32,
19 pub pattern: FatPattern,
20 pub visceral_ratio: f32,
21 pub morph_count: usize,
22 pub enabled: bool,
23}
24
25impl SubcutaneousFatMorph {
26 pub fn new(morph_count: usize) -> Self {
27 SubcutaneousFatMorph {
28 total_fat: 0.3,
29 pattern: FatPattern::Uniform,
30 visceral_ratio: 0.2,
31 morph_count,
32 enabled: true,
33 }
34 }
35}
36
37pub fn new_subcutaneous_fat_morph(morph_count: usize) -> SubcutaneousFatMorph {
39 SubcutaneousFatMorph::new(morph_count)
40}
41
42pub fn sfm_set_fat(morph: &mut SubcutaneousFatMorph, fat: f32) {
44 morph.total_fat = fat.clamp(0.0, 1.0);
45}
46
47pub fn sfm_set_pattern(morph: &mut SubcutaneousFatMorph, pattern: FatPattern) {
49 morph.pattern = pattern;
50}
51
52pub fn sfm_set_visceral_ratio(morph: &mut SubcutaneousFatMorph, ratio: f32) {
54 morph.visceral_ratio = ratio.clamp(0.0, 1.0);
55}
56
57pub fn sfm_evaluate(morph: &SubcutaneousFatMorph) -> Vec<f32> {
59 if !morph.enabled || morph.morph_count == 0 {
61 return vec![];
62 }
63 let scale = match morph.pattern {
64 FatPattern::Android => 0.9,
65 FatPattern::Gynoid => 0.85,
66 FatPattern::Uniform => 1.0,
67 };
68 vec![morph.total_fat * scale; morph.morph_count]
69}
70
71pub fn sfm_set_enabled(morph: &mut SubcutaneousFatMorph, enabled: bool) {
73 morph.enabled = enabled;
74}
75
76pub fn sfm_to_json(morph: &SubcutaneousFatMorph) -> String {
78 let pat = match morph.pattern {
79 FatPattern::Android => "android",
80 FatPattern::Gynoid => "gynoid",
81 FatPattern::Uniform => "uniform",
82 };
83 format!(
84 r#"{{"total_fat":{},"pattern":"{}","visceral_ratio":{},"enabled":{}}}"#,
85 morph.total_fat, pat, morph.visceral_ratio, morph.enabled
86 )
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_default_fat() {
95 let m = new_subcutaneous_fat_morph(4);
96 assert!((m.total_fat - 0.3).abs() < 1e-6 );
97 }
98
99 #[test]
100 fn test_set_fat_clamps() {
101 let mut m = new_subcutaneous_fat_morph(4);
102 sfm_set_fat(&mut m, 1.5);
103 assert!((m.total_fat - 1.0).abs() < 1e-6 );
104 }
105
106 #[test]
107 fn test_set_pattern() {
108 let mut m = new_subcutaneous_fat_morph(4);
109 sfm_set_pattern(&mut m, FatPattern::Gynoid);
110 assert_eq!(m.pattern, FatPattern::Gynoid );
111 }
112
113 #[test]
114 fn test_visceral_ratio_clamped() {
115 let mut m = new_subcutaneous_fat_morph(4);
116 sfm_set_visceral_ratio(&mut m, -0.1);
117 assert!((m.visceral_ratio).abs() < 1e-6 );
118 }
119
120 #[test]
121 fn test_evaluate_length() {
122 let m = new_subcutaneous_fat_morph(5);
123 assert_eq!(
124 sfm_evaluate(&m).len(),
125 5 );
127 }
128
129 #[test]
130 fn test_evaluate_disabled() {
131 let mut m = new_subcutaneous_fat_morph(4);
132 sfm_set_enabled(&mut m, false);
133 assert!(sfm_evaluate(&m).is_empty() );
134 }
135
136 #[test]
137 fn test_to_json_has_pattern() {
138 let m = new_subcutaneous_fat_morph(4);
139 let j = sfm_to_json(&m);
140 assert!(j.contains("\"pattern\"") );
141 }
142
143 #[test]
144 fn test_enabled_default() {
145 let m = new_subcutaneous_fat_morph(4);
146 assert!(m.enabled );
147 }
148
149 #[test]
150 fn test_android_scale() {
151 let mut m = new_subcutaneous_fat_morph(2);
152 sfm_set_fat(&mut m, 1.0);
153 sfm_set_pattern(&mut m, FatPattern::Android);
154 let out = sfm_evaluate(&m);
155 assert!((out[0] - 0.9).abs() < 1e-5 );
156 }
157}