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    pub vx: Real,
12    pub vy: Real,
13    pub vz: Real,
14}
15
16impl Velocity {
17    /// Creates a new `Velocity` directly from its Cartesian components in m/s.
18    #[inline]
19    pub const fn new(vx: Real, vy: Real, vz: Real) -> Velocity {
20        Self { vx, vy, vz }
21    }
22
23    pub const ZERO: Self = Self::new(f!(0.0), f!(0.0), f!(0.0));
24
25    /// Creates a `Velocity` from its scalar speed (magnitude) in m/s.
26    ///
27    /// Direction is set along the x-axis because only the speed matters
28    /// for relativistic calculations (`beta()`, `norm_squared()`, etc.).
29    /// This is the convenience constructor used by `Drift::from_velocity_potential_and_scale`.
30    #[inline]
31    pub const fn from_speed(speed_m_s: Real) -> Velocity {
32        Self::new(speed_m_s, f!(0.0), f!(0.0))
33    }
34
35    /// Returns the squared Euclidean norm (v²).
36    #[inline]
37    pub const fn norm_squared(self) -> Real {
38        self.vx * self.vx + self.vy * self.vy + self.vz * self.vz
39    }
40
41    /// Speed in m/s (Euclidean magnitude).
42    #[inline]
43    pub const fn speed(self) -> Real {
44        sqrt(self.norm_squared().max(f!(0.0)))
45    }
46
47    /// Dimensionless 3-velocity β = v/c relative to the local chrono-rest frame.
48    /// This is what the master Lagrangian and `Spacetime` expect.
49    #[inline]
50    pub const fn beta(self) -> Real {
51        sqrt((self.norm_squared() / C_SQUARED).max(f!(0.0)))
52    }
53}
54
55#[cfg(feature = "wire")]
56impl Velocity {
57    /// Size of the canonical wire representation in bytes (24 bytes).
58    pub const WIRE_SIZE: usize = 24;
59
60    /// Serializes this [`Velocity`] into a fixed 24-byte buffer.
61    ///
62    /// All fields are stored as little-endian IEEE 754 `f64`.
63    pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
64        let mut buf = [0u8; Self::WIRE_SIZE];
65        buf[0..8].copy_from_slice(&self.vx.to_le_bytes());
66        buf[8..16].copy_from_slice(&self.vy.to_le_bytes());
67        buf[16..24].copy_from_slice(&self.vz.to_le_bytes());
68        buf
69    }
70
71    /// Deserializes a [`Velocity`] from exactly 24 bytes.
72    ///
73    /// ## Security
74    ///
75    /// Accepts any [`Real`] bit pattern (including `NaN`/`Inf`) to match the
76    /// type’s own invariants. Fixed size makes it immune to length-based
77    /// attacks. Safe for untrusted input.
78    pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
79        if bytes.len() != Self::WIRE_SIZE {
80            return None;
81        }
82        let vx = Real::from_le_bytes([
83            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
84        ]);
85        let vy = Real::from_le_bytes([
86            bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
87        ]);
88        let vz = Real::from_le_bytes([
89            bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23],
90        ]);
91        Some(Self { vx, vy, vz })
92    }
93}