.. _program_listing_file_include_ripple_math_quat.hpp: Program Listing for File quat.hpp ================================= |exhale_lsh| :ref:`Return to documentation for file ` (``include/ripple/math/quat.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #ifndef RIPPLE_MATH_QUAT_HPP #define RIPPLE_MATH_QUAT_HPP #include #include #include #include #include #include #include namespace ripple { template struct Quat : public PolymorphicLayout> { private: /*==--- [constants] ------------------------------------------------------==*/ static constexpr auto elements = 4; //==--- [aliases] --------------------------------------------------------==// // clang-format off using Descriptor = StorageDescriptor>; using Storage = typename Descriptor::Storage; using Value = std::decay_t; using WAccessor = StructAccessor; using XAccessor = StructAccessor; using YAccessor = StructAccessor; using ZAccessor = StructAccessor; // clang-format on // clang-format on template friend struct Quat; template friend struct LayoutTraits; public: union { Storage storage; WAccessor w; XAccessor x; YAccessor y; ZAccessor z; }; /*==--- [construction] ---------------------------------------------------==*/ ripple_all constexpr Quat() noexcept {} ripple_all constexpr Quat(T val) noexcept { unrolled_for([&](auto i) { storage.template get<0, i>() = val; }); } template = 0> ripple_all constexpr Quat(Values&&... values) noexcept { const auto v = Tuple{values...}; constexpr size_t arg_count = sizeof...(Values); constexpr size_t extra = elements - arg_count; unrolled_for( [&](auto i) { storage.template get<0, i>() = get(v); }); unrolled_for([&](auto i) { constexpr size_t idx = i + arg_count; storage.template get<0, idx>() = Value{0}; }); } ripple_all constexpr Quat(Storage storage) noexcept : storage{storage} {} ripple_all constexpr Quat(const Quat& other) noexcept : storage{other.storage} {} ripple_all constexpr Quat(Quat&& other) noexcept : storage{ripple_move(other.storage)} {} template ripple_all constexpr Quat(const Quat& other) noexcept : storage{other.storage} {} template ripple_all constexpr Quat(Quat&& other) noexcept : storage{ripple_move(other.storage)} {} /*==--- [operator overloads] ---------------------------------------------==*/ ripple_all auto operator=(const Quat& other) noexcept -> Quat& { storage = other.storage; return *this; } ripple_all auto operator=(Quat&& other) noexcept -> Quat& { storage = ripple_move(other.storage); return *this; } template ripple_all auto operator=(const Quat& other) noexcept -> Quat& { unrolled_for([&](auto i) { storage.template get<0, i>() = other.storage.template get<0, i>(); }); return *this; } template ripple_all auto operator=(Quat&& other) noexcept -> Quat& { storage = ripple_move(other.storage); return *this; } ripple_all auto operator[](size_t i) noexcept -> Value& { return storage.template get<0>(i); } /*==--- [interface] ------------------------------------------------------==*/ ripple_all auto scalar() noexcept -> Value& { return storage.template get<0, 0>(); } ripple_all auto scalar() const noexcept -> const Value& { return storage.template get<0, 0>(); } template ripple_all auto vec_component() noexcept -> Value& { return storage.template get<0, I + 1>(); } template ripple_all auto vec_component() const noexcept -> const Value& { return storage.template get<0, I + 1>(); } ripple_all auto vec_component(size_t i) noexcept -> Value& { return storage.template get<0>(i + 1); } ripple_all auto vec_component(size_t i) const noexcept -> const Value& { return storage.template get<0>(i + 1); } ripple_all auto length_squared() const noexcept -> Value { return w * w + x * x + y * y + z * z; } ripple_all auto length() const noexcept -> Value { return std::sqrt(length_squared()); } ripple_all auto normalize() noexcept -> void { const auto scale = Value{1} / length(); w *= scale; x *= scale; y *= scale; z *= scale; } ripple_all auto invert() noexcept -> void { const auto scale = Value{1} / length_squared(); w *= scale; x *= -scale; y *= -scale; z *= -scale; } ripple_all auto inverse() const noexcept -> Quat { const auto scale = Value{-1} / length_squared(); return Quat{ w * -scale, x * scale, y * scale, z * scale}; } template ripple_all auto operator*(const Quat& q) const noexcept -> Quat { // clang-format off return Quat{ w * q.w - x * q.x - y * q.y - z * q.z, w * q.x + x * q.w + y * q.z - z * q.y, w * q.y - x * q.z + y * q.w + z * q.x, w * q.z + x * q.y - y * q.x + z * q.w}; // clang-format on } }; template ripple_all auto to_mat2x2(const Quat& q) noexcept -> Mat { const auto xy = q.x * q.y; const auto wz = q.w * q.z; // clang-format off return Mat{ q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z, T{2} * (xy - wz), T{2} * (xy + wz), q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z}; // clang-format on } template ripple_all auto rotate(const Quat& q, const Vec3d& v) noexcept -> Vec3d, ContiguousOwned> { using TT = std::decay_t; // clang-format off /* First compute the temp cross product and addition. t = r x v + w * v */ const auto t = Vec3d{ q.w * v.x + q.y * v.z - q.z * v.y, q.w * v.y + q.z * v.x - q.x * v.z, q.w * v.z + q.x * v.y - q.y * v.x }; /* Note: We expand out the multiplication here, so that we don't store any temporary results which may bloat the register usage. */ return Vec3d{ v.x + U{2} * (q.y * t.z - q.z * t.y), v.y + U{2} * (q.z * t.x - q.x * t.z), v.z + U{2} * (q.x * t.y - q.y * t.x) }; // clang-format on } template ripple_all auto rotate(const Quat& q, const Vec2d& v) noexcept -> Vec2d, ContiguousOwned> { using TT = std::decay_t; // clang-format off /* First compute the temp cross product and addition. t = r x v + w * v */ const TT x = q.w * v.x - q.z * v.y; const TT y = q.w * v.y + q.z * v.x; const TT z = q.x * v.y - q.y * v.x; /* Note: We expand out the multiplication here, so that we don't store any temporary results which may bloat the register usage. */ return Vec2d{ v.x + U{2} * (q.y * z - q.z * y), v.y + U{2} * (q.z * x - q.x * z) }; // clang-format on } template ripple_all auto create_quat(const Vec3d& v1, const Vec3d& v2) noexcept -> Quat> { using TT = std::decay_t; constexpr TT tol = 0.999999999999999999; const TT t = math::dot(v1, v2); Quat q{0, 0, 0, 0}; if (t > tol) { q.w = 1; } else if (t < -tol) { // Here we have to check if the cross with the x/y unit vector is valid, // and then define the result in terms of that. // We would want to set the quat from the axis (the vector we define // here), // and the angle (180), but sin and cos terms reduce to 1 and 0, so we // don't need to do that. const TT scale = TT{1} / v1.length(); if (std::sqrt(v1.z * v1.z + v1.y * v1.y) >= TT{1} - tol) { q.y = -v1.z * scale; q.z = v1.y * scale; } else { q.x = v1.z * scale; q.z = -v1.x * scale; } } else { // clang-format off q.w = std::sqrt(v1.length_squared() * v2.length_squared()) + t; q.x = v1.y * v2.z - v1.z *v2.y; q.y = v1.z * v2.x - v1.x *v2.z; q.z = v1.x * v2.y - v1.y *v2.x; q.normalize(); // clang-format on } return q; } template ripple_all auto create_quat(const Vec2d& v1, const Vec2d& v2) noexcept -> Quat> { using TT = std::decay_t; constexpr TT tol = 0.999999999999999999; const TT t = math::dot(v1, v2); Quat q{0, 0, 0, 0}; if (t > tol) { q.w = 1; } else if (t < -tol) { const auto scale = TT{1} / v1.length(); if (v1.y >= TT{1} - tol) { q.z = v1.y * scale; } else { q.z = -v1.x * scale; } } else { // clang-format off q.w = std::sqrt(v1.length_squared() * v2.length_squared()) + t; q.z = v1.x * v2.y - v1.y * v2.x; q.normalize(); } return q; } } // namespace ripple #endif // namespace RIPPLE_MATH_MAT_HPP