deep_time/position.rs
1use crate::{C_SQUARED, Real, hypot, sqrt};
2
3/// A 3-dimensional position vector expressed in Cartesian coordinates (x, y, z)
4/// with units of meters (SI).
5///
6/// This type is designed for high-precision relativistic calculations in space
7/// navigation, deep-space tracking, and interplanetary timing. Positions are
8/// typically expressed in a heliocentric (Sun-centered) reference frame because
9/// the dominant gravitational light-time correction—the Shapiro delay—is
10/// calculated with respect to the Sun.
11#[derive(Clone, Copy, Debug, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "js", derive(tsify::Tsify))]
14pub struct Position {
15 pub x: Real,
16 pub y: Real,
17 pub z: Real,
18}
19
20impl Position {
21 /// Creates a new `Position` directly from its Cartesian components in meters.
22 #[inline]
23 pub const fn new(x: Real, y: Real, z: Real) -> Self {
24 Self { x, y, z }
25 }
26
27 /// The zero vector, representing the origin of the coordinate system
28 /// (commonly the center of the Sun).
29 pub const ZERO: Self = Self::new(f!(0.0), f!(0.0), f!(0.0));
30
31 /// Creates a `Position` from coordinates expressed in Astronomical Units (AU),
32 /// converting them to meters using the exact IAU 2012 definition
33 /// (1 AU = 149 597 870 700 m).
34 ///
35 /// Especially convenient when working with planetary ephemerides or solar-system
36 /// models that are natively given in AU.
37 #[inline]
38 pub const fn from_au(x: Real, y: Real, z: Real) -> Self {
39 const AU: Real = f!(1.495978707e11);
40 Self {
41 x: x * AU,
42 y: y * AU,
43 z: z * AU,
44 }
45 }
46
47 /// Returns the Euclidean norm (straight-line distance) of this position from
48 /// the origin.
49 ///
50 /// When the position is Sun-centered, this is the radial distance from the Sun
51 /// required for Shapiro-delay calculations.
52 #[inline]
53 pub const fn norm(self) -> Real {
54 hypot(hypot(self.x, self.y), self.z)
55 }
56
57 /// Computes the straight-line (Euclidean) distance between this position and
58 /// another `Position`.
59 ///
60 /// Together with the two radial distances from the Sun, this value supplies the
61 /// three geometric inputs needed to evaluate the Shapiro delay.
62 pub const fn distance_to(self, other: Self) -> Real {
63 let dx = self.x - other.x;
64 let dy = self.y - other.y;
65 let dz = self.z - other.z;
66 hypot(hypot(dx, dy), dz)
67 }
68
69 /// Returns a new position that lies a fraction `t` of the way along the straight
70 /// line between `self` and `other`.
71 ///
72 /// This is known as linear interpolation (lerp). It is most commonly used when
73 /// you need to generate evenly spaced sample points along a path — for example,
74 /// when building the `samples` slice for [`ObserverState::one_way_relativistic_delay_integrated`].
75 ///
76 /// ## Parameters
77 ///
78 /// - `other` – the ending position
79 /// - `t` – interpolation parameter (0.0 = start point, 1.0 = end point).
80 /// Values outside [0, 1] are allowed and will extrapolate.
81 ///
82 /// ## Examples
83 ///
84 /// ```rust
85 /// use deep_time::Position;
86 ///
87 /// let a = Position::new(0.0, 0.0, 0.0);
88 /// let b = Position::new(10.0, 20.0, 30.0);
89 ///
90 /// let midpoint = a.lerp(b, 0.5); // (5.0, 10.0, 15.0)
91 /// let quarter = a.lerp(b, 0.25); // (2.5, 5.0, 7.5)
92 /// let beyond = a.lerp(b, 1.5); // (15.0, 30.0, 45.0)
93 /// ```
94 #[inline]
95 pub const fn lerp(self, other: Self, t: Real) -> Self {
96 Self::new(
97 self.x * (f!(1.0) - t) + other.x * t,
98 self.y * (f!(1.0) - t) + other.y * t,
99 self.z * (f!(1.0) - t) + other.z * t,
100 )
101 }
102}
103
104/// A 3-dimensional velocity vector expressed in Cartesian coordinates (vx, vy, vz)
105/// with units of meters per second (SI).
106#[derive(Clone, Copy, Debug, PartialEq)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108#[cfg_attr(feature = "js", derive(tsify::Tsify))]
109pub struct Velocity {
110 pub vx: Real,
111 pub vy: Real,
112 pub vz: Real,
113}
114
115impl Velocity {
116 /// Creates a new `Velocity` directly from its Cartesian components in m/s.
117 #[inline]
118 pub const fn new(vx: Real, vy: Real, vz: Real) -> Self {
119 Self { vx, vy, vz }
120 }
121
122 pub const ZERO: Self = Self::new(f!(0.0), f!(0.0), f!(0.0));
123
124 /// Creates a `Velocity` from its scalar speed (magnitude) in m/s.
125 ///
126 /// Direction is set along the x-axis because only the speed matters
127 /// for relativistic calculations (`beta()`, `norm_squared()`, etc.).
128 /// This is the convenience constructor used by `Drift::from_velocity_potential_and_scale`.
129 #[inline]
130 pub const fn from_speed(speed_m_s: Real) -> Self {
131 Self::new(speed_m_s, f!(0.0), f!(0.0))
132 }
133
134 /// Returns the squared Euclidean norm (v²).
135 #[inline]
136 pub const fn norm_squared(self) -> Real {
137 self.vx * self.vx + self.vy * self.vy + self.vz * self.vz
138 }
139
140 /// Speed in m/s (Euclidean magnitude).
141 #[inline]
142 pub const fn speed(self) -> Real {
143 sqrt(self.norm_squared().max(f!(0.0)))
144 }
145
146 /// Dimensionless 3-velocity β = v/c relative to the local chrono-rest frame.
147 /// This is exactly what the master Lagrangian and `Spacetime` expect.
148 #[inline]
149 pub const fn beta(self) -> Real {
150 sqrt((self.norm_squared() / C_SQUARED).max(f!(0.0)))
151 }
152}