File indexing completed on 2024-12-15 04:01:13

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #pragma once
0008 
0009 #include <tuple>
0010 #include <utility>
0011 #include <cmath>
0012 #include <QMetaType>
0013 #include <QColor>
0014 
0015 #include "math/math.hpp"
0016 class QVector2D; class QVector3D; class QVector4D; class QPointF;
0017 
0018 namespace glaxnimate::math {
0019 
0020 
0021 namespace detail {
0022 
0023 template<class V> struct VecSize;
0024 template<> struct VecSize<QVector2D> { static constexpr int value = 2; };
0025 template<> struct VecSize<QVector3D> { static constexpr int value = 3; };
0026 template<> struct VecSize<QVector4D> { static constexpr int value = 4; };
0027 template<> struct VecSize<QPointF> { static constexpr int value = 2; };
0028 
0029 template<class VecT> struct VecScalar
0030 {
0031     using type = std::decay_t<decltype(std::declval<VecT>()[0])>;
0032 };
0033 
0034 
0035 template<> struct VecScalar<QPointF> { using type = qreal; };
0036 template<> struct VecScalar<QSizeF> { using type = qreal; };
0037 
0038 template<class VecT>
0039 using scalar_type = typename detail::VecScalar<std::decay_t<VecT>>::type;
0040 
0041 template<class VecT>
0042 constexpr const scalar_type<VecT>& get(const VecT& vt, int off) noexcept
0043 {
0044     return reinterpret_cast<const typename VecScalar<VecT>::type*>(&vt)[off];
0045 }
0046 
0047 template<class VecT>
0048 constexpr scalar_type<VecT>& get(VecT& vt, int off) noexcept
0049 {
0050     return reinterpret_cast<typename VecScalar<VecT>::type*>(&vt)[off];
0051 }
0052 
0053 template<class VecT, int d>
0054 struct LengthHelper
0055 {
0056     static constexpr scalar_type<VecT> sumsq(const VecT& v) noexcept
0057     {
0058         return get(v, d-1) * get(v, d-1) + LengthHelper<VecT, d-1>::sumsq(v);
0059     }
0060 };
0061 
0062 template<class VecT>
0063 struct LengthHelper<VecT, 1>
0064 {
0065     static constexpr scalar_type<VecT> sumsq(const VecT& v) noexcept
0066     {
0067         return get(v, 0) * get(v, 0);
0068     }
0069 };
0070 
0071 
0072 } // namespace detail
0073 
0074 using detail::scalar_type;
0075 using detail::get;
0076 
0077 inline QColor lerp(const QColor& a, const QColor& b, double factor)
0078 {
0079     return QColor::fromRgbF(
0080         lerp(a.redF(), b.redF(), factor),
0081         lerp(a.greenF(), b.greenF(), factor),
0082         lerp(a.blueF(), b.blueF(), factor),
0083         lerp(a.alphaF(), b.alphaF(), factor)
0084     );
0085 }
0086 
0087 template<class T>
0088 constexpr std::vector<T> lerp(const std::vector<T>& a, const std::vector<T>& b, double factor)
0089 {
0090     if ( a.size() != b.size() )
0091         return a;
0092     std::vector<T> c;
0093     c.reserve(a.size());
0094     for ( std::size_t i = 0; i < a.size(); i++ )
0095         c.push_back(lerp(a[i], b[i], factor));
0096     return c;
0097 }
0098 
0099 template<class VecT>
0100 constexpr scalar_type<VecT> length_squared(const VecT& v)
0101 {
0102     return detail::LengthHelper<VecT, detail::VecSize<VecT>::value>::sumsq(v);
0103 }
0104 
0105 /**
0106  * \brief 2-norm length of a vector
0107  */
0108 template<class VecT>
0109 constexpr scalar_type<VecT> length(const VecT& v)
0110 {
0111     return std::sqrt(length_squared(v));
0112 }
0113 
0114 /**
0115  * \brief 2-norm distance between two points
0116  */
0117 template<class VecT>
0118 constexpr scalar_type<VecT> distance(const VecT& a, const VecT& b)
0119 {
0120     return length(b - a);
0121 }
0122 
0123 
0124 /**
0125  * \brief Angle to the x axis of a 2D cartesian vector
0126  */
0127 template<class VecT>
0128 scalar_type<VecT> angle(const VecT& cartesian)
0129 {
0130     return std::atan2(detail::get(cartesian, 1), detail::get(cartesian, 0));
0131 }
0132 
0133 template<class VecT>
0134 VecT from_polar(scalar_type<VecT> length, scalar_type<VecT> angle)
0135 {
0136     return {std::cos(angle) * length, std::sin(angle) * length};
0137 }
0138 
0139 template<class VecT>
0140 struct PolarVector
0141 {
0142     using scalar = scalar_type<VecT>;
0143 
0144     scalar length;
0145     scalar angle;
0146 
0147     constexpr PolarVector() noexcept
0148     : PolarVector(0, 0)
0149     {}
0150 
0151     constexpr PolarVector(scalar length, scalar angle) noexcept
0152     : length(length), angle(angle)
0153     {}
0154 
0155     PolarVector(const VecT& cartesian)
0156     : length(math::length(cartesian)),
0157       angle(math::angle(cartesian))
0158     {}
0159 
0160     VecT to_cartesian() const
0161     {
0162         return {std::cos(angle) * length, std::sin(angle) * length};
0163     }
0164 };
0165 
0166 template<class VecT>
0167 bool fuzzy_compare(const VecT& a, const VecT& b)
0168 {
0169     for ( int i = 0; i < detail::VecSize<VecT>::value; i++ )
0170         if ( !qFuzzyCompare(detail::get(a, i), detail::get(b, i)) )
0171             return false;
0172     return true;
0173 }
0174 
0175 } // namespace glaxnimate::math