siderust-cpp 0.8.0
Header-only C++ wrapper for siderust
Loading...
Searching...
No Matches
cartesian.hpp
Go to the documentation of this file.
1#pragma once
2
9#include "../astro_context.hpp"
10#include "../centers.hpp"
11#include "../ffi_core.hpp"
12#include "../frames.hpp"
13#include "../time.hpp"
14
15#include <qtty/qtty.hpp>
16
17#include "detail/stream.hpp"
18
19#include <cmath>
20#include <ostream>
21#include <type_traits>
22
23// Forward-declare spherical Position to avoid circular include.
24namespace siderust {
25namespace spherical {
26template <typename C, typename F, typename U> struct Position;
27}
28} // namespace siderust
29
30namespace siderust {
31namespace cartesian {
32
41template <typename F> struct Direction {
42 static_assert(frames::is_frame_v<F>, "F must be a valid frame tag");
43
44 double x;
45 double y;
46 double z;
47
48 Direction() : x(0), y(0), z(0) {}
49 Direction(double x_, double y_, double z_) : x(x_), y(y_), z(z_) {}
50
52
59 double dot(const Direction &other) const { return x * other.x + y * other.y + z * other.z; }
60
70 double angle_to(const Direction &other) const {
71 double d = dot(other);
72 if (d > 1.0)
73 d = 1.0;
74 if (d < -1.0)
75 d = -1.0;
76 return std::acos(d);
77 }
78
89 template <typename Target>
90 std::enable_if_t<frames::has_frame_transform_v<F, Target>, Direction<Target>>
91 to_frame(const Time<TT, JD> &jd) const {
92 if constexpr (std::is_same_v<F, Target>) {
93 return Direction<Target>(x, y, z);
94 } else {
98 jd.value(), &out),
99 "cartesian::Direction::to_frame");
100 return Direction<Target>(out.x, out.y, out.z);
101 }
102 }
103
107 template <typename Target>
108 std::enable_if_t<frames::has_frame_transform_v<F, Target>, Direction<Target>>
110 if constexpr (std::is_same_v<F, Target>) {
111 return Direction<Target>(x, y, z);
112 } else {
117 jd.value(), fctx.get(), &out),
118 "cartesian::Direction::to_frame_with");
119 return Direction<Target>(out.x, out.y, out.z);
120 }
121 }
122
126 template <typename Target>
127 auto to(const Time<TT, JD> &jd) const -> decltype(this->template to_frame<Target>(jd)) {
128 return to_frame<Target>(jd);
129 }
130};
131
135template <typename F> inline std::ostream &operator<<(std::ostream &os, const Direction<F> &dir) {
136 coordinates::detail::write_frame<F>(os);
137 return os << " direction (x=" << dir.x << ", y=" << dir.y << ", z=" << dir.z << ')';
138}
139
140// Forward-declare Position for Displacement operators.
141template <typename C, typename F, typename U> struct Position;
142
160template <typename F, typename U> struct Displacement {
161 static_assert(frames::is_frame_v<F>, "F must be a valid frame tag");
162
166
167 Displacement() : comp_x(U(0)), comp_y(U(0)), comp_z(U(0)) {}
168
169 Displacement(U x_, U y_, U z_) : comp_x(x_), comp_y(y_), comp_z(z_) {}
170
171 Displacement(double x_, double y_, double z_) : comp_x(U(x_)), comp_y(U(y_)), comp_z(U(z_)) {}
172
173 U x() const { return comp_x; }
174 U y() const { return comp_y; }
175 U z() const { return comp_z; }
176
180 U magnitude() const {
181 using std::sqrt;
182 const double vx = comp_x.value();
183 const double vy = comp_y.value();
184 const double vz = comp_z.value();
185 return U(sqrt(vx * vx + vy * vy + vz * vz));
186 }
187
188 static constexpr siderust_frame_t frame_id() { return frames::FrameTraits<F>::ffi_id; }
189
193 Displacement operator+(const Displacement &other) const {
194 return Displacement(comp_x + other.comp_x, comp_y + other.comp_y, comp_z + other.comp_z);
195 }
196
200 Displacement operator-(const Displacement &other) const {
201 return Displacement(comp_x - other.comp_x, comp_y - other.comp_y, comp_z - other.comp_z);
202 }
203
207 Displacement operator-() const { return Displacement(-comp_x, -comp_y, -comp_z); }
208
212 Displacement operator*(double scalar) const {
213 return Displacement(comp_x * scalar, comp_y * scalar, comp_z * scalar);
214 }
215
222 template <typename Target>
223 std::enable_if_t<frames::has_frame_transform_v<F, Target>, Displacement<Target, U>>
224 to_frame(const Time<TT, JD> &jd) const {
225 if constexpr (std::is_same_v<F, Target>) {
226 return Displacement<Target, U>(comp_x, comp_y, comp_z);
227 } else {
228 siderust_cartesian_pos_t out{};
229 check_status(siderust_cartesian_dir_transform_frame(
230 comp_x.value(), comp_y.value(), comp_z.value(),
232 jd.value(), &out),
233 "cartesian::Displacement::to_frame");
234 return Displacement<Target, U>(out.x, out.y, out.z);
235 }
236 }
237
241 template <typename Target>
242 std::enable_if_t<frames::has_frame_transform_v<F, Target>, Displacement<Target, U>>
243 to_frame_with(const Time<TT, JD> &jd, const AstroContext &ctx) const {
244 if constexpr (std::is_same_v<F, Target>) {
245 return Displacement<Target, U>(comp_x, comp_y, comp_z);
246 } else {
247 siderust_cartesian_pos_t out{};
248 detail::OwnedFfiContext fctx(ctx);
249 check_status(siderust_cartesian_dir_transform_frame_with_context(
250 comp_x.value(), comp_y.value(), comp_z.value(),
252 jd.value(), fctx.get(), &out),
253 "cartesian::Displacement::to_frame_with");
254 return Displacement<Target, U>(out.x, out.y, out.z);
255 }
256 }
257};
258
262template <typename F, typename U>
263inline std::ostream &operator<<(std::ostream &os, const Displacement<F, U> &d) {
264 coordinates::detail::write_frame<F>(os);
265 return os << " displacement (dx=" << d.x() << ", dy=" << d.y() << ", dz=" << d.z() << ')';
266}
267
271template <typename F, typename U>
272inline Displacement<F, U> operator*(double scalar, const Displacement<F, U> &d) {
273 return d * scalar;
274}
275
286template <typename C, typename F, typename U> struct Position {
287 static_assert(frames::is_frame_v<F>, "F must be a valid frame tag");
288 static_assert(centers::is_center_v<C>, "C must be a valid center tag");
289
293
294 Position() : comp_x(U(0)), comp_y(U(0)), comp_z(U(0)) {}
295
297
298 Position(double x_, double y_, double z_) : comp_x(U(x_)), comp_y(U(y_)), comp_z(U(z_)) {}
299
300 U x() const { return comp_x; }
301 U y() const { return comp_y; }
302 U z() const { return comp_z; }
303
304 U distance() const {
305 using std::sqrt;
306 const double vx = comp_x.value();
307 const double vy = comp_y.value();
308 const double vz = comp_z.value();
309 return U(sqrt(vx * vx + vy * vy + vz * vz));
310 }
311
316
317 U distance_to(const Position &other) const {
318 using std::sqrt;
319 const double dx = comp_x.value() - other.comp_x.value();
320 const double dy = comp_y.value() - other.comp_y.value();
321 const double dz = comp_z.value() - other.comp_z.value();
322 return U(sqrt(dx * dx + dy * dy + dz * dz));
323 }
324
327
330 return {comp_x.value(), comp_y.value(), comp_z.value(), frame_id(), center_id()};
331 }
332
334 static Position from_c(const siderust_cartesian_pos_t &c) { return Position(c.x, c.y, c.z); }
335
347 template <typename Target>
348 std::enable_if_t<frames::has_frame_transform_v<F, Target>, Position<C, Target, U>>
349 to_frame(const Time<TT, JD> &jd) const {
350 if constexpr (std::is_same_v<F, Target>) {
351 return *this;
352 } else {
356 "cartesian::Position::to_frame");
357 return Position<C, Target, U>(out.x, out.y, out.z);
358 }
359 }
360
364 template <typename Target>
365 std::enable_if_t<frames::has_frame_transform_v<F, Target>, Position<C, Target, U>>
367 if constexpr (std::is_same_v<F, Target>) {
368 return *this;
369 } else {
373 to_c(), frames::FrameTraits<Target>::ffi_id, jd.value(), fctx.get(), &out),
374 "cartesian::Position::to_frame_with");
375 return Position<C, Target, U>(out.x, out.y, out.z);
376 }
377 }
378
382 template <typename Target>
383 auto to(const Time<TT, JD> &jd) const -> decltype(this->template to_frame<Target>(jd)) {
384 return to_frame<Target>(jd);
385 }
386
399 template <typename TargetC>
400 std::enable_if_t<centers::has_center_transform_v<C, TargetC>, Position<TargetC, F, U>>
401 to_center(const Time<TT, JD> &jd) const {
402 if constexpr (std::is_same_v<C, TargetC>) {
403 return *this;
404 } else if constexpr (std::is_same_v<F, frames::EclipticMeanJ2000>) {
405 // Direct FFI call — shift vectors and position are both in ecliptic.
409 "cartesian::Position::to_center");
410 return Position<TargetC, F, U>(out.x, out.y, out.z);
411 } else {
412 // Route through ecliptic so the shift vectors match the frame.
414 auto shifted = ecl.template to_center<TargetC>(jd);
415 return shifted.template to_frame<F>(jd);
416 }
417 }
418
432 template <typename TargetC, typename TargetF>
433 std::enable_if_t<frames::has_frame_transform_v<F, TargetF> &&
434 centers::has_center_transform_v<C, TargetC>,
436 transform(const Time<TT, JD> &jd) const {
438 auto shifted = ecl.template to_center<TargetC>(jd);
439 return shifted.template to_frame<TargetF>(jd);
440 }
441
449 return Displacement<F, U>(U(comp_x.value() - other.comp_x.value()),
450 U(comp_y.value() - other.comp_y.value()),
451 U(comp_z.value() - other.comp_z.value()));
452 }
453
460 return Position(comp_x + displacement.comp_x, comp_y + displacement.comp_y,
461 comp_z + displacement.comp_z);
462 }
463
470 return Position(comp_x - displacement.comp_x, comp_y - displacement.comp_y,
471 comp_z - displacement.comp_z);
472 }
473
477 U magnitude() const { return distance(); }
478};
479
480// ============================================================================
481// Stream operators
482// ============================================================================
483
487template <typename C, typename F, typename U>
488inline std::ostream &operator<<(std::ostream &os, const Position<C, F, U> &pos) {
489 coordinates::detail::write_center_frame<C, F>(os);
490 return os << " (x=" << pos.x() << ", y=" << pos.y() << ", z=" << pos.z() << ')';
491}
492
493} // namespace cartesian
494} // namespace siderust
const siderust_context_t * get() const
Displacement< F, U > operator*(double scalar, const Displacement< F, U > &d)
Scale a displacement by a scalar (scalar on left).
std::ostream & operator<<(std::ostream &os, const Direction< F > &dir)
Stream operator for cartesian::Direction.
tempoch::EncodedTime< Scale, Format > Time
Definition time.hpp:36
void check_status(siderust_status_t status, const char *operation)
Definition ffi_core.hpp:111
Shared helpers for human-readable coordinate stream output.
A unit-vector direction in Cartesian form, compile-time frame-tagged.
Definition cartesian.hpp:41
double x
X component (unitless).
Definition cartesian.hpp:44
auto to(const Time< TT, JD > &jd) const -> decltype(this->template to_frame< Target >(jd))
Shorthand: .to<Target>(jd) (calls to_frame).
double angle_to(const Direction &other) const
Angle between this direction and another, in radians.
Definition cartesian.hpp:70
double y
Y component (unitless).
Definition cartesian.hpp:45
static constexpr siderust_frame_t frame_id()
Definition cartesian.hpp:51
Direction(double x_, double y_, double z_)
Definition cartesian.hpp:49
std::enable_if_t< frames::has_frame_transform_v< F, Target >, Direction< Target > > to_frame_with(const Time< TT, JD > &jd, const AstroContext &ctx) const
Transform this direction with an explicit astronomical context.
double dot(const Direction &other) const
Dot product of two unit-direction vectors.
Definition cartesian.hpp:59
double z
Z component (unitless).
Definition cartesian.hpp:46
std::enable_if_t< frames::has_frame_transform_v< F, Target >, Direction< Target > > to_frame(const Time< TT, JD > &jd) const
Transform this direction to a different reference frame.
Definition cartesian.hpp:91
A 3D Cartesian displacement (free vector), compile-time frame-tagged.
Displacement operator-(const Displacement &other) const
Subtract two displacements.
Displacement operator-() const
Negate a displacement.
Displacement operator+(const Displacement &other) const
Add two displacements.
Displacement(double x_, double y_, double z_)
static constexpr siderust_frame_t frame_id()
Displacement operator*(double scalar) const
Scale a displacement by a scalar.
std::enable_if_t< frames::has_frame_transform_v< F, Target >, Displacement< Target, U > > to_frame_with(const Time< TT, JD > &jd, const AstroContext &ctx) const
Transform this displacement with an explicit astronomical context.
std::enable_if_t< frames::has_frame_transform_v< F, Target >, Displacement< Target, U > > to_frame(const Time< TT, JD > &jd) const
Transform this displacement to a different reference frame.
U magnitude() const
Magnitude of the displacement vector.
A 3D Cartesian position, compile-time tagged by center, frame, unit.
Position operator+(const Displacement< F, U > &displacement) const
Translate a position by a displacement.
std::enable_if_t< frames::has_frame_transform_v< F, Target >, Position< C, Target, U > > to_frame_with(const Time< TT, JD > &jd, const AstroContext &ctx) const
Transform this position with an explicit astronomical context.
std::enable_if_t< frames::has_frame_transform_v< F, TargetF > &&centers::has_center_transform_v< C, TargetC >, Position< TargetC, TargetF, U > > transform(const Time< TT, JD > &jd) const
Combined frame + center transform in one call.
static constexpr siderust_center_t center_id()
siderust_cartesian_pos_t to_c() const
Convert to C FFI struct.
U distance_to(const Position &other) const
Position operator-(const Displacement< F, U > &displacement) const
Translate a position backwards by a displacement.
static Position from_c(const siderust_cartesian_pos_t &c)
Create from C FFI struct (ignoring runtime frame/center - trust the type).
std::enable_if_t< centers::has_center_transform_v< C, TargetC >, Position< TargetC, F, U > > to_center(const Time< TT, JD > &jd) const
Transform this position to a different reference center (same frame).
spherical::Position< C, F, U > to_spherical() const
Convert this cartesian position to a spherical Position<C,F,U>.
auto to(const Time< TT, JD > &jd) const -> decltype(this->template to_frame< Target >(jd))
Shorthand: .to<Target>(jd) (calls to_frame).
Position(double x_, double y_, double z_)
std::enable_if_t< frames::has_frame_transform_v< F, Target >, Position< C, Target, U > > to_frame(const Time< TT, JD > &jd) const
Transform this position to a different reference frame (same center).
U magnitude() const
Magnitude of the position vector (alias for distance()).
Displacement< F, U > operator-(const Position &other) const
Subtract two positions in the same center/frame/unit.
static constexpr siderust_frame_t frame_id()
SFINAE helper: every frame tag must provide these static members.
Definition frames.hpp:27
A spherical position (direction + distance), compile-time tagged.