Skip to main content

deep_time/physics/
velocity.rs

1//! Velocity vector in meters per second.
2
3use crate::{C_SQUARED, Real, sqrt};
4
5/// A 3-dimensional velocity vector expressed in Cartesian coordinates (vx, vy, vz)
6/// with units of meters per second (SI).
7#[derive(Clone, Copy, Debug, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
10pub struct Velocity {
11    /// X-component of velocity in meters per second (SI).
12    pub vx: Real,
13    /// Y-component of velocity in meters per second (SI).
14    pub vy: Real,
15    /// Z-component of velocity in meters per second (SI).
16    pub vz: Real,
17}
18
19impl Velocity {
20    /// Creates a new [`Velocity`] directly from its Cartesian components in m/s.
21    #[inline]
22    pub const fn new(vx: Real, vy: Real, vz: Real) -> Velocity {
23        Self { vx, vy, vz }
24    }
25
26    /// The zero velocity vector (at rest in the coordinate frame).
27    pub const ZERO: Self = Self::new(f!(0.0), f!(0.0), f!(0.0));
28
29    /// Creates a [`Velocity`] from its scalar speed (magnitude) in m/s.
30    ///
31    /// Direction is set along the x-axis because only the speed enters the
32    /// interval (`beta()`, `norm_squared()`, etc.).
33    #[inline]
34    pub const fn from_speed(speed_m_s: Real) -> Velocity {
35        Self::new(speed_m_s, f!(0.0), f!(0.0))
36    }
37
38    /// Returns the squared Euclidean norm (v²).
39    #[inline]
40    pub const fn norm_squared(self) -> Real {
41        self.vx * self.vx + self.vy * self.vy + self.vz * self.vz
42    }
43
44    /// Speed in m/s (Euclidean magnitude).
45    #[inline]
46    pub const fn speed(self) -> Real {
47        sqrt(self.norm_squared().max(f!(0.0)))
48    }
49
50    /// Spatial velocity as a fraction of light speed: \(\beta = |v|/c\).
51    ///
52    /// Spatial velocity \(v\) is this vector in metres of travel through space
53    /// per one second of the coordinate time \(t\) in which `(vx, vy, vz)` were
54    /// measured. [`Spacetime`](super::Spacetime) uses β in \(d\tau/dt\).
55    #[inline]
56    pub const fn beta(self) -> Real {
57        sqrt((self.norm_squared() / C_SQUARED).max(f!(0.0)))
58    }
59}
60
61#[cfg(feature = "wire")]
62impl Velocity {
63    /// Size of the canonical wire representation in bytes (24 bytes).
64    pub const WIRE_SIZE: usize = 24;
65
66    /// Serializes this [`Velocity`] into a fixed 24-byte buffer.
67    ///
68    /// All fields are stored as little-endian IEEE 754 `f64`.
69    pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
70        let mut buf = [0u8; Self::WIRE_SIZE];
71        buf[0..8].copy_from_slice(&self.vx.to_le_bytes());
72        buf[8..16].copy_from_slice(&self.vy.to_le_bytes());
73        buf[16..24].copy_from_slice(&self.vz.to_le_bytes());
74        buf
75    }
76
77    /// Deserializes a [`Velocity`] from exactly 24 bytes.
78    ///
79    /// ## Security
80    ///
81    /// Accepts any [`Real`] bit pattern (including `NaN`/`Inf`) to match the
82    /// type’s own invariants. Fixed size makes it immune to length-based
83    /// attacks. Safe for untrusted input.
84    pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
85        if bytes.len() != Self::WIRE_SIZE {
86            return None;
87        }
88        let vx = Real::from_le_bytes([
89            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
90        ]);
91        let vy = Real::from_le_bytes([
92            bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
93        ]);
94        let vz = Real::from_le_bytes([
95            bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23],
96        ]);
97        Some(Self { vx, vy, vz })
98    }
99}