1use crate::{extract::Extract, AgentId, Event, StateChange, TryExtract};
4use std::{
5 mem::transmute,
6 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
7};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15pub struct PositionEvent {
16 pub time: u64,
18
19 pub agent: AgentId,
21
22 pub position: Position,
24}
25
26impl Extract for PositionEvent {
27 #[inline]
28 unsafe fn extract(event: &Event) -> Self {
29 Self {
30 time: event.time,
31 agent: AgentId::from_src(event),
32 position: Position::extract(event),
33 }
34 }
35}
36
37impl TryExtract for PositionEvent {
38 #[inline]
39 fn can_extract(event: &Event) -> bool {
40 matches!(
41 event.get_statechange(),
42 StateChange::Position | StateChange::Velocity | StateChange::Facing
43 )
44 }
45}
46
47#[derive(Debug, Clone, PartialEq)]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60pub struct Position {
61 pub x: f32,
62 pub y: f32,
63 pub z: f32,
64}
65
66impl Position {
67 pub const INCH_TO_METER: f32 = 0.0254;
69
70 #[inline]
72 pub const fn new(x: f32, y: f32, z: f32) -> Self {
73 Self { x, y, z }
74 }
75
76 #[inline]
78 pub fn from_mumble(coords: [f32; 3]) -> Self {
79 let [x, y, z] = coords;
80 Self::new(
81 x / Self::INCH_TO_METER,
82 z / Self::INCH_TO_METER,
83 -y / Self::INCH_TO_METER,
84 )
85 }
86
87 #[inline]
89 pub fn from_scaled_i16s(x: i16, y: i16, z: i16, factor: f32) -> Self {
90 let x = x as f32 * factor;
91 let y = y as f32 * factor;
92 let z = z as f32 * factor;
93 Self::new(x, y, z)
94 }
95
96 #[inline]
98 pub fn to_array(self) -> [f32; 3] {
99 [self.x, self.y, self.z]
100 }
101
102 #[inline]
104 pub fn to_tuple(self) -> (f32, f32, f32) {
105 (self.x, self.y, self.z)
106 }
107
108 #[inline]
110 pub fn to_mumble(&self) -> [f32; 3] {
111 [
112 self.x * Self::INCH_TO_METER,
113 -self.z * Self::INCH_TO_METER,
114 self.y * Self::INCH_TO_METER,
115 ]
116 }
117
118 #[inline]
120 pub fn len(&self) -> f32 {
121 (self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).sqrt()
122 }
123
124 #[inline]
126 pub fn mat_mul(&self, matrix: [[f32; 3]; 3]) -> Self {
127 let x = matrix[0][0] * self.x + matrix[0][1] * self.y + matrix[0][2] * self.z;
128 let y = matrix[1][0] * self.x + matrix[1][1] * self.y + matrix[1][2] * self.z;
129 let z = matrix[2][0] * self.x + matrix[2][1] * self.y + matrix[2][2] * self.z;
130 Self::new(x, y, z)
131 }
132
133 #[inline]
137 pub fn as_rotation_matrix(&self) -> [[f32; 3]; 3] {
138 let Self {
139 x: alpha,
140 y: beta,
141 z: gamma,
142 } = self;
143 [
144 [
145 beta.cos() * gamma.cos(),
146 alpha.sin() * beta.sin() * gamma.cos() - alpha.cos() + gamma.sin(),
147 alpha.cos() * beta.sin() * gamma.cos() + alpha.sin() * gamma.sin(),
148 ],
149 [
150 beta.cos() * gamma.sin(),
151 alpha.sin() * beta.sin() * gamma.sin() + alpha.cos() + gamma.cos(),
152 alpha.cos() * beta.sin() * gamma.sin() - alpha.sin() * gamma.cos(),
153 ],
154 [
155 -beta.sin(),
156 alpha.sin() * beta.cos(),
157 alpha.cos() * beta.cos(),
158 ],
159 ]
160 }
161
162 #[inline]
164 pub fn rotate(&self, vector: Self) -> Self {
165 vector.mat_mul(self.as_rotation_matrix())
166 }
167
168 #[inline]
170 fn component_wise_op(&self, other: &Position, op: impl Fn(f32, f32) -> f32) -> Self {
171 Self::new(
172 op(self.x, other.x),
173 op(self.y, other.y),
174 op(self.z, other.z),
175 )
176 }
177
178 #[inline]
180 fn scalar_op(&self, scalar: f32, op: impl Fn(f32, f32) -> f32) -> Self {
181 Self::new(op(self.x, scalar), op(self.y, scalar), op(self.z, scalar))
182 }
183}
184
185impl From<[f32; 3]> for Position {
186 #[inline]
187 fn from(value: [f32; 3]) -> Self {
188 let [x, y, z] = value;
189 Self { x, y, z }
190 }
191}
192
193impl From<Position> for [f32; 3] {
194 #[inline]
195 fn from(pos: Position) -> Self {
196 pos.to_array()
197 }
198}
199
200impl From<(f32, f32, f32)> for Position {
201 #[inline]
202 fn from(value: (f32, f32, f32)) -> Self {
203 let (x, y, z) = value;
204 Self { x, y, z }
205 }
206}
207
208impl From<Position> for (f32, f32, f32) {
209 #[inline]
210 fn from(pos: Position) -> Self {
211 pos.to_tuple()
212 }
213}
214
215impl Add for &Position {
216 type Output = Position;
217
218 #[inline]
219 fn add(self, rhs: &Position) -> Self::Output {
220 self.component_wise_op(rhs, Add::add)
221 }
222}
223
224impl AddAssign for Position {
225 #[inline]
226 fn add_assign(&mut self, rhs: Self) {
227 *self = &*self + &rhs
228 }
229}
230
231impl Sub for &Position {
232 type Output = Position;
233
234 #[inline]
235 fn sub(self, rhs: &Position) -> Self::Output {
236 self.component_wise_op(rhs, Sub::sub)
237 }
238}
239
240impl SubAssign for Position {
241 #[inline]
242 fn sub_assign(&mut self, rhs: Self) {
243 *self = &*self - &rhs
244 }
245}
246
247impl Mul<f32> for &Position {
248 type Output = Position;
249
250 #[inline]
251 fn mul(self, rhs: f32) -> Self::Output {
252 self.scalar_op(rhs, Mul::mul)
253 }
254}
255
256impl MulAssign<f32> for Position {
257 #[inline]
258 fn mul_assign(&mut self, rhs: f32) {
259 *self = &*self * rhs;
260 }
261}
262
263impl Mul<&Position> for f32 {
264 type Output = Position;
265
266 #[inline]
267 fn mul(self, rhs: &Position) -> Self::Output {
268 rhs.scalar_op(self, Mul::mul)
269 }
270}
271
272impl Div<f32> for &Position {
273 type Output = Position;
274
275 #[inline]
276 fn div(self, rhs: f32) -> Self::Output {
277 self.scalar_op(rhs, Div::div)
278 }
279}
280
281impl DivAssign<f32> for Position {
282 #[inline]
283 fn div_assign(&mut self, rhs: f32) {
284 *self = &*self / rhs;
285 }
286}
287
288impl Div<&Position> for f32 {
289 type Output = Position;
290
291 #[inline]
292 fn div(self, rhs: &Position) -> Self::Output {
293 rhs.scalar_op(self, Div::div)
294 }
295}
296
297impl Extract for Position {
298 #[inline]
299 unsafe fn extract(event: &Event) -> Self {
300 let [x, y]: [f32; 2] = transmute(event.dst_agent);
301 let z = f32::from_ne_bytes(event.value.to_ne_bytes());
302 Self::new(x, y, z)
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use approx::assert_relative_eq;
310 use std::f32::consts::{FRAC_1_SQRT_2, PI};
311
312 #[test]
313 fn mumble_conversion() {
314 let pos = Position::new(3993.409, 6225.539, -549.570);
315
316 let mumble = pos.to_mumble();
317 assert_relative_eq!(
318 *mumble.as_slice(),
319 [101.433, 13.959, 158.129],
320 max_relative = 1e-3
321 );
322
323 let back = Position::from_mumble(mumble);
324 assert_eq!(back, pos);
325 }
326
327 #[test]
328 fn rotation() {
329 let rotation = Position::new(0.0, 0.25 * PI, 0.5 * PI);
330 let vector = Position::new(1.0, 0.0, 0.0);
331
332 let result = rotation.rotate(vector).to_array();
333 assert_relative_eq!(
334 *result.as_slice(),
335 [0.0, FRAC_1_SQRT_2, -FRAC_1_SQRT_2],
336 max_relative = 1e-7
337 );
338 }
339}