oxihuman_morph/
muscle_tone_morph.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum MuscleGroup {
10 Arms,
11 Shoulders,
12 Chest,
13 Abdomen,
14 Back,
15 Legs,
16 Glutes,
17 Neck,
18}
19
20#[derive(Debug, Clone)]
22pub struct MuscleToneMorph {
23 pub global_tone: f32,
24 pub group_overrides: Vec<(MuscleGroup, f32)>,
25 pub definition: f32,
26 pub morph_count: usize,
27 pub enabled: bool,
28}
29
30impl MuscleToneMorph {
31 pub fn new(morph_count: usize) -> Self {
32 MuscleToneMorph {
33 global_tone: 0.5,
34 group_overrides: Vec::new(),
35 definition: 0.5,
36 morph_count,
37 enabled: true,
38 }
39 }
40}
41
42pub fn new_muscle_tone_morph(morph_count: usize) -> MuscleToneMorph {
44 MuscleToneMorph::new(morph_count)
45}
46
47pub fn mtm_set_tone(morph: &mut MuscleToneMorph, tone: f32) {
49 morph.global_tone = tone.clamp(0.0, 1.0);
50}
51
52pub fn mtm_set_definition(morph: &mut MuscleToneMorph, definition: f32) {
54 morph.definition = definition.clamp(0.0, 1.0);
55}
56
57pub fn mtm_set_group_override(morph: &mut MuscleToneMorph, group: MuscleGroup, tone: f32) {
59 let clamped = tone.clamp(0.0, 1.0);
60 if let Some(entry) = morph.group_overrides.iter_mut().find(|(g, _)| *g == group) {
61 entry.1 = clamped;
62 } else {
63 morph.group_overrides.push((group, clamped));
64 }
65}
66
67pub fn mtm_evaluate(morph: &MuscleToneMorph) -> Vec<f32> {
69 if !morph.enabled || morph.morph_count == 0 {
71 return vec![];
72 }
73 let w = morph.global_tone * morph.definition;
74 vec![w; morph.morph_count]
75}
76
77pub fn mtm_set_enabled(morph: &mut MuscleToneMorph, enabled: bool) {
79 morph.enabled = enabled;
80}
81
82pub fn mtm_override_count(morph: &MuscleToneMorph) -> usize {
84 morph.group_overrides.len()
85}
86
87pub fn mtm_to_json(morph: &MuscleToneMorph) -> String {
89 format!(
90 r#"{{"global_tone":{},"definition":{},"morph_count":{},"enabled":{}}}"#,
91 morph.global_tone, morph.definition, morph.morph_count, morph.enabled
92 )
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_default_tone() {
101 let m = new_muscle_tone_morph(8);
102 assert!((m.global_tone - 0.5).abs() < 1e-6 );
103 }
104
105 #[test]
106 fn test_set_tone_clamps() {
107 let mut m = new_muscle_tone_morph(4);
108 mtm_set_tone(&mut m, 1.5);
109 assert!((m.global_tone - 1.0).abs() < 1e-6 );
110 }
111
112 #[test]
113 fn test_set_definition() {
114 let mut m = new_muscle_tone_morph(4);
115 mtm_set_definition(&mut m, 0.8);
116 assert!((m.definition - 0.8).abs() < 1e-5 );
117 }
118
119 #[test]
120 fn test_group_override_added() {
121 let mut m = new_muscle_tone_morph(4);
122 mtm_set_group_override(&mut m, MuscleGroup::Arms, 0.9);
123 assert_eq!(
124 mtm_override_count(&m),
125 1 );
127 }
128
129 #[test]
130 fn test_group_override_updated() {
131 let mut m = new_muscle_tone_morph(4);
132 mtm_set_group_override(&mut m, MuscleGroup::Arms, 0.5);
133 mtm_set_group_override(&mut m, MuscleGroup::Arms, 0.9);
134 assert_eq!(
135 mtm_override_count(&m),
136 1 );
138 }
139
140 #[test]
141 fn test_evaluate_length() {
142 let m = new_muscle_tone_morph(5);
143 let out = mtm_evaluate(&m);
144 assert_eq!(out.len(), 5 );
145 }
146
147 #[test]
148 fn test_evaluate_disabled() {
149 let mut m = new_muscle_tone_morph(4);
150 mtm_set_enabled(&mut m, false);
151 assert!(mtm_evaluate(&m).is_empty() );
152 }
153
154 #[test]
155 fn test_to_json_has_fields() {
156 let m = new_muscle_tone_morph(4);
157 let j = mtm_to_json(&m);
158 assert!(j.contains("\"global_tone\"") );
159 }
160
161 #[test]
162 fn test_enabled_default() {
163 let m = new_muscle_tone_morph(4);
164 assert!(m.enabled );
165 }
166
167 #[test]
168 fn test_evaluate_product() {
169 let mut m = new_muscle_tone_morph(2);
170 mtm_set_tone(&mut m, 0.4);
171 mtm_set_definition(&mut m, 0.5);
172 let out = mtm_evaluate(&m);
173 assert!((out[0] - 0.2).abs() < 1e-5 );
174 }
175}