Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
Geometry.hpp
Go to the documentation of this file.
1// Vulcan Geometry Primitives Module
2// Spatial computation utilities for guidance, visibility, and sensor FOV
3#pragma once
4
8
9#include <janus/math/Arithmetic.hpp>
10#include <janus/math/Linalg.hpp>
11#include <janus/math/Logic.hpp>
12#include <janus/math/Trig.hpp>
13
15
16// =============================================================================
17// Line-of-Sight Computations
18// =============================================================================
19
28template <typename Scalar>
29Scalar slant_range(const Vec3<Scalar> &r_observer,
30 const Vec3<Scalar> &r_target) {
31 Vec3<Scalar> delta = r_target - r_observer;
32 return janus::norm(delta);
33}
34
50template <typename Scalar>
51Vec2<Scalar> los_angles(const Vec3<Scalar> &r_observer,
52 const Vec3<Scalar> &r_target) {
53 Vec3<Scalar> delta = r_target - r_observer;
54
55 Scalar dx = delta(0);
56 Scalar dy = delta(1);
57 Scalar dz = delta(2);
58
59 // Horizontal distance
60 Scalar horiz_dist = janus::sqrt(dx * dx + dy * dy);
61 Scalar range = janus::norm(delta);
62
63 // Avoid division by zero
64 Scalar eps = Scalar(1e-12);
65 Scalar is_zero = range < eps;
66
67 // Azimuth: atan2(y, x)
68 Scalar azimuth = janus::atan2(dy, dx);
69 azimuth = janus::where(is_zero, Scalar(0), azimuth);
70
71 // Elevation: atan2(z, horizontal_distance)
72 // Negative z means target is above in NED frame
73 Scalar elevation = janus::atan2(-dz, horiz_dist);
74 elevation = janus::where(is_zero, Scalar(0), elevation);
75
76 Vec2<Scalar> angles;
77 angles << azimuth, elevation;
78 return angles;
79}
80
94template <typename Scalar>
95Vec2<Scalar> los_rate(const Vec3<Scalar> &r_obs, const Vec3<Scalar> &v_obs,
96 const Vec3<Scalar> &r_tgt, const Vec3<Scalar> &v_tgt) {
97 // Relative position and velocity
98 Vec3<Scalar> r_rel = r_tgt - r_obs;
99 Vec3<Scalar> v_rel = v_tgt - v_obs;
100
101 Scalar dx = r_rel(0);
102 Scalar dy = r_rel(1);
103 Scalar dz = r_rel(2);
104 Scalar vx = v_rel(0);
105 Scalar vy = v_rel(1);
106 Scalar vz = v_rel(2);
107
108 // Range and range rate
109 Scalar range_sq = dx * dx + dy * dy + dz * dz;
110 Scalar range = janus::sqrt(range_sq);
111 Scalar horiz_sq = dx * dx + dy * dy;
112 Scalar horiz = janus::sqrt(horiz_sq);
113
114 // Avoid division by zero
115 Scalar eps = Scalar(1e-12);
116 Scalar is_range_zero = range < eps;
117 Scalar is_horiz_zero = horiz < eps;
118
119 // Azimuth rate: d/dt[atan2(y,x)] = (x*vy - y*vx) / (x^2 + y^2)
120 Scalar az_rate = (dx * vy - dy * vx) / horiz_sq;
121 az_rate = janus::where(is_horiz_zero, Scalar(0), az_rate);
122
123 // Elevation rate: d/dt[atan2(-z, horiz)]
124 // = d/dt[atan2(-z, sqrt(x^2+y^2))]
125 // = (horiz * (-vz) - (-z) * d_horiz/dt) / (horiz^2 + z^2)
126 // where d_horiz/dt = (x*vx + y*vy) / horiz
127 Scalar d_horiz_dt = (dx * vx + dy * vy) / horiz;
128 d_horiz_dt = janus::where(is_horiz_zero, Scalar(0), d_horiz_dt);
129
130 Scalar el_rate = (-horiz * vz + dz * d_horiz_dt) / range_sq;
131 el_rate = janus::where(is_range_zero, Scalar(0), el_rate);
132
133 Vec2<Scalar> rates;
134 rates << az_rate, el_rate;
135 return rates;
136}
137
138// =============================================================================
139// Ray Intersections
140// =============================================================================
141
155template <typename Scalar>
156Scalar ray_sphere_intersection(const Vec3<Scalar> &origin,
157 const Vec3<Scalar> &direction,
158 const Vec3<Scalar> &center,
159 const Scalar &radius) {
160 // Vector from ray origin to sphere center
161 Vec3<Scalar> oc = center - origin;
162
163 // Project oc onto ray direction
164 Scalar tca = janus::dot(oc, direction);
165
166 // Distance squared from sphere center to ray
167 Scalar oc_sq = janus::dot(oc, oc);
168 Scalar d_sq = oc_sq - tca * tca;
169
170 // Check if ray misses sphere
171 Scalar r_sq = radius * radius;
172 Scalar misses = d_sq > r_sq;
173
174 // Distance from closest approach to intersection points
175 Scalar thc = janus::sqrt(janus::abs(r_sq - d_sq));
176
177 // Two intersection points at t = tca +/- thc
178 // We want the nearest positive intersection
179 Scalar t0 = tca - thc;
180 Scalar t1 = tca + thc;
181
182 // If t0 is positive, use it; otherwise use t1 if positive
183 Scalar t0_positive = t0 > Scalar(0);
184 Scalar t1_positive = t1 > Scalar(0);
185 Scalar t = janus::where(t0_positive, t0, t1);
186
187 // Return -1 if no valid intersection
188 Scalar no_intersection = misses + (!t0_positive * !t1_positive) > Scalar(0);
189 return janus::where(no_intersection, Scalar(-1), t);
190}
191
204template <typename Scalar>
205Scalar ray_plane_intersection(const Vec3<Scalar> &origin,
206 const Vec3<Scalar> &direction,
207 const Vec3<Scalar> &plane_normal,
208 const Vec3<Scalar> &plane_point) {
209 // Compute denominator (dot of direction and normal)
210 Scalar denom = janus::dot(direction, plane_normal);
211
212 // Check if ray is parallel to plane
213 Scalar eps = Scalar(1e-12);
214 Scalar is_parallel = janus::abs(denom) < eps;
215
216 // Distance from origin to plane along normal
217 Vec3<Scalar> diff = plane_point - origin;
218 Scalar t = janus::dot(diff, plane_normal) / denom;
219
220 // Return -1 if parallel or intersection is behind ray origin
221 Scalar invalid = is_parallel + (t < Scalar(0)) > Scalar(0);
222 return janus::where(invalid, Scalar(-1), t);
223}
224
239template <typename Scalar>
240Scalar point_in_cone(const Vec3<Scalar> &point, const Vec3<Scalar> &apex,
241 const Vec3<Scalar> &axis, const Scalar &half_angle) {
242 // Vector from apex to point
243 Vec3<Scalar> to_point = point - apex;
244 Scalar dist = janus::norm(to_point);
245
246 // Avoid division by zero
247 Scalar eps = Scalar(1e-12);
248 Scalar is_at_apex = dist < eps;
249
250 // Normalize direction to point
251 Vec3<Scalar> to_point_norm = to_point / dist;
252
253 // Angle between axis and direction to point
254 Scalar cos_angle = janus::dot(axis, to_point_norm);
255 Scalar cos_half_angle = janus::cos(half_angle);
256
257 // Point is inside if angle is less than half_angle
258 // (cos_angle > cos_half_angle since cos is decreasing)
259 Scalar inside = cos_angle > cos_half_angle;
260
261 // Point at apex is considered inside
262 return janus::where(is_at_apex, Scalar(1), inside);
263}
264
265// =============================================================================
266// Projections
267// =============================================================================
268
278template <typename Scalar>
279Vec3<Scalar> project_to_plane(const Vec3<Scalar> &point,
280 const Vec3<Scalar> &plane_normal,
281 const Vec3<Scalar> &plane_point) {
282 // Distance from point to plane along normal
283 Vec3<Scalar> diff = point - plane_point;
284 Scalar dist = janus::dot(diff, plane_normal);
285
286 // Subtract the normal component
287 return point - dist * plane_normal;
288}
289
301template <typename Scalar>
302LLA<Scalar> ground_track_point(const Vec3<Scalar> &r_ecef,
303 const EarthModel &model = EarthModel::WGS84()) {
304 // Convert to LLA and set altitude to zero
305 LLA<Scalar> lla = ecef_to_lla<Scalar>(r_ecef, model);
306 return LLA<Scalar>(lla.lon, lla.lat, Scalar(0));
307}
308
320template <typename Scalar>
321Vec3<Scalar> ground_track_ecef(const Vec3<Scalar> &r_ecef,
322 const EarthModel &model = EarthModel::WGS84()) {
323 LLA<Scalar> ground = ground_track_point<Scalar>(r_ecef, model);
324 return lla_to_ecef<Scalar>(ground, model);
325}
326
327} // namespace vulcan::geometry
Definition Geometry.hpp:14
LLA< Scalar > ground_track_point(const Vec3< Scalar > &r_ecef, const EarthModel &model=EarthModel::WGS84())
Project ECEF position to ellipsoid surface (ground track point).
Definition Geometry.hpp:302
Scalar point_in_cone(const Vec3< Scalar > &point, const Vec3< Scalar > &apex, const Vec3< Scalar > &axis, const Scalar &half_angle)
Check if a point lies within a cone (e.g., sensor FOV).
Definition Geometry.hpp:240
Vec2< Scalar > los_angles(const Vec3< Scalar > &r_observer, const Vec3< Scalar > &r_target)
Compute line-of-sight azimuth and elevation angles.
Definition Geometry.hpp:51
Vec3< Scalar > ground_track_ecef(const Vec3< Scalar > &r_ecef, const EarthModel &model=EarthModel::WGS84())
Compute ground track point and return as ECEF.
Definition Geometry.hpp:321
Scalar slant_range(const Vec3< Scalar > &r_observer, const Vec3< Scalar > &r_target)
Compute slant range (distance) between two points.
Definition Geometry.hpp:29
Scalar ray_sphere_intersection(const Vec3< Scalar > &origin, const Vec3< Scalar > &direction, const Vec3< Scalar > &center, const Scalar &radius)
Ray-sphere intersection.
Definition Geometry.hpp:156
Vec3< Scalar > project_to_plane(const Vec3< Scalar > &point, const Vec3< Scalar > &plane_normal, const Vec3< Scalar > &plane_point)
Project a point onto a plane.
Definition Geometry.hpp:279
Vec2< Scalar > los_rate(const Vec3< Scalar > &r_obs, const Vec3< Scalar > &v_obs, const Vec3< Scalar > &r_tgt, const Vec3< Scalar > &v_tgt)
Compute line-of-sight angular rates.
Definition Geometry.hpp:95
Scalar ray_plane_intersection(const Vec3< Scalar > &origin, const Vec3< Scalar > &direction, const Vec3< Scalar > &plane_normal, const Vec3< Scalar > &plane_point)
Ray-plane intersection.
Definition Geometry.hpp:205
Scalar range(const Vec3< Scalar > &r1, const Vec3< Scalar > &r2)
Definition FrameKinematics.hpp:178
Vec3< Scalar > lla_to_ecef(const LLA< Scalar > &lla, const EarthModel &m=EarthModel::WGS84())
Definition Geodetic.hpp:152
LLA< Scalar > ecef_to_lla(const Vec3< Scalar > &r, const EarthModel &m=EarthModel::WGS84())
Definition Geodetic.hpp:78
Definition EarthModel.hpp:27
static constexpr EarthModel WGS84()
WGS84 reference ellipsoid (most common for GPS/navigation).
Definition EarthModel.hpp:45
Definition Geodetic.hpp:27
Scalar lon
Longitude [rad], positive East, range [-π, π].
Definition Geodetic.hpp:28
Scalar lat
Geodetic latitude [rad], range [-π/2, π/2].
Definition Geodetic.hpp:29