1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy)]
9pub struct SdkCurvePoint {
10 pub driver_value: f32,
11 pub shape_weight: f32,
12}
13
14#[derive(Debug, Clone)]
16pub struct SdkDrivenShape {
17 pub driver_attr: String,
18 pub shape_name: String,
19 pub curve: Vec<SdkCurvePoint>,
20 pub current_weight: f32,
21}
22
23impl SdkDrivenShape {
24 pub fn new(driver_attr: &str, shape_name: &str) -> Self {
25 SdkDrivenShape {
26 driver_attr: driver_attr.to_string(),
27 shape_name: shape_name.to_string(),
28 curve: Vec::new(),
29 current_weight: 0.0,
30 }
31 }
32}
33
34pub fn new_sdk_driven_shape(driver_attr: &str, shape_name: &str) -> SdkDrivenShape {
36 SdkDrivenShape::new(driver_attr, shape_name)
37}
38
39pub fn sdk_add_point(shape: &mut SdkDrivenShape, driver_value: f32, shape_weight: f32) {
41 shape.curve.push(SdkCurvePoint {
42 driver_value,
43 shape_weight: shape_weight.clamp(0.0, 1.0),
44 });
45 shape.curve.sort_by(|a, b| {
46 a.driver_value
47 .partial_cmp(&b.driver_value)
48 .unwrap_or(std::cmp::Ordering::Equal)
49 });
50}
51
52pub fn sdk_evaluate(shape: &mut SdkDrivenShape, driver_value: f32) -> f32 {
54 if shape.curve.is_empty() {
55 shape.current_weight = 0.0;
56 return 0.0;
57 }
58 let first = shape.curve[0];
59 let last = shape.curve[shape.curve.len() - 1];
60 if driver_value <= first.driver_value {
61 shape.current_weight = first.shape_weight;
62 return first.shape_weight;
63 }
64 if driver_value >= last.driver_value {
65 shape.current_weight = last.shape_weight;
66 return last.shape_weight;
67 }
68 for i in 0..shape.curve.len().saturating_sub(1) {
69 let a = shape.curve[i];
70 let b = shape.curve[i + 1];
71 if driver_value >= a.driver_value && driver_value <= b.driver_value {
72 let t = (driver_value - a.driver_value) / (b.driver_value - a.driver_value);
73 let w = a.shape_weight + t * (b.shape_weight - a.shape_weight);
74 shape.current_weight = w;
75 return w;
76 }
77 }
78 shape.current_weight = 0.0;
79 0.0
80}
81
82pub fn sdk_reset(shape: &mut SdkDrivenShape) {
84 shape.current_weight = 0.0;
85}
86
87pub fn sdk_to_json(shape: &SdkDrivenShape) -> String {
89 format!(
90 r#"{{"driver":"{}","shape":"{}","weight":{:.4},"points":{}}}"#,
91 shape.driver_attr,
92 shape.shape_name,
93 shape.current_weight,
94 shape.curve.len()
95 )
96}
97
98pub fn sdk_point_count(shape: &SdkDrivenShape) -> usize {
100 shape.curve.len()
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_new_sdk_shape_empty() {
109 let s = new_sdk_driven_shape("jaw_open", "mouth_open");
110 assert_eq!(sdk_point_count(&s), 0 ,);
111 }
112
113 #[test]
114 fn test_add_point_increases_count() {
115 let mut s = new_sdk_driven_shape("jaw_open", "mouth_open");
116 sdk_add_point(&mut s, 0.0, 0.0);
117 sdk_add_point(&mut s, 1.0, 1.0);
118 assert_eq!(sdk_point_count(&s), 2 ,);
119 }
120
121 #[test]
122 fn test_evaluate_below_min_returns_first() {
123 let mut s = new_sdk_driven_shape("brow", "brow_raise");
124 sdk_add_point(&mut s, 0.5, 0.2);
125 sdk_add_point(&mut s, 1.0, 1.0);
126 let w = sdk_evaluate(&mut s, 0.0);
127 assert!((w - 0.2).abs() < 1e-5, );
128 }
129
130 #[test]
131 fn test_evaluate_above_max_returns_last() {
132 let mut s = new_sdk_driven_shape("brow", "brow_raise");
133 sdk_add_point(&mut s, 0.0, 0.0);
134 sdk_add_point(&mut s, 1.0, 0.8);
135 let w = sdk_evaluate(&mut s, 2.0);
136 assert!((w - 0.8).abs() < 1e-5, );
137 }
138
139 #[test]
140 fn test_evaluate_midpoint_interpolates() {
141 let mut s = new_sdk_driven_shape("eye", "eye_wide");
142 sdk_add_point(&mut s, 0.0, 0.0);
143 sdk_add_point(&mut s, 1.0, 1.0);
144 let w = sdk_evaluate(&mut s, 0.5);
145 assert!((w - 0.5).abs() < 1e-5, );
146 }
147
148 #[test]
149 fn test_reset_zeroes_weight() {
150 let mut s = new_sdk_driven_shape("lip", "lip_compress");
151 sdk_add_point(&mut s, 0.0, 0.0);
152 sdk_add_point(&mut s, 1.0, 1.0);
153 sdk_evaluate(&mut s, 1.0);
154 sdk_reset(&mut s);
155 assert!((s.current_weight).abs() < 1e-6, );
156 }
157
158 #[test]
159 fn test_empty_evaluate_returns_zero() {
160 let mut s = new_sdk_driven_shape("chin", "chin_raise");
161 let w = sdk_evaluate(&mut s, 0.5);
162 assert!((w).abs() < 1e-6 ,);
163 }
164
165 #[test]
166 fn test_points_sorted_by_driver_value() {
167 let mut s = new_sdk_driven_shape("test", "shape");
168 sdk_add_point(&mut s, 1.0, 0.8);
169 sdk_add_point(&mut s, 0.0, 0.0);
170 assert!(s.curve[0].driver_value < s.curve[1].driver_value, );
171 }
172
173 #[test]
174 fn test_to_json_contains_driver() {
175 let s = new_sdk_driven_shape("jaw_open", "mouth_shape");
176 let j = sdk_to_json(&s);
177 assert!(j.contains("jaw_open"), );
178 }
179
180 #[test]
181 fn test_weight_clamped_to_one() {
182 let mut s = new_sdk_driven_shape("test", "shape");
183 sdk_add_point(&mut s, 0.0, 2.0);
184 assert!(s.curve[0].shape_weight <= 1.0, );
185 }
186
187 #[test]
188 fn test_driver_attr_stored() {
189 let s = new_sdk_driven_shape("shoulder_rot", "shoulder_shape");
190 assert_eq!(
191 s.driver_attr,
192 "shoulder_rot", );
194 }
195}