#pragma once #include "config/types.h" #include #include #include #include #include #include #include struct Arc : public std::pair { template requires(Lc >= 2, Le >= 2) Arc(const glm::vec & centre, const glm::vec & e0p, const glm::vec & e1p) : Arc {RelativePosition2D {e0p.xy() - centre.xy()}, RelativePosition2D {e1p.xy() - centre.xy()}} { } Arc(const RelativePosition2D & dir0, const RelativePosition2D & dir1); Arc(Angle anga, Angle angb); auto operator[](bool getSecond) const { return getSecond ? second : first; } [[nodiscard]] constexpr float length() const { return second - first; } }; constexpr const RelativePosition3D up {0, 0, 1}; // NOLINT(readability-identifier-length) constexpr const RelativePosition3D down {0, 0, -1}; constexpr const RelativePosition3D north {0, 1, 0}; constexpr const RelativePosition3D south {0, -1, 0}; constexpr const RelativePosition3D east {1, 0, 0}; constexpr const RelativePosition3D west {-1, 0, 0}; constexpr auto half_pi {glm::half_pi()}; constexpr auto quarter_pi {half_pi / 2}; constexpr auto pi {glm::pi()}; // NOLINT(readability-identifier-length) constexpr auto two_pi {glm::two_pi()}; constexpr auto degreesToRads = pi / 180.F; constexpr auto earthMeanRadius = 6371.01F; // In km constexpr auto astronomicalUnit = 149597890.F; // In km template constexpr GlobalPosition operator+(const GlobalPosition & global, const RelativePosition & relative) { return global + GlobalPosition(glm::round(relative)); } template constexpr GlobalPosition operator+(const GlobalPosition & global, const CalcPosition & relative) { return global + GlobalPosition(relative); } template constexpr GlobalPosition operator-(const GlobalPosition & global, const RelativePosition & relative) { return global - GlobalPosition(glm::round(relative)); } template constexpr GlobalPosition operator-(const GlobalPosition & global, const CalcPosition & relative) { return global - GlobalPosition(relative); } template constexpr RelativePosition difference(const glm::vec & globalA, const glm::vec & globalB) { return globalA - globalB; } glm::mat4 flat_orientation(const Rotation3D & diff); namespace { // Helpers // C++ wrapper for C's sincosf, but with references, not pointers template constexpr void sincos(T angle, T & sinOut, T & cosOut) { if consteval { sinOut = std::sin(angle); cosOut = std::cos(angle); } else { if constexpr (std::is_same_v) { ::sincosf(angle, &sinOut, &cosOut); } else if constexpr (std::is_same_v) { ::sincos(angle, &sinOut, &cosOut); } else if constexpr (std::is_same_v) { ::sincosl(angle, &sinOut, &cosOut); } } } template constexpr auto sincos(const T angle) { glm::vec<2, T, Q> sincosOut {}; sincos(angle, sincosOut.x, sincosOut.y); return sincosOut; } // Helper to lookup into a matrix given an xy vector coordinate template constexpr auto & operator^(glm::mat & matrix, const glm::vec<2, I> rowCol) { return matrix[rowCol.x][rowCol.y]; } // Create a matrix for the angle, given the targets into the matrix template constexpr auto rotation(const T angle, const glm::vec<2, I> cos1, const glm::vec<2, I> sin1, const glm::vec<2, I> cos2, const glm::vec<2, I> negSin1) { glm::mat out(1); sincos(angle, out ^ sin1, out ^ cos1); out ^ cos2 = out ^ cos1; out ^ negSin1 = -(out ^ sin1); return out; } } // Create a flat transformation matrix template requires(D >= 2) constexpr auto rotate_flat(const T angle) { return rotation(angle, {0, 0}, {0, 1}, {1, 1}, {1, 0}); } // Create a yaw transformation matrix template requires(D >= 2) constexpr auto rotate_yaw(const T angle) { return rotation(angle, {0, 0}, {1, 0}, {1, 1}, {0, 1}); } // Create a roll transformation matrix template requires(D >= 3) constexpr auto rotate_roll(const T angle) { return rotation(angle, {0, 0}, {2, 0}, {2, 2}, {0, 2}); } // Create a pitch transformation matrix template requires(D >= 3) constexpr auto rotate_pitch(const T angle) { return rotation(angle, {1, 1}, {1, 2}, {2, 2}, {2, 1}); } // Create a combined yaw, pitch, roll transformation matrix template requires(D >= 3) constexpr auto rotate_ypr(const glm::vec<3, T, Q> & angles) { return rotate_yaw(angles.y) * rotate_pitch(angles.x) * rotate_roll(angles.z); } template requires(D >= 3) constexpr auto rotate_yp(const T yaw, const T pitch) { return rotate_yaw(yaw) * rotate_pitch(pitch); } template requires(D >= 3) constexpr auto rotate_yp(const glm::vec<2, T, Q> & angles) { return rotate_yp(angles.y, angles.x); } template requires(D >= 2) constexpr auto vector_yaw(const glm::vec & diff) { return std::atan2(diff.x, diff.y); } template requires(D >= 3) constexpr auto vector_pitch(const glm::vec & diff) { return std::atan(diff.z); } template constexpr glm::vec<2, T, Q> vector_normal(const glm::vec<2, T, Q> & vector) { return {-vector.y, vector.x}; }; template constexpr auto round_frac(const T value, const T frac) { return std::round(value / frac) * frac; } template requires requires(T value) { value * value; } constexpr auto sq(T value) { return value * value; } template constexpr glm::vec<3, int64_t, Q> crossProduct(const glm::vec<3, int64_t, Q> & valueA, const glm::vec<3, int64_t, Q> & valueB) { return { (valueA.y * valueB.z) - (valueA.z * valueB.y), (valueA.z * valueB.x) - (valueA.x * valueB.z), (valueA.x * valueB.y) - (valueA.y * valueB.x), }; } template constexpr glm::vec<3, T, Q> crossProduct(const glm::vec<3, T, Q> & valueA, const glm::vec<3, T, Q> & valueB) { return crossProduct(valueA, valueB); } template constexpr glm::vec<3, T, Q> crossProduct(const glm::vec<3, T, Q> & valueA, const glm::vec<3, T, Q> & valueB) { return glm::cross(valueA, valueB); } template constexpr auto ratio(const Ta valueA, const Tb valueB) { return (static_cast(valueA) / static_cast(valueB)); } template constexpr auto ratio(const glm::vec<2, T, Q> & value) { return ratio(value.x, value.y); } template constexpr auto perspective_divide(const glm::vec<4, T, Q> & value) { return value / value.w; } template constexpr glm::vec operator||(const glm::vec valueA, const glm::vec valueB) { return {valueA, valueB}; } template constexpr glm::vec operator||(const glm::vec valueA, const T valueB) { return {valueA, valueB}; } template constexpr glm::vec perspectiveMultiply(const glm::vec & base, const glm::mat & mutation) { const auto mutated = mutation * (base || T(1)); return mutated / mutated.w; } template constexpr glm::vec perspectiveApply(glm::vec & base, const glm::mat & mutation) { return base = perspectiveMultiply(base, mutation); } template constexpr T normalize(T ang) { while (ang > glm::pi()) { ang -= glm::two_pi(); } while (ang <= -glm::pi()) { ang += glm::two_pi(); } return ang; } template using CalcType = std::conditional_t, T, int64_t>; template [[nodiscard]] constexpr std::optional> linesIntersectAt(const glm::vec<2, T, Q> Aabs, const glm::vec<2, T, Q> Babs, const glm::vec<2, T, Q> Cabs, const glm::vec<2, T, Q> Dabs) { using CT = CalcType; using CVec = glm::vec<2, CT, Q>; // Line AB represented as a1x + b1y = c1 const CVec Brel = Babs - Aabs; const CT a1 = Brel.y; const CT b1 = -Brel.x; // Line CD represented as a2x + b2y = c2 const CVec Crel = Cabs - Aabs, Del = Dabs - Aabs; const CT a2 = Del.y - Crel.y; const CT b2 = Crel.x - Del.x; const CT c2 = (a2 * Crel.x) + (b2 * Crel.y); const auto determinant = (a1 * b2) - (a2 * b1); if (determinant == 0) { return std::nullopt; } return Aabs + CVec {(b1 * c2) / -determinant, (a1 * c2) / determinant}; } template std::pair, bool> find_arc_centre(glm::vec<2, T, Q> start, Rotation2D startDir, glm::vec<2, T, Q> end, Rotation2D endDir) { const auto det = endDir.x * startDir.y - endDir.y * startDir.x; if (det != 0) { // near parallel line will yield noisy results const glm::vec<2, RelativeDistance, Q> d = end - start; const auto u = (d.y * endDir.x - d.x * endDir.y) / det; return {start + glm::vec<2, T, Q>(startDir * u), u < 0}; } throw std::runtime_error("no intersection"); } template std::pair, bool> find_arc_centre(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, Angle entrye) { if (start == end) { return {start, false}; } return find_arc_centre(start, sincos(entrys + half_pi), end, sincos(entrye - half_pi)); } template Angle find_arcs_radius(glm::vec<2, T, Q> start, Rotation2D ad, glm::vec<2, T, Q> end, Rotation2D bd) { using std::sqrt; // Calculates path across both arcs along the normals... pythagorean theorem... for some known radius r // (2r)^2 = ((m + (X*r)) - (o + (Z*r)))^2 + ((n + (Y*r)) - (p + (W*r)))^2 // According to symbolabs.com equation tool, that solves for r to give: // r=(-2 m X+2 X o+2 m Z-2 o Z-2 n Y+2 Y p+2 n W-2 p W-sqrt((2 m X-2 X o-2 m Z+2 o Z+2 n Y-2 Y p-2 n W+2 p W)^(2)-4 // (X^(2)-2 X Z+Z^(2)+Y^(2)-2 Y W+W^(2)-4) (m^(2)-2 m o+o^(2)+n^(2)-2 n p+p^(2))))/(2 (X^(2)-2 X Z+Z^(2)+Y^(2)-2 Y // W+W^(2)-4)) // Locally simplified to work relative, removing one half of the problem and operating on relative positions. // These exist cos limitations of online formula rearrangement, and I'm OK with that. const RelativePosition2D diff {end - start}; const auto &o {diff.x}, &p {diff.y}; const auto &X {ad.x}, &Y {ad.y}, &Z {bd.x}, &W {bd.y}; return (-2 * X * o + 2 * o * Z - 2 * Y * p + 2 * p * W - sqrt(sq(2 * X * o - 2 * o * Z + 2 * Y * p - 2 * p * W) - (4 * (sq(X) - 2 * X * Z + sq(Z) + sq(Y) - 2 * Y * W + sq(W) - 4) * (sq(o) + sq(p))))) / (2 * (sq(X) - 2 * X * Z + sq(Z) + sq(Y) - 2 * Y * W + sq(W) - 4)); } template std::pair find_arcs_radius(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, Angle entrye) { const auto getrad = [&](auto leftOrRight) { return find_arcs_radius(start, sincos(entrys + leftOrRight), end, sincos(entrye + leftOrRight)); }; return {getrad(-half_pi), getrad(half_pi)}; } template auto midpoint(const std::pair & v) { return std::midpoint(v.first, v.second); } // std::pow is not constexpr template requires requires(T n) { n *= n; } constexpr T pow(const T base, std::integral auto exp) { T res {1}; while (exp--) { res *= base; } return res; } // Conversions template constexpr auto mph_to_ms(T v) { return v / 2.237L; } template constexpr auto kph_to_ms(T v) { return v / 3.6L; } // ... literals are handy for now, probably go away when we load stuff externally float operator"" _mph(const long double v); float operator"" _kph(const long double v); constexpr float operator"" _degrees(long double degrees) { return static_cast(degrees) * degreesToRads; }