1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct PivotPoint {
11 pub name: String,
12 pub position: [f32; 3],
13 pub orientation: [f32; 4],
14}
15
16#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct PivotPointExport {
20 pub pivots: Vec<PivotPoint>,
21}
22
23#[allow(dead_code)]
24pub fn new_pivot_point_export() -> PivotPointExport {
25 PivotPointExport { pivots: Vec::new() }
26}
27
28#[allow(dead_code)]
29pub fn pp_add(e: &mut PivotPointExport, name: &str, pos: [f32; 3]) {
30 e.pivots.push(PivotPoint {
31 name: name.to_string(),
32 position: pos,
33 orientation: [0.0, 0.0, 0.0, 1.0],
34 });
35}
36
37#[allow(dead_code)]
38pub fn pp_add_with_orientation(
39 e: &mut PivotPointExport,
40 name: &str,
41 pos: [f32; 3],
42 orient: [f32; 4],
43) {
44 e.pivots.push(PivotPoint {
45 name: name.to_string(),
46 position: pos,
47 orientation: orient,
48 });
49}
50
51#[allow(dead_code)]
52pub fn pp_count(e: &PivotPointExport) -> usize {
53 e.pivots.len()
54}
55
56#[allow(dead_code)]
57pub fn pp_get(e: &PivotPointExport, idx: usize) -> Option<&PivotPoint> {
58 e.pivots.get(idx)
59}
60
61#[allow(dead_code)]
62pub fn pp_find_by_name(e: &PivotPointExport, name: &str) -> Option<usize> {
63 e.pivots.iter().position(|p| p.name == name)
64}
65
66#[allow(dead_code)]
67pub fn pp_set_position(e: &mut PivotPointExport, idx: usize, pos: [f32; 3]) {
68 if let Some(p) = e.pivots.get_mut(idx) {
69 p.position = pos;
70 }
71}
72
73#[allow(dead_code)]
74pub fn pp_distance(a: &PivotPoint, b: &PivotPoint) -> f32 {
75 let dx = a.position[0] - b.position[0];
76 let dy = a.position[1] - b.position[1];
77 let dz = a.position[2] - b.position[2];
78 (dx * dx + dy * dy + dz * dz).sqrt()
79}
80
81#[allow(dead_code)]
82pub fn pp_centroid(e: &PivotPointExport) -> [f32; 3] {
83 if e.pivots.is_empty() {
84 return [0.0; 3];
85 }
86 let n = e.pivots.len() as f32;
87 let mut sum = [0.0_f32; 3];
88 for p in &e.pivots {
89 for (k, s) in sum.iter_mut().enumerate() {
90 *s += p.position[k];
91 }
92 }
93 [sum[0] / n, sum[1] / n, sum[2] / n]
94}
95
96#[allow(dead_code)]
97pub fn pp_validate(e: &PivotPointExport) -> bool {
98 e.pivots.iter().all(|p| {
99 !p.name.is_empty()
100 && p.position.iter().all(|v| v.is_finite())
101 && p.orientation.iter().all(|v| v.is_finite())
102 })
103}
104
105#[allow(dead_code)]
106pub fn pivot_point_to_json(e: &PivotPointExport) -> String {
107 format!("{{\"pivots\":{}}}", e.pivots.len())
108}
109
110#[allow(dead_code)]
111pub fn pp_clear(e: &mut PivotPointExport) {
112 e.pivots.clear();
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_new() {
121 assert_eq!(pp_count(&new_pivot_point_export()), 0);
122 }
123
124 #[test]
125 fn test_add() {
126 let mut e = new_pivot_point_export();
127 pp_add(&mut e, "root", [0.0, 0.0, 0.0]);
128 assert_eq!(pp_count(&e), 1);
129 }
130
131 #[test]
132 fn test_add_with_orientation() {
133 let mut e = new_pivot_point_export();
134 pp_add_with_orientation(&mut e, "hand", [1.0, 0.0, 0.0], [0.0, 0.707, 0.0, 0.707]);
135 let p = pp_get(&e, 0).expect("should succeed");
136 assert!((p.orientation[1] - 0.707).abs() < 1e-3);
137 }
138
139 #[test]
140 fn test_get() {
141 let mut e = new_pivot_point_export();
142 pp_add(&mut e, "hip", [0.0, 1.0, 0.0]);
143 let p = pp_get(&e, 0).expect("should succeed");
144 assert_eq!(p.name, "hip");
145 }
146
147 #[test]
148 fn test_get_oob() {
149 assert!(pp_get(&new_pivot_point_export(), 0).is_none());
150 }
151
152 #[test]
153 fn test_find_by_name() {
154 let mut e = new_pivot_point_export();
155 pp_add(&mut e, "a", [0.0; 3]);
156 pp_add(&mut e, "b", [1.0; 3]);
157 assert_eq!(pp_find_by_name(&e, "b"), Some(1));
158 assert!(pp_find_by_name(&e, "c").is_none());
159 }
160
161 #[test]
162 fn test_set_position() {
163 let mut e = new_pivot_point_export();
164 pp_add(&mut e, "root", [0.0; 3]);
165 pp_set_position(&mut e, 0, [5.0, 6.0, 7.0]);
166 assert!((pp_get(&e, 0).expect("should succeed").position[0] - 5.0).abs() < 1e-6);
167 }
168
169 #[test]
170 fn test_distance() {
171 let a = PivotPoint {
172 name: "a".into(),
173 position: [0.0, 0.0, 0.0],
174 orientation: [0.0; 4],
175 };
176 let b = PivotPoint {
177 name: "b".into(),
178 position: [3.0, 4.0, 0.0],
179 orientation: [0.0; 4],
180 };
181 assert!((pp_distance(&a, &b) - 5.0).abs() < 1e-6);
182 }
183
184 #[test]
185 fn test_centroid() {
186 let mut e = new_pivot_point_export();
187 pp_add(&mut e, "a", [0.0, 0.0, 0.0]);
188 pp_add(&mut e, "b", [2.0, 4.0, 6.0]);
189 let c = pp_centroid(&e);
190 assert!((c[0] - 1.0).abs() < 1e-6);
191 assert!((c[1] - 2.0).abs() < 1e-6);
192 }
193
194 #[test]
195 fn test_validate() {
196 let mut e = new_pivot_point_export();
197 pp_add(&mut e, "ok", [1.0, 2.0, 3.0]);
198 assert!(pp_validate(&e));
199 }
200
201 #[test]
202 fn test_to_json() {
203 let e = new_pivot_point_export();
204 assert!(pivot_point_to_json(&e).contains("\"pivots\":0"));
205 }
206}