oxihuman_morph/
posture_morph.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct PostureMorphConfig {
10 pub sway_amplitude: f32,
11 pub forward_lean: f32,
12 pub lateral_lean: f32,
13}
14
15impl Default for PostureMorphConfig {
16 fn default() -> Self {
17 Self {
18 sway_amplitude: 0.0,
19 forward_lean: 0.0,
20 lateral_lean: 0.0,
21 }
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct PostureMorph {
28 pub config: PostureMorphConfig,
29 pub intensity: f32,
30 pub enabled: bool,
31}
32
33impl PostureMorph {
34 pub fn new() -> Self {
35 Self {
36 config: PostureMorphConfig::default(),
37 intensity: 0.0,
38 enabled: true,
39 }
40 }
41}
42
43impl Default for PostureMorph {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49pub fn new_posture_morph() -> PostureMorph {
51 PostureMorph::new()
52}
53
54pub fn posture_set_sway(morph: &mut PostureMorph, amplitude: f32) {
56 morph.config.sway_amplitude = amplitude.clamp(0.0, 1.0);
57}
58
59pub fn posture_set_forward_lean(morph: &mut PostureMorph, lean: f32) {
61 morph.config.forward_lean = lean.clamp(-1.0, 1.0);
62}
63
64pub fn posture_set_lateral_lean(morph: &mut PostureMorph, lean: f32) {
66 morph.config.lateral_lean = lean.clamp(-1.0, 1.0);
67}
68
69#[allow(clippy::needless_range_loop)]
71pub fn posture_apply_weights(morph: &PostureMorph, weights: &mut [f32]) {
72 let scale = morph.intensity * morph.config.sway_amplitude;
73 for i in 0..weights.len() {
74 weights[i] *= scale;
75 }
76}
77
78pub fn posture_to_json(morph: &PostureMorph) -> String {
80 format!(
81 r#"{{"intensity":{},"sway":{},"forward_lean":{},"lateral_lean":{}}}"#,
82 morph.intensity,
83 morph.config.sway_amplitude,
84 morph.config.forward_lean,
85 morph.config.lateral_lean,
86 )
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_new_posture_morph() {
95 let m = new_posture_morph();
96 assert!((m.intensity - 0.0).abs() < 1e-6 );
97 }
98
99 #[test]
100 fn test_set_sway_clamp() {
101 let mut m = new_posture_morph();
102 posture_set_sway(&mut m, 2.5);
103 assert!((m.config.sway_amplitude - 1.0).abs() < 1e-6 );
104 }
105
106 #[test]
107 fn test_set_sway_negative() {
108 let mut m = new_posture_morph();
109 posture_set_sway(&mut m, -0.5);
110 assert!((m.config.sway_amplitude - 0.0).abs() < 1e-6 );
111 }
112
113 #[test]
114 fn test_forward_lean() {
115 let mut m = new_posture_morph();
116 posture_set_forward_lean(&mut m, 0.3);
117 assert!((m.config.forward_lean - 0.3).abs() < 1e-6 );
118 }
119
120 #[test]
121 fn test_lateral_lean_clamp() {
122 let mut m = new_posture_morph();
123 posture_set_lateral_lean(&mut m, 5.0);
124 assert!((m.config.lateral_lean - 1.0).abs() < 1e-6 );
125 }
126
127 #[test]
128 fn test_apply_weights_empty() {
129 let m = new_posture_morph();
130 let mut w: Vec<f32> = vec![];
131 posture_apply_weights(&m, &mut w);
132 assert!(w.is_empty() );
133 }
134
135 #[test]
136 fn test_apply_weights_zero_intensity() {
137 let mut m = new_posture_morph();
138 posture_set_sway(&mut m, 1.0);
139 m.intensity = 0.0;
140 let mut w = vec![1.0f32, 1.0];
141 posture_apply_weights(&m, &mut w);
142 assert!((w[0] - 0.0).abs() < 1e-6 );
143 }
144
145 #[test]
146 fn test_json_output() {
147 let m = new_posture_morph();
148 let j = posture_to_json(&m);
149 assert!(j.contains("intensity") );
150 }
151
152 #[test]
153 fn test_default_enabled() {
154 let m = PostureMorph::default();
155 assert!(m.enabled );
156 }
157
158 #[test]
159 fn test_clone() {
160 let m = new_posture_morph();
161 let m2 = m.clone();
162 assert!((m2.intensity - m.intensity).abs() < 1e-6 );
163 }
164}