oxihuman_export/
bone_envelope_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct BoneEnvelope {
11 pub bone_name: String,
12 pub head: [f32; 3],
13 pub tail: [f32; 3],
14 pub radius_head: f32,
15 pub radius_tail: f32,
16 pub weight: f32,
17}
18
19#[allow(dead_code)]
21#[derive(Debug, Clone, Default)]
22pub struct EnvelopeSet {
23 pub envelopes: Vec<BoneEnvelope>,
24}
25
26#[allow(dead_code)]
28pub fn new_envelope_set() -> EnvelopeSet {
29 EnvelopeSet::default()
30}
31
32#[allow(dead_code)]
34pub fn add_envelope(set: &mut EnvelopeSet, env: BoneEnvelope) {
35 set.envelopes.push(env);
36}
37
38#[allow(dead_code)]
40pub fn envelope_length(env: &BoneEnvelope) -> f32 {
41 let d = [
42 env.tail[0] - env.head[0],
43 env.tail[1] - env.head[1],
44 env.tail[2] - env.head[2],
45 ];
46 (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt()
47}
48
49#[allow(dead_code)]
51pub fn point_in_envelope(p: [f32; 3], env: &BoneEnvelope) -> bool {
52 let seg = [
54 env.tail[0] - env.head[0],
55 env.tail[1] - env.head[1],
56 env.tail[2] - env.head[2],
57 ];
58 let len2 = seg[0] * seg[0] + seg[1] * seg[1] + seg[2] * seg[2];
59 if len2 < 1e-10 {
60 let d2 = sq_dist(p, env.head);
61 return d2 <= env.radius_head * env.radius_head;
62 }
63 let hp = [p[0] - env.head[0], p[1] - env.head[1], p[2] - env.head[2]];
64 let t = (hp[0] * seg[0] + hp[1] * seg[1] + hp[2] * seg[2]) / len2;
65 let t = t.clamp(0.0, 1.0);
66 let closest = [
67 env.head[0] + t * seg[0],
68 env.head[1] + t * seg[1],
69 env.head[2] + t * seg[2],
70 ];
71 let d2 = sq_dist(p, closest);
72 let r = env.radius_head + t * (env.radius_tail - env.radius_head);
73 d2 <= r * r
74}
75
76fn sq_dist(a: [f32; 3], b: [f32; 3]) -> f32 {
77 let d = [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
78 d[0] * d[0] + d[1] * d[1] + d[2] * d[2]
79}
80
81#[allow(dead_code)]
83pub fn envelope_volume(env: &BoneEnvelope) -> f32 {
84 use std::f32::consts::PI;
85 let l = envelope_length(env);
86 let r = (env.radius_head + env.radius_tail) * 0.5;
87 PI * r * r * l + (4.0 / 3.0) * PI * r * r * r
88}
89
90#[allow(dead_code)]
92pub fn find_envelope<'a>(set: &'a EnvelopeSet, name: &str) -> Option<&'a BoneEnvelope> {
93 set.envelopes.iter().find(|e| e.bone_name == name)
94}
95
96#[allow(dead_code)]
98pub fn serialise_envelopes(set: &EnvelopeSet) -> Vec<f32> {
99 set.envelopes
100 .iter()
101 .flat_map(|e| {
102 [
103 e.head[0],
104 e.head[1],
105 e.head[2],
106 e.tail[0],
107 e.tail[1],
108 e.tail[2],
109 e.radius_head,
110 e.radius_tail,
111 e.weight,
112 ]
113 })
114 .collect()
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 fn sample_env() -> BoneEnvelope {
122 BoneEnvelope {
123 bone_name: "arm".to_string(),
124 head: [0.0, 0.0, 0.0],
125 tail: [0.0, 1.0, 0.0],
126 radius_head: 0.1,
127 radius_tail: 0.05,
128 weight: 1.0,
129 }
130 }
131
132 #[test]
133 fn test_add_envelope() {
134 let mut s = new_envelope_set();
135 add_envelope(&mut s, sample_env());
136 assert_eq!(s.envelopes.len(), 1);
137 }
138
139 #[test]
140 fn test_envelope_length() {
141 let e = sample_env();
142 assert!((envelope_length(&e) - 1.0).abs() < 1e-6);
143 }
144
145 #[test]
146 fn test_point_in_envelope_inside() {
147 let e = sample_env();
148 assert!(point_in_envelope([0.0, 0.5, 0.0], &e));
149 }
150
151 #[test]
152 fn test_point_in_envelope_outside() {
153 let e = sample_env();
154 assert!(!point_in_envelope([1.0, 0.5, 0.0], &e));
155 }
156
157 #[test]
158 fn test_envelope_volume_positive() {
159 assert!(envelope_volume(&sample_env()) > 0.0);
160 }
161
162 #[test]
163 fn test_find_envelope_found() {
164 let mut s = new_envelope_set();
165 add_envelope(&mut s, sample_env());
166 assert!(find_envelope(&s, "arm").is_some());
167 }
168
169 #[test]
170 fn test_find_envelope_not_found() {
171 let s = new_envelope_set();
172 assert!(find_envelope(&s, "leg").is_none());
173 }
174
175 #[test]
176 fn test_serialise_envelopes_length() {
177 let mut s = new_envelope_set();
178 add_envelope(&mut s, sample_env());
179 assert_eq!(serialise_envelopes(&s).len(), 9);
180 }
181
182 #[test]
183 fn test_serialise_empty() {
184 let s = new_envelope_set();
185 assert!(serialise_envelopes(&s).is_empty());
186 }
187
188 #[test]
189 fn test_weight_stored() {
190 let e = sample_env();
191 assert!((e.weight - 1.0).abs() < 1e-6);
192 }
193}