/*
* Copyright (C) 2020 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see .
*/
#ifndef ANTKEEPER_MATH_MATRIX_FUNCTIONS_HPP
#define ANTKEEPER_MATH_MATRIX_FUNCTIONS_HPP
#include "math/matrix-type.hpp"
#include "math/vector-type.hpp"
#include "math/vector-functions.hpp"
#include
namespace math {
/// @addtogroup matrix
/// @{
/**
* Adds two matrices.
*
* @param x First matrix.
* @param y Second matrix.
* @return Sum of the two matrices.
*/
template
matrix add(const matrix& x, const matrix& y);
/// @copydoc add(const matrix&, const matrix&)
template
matrix add(const matrix& x, const matrix& y);
/// @copydoc add(const matrix&, const matrix&)
template
matrix add(const matrix& x, const matrix& y);
/**
* Reinterprets data as an `NxM` matrix of type `T`.
*
* @tparam N Number of columns.
* @tparam M Number of rows.
* @tparam T Element type.
* @param data Data to reinterpret.
*/
template
matrix& as_matrix(T& data);
/**
* Calculates the determinant of a matrix.
*
* @param m Matrix of which to take the determinant.
*/
template
T determinant(const matrix& m);
/// @copydoc determinant(const matrix&)
template
T determinant(const matrix& m);
/// @copydoc determinant(const matrix&)
template
T determinant(const matrix& m);
/**
* Calculates the inverse of a matrix.
*
* @param m Matrix of which to take the inverse.
*/
template
matrix inverse(const matrix& m);
/// @copydoc inverse(const matrix&)
template
matrix inverse(const matrix& m);
/// @copydoc inverse(const matrix&)
template
matrix inverse(const matrix& m);
/**
* Performs a component-wise multiplication of two matrices.
*
* @param x First matrix multiplicand.
* @param y Second matrix multiplicand.
*/
template
matrix componentwise_mul(const matrix& x, const matrix& y);
/// @copydoc componentwise_mul(const matrix&, const matrix&)
template
matrix componentwise_mul(const matrix& x, const matrix& y);
/// @copydoc componentwise_mul(const matrix&, const matrix&)
template
matrix componentwise_mul(const matrix& x, const matrix& y);
/**
* Creates a viewing transformation matrix.
*
* @param position Position of the view point.
* @param target Position of the target.
* @param up Normalized direction of the up vector.
* @return Viewing transformation matrix.
*/
template
matrix look_at(const vector& position, const vector& target, vector up);
/**
* Multiplies two matrices.
*
* @param x First matrix.
* @param y Second matrix.
* @return Product of the two matrices.
*/
template
matrix mul(const matrix& x, const matrix& y);
/// @copydoc mul(const matrix&, const matrix&);
template
matrix mul(const matrix& x, const matrix& y);
/// @copydoc mul(const matrix&, const matrix&);
template
matrix mul(const matrix& x, const matrix& y);
/**
* Multiplies a matrix by a scalar.
*
* @param m Matrix.
* @param s Scalar.
* @return Product of the matrix and the scalar..
*/
template
matrix mul(const matrix& m, T s);
/**
* Transforms a vector by a matrix.
*
* @param m Matrix.
* @param v Vector.
* @return Transformed vector.
*/
template
vector mul(const matrix& m, const vector& v);
/// @copydoc mul(const matrix&, const vector&)
template
vector mul(const matrix& m, const vector& v);
/// @copydoc mul(const matrix&, const vector&)
template
vector mul(const matrix& m, const vector& v);
/**
* Creates an orthographic projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively.
*
* @param left Signed distance to the left clipping plane.
* @param right Signed distance to the right clipping plane.
* @param bottom Signed distance to the bottom clipping plane.
* @param top Signed distance to the top clipping plane.
* @param z_near Signed distance to the near clipping plane.
* @param z_far Signed distance to the far clipping plane.
* @return Orthographic projection matrix.
*/
template
matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far);
/**
* Creates an orthographic projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively.
*
* @param left Signed distance to the left clipping plane.
* @param right Signed distance to the right clipping plane.
* @param bottom Signed distance to the bottom clipping plane.
* @param top Signed distance to the top clipping plane.
* @param z_near Signed distance to the near clipping plane.
* @param z_far Signed distance to the far clipping plane.
* @return Orthographic projection matrix.
*/
template
matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far);
/**
* Calculates the outer product of a pair of vectors.
*
* @param c Parameter to be treated as a column vector.
* @param r Parameter to be treated as a row vector.
*/
template
matrix outer_product(const vector& c, const vector& r);
/// @copydoc outer_product(const vector&, const vector&)
template
matrix outer_product(const vector& c, const vector& r);
/// @copydoc outer_product(const vector&, const vector&)
template
matrix outer_product(const vector& c, const vector r);
/**
* Creates a perspective projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively.
*
* @param vertical_fov Vertical field of view angle, in radians.
* @param aspect_ratio Aspect ratio which determines the horizontal field of view.
* @param z_near Distance to the near clipping plane.
* @param z_far Distance to the far clipping plane.
* @return Perspective projection matrix.
*/
template
matrix perspective(T vertical_fov, T aspect_ratio, T z_near, T z_far);
/**
* Creates a perspective projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively.
*
* @param vertical_fov Vertical field of view angle, in radians.
* @param aspect_ratio Aspect ratio which determines the horizontal field of view.
* @param z_near Distance to the near clipping plane.
* @param z_far Distance to the far clipping plane.
* @return Perspective projection matrix.
*/
template
matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z_far);
/**
* Resizes a matrix. Any new elements will be set to `1` if in the diagonal, and `0` otherwise.
*
* @param m Matrix to resize.
* @return Resized matrix.
*/
template
matrix resize(const matrix& m);
/**
* Rotates a matrix.
*
* @param m Matrix to rotate.
* @param angle Angle of rotation (in radians).
* @param axis Axis of rotation
* @return Rotated matrix.
*/
template
matrix rotate(const matrix& m, T angle, const vector& axis);
/**
* Scales a matrix.
*
* @param m Matrix to scale.
* @param v Scale vector.
* @return Scaled matrix.
*/
template
matrix scale(const matrix& m, const vector& v);
/**
* Subtracts a matrix from another matrix.
*
* @param x First matrix.
* @param y Second matrix.
* @return Difference between the two matrices.
*/
template
matrix sub(const matrix& x, const matrix& y);
/// @copydoc sub(const matrix&, const matrix&)
template
matrix sub(const matrix& x, const matrix& y);
/// @copydoc sub(const matrix&, const matrix&)
template
matrix sub(const matrix& x, const matrix& y);
/**
* Translates a matrix.
*
* @param m Matrix to translate.
* @param v Translation vector.
* @return Translated matrix.
*/
template
matrix translate(const matrix& m, const vector& v);
/**
* Calculates the transpose of a matrix.
*
* @param m Matrix of which to take the transpose.
*/
template
matrix transpose(const matrix& m);
/// @copydoc transpose(const matrix&)
template
matrix transpose(const matrix& m);
/// @copydoc transpose(const matrix&)
template
matrix transpose(const matrix& m);
template
matrix add(const matrix& x, const matrix& y)
{
return
{{
x[0] + y[0],
x[1] + y[1]
}};
}
template
matrix add(const matrix& x, const matrix& y)
{
return
{{
x[0] + y[0],
x[1] + y[1],
x[2] + y[2]
}};
}
template
matrix add(const matrix& x, const matrix& y)
{
return
{{
x[0] + y[0],
x[1] + y[1],
x[2] + y[2],
x[3] + y[3]
}};
}
template
inline matrix& as_matrix(T& data)
{
static_assert(std::is_pod>::value);
return reinterpret_cast&>(data);
}
template
T determinant(const matrix& m)
{
return m[0][0] * m[1][1] - m[0][1] * m[1][0];
}
template
T determinant(const matrix& m)
{
return m[0][0] * m [1][1] * m[2][2] +
m[0][1] * m[1][2] * m[2][0] +
m[0][2] * m[1][0] * m[2][1] -
m[0][0] * m[1][2] * m[2][1] -
m[0][1] * m[1][0] * m[2][2] -
m[0][2] * m[1][1] * m[2][0];
}
template
T determinant(const matrix& m)
{
return m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] -
m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] +
m[0][2] * m[1][1] * m[2][3] * m[3][0] - m[0][1] * m[1][2] * m[2][3] * m[3][0] -
m[0][3] * m[1][2] * m[2][0] * m[3][1] + m[0][2] * m[1][3] * m[2][0] * m[3][1] +
m[0][3] * m[1][0] * m[2][2] * m[3][1] - m[0][0] * m[1][3] * m[2][2] * m[3][1] -
m[0][2] * m[1][0] * m[2][3] * m[3][1] + m[0][0] * m[1][2] * m[2][3] * m[3][1] +
m[0][3] * m[1][1] * m[2][0] * m[3][2] - m[0][1] * m[1][3] * m[2][0] * m[3][2] -
m[0][3] * m[1][0] * m[2][1] * m[3][2] + m[0][0] * m[1][3] * m[2][1] * m[3][2] +
m[0][1] * m[1][0] * m[2][3] * m[3][2] - m[0][0] * m[1][1] * m[2][3] * m[3][2] -
m[0][2] * m[1][1] * m[2][0] * m[3][3] + m[0][1] * m[1][2] * m[2][0] * m[3][3] +
m[0][2] * m[1][0] * m[2][1] * m[3][3] - m[0][0] * m[1][2] * m[2][1] * m[3][3] -
m[0][1] * m[1][0] * m[2][2] * m[3][3] + m[0][0] * m[1][1] * m[2][2] * m[3][3];
}
template
matrix inverse(const matrix& m)
{
static_assert(std::is_floating_point::value);
const T rd(T(1) / determinant(m));
return
{{
{ m[1][1] * rd, -m[0][1] * rd},
{-m[1][0] * rd, m[0][0] * rd}
}};
}
template
matrix inverse(const matrix& m)
{
static_assert(std::is_floating_point::value);
const T rd(T(1) / determinant(m));
return
{{
{
(m[1][1] * m[2][2] - m[1][2] * m[2][1]) * rd,
(m[0][2] * m[2][1] - m[0][1] * m[2][2]) * rd,
(m[0][1] * m[1][2] - m[0][2] * m[1][1]) * rd
},
{
(m[1][2] * m[2][0] - m[1][0] * m[2][2]) * rd,
(m[0][0] * m[2][2] - m[0][2] * m[2][0]) * rd,
(m[0][2] * m[1][0] - m[0][0] * m[1][2]) * rd
},
{
(m[1][0] * m[2][1] - m[1][1] * m[2][0]) * rd,
(m[0][1] * m[2][0] - m[0][0] * m[2][1]) * rd,
(m[0][0] * m[1][1] - m[0][1] * m[1][0]) * rd
}
}};
}
template
matrix inverse(const matrix& m)
{
static_assert(std::is_floating_point::value);
const T rd(T(1) / determinant(m));
return
{{
{
(m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] + m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] - m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3]) * rd,
(m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] - m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] + m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3]) * rd,
(m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] + m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] - m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3]) * rd,
(m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] - m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] + m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3]) * rd
},
{
(m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] - m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] + m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3]) * rd,
(m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] + m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] - m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3]) * rd,
(m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] - m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] + m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3]) * rd,
(m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] + m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] - m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3]) * rd
},
{
(m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] + m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] - m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3]) * rd,
(m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] - m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] + m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3]) * rd,
(m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] + m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] - m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3]) * rd,
(m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] - m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] + m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3]) * rd
},
{
(m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] - m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] + m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2]) * rd,
(m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] + m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] - m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2]) * rd,
(m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] - m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] + m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2]) * rd,
(m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) * rd
}
}};
}
template
matrix componentwise_mul(const matrix& x, const matrix& y)
{
return
{{
{x[0][0] * y[0][0], x[0][1] * y[0][1]},
{x[1][0] * y[1][0], x[1][1] * y[1][1]}
}};
}
template
matrix componentwise_mul(const matrix& x, const matrix& y)
{
return
{{
{x[0][0] * y[0][0], x[0][1] * y[0][1], x[0][2] * y[0][2]},
{x[1][0] * y[1][0], x[1][1] * y[1][1], x[1][2] * y[1][2]},
{x[2][0] * y[2][0], x[2][1] * y[2][1], x[2][2] * y[2][2]}
}};
}
template
matrix componentwise_mul(const matrix& x, const matrix& y)
{
return
{{
{x[0][0] * y[0][0], x[0][1] * y[0][1], x[0][2] * y[0][2], x[0][3] * y[0][3]},
{x[1][0] * y[1][0], x[1][1] * y[1][1], x[1][2] * y[1][2], x[1][3] * y[1][3]},
{x[2][0] * y[2][0], x[2][1] * y[2][1], x[2][2] * y[2][2], x[2][3] * y[2][3]},
{x[3][0] * y[3][0], x[3][1] * y[3][1], x[3][2] * y[3][2], x[3][3] * y[3][3]}
}};
}
template
matrix look_at(const vector& position, const vector& target, vector up)
{
vector forward = normalize(sub(target, position));
vector right = normalize(cross(forward, up));
up = cross(right, forward);
matrix m =
{{
{right[0], up[0], -forward[0], T(0)},
{right[1], up[1], -forward[1], T(0)},
{right[2], up[2], -forward[2], T(0)},
{T(0), T(0), T(0), T(1)}
}};
return translate(m, negate(position));
}
template
matrix mul(const matrix& x, const matrix& y)
{
return
{{
x[0] * y[0][0] + x[1] * y[0][1],
x[0] * y[1][0] + x[1] * y[1][1]
}};
}
template
matrix mul(const matrix& x, const matrix& y)
{
return
{{
x[0] * y[0][0] + x[1] * y[0][1] + x[2] * y[0][2],
x[0] * y[1][0] + x[1] * y[1][1] + x[2] * y[1][2],
x[0] * y[2][0] + x[1] * y[2][1] + x[2] * y[2][2]
}};
}
template
matrix mul(const matrix& x, const matrix& y)
{
return
{{
x[0] * y[0][0] + x[1] * y[0][1] + x[2] * y[0][2] + x[3] * y[0][3],
x[0] * y[1][0] + x[1] * y[1][1] + x[2] * y[1][2] + x[3] * y[1][3],
x[0] * y[2][0] + x[1] * y[2][1] + x[2] * y[2][2] + x[3] * y[2][3],
x[0] * y[3][0] + x[1] * y[3][1] + x[2] * y[3][2] + x[3] * y[3][3]
}};
}
/// @private
template
inline matrix mul(const matrix& m, T s, std::index_sequence)
{
return {{(m[I] * s)...}};
}
template
inline matrix mul(const matrix& m, T s)
{
return mul(m, s, std::make_index_sequence{});
}
template
vector mul(const matrix& m, const vector& v)
{
return
{
m[0][0] * v[0] + m[1][0] * v[1],
m[0][1] * v[0] + m[1][1] * v[1]
};
}
template
vector mul(const matrix& m, const vector& v)
{
return
{
m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2],
m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2],
m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2]
};
}
template
vector mul(const matrix& m, const vector& v)
{
return
{
m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3],
m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3],
m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3],
m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3]
};
}
template
matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far)
{
return
{{
{T(2) / (right - left), T(0), T(0), T(0)},
{T(0), T(2) / (top - bottom), T(0), T(0)},
{T(0), T(0), T(-2) / (z_far - z_near), T(0)},
{-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((z_far + z_near) / (z_far - z_near)), T(1)}
}};
}
template
matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far)
{
return
{{
{T(2) / (right - left), T(0), T(0), T(0)},
{T(0), T(2) / (top - bottom), T(0), T(0)},
{T(0), T(0), T(-1) / (z_far - z_near), T(0)},
{-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -z_near / (z_far - z_near), T(1)}
}};
}
template
matrix outer_product(const vector& c, const vector& r)
{
return
{{
{c[0] * r[0], c[1] * r[0]},
{c[0] * r[1], c[1] * r[1]}
}};
}
template