oxihuman_morph/
head_tilt_morph.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct HeadTiltMorphConfig {
10 pub lateral_tilt: f32,
11 pub forward_tilt: f32,
12 pub axial_rotation: f32,
13}
14
15impl Default for HeadTiltMorphConfig {
16 fn default() -> Self {
17 Self {
18 lateral_tilt: 0.0,
19 forward_tilt: 0.0,
20 axial_rotation: 0.0,
21 }
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct HeadTiltMorph {
28 pub config: HeadTiltMorphConfig,
29 pub intensity: f32,
30 pub enabled: bool,
31}
32
33impl HeadTiltMorph {
34 pub fn new() -> Self {
35 Self {
36 config: HeadTiltMorphConfig::default(),
37 intensity: 0.0,
38 enabled: true,
39 }
40 }
41}
42
43impl Default for HeadTiltMorph {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49pub fn new_head_tilt_morph() -> HeadTiltMorph {
51 HeadTiltMorph::new()
52}
53
54pub fn head_tilt_set_lateral(morph: &mut HeadTiltMorph, v: f32) {
56 morph.config.lateral_tilt = v.clamp(-1.0, 1.0);
57}
58
59pub fn head_tilt_set_forward(morph: &mut HeadTiltMorph, v: f32) {
61 morph.config.forward_tilt = v.clamp(-1.0, 1.0);
62}
63
64pub fn head_tilt_set_axial(morph: &mut HeadTiltMorph, v: f32) {
66 morph.config.axial_rotation = v.clamp(-1.0, 1.0);
67}
68
69pub fn head_tilt_magnitude(morph: &HeadTiltMorph) -> f32 {
71 let l = morph.config.lateral_tilt;
72 let f = morph.config.forward_tilt;
73 let r = morph.config.axial_rotation;
74 ((l * l + f * f + r * r).sqrt() * morph.intensity).min(1.0)
75}
76
77pub fn head_tilt_to_json(morph: &HeadTiltMorph) -> String {
79 format!(
80 r#"{{"intensity":{},"lateral_tilt":{},"forward_tilt":{},"axial_rotation":{}}}"#,
81 morph.intensity,
82 morph.config.lateral_tilt,
83 morph.config.forward_tilt,
84 morph.config.axial_rotation,
85 )
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_new() {
94 let m = new_head_tilt_morph();
95 assert!((m.config.lateral_tilt - 0.0).abs() < 1e-6 );
96 }
97
98 #[test]
99 fn test_lateral_clamp_high() {
100 let mut m = new_head_tilt_morph();
101 head_tilt_set_lateral(&mut m, 5.0);
102 assert!((m.config.lateral_tilt - 1.0).abs() < 1e-6 );
103 }
104
105 #[test]
106 fn test_lateral_clamp_low() {
107 let mut m = new_head_tilt_morph();
108 head_tilt_set_lateral(&mut m, -5.0);
109 assert!((m.config.lateral_tilt - (-1.0)).abs() < 1e-6 );
110 }
111
112 #[test]
113 fn test_forward() {
114 let mut m = new_head_tilt_morph();
115 head_tilt_set_forward(&mut m, 0.6);
116 assert!((m.config.forward_tilt - 0.6).abs() < 1e-6 );
117 }
118
119 #[test]
120 fn test_axial() {
121 let mut m = new_head_tilt_morph();
122 head_tilt_set_axial(&mut m, 0.3);
123 assert!((m.config.axial_rotation - 0.3).abs() < 1e-6 );
124 }
125
126 #[test]
127 fn test_magnitude_zero() {
128 let m = new_head_tilt_morph();
129 assert!((head_tilt_magnitude(&m) - 0.0).abs() < 1e-6 );
130 }
131
132 #[test]
133 fn test_magnitude_nonzero() {
134 let mut m = new_head_tilt_morph();
135 head_tilt_set_lateral(&mut m, 1.0);
136 m.intensity = 1.0;
137 assert!(head_tilt_magnitude(&m) > 0.0 );
138 }
139
140 #[test]
141 fn test_json_key() {
142 let m = new_head_tilt_morph();
143 let j = head_tilt_to_json(&m);
144 assert!(j.contains("lateral_tilt") );
145 }
146
147 #[test]
148 fn test_default() {
149 let m = HeadTiltMorph::default();
150 assert!(m.enabled );
151 }
152}