oxihuman_morph/
corrective_pose_driver.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct CorrectivePoseDriverConfig {
10 pub threshold_rad: f32,
12 pub max_weight: f32,
14}
15
16impl Default for CorrectivePoseDriverConfig {
17 fn default() -> Self {
18 CorrectivePoseDriverConfig {
19 threshold_rad: 0.3,
20 max_weight: 1.0,
21 }
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct CorrectivePoseDriver {
28 pub joint_name: String,
29 pub target_shape: String,
30 pub config: CorrectivePoseDriverConfig,
31 pub current_weight: f32,
32}
33
34impl CorrectivePoseDriver {
35 pub fn new(joint_name: &str, target_shape: &str) -> Self {
36 CorrectivePoseDriver {
37 joint_name: joint_name.to_string(),
38 target_shape: target_shape.to_string(),
39 config: CorrectivePoseDriverConfig::default(),
40 current_weight: 0.0,
41 }
42 }
43}
44
45pub fn new_corrective_pose_driver(joint_name: &str, target_shape: &str) -> CorrectivePoseDriver {
47 CorrectivePoseDriver::new(joint_name, target_shape)
48}
49
50pub fn evaluate_pose_driver(driver: &mut CorrectivePoseDriver, joint_angle_rad: f32) -> f32 {
52 let t = driver.config.threshold_rad;
53 let w = if joint_angle_rad >= t {
54 (joint_angle_rad - t) / (std::f32::consts::PI - t)
55 } else {
56 0.0
57 };
58 driver.current_weight = w.clamp(0.0, driver.config.max_weight);
59 driver.current_weight
60}
61
62pub fn reset_pose_driver(driver: &mut CorrectivePoseDriver) {
64 driver.current_weight = 0.0;
65}
66
67pub fn pose_driver_to_json(driver: &CorrectivePoseDriver) -> String {
69 format!(
70 r#"{{"joint":"{}","shape":"{}","weight":{:.4}}}"#,
71 driver.joint_name, driver.target_shape, driver.current_weight
72 )
73}
74
75pub fn set_pose_driver_threshold(driver: &mut CorrectivePoseDriver, threshold_rad: f32) {
77 driver.config.threshold_rad = threshold_rad.clamp(0.0, std::f32::consts::PI);
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_new_driver_zero_weight() {
86 let d = new_corrective_pose_driver("shoulder", "shoulder_corrective");
87 assert!((d.current_weight).abs() < 1e-6, );
88 }
89
90 #[test]
91 fn test_evaluate_below_threshold_is_zero() {
92 let mut d = new_corrective_pose_driver("elbow", "elbow_bulge");
93 let w = evaluate_pose_driver(&mut d, 0.1);
94 assert!((w).abs() < 1e-6, );
95 }
96
97 #[test]
98 fn test_evaluate_above_threshold_positive() {
99 let mut d = new_corrective_pose_driver("elbow", "elbow_bulge");
100 let w = evaluate_pose_driver(&mut d, 1.5);
101 assert!(w > 0.0, );
102 }
103
104 #[test]
105 fn test_evaluate_clamps_to_max_weight() {
106 let mut d = new_corrective_pose_driver("knee", "knee_corrective");
107 let w = evaluate_pose_driver(&mut d, std::f32::consts::PI);
108 assert!(w <= d.config.max_weight, );
109 }
110
111 #[test]
112 fn test_reset_clears_weight() {
113 let mut d = new_corrective_pose_driver("hip", "hip_corrective");
114 evaluate_pose_driver(&mut d, 2.0);
115 reset_pose_driver(&mut d);
116 assert!((d.current_weight).abs() < 1e-6, );
117 }
118
119 #[test]
120 fn test_set_threshold() {
121 let mut d = new_corrective_pose_driver("wrist", "wrist_corrective");
122 set_pose_driver_threshold(&mut d, 0.8);
123 assert!((d.config.threshold_rad - 0.8).abs() < 1e-6, );
124 }
125
126 #[test]
127 fn test_to_json_contains_joint_name() {
128 let d = new_corrective_pose_driver("ankle", "ankle_corrective");
129 let s = pose_driver_to_json(&d);
130 assert!(s.contains("ankle"), );
131 }
132
133 #[test]
134 fn test_threshold_clamps_to_pi() {
135 let mut d = new_corrective_pose_driver("test", "test_shape");
136 set_pose_driver_threshold(&mut d, 99.0);
137 assert!(d.config.threshold_rad <= std::f32::consts::PI, );
138 }
139
140 #[test]
141 fn test_joint_name_stored() {
142 let d = new_corrective_pose_driver("shoulder_l", "shoulder_bulge");
143 assert_eq!(d.joint_name, "shoulder_l" ,);
144 }
145
146 #[test]
147 fn test_shape_name_stored() {
148 let d = new_corrective_pose_driver("hip", "hip_crease");
149 assert_eq!(
150 d.target_shape,
151 "hip_crease", );
153 }
154}