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