404 lines
14 KiB
C++
404 lines
14 KiB
C++
/**
|
|
This file is a part of our_dick
|
|
Copyright (C) 2020 rexy712
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef REXY_QUAT_TPP
|
|
#define REXY_QUAT_TPP
|
|
|
|
#include <cmath> //sin, cos, tan, sqrt, etc
|
|
#include "mat.hpp"
|
|
#include "vec.hpp"
|
|
|
|
namespace math{
|
|
|
|
template<typename T>
|
|
constexpr quaternion<T>::quaternion():
|
|
quaternion(id_initialize){}
|
|
template<typename T>
|
|
constexpr quaternion<T>::quaternion(detail::zero_initialize_t):
|
|
m_data{0, 0, 0, 0}{}
|
|
template<typename T>
|
|
constexpr quaternion<T>::quaternion(detail::id_initialize_t):
|
|
m_data{1, 0, 0, 0}{}
|
|
template<typename T>
|
|
constexpr quaternion<T>::quaternion(detail::no_initialize_t){}
|
|
|
|
template<typename T>
|
|
constexpr quaternion<T>::quaternion(detail::manual_initialize_t,
|
|
value_type w, value_type x,
|
|
value_type y, value_type z):
|
|
m_data{w, x, y, z}{}
|
|
|
|
template<typename T>
|
|
quaternion<T>::quaternion(value_type bank, value_type heading, value_type attitude){
|
|
bank /= value_type{2};
|
|
heading /= value_type{2};
|
|
attitude /= value_type{2};
|
|
value_type cos_heading = std::cos(heading);
|
|
value_type sin_heading = std::sin(heading);
|
|
value_type cos_attitude = std::cos(attitude);
|
|
value_type sin_attitude = std::sin(attitude);
|
|
value_type cos_bank = std::cos(bank);
|
|
value_type sin_bank = std::sin(bank);
|
|
|
|
m_data[0] = (cos_heading * cos_attitude * cos_bank) - (sin_heading * sin_attitude * sin_bank);
|
|
m_data[1] = (sin_heading * sin_attitude * cos_bank) + (cos_heading * cos_attitude * sin_bank);
|
|
m_data[2] = (sin_heading * cos_attitude * cos_bank) + (cos_heading * sin_attitude * sin_bank);
|
|
m_data[3] = (cos_heading * sin_attitude * cos_bank) - (sin_heading * cos_attitude * sin_bank);
|
|
}
|
|
template<typename T>
|
|
quaternion<T>::quaternion(const vec3<T>& angles):
|
|
quaternion(angles.x(), angles.y(), angles.z()){}
|
|
template<typename T>
|
|
quaternion<T>::quaternion(value_type angle, const vec3<value_type>& axis):
|
|
quaternion(angle, axis.get_x(), axis.get_y(), axis.get_z()){}
|
|
template<typename T>
|
|
quaternion<T>::quaternion(value_type angle, value_type x, value_type y, value_type z){
|
|
angle /= value_type{2.0};
|
|
value_type sin_angle = std::sin(angle);
|
|
m_data[0] = std::cos(angle);
|
|
m_data[1] = sin_angle * x;
|
|
m_data[2] = sin_angle * y;
|
|
m_data[3] = sin_angle * z;
|
|
}
|
|
|
|
template<typename T>
|
|
constexpr quaternion<T>::operator pointer(void){
|
|
return m_data;
|
|
}
|
|
template<typename T>
|
|
constexpr quaternion<T>::operator const_pointer(void)const{
|
|
return m_data;
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::operator[](size_type i) -> reference{
|
|
return m_data[i];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::operator[](size_type i)const -> const_reference{
|
|
return m_data[i];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::get(size_type i) -> reference{
|
|
return m_data[i];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::get(size_type i)const -> const_reference{
|
|
return m_data[i];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::w() -> reference{
|
|
return m_data[0];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::w()const -> const_reference{
|
|
return m_data[0];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::x() -> reference{
|
|
return m_data[1];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::x()const -> const_reference{
|
|
return m_data[1];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::y() -> reference{
|
|
return m_data[2];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::y()const -> const_reference{
|
|
return m_data[2];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::z() -> reference{
|
|
return m_data[3];
|
|
}
|
|
template<typename T>
|
|
constexpr auto quaternion<T>::z()const -> const_reference{
|
|
return m_data[3];
|
|
}
|
|
template<typename T>
|
|
void quaternion<T>::set_axis(value_type x, value_type y, value_type z){
|
|
value_type sin_angle = std::sin(std::acos(m_data[0]));
|
|
m_data[1] = sin_angle * x;
|
|
m_data[2] = sin_angle * y;
|
|
m_data[3] = sin_angle * z;
|
|
}
|
|
template<typename T>
|
|
void quaternion<T>::set_axis(const vec3<value_type>& v){
|
|
set_axis(v.x(), v.y(), v.z());
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::get_axis()const -> vec3<value_type>{
|
|
quaternion tmp(*this);
|
|
if(m_data[0] > value_type{1.0})
|
|
tmp.set_normalized();
|
|
value_type s = std::sqrt(1 - tmp.m_data[0] * tmp.m_data[0]);
|
|
if(s <= value_type{0.001})
|
|
return vec3<T>(1, 0, 0);
|
|
return vec3<T>(tmp.data[1] / s, tmp.data[2] / s, tmp.data[3] / s);
|
|
}
|
|
template<typename T>
|
|
void quaternion<T>::set_angle(value_type t){
|
|
t /= value_type{2.0};
|
|
value_type old_sin_angle = std::sin(std::acos(m_data[0]));
|
|
value_type sin_angle = std::sin(t);
|
|
m_data[0] = std::cos(t);
|
|
m_data[1] = (m_data[1] / old_sin_angle) * sin_angle;
|
|
m_data[2] = (m_data[2] / old_sin_angle) * sin_angle;
|
|
m_data[3] = (m_data[3] / old_sin_angle) * sin_angle;
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::get_angle(void)const -> value_type{
|
|
return 2.0 * std::acos(m_data[0]);
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::norm(void)const -> value_type{
|
|
return m_data[0] * m_data[0] + m_data[1] * m_data[1] + m_data[2] * m_data[2] + m_data[3] * m_data[3];
|
|
}
|
|
template<typename T>
|
|
quaternion<T> quaternion<T>::conjugate(void)const{
|
|
return quaternion(manual_initialize, m_data[0], -m_data[1], -m_data[2], -m_data[3]);
|
|
}
|
|
template<typename T>
|
|
quaternion<T> quaternion<T>::inverse(void)const{
|
|
return conjugate() / norm();
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::magnitude(void)const -> value_type{
|
|
return std::sqrt(norm());
|
|
}
|
|
template<typename T>
|
|
quaternion<T> quaternion<T>::normalize(void)const{
|
|
value_type mag = magnitude();
|
|
return quaternion(manual_initialize, m_data[0] / mag, m_data[1] / mag, m_data[2] / mag, m_data[3] / mag);
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::get_right(void)const -> vec3<value_type>{
|
|
return vec3<value_type>(1 - 2 * ((m_data[2] * m_data[2]) + (m_data[3] * m_data[3])),
|
|
2 * ((m_data[1] * m_data[2]) - (m_data[3] * m_data[0])),
|
|
2 * ((m_data[1] * m_data[3]) + (m_data[2] * m_data[0])));
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::get_up(void)const -> vec3<value_type>{
|
|
return vec3<value_type>( 2 * ((m_data[1] * m_data[2]) + (m_data[3] * m_data[0])),
|
|
1 - 2 * ((m_data[1] * m_data[1]) + (m_data[3] * m_data[3])),
|
|
2 * ((m_data[2] * m_data[3]) - (m_data[1] * m_data[0])));
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::get_forward(void)const -> vec3<value_type>{
|
|
return vec3<value_type>( 2 * ((m_data[1] * m_data[3]) - (m_data[2] * m_data[0])),
|
|
2 * ((m_data[2] * m_data[3]) + (m_data[1] * m_data[0])),
|
|
1 - 2 * ((m_data[1] * m_data[1]) + (m_data[2] * m_data[2])));
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::to_vec3(void)const -> vec3<value_type>{
|
|
return vec3<value_type>(m_data[1], m_data[2], m_data[3]);
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::to_vec4(void)const -> vec4<value_type>{
|
|
return vec4<value_type>(m_data[1], m_data[2], m_data[3]);
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::to_mat3(void)const -> mat3<value_type>{
|
|
mat3<value_type> m;
|
|
|
|
value_type xx = m_data[1] * m_data[1];
|
|
value_type yy = m_data[2] * m_data[2];
|
|
value_type zz = m_data[3] * m_data[3];
|
|
|
|
value_type xy = m_data[1] * m_data[2];
|
|
value_type xz = m_data[1] * m_data[3];
|
|
value_type xw = m_data[1] * m_data[0];
|
|
value_type yz = m_data[2] * m_data[3];
|
|
value_type yw = m_data[2] * m_data[0];
|
|
value_type zw = m_data[3] * m_data[0];
|
|
|
|
m[0] = 1 - 2 * (yy + zz);
|
|
m[1] = 2 * (xy + zw);
|
|
m[2] = 2 * (xz - yw);
|
|
m[3] = 2 * (xy - zw);
|
|
m[4] = 1 - 2 * (xx + zz);
|
|
m[5] = 2 * (yz + xw);
|
|
m[6] = 2 * (xz + yw);
|
|
m[7] = 2 * (yz - xw);
|
|
m[8] = 1 - 2 * (xx + yy);
|
|
return m;
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::to_mat4(void)const -> mat4<value_type>{
|
|
mat4<value_type> m;
|
|
|
|
value_type xx = m_data[1] * m_data[1];
|
|
value_type yy = m_data[2] * m_data[2];
|
|
value_type zz = m_data[3] * m_data[3];
|
|
|
|
value_type xy = m_data[1] * m_data[2];
|
|
value_type xz = m_data[1] * m_data[3];
|
|
value_type xw = m_data[1] * m_data[0];
|
|
value_type yz = m_data[2] * m_data[3];
|
|
value_type yw = m_data[2] * m_data[0];
|
|
value_type zw = m_data[3] * m_data[0];
|
|
|
|
m[0] = 1 - 2 * (yy + zz);
|
|
m[1] = 2 * (xy + zw);
|
|
m[2] = 2 * (xz - yw);
|
|
m[3] = 0;
|
|
m[4] = 2 * (xy - zw);
|
|
m[5] = 1 - 2 * (xx + zz);
|
|
m[6] = 2 * (yz + xw);
|
|
m[7] = 0;
|
|
m[8] = 2 * (xz + yw);
|
|
m[9] = 2 * (yz - xw);
|
|
m[10] = 1 - 2 * (xx + yy);
|
|
m[11] = 0;
|
|
m[12] = 0;
|
|
m[13] = 0;
|
|
m[14] = 0;
|
|
m[15] = 1;
|
|
return m;
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::to_euler_angles(void)const -> vec3<value_type>{
|
|
value_type ww = m_data[0] * m_data[0];
|
|
value_type xx = m_data[1] * m_data[1];
|
|
value_type yy = m_data[2] * m_data[2];
|
|
value_type zz = m_data[3] * m_data[3];
|
|
|
|
value_type correction = ww + xx + yy + zz;
|
|
value_type test = m_data[1] * m_data[2] + m_data[3] * m_data[0];
|
|
if(test > 0.499 * correction){
|
|
return vec3<value_type>(0, 2 * std::atan2(m_data[1], m_data[0]), pi<value_type>() / 2.0);
|
|
}else if(test < -0.499 * correction){
|
|
return vec3<value_type>(0, -2 * std::atan2(m_data[1], m_data[0]), -pi<value_type>() / 2.0);
|
|
}
|
|
return vec3<value_type>(std::atan2((2 * m_data[1] * m_data[0]) - (2 * m_data[2] * m_data[3]), ww - xx + yy - zz),
|
|
std::atan2((2 * m_data[2] * m_data[0]) - (2 * m_data[1] * m_data[3]), xx - yy - zz + ww),
|
|
std::asin(2 * test / correction));
|
|
}
|
|
template<typename T>
|
|
auto quaternion<T>::to_axis_angle()const -> std::pair<value_type,vec3<value_type>>{
|
|
quaternion q(*this);
|
|
if(m_data[0] > 1.0)
|
|
q = q.normalize();
|
|
|
|
value_type s = std::sqrt(1 - q.m_data[0] * q.m_data[0]);
|
|
if(s <= value_type{0.001}){
|
|
return {2 * std::acos(q.m_data[0]), {1, 0, 0}};
|
|
}
|
|
return {2 * std::acos(q.m_data[0]), {q.m_data[1] / s, q.m_data[2] / s, q.m_data[3] / s}};
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
bool operator==(const quaternion<T>& left, const quaternion<U>& right){
|
|
return left.w() == right.w() &&
|
|
left.x() == right.x() &&
|
|
left.y() == right.y() &&
|
|
left.z() == right.z();
|
|
}
|
|
template<typename T, typename U>
|
|
bool operator!=(const quaternion<T>& left, const quaternion<U>& right){
|
|
return !(left == right);
|
|
}
|
|
template<typename T>
|
|
auto operator-(const quaternion<T>& left){
|
|
using res_t = T;
|
|
return quaternion<res_t>(manual_initialize, -left.w(), -left.x(), -left.y(), -left.z());
|
|
}
|
|
template<typename T, typename U>
|
|
auto operator-(const quaternion<T>& left, const quaternion<U>& right){
|
|
using res_t = decltype(std::declval<T>() - std::declval<U>());
|
|
return quaternion<res_t>(manual_initialize, left.w() - right.w(), left.x() - right.x(),
|
|
left.y() - right.y(), left.z() - right.z());
|
|
}
|
|
template<typename T, typename U>
|
|
auto operator+(const quaternion<T>& left, const quaternion<U>& right){
|
|
using res_t = decltype(std::declval<T>() + std::declval<U>());
|
|
return quaternion<res_t>(manual_initialize, left.w() + right.w(), left.x() + right.x(),
|
|
left.y() + right.y(), left.z() + right.z());
|
|
}
|
|
template<typename T, typename U>
|
|
auto operator*(const quaternion<T>& left, const quaternion<U>& right){
|
|
using res_t = decltype(std::declval<T>() * std::declval<U>());
|
|
return quaternion<res_t>(manual_initialize,
|
|
(right.w() * left.w()) - (right.x() * left.x()) -
|
|
(right.y() * left.y()) - (right.z() * left.z()),
|
|
(right.w() * left.x()) + (right.x() * left.w()) +
|
|
(right.y() * left.z()) - (right.z() * left.y()),
|
|
(right.w() * left.y()) - (right.x() * left.z()) +
|
|
(right.y() * left.w()) + (right.z() * left.x()),
|
|
(right.w() * left.z()) + (right.x() * left.y()) -
|
|
(right.y() * left.x()) + (right.z() * left.w()));
|
|
}
|
|
template<typename T, typename U>
|
|
auto operator*(const quaternion<T>& left, const vec3<U>& right){
|
|
return left.to_mat3() * right;
|
|
}
|
|
template<typename T, typename U, std::enable_if_t<std::is_arithmetic_v<std::decay_t<U>>,int>>
|
|
auto operator*(const quaternion<T>& left, U&& right){
|
|
using res_t = decltype(std::declval<T>() * std::declval<U>());
|
|
return quaternion<res_t>(manual_initialize, left.w() * right, left.x() * right, left.y() * right, left.z() * right);
|
|
}
|
|
template<typename T, typename U, std::enable_if_t<std::is_arithmetic_v<std::decay_t<U>>,int>>
|
|
auto operator/(const quaternion<T>& left, U&& right){
|
|
using res_t = decltype(std::declval<T>() / std::declval<U>());
|
|
return quaternion<res_t>(manual_initialize, left.w() / right, left.x() / right, left.y() / right, left.z() / right);
|
|
}
|
|
template<typename T, typename U>
|
|
decltype(auto) operator+=(quaternion<T>& left, const quaternion<U>& right){
|
|
left.w() += right.w();
|
|
left.x() += right.x();
|
|
left.y() += right.y();
|
|
left.z() += right.z();
|
|
return left;
|
|
}
|
|
template<typename T, typename U>
|
|
decltype(auto) operator-=(quaternion<T>& left, const quaternion<U>& right){
|
|
left.w() -= right.w();
|
|
left.x() -= right.x();
|
|
left.y() -= right.y();
|
|
left.z() -= right.z();
|
|
return left;
|
|
}
|
|
template<typename T, typename U>
|
|
decltype(auto) operator*=(quaternion<T>& left, const quaternion<U>& right){
|
|
left = left * right;
|
|
return left;
|
|
}
|
|
|
|
template<typename T, typename U, std::enable_if_t<std::is_arithmetic_v<std::decay_t<U>>,int>>
|
|
decltype(auto) operator*=(quaternion<T>& left, U&& right){
|
|
left.w() *= right;
|
|
left.x() *= right;
|
|
left.y() *= right;
|
|
left.z() *= right;
|
|
return left;
|
|
}
|
|
template<typename T, typename U, std::enable_if_t<std::is_arithmetic_v<std::decay_t<U>>,int>>
|
|
decltype(auto) operator/=(quaternion<T>& left, U&& right){
|
|
left.w() /= right;
|
|
left.x() /= right;
|
|
left.y() /= right;
|
|
left.z() /= right;
|
|
return left;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|