oxihuman_morph/
inbetween_shape.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct InbetweenShape {
10 pub name: String,
11 pub trigger_weight: f32,
12 pub deltas: Vec<[f32; 3]>,
13 pub current_weight: f32,
14}
15
16impl InbetweenShape {
17 pub fn new(name: &str, trigger_weight: f32, vertex_count: usize) -> Self {
18 InbetweenShape {
19 name: name.to_string(),
20 trigger_weight: trigger_weight.clamp(0.0, 1.0),
21 deltas: vec![[0.0; 3]; vertex_count],
22 current_weight: 0.0,
23 }
24 }
25}
26
27pub fn new_inbetween_shape(name: &str, trigger_weight: f32, vertex_count: usize) -> InbetweenShape {
29 InbetweenShape::new(name, trigger_weight, vertex_count)
30}
31
32pub fn inbetween_evaluate(shape: &mut InbetweenShape, driver_weight: f32) -> f32 {
35 let dist = (driver_weight - shape.trigger_weight).abs();
37 let half_width = 0.25_f32;
38 if dist >= half_width {
39 shape.current_weight = 0.0;
40 } else {
41 shape.current_weight = 1.0 - dist / half_width;
42 }
43 shape.current_weight
44}
45
46pub fn inbetween_set_delta(shape: &mut InbetweenShape, index: usize, delta: [f32; 3]) {
48 if index < shape.deltas.len() {
49 shape.deltas[index] = delta;
50 }
51}
52
53pub fn inbetween_reset(shape: &mut InbetweenShape) {
55 shape.current_weight = 0.0;
56}
57
58pub fn inbetween_vertex_count(shape: &InbetweenShape) -> usize {
60 shape.deltas.len()
61}
62
63pub fn inbetween_to_json(shape: &InbetweenShape) -> String {
65 format!(
66 r#"{{"name":"{}","trigger":{:.4},"weight":{:.4},"vertices":{}}}"#,
67 shape.name,
68 shape.trigger_weight,
69 shape.current_weight,
70 shape.deltas.len()
71 )
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_new_inbetween_vertex_count() {
80 let s = new_inbetween_shape("smile_half", 0.5, 10);
81 assert_eq!(
82 inbetween_vertex_count(&s),
83 10, );
85 }
86
87 #[test]
88 fn test_trigger_weight_clamped() {
89 let s = new_inbetween_shape("test", 2.0, 5);
90 assert!((s.trigger_weight - 1.0).abs() < 1e-5, );
91 }
92
93 #[test]
94 fn test_evaluate_at_trigger_is_one() {
95 let mut s = new_inbetween_shape("brow_half", 0.5, 5);
96 let w = inbetween_evaluate(&mut s, 0.5);
97 assert!((w - 1.0).abs() < 1e-5, );
98 }
99
100 #[test]
101 fn test_evaluate_far_from_trigger_is_zero() {
102 let mut s = new_inbetween_shape("brow_half", 0.5, 5);
103 let w = inbetween_evaluate(&mut s, 0.0);
104 assert!((w).abs() < 1e-5 ,);
105 }
106
107 #[test]
108 fn test_reset_zeroes_weight() {
109 let mut s = new_inbetween_shape("lip_half", 0.3, 4);
110 inbetween_evaluate(&mut s, 0.3);
111 inbetween_reset(&mut s);
112 assert!((s.current_weight).abs() < 1e-6, );
113 }
114
115 #[test]
116 fn test_set_delta_updates() {
117 let mut s = new_inbetween_shape("test", 0.5, 5);
118 inbetween_set_delta(&mut s, 0, [1.0, 2.0, 3.0]);
119 assert!((s.deltas[0][0] - 1.0).abs() < 1e-5, );
120 }
121
122 #[test]
123 fn test_set_delta_out_of_bounds_ignored() {
124 let mut s = new_inbetween_shape("test", 0.5, 2);
125 inbetween_set_delta(&mut s, 99, [1.0, 0.0, 0.0]);
126 assert_eq!(
127 inbetween_vertex_count(&s),
128 2, );
130 }
131
132 #[test]
133 fn test_to_json_contains_name() {
134 let s = new_inbetween_shape("smile_quarter", 0.25, 3);
135 let j = inbetween_to_json(&s);
136 assert!(j.contains("smile_quarter"), );
137 }
138
139 #[test]
140 fn test_initial_weight_zero() {
141 let s = new_inbetween_shape("test", 0.5, 3);
142 assert!((s.current_weight).abs() < 1e-6, );
143 }
144
145 #[test]
146 fn test_evaluate_partial_activation() {
147 let mut s = new_inbetween_shape("mid", 0.5, 3);
148 let w = inbetween_evaluate(&mut s, 0.625);
149 assert!(w > 0.0 && w < 1.0, );
150 }
151}