kopia lustrzana https://github.com/jameshball/osci-render
Use mathter library for Camera and Frustum, and add viewMatrix-based camera
rodzic
8ff283aba8
commit
eaf5a3eb9c
|
@ -148,11 +148,11 @@ public:
|
|||
std::vector<EffectParameter*>{
|
||||
new EffectParameter("3D Perspective", "Controls the strength of the 3D perspective projection.", "perspectiveStrength", VERSION_HINT, 1.0, 0.0, 1.0),
|
||||
new EffectParameter("Focal Length", "Controls the focal length of the 3D perspective effect. A higher focal length makes the image look more flat, and a lower focal length makes the image look more 3D.", "perspectiveFocalLength", VERSION_HINT, 1.0, 0.0, 10.0),
|
||||
new EffectParameter("Distance (z)", "Controls how far away the 3D object is drawn away from the camera (the Z position).", "perspectiveZPos", VERSION_HINT, 0.1, 0.0, 1.0),
|
||||
new EffectParameter("Distance (z)", "Controls how far away the 3D object is drawn away from the camera (the Z position).", "perspectiveZPos", VERSION_HINT, 0.0, -10.0, 10.0),
|
||||
new EffectParameter("Rotate Speed", "Controls how fast the 3D object rotates in the direction determined by the rotation sliders below.", "perspectiveRotateSpeed", VERSION_HINT, 0.0, -1.0, 1.0),
|
||||
new EffectParameter("Rotate X", "Controls the rotation of the object in the X axis.", "perspectiveRotateX", VERSION_HINT, 1.0, -1.0, 1.0),
|
||||
new EffectParameter("Rotate Y", "Controls the rotation of the object in the Y axis.", "perspectiveRotateY", VERSION_HINT, 1.0, -1.0, 1.0),
|
||||
new EffectParameter("Rotate Z", "Controls the rotation of the object in the Z axis.", "perspectiveRotateZ", VERSION_HINT, 0.0, -1.0, 1.0),
|
||||
new EffectParameter("Rotate Z", "Controls the rotation of the object in the Z axis.", "perspectiveRotateZ", VERSION_HINT, 1.0, -1.0, 1.0),
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <JuceHeader.h>
|
||||
#include "shape/Point.h"
|
||||
#include "obj/Frustum.h"
|
||||
#include "obj/Camera.h"
|
||||
#include "mathter/Common/Approx.hpp"
|
||||
|
||||
class FrustumTest : public juce::UnitTest {
|
||||
public:
|
||||
|
@ -8,100 +8,108 @@ public:
|
|||
|
||||
void runTest() override {
|
||||
double focalLength = 1;
|
||||
double ratio = 1;
|
||||
double nearDistance = 0.1;
|
||||
double farDistance = 100;
|
||||
|
||||
Frustum frustum(focalLength, ratio, nearDistance, farDistance);
|
||||
frustum.setCameraOrigin(Point(0, 0, -focalLength));
|
||||
Camera camera;
|
||||
camera.setFocalLength(focalLength);
|
||||
Vec3 position = Vec3(0, 0, -focalLength);
|
||||
camera.setPosition(position);
|
||||
Frustum frustum = camera.getFrustum();
|
||||
|
||||
beginTest("Focal Plane Frustum In-Bounds");
|
||||
|
||||
// Focal plane is at z = 0
|
||||
Point points[][2] = {
|
||||
{Point(0, 0, 0), Point(0, 0, 0)},
|
||||
{Point(1, 1, 0), Point(1, 1, 0)},
|
||||
{Point(-1, -1, 0), Point(-1, -1, 0)},
|
||||
{Point(1, -1, 0), Point(1, -1, 0)},
|
||||
{Point(-1, 1, 0), Point(-1, 1, 0)},
|
||||
{Point(0.5, 0.5, 0), Point(0.5, 0.5, 0)},
|
||||
Vec3 vecs[] = {
|
||||
Vec3(0, 0, 0), Vec3(0, 0, 0),
|
||||
Vec3(1, 1, 0), Vec3(1, 1, 0),
|
||||
Vec3(-1, -1, 0), Vec3(-1, -1, 0),
|
||||
Vec3(1, -1, 0), Vec3(1, -1, 0),
|
||||
Vec3(-1, 1, 0), Vec3(-1, 1, 0),
|
||||
Vec3(0.5, 0.5, 0), Vec3(0.5, 0.5, 0),
|
||||
};
|
||||
|
||||
testFrustumClippedEqualsExpected(points, frustum, 6);
|
||||
testFrustumClippedEqualsExpected(vecs, camera, frustum, 6);
|
||||
|
||||
beginTest("Focal Plane Frustum Out-Of-Bounds");
|
||||
|
||||
// Focal plane is at z = 0
|
||||
Point points2[][2] = {
|
||||
{Point(1.1, 1.1, 0), Point(1, 1, 0)},
|
||||
{Point(-1.1, -1.1, 0), Point(-1, -1, 0)},
|
||||
{Point(1.1, -1.1, 0), Point(1, -1, 0)},
|
||||
{Point(-1.1, 1.1, 0), Point(-1, 1, 0)},
|
||||
{Point(1.1, 0.5, 0), Point(1, 0.5, 0)},
|
||||
{Point(-1.1, 0.5, 0), Point(-1, 0.5, 0)},
|
||||
{Point(0.5, -1.1, 0), Point(0.5, -1, 0)},
|
||||
{Point(0.5, 1.1, 0), Point(0.5, 1, 0)},
|
||||
{Point(10, 10, 0), Point(1, 1, 0)},
|
||||
{Point(-10, -10, 0), Point(-1, -1, 0)},
|
||||
{Point(10, -10, 0), Point(1, -1, 0)},
|
||||
{Point(-10, 10, 0), Point(-1, 1, 0)},
|
||||
Vec3 vecs2[] = {
|
||||
Vec3(1.1, 1.1, 0), Vec3(1, 1, 0),
|
||||
Vec3(-1.1, -1.1, 0), Vec3(-1, -1, 0),
|
||||
Vec3(1.1, -1.1, 0), Vec3(1, -1, 0),
|
||||
Vec3(-1.1, 1.1, 0), Vec3(-1, 1, 0),
|
||||
Vec3(1.1, 0.5, 0), Vec3(1, 0.5, 0),
|
||||
Vec3(-1.1, 0.5, 0), Vec3(-1, 0.5, 0),
|
||||
Vec3(0.5, -1.1, 0), Vec3(0.5, -1, 0),
|
||||
Vec3(0.5, 1.1, 0), Vec3(0.5, 1, 0),
|
||||
Vec3(10, 10, 0), Vec3(1, 1, 0),
|
||||
Vec3(-10, -10, 0), Vec3(-1, -1, 0),
|
||||
Vec3(10, -10, 0), Vec3(1, -1, 0),
|
||||
Vec3(-10, 10, 0), Vec3(-1, 1, 0),
|
||||
};
|
||||
|
||||
testFrustumClippedEqualsExpected(points2, frustum, 12);
|
||||
testFrustumClippedEqualsExpected(vecs2, camera, frustum, 12);
|
||||
|
||||
beginTest("Behind Camera Out-Of-Bounds");
|
||||
|
||||
double minZWorldCoords = -focalLength + nearDistance;
|
||||
double minZWorldCoords = -focalLength + frustum.nearDistance;
|
||||
|
||||
Point points3[][2] = {
|
||||
{Point(0, 0, -focalLength), Point(0, 0, minZWorldCoords)},
|
||||
{Point(0, 0, -100), Point(0, 0, minZWorldCoords)},
|
||||
{Point(0.5, 0.5, -focalLength), Point(0.1, 0.1, minZWorldCoords)},
|
||||
{Point(10, -10, -focalLength), Point(0.1, -0.1, minZWorldCoords)},
|
||||
{Point(-0.5, 0.5, -100), Point(-0.1, 0.1, minZWorldCoords)},
|
||||
{Point(-10, 10, -100), Point(-0.1, 0.1, minZWorldCoords)},
|
||||
Vec3 vecs3[] = {
|
||||
Vec3(0, 0, -focalLength), Vec3(0, 0, minZWorldCoords),
|
||||
Vec3(0, 0, -100), Vec3(0, 0, minZWorldCoords),
|
||||
Vec3(0.5, 0.5, -focalLength), Vec3(0.1, 0.1, minZWorldCoords),
|
||||
Vec3(10, -10, -focalLength), Vec3(0.1, -0.1, minZWorldCoords),
|
||||
Vec3(-0.5, 0.5, -100), Vec3(-0.1, 0.1, minZWorldCoords),
|
||||
Vec3(-10, 10, -100), Vec3(-0.1, 0.1, minZWorldCoords),
|
||||
};
|
||||
|
||||
testFrustumClippedEqualsExpected(points3, frustum, 6);
|
||||
testFrustumClippedEqualsExpected(vecs3, camera, frustum, 6);
|
||||
|
||||
beginTest("3D Point Out-Of-Bounds");
|
||||
|
||||
Point points4[] = {
|
||||
Point(1, 1, -0.1),
|
||||
Point(-1, -1, -0.1),
|
||||
Point(1, -1, -0.1),
|
||||
Point(-1, 1, -0.1),
|
||||
Point(0.5, 0.5, minZWorldCoords),
|
||||
Vec3 vecs4[] = {
|
||||
Vec3(1, 1, -0.1),
|
||||
Vec3(-1, -1, -0.1),
|
||||
Vec3(1, -1, -0.1),
|
||||
Vec3(-1, 1, -0.1),
|
||||
Vec3(0.5, 0.5, minZWorldCoords),
|
||||
};
|
||||
|
||||
testFrustumClipOccurs(points4, frustum, 5);
|
||||
testFrustumClipOccurs(vecs4, camera, frustum, 5);
|
||||
}
|
||||
|
||||
Point project(Point& p, double focalLength) {
|
||||
return Point(
|
||||
Vec3 project(Vec3& p, double focalLength) {
|
||||
return Vec3(
|
||||
p.x * focalLength / p.z,
|
||||
p.y * focalLength / p.z,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
juce::String errorMessage(Point& actual, Point& expected) {
|
||||
return "Expected: " + expected.toString() + " Got: " + actual.toString();
|
||||
juce::String vec3ToString(Vec3& p) {
|
||||
return "(" + juce::String(p.x) + ", " + juce::String(p.y) + ", " + juce::String(p.z) + ")";
|
||||
}
|
||||
|
||||
void testFrustumClippedEqualsExpected(Point points[][2], Frustum& frustum, int length) {
|
||||
juce::String errorMessage(Vec3& actual, Vec3& expected) {
|
||||
return "Expected: " + vec3ToString(expected) + ", Actual: " + vec3ToString(actual);
|
||||
}
|
||||
|
||||
void testFrustumClippedEqualsExpected(Vec3 vecs[], Camera& camera, Frustum& frustum, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
Point p = points[i][0];
|
||||
Vec3 p = vecs[2 * i];
|
||||
p = camera.toCameraSpace(p);
|
||||
frustum.clipToFrustum(p);
|
||||
expect(p == points[i][1], errorMessage(p, points[i][1]));
|
||||
p = camera.toWorldSpace(p);
|
||||
expect(mathter::AlmostEqual(p, vecs[2 * i + 1]), errorMessage(p, vecs[2 * i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
void testFrustumClipOccurs(Point points[], Frustum& frustum, int length) {
|
||||
void testFrustumClipOccurs(Vec3 vecs[], Camera& camera, Frustum& frustum, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
Point p = points[i];
|
||||
Vec3 p = vecs[i];
|
||||
p = camera.toCameraSpace(p);
|
||||
frustum.clipToFrustum(p);
|
||||
expect(p != points[i], errorMessage(p, points[i]));
|
||||
p = camera.toWorldSpace(p);
|
||||
expect(!mathter::AlmostEqual(p, vecs[i]), errorMessage(p, vecs[i]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "PerspectiveEffect.h"
|
||||
#include <numbers>
|
||||
#include "../MathUtil.h"
|
||||
#include "../obj/Camera.h"
|
||||
|
||||
PerspectiveEffect::PerspectiveEffect(int versionHint) {
|
||||
fixedRotateX = new BooleanParameter("Perspective Fixed Rotate X", "perspectiveFixedRotateX", versionHint, false);
|
||||
|
@ -13,7 +14,7 @@ PerspectiveEffect::~PerspectiveEffect() {}
|
|||
Point PerspectiveEffect::apply(int index, Point input, const std::vector<double>& values, double sampleRate) {
|
||||
auto effectScale = values[0];
|
||||
auto focalLength = juce::jmax(values[1], 0.001);
|
||||
auto depth = 1.0 + (values[2] - 0.1) * 3;
|
||||
auto depth = values[2];
|
||||
auto rotateSpeed = linearSpeedToActualSpeed(values[3]);
|
||||
double baseRotateX, baseRotateY, baseRotateZ;
|
||||
if (fixedRotateX->getBoolValue()) {
|
||||
|
@ -67,17 +68,16 @@ Point PerspectiveEffect::apply(int index, Point input, const std::vector<double>
|
|||
|
||||
Point p = Point(x3, y3, z3);
|
||||
|
||||
Frustum frustum = Frustum(focalLength, 1.0, 0.1, 1000);
|
||||
Point origin = Point(0, 0, -focalLength - depth);
|
||||
frustum.setCameraOrigin(origin);
|
||||
frustum.clipToFrustum(p);
|
||||
Vec3 origin = Vec3(0, 0, -focalLength - depth);
|
||||
camera.setPosition(origin);
|
||||
camera.setFocalLength(focalLength);
|
||||
Vec3 vec = Vec3(p.x, p.y, p.z);
|
||||
|
||||
// need to convert point from world space to camera space before projecting
|
||||
p = p - origin;
|
||||
Vec3 projected = camera.project(vec);
|
||||
|
||||
return Point(
|
||||
(1 - effectScale) * input.x + effectScale * (p.x * focalLength / p.z),
|
||||
(1 - effectScale) * input.y + effectScale * (p.y * focalLength / p.z),
|
||||
(1 - effectScale) * input.x + effectScale * projected.x,
|
||||
(1 - effectScale) * input.y + effectScale * projected.y,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "../shape/Point.h"
|
||||
#include "../audio/Effect.h"
|
||||
#include "../lua/LuaParser.h"
|
||||
#include "../obj/Frustum.h"
|
||||
#include "../obj/Camera.h"
|
||||
|
||||
class PerspectiveEffect : public EffectApplication {
|
||||
public:
|
||||
|
@ -29,4 +29,6 @@ private:
|
|||
}
|
||||
return actualSpeed;
|
||||
}
|
||||
|
||||
Camera camera;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
add_library(Mathter INTERFACE)
|
||||
|
||||
message("${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_include_directories(Mathter INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
|
||||
target_sources(Mathter
|
||||
INTERFACE
|
||||
"Mathter.natvis"
|
||||
)
|
||||
|
||||
target_compile_features(Mathter INTERFACE cxx_std_17)
|
||||
|
||||
if (${MATHTER_USE_XSIMD})
|
||||
find_package(xsimd)
|
||||
target_link_libraries(Mathter INTERFACE xsimd)
|
||||
endif()
|
|
@ -0,0 +1,125 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
// Specialization for floats.
|
||||
#include "../IoStream.hpp"
|
||||
#include "../Matrix.hpp"
|
||||
#include "../Vector.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T>
|
||||
bool AlmostEqual(T d1, T d2, std::true_type) {
|
||||
if (std::abs(d1) < 1e-38 && std::abs(d2) < 1e-38) {
|
||||
return true;
|
||||
}
|
||||
if ((d1 == 0 && d2 < 1e-4) || (d2 == 0 && d1 < 1e-4)) {
|
||||
return true;
|
||||
}
|
||||
T scaler = pow(T(10), floor(std::log10(std::abs(d1))));
|
||||
d1 /= scaler;
|
||||
d2 /= scaler;
|
||||
d1 *= T(1000.0);
|
||||
d2 *= T(1000.0);
|
||||
return round(d1) == round(d2);
|
||||
}
|
||||
|
||||
// Specialization for int, complex and custom types: simple equality.
|
||||
template <class T>
|
||||
bool AlmostEqual(T d1, T d2, std::false_type) {
|
||||
return d1 == d2;
|
||||
}
|
||||
|
||||
// Check equivalence with tolerance.
|
||||
template <class T, class U, class = std::enable_if_t<traits::NotVector<T>::value && traits::NotMatrix<T>::value && traits::NotQuaternion<T>::value>>
|
||||
bool AlmostEqual(T d1, U d2) {
|
||||
using P = traits::MatMulElemT<T, U>;
|
||||
return AlmostEqual(P(d1), P(d2), std::integral_constant<bool, std::is_floating_point<P>::value>());
|
||||
}
|
||||
|
||||
template <class T1, class T2, int Dim, bool Packed1, bool Packed2>
|
||||
bool AlmostEqual(const Vector<T1, Dim, Packed1>& lhs, const Vector<T2, Dim, Packed2>& rhs) {
|
||||
bool eq = true;
|
||||
for (auto i : impl::Range(Dim)) {
|
||||
eq = eq && AlmostEqual(lhs[i], rhs[i]);
|
||||
}
|
||||
return eq;
|
||||
}
|
||||
|
||||
template <class T, bool Packed1, bool Packed2>
|
||||
bool AlmostEqual(const Quaternion<T, Packed1>& lhs, const Quaternion<T, Packed2>& rhs) {
|
||||
bool eq = true;
|
||||
for (auto i : impl::Range(4)) {
|
||||
eq = eq && AlmostEqual(lhs.vec[i], rhs.vec[i]);
|
||||
}
|
||||
return eq;
|
||||
}
|
||||
|
||||
template <class T1,
|
||||
class T2,
|
||||
int Rows,
|
||||
int Columns,
|
||||
eMatrixOrder Order1,
|
||||
eMatrixLayout Layout1,
|
||||
bool Packed1,
|
||||
eMatrixOrder Order2,
|
||||
eMatrixLayout Layout2,
|
||||
bool Packed2>
|
||||
bool AlmostEqual(const Matrix<T1, Rows, Columns, Order1, Layout1, Packed1>& lhs, const Matrix<T2, Rows, Columns, Order2, Layout2, Packed2>& rhs) {
|
||||
bool eq = true;
|
||||
for (auto i : impl::Range(Rows)) {
|
||||
for (auto j : impl::Range(Columns)) {
|
||||
eq = eq && AlmostEqual(lhs(i, j), rhs(i, j));
|
||||
}
|
||||
}
|
||||
return eq;
|
||||
}
|
||||
|
||||
|
||||
// Floating point comparison helper class, works like Catch2 units testing framework's float Approx.
|
||||
template <class LinalgClass>
|
||||
struct ApproxHelper {
|
||||
ApproxHelper() {}
|
||||
explicit ApproxHelper(LinalgClass object) {
|
||||
this->object = object;
|
||||
}
|
||||
LinalgClass object;
|
||||
};
|
||||
|
||||
|
||||
template <class LinalgClass1, class LinalgClass2>
|
||||
bool operator==(const ApproxHelper<LinalgClass1>& lhs, const LinalgClass2& rhs) {
|
||||
return AlmostEqual(lhs.object, rhs);
|
||||
}
|
||||
|
||||
template <class LinalgClass1, class LinalgClass2>
|
||||
bool operator==(const LinalgClass1& lhs, const ApproxHelper<LinalgClass2>& rhs) {
|
||||
return AlmostEqual(rhs.object, lhs);
|
||||
}
|
||||
|
||||
template <class LinalgClass1, class LinalgClass2>
|
||||
bool operator==(const ApproxHelper<LinalgClass1>& lhs, const ApproxHelper<LinalgClass2>& rhs) {
|
||||
return AlmostEqual(lhs.object, rhs.object);
|
||||
}
|
||||
|
||||
template <class LinalgClass>
|
||||
std::ostream& operator<<(std::ostream& os, const ApproxHelper<LinalgClass>& arg) {
|
||||
os << arg.object;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class LinalgClass>
|
||||
ApproxHelper<LinalgClass> ApproxVec(const LinalgClass& arg) {
|
||||
return ApproxHelper<LinalgClass>{ arg };
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,75 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace mathter {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Enums
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Determines if you want to left- or right-multiply your matrices with vectors. </summary>
|
||||
/// <remarks>
|
||||
/// <para> This flag affects the generated transformation matrices. If you want to write M2*M1*v in your code,
|
||||
/// then choose PRECEDE_VECTOR, if you want v*M1*M2, choose FOLLOW_VECTOR. Matrices generated by Transform, Scale,
|
||||
/// Rotation and similar functions will match your order of multiplication. (I.e. bottom row is translation if you
|
||||
/// choose FOLLOW_VECTOR). </para>
|
||||
/// <para> You can still use M*v and v*M in your code. </para>
|
||||
/// </remarks>
|
||||
enum class eMatrixOrder {
|
||||
PRECEDE_VECTOR,
|
||||
FOLLOW_VECTOR,
|
||||
};
|
||||
|
||||
/// <summary> Determines the memory layout of matrices. </summary>
|
||||
/// <remarks>
|
||||
/// <para> For ROW_MAJOR layout, the matrix's first row comes first in memory, followed immediately by
|
||||
/// the second row's elements. For COLUMN_MAJOR matrices, the memory region begins with the first column. </para>
|
||||
/// <para> This does not affect arithmetic or matrix generator function in any way. Your arithmetic will work
|
||||
/// the same way if you change this. </para>
|
||||
/// <para> Please note that changing this flag may affect performance of arithmetic operations. Profile your
|
||||
/// code to determine optimal settings. Performance may depend on multiple factors. </para>
|
||||
/// </remarks>
|
||||
enum class eMatrixLayout {
|
||||
ROW_MAJOR,
|
||||
COLUMN_MAJOR,
|
||||
};
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Specify this as Vector or Matrix dimension template parameter to set size at runtime.
|
||||
/// PLEASE NOTE THAT DYNAMICALLY SIZED VECTORS AND MATRICES ARE NOT SUPPORTED YET. </summary>
|
||||
constexpr int DYNAMIC = -1;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Classes
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
struct VectorData;
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class Vector;
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices>
|
||||
class Swizzle;
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
class Matrix;
|
||||
|
||||
template <class MatrixT, int SRows, int SColumns>
|
||||
class SubmatrixHelper;
|
||||
|
||||
template <class T, bool Packed>
|
||||
class Quaternion;
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,112 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <complex>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mathter::impl {
|
||||
|
||||
|
||||
/// <summary> Return a value initialized to zero, or, if not possible, a value-initialized object. </summary>
|
||||
/// <remarks> If you want special treatment for a particular type, specialize this method. </remarks>
|
||||
template <class T>
|
||||
struct NullScalarInitializer {
|
||||
static constexpr T Get() {
|
||||
if constexpr (std::is_convertible_v<decltype(0), T>) {
|
||||
return T(0);
|
||||
}
|
||||
else {
|
||||
return T{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Return a value initialized to zero, or, if not possible, a value-initialized object. </summary>
|
||||
template <class T>
|
||||
constexpr T NullScalar() {
|
||||
return NullScalarInitializer<T>::Get();
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Initializes an object of type T to invalid (NaN) value. </summary>
|
||||
/// <remarks> Returns a signaling NaN,
|
||||
/// if not possible, a quiet NaN,
|
||||
/// if not possible, an infinity,
|
||||
/// if not possible, the highest value for T,
|
||||
/// if not possible, then a null-initialized value.
|
||||
/// If you want special treatment for a particular type, specialize this method. </remarks>
|
||||
template <class T>
|
||||
struct InvalidScalarInitializer {
|
||||
static constexpr T Get() {
|
||||
if constexpr (std::numeric_limits<T>::is_specialized) {
|
||||
if constexpr (std::numeric_limits<T>::has_signaling_NaN) {
|
||||
return std::numeric_limits<T>::signaling_NaN();
|
||||
}
|
||||
else if constexpr (std::numeric_limits<T>::has_quiet_NaN) {
|
||||
return std::numeric_limits<T>::quiet_NaN();
|
||||
}
|
||||
else if constexpr (std::numeric_limits<T>::has_infinity) {
|
||||
return std::numeric_limits<T>::infinity();
|
||||
}
|
||||
else {
|
||||
return std::numeric_limits<T>::max();
|
||||
}
|
||||
}
|
||||
return NullScalar<T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Return a signaling NaN complex number. Same as the unspecialized version. </summary>
|
||||
template <class T>
|
||||
struct InvalidScalarInitializer<std::complex<T>> {
|
||||
static constexpr std::complex<T> Get() {
|
||||
return { InvalidScalarInitializer<T>::Get(), InvalidScalarInitializer<T>::Get() };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Returns a signaling NaN,
|
||||
/// if not possible, a quiet NaN,
|
||||
/// if not possible, an infinity,
|
||||
/// if not possible, the highest value for T,
|
||||
/// if not possible, then a null-initialized value. </summary>
|
||||
template <class T>
|
||||
constexpr T InvalidScalar() {
|
||||
return InvalidScalarInitializer<T>::Get();
|
||||
}
|
||||
|
||||
|
||||
#if !defined(MATHTER_NULL_INITIALIZE) && !defined(MATHTER_INVALID_INITIALIZE) && !defined(MATHTER_DONT_INITIALIZE)
|
||||
#ifdef NDEBUG
|
||||
#define MATHTER_DONT_INITIALIZE 1
|
||||
#else
|
||||
#define MATHTER_INVALID_INITIALIZE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MATHTER_NULL_INITIALIZE)
|
||||
#define MATHTER_SCALAR_INIT_EXPRESSION(T) mathter::impl::NullScalar<T>()
|
||||
#elif defined(MATHTER_INVALID_INITIALIZE)
|
||||
#define MATHTER_SCALAR_INIT_EXPRESSION(T) mathter::impl::InvalidScalar<T>()
|
||||
#elif defined(MATHTER_DONT_INITIALIZE)
|
||||
#define MATHTER_SCALAR_INIT_EXPRESSION(T)
|
||||
#else
|
||||
#error Set at least one of the MATHTER_NULL/INVALID/DONT_INITIALIZE macros.
|
||||
#endif
|
||||
|
||||
#if defined(MATHTER_NULL_INITIALIZE) || defined(MATHTER_INVALID_INITIALIZE)
|
||||
#define MATHTER_VECTOR_INITIALIZER(T) : Vector(MATHTER_SCALAR_INIT_EXPRESSION(T))
|
||||
#else
|
||||
#define MATHTER_VECTOR_INITIALIZER(T)
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace mathter::impl
|
|
@ -0,0 +1,34 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mathter::impl {
|
||||
|
||||
|
||||
template <class T>
|
||||
T sign(T arg) {
|
||||
return T(arg > T(0)) - (arg < T(0));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T sign_nonzero(T arg) {
|
||||
return std::copysign(T(1), arg);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr T ConstexprExp10(int exponent) {
|
||||
return exponent == 0 ? T(1) : T(10) * ConstexprExp10<T>(exponent - 1);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr T ConstexprAbs(T arg) {
|
||||
return arg >= T(0) ? arg : -arg;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter::impl
|
|
@ -0,0 +1,79 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace mathter::impl {
|
||||
|
||||
// Helper for writing for loops as for (auto i : Range(0,10))
|
||||
template <class T>
|
||||
class RangeHelper {
|
||||
public:
|
||||
class iterator {
|
||||
friend class RangeHelper;
|
||||
iterator(T value, T step) : value(value), step(step) {}
|
||||
|
||||
public:
|
||||
iterator() : value(std::numeric_limits<T>::lowest()) {}
|
||||
|
||||
using value_type = T;
|
||||
using difference_type = ptrdiff_t;
|
||||
using reference = T&;
|
||||
using pointer = T*;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
void operator++() {
|
||||
value += step;
|
||||
}
|
||||
T operator*() const {
|
||||
return value;
|
||||
}
|
||||
bool operator==(const iterator& rhs) const {
|
||||
return value == rhs.value;
|
||||
}
|
||||
bool operator!=(const iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
T value;
|
||||
T step;
|
||||
};
|
||||
|
||||
RangeHelper(T first, T last, T step) : first(first), last(last), step(step) {}
|
||||
|
||||
iterator begin() const { return iterator(first, step); }
|
||||
iterator end() const { return iterator(last, step); }
|
||||
iterator cbegin() const { return iterator(first, step); }
|
||||
iterator cend() const { return iterator(last, step); }
|
||||
|
||||
private:
|
||||
T first, last, step;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
RangeHelper<T> Range(T first, T last, T step) {
|
||||
return RangeHelper<T>(first, last, step);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
RangeHelper<T> Range(T first, T last) {
|
||||
T step = last >= first ? T(1) : T(-1);
|
||||
return Range(first, last, step);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
RangeHelper<T> Range(T last) {
|
||||
T first = T(0);
|
||||
T step = last >= first ? T(1) : T(-1);
|
||||
return Range(first, last, step);
|
||||
}
|
||||
|
||||
} // namespace mathter::impl
|
|
@ -0,0 +1,309 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Definitions.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace mathter::traits {
|
||||
|
||||
// Vector properties
|
||||
template <class VectorT>
|
||||
class VectorTraitsHelper {};
|
||||
|
||||
template <class T_, int Dim_, bool Packed_>
|
||||
class VectorTraitsHelper<Vector<T_, Dim_, Packed_>> {
|
||||
public:
|
||||
using Type = T_;
|
||||
static constexpr int Dim = Dim_;
|
||||
static constexpr bool Packed = Packed_;
|
||||
};
|
||||
|
||||
template <class T_, int Dim_, bool Packed_>
|
||||
class VectorTraitsHelper<VectorData<T_, Dim_, Packed_>> {
|
||||
public:
|
||||
using Type = T_;
|
||||
static constexpr int Dim = Dim_;
|
||||
static constexpr bool Packed = Packed_;
|
||||
};
|
||||
|
||||
template <class VectorT>
|
||||
class VectorTraits : public VectorTraitsHelper<typename std::decay<VectorT>::type> {};
|
||||
|
||||
|
||||
// Matrix properties
|
||||
template <class MatrixT>
|
||||
class MatrixTraitsHelper {};
|
||||
|
||||
template <class T_, int Rows_, int Columns_, eMatrixOrder Order_, eMatrixLayout Layout_, bool Packed_>
|
||||
class MatrixTraitsHelper<Matrix<T_, Rows_, Columns_, Order_, Layout_, Packed_>> {
|
||||
public:
|
||||
using Type = T_;
|
||||
static constexpr int Rows = Rows_;
|
||||
static constexpr int Columns = Columns_;
|
||||
static constexpr eMatrixOrder Order = Order_;
|
||||
static constexpr eMatrixLayout Layout = Layout_;
|
||||
static constexpr bool Packed = Packed_;
|
||||
};
|
||||
|
||||
template <class MatrixT>
|
||||
class MatrixTraits : public MatrixTraitsHelper<typename std::decay<MatrixT>::type> {};
|
||||
|
||||
|
||||
template <eMatrixOrder Order>
|
||||
class OppositeOrder {
|
||||
public:
|
||||
static constexpr eMatrixOrder value = (Order == eMatrixOrder::FOLLOW_VECTOR ? eMatrixOrder::PRECEDE_VECTOR : eMatrixOrder::FOLLOW_VECTOR);
|
||||
};
|
||||
|
||||
template <eMatrixLayout Layout>
|
||||
class OppositeLayout {
|
||||
public:
|
||||
static constexpr eMatrixLayout value = (Layout == eMatrixLayout::ROW_MAJOR ? eMatrixLayout::COLUMN_MAJOR : eMatrixLayout::ROW_MAJOR);
|
||||
};
|
||||
|
||||
|
||||
// Common utility
|
||||
template <class T, class U>
|
||||
using MatMulElemT = decltype(T() * U() + T() * U());
|
||||
|
||||
|
||||
|
||||
// Template metaprogramming utilities
|
||||
template <template <class> class Cond, class... T>
|
||||
struct All;
|
||||
|
||||
template <template <class> class Cond, class Head, class... Rest>
|
||||
struct All<Cond, Head, Rest...> {
|
||||
static constexpr bool value = Cond<Head>::value && All<Cond, Rest...>::value;
|
||||
};
|
||||
|
||||
template <template <class> class Cond>
|
||||
struct All<Cond> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
|
||||
template <template <class> class Cond, class... T>
|
||||
struct Any;
|
||||
|
||||
template <template <class> class Cond, class Head, class... Rest>
|
||||
struct Any<Cond, Head, Rest...> {
|
||||
static constexpr bool value = Cond<Head>::value || Any<Cond, Rest...>::value;
|
||||
};
|
||||
|
||||
template <template <class> class Cond>
|
||||
struct Any<Cond> {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class... T>
|
||||
struct TypeList {};
|
||||
|
||||
template <class Tl1, class Tl2>
|
||||
struct ConcatTypeList;
|
||||
|
||||
template <class... T, class... U>
|
||||
struct ConcatTypeList<TypeList<T...>, TypeList<U...>> {
|
||||
using type = TypeList<T..., U...>;
|
||||
};
|
||||
|
||||
template <class T, int N>
|
||||
struct RepeatType {
|
||||
using type = typename std::conditional<N <= 0, TypeList<>, typename ConcatTypeList<TypeList<T>, typename RepeatType<T, N - 1>::type>::type>::type;
|
||||
};
|
||||
|
||||
|
||||
// Decide if type is Scalar, Vector or Matrix.
|
||||
template <class Arg>
|
||||
struct IsVector {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <class T, int Dim, bool Packed>
|
||||
struct IsVector<Vector<T, Dim, Packed>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <class Arg>
|
||||
struct NotVector {
|
||||
static constexpr bool value = !IsVector<Arg>::value;
|
||||
};
|
||||
|
||||
template <class Arg>
|
||||
struct IsSwizzle {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <class T, int... Indices>
|
||||
struct IsSwizzle<Swizzle<T, Indices...>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <class Arg>
|
||||
struct NotSwizzle {
|
||||
static constexpr bool value = !IsSwizzle<Arg>::value;
|
||||
};
|
||||
|
||||
template <class Arg>
|
||||
struct IsVectorOrSwizzle {
|
||||
static constexpr bool value = IsVector<Arg>::value || IsSwizzle<Arg>::value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct IsMatrix {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
struct IsMatrix<Matrix<T, Rows, Columns, Order, Layout, Packed>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <class T>
|
||||
struct NotMatrix {
|
||||
static constexpr bool value = !IsMatrix<T>::value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct IsSubmatrix {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <class M, int Rows, int Columns>
|
||||
struct IsSubmatrix<SubmatrixHelper<M, Rows, Columns>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <class T>
|
||||
struct NotSubmatrix {
|
||||
static constexpr bool value = !IsSubmatrix<T>::value;
|
||||
};
|
||||
|
||||
template <class Arg>
|
||||
struct IsQuaternion {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <class T, bool Packed>
|
||||
struct IsQuaternion<Quaternion<T, Packed>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <class Arg>
|
||||
struct NotQuaternion {
|
||||
static constexpr bool value = !IsQuaternion<Arg>::value;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
struct IsScalar {
|
||||
static constexpr bool value = !IsMatrix<T>::value && !IsVector<T>::value && !IsSwizzle<T>::value && !IsQuaternion<T>::value && !IsSubmatrix<T>::value;
|
||||
};
|
||||
|
||||
// Dimension of an argument (add dynamically sized vectors later).
|
||||
template <class U, int Along = 0>
|
||||
struct DimensionOf {
|
||||
static constexpr int value = 1;
|
||||
};
|
||||
template <class T, int Dim, bool Packed>
|
||||
struct DimensionOf<Vector<T, Dim, Packed>, 0> {
|
||||
static constexpr int value = Dim;
|
||||
};
|
||||
template <class T, int... Indices>
|
||||
struct DimensionOf<Swizzle<T, Indices...>> {
|
||||
static constexpr int value = sizeof...(Indices);
|
||||
};
|
||||
|
||||
// Sum dimensions of arguments.
|
||||
template <class... Rest>
|
||||
struct SumDimensions;
|
||||
|
||||
template <class Head, class... Rest>
|
||||
struct SumDimensions<Head, Rest...> {
|
||||
static constexpr int value = DimensionOf<Head>::value > 0 ? DimensionOf<Head>::value + SumDimensions<Rest...>::value : DYNAMIC;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SumDimensions<> {
|
||||
static constexpr int value = 0;
|
||||
};
|
||||
|
||||
// Weather vector uses SIMD.
|
||||
template <class VectorDataT>
|
||||
struct HasSimd {
|
||||
template <class U>
|
||||
static constexpr bool test(const void*) { return false; }
|
||||
|
||||
template <class U, class = decltype(std::declval<U>().simd)>
|
||||
static constexpr bool test(std::nullptr_t) { return true; }
|
||||
|
||||
|
||||
static constexpr bool value = test<VectorDataT>(nullptr);
|
||||
};
|
||||
|
||||
// Reverse integer sequence
|
||||
template <class IS1, class IS2>
|
||||
struct MergeIntegerSequence;
|
||||
|
||||
template <class T, T... Indices1, T... Indices2>
|
||||
struct MergeIntegerSequence<std::integer_sequence<T, Indices1...>, std::integer_sequence<T, Indices2...>> {
|
||||
using type = std::integer_sequence<T, Indices1..., Indices2...>;
|
||||
};
|
||||
|
||||
template <class IS>
|
||||
struct ReverseIntegerSequence;
|
||||
|
||||
template <class T, T Head, T... Indices>
|
||||
struct ReverseIntegerSequence<std::integer_sequence<T, Head, Indices...>> {
|
||||
using type = typename MergeIntegerSequence<typename ReverseIntegerSequence<std::integer_sequence<T, Indices...>>::type, std::integer_sequence<T, Head>>::type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct ReverseIntegerSequence<std::integer_sequence<T>> {
|
||||
using type = std::integer_sequence<T>;
|
||||
};
|
||||
|
||||
|
||||
template <class IntegerSequence, typename IntegerSequence::value_type PadValue, size_t Size>
|
||||
struct pad_integer_sequence;
|
||||
|
||||
template <class Elem, Elem... Indices, Elem PadValue, size_t Size>
|
||||
struct pad_integer_sequence<std::integer_sequence<Elem, Indices...>, PadValue, Size> {
|
||||
static constexpr auto Infer() {
|
||||
if constexpr (sizeof...(Indices) >= Size) {
|
||||
return std::integer_sequence<Elem, Indices...>{};
|
||||
}
|
||||
else {
|
||||
return typename pad_integer_sequence<std::integer_sequence<Elem, Indices..., PadValue>, PadValue, Size>::type{};
|
||||
}
|
||||
}
|
||||
using type = std::invoke_result_t<decltype(&pad_integer_sequence::Infer)>;
|
||||
};
|
||||
|
||||
template <class IntegerSequence, typename IntegerSequence::value_type PadValue, size_t Size>
|
||||
using pad_integer_sequence_t = typename pad_integer_sequence<IntegerSequence, PadValue, Size>::type;
|
||||
|
||||
|
||||
template <class T>
|
||||
struct same_size_int {
|
||||
template <class U>
|
||||
static constexpr auto GetSize(std::complex<U>*) {
|
||||
return sizeof(U);
|
||||
}
|
||||
template <class U>
|
||||
static constexpr auto GetSize(U*) {
|
||||
return sizeof(U);
|
||||
}
|
||||
static constexpr auto size = GetSize(static_cast<T*>(nullptr));
|
||||
using SelectType = std::tuple<std::uint8_t, std::uint16_t, void, std::uint32_t, void, void, void, std::uint64_t>;
|
||||
using type = std::tuple_element_t<size - 1, SelectType>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using same_size_int_t = typename same_size_int<T>::type;
|
||||
|
||||
|
||||
} // namespace mathter::traits
|
|
@ -0,0 +1,274 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Common/Definitions.hpp"
|
||||
#include "../Common/Range.hpp"
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> A utility class that can do common operations with the LU decomposition,
|
||||
/// i.e. solving equation systems. </summary>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
class DecompositionLU {
|
||||
using MatrixT = Matrix<T, Dim, Dim, Order, Layout, Packed>;
|
||||
|
||||
template <class T2, int Dim2, eMatrixOrder Order2, eMatrixLayout Layout2, bool Packed2>
|
||||
friend class DecompositionLUP;
|
||||
|
||||
private:
|
||||
static Vector<float, Dim, Packed> Solve(const MatrixT& L, const MatrixT& U, const Vector<T, Dim, Packed>& b);
|
||||
|
||||
public:
|
||||
// DecompositionLU(MatrixT L, MatrixT U) : L(L), U(U) {}
|
||||
|
||||
/// <summary> Solves the equation system Ax=b, that is LUx=b. </summary>
|
||||
/// <remarks> If the equation is singular or the LU decomposition fails, garbage is returned. </remarks>
|
||||
/// <param name="b"> The right hand side vector. </summary>
|
||||
/// <returns> The solution x. </returns>
|
||||
Vector<float, Dim, Packed> Solve(const Vector<T, Dim, Packed>& b) const {
|
||||
return Solve(L, U, b);
|
||||
}
|
||||
|
||||
bool Solvable() const {
|
||||
T prod = L(0, 0);
|
||||
T sum = std::abs(prod);
|
||||
for (int i = 1; i < Dim; ++i) {
|
||||
prod *= L(i, i);
|
||||
sum += std::abs(L(i, i));
|
||||
}
|
||||
sum /= Dim;
|
||||
return std::abs(prod) / sum > T(1e-6);
|
||||
}
|
||||
|
||||
/// <param name="L"> Lower triangular matrix, LU=P'A. </param>
|
||||
MatrixT L;
|
||||
/// <param name="U"> Upper triangular matrix, LU=P'A. </param>
|
||||
MatrixT U;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> A utility class that can do common operations with the LUP decomposition,
|
||||
/// i.e. solving equation systems. </summary>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
class DecompositionLUP {
|
||||
using MatrixT = Matrix<T, Dim, Dim, Order, Layout, Packed>;
|
||||
|
||||
public:
|
||||
// DecompositionLUP(MatrixT L, MatrixT U, Vector<int, Dim, false> P) : L(L), U(U), P(P) {}
|
||||
|
||||
/// <summary> Solves the equation system Ax=b, that is LUx=Pb. </summary>
|
||||
/// <remarks> If the equation is singular garbage is returned. </remarks>
|
||||
/// <param name="b"> The right hand side vector. </param>
|
||||
/// <returns> The solution x. </returns>
|
||||
Vector<float, Dim, Packed> Solve(const Vector<T, Dim, Packed>& b) const;
|
||||
|
||||
bool Solvable() {
|
||||
T prod = L(0, 0);
|
||||
T sum = std::abs(prod);
|
||||
for (int i = 1; i < Dim; ++i) {
|
||||
prod *= L(i, i);
|
||||
sum += std::abs(L(i, i));
|
||||
}
|
||||
sum /= Dim;
|
||||
return std::abs(prod) / sum > T(1e-6);
|
||||
}
|
||||
|
||||
/// <param name="L"> Lower triangular matrix, LU=P'A. </param>
|
||||
MatrixT L;
|
||||
/// <param name="U"> Upper triangular matrix, LU=P'A. </param>
|
||||
MatrixT U;
|
||||
/// <param name="P"> Row permutations. LU=P'A, where P' is a matrix whose i-th row's P[i]-th element is one. </param>
|
||||
Vector<int, Dim, false> P;
|
||||
};
|
||||
|
||||
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeLU(const Matrix<T, Dim, Dim, Order, Layout, Packed>& m) {
|
||||
// From: https://www.gamedev.net/resources/_/technical/math-and-physics/matrix-inversion-using-lu-decomposition-r3637
|
||||
Matrix<T, Dim, Dim, Order, Layout, Packed> L;
|
||||
Matrix<T, Dim, Dim, Order, Layout, Packed> U;
|
||||
|
||||
const auto& A = m;
|
||||
constexpr int n = Dim;
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
for (int j = i + 1; j < n; ++j) {
|
||||
L(i, j) = 0;
|
||||
}
|
||||
for (int j = 0; j <= i; ++j) {
|
||||
U(i, j) = i == j;
|
||||
}
|
||||
}
|
||||
|
||||
// Crout's algorithm
|
||||
for (int i = 0; i < n; ++i) {
|
||||
L(i, 0) = A(i, 0);
|
||||
}
|
||||
for (int j = 1; j < n; ++j) {
|
||||
U(0, j) = A(0, j) / L(0, 0);
|
||||
}
|
||||
|
||||
for (int j = 1; j < n - 1; ++j) {
|
||||
for (int i = j; i < n; ++i) {
|
||||
float Lij;
|
||||
Lij = A(i, j);
|
||||
for (int k = 0; k <= j - 1; ++k) {
|
||||
Lij -= L(i, k) * U(k, j);
|
||||
}
|
||||
L(i, j) = Lij;
|
||||
}
|
||||
for (int k = j; k < n; ++k) {
|
||||
float Ujk;
|
||||
Ujk = A(j, k);
|
||||
for (int i = 0; i <= j - 1; ++i) {
|
||||
Ujk -= L(j, i) * U(i, k);
|
||||
}
|
||||
Ujk /= L(j, j);
|
||||
U(j, k) = Ujk;
|
||||
}
|
||||
}
|
||||
|
||||
L(n - 1, n - 1) = A(n - 1, n - 1);
|
||||
for (int k = 0; k < n - 1; ++k) {
|
||||
L(n - 1, n - 1) -= L(n - 1, k) * U(k, n - 1);
|
||||
}
|
||||
|
||||
return DecompositionLU<T, Dim, Order, Layout, Packed>{ L, U };
|
||||
}
|
||||
|
||||
/// <summary> Implements LU decomposition with partial pivoting. </summary>
|
||||
/// <remarks> Handles singular matrices as well. </remarks>
|
||||
/// <param name="L"> Lower triangular matrix, LU=P'A. </param>
|
||||
/// <param name="U"> Upper triangular matrix, LU=P'A. </param>
|
||||
/// <param name="P"> Row permutations. LU=P'A, where P' is a matrix whose i-th row's P[i]-th element is one. </param>
|
||||
/// <param name="parity"> The parity of the permutation described by P. Odd: 1, Even: -1. </param>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeLUP(const Matrix<T, Dim, Dim, Order, Layout, Packed>& m, int& parity) {
|
||||
Matrix<T, Dim, Dim, Order, Layout, Packed> L;
|
||||
Matrix<T, Dim, Dim, Order, Layout, Packed> U;
|
||||
Vector<int, Dim, false> P;
|
||||
U = m;
|
||||
|
||||
int n = m.RowCount();
|
||||
parity = 1;
|
||||
|
||||
for (int i : impl::Range(0, n)) {
|
||||
P(i) = i;
|
||||
}
|
||||
|
||||
for (int j : impl::Range(0, n)) {
|
||||
// find largest pivot elements
|
||||
T p = 0;
|
||||
int largest;
|
||||
for (int i : impl::Range(j, n)) {
|
||||
if (std::abs(U(i, j)) > p) {
|
||||
largest = i;
|
||||
p = std::abs(U(i, j));
|
||||
}
|
||||
}
|
||||
|
||||
// if pivot is zero TODO
|
||||
if (p == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// swap rows to move pivot to top row
|
||||
std::swap(P(j), P(largest));
|
||||
parity *= (j != largest ? -1 : 1);
|
||||
for (int i : impl::Range(0, n)) {
|
||||
std::swap(U(j, i), U(largest, i));
|
||||
}
|
||||
|
||||
// do some magic
|
||||
for (int i : impl::Range(j + 1, n)) {
|
||||
U(i, j) = U(i, j) / U(j, j);
|
||||
for (int k : impl::Range(j + 1, n)) {
|
||||
U(i, k) = U(i, k) - U(i, j) * U(j, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy elements to L
|
||||
for (int j : impl::Range(0, n)) {
|
||||
for (int i : impl::Range(j + 1, n)) {
|
||||
L(i, j) = U(i, j);
|
||||
U(i, j) = T(0);
|
||||
L(j, i) = T(0);
|
||||
}
|
||||
}
|
||||
for (int i : impl::Range(n)) {
|
||||
L(i, i) = 1;
|
||||
}
|
||||
|
||||
return DecompositionLUP<T, Dim, Order, Layout, Packed>{ L, U, P };
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Implements LU decomposition with partial pivoting. </summary>
|
||||
/// <remarks> Handles singular matrices as well. </remarks>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeLUP(const Matrix<T, Dim, Dim, Order, Layout, Packed>& m) {
|
||||
int ignore;
|
||||
return DecomposeLUP(m, ignore);
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
Vector<float, Dim, Packed> DecompositionLU<T, Dim, Order, Layout, Packed>::Solve(const MatrixT& L, const MatrixT& U, const Vector<T, Dim, Packed>& b) {
|
||||
// Matrix to do gaussian elimination with
|
||||
Matrix<T, Dim, Dim + 1, eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout::ROW_MAJOR, Packed> E;
|
||||
|
||||
// Solve Ld = b;
|
||||
E.template Submatrix<Dim, Dim>(0, 0) = L;
|
||||
E.Column(Dim) = b;
|
||||
|
||||
for (int i = 0; i < Dim - 1; ++i) {
|
||||
for (int i2 = i + 1; i2 < Dim; ++i2) {
|
||||
E.stripes[i] /= E(i, i);
|
||||
T coeff = E(i2, i);
|
||||
E.stripes[i2] -= E.stripes[i] * coeff;
|
||||
}
|
||||
}
|
||||
E(Dim - 1, Dim) /= E(Dim - 1, Dim - 1);
|
||||
// d is now the last column of E
|
||||
|
||||
// Solve Ux = d
|
||||
E.template Submatrix<Dim, Dim>(0, 0) = U;
|
||||
|
||||
for (int i = Dim - 1; i > 0; --i) {
|
||||
for (int i2 = i - 1; i2 >= 0; --i2) {
|
||||
E.stripes[i] /= E(i, i);
|
||||
T coeff = E(i2, i);
|
||||
E.stripes[i2] -= E.stripes[i] * coeff;
|
||||
}
|
||||
}
|
||||
E(0, Dim) /= E(0, 0);
|
||||
// x is now the last column of E
|
||||
|
||||
return E.Column(Dim);
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
Vector<float, Dim, Packed> DecompositionLUP<T, Dim, Order, Layout, Packed>::Solve(const Vector<T, Dim, Packed>& b) const {
|
||||
// Permute b
|
||||
Vector<T, Dim, Packed> bp;
|
||||
for (int i : impl::Range(0, P.Dimension())) {
|
||||
bp(i) = b(P(i));
|
||||
}
|
||||
|
||||
// Solve
|
||||
auto x = DecompositionLU<T, Dim, Order, Layout, Packed>::Solve(L, U, bp);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,73 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Common/MathUtil.hpp"
|
||||
#include "../Transforms/IdentityBuilder.hpp"
|
||||
#include "../Transforms/ZeroBuilder.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> A utility class that can do common operations with the QR decomposition,
|
||||
/// i.e. solving equation systems. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
class DecompositionQR {
|
||||
using MatrixT = Matrix<T, Rows, Columns, Order, Layout, Packed>;
|
||||
|
||||
public:
|
||||
// DecompositionQR(Matrix<T, Rows, Rows, Order, Layout, Packed> Q,
|
||||
// Matrix<T, Rows, Columns, Order, Layout, Packed> R) : Q(Q), R(R) {}
|
||||
|
||||
Matrix<T, Rows, Rows, Order, Layout, Packed> Q;
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> R;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Calculates the QR decomposition of the matrix using Householder transforms. </summary>
|
||||
/// <remarks> The matrix must have Rows <= Columns. It's a full QR decomposition, not a thin one. </remarks>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeQR(Matrix<T, Rows, Columns, Order, Layout, Packed> m) {
|
||||
static_assert(Rows >= Columns);
|
||||
|
||||
Matrix<T, Rows, Rows, Order, Layout, Packed> Q;
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> R;
|
||||
|
||||
R = m;
|
||||
Q = Identity();
|
||||
|
||||
Matrix<T, Rows, Rows, Order, Layout, Packed> Qi;
|
||||
Vector<T, Rows, Packed> u;
|
||||
Matrix<T, Rows, 1, Order, eMatrixLayout::ROW_MAJOR, Packed> v;
|
||||
|
||||
for (int col = 0; col < m.ColumnCount(); ++col) {
|
||||
u = R.Column(col);
|
||||
for (int i = 0; i < col; ++i) {
|
||||
u(i) = T(0);
|
||||
}
|
||||
T alpha = impl::sign(R(col, col)) * LengthPrecise(u);
|
||||
u(col) -= alpha;
|
||||
T norm = LengthPrecise(u);
|
||||
if (norm == 0) {
|
||||
continue;
|
||||
}
|
||||
u /= norm;
|
||||
v = u;
|
||||
Qi = (T(-2) * v) * Transpose(v);
|
||||
for (int i = 0; i < Q.ColumnCount(); ++i) {
|
||||
Qi(i, i) += T(1);
|
||||
}
|
||||
R = Qi * R;
|
||||
Q = Qi * Q;
|
||||
}
|
||||
Q = Transpose(Q);
|
||||
|
||||
return DecompositionQR<T, Rows, Columns, Order, Layout, Packed>{ Q, R };
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,213 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DecomposeQR.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> A utility class that can do common operations with the singular value decomposition,
|
||||
/// i.e. solving equation systems. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
class DecompositionSVD {
|
||||
template <int Rows_, int Columns_>
|
||||
using MatrixT = Matrix<T, Rows_, Columns_, Order, Layout, Packed>;
|
||||
|
||||
static constexpr int Sdim = std::min(Rows, Columns);
|
||||
static constexpr int Udim = Rows;
|
||||
static constexpr int Vdim = Columns;
|
||||
|
||||
public:
|
||||
// DecompositionSVD(MatrixT<Udim, Sdim> U, MatrixT<Sdim, Sdim> S, MatrixT<Sdim, Vdim> V) : U(U), S(S), V(V) {}
|
||||
|
||||
MatrixT<Udim, Sdim> U;
|
||||
MatrixT<Sdim, Sdim> S;
|
||||
MatrixT<Sdim, Vdim> V;
|
||||
};
|
||||
|
||||
|
||||
namespace impl {
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
void Rq2x2Helper(const Matrix<T, 2, 2, Order, Layout, Packed>& A, T& x, T& y, T& z, T& c2, T& s2) {
|
||||
T a = A(0, 0);
|
||||
T b = A(0, 1);
|
||||
T c = A(1, 0);
|
||||
T d = A(1, 1);
|
||||
|
||||
if (c == 0) {
|
||||
x = a;
|
||||
y = b;
|
||||
z = d;
|
||||
c2 = 1;
|
||||
s2 = 0;
|
||||
return;
|
||||
}
|
||||
T maxden = std::max(std::abs(c), std::abs(d));
|
||||
|
||||
T rcmaxden = 1 / maxden;
|
||||
c *= rcmaxden;
|
||||
d *= rcmaxden;
|
||||
|
||||
T den = 1 / sqrt(c * c + d * d);
|
||||
|
||||
T numx = (-b * c + a * d);
|
||||
T numy = (a * c + b * d);
|
||||
x = numx * den;
|
||||
y = numy * den;
|
||||
z = maxden / den;
|
||||
|
||||
s2 = -c * den;
|
||||
c2 = d * den;
|
||||
}
|
||||
|
||||
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
void Svd2x2Helper(const Matrix<T, 2, 2, Order, Layout, Packed>& A, T& c1, T& s1, T& c2, T& s2, T& d1, T& d2) {
|
||||
// Calculate RQ decomposition of A
|
||||
T x, y, z;
|
||||
Rq2x2Helper(A, x, y, z, c2, s2);
|
||||
|
||||
// Calculate tangent of rotation on R[x,y;0,z] to diagonalize R^T*R
|
||||
T scaler = T(1) / std::max(std::abs(x), std::max(std::abs(y), std::numeric_limits<T>::min()));
|
||||
T x_ = x * scaler, y_ = y * scaler, z_ = z * scaler;
|
||||
T numer = ((z_ - x_) * (z_ + x_)) + y_ * y_;
|
||||
T gamma = x_ * y_;
|
||||
numer = numer == 0 ? std::numeric_limits<T>::infinity() : numer;
|
||||
T zeta = numer / gamma;
|
||||
|
||||
T t = 2 * sign_nonzero(zeta) / (std::abs(zeta) + std::sqrt(zeta * zeta + 4));
|
||||
|
||||
// Calculate sines and cosines
|
||||
c1 = T(1) / std::sqrt(T(1) + t * t);
|
||||
s1 = c1 * t;
|
||||
|
||||
// Calculate U*S = R*R(c1,s1)
|
||||
T usa = c1 * x - s1 * y;
|
||||
T usb = s1 * x + c1 * y;
|
||||
T usc = -s1 * z;
|
||||
T usd = c1 * z;
|
||||
|
||||
// Update V = R(c1,s1)^T*Q
|
||||
t = c1 * c2 + s1 * s2;
|
||||
s2 = c2 * s1 - c1 * s2;
|
||||
c2 = t;
|
||||
|
||||
// Separate U and S
|
||||
d1 = std::hypot(usa, usc);
|
||||
d2 = std::hypot(usb, usd);
|
||||
T dmax = std::max(d1, d2);
|
||||
T usmax1 = d2 > d1 ? usd : usa;
|
||||
T usmax2 = d2 > d1 ? usb : -usc;
|
||||
|
||||
T signd1 = sign_nonzero(x * z);
|
||||
dmax *= d2 > d1 ? signd1 : 1;
|
||||
d2 *= signd1;
|
||||
T rcpdmax = 1 / dmax;
|
||||
|
||||
c1 = dmax != T(0) ? usmax1 * rcpdmax : T(1);
|
||||
s1 = dmax != T(0) ? usmax2 * rcpdmax : T(0);
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeSVD(Matrix<T, Rows, Columns, Order, Layout, Packed> m, std::true_type) {
|
||||
Matrix<T, Rows, Columns, Order, eMatrixLayout::COLUMN_MAJOR, false> B;
|
||||
Matrix<T, Rows, Columns, Order, eMatrixLayout::COLUMN_MAJOR, false> U;
|
||||
Matrix<T, Columns, Columns, Order, eMatrixLayout::COLUMN_MAJOR, false> V;
|
||||
|
||||
// Precondition with QR if needed
|
||||
if (Rows > Columns) {
|
||||
auto [Q, R] = DecomposeQR(m);
|
||||
B = R;
|
||||
U = Q.template Submatrix<Rows, Columns>(0, 0);
|
||||
V = Identity();
|
||||
}
|
||||
else {
|
||||
B = m;
|
||||
U = Identity();
|
||||
V = Identity();
|
||||
}
|
||||
|
||||
T tolerance = T(1e-6);
|
||||
|
||||
T N = NormSquared(B);
|
||||
T s;
|
||||
|
||||
do {
|
||||
s = 0;
|
||||
|
||||
for (int i = 0; i < m.ColumnCount(); ++i) {
|
||||
for (int j = i + 1; j < m.ColumnCount(); ++j) {
|
||||
s += B(i, j) * B(i, j) + B(j, i) * B(j, i);
|
||||
|
||||
T s1, c1, s2, c2, d1, d2; // SVD of the submat row,col i,j of B
|
||||
Matrix<T, 2, 2, eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout::ROW_MAJOR, false> Bsub = {
|
||||
B(i, i), B(i, j),
|
||||
B(j, i), B(j, j)
|
||||
};
|
||||
Svd2x2Helper(Bsub, c1, s1, c2, s2, d1, d2);
|
||||
|
||||
// Apply givens rotations given by 2x2 SVD to working matrices
|
||||
// B = R(c1,s1)*B*R(c2,-s2)
|
||||
Vector<T, 4, false> givensCoeffs = { c1, -s1, s1, c1 };
|
||||
Vector<T, 4, false> bElems;
|
||||
for (int col = 0; col < B.ColumnCount(); ++col) {
|
||||
bElems = { B(i, col), B(j, col), B(i, col), B(j, col) };
|
||||
bElems *= givensCoeffs;
|
||||
B(i, col) = bElems(0) + bElems(1);
|
||||
B(j, col) = bElems(2) + bElems(3);
|
||||
}
|
||||
auto coli = B.stripes[i];
|
||||
B.stripes[i] = c2 * coli + -s2 * B.stripes[j];
|
||||
B.stripes[j] = s2 * coli + c2 * B.stripes[j];
|
||||
|
||||
// U = U*R(c1,s1);
|
||||
coli = U.stripes[i];
|
||||
U.stripes[i] = c1 * coli + -s1 * U.stripes[j];
|
||||
U.stripes[j] = s1 * coli + c1 * U.stripes[j];
|
||||
|
||||
// V = V*R(c2,s2);
|
||||
auto coliv = V.stripes[i];
|
||||
V.stripes[i] = c2 * coliv + -s2 * V.stripes[j];
|
||||
V.stripes[j] = s2 * coliv + c2 * V.stripes[j];
|
||||
}
|
||||
}
|
||||
} while (s > tolerance * N);
|
||||
|
||||
Matrix<T, Columns, Columns, Order, Layout, Packed> Sout;
|
||||
Sout = Zero();
|
||||
for (int i = 0; i < B.ColumnCount(); ++i) {
|
||||
Sout(i, i) = B(i, i);
|
||||
}
|
||||
|
||||
return DecompositionSVD<T, Rows, Columns, Order, Layout, Packed>{ U, Sout, Transpose(V) };
|
||||
}
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeSVD(Matrix<T, Rows, Columns, Order, Layout, Packed> m, std::false_type) {
|
||||
auto [U, S, V] = DecomposeSVD(Transpose(m), std::true_type{});
|
||||
return DecompositionSVD<T, Rows, Columns, Order, Layout, Packed>{ Transpose(V), S, Transpose(U) };
|
||||
}
|
||||
|
||||
|
||||
} // namespace impl
|
||||
|
||||
|
||||
/// <summary> Calculates the thin SVD of the matrix. </summary>
|
||||
/// <remarks> For wide matrices, V is wide while U and S square.
|
||||
/// For tall matrices, U is tall while S and V square. </remarks>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DecomposeSVD(Matrix<T, Rows, Columns, Order, Layout, Packed> m) {
|
||||
return impl::DecomposeSVD(m, std::integral_constant<bool, (Rows >= Columns)>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,499 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Vector.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Shapes
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Dim>
|
||||
class Hyperplane;
|
||||
|
||||
template <class T, int Dim>
|
||||
class Line {
|
||||
using VectorT = Vector<T, Dim>;
|
||||
|
||||
public:
|
||||
/// <summary> Does not zero-initialize members. </summary>
|
||||
Line() = default;
|
||||
|
||||
/// <summary> Construct a line through <paramref name="base"/> in given <paramref name="direction"/>. </summary>
|
||||
/// <param name="base"> Any point in 3D space. </param>
|
||||
/// <param name="direction"> Must be normalized. </param>
|
||||
Line(const VectorT& base, const VectorT& direction) : direction(direction), base(base) {
|
||||
assert(IsNormalized(direction));
|
||||
}
|
||||
|
||||
/// <summary> Constructs a line through both points. </summary>
|
||||
/// <param name="point1"> Base of the line. </param>
|
||||
/// <param name="point2"> Specifies direction only. </param>
|
||||
static Line Through(const VectorT& point1, const VectorT& point2) {
|
||||
return Line(point1, SafeNormalize(point2 - point1));
|
||||
}
|
||||
|
||||
/// <summary> A 2D plane and line are equivalent, converts representation. Only for 2D. </summary>
|
||||
Line(const Hyperplane<T, 2>& plane);
|
||||
|
||||
/// <summary> Return the signed direction of the line (as given in constructor). </summary>
|
||||
VectorT Direction() const { return direction; }
|
||||
|
||||
/// <summary> Returns the base point or point1 as given in constructor. </summary>
|
||||
VectorT Base() const { return base; }
|
||||
|
||||
/// <summary> Returns the point at <paramref name="param"/> distance from the base point along direction. </summary>
|
||||
VectorT PointAt(T param) const { return base + param * direction; }
|
||||
|
||||
public:
|
||||
VectorT direction, base;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim>
|
||||
class LineSegment {
|
||||
using VectorT = Vector<T, Dim>;
|
||||
|
||||
public:
|
||||
LineSegment() : point1(0), point2(0) {
|
||||
point2(0) = 1;
|
||||
}
|
||||
LineSegment(const VectorT& base, const VectorT& direction, T length) {
|
||||
point1 = base;
|
||||
point2 = base + direction * length;
|
||||
}
|
||||
LineSegment(const VectorT& point1, const VectorT& point2) : point1(point1), point2(point2) {}
|
||||
|
||||
T Length() const { return mathter::Length(point2 - point1); }
|
||||
VectorT Direction() const { return Normalize(point2 - point1); }
|
||||
VectorT Start() const { return point1; }
|
||||
VectorT End() const { return point2; }
|
||||
VectorT Interpol(T t) const { return t * point2 + (T(1) - t) * point1; }
|
||||
|
||||
mathter::Line<T, Dim> Line() const {
|
||||
return mathter::Line<T, Dim>{ point1, Direction() };
|
||||
}
|
||||
|
||||
public:
|
||||
VectorT point1, point2;
|
||||
};
|
||||
|
||||
|
||||
template <class T, int Dim>
|
||||
class Ray : protected Line<T, Dim> {
|
||||
public:
|
||||
// Inhreitance is protected to deny casting.
|
||||
// Casting is bad 'cause we don't want to implicit cast a Ray to a Line and Intersect() it with a plane.
|
||||
using Line<T, Dim>::Line;
|
||||
using Line<T, Dim>::Through;
|
||||
using Line<T, Dim>::Direction;
|
||||
using Line<T, Dim>::Base;
|
||||
using Line<T, Dim>::PointAt;
|
||||
using Line<T, Dim>::direction;
|
||||
using Line<T, Dim>::base;
|
||||
mathter::Line<T, Dim> Line() const {
|
||||
return static_cast<mathter::Line<T, Dim>>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim>
|
||||
class Hyperplane {
|
||||
using VectorT = Vector<T, Dim>;
|
||||
|
||||
public:
|
||||
Hyperplane() : normal(0), scalar(0) { normal(0) = 1; }
|
||||
Hyperplane(const VectorT& base, const VectorT& normal) : normal(normal) {
|
||||
assert(IsNormalized(normal));
|
||||
scalar = Dot(normal, base);
|
||||
}
|
||||
Hyperplane(const VectorT& normal, T scalar) : normal(normal), scalar(scalar) {
|
||||
assert(IsNormalized(normal));
|
||||
}
|
||||
Hyperplane(const Line<T, 2>& line) {
|
||||
static_assert(Dim == 2, "Plane dimension must be two, which is a line.");
|
||||
normal = { -line.Direction()(1), line.Direction()(0) };
|
||||
scalar = Dot(normal, line.Base());
|
||||
}
|
||||
|
||||
const VectorT& Normal() const { return normal; }
|
||||
T Scalar() const { return scalar; }
|
||||
|
||||
template <bool Packed>
|
||||
T Distance(const Vector<T, Dim, Packed>& point) {
|
||||
return Dot(point, normal) - scalar;
|
||||
}
|
||||
|
||||
private:
|
||||
VectorT normal;
|
||||
T scalar;
|
||||
};
|
||||
|
||||
|
||||
template <class T, int Dim>
|
||||
Line<T, Dim>::Line(const Hyperplane<T, 2>& plane) {
|
||||
static_assert(Dim == 2, "Line dimension must be two, since it a plane in 2 dimensional space.");
|
||||
|
||||
// Intersect plane's line with line through origo perpendicular to plane to find suitable base
|
||||
T a = plane.Normal()(0);
|
||||
T b = plane.Normal()(1);
|
||||
T d = plane.Scalar();
|
||||
T div = (a * a + b * b);
|
||||
base = { a * d / div, b * d / div };
|
||||
direction = { b, -a };
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
class Triangle3D {
|
||||
public:
|
||||
Triangle3D() = default;
|
||||
Triangle3D(const Vector<T, 3, false>& a, const Vector<T, 3, false>& b, const Vector<T, 3, false>& c) : a(a), b(b), c(c) {}
|
||||
Vector<T, 3, false> a, b, c; // Corners of the traingle.
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Intersections
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
template <class T, class U>
|
||||
class Intersection;
|
||||
|
||||
|
||||
|
||||
template <class T, class U>
|
||||
auto Intersect(const T& t, const U& u) {
|
||||
return Intersection<T, U>(t, u);
|
||||
}
|
||||
|
||||
|
||||
// Plane-line intersection
|
||||
template <class T, int Dim>
|
||||
class Intersection<Hyperplane<T, Dim>, Line<T, Dim>> {
|
||||
protected:
|
||||
using PlaneT = Hyperplane<T, Dim>;
|
||||
using LineT = Line<T, Dim>;
|
||||
using VectorT = Vector<T, Dim>;
|
||||
|
||||
public:
|
||||
Intersection(const PlaneT& plane, const LineT& line);
|
||||
|
||||
bool Intersecting() const { return !std::isinf(param); }
|
||||
VectorT Point() const { return line.PointAt(param); }
|
||||
T LineParameter() const { return param; }
|
||||
|
||||
private:
|
||||
LineT line;
|
||||
T param;
|
||||
};
|
||||
|
||||
template <class T, int Dim>
|
||||
class Intersection<Line<T, Dim>, Hyperplane<T, Dim>> : public Intersection<Hyperplane<T, Dim>, Line<T, Dim>> {
|
||||
using PlaneT = Hyperplane<T, Dim>;
|
||||
using LineT = Line<T, Dim>;
|
||||
|
||||
public:
|
||||
Intersection(const LineT& line, const PlaneT& plane) : Intersection<Hyperplane<T, Dim>, Line<T, Dim>>(plane, line) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Plane-line segment intersection
|
||||
template <class T, int Dim>
|
||||
class Intersection<Hyperplane<T, Dim>, LineSegment<T, Dim>> {
|
||||
using PlaneT = Hyperplane<T, Dim>;
|
||||
using LineT = LineSegment<T, Dim>;
|
||||
using VectorT = Vector<T, Dim>;
|
||||
|
||||
public:
|
||||
Intersection(const PlaneT& plane, const LineT& line) {
|
||||
lineSegment = line;
|
||||
auto intersection = Intersect(plane, line.Line());
|
||||
param = intersection.LineParameter() / line.Length();
|
||||
}
|
||||
|
||||
bool Intersecting() const { return T(0) <= param && param <= T(1); }
|
||||
VectorT Point() const { return lineSegment.Interpol(param); }
|
||||
T InterpolParameter() const { return param; }
|
||||
T LineParameter() const { return param * lineSegment.Length(); }
|
||||
|
||||
private:
|
||||
LineT lineSegment;
|
||||
T param;
|
||||
};
|
||||
|
||||
template <class T, int Dim>
|
||||
class Intersection<LineSegment<T, Dim>, Hyperplane<T, Dim>> : public Intersection<Hyperplane<T, Dim>, LineSegment<T, Dim>> {
|
||||
using PlaneT = Hyperplane<T, Dim>;
|
||||
using LineT = LineSegment<T, Dim>;
|
||||
|
||||
public:
|
||||
Intersection(const LineT& line, const PlaneT& plane) : Intersection<Hyperplane<T, Dim>, LineSegment<T, Dim>>(plane, line) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim>
|
||||
Intersection<Hyperplane<T, Dim>, Line<T, Dim>>::Intersection(const PlaneT& plane, const LineT& line) {
|
||||
this->line = line;
|
||||
|
||||
// We have to solve the system of linear equations for x,y,z,t
|
||||
// |d | |a b c 0| |x|
|
||||
// |px| |1 0 0 p| |y|
|
||||
// |py| = |0 1 0 q| * |z|
|
||||
// |pz| |0 0 1 r| |t|
|
||||
// b = A * x
|
||||
// where [px,py,pz] + t*[p,q,r] = [x,y,z] is the line's equation
|
||||
// and ax + by + cz + d = 0 is the plane's equation
|
||||
|
||||
Vector<T, Dim + 1> b;
|
||||
Vector<T, Dim + 1> A_inv_t;
|
||||
|
||||
// Fill up 'b'
|
||||
b = -plane.Scalar() | line.Base();
|
||||
|
||||
// Fill up 'A_inv_t', which is the last line of A^-1, used to calculate 't'
|
||||
A_inv_t = 1 | plane.Normal();
|
||||
|
||||
// Compute result of the equation
|
||||
T scaler = Dot(line.Direction(), plane.Normal());
|
||||
T x_t = Dot(A_inv_t, b);
|
||||
T t = x_t / scaler;
|
||||
param = -t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 2D line intersection
|
||||
// with lines
|
||||
template <class T>
|
||||
class Intersection<Line<T, 2>, Line<T, 2>> {
|
||||
using LineT = Line<T, 2>;
|
||||
|
||||
public:
|
||||
Intersection(const LineT& l1, const LineT& l2) {
|
||||
line2 = l2;
|
||||
auto intersection = Intersect(Hyperplane<T, 2>(l1), l2);
|
||||
param2 = intersection.LineParameter();
|
||||
param1 = std::isinf(param2) ? std::numeric_limits<T>::infinity() : Length(intersection.Point() - l1.Base());
|
||||
}
|
||||
|
||||
bool Intersecting() const { return !std::isinf(param1); }
|
||||
T LineParameter1() const { return param1; }
|
||||
T LineParameter2() const { return param2; }
|
||||
Vector<T, 2> Point() const { return line2.PointAt(param2); }
|
||||
|
||||
private:
|
||||
T param1, param2;
|
||||
LineT line2;
|
||||
};
|
||||
|
||||
// with hyperplanes
|
||||
template <class T>
|
||||
class Intersection<Hyperplane<T, 2>, Hyperplane<T, 2>> : public Intersection<Line<T, 2>, Line<T, 2>> {
|
||||
public:
|
||||
Intersection(const Hyperplane<T, 2>& p1, const Hyperplane<T, 2>& p2) : Intersection<Line<T, 2>, Line<T, 2>>(p1, p2) {}
|
||||
};
|
||||
|
||||
|
||||
// line segments
|
||||
template <class T>
|
||||
class Intersection<LineSegment<T, 2>, LineSegment<T, 2>> {
|
||||
public:
|
||||
Intersection(const LineSegment<T, 2>& l1, const LineSegment<T, 2>& l2) {
|
||||
lineSegment1 = l1;
|
||||
lineSegment2 = l2;
|
||||
auto intersection = Intersect(l1.Line(), l2.Line());
|
||||
if (intersection.Intersecting()) {
|
||||
param1 = intersection.LineParameter1() / l1.Length();
|
||||
param2 = intersection.LineParameter2() / l2.Length();
|
||||
}
|
||||
else {
|
||||
param1 = param2 = std::numeric_limits<T>::infinity();
|
||||
}
|
||||
}
|
||||
|
||||
bool Intersecting() const {
|
||||
return (T(0) <= param1 && param1 <= T(1))
|
||||
&& (T(0) <= param2 && param2 <= T(1));
|
||||
}
|
||||
Vector<T, 2> Point() const { return lineSegment1.Interpol(param1); }
|
||||
T InterpolParameter1() const { return param1; }
|
||||
T InterpolParameter2() const { return param2; }
|
||||
T LineParameter1() const { return param1 * lineSegment1.Length(); }
|
||||
T LineParameter2() const { return param2 * lineSegment2.Length(); }
|
||||
|
||||
private:
|
||||
T param1;
|
||||
T param2;
|
||||
LineSegment<T, 2> lineSegment1;
|
||||
LineSegment<T, 2> lineSegment2;
|
||||
};
|
||||
|
||||
|
||||
// line segment vs line2d
|
||||
template <class T>
|
||||
class Intersection<LineSegment<T, 2>, Line<T, 2>> {
|
||||
public:
|
||||
Intersection(const LineSegment<T, 2>& line1, const Line<T, 2>& line2) {
|
||||
auto inter = Intersect(line1.Line(), line2);
|
||||
if (inter.Intersecting() && inter.LineParameter1() < line1.Length()) {
|
||||
param1 = inter.LineParameter1();
|
||||
param2 = inter.LineParameter2();
|
||||
}
|
||||
else {
|
||||
param1 = param2 = std::numeric_limits<T>::infinity();
|
||||
}
|
||||
}
|
||||
|
||||
bool Intersecting() const { return !isinf(param1); }
|
||||
Vector<T, 2> Point() const { return line1.Line().PointAt(param1); }
|
||||
T LineParameter1() const { return param1; }
|
||||
T InterpolParameter1() const { return param1 / line1.Length(); }
|
||||
T LineParameter2() const { return param2; }
|
||||
|
||||
private:
|
||||
T param1;
|
||||
T param2;
|
||||
LineSegment<T, 2> line1;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Intersection<Line<T, 2>, LineSegment<T, 2>> : private Intersection<LineSegment<T, 2>, Line<T, 2>> {
|
||||
public:
|
||||
Intersection(const Line<T, 2>& line1, const LineSegment<T, 2>& line2)
|
||||
: Intersection<LineSegment<T, 2>, Line<T, 2>>(line2, line1) {}
|
||||
|
||||
using Intersection<LineSegment<T, 2>, Line<T, 2>>::Intersecting;
|
||||
using Intersection<LineSegment<T, 2>, Line<T, 2>>::Point;
|
||||
|
||||
T LineParameter1() const { return Intersection<LineSegment<T, 2>, Line<T, 2>>::LineParameter2(); }
|
||||
T InterpolParameter2() const { return Intersection<LineSegment<T, 2>, Line<T, 2>>::InterpolParameter1(); }
|
||||
T LineParameter2() const { return Intersection<LineSegment<T, 2>, Line<T, 2>>::LineParameter1(); }
|
||||
};
|
||||
|
||||
|
||||
// Ray-triangle intersection (M<>ller-Trumbore algorithm)
|
||||
template <class T>
|
||||
class Intersection<Ray<T, 3>, Triangle3D<T>> {
|
||||
using VectorT = Vector<T, 3, false>;
|
||||
|
||||
public:
|
||||
Intersection(const Ray<T, 3>& ray, const Triangle3D<T>& triangle);
|
||||
|
||||
bool IsIntersecting() const { return intersecting; }
|
||||
VectorT Point() const { return point; }
|
||||
|
||||
template <class U>
|
||||
U Interpolate(const U& a, const U& b, const U& c) const;
|
||||
|
||||
T GetT() const { return t; }
|
||||
T GetU() const { return u; }
|
||||
T GetV() const { return v; }
|
||||
|
||||
private:
|
||||
T t, u, v;
|
||||
bool intersecting;
|
||||
VectorT point;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
Intersection<Ray<T, 3>, Triangle3D<T>>::Intersection(const Ray<T, 3>& ray, const Triangle3D<T>& triangle) {
|
||||
constexpr T EPSILON = T(0.00000001);
|
||||
|
||||
VectorT edge1 = triangle.b - triangle.a;
|
||||
VectorT edge2 = triangle.c - triangle.a;
|
||||
|
||||
VectorT h = Cross(ray.Direction(), edge2);
|
||||
T a = Dot(edge1, h);
|
||||
|
||||
if (std::abs(a) < EPSILON) {
|
||||
intersecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
T f = T(1) / a;
|
||||
VectorT s = ray.Base() - triangle.a;
|
||||
u = f * Dot(s, h);
|
||||
|
||||
if (u < T(0) || u > T(1)) {
|
||||
intersecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
VectorT q = Cross(s, edge1);
|
||||
v = f * Dot(ray.Direction(), q);
|
||||
|
||||
if (v < 0.0 || u + v > 1.0) {
|
||||
intersecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
t = f * Dot(edge2, q);
|
||||
intersecting = t > EPSILON;
|
||||
if (intersecting) {
|
||||
point = ray.PointAt(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
template <class U>
|
||||
U Intersection<Ray<T, 3>, Triangle3D<T>>::Interpolate(const U& a, const U& b, const U& c) const {
|
||||
T w = T(1) - u - v;
|
||||
return u * b + v * c + w * a;
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, int Order>
|
||||
class BezierCurve {
|
||||
static_assert(Order >= 1, "Bezier curve must have order n>=1.");
|
||||
|
||||
public:
|
||||
using VectorT = Vector<T, Dim, false>;
|
||||
|
||||
VectorT operator()(T t) const {
|
||||
return EvalInterpolRecurse(t);
|
||||
}
|
||||
|
||||
protected:
|
||||
VectorT EvalInterpolRecurse(T t) const;
|
||||
|
||||
public:
|
||||
std::array<VectorT, Order + 1> p;
|
||||
};
|
||||
|
||||
|
||||
template <class T, int Dim, int Order>
|
||||
auto BezierCurve<T, Dim, Order>::EvalInterpolRecurse(T t) const -> VectorT {
|
||||
std::array<VectorT, Order + 1> reduction = p;
|
||||
|
||||
T u = T(1) - t;
|
||||
|
||||
for (int i = Order; i >= 1; --i) {
|
||||
for (int j = 1; j <= i; ++j) {
|
||||
reduction[j - 1] = u * reduction[j - 1] + t * reduction[j];
|
||||
}
|
||||
}
|
||||
|
||||
return reduction[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,263 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Vector.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace mathter {
|
||||
|
||||
enum class eEnclosingBracket {
|
||||
NONE,
|
||||
PARANTHESE,
|
||||
BRACKET,
|
||||
BRACE,
|
||||
};
|
||||
|
||||
/// <summary> Prints the vector like [1,2,3]. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
std::ostream& operator<<(std::ostream& os, const mathter::Vector<T, Dim, Packed>& v) {
|
||||
os << "[";
|
||||
for (int x = 0; x < Dim; ++x) {
|
||||
os << v(x) << (x == Dim - 1 ? "" : ", ");
|
||||
}
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
namespace impl {
|
||||
template <class T>
|
||||
struct dependent_false {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <class T>
|
||||
constexpr bool dependent_false_v = dependent_false<T>::value;
|
||||
|
||||
template <class AritT, typename std::enable_if<std::is_integral<AritT>::value && std::is_signed<AritT>::value, int>::type = 0>
|
||||
AritT strtonum(const char* str, const char** end) {
|
||||
AritT value;
|
||||
value = (AritT)strtoll(str, (char**)end, 10);
|
||||
return value;
|
||||
}
|
||||
template <class AritT, typename std::enable_if<std::is_integral<AritT>::value && !std::is_signed<AritT>::value, int>::type = 0>
|
||||
AritT strtonum(const char* str, const char** end) {
|
||||
AritT value;
|
||||
value = (AritT)strtoull(str, (char**)end, 10);
|
||||
return value;
|
||||
}
|
||||
template <class AritT, typename std::enable_if<std::is_floating_point<AritT>::value, int>::type = 0>
|
||||
AritT strtonum(const char* str, const char** end) {
|
||||
AritT value;
|
||||
value = (AritT)strtold(str, (char**)end);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline const char* StripSpaces(const char* str) {
|
||||
while (*str != '\0' && isspace(*str))
|
||||
++str;
|
||||
return str;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
/// <summary> Parses a vector from a string. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> strtovec(const char* str, const char** end) {
|
||||
Vector<T, Dim, Packed> ret;
|
||||
|
||||
const char* strproc = str;
|
||||
|
||||
// parse initial bracket if any
|
||||
strproc = impl::StripSpaces(strproc);
|
||||
if (*strproc == '\0') {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char startBracket = *strproc;
|
||||
char endBracket;
|
||||
bool hasBrackets = false;
|
||||
switch (startBracket) {
|
||||
case '(':
|
||||
endBracket = ')';
|
||||
hasBrackets = true;
|
||||
++strproc;
|
||||
break;
|
||||
case '[':
|
||||
endBracket = ']';
|
||||
hasBrackets = true;
|
||||
++strproc;
|
||||
break;
|
||||
case '{':
|
||||
endBracket = '}';
|
||||
hasBrackets = true;
|
||||
++strproc;
|
||||
break;
|
||||
}
|
||||
|
||||
// parse elements
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
const char* elemend;
|
||||
T elem = impl::strtonum<T>(strproc, &elemend);
|
||||
if (elemend == strproc) {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
ret[i] = elem;
|
||||
strproc = elemend;
|
||||
}
|
||||
strproc = impl::StripSpaces(strproc);
|
||||
if (*strproc == ',') {
|
||||
++strproc;
|
||||
}
|
||||
}
|
||||
|
||||
// parse ending bracket corresponding to initial bracket
|
||||
if (hasBrackets) {
|
||||
strproc = impl::StripSpaces(strproc);
|
||||
if (*strproc != endBracket) {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
++strproc;
|
||||
}
|
||||
|
||||
*end = strproc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class VectorT>
|
||||
VectorT strtovec(const char* str, const char** end) {
|
||||
static_assert(traits::IsVector<VectorT>::value, "This type is not a Vector, dumbass.");
|
||||
|
||||
return strtovec<
|
||||
typename traits::VectorTraits<VectorT>::Type,
|
||||
traits::VectorTraits<VectorT>::Dim,
|
||||
traits::VectorTraits<VectorT>::Packed>(str, end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
std::ostream& operator<<(std::ostream& os, const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat) {
|
||||
os << "[";
|
||||
for (int i = 0; i < mat.Height(); ++i) {
|
||||
for (int j = 0; j < mat.Width(); ++j) {
|
||||
os << mat(i, j) << (j == mat.Width() - 1 ? "" : ", ");
|
||||
}
|
||||
if (i < Rows - 1) {
|
||||
os << "; ";
|
||||
}
|
||||
}
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> strtomat(const char* str, const char** end) {
|
||||
using MatrixT = Matrix<T, Rows, Columns, Order, Layout, Packed>;
|
||||
using VectorT = Vector<T, Columns, Packed>;
|
||||
MatrixT ret;
|
||||
|
||||
const char* strproc = str;
|
||||
|
||||
// parse initial bracket if any
|
||||
strproc = impl::StripSpaces(strproc);
|
||||
if (*strproc == '\0') {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char startBracket = *strproc;
|
||||
char endBracket;
|
||||
bool hasBrackets = false;
|
||||
switch (startBracket) {
|
||||
case '(':
|
||||
endBracket = ')';
|
||||
hasBrackets = true;
|
||||
++strproc;
|
||||
break;
|
||||
case '[':
|
||||
endBracket = ']';
|
||||
hasBrackets = true;
|
||||
++strproc;
|
||||
break;
|
||||
case '{':
|
||||
endBracket = '}';
|
||||
hasBrackets = true;
|
||||
++strproc;
|
||||
break;
|
||||
}
|
||||
|
||||
// parse rows
|
||||
for (int i = 0; i < Rows; ++i) {
|
||||
const char* rowend;
|
||||
VectorT row = strtovec<VectorT>(strproc, &rowend);
|
||||
if (rowend == strproc) {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
ret.Row(i) = row;
|
||||
strproc = rowend;
|
||||
}
|
||||
strproc = impl::StripSpaces(strproc);
|
||||
if (i < Rows - 1) {
|
||||
if (*strproc == ';') {
|
||||
++strproc;
|
||||
}
|
||||
else {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse ending bracket corresponding to initial bracket
|
||||
if (hasBrackets) {
|
||||
strproc = impl::StripSpaces(strproc);
|
||||
if (*strproc != endBracket) {
|
||||
*end = str;
|
||||
return ret;
|
||||
}
|
||||
++strproc;
|
||||
}
|
||||
|
||||
*end = strproc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class MatrixT>
|
||||
MatrixT strtomat(const char* str, const char** end) {
|
||||
static_assert(traits::IsMatrix<MatrixT>::value, "This type if not a matrix, dumbass.");
|
||||
|
||||
return strtomat<
|
||||
typename traits::MatrixTraits<MatrixT>::Type,
|
||||
traits::MatrixTraits<MatrixT>::Rows,
|
||||
traits::MatrixTraits<MatrixT>::Columns,
|
||||
traits::MatrixTraits<MatrixT>::Order,
|
||||
traits::MatrixTraits<MatrixT>::Layout,
|
||||
traits::MatrixTraits<MatrixT>::Packed>(str, end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, bool Packed>
|
||||
std::ostream& operator<<(std::ostream& os, const Quaternion<T, Packed>& q) {
|
||||
os << "["
|
||||
<< q.Angle() * T(180.0) / T(3.1415926535897932384626)
|
||||
<< " deg @ "
|
||||
<< q.Axis()
|
||||
<< "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
//L=============================================================================
|
||||
//L This software is distributed under the MIT license.
|
||||
//L Copyright 2021 Péter Kardos
|
||||
//L=============================================================================
|
||||
-->
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
|
||||
<Type Name="mathter::Vector<*>">
|
||||
<DisplayString Condition="$T2==1">[{x}]</DisplayString>
|
||||
<DisplayString Condition="$T2==2">[{x}, {y}]</DisplayString>
|
||||
<DisplayString Condition="$T2==3">[{x}, {y}, {z}]</DisplayString>
|
||||
<DisplayString Condition="$T2==4">[{data[0]}, {y}, {z}, {w}]</DisplayString>
|
||||
<DisplayString Condition="$T2==5">[{data[0]}, {data[1]}, {data[2]}, {data[3]}, {data[4]}]</DisplayString>
|
||||
<DisplayString Condition="$T2>5">dim={$T2} [{data[0]}, {data[0]}, ..., {data[$T2-1]}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[dimension]" ExcludeView="simple">$T2</Item>
|
||||
<ArrayItems>
|
||||
<Size>$T2</Size>
|
||||
<ValuePointer>($T1*)this</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="mathter::Matrix<*>">
|
||||
<DisplayString>{{rows={$T2}, columns={$T3}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[rows]" ExcludeView="simple">$T2</Item>
|
||||
<Item Name="[columns]" ExcludeView="simple">$T3</Item>
|
||||
<ArrayItems Condition="$T5==0">
|
||||
<Size>$T2</Size>
|
||||
<ValuePointer>stripes._Elems</ValuePointer>
|
||||
</ArrayItems>
|
||||
<ArrayItems Condition="$T5==1">
|
||||
<Size>$T3</Size>
|
||||
<ValuePointer>stripes._Elems</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="mathter::Quaternion<*>">
|
||||
<DisplayString>[{w} + {i} i + {j} j + {k} k]</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="mathter::Hyperplane<*>">
|
||||
<DisplayString Condition="$T2==3">{normal.x} x + {normal.y} y + {normal.z} z = {scalar}</DisplayString>
|
||||
<DisplayString Condition="$T2==2">{normal.x} x + {normal.y} y = {scalar}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[normal]">normal</Item>
|
||||
<Item Name="[scalar]">scalar</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="mathter::Line<*>">
|
||||
<DisplayString>[v] = {base} + {direction} t</DisplayString>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
|
@ -0,0 +1,26 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Decompositions/DecomposeLU.hpp"
|
||||
#include "Decompositions/DecomposeQR.hpp"
|
||||
#include "Decompositions/DecomposeSVD.hpp"
|
||||
#include "Matrix/MatrixArithmetic.hpp"
|
||||
#include "Matrix/MatrixCast.hpp"
|
||||
#include "Matrix/MatrixCompare.hpp"
|
||||
#include "Matrix/MatrixFunction.hpp"
|
||||
#include "Matrix/MatrixImpl.hpp"
|
||||
#include "Matrix/MatrixVectorArithmetic.hpp"
|
||||
#include "Transforms/IdentityBuilder.hpp"
|
||||
#include "Transforms/OrthographicBuilder.hpp"
|
||||
#include "Transforms/PerspectiveBuilder.hpp"
|
||||
#include "Transforms/Rotation2DBuilder.hpp"
|
||||
#include "Transforms/Rotation3DBuilder.hpp"
|
||||
#include "Transforms/ScaleBuilder.hpp"
|
||||
#include "Transforms/ShearBuilder.hpp"
|
||||
#include "Transforms/TranslationBuilder.hpp"
|
||||
#include "Transforms/ViewBuilder.hpp"
|
||||
#include "Transforms/ZeroBuilder.hpp"
|
|
@ -0,0 +1,382 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MatrixImpl.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Matrix-matrix multiplication
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace impl {
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, bool Packed, int... MatchIndices>
|
||||
inline auto SmallProductRowRR(const Matrix<T, Rows1, Match, Order, eMatrixLayout::ROW_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed>& rhs,
|
||||
int row,
|
||||
std::integer_sequence<int, MatchIndices...>) {
|
||||
return (... + (rhs.stripes[MatchIndices] * lhs(row, MatchIndices)));
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, bool Packed, int... RowIndices>
|
||||
inline auto SmallProductRR(const Matrix<T, Rows1, Match, Order, eMatrixLayout::ROW_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed>& rhs,
|
||||
std::integer_sequence<int, RowIndices...>) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
using ResultT = Matrix<V, Rows1, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed>;
|
||||
return ResultT{ ResultT::FromStripes, SmallProductRowRR(lhs, rhs, RowIndices, std::make_integer_sequence<int, Match>{})... };
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, eMatrixLayout Layout2, bool Packed, int... MatchIndices>
|
||||
inline auto SmallProductRowCC(const Matrix<T, Rows1, Match, Order, eMatrixLayout::COLUMN_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, Layout2, Packed>& rhs,
|
||||
int col,
|
||||
std::integer_sequence<int, MatchIndices...>) {
|
||||
return (... + (lhs.stripes[MatchIndices] * rhs(MatchIndices, col)));
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, eMatrixLayout Layout2, bool Packed, int... ColIndices>
|
||||
inline auto SmallProductCC(const Matrix<T, Rows1, Match, Order, eMatrixLayout::COLUMN_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, Layout2, Packed>& rhs,
|
||||
std::integer_sequence<int, ColIndices...>) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
using ResultT = Matrix<V, Rows1, Columns2, Order, eMatrixLayout::COLUMN_MAJOR, Packed>;
|
||||
return ResultT{ ResultT::FromStripes, SmallProductRowCC(lhs, rhs, ColIndices, std::make_integer_sequence<int, Match>{})... };
|
||||
}
|
||||
} // namespace impl
|
||||
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, bool Packed>
|
||||
inline auto operator*(const Matrix<T, Rows1, Match, Order, eMatrixLayout::ROW_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed>& rhs) {
|
||||
if constexpr (Rows1 <= 4 && Match <= 4 && Columns2 <= 4) {
|
||||
return impl::SmallProductRR(lhs, rhs, std::make_integer_sequence<int, Rows1>{});
|
||||
}
|
||||
else {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
Matrix<V, Rows1, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed> result;
|
||||
for (int i = 0; i < Rows1; ++i) {
|
||||
result.stripes[i] = rhs.stripes[0] * lhs(i, 0);
|
||||
}
|
||||
for (int i = 0; i < Rows1; ++i) {
|
||||
for (int j = 1; j < Match; ++j) {
|
||||
result.stripes[i] += rhs.stripes[j] * lhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, bool Packed>
|
||||
inline auto operator*(const Matrix<T, Rows1, Match, Order, eMatrixLayout::ROW_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, eMatrixLayout::COLUMN_MAJOR, Packed>& rhs) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
Matrix<V, Rows1, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed> result;
|
||||
|
||||
for (int j = 0; j < Columns2; ++j) {
|
||||
for (int i = 0; i < Rows1; ++i) {
|
||||
result(i, j) = Dot(lhs.stripes[i], rhs.stripes[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, bool Packed>
|
||||
inline auto operator*(const Matrix<T, Rows1, Match, Order, eMatrixLayout::COLUMN_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, eMatrixLayout::COLUMN_MAJOR, Packed>& rhs) {
|
||||
if constexpr (Rows1 <= 4 && Match <= 4 && Columns2 <= 4) {
|
||||
return impl::SmallProductCC(lhs, rhs, std::make_integer_sequence<int, Columns2>{});
|
||||
}
|
||||
else {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
Matrix<V, Rows1, Columns2, Order, eMatrixLayout::COLUMN_MAJOR, Packed> result;
|
||||
for (int j = 0; j < Columns2; ++j) {
|
||||
result.stripes[j] = lhs.stripes[0] * rhs(0, j);
|
||||
}
|
||||
for (int i = 1; i < Match; ++i) {
|
||||
for (int j = 0; j < Columns2; ++j) {
|
||||
result.stripes[j] += lhs.stripes[i] * rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows1, int Match, int Columns2, eMatrixOrder Order, bool Packed>
|
||||
inline auto operator*(const Matrix<T, Rows1, Match, Order, eMatrixLayout::COLUMN_MAJOR, Packed>& lhs,
|
||||
const Matrix<U, Match, Columns2, Order, eMatrixLayout::ROW_MAJOR, Packed>& rhs) {
|
||||
// CC algorithm is completely fine for COL_MAJOR x ROW_MAJOR.
|
||||
// See that rhs is only indexed per-element, so its layout does not matter.
|
||||
if constexpr (Rows1 <= 4 && Match <= 4 && Columns2 <= 4) {
|
||||
return impl::SmallProductCC(lhs, rhs, std::make_integer_sequence<int, Columns2>{});
|
||||
}
|
||||
else {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
Matrix<V, Rows1, Columns2, Order, eMatrixLayout::COLUMN_MAJOR, Packed> result;
|
||||
for (int j = 0; j < Columns2; ++j) {
|
||||
result.stripes[j] = lhs.stripes[0] * rhs(0, j);
|
||||
}
|
||||
for (int i = 1; i < Match; ++i) {
|
||||
for (int j = 0; j < Columns2; ++j) {
|
||||
result.stripes[j] += lhs.stripes[i] * rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Assign-multiply
|
||||
template <class T1, class T2, int Dim, eMatrixOrder Order, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed>
|
||||
inline Matrix<T1, Dim, Dim, Order, Layout1, Packed>& operator*=(Matrix<T1, Dim, Dim, Order, Layout1, Packed>& lhs, const Matrix<T2, Dim, Dim, Order, Layout2, Packed>& rhs) {
|
||||
lhs = lhs * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Matrix-matrix addition & subtraction
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace impl {
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout SameLayout, bool Packed, int... StripeIndices>
|
||||
inline auto SmallAdd(const Matrix<T, Rows, Columns, Order, SameLayout, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, SameLayout, Packed>& rhs,
|
||||
std::integer_sequence<int, StripeIndices...>) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
using ResultT = Matrix<V, Rows, Columns, Order, SameLayout, Packed>;
|
||||
return ResultT{ ResultT::FromStripes, (lhs.stripes[StripeIndices] + rhs.stripes[StripeIndices])... };
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout SameLayout, bool Packed, int... StripeIndices>
|
||||
inline auto SmallSub(const Matrix<T, Rows, Columns, Order, SameLayout, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, SameLayout, Packed>& rhs,
|
||||
std::integer_sequence<int, StripeIndices...>) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
using ResultT = Matrix<V, Rows, Columns, Order, SameLayout, Packed>;
|
||||
return ResultT{ ResultT::FromStripes, (lhs.stripes[StripeIndices] - rhs.stripes[StripeIndices])... };
|
||||
}
|
||||
} // namespace impl
|
||||
|
||||
// Same layout
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout SameLayout, bool Packed>
|
||||
inline auto operator+(const Matrix<T, Rows, Columns, Order, SameLayout, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, SameLayout, Packed>& rhs) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
|
||||
if constexpr (Rows * Columns == 4) {
|
||||
Matrix<V, Rows, Columns, Order, SameLayout, Packed> result;
|
||||
for (int i = 0; i < result.RowCount(); ++i) {
|
||||
for (int j = 0; j < result.ColumnCount(); ++j) {
|
||||
result(i, j) = lhs(i, j) + rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if constexpr (Rows <= 4 && Columns <= 4) {
|
||||
return impl::SmallAdd(lhs, rhs, std::make_integer_sequence<int, std::decay_t<decltype(lhs)>::StripeCount>{});
|
||||
}
|
||||
else {
|
||||
Matrix<V, Rows, Columns, Order, SameLayout, Packed> result;
|
||||
for (int i = 0; i < result.StripeCount; ++i) {
|
||||
result.stripes[i] = lhs.stripes[i] + rhs.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout SameLayout, bool Packed>
|
||||
inline auto operator-(const Matrix<T, Rows, Columns, Order, SameLayout, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, SameLayout, Packed>& rhs) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
|
||||
if constexpr (Rows * Columns == 4) {
|
||||
Matrix<V, Rows, Columns, Order, SameLayout, Packed> result;
|
||||
for (int i = 0; i < result.RowCount(); ++i) {
|
||||
for (int j = 0; j < result.ColumnCount(); ++j) {
|
||||
result(i, j) = lhs(i, j) - rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if constexpr (Rows <= 4 && Columns <= 4) {
|
||||
return impl::SmallSub(lhs, rhs, std::make_integer_sequence<int, std::decay_t<decltype(lhs)>::StripeCount>{});
|
||||
}
|
||||
else {
|
||||
Matrix<V, Rows, Columns, Order, SameLayout, Packed> result;
|
||||
for (int i = 0; i < result.StripeCount; ++i) {
|
||||
result.stripes[i] = lhs.stripes[i] - rhs.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add & sub opposite layout
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed, class = typename std::enable_if<Layout1 != Layout2>::type>
|
||||
inline auto operator+(const Matrix<T, Rows, Columns, Order, Layout1, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, Layout2, Packed>& rhs) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
Matrix<V, Rows, Columns, Order, Layout1, Packed> result;
|
||||
for (int i = 0; i < result.RowCount(); ++i) {
|
||||
for (int j = 0; j < result.ColumnCount(); ++j) {
|
||||
result(i, j) = lhs(i, j) + rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed, class = typename std::enable_if<Layout1 != Layout2>::type>
|
||||
inline auto operator-(const Matrix<T, Rows, Columns, Order, Layout1, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, Layout2, Packed>& rhs) {
|
||||
using V = traits::MatMulElemT<T, U>;
|
||||
Matrix<V, Rows, Columns, Order, Layout1, Packed> result;
|
||||
for (int i = 0; i < result.RowCount(); ++i) {
|
||||
for (int j = 0; j < result.ColumnCount(); ++j) {
|
||||
result(i, j) = lhs(i, j) - rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary> Performs matrix addition and stores result in this. </summary>
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed>
|
||||
inline Matrix<U, Rows, Columns, Order, Layout1, Packed>& operator+=(
|
||||
Matrix<T, Rows, Columns, Order, Layout1, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, Layout2, Packed>& rhs) {
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/// <summary> Performs matrix subtraction and stores result in this. </summary>
|
||||
template <class T, class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed>
|
||||
inline Matrix<U, Rows, Columns, Order, Layout1, Packed>& operator-=(
|
||||
Matrix<T, Rows, Columns, Order, Layout1, Packed>& lhs,
|
||||
const Matrix<U, Rows, Columns, Order, Layout2, Packed>& rhs) {
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Matrix-Scalar arithmetic
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Scalar multiplication
|
||||
/// <summary> Multiplies all elements of the matrix by scalar. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
inline Matrix<T, Rows, Columns, Order, Layout, Packed>& operator*=(Matrix<T, Rows, Columns, Order, Layout, Packed>& mat, U s) {
|
||||
for (auto& stripe : mat.stripes) {
|
||||
stripe *= s;
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
/// <summary> Divides all elements of the matrix by scalar. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
inline Matrix<T, Rows, Columns, Order, Layout, Packed>& operator/=(Matrix<T, Rows, Columns, Order, Layout, Packed>& mat, U s) {
|
||||
mat *= U(1) / s;
|
||||
return mat;
|
||||
}
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> operator*(const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat, U s) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> copy(mat);
|
||||
copy *= s;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> operator/(const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat, U s) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> copy(mat);
|
||||
copy /= s;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> operator*(U s, const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat) {
|
||||
return mat * s;
|
||||
}
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> operator/(U s, const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> result;
|
||||
for (int i = 0; i < Matrix<T, Rows, Columns, Order, Layout, Packed>::StripeCount; ++i) {
|
||||
result.stripes[i] = T(s) / mat.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Elementwise multiply and divide
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T, class T2, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto MulElementwise(const Matrix<T, Rows, Columns, Order, Layout, Packed>& lhs, const Matrix<T2, Rows, Columns, Order, Layout, Packed>& rhs) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> result;
|
||||
for (int i = 0; i < result.StripeCount; ++i) {
|
||||
result.stripes[i] = lhs.stripes[i] * rhs.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class T2, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto MulElementwise(const Matrix<T, Rows, Columns, Order, Layout, Packed>& lhs, const Matrix<T2, Rows, Columns, Order, traits::OppositeLayout<Layout>::value, Packed>& rhs) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> result;
|
||||
for (int i = 0; i < Rows; ++i) {
|
||||
for (int j = 0; j < Columns; ++j) {
|
||||
result(i, j) = lhs(i, j) * rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class T2, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DivElementwise(const Matrix<T, Rows, Columns, Order, Layout, Packed>& lhs, const Matrix<T2, Rows, Columns, Order, Layout, Packed>& rhs) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> result;
|
||||
for (int i = 0; i < result.StripeCount; ++i) {
|
||||
result.stripes[i] = lhs.stripes[i] / rhs.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class T2, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto DivElementwise(const Matrix<T, Rows, Columns, Order, Layout, Packed>& lhs, const Matrix<T2, Rows, Columns, Order, traits::OppositeLayout<Layout>::value, Packed>& rhs) {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> result;
|
||||
for (int i = 0; i < Rows; ++i) {
|
||||
for (int j = 0; j < Columns; ++j) {
|
||||
result(i, j) = lhs(i, j) / rhs(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Unary signs
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto operator+(const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat) {
|
||||
return Matrix<T, Rows, Columns, Order, Layout, Packed>(mat);
|
||||
}
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto operator-(const Matrix<T, Rows, Columns, Order, Layout, Packed>& mat) {
|
||||
return Matrix<T, Rows, Columns, Order, Layout, Packed>(mat) * T(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,95 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MatrixImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
namespace impl {
|
||||
template <class MatrixDestT, class MatrixSourceT>
|
||||
struct ReinterpretCompatible : std::false_type {};
|
||||
|
||||
template <class T1,
|
||||
class T2,
|
||||
int Rows,
|
||||
int Columns,
|
||||
eMatrixOrder Order1,
|
||||
eMatrixOrder Order2,
|
||||
eMatrixLayout Layout1,
|
||||
eMatrixLayout Layout2,
|
||||
bool Packed1,
|
||||
bool Packed2>
|
||||
struct ReinterpretCompatible<Matrix<T1, Rows, Columns, Order1, Layout1, Packed1>, Matrix<T2, Rows, Columns, Order2, Layout2, Packed2>> {
|
||||
static constexpr bool value = std::is_convertible_v<T2, T1>;
|
||||
};
|
||||
|
||||
template <class MatrixDestT, class MatrixSourceT>
|
||||
struct RepresentationCompatible : std::false_type {};
|
||||
|
||||
template <class T1,
|
||||
class T2,
|
||||
int Rows,
|
||||
int Columns,
|
||||
eMatrixOrder Order1,
|
||||
eMatrixLayout Layout1,
|
||||
eMatrixLayout Layout2,
|
||||
bool Packed1,
|
||||
bool Packed2>
|
||||
struct RepresentationCompatible<Matrix<T1, Rows, Columns, Order1, Layout1, Packed1>, Matrix<T2, Columns, Rows, traits::OppositeOrder<Order1>::value, Layout2, Packed2>> {
|
||||
static constexpr bool value = std::is_convertible_v<T2, T1>;
|
||||
};
|
||||
|
||||
template <class T1,
|
||||
class T2,
|
||||
int Rows,
|
||||
int Columns,
|
||||
eMatrixOrder Order1,
|
||||
eMatrixLayout Layout1,
|
||||
eMatrixLayout Layout2,
|
||||
bool Packed1,
|
||||
bool Packed2>
|
||||
struct RepresentationCompatible<Matrix<T1, Rows, Columns, Order1, Layout1, Packed1>, Matrix<T2, Rows, Columns, Order1, Layout2, Packed2>> {
|
||||
static constexpr bool value = std::is_convertible_v<T2, T1>;
|
||||
};
|
||||
} // namespace impl
|
||||
|
||||
|
||||
|
||||
/// <summary> Changes the type, order and layout of the matrix, but the elements stay at the same place. </summary>
|
||||
template <class MatrixDestT, class MatrixSourceT>
|
||||
auto matrix_reinterpret_cast(const MatrixSourceT& source) -> std::enable_if_t<impl::ReinterpretCompatible<MatrixDestT, MatrixSourceT>::value, MatrixDestT> {
|
||||
MatrixDestT dest;
|
||||
for (int i = 0; i < source.RowCount(); ++i) {
|
||||
for (int j = 0; j < source.ColumnCount(); ++j) {
|
||||
dest(i, j) = typename traits::MatrixTraits<MatrixDestT>::Type(source(i, j));
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Changes the type, order and layout of the matrix.
|
||||
/// The elements are transposed according to the change in order. </summary>
|
||||
template <class MatrixDestT, class MatrixSourceT>
|
||||
auto matrix_representation_cast(const MatrixSourceT& source) -> std::enable_if_t<impl::RepresentationCompatible<MatrixDestT, MatrixSourceT>::value, MatrixDestT> {
|
||||
MatrixDestT dest;
|
||||
for (int i = 0; i < source.RowCount(); ++i) {
|
||||
for (int j = 0; j < source.ColumnCount(); ++j) {
|
||||
if constexpr (traits::MatrixTraits<MatrixDestT>::Order == traits::MatrixTraits<MatrixSourceT>::Order) {
|
||||
dest(i, j) = typename traits::MatrixTraits<MatrixDestT>::Type(source(i, j));
|
||||
}
|
||||
else {
|
||||
dest(j, i) = typename traits::MatrixTraits<MatrixDestT>::Type(source(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,29 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MatrixImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
template <int Rows, int Columns, class T1, class T2, eMatrixOrder Order1, eMatrixOrder Order2, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed1, bool Packed2>
|
||||
bool operator==(const Matrix<T1, Rows, Columns, Order1, Layout1, Packed1>& lhs, const Matrix<T2, Rows, Columns, Order2, Layout2, Packed2>& rhs) {
|
||||
bool equal = true;
|
||||
for (int i = 0; i < Rows; ++i) {
|
||||
for (int j = 0; j < Columns; ++j) {
|
||||
equal = equal && lhs(i, j) == rhs(i, j);
|
||||
}
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
template <int Rows, int Columns, class T1, class T2, eMatrixOrder Order1, eMatrixOrder Order2, eMatrixLayout Layout1, eMatrixLayout Layout2, bool Packed1, bool Packed2>
|
||||
bool operator!=(const Matrix<T1, Rows, Columns, Order1, Layout1, Packed1>& lhs, const Matrix<T2, Rows, Columns, Order2, Layout2, Packed2>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,317 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MatrixArithmetic.hpp"
|
||||
#include "MatrixImpl.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> Returns the trace (sum of diagonal elements) of the matrix. </summary>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T Trace(const Matrix<T, Dim, Dim, Order, Layout, Packed>& m) {
|
||||
T sum = m(0, 0);
|
||||
for (int i = 1; i < Dim; ++i) {
|
||||
sum += m(i, i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the determinant of a 2x2 matrix. </summary>
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T Determinant(const Matrix<T, 2, 2, Order, Layout, Packed>& m) {
|
||||
return m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1);
|
||||
}
|
||||
|
||||
/// <summary> Returns the determinant of a 3x3matrix. </summary>
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T Determinant(const Matrix<T, 3, 3, Order, Layout, Packed>& m) {
|
||||
using Vec3 = Vector<T, 3, false>;
|
||||
|
||||
Vec3 r0_zyx = m.stripes[0].zyx;
|
||||
Vec3 r1_xzy = m.stripes[1].xzy;
|
||||
Vec3 r1_yxz = m.stripes[1].yxz;
|
||||
Vec3 r2_yxz = m.stripes[2].yxz;
|
||||
Vec3 r2_xzy = m.stripes[2].xzy;
|
||||
|
||||
T det = Dot(r0_zyx, r1_xzy * r2_yxz - r1_yxz * r2_xzy);
|
||||
|
||||
return det;
|
||||
}
|
||||
|
||||
/// <summary> Returns the determinant of a 4x4matrix. </summary>
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T Determinant(const Matrix<T, 4, 4, Order, Layout, Packed>& m) {
|
||||
using Vec3 = Vector<T, 3, false>;
|
||||
using Vec4 = Vector<T, 4, false>;
|
||||
|
||||
Vec4 evenPair = { 1, -1, -1, 1 };
|
||||
Vec4 oddPair = { -1, 1, 1, -1 };
|
||||
|
||||
const Vec4& r0 = m.stripes[0];
|
||||
const Vec4& r1 = m.stripes[1];
|
||||
const Vec4& r2 = m.stripes[2];
|
||||
const Vec4& r3 = m.stripes[3];
|
||||
|
||||
Vec4 r2_zwzw = r2.zwzw;
|
||||
Vec4 r0_yyxx = r0.yyxx;
|
||||
Vec4 r1_wwxy = r1.wwxy;
|
||||
Vec4 r2_xyzz = r2.xyzz;
|
||||
Vec4 r3_wwww = r3.wwww;
|
||||
Vec4 r1_zzxy = r1.zzxy;
|
||||
Vec4 r0_yxyx = r0.yxyx;
|
||||
Vec4 r3_xxyy = r3.xxyy;
|
||||
Vec4 r1_wzwz = r1.wzwz;
|
||||
Vec4 r2_xyww = r2.xyww;
|
||||
Vec4 r3_zzzz = r3.zzzz;
|
||||
|
||||
Vec3 r2_yxz = r2.yxz;
|
||||
Vec3 r3_xzy = r3.xzy;
|
||||
Vec3 r2_xzy = r2.xzy;
|
||||
Vec3 r3_yxz = r3.yxz;
|
||||
Vec3 r2_yxw = r2.yxw;
|
||||
Vec3 r1_zyx = r1.zyx;
|
||||
Vec3 r3_yxw = r3.yxw;
|
||||
Vec3 r2_xwy = r2.xwy;
|
||||
Vec3 r3_xwy = r3.xwy;
|
||||
Vec3 r1_wyx = r1.wyx;
|
||||
T r0_w = r0.w;
|
||||
T r0_z = r0.z;
|
||||
|
||||
T det = Dot(evenPair, r0_yyxx * r1_wzwz * r2_zwzw * r3_xxyy)
|
||||
+ Dot(oddPair, r0_yxyx * r1_wwxy * r2_xyww * r3_zzzz)
|
||||
+ Dot(evenPair, r0_yxyx * r1_zzxy * r2_xyzz * r3_wwww)
|
||||
+ (r0_w * Dot(r1_zyx, r2_yxz * r3_xzy - r2_xzy * r3_yxz))
|
||||
+ (r0_z * Dot(r1_wyx, r2_xwy * r3_yxw - r2_yxw * r3_xwy));
|
||||
|
||||
return det;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the determinant of the matrix. </summary>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T Determinant(const Matrix<T, Dim, Dim, Order, Layout, Packed>& m) {
|
||||
// only works if L's diagonal is 1s
|
||||
int parity;
|
||||
auto [L, U, P] = DecomposeLUP(m, parity);
|
||||
T prod = U(0, 0);
|
||||
for (int i = 1; i < U.RowCount(); ++i) {
|
||||
prod *= U(i, i);
|
||||
}
|
||||
return parity * prod;
|
||||
}
|
||||
|
||||
/// <summary> Transposes the matrix in-place. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
Matrix<T, Columns, Rows, Order, Layout, Packed> Transpose(const Matrix<T, Rows, Columns, Order, Layout, Packed>& m) {
|
||||
Matrix<T, Columns, Rows, Order, Layout, Packed> result;
|
||||
for (int i = 0; i < m.RowCount(); ++i) {
|
||||
for (int j = 0; j < m.ColumnCount(); ++j) {
|
||||
result(j, i) = m(i, j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the inverse of a 2x2 matrix. </summary>
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto Inverse(const Matrix<T, 2, 2, Order, Layout, Packed>& m) {
|
||||
Matrix<T, 2, 2, Order, Layout, Packed> result;
|
||||
|
||||
const auto& r0 = m.stripes[0];
|
||||
const auto& r1 = m.stripes[1];
|
||||
|
||||
result.stripes[0] = { r1.y, -r0.y };
|
||||
result.stripes[1] = { -r1.x, r0.x };
|
||||
|
||||
auto det = T(1) / (r0.x * r1.y - r0.y * r1.x);
|
||||
result *= det;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the inverse of a 3x3 matrix. </summary>
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto Inverse(const Matrix<T, 3, 3, Order, Layout, Packed>& m) {
|
||||
Matrix<T, 3, 3, Order, Layout, Packed> result;
|
||||
|
||||
using Vec3 = Vector<T, 3, false>;
|
||||
|
||||
// This code below uses notation for row-major matrices' stripes.
|
||||
// It, however, "magically" works for column-major layout as well.
|
||||
Vec3 r0_zxy = m.stripes[0].zxy;
|
||||
Vec3 r0_yzx = m.stripes[0].yzx;
|
||||
Vec3 r1_yzx = m.stripes[1].yzx;
|
||||
Vec3 r1_zxy = m.stripes[1].zxy;
|
||||
Vec3 r2_zxy = m.stripes[2].zxy;
|
||||
Vec3 r2_yzx = m.stripes[2].yzx;
|
||||
|
||||
Vec3 c0 = r1_yzx * r2_zxy - r1_zxy * r2_yzx;
|
||||
Vec3 c1 = r0_zxy * r2_yzx - r0_yzx * r2_zxy;
|
||||
Vec3 c2 = r0_yzx * r1_zxy - r0_zxy * r1_yzx;
|
||||
|
||||
Vec3 r0_zyx = m.stripes[0].zyx;
|
||||
Vec3 r1_xzy = m.stripes[1].xzy;
|
||||
Vec3 r1_yxz = m.stripes[1].yxz;
|
||||
Vec3 r2_yxz = m.stripes[2].yxz;
|
||||
Vec3 r2_xzy = m.stripes[2].xzy;
|
||||
|
||||
result.stripes[0] = { c0[0], c1[0], c2[0] };
|
||||
result.stripes[1] = { c0[1], c1[1], c2[1] };
|
||||
result.stripes[2] = { c0[2], c1[2], c2[2] };
|
||||
|
||||
T det = T(1) / Dot(r0_zyx, r1_xzy * r2_yxz - r1_yxz * r2_xzy);
|
||||
|
||||
result.stripes[0] *= det;
|
||||
result.stripes[1] *= det;
|
||||
result.stripes[2] *= det;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the inverse of a 4x4 matrix. </summary>
|
||||
template <class T, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
auto Inverse(const Matrix<T, 4, 4, Order, Layout, Packed>& m) {
|
||||
Matrix<T, 4, 4, Order, Layout, Packed> result;
|
||||
|
||||
using Vec3 = Vector<T, 3, false>;
|
||||
using Vec4 = Vector<T, 4, false>;
|
||||
|
||||
Vec4 even = { 1, -1, 1, -1 };
|
||||
Vec4 odd = { -1, 1, -1, 1 };
|
||||
Vec4 evenPair = { 1, -1, -1, 1 };
|
||||
Vec4 oddPair = { -1, 1, 1, -1 };
|
||||
|
||||
const Vec4& r0 = m.stripes[0];
|
||||
const Vec4& r1 = m.stripes[1];
|
||||
const Vec4& r2 = m.stripes[2];
|
||||
const Vec4& r3 = m.stripes[3];
|
||||
|
||||
Vec4 r0_wwwz = r0.wwwz;
|
||||
Vec4 r0_yxxx = r0.yxxx;
|
||||
Vec4 r0_zzyy = r0.zzyy;
|
||||
Vec4 r1_wwwz = r1.wwwz;
|
||||
Vec4 r1_yxxx = r1.yxxx;
|
||||
Vec4 r1_zzyy = r1.zzyy;
|
||||
Vec4 r2_wwwz = r2.wwwz;
|
||||
Vec4 r2_yxxx = r2.yxxx;
|
||||
Vec4 r2_zzyy = r2.zzyy;
|
||||
Vec4 r3_wwwz = r3.wwwz;
|
||||
Vec4 r3_yxxx = r3.yxxx;
|
||||
Vec4 r3_zzyy = r3.zzyy;
|
||||
|
||||
Vec4 r0_wwwz_r1_yxxx = r0_wwwz * r1_yxxx;
|
||||
Vec4 r0_wwwz_r1_zzyy = r0_wwwz * r1_zzyy;
|
||||
Vec4 r0_yxxx_r1_wwwz = r0_yxxx * r1_wwwz;
|
||||
Vec4 r0_yxxx_r1_zzyy = r0_yxxx * r1_zzyy;
|
||||
Vec4 r0_zzyy_r1_wwwz = r0_zzyy * r1_wwwz;
|
||||
Vec4 r0_zzyy_r1_yxxx = r0_zzyy * r1_yxxx;
|
||||
Vec4 r2_wwwz_r3_yxxx = r2_wwwz * r3_yxxx;
|
||||
Vec4 r2_wwwz_r3_zzyy = r2_wwwz * r3_zzyy;
|
||||
Vec4 r2_yxxx_r3_wwwz = r2_yxxx * r3_wwwz;
|
||||
Vec4 r2_yxxx_r3_zzyy = r2_yxxx * r3_zzyy;
|
||||
Vec4 r2_zzyy_r3_wwwz = r2_zzyy * r3_wwwz;
|
||||
Vec4 r2_zzyy_r3_yxxx = r2_zzyy * r3_yxxx;
|
||||
|
||||
Vec4 c0 = odd * (r1_wwwz * r2_zzyy_r3_yxxx - r1_zzyy * r2_wwwz_r3_yxxx - r1_wwwz * r2_yxxx_r3_zzyy + r1_yxxx * r2_wwwz_r3_zzyy + r1_zzyy * r2_yxxx_r3_wwwz - r1_yxxx * r2_zzyy_r3_wwwz);
|
||||
Vec4 c1 = even * (r0_wwwz * r2_zzyy_r3_yxxx - r0_zzyy * r2_wwwz_r3_yxxx - r0_wwwz * r2_yxxx_r3_zzyy + r0_yxxx * r2_wwwz_r3_zzyy + r0_zzyy * r2_yxxx_r3_wwwz - r0_yxxx * r2_zzyy_r3_wwwz);
|
||||
Vec4 c2 = odd * (r0_wwwz_r1_zzyy * r3_yxxx - r0_zzyy_r1_wwwz * r3_yxxx - r0_wwwz_r1_yxxx * r3_zzyy + r0_yxxx_r1_wwwz * r3_zzyy + r0_zzyy_r1_yxxx * r3_wwwz - r0_yxxx_r1_zzyy * r3_wwwz);
|
||||
Vec4 c3 = even * (r0_wwwz_r1_zzyy * r2_yxxx - r0_zzyy_r1_wwwz * r2_yxxx - r0_wwwz_r1_yxxx * r2_zzyy + r0_yxxx_r1_wwwz * r2_zzyy + r0_zzyy_r1_yxxx * r2_wwwz - r0_yxxx_r1_zzyy * r2_wwwz);
|
||||
|
||||
result.stripes[0] = { c0[0], c1[0], c2[0], c3[0] };
|
||||
result.stripes[1] = { c0[1], c1[1], c2[1], c3[1] };
|
||||
result.stripes[2] = { c0[2], c1[2], c2[2], c3[2] };
|
||||
result.stripes[3] = { c0[3], c1[3], c2[3], c3[3] };
|
||||
|
||||
Vec4 r2_zwzw = r2.zwzw;
|
||||
Vec4 r0_yyxx = r0.yyxx;
|
||||
Vec4 r1_wwxy = r1.wwxy;
|
||||
Vec4 r2_xyzz = r2.xyzz;
|
||||
Vec4 r3_wwww = r3.wwww;
|
||||
Vec4 r1_zzxy = r1.zzxy;
|
||||
Vec4 r0_yxyx = r0.yxyx;
|
||||
Vec4 r3_xxyy = r3.xxyy;
|
||||
Vec4 r1_wzwz = r1.wzwz;
|
||||
Vec4 r2_xyww = r2.xyww;
|
||||
Vec4 r3_zzzz = r3.zzzz;
|
||||
|
||||
Vec3 r2_yxz = r2.yxz;
|
||||
Vec3 r3_xzy = r3.xzy;
|
||||
Vec3 r2_xzy = r2.xzy;
|
||||
Vec3 r3_yxz = r3.yxz;
|
||||
Vec3 r2_yxw = r2.yxw;
|
||||
Vec3 r1_zyx = r1.zyx;
|
||||
Vec3 r3_yxw = r3.yxw;
|
||||
Vec3 r2_xwy = r2.xwy;
|
||||
Vec3 r3_xwy = r3.xwy;
|
||||
Vec3 r1_wyx = r1.wyx;
|
||||
T r0_w = r0.w;
|
||||
T r0_z = r0.z;
|
||||
|
||||
T det = Dot(evenPair, r0_yyxx * r1_wzwz * r2_zwzw * r3_xxyy)
|
||||
+ Dot(oddPair, r0_yxyx * r1_wwxy * r2_xyww * r3_zzzz)
|
||||
+ Dot(evenPair, r0_yxyx * r1_zzxy * r2_xyzz * r3_wwww)
|
||||
+ (r0_w * Dot(r1_zyx, r2_yxz * r3_xzy - r2_xzy * r3_yxz))
|
||||
+ (r0_z * Dot(r1_wyx, r2_xwy * r3_yxw - r2_yxw * r3_xwy));
|
||||
|
||||
T invDet = 1 / det;
|
||||
|
||||
result.stripes[0] *= invDet;
|
||||
result.stripes[1] *= invDet;
|
||||
result.stripes[2] *= invDet;
|
||||
result.stripes[3] *= invDet;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary> Returns the inverse of the matrix. </summary>
|
||||
template <class T, int Dim, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
Matrix<T, Dim, Dim, Order, Layout, Packed> Inverse(const Matrix<T, Dim, Dim, Order, Layout, Packed>& m) {
|
||||
Matrix<T, Dim, Dim, Order, Layout, Packed> ret;
|
||||
|
||||
auto LUP = DecomposeLUP(m);
|
||||
|
||||
Vector<T, Dim, Packed> b(0);
|
||||
Vector<T, Dim, Packed> x;
|
||||
for (int col = 0; col < Dim; ++col) {
|
||||
b(std::max(0, col - 1)) = 0;
|
||||
b(col) = 1;
|
||||
x = LUP.Solve(b);
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
ret(i, col) = x(i);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Calculates the square of the Frobenius norm of the matrix. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T NormSquared(const Matrix<T, Rows, Columns, Order, Layout, Packed>& m) {
|
||||
T sum = T(0);
|
||||
for (auto& stripe : m.stripes) {
|
||||
sum += LengthSquared(stripe);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary> Calculates the Frobenius norm of the matrix. </summary>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
T Norm(const Matrix<T, Rows, Columns, Order, Layout, Packed>& m) {
|
||||
return sqrt(NormSquared(m));
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,340 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../Common/Definitions.hpp"
|
||||
#include "../Common/Traits.hpp"
|
||||
#include "../Vector.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Matrix base class only allocating the memory
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order = eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout Layout = eMatrixLayout::ROW_MAJOR, bool Packed = false>
|
||||
class MatrixData {
|
||||
public:
|
||||
/// <summary> Returns the number of columns of the matrix. </summary>
|
||||
constexpr int ColumnCount() const {
|
||||
return Columns;
|
||||
}
|
||||
/// <summary> Returns the number of rows of the matrix. </summary>
|
||||
constexpr int RowCount() const {
|
||||
return Rows;
|
||||
}
|
||||
/// <summary> Returns the number of columns of the matrix. </summary>
|
||||
constexpr int Width() const {
|
||||
return Columns;
|
||||
}
|
||||
/// <summary> Returns the number of rows of the matrix. </summary>
|
||||
constexpr int Height() const {
|
||||
return Rows;
|
||||
}
|
||||
// Rows equal height, Columns equal width, row-major has column-sized stripes
|
||||
static constexpr int StripeDim = Layout == eMatrixLayout::ROW_MAJOR ? Columns : Rows;
|
||||
static constexpr int StripeCount = Layout == eMatrixLayout::ROW_MAJOR ? Rows : Columns;
|
||||
|
||||
using StripeVecT = Vector<T, StripeDim, Packed>;
|
||||
std::array<StripeVecT, StripeCount> stripes;
|
||||
|
||||
protected:
|
||||
// Get element
|
||||
inline T& GetElement(int row, int col) {
|
||||
assert(row < RowCount());
|
||||
assert(col < ColumnCount());
|
||||
if constexpr (Layout == eMatrixLayout::ROW_MAJOR) {
|
||||
return stripes[row][col];
|
||||
}
|
||||
else {
|
||||
return stripes[col][row];
|
||||
}
|
||||
}
|
||||
inline T GetElement(int row, int col) const {
|
||||
assert(row < RowCount());
|
||||
assert(col < ColumnCount());
|
||||
if constexpr (Layout == eMatrixLayout::ROW_MAJOR) {
|
||||
return stripes[row][col];
|
||||
}
|
||||
else {
|
||||
return stripes[col][row];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Submatrix helper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class MatrixT, int SRows, int SColumns>
|
||||
class SubmatrixHelper {
|
||||
friend MatrixT;
|
||||
using Props = traits::MatrixTraits<MatrixT>;
|
||||
template <class, int, int>
|
||||
friend class SubmatrixHelper;
|
||||
static constexpr int VecDim = std::max(SRows, SColumns);
|
||||
static constexpr bool VectorAssignable = std::min(SRows, SColumns) == 1;
|
||||
|
||||
protected:
|
||||
SubmatrixHelper(MatrixT& mat, int row, int col) : mat(mat), row(row), col(col) {}
|
||||
|
||||
public:
|
||||
SubmatrixHelper(const SubmatrixHelper& rhs) = delete;
|
||||
SubmatrixHelper(SubmatrixHelper&& rhs) : mat(rhs.mat), row(rhs.row), col(rhs.col) {}
|
||||
|
||||
|
||||
template <class U, eMatrixOrder UOrder, eMatrixLayout ULayout, bool UPacked>
|
||||
operator Matrix<U, SRows, SColumns, UOrder, ULayout, UPacked>() const {
|
||||
Matrix<U, SRows, SColumns, UOrder, ULayout, UPacked> ret;
|
||||
for (int i = 0; i < SRows; ++i) {
|
||||
for (int j = 0; j < SColumns; ++j) {
|
||||
ret(i, j) = (*this)(i, j);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class U, bool Packed2, class = typename std::enable_if<VectorAssignable, U>::type>
|
||||
operator Vector<U, VecDim, Packed2>() const {
|
||||
Vector<U, std::max(SRows, SColumns), Packed2> v;
|
||||
int k = 0;
|
||||
for (int i = 0; i < SRows; ++i) {
|
||||
for (int j = 0; j < SColumns; ++j) {
|
||||
v(k) = (*this)(i, j);
|
||||
++k;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
template <class U, eMatrixOrder UOrder, eMatrixLayout ULayout, bool UPacked>
|
||||
SubmatrixHelper& operator=(const Matrix<U, SRows, SColumns, UOrder, ULayout, UPacked>& rhs) {
|
||||
static_assert(!std::is_const<MatrixT>::value, "Cannot assign to submatrix of const matrix.");
|
||||
|
||||
// If aliasing happens, the same matrix is copied to itself with no side-effects.
|
||||
for (int i = 0; i < SRows; ++i) {
|
||||
for (int j = 0; j < SColumns; ++j) {
|
||||
mat(row + i, col + j) = rhs(i, j);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// From vector if applicable (for 1*N and N*1 submatrices)
|
||||
template <class U, bool Packed, class = typename std::enable_if<VectorAssignable, U>::type>
|
||||
SubmatrixHelper& operator=(const Vector<U, VecDim, Packed>& v) {
|
||||
static_assert(!std::is_const<MatrixT>::value, "Cannot assign to submatrix of const matrix.");
|
||||
|
||||
int k = 0;
|
||||
for (int i = 0; i < SRows; ++i) {
|
||||
for (int j = 0; j < SColumns; ++j) {
|
||||
mat(row + i, col + j) = v(k);
|
||||
++k;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <class MatrixU>
|
||||
SubmatrixHelper& operator=(const SubmatrixHelper<MatrixU, SRows, SColumns>& rhs) {
|
||||
static_assert(!std::is_const<MatrixT>::value, "Cannot assign to submatrix of const matrix.");
|
||||
|
||||
// If *this and rhs reference the same matrix, aliasing must be resolved.
|
||||
if ((void*)&mat == (void*)&rhs.mat) {
|
||||
Matrix<typename traits::MatrixTraits<MatrixU>::Type,
|
||||
SRows,
|
||||
SColumns,
|
||||
traits::MatrixTraits<MatrixU>::Order,
|
||||
traits::MatrixTraits<MatrixU>::Layout,
|
||||
traits::MatrixTraits<MatrixU>::Packed>
|
||||
tmpmat;
|
||||
tmpmat = rhs;
|
||||
operator=(tmpmat);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < SRows; ++i) {
|
||||
for (int j = 0; j < SColumns; ++j) {
|
||||
mat(row + i, col + j) = rhs(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
SubmatrixHelper& operator=(const SubmatrixHelper& rhs) {
|
||||
static_assert(!std::is_const<MatrixT>::value, "Cannot assign to submatrix of const matrix.");
|
||||
return operator=<MatrixT>(rhs);
|
||||
}
|
||||
|
||||
typename Props::Type& operator()(int row, int col) {
|
||||
return mat(this->row + row, this->col + col);
|
||||
}
|
||||
|
||||
typename Props::Type operator()(int row, int col) const {
|
||||
return mat(this->row + row, this->col + col);
|
||||
}
|
||||
|
||||
private:
|
||||
MatrixT& mat;
|
||||
int row = -1, col = -1;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Matrix class providing the common interface for all matrices
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order = eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout Layout = eMatrixLayout::ROW_MAJOR, bool Packed = false>
|
||||
class Matrix : public MatrixData<T, Rows, Columns, Order, Layout, Packed> {
|
||||
static_assert(Columns >= 1 && Rows >= 1, "Dimensions must be positive integers.");
|
||||
|
||||
static constexpr int VecDim = std::max(Rows, Columns);
|
||||
static constexpr bool VectorAssignable = std::min(Rows, Columns) == 1;
|
||||
|
||||
protected:
|
||||
using MatrixData<T, Rows, Columns, Order, Layout, Packed>::GetElement;
|
||||
|
||||
template <class T2, int Rows2, int Columns2, eMatrixOrder Order2, eMatrixLayout Layout2, bool Packed2>
|
||||
friend class Matrix;
|
||||
|
||||
public:
|
||||
using MatrixData<T, Rows, Columns, Order, Layout, Packed>::RowCount;
|
||||
using MatrixData<T, Rows, Columns, Order, Layout, Packed>::ColumnCount;
|
||||
using typename MatrixData<T, Rows, Columns, Order, Layout, Packed>::StripeVecT;
|
||||
using MatrixData<T, Rows, Columns, Order, Layout, Packed>::stripes;
|
||||
using MatrixData<T, Rows, Columns, Order, Layout, Packed>::StripeCount;
|
||||
struct FromStripes_ {};
|
||||
static constexpr FromStripes_ FromStripes = {};
|
||||
|
||||
//--------------------------------------------
|
||||
// Constructors
|
||||
//--------------------------------------------
|
||||
|
||||
Matrix() = default;
|
||||
|
||||
// From same multiplication order
|
||||
template <class T2, eMatrixLayout Layout2, bool Packed2>
|
||||
Matrix(const Matrix<T2, Rows, Columns, Order, Layout2, Packed2>& rhs) {
|
||||
for (int i = 0; i < RowCount(); ++i) {
|
||||
for (int j = 0; j < ColumnCount(); ++j) {
|
||||
(*this)(i, j) = rhs(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class H, class... Args,
|
||||
typename std::enable_if<traits::All<traits::IsScalar, H, Args...>::value, int>::type = 0,
|
||||
typename std::enable_if<1 + sizeof...(Args) == Rows * Columns, int>::type = 0>
|
||||
Matrix(H h, Args... args) {
|
||||
Assign<0, 0>(h, args...);
|
||||
}
|
||||
|
||||
// From vector if applicable (for 1*N and N*1 matrices)
|
||||
template <class T2, bool Packed2, class = typename std::enable_if<VectorAssignable, T2>::type>
|
||||
Matrix(const Vector<T2, VecDim, Packed2>& v) {
|
||||
for (int i = 0; i < v.Dimension(); ++i) {
|
||||
(*this)(i) = v(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Used by internal methods. </summary>
|
||||
template <class... Stripes>
|
||||
Matrix(FromStripes_, Stripes... stripes)
|
||||
: MatrixData<T, Rows, Columns, Order, Layout, Packed>{ std::forward<Stripes>(stripes)... } {}
|
||||
|
||||
//--------------------------------------------
|
||||
// Accessors
|
||||
//--------------------------------------------
|
||||
|
||||
// General matrix indexing
|
||||
inline T& operator()(int row, int col) {
|
||||
return GetElement(row, col);
|
||||
}
|
||||
inline T operator()(int row, int col) const {
|
||||
return GetElement(row, col);
|
||||
}
|
||||
|
||||
// Column and row vector simple indexing
|
||||
template <class Q = T>
|
||||
inline typename std::enable_if<(Columns == 1 && Rows > 1) || (Columns > 1 && Rows == 1), Q>::type& operator()(int idx) {
|
||||
return GetElement(Rows == 1 ? 0 : idx, Columns == 1 ? 0 : idx);
|
||||
}
|
||||
template <class Q = T>
|
||||
inline typename std::enable_if<(Columns == 1 && Rows > 1) || (Columns > 1 && Rows == 1), Q>::type operator()(int idx) const {
|
||||
return GetElement(Rows == 1 ? 0 : idx, Columns == 1 ? 0 : idx);
|
||||
}
|
||||
|
||||
// Submatrices
|
||||
/// <summary> DEPRECATED: I plan to replace it with a nicer MatrixView like std::string_view. </summary>
|
||||
template <int Subrows, int Subcolumns>
|
||||
mathter::SubmatrixHelper<Matrix, Subrows, Subcolumns> Submatrix(int rowIdx, int colIdx) {
|
||||
assert(Subrows + rowIdx <= Rows);
|
||||
assert(Subcolumns + colIdx <= Columns);
|
||||
|
||||
return SubmatrixHelper<Matrix, Subrows, Subcolumns>(*this, rowIdx, colIdx);
|
||||
}
|
||||
|
||||
template <int Subrows, int Subcolumns>
|
||||
/// <summary> DEPRECATED: I plan to replace it with a nicer MatrixView like std::string_view. </summary>
|
||||
mathter::SubmatrixHelper<const Matrix, Subrows, Subcolumns> Submatrix(int rowIdx, int colIdx) const {
|
||||
assert(Subrows + rowIdx <= Rows);
|
||||
assert(Subcolumns + colIdx <= Columns);
|
||||
|
||||
return SubmatrixHelper<const Matrix, Subrows, Subcolumns>(*this, rowIdx, colIdx);
|
||||
}
|
||||
|
||||
/// <summary> Return the submatrix corresponding to the specified column. </summary>
|
||||
auto Column(int colIdx) {
|
||||
return Submatrix<Rows, 1>(0, colIdx);
|
||||
}
|
||||
/// <summary> Return the submatrix corresponding to the specified row. </summary>
|
||||
auto Row(int rowIdx) {
|
||||
return Submatrix<1, Columns>(rowIdx, 0);
|
||||
}
|
||||
/// <summary> Return the submatrix corresponding to the specified column. </summary>
|
||||
auto Column(int colIdx) const {
|
||||
return Submatrix<Rows, 1>(0, colIdx);
|
||||
}
|
||||
/// <summary> Return the submatrix corresponding to the specified row. </summary>
|
||||
auto Row(int rowIdx) const {
|
||||
return Submatrix<1, Columns>(rowIdx, 0);
|
||||
}
|
||||
|
||||
// Conversion to vector if applicable
|
||||
template <class T2, bool Packed2, class = typename std::enable_if<VectorAssignable, T2>::type>
|
||||
operator Vector<T2, VecDim, Packed2>() const {
|
||||
Vector<T2, std::max(Rows, Columns), Packed2> v;
|
||||
int k = 0;
|
||||
for (int i = 0; i < Rows; ++i) {
|
||||
for (int j = 0; j < Columns; ++j) {
|
||||
v(k) = (*this)(i, j);
|
||||
++k;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
protected:
|
||||
template <int i, int j, class Head, class... Args>
|
||||
void Assign(Head head, Args... args) {
|
||||
(*this)(i, j) = (T)head;
|
||||
Assign<((j != Columns - 1) ? i : (i + 1)), ((j + 1) % Columns)>(args...);
|
||||
}
|
||||
|
||||
template <int, int>
|
||||
void Assign() {}
|
||||
}; // namespace mathter
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,114 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MatrixImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Matrix-vector arithmetic
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// v*M
|
||||
template <class Vt, class Mt, int Vd, int Mcol, bool Packed>
|
||||
auto operator*(const Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd, Mcol, eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout::ROW_MAJOR, Packed>& mat) {
|
||||
using Rt = traits::MatMulElemT<Vt, Mt>;
|
||||
Vector<Rt, Mcol, Packed> result;
|
||||
|
||||
result = vec(0) * mat.stripes[0];
|
||||
for (int i = 1; i < Vd; ++i) {
|
||||
result += vec(i) * mat.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Vt, class Mt, int Vd, int Mcol, bool Packed>
|
||||
auto operator*(const Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd, Mcol, eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout::COLUMN_MAJOR, Packed>& mat) {
|
||||
using Rt = traits::MatMulElemT<Vt, Mt>;
|
||||
Vector<Rt, Mcol, Packed> result;
|
||||
|
||||
for (int i = 0; i < Mcol; ++i) {
|
||||
result(i) = Dot(vec, mat.stripes[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (v|1)*M
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Mlayout, bool Packed>
|
||||
auto operator*(const Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd + 1, Vd, eMatrixOrder::FOLLOW_VECTOR, Mlayout, Packed>& mat) {
|
||||
return (vec | Vt(1)) * mat;
|
||||
}
|
||||
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Mlayout, bool Packed>
|
||||
auto operator*(const Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd + 1, Vd + 1, eMatrixOrder::FOLLOW_VECTOR, Mlayout, Packed>& mat) {
|
||||
using Rt = traits::MatMulElemT<Vt, Mt>;
|
||||
auto res = (vec | Vt(1)) * mat;
|
||||
res /= res(res.Dimension() - 1);
|
||||
return Vector<Rt, Vd, Packed>(res);
|
||||
}
|
||||
|
||||
// M*v
|
||||
template <class Vt, class Mt, int Vd, int Mrow, bool Packed>
|
||||
auto operator*(const Matrix<Mt, Mrow, Vd, eMatrixOrder::PRECEDE_VECTOR, eMatrixLayout::ROW_MAJOR, Packed>& mat, const Vector<Vt, Vd, Packed>& vec) {
|
||||
using Rt = traits::MatMulElemT<Vt, Mt>;
|
||||
Vector<Rt, Mrow, Packed> result;
|
||||
|
||||
for (int i = 0; i < Mrow; ++i) {
|
||||
result(i) = Dot(vec, mat.stripes[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Vt, class Mt, int Vd, int Mrow, bool Packed>
|
||||
auto operator*(const Matrix<Mt, Mrow, Vd, eMatrixOrder::PRECEDE_VECTOR, eMatrixLayout::COLUMN_MAJOR, Packed>& mat, const Vector<Vt, Vd, Packed>& vec) {
|
||||
using Rt = traits::MatMulElemT<Vt, Mt>;
|
||||
Vector<Rt, Mrow, Packed> result;
|
||||
|
||||
result = vec(0) * mat.stripes[0];
|
||||
for (int i = 1; i < Vd; ++i) {
|
||||
result += vec(i) * mat.stripes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// M*(v|1)
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Mlayout, bool Packed>
|
||||
auto operator*(const Matrix<Mt, Vd, Vd + 1, eMatrixOrder::PRECEDE_VECTOR, Mlayout, Packed>& mat, const Vector<Vt, Vd, Packed>& vec) {
|
||||
return mat * (vec | Vt(1));
|
||||
}
|
||||
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Mlayout, bool Packed>
|
||||
auto operator*(const Matrix<Mt, Vd + 1, Vd + 1, eMatrixOrder::PRECEDE_VECTOR, Mlayout, Packed>& mat, const Vector<Vt, Vd, Packed>& vec) {
|
||||
using Rt = traits::MatMulElemT<Vt, Mt>;
|
||||
auto res = mat * (vec | Vt(1));
|
||||
res /= res(res.Dimension() - 1);
|
||||
return (Vector<Rt, Vd, Packed>)res;
|
||||
}
|
||||
|
||||
// v*=M
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Layout, bool Packed>
|
||||
Vector<Vt, Vd, Packed>& operator*=(Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd, Vd, eMatrixOrder::FOLLOW_VECTOR, Layout, Packed>& mat) {
|
||||
vec = vec * mat;
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Layout, bool Packed>
|
||||
Vector<Vt, Vd, Packed>& operator*=(Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd + 1, Vd, eMatrixOrder::FOLLOW_VECTOR, Layout, Packed>& mat) {
|
||||
vec = vec * mat;
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <class Vt, class Mt, int Vd, eMatrixLayout Layout, bool Packed>
|
||||
Vector<Vt, Vd, Packed>& operator*=(Vector<Vt, Vd, Packed>& vec, const Matrix<Mt, Vd + 1, Vd + 1, eMatrixOrder::FOLLOW_VECTOR, Layout, Packed>& mat) {
|
||||
vec = vec * mat;
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,13 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Quaternion/QuaternionArithmetic.hpp"
|
||||
#include "Quaternion/QuaternionCompare.hpp"
|
||||
#include "Quaternion/QuaternionFunction.hpp"
|
||||
#include "Quaternion/QuaternionImpl.hpp"
|
||||
#include "Quaternion/QuaternionLiterals.hpp"
|
||||
#include "Quaternion/QuaternionVectorArithmetic.hpp"
|
|
@ -0,0 +1,159 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "QuaternionImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Product(const Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
if constexpr (IsBatched<T, 4, Packed>()) {
|
||||
using Vec4 = Vector<T, 4, Packed>;
|
||||
const auto lhsv = lhs.vec;
|
||||
const auto rhsv = rhs.vec;
|
||||
const Vec4 s1 = { -1, 1, -1, 1 };
|
||||
const Vec4 s2 = { -1, 1, 1, -1 };
|
||||
const Vec4 s3 = { -1, -1, 1, 1 };
|
||||
|
||||
// [ 3, 2, 1, 0 ]
|
||||
// [ 0, 3, 2, 1 ]
|
||||
const Vec4 t0 = lhsv.xxxx;
|
||||
const Vec4 t1 = rhsv.xyzw;
|
||||
|
||||
const Vec4 t2 = lhsv.yyyy;
|
||||
const Vec4 t3 = rhsv.yxwz;
|
||||
|
||||
const Vec4 t4 = lhsv.zzzz;
|
||||
const Vec4 t5 = rhsv.zwxy;
|
||||
|
||||
const Vec4 t6 = lhsv.wwww;
|
||||
const Vec4 t7 = rhsv.wzyx;
|
||||
|
||||
const auto m0 = t0 * t1;
|
||||
const auto m1 = s1 * t2 * t3;
|
||||
const auto m2 = s2 * t4 * t5;
|
||||
const auto m3 = s3 * t6 * t7;
|
||||
|
||||
const auto s = m0 + m1 + m2 + m3;
|
||||
|
||||
const auto w = lhs.s * rhs.s - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z;
|
||||
const auto x = lhs.s * rhs.x + lhs.x * rhs.s + lhs.y * rhs.z - lhs.z * rhs.y;
|
||||
const auto y = lhs.s * rhs.y - lhs.x * rhs.z + lhs.y * rhs.s + lhs.z * rhs.x;
|
||||
const auto z = lhs.s * rhs.z + lhs.x * rhs.y - lhs.y * rhs.x + lhs.z * rhs.s;
|
||||
|
||||
return Quaternion<T, Packed>{ s };
|
||||
}
|
||||
else {
|
||||
const auto w = lhs.s * rhs.s - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z;
|
||||
const auto x = lhs.s * rhs.x + lhs.x * rhs.s + lhs.y * rhs.z - lhs.z * rhs.y;
|
||||
const auto y = lhs.s * rhs.y - lhs.x * rhs.z + lhs.y * rhs.s + lhs.z * rhs.x;
|
||||
const auto z = lhs.s * rhs.z + lhs.x * rhs.y - lhs.y * rhs.x + lhs.z * rhs.s;
|
||||
return { w, x, y, z };
|
||||
}
|
||||
}
|
||||
} // namespace impl
|
||||
|
||||
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed>& operator+=(Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
lhs.vec += rhs.vec;
|
||||
return lhs;
|
||||
} // Helpers to write quaternion in paper-style such as (1 + 2_i + 3_j + 4_k). Slow performance, be careful.
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed>& operator-=(Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
lhs.vec -= rhs.vec;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed>& operator*=(Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
lhs = impl::Product(lhs, rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed>& operator*=(Quaternion<T, Packed>& lhs, T s) {
|
||||
lhs.vec *= s;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed>& operator/=(Quaternion<T, Packed>& lhs, T s) {
|
||||
lhs *= T(1) / s;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator+(const Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
Quaternion<T, Packed> copy(lhs);
|
||||
copy += rhs;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator-(const Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
Quaternion<T, Packed> copy(lhs);
|
||||
copy -= rhs;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator*(const Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
Quaternion<T, Packed> copy(lhs);
|
||||
copy *= rhs;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator*(const Quaternion<T, Packed>& lhs, T s) {
|
||||
Quaternion<T, Packed> copy(lhs);
|
||||
copy *= s;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator/(const Quaternion<T, Packed>& lhs, T s) {
|
||||
Quaternion<T, Packed> copy(lhs);
|
||||
copy /= s;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator+(const Quaternion<T, Packed>& arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> operator-(const Quaternion<T, Packed>& arg) {
|
||||
return Quaternion(-arg.vec);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Multiplies all coefficients of the quaternion by <paramref name="s"/>. </summary>
|
||||
template <class T, bool Packed, class U, class = typename std::enable_if<!std::is_same<U, Quaternion<T, Packed>>::value>::type>
|
||||
Quaternion<T, Packed> operator*(U s, const Quaternion<T, Packed>& rhs) {
|
||||
return rhs * s;
|
||||
}
|
||||
/// <summary> Divides all coefficients of the quaternion by <paramref name="s"/>. </summary>
|
||||
template <class T, bool Packed, class U, class = typename std::enable_if<!std::is_same<U, Quaternion<T, Packed>>::value>::type>
|
||||
Quaternion<T, Packed> operator/(U s, const Quaternion<T, Packed>& rhs) {
|
||||
return rhs / s;
|
||||
}
|
||||
|
||||
/// <summary> Adds a real to the real part of the quaternion. </summary>
|
||||
template <class T, bool Packed, class U, class = typename std::enable_if<!traits::IsQuaternion<U>::value>::type>
|
||||
Quaternion<T, Packed> operator+(const U& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
return Quaternion<T, Packed>(rhs.w + lhs, rhs.x, rhs.y, rhs.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,24 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "QuaternionImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> Check exact equality of coefficients. </summary>
|
||||
template <class T, bool Packed>
|
||||
bool operator==(const Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
return lhs.vec == rhs.vec;
|
||||
}
|
||||
/// <summary> Check exact unequality of coefficients. </summary>
|
||||
template <class T, bool Packed>
|
||||
bool operator!=(const Quaternion<T, Packed>& lhs, const Quaternion<T, Packed>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,88 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "QuaternionImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
/// <summary> The euclidean length of the vector of the 4 elements of the quaternion. </summary>
|
||||
template <class T, bool Packed>
|
||||
T Abs(const Quaternion<T, Packed>& q) {
|
||||
return Length(q.vec);
|
||||
}
|
||||
|
||||
/// <summary> Negates the imaginary values of the quaternion. </summary>
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Conjugate(const Quaternion<T, Packed>& q) {
|
||||
return Quaternion<T, Packed>{ q.vec * Vector<T, 4, Packed>{ T(1), T(-1), T(-1), T(-1) } };
|
||||
}
|
||||
|
||||
/// <summary> Natural quaternion exponentiation, base e. </summary>
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Exp(const Quaternion<T, Packed>& q) {
|
||||
auto a = q.ScalarPart();
|
||||
auto v = q.VectorPart();
|
||||
T mag = Length(v);
|
||||
T es = exp(a);
|
||||
|
||||
Quaternion<T, Packed> ret = { std::cos(mag), v * (std::sin(mag) / mag) };
|
||||
ret *= es;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Natural quaternion logarithm, base e. </summary>
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Log(const Quaternion<T, Packed>& q) {
|
||||
auto magq = Length(q);
|
||||
auto vn = Normalize(q.VectorPart());
|
||||
|
||||
Quaternion ret = { std::log(magq), vn * std::acos(q.s / magq) };
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Raises <paramref name="q"/> to the power of <paramref name="a"/>. </summary>
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Pow(const Quaternion<T, Packed>& q, T a) {
|
||||
return Exp(a * Log(q));
|
||||
}
|
||||
|
||||
/// <summary> Returns the square of the absolute value. </summary>
|
||||
/// <remarks> Just like complex numbers, it's the square of the length of the vector formed by the coefficients.
|
||||
/// This is much faster than <see cref="Length">. </remarks>
|
||||
template <class T, bool Packed>
|
||||
T LengthSquared(const Quaternion<T, Packed>& q) {
|
||||
return LengthSquared(q.vec);
|
||||
}
|
||||
|
||||
/// <summary> Returns the absolute value of the quaternion. </summary>
|
||||
/// <remarks> Just like complex numbers, it's the length of the vector formed by the coefficients. </remarks>
|
||||
template <class T, bool Packed>
|
||||
T Length(const Quaternion<T, Packed>& q) {
|
||||
return Abs(q);
|
||||
}
|
||||
|
||||
/// <summary> Returns the unit quaternion of the same direction. Does not change this object. </summary>
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Normalize(const Quaternion<T, Packed>& q) {
|
||||
return Quaternion<T, Packed>{ Normalize(q.vec) };
|
||||
}
|
||||
|
||||
/// <summary> Returns the quaternion of opposite rotation. </summary>
|
||||
template <class T, bool Packed>
|
||||
Quaternion<T, Packed> Inverse(const Quaternion<T, Packed>& q) {
|
||||
return Conjugate(q);
|
||||
}
|
||||
|
||||
/// <summary> Check if the quaternion is a unit quaternion, with some tolerance for floats. </summary>
|
||||
template <class T, bool Packed>
|
||||
bool IsNormalized(const Quaternion<T, Packed>& q) {
|
||||
return IsNormalized(q.vec);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,247 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../Common/MathUtil.hpp"
|
||||
#include "../Common/Traits.hpp"
|
||||
#include "../Matrix.hpp"
|
||||
#include "../Utility.hpp"
|
||||
#include "../Vector.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> Allows you to do quaternion math and represent rotation in a compact way. </summary>
|
||||
/// <typeparam name="T"> The scalar type of w, x, y and z. Use a builtin or custom floating or fixed point type. </typeparam>
|
||||
/// <typeparam name="Packed"> If true, tightly packs quaternion members and disables padding due to overalignment in arrays.
|
||||
/// Disables SIMD optimization. </typeparam>
|
||||
/// <remarks>
|
||||
/// These are plain mathematical quaternions, so expect the operations to work as mathematically defined.
|
||||
/// There are helper functions to represent rotation with quaternions.
|
||||
/// </remarks>
|
||||
template <class T, bool Packed = false>
|
||||
class Quaternion {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
T s, i, j, k;
|
||||
};
|
||||
struct {
|
||||
T w, x, y, z;
|
||||
};
|
||||
Vector<T, 4, Packed> vec;
|
||||
};
|
||||
|
||||
//-----------------------------------------------
|
||||
// Constructors
|
||||
//-----------------------------------------------
|
||||
/// <summary> Does NOT zero-initialize values. </summary>
|
||||
Quaternion() : vec() {}
|
||||
|
||||
Quaternion(const Quaternion& rhs) : vec(rhs.vec) {}
|
||||
|
||||
/// <summary> Set values directly. </summary>
|
||||
Quaternion(T scalar, T x, T y, T z) : w(scalar), x(x), y(y), z(z) {}
|
||||
|
||||
/// <summary> Sets the scalar part (w) and the vector part (xyz). This is not <see cref="AxisAngle"/> rotation. </summary>
|
||||
Quaternion(T scalar, const Vector<T, 3, true>& vector) : w(scalar), x(vector.x), y(vector.y), z(vector.z) {}
|
||||
|
||||
/// <summary> Sets the scalar part (w) and the vector part (xyz). This is not <see cref="AxisAngle"/> rotation. </summary>
|
||||
Quaternion(T scalar, const Vector<T, 3, false>& vector) : w(scalar), x(vector.x), y(vector.y), z(vector.z) {}
|
||||
|
||||
/// <summary> Sets the scalar part to zero, and the vector part to given argument. </summary>
|
||||
explicit Quaternion(const Vector<T, 3, true>& vector) : Quaternion(0, vector) {}
|
||||
|
||||
/// <summary> Sets the scalar part to zero, and the vector part to given argument. </summary>
|
||||
explicit Quaternion(const Vector<T, 3, false>& vector) : Quaternion(0, vector) {}
|
||||
|
||||
template <class U, bool P>
|
||||
Quaternion(const Quaternion<U, P>& rhs) : vec(rhs.vec) {}
|
||||
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). </remarks>
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
explicit Quaternion(const Matrix<U, 3, 3, Order, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
}
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). Translation part is ignored. </remarks>
|
||||
template <class U, eMatrixLayout Layout, bool PackedA>
|
||||
explicit Quaternion(const Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
}
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). Translation part is ignored. </remarks>
|
||||
template <class U, eMatrixLayout Layout, bool PackedA>
|
||||
explicit Quaternion(const Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
}
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). Translation part is ignored. </remarks>
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
explicit Quaternion(const Matrix<U, 4, 4, Order, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
}
|
||||
|
||||
explicit Quaternion(const Vector<T, 4, Packed>& vec) : vec(vec) {}
|
||||
|
||||
//-----------------------------------------------
|
||||
// Assignment
|
||||
//-----------------------------------------------
|
||||
Quaternion& operator=(const Quaternion& rhs) {
|
||||
vec = rhs.vec;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary> Convert from quaternion with different base type and packing. </summary>
|
||||
template <class U, bool P>
|
||||
Quaternion& operator=(const Quaternion<U, P>& rhs) {
|
||||
vec = rhs.vec;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). </remarks>
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
Quaternion& operator=(const Matrix<U, 3, 3, Order, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
return *this;
|
||||
}
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). Translation part is ignored. </remarks>
|
||||
template <class U, eMatrixLayout Layout, bool PackedA>
|
||||
Quaternion& operator=(const Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
return *this;
|
||||
}
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). Translation part is ignored. </remarks>
|
||||
template <class U, eMatrixLayout Layout, bool PackedA>
|
||||
Quaternion& operator=(const Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
return *this;
|
||||
}
|
||||
/// <summary> Convert a rotation matrix to equivalent quaternion. </summary>
|
||||
/// <remarks> Matrix must be in SO(3). Translation part is ignored. </remarks>
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
Quaternion& operator=(const Matrix<U, 4, 4, Order, Layout, PackedA>& rhs) {
|
||||
FromMatrix(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
// Functions
|
||||
//-----------------------------------------------
|
||||
|
||||
/// <summary> Returns the scalar part (w) of (w + xi + yj + zk). </summary>
|
||||
const T ScalarPart() const {
|
||||
return s;
|
||||
}
|
||||
/// <summary> Returns the vector part (x, y, z) of (w + xi + yj + zk). </summary>
|
||||
const Vector<T, 3, Packed> VectorPart() const {
|
||||
return { x, y, z };
|
||||
}
|
||||
|
||||
/// <summary> Returns the angle of the rotation represented by quaternion. </summary>
|
||||
/// <remarks> Only valid for unit quaternions. </remarks>
|
||||
const T Angle() const {
|
||||
return impl::sign_nonzero(s) * 2 * std::acos(std::clamp(std::abs(s) / Length(vec), T(-1), T(1)));
|
||||
}
|
||||
/// <summary> Returns the axis of rotation represented by quaternion. </summary>
|
||||
/// <remarks> Only valid for unit quaternions. Returns (1,0,0) for near 180 degree rotations. </remarks>
|
||||
const Vector<T, 3, Packed> Axis() const {
|
||||
auto direction = VectorPart();
|
||||
return SafeNormalize(direction);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
// Matrix conversions
|
||||
//-----------------------------------------------
|
||||
|
||||
/// <summary> Creates a rotation matrix equivalent to the quaternion. </summary>
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
explicit operator Matrix<U, 3, 3, Order, Layout, PackedA>() const {
|
||||
return ToMatrix<U, 3, 3, Order, Layout, PackedA>();
|
||||
}
|
||||
/// <summary> Creates a rotation matrix equivalent to the quaternion. </summary>
|
||||
template <class U, eMatrixLayout Layout, bool PackedA>
|
||||
explicit operator Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, PackedA>() const {
|
||||
return ToMatrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, PackedA>();
|
||||
}
|
||||
/// <summary> Creates a rotation matrix equivalent to the quaternion. </summary>
|
||||
template <class U, eMatrixLayout Layout, bool PackedA>
|
||||
explicit operator Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, PackedA>() const {
|
||||
return ToMatrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, PackedA>();
|
||||
}
|
||||
/// <summary> Creates a rotation matrix equivalent to the quaternion. </summary>
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
explicit operator Matrix<U, 4, 4, Order, Layout, PackedA>() const {
|
||||
return ToMatrix<U, 4, 4, Order, Layout, PackedA>();
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
// Truncate to vector
|
||||
//-----------------------------------------------
|
||||
|
||||
/// <summary> Truncates the quaternion to the vector part (x, y, z). </summary>
|
||||
template <class U, bool PackedA>
|
||||
explicit operator Vector<U, 3, PackedA>() const {
|
||||
return { x, y, z };
|
||||
}
|
||||
|
||||
protected:
|
||||
//-----------------------------------------------
|
||||
// Matrix conversion helpers
|
||||
//-----------------------------------------------
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
Matrix<U, Rows, Columns, Order, Layout, PackedA> ToMatrix() const {
|
||||
assert(IsNormalized(vec));
|
||||
Matrix<U, Rows, Columns, Order, Layout, PackedA> mat;
|
||||
auto elem = [&mat](int i, int j) -> U& {
|
||||
return Order == eMatrixOrder::PRECEDE_VECTOR ? mat(i, j) : mat(j, i);
|
||||
};
|
||||
elem(0, 0) = 1 - 2 * (j * j + k * k);
|
||||
elem(0, 1) = 2 * (i * j - k * s);
|
||||
elem(0, 2) = 2 * (i * k + j * s);
|
||||
elem(1, 0) = 2 * (i * j + k * s);
|
||||
elem(1, 1) = 1 - 2 * (i * i + k * k);
|
||||
elem(1, 2) = 2 * (j * k - i * s);
|
||||
elem(2, 0) = 2 * (i * k - j * s);
|
||||
elem(2, 1) = 2 * (j * k + i * s);
|
||||
elem(2, 2) = 1 - 2 * (i * i + j * j);
|
||||
|
||||
// Rest
|
||||
for (int j = 0; j < mat.Width(); ++j) {
|
||||
for (int i = (j < 3 ? 3 : 0); i < mat.Height(); ++i) {
|
||||
mat(i, j) = T(j == i);
|
||||
}
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool PackedA>
|
||||
void FromMatrix(const Matrix<U, Rows, Columns, Order, Layout, PackedA>& mat) {
|
||||
assert(IsRotationMatrix3D(mat));
|
||||
auto elem = [&mat](int i, int j) -> U {
|
||||
return Order == eMatrixOrder::PRECEDE_VECTOR ? mat(i, j) : mat(j, i);
|
||||
};
|
||||
w = std::sqrt(1 + elem(0, 0) + elem(1, 1) + elem(2, 2)) * T(0.5);
|
||||
T div = T(1) / (T(4) * w);
|
||||
x = (elem(2, 1) - elem(1, 2)) * div;
|
||||
y = (elem(0, 2) - elem(2, 0)) * div;
|
||||
z = (elem(1, 0) - elem(0, 1)) * div;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,34 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "QuaternionImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
namespace quat_literals {
|
||||
inline Quaternion<long double> operator"" _i(unsigned long long int arg) {
|
||||
return Quaternion<long double>(0, (long double)arg, 0, 0);
|
||||
}
|
||||
inline Quaternion<long double> operator"" _j(unsigned long long int arg) {
|
||||
return Quaternion<long double>(0, 0, (long double)arg, 0);
|
||||
}
|
||||
inline Quaternion<long double> operator"" _k(unsigned long long int arg) {
|
||||
return Quaternion<long double>(0, 0, 0, (long double)arg);
|
||||
}
|
||||
|
||||
inline Quaternion<long double> operator"" _i(long double arg) {
|
||||
return Quaternion<long double>(0, arg, 0, 0);
|
||||
}
|
||||
inline Quaternion<long double> operator"" _j(long double arg) {
|
||||
return Quaternion<long double>(0, 0, arg, 0);
|
||||
}
|
||||
inline Quaternion<long double> operator"" _k(long double arg) {
|
||||
return Quaternion<long double>(0, 0, 0, arg);
|
||||
}
|
||||
} // namespace quat_literals
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,35 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "QuaternionImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> Rotates (and scales) vector by quaternion. </summary>
|
||||
template <class T, bool QPacked, bool PackedA>
|
||||
Vector<T, 3, PackedA> operator*(const Quaternion<T, QPacked>& q, const Vector<T, 3, PackedA>& vec) {
|
||||
// sandwich product
|
||||
return Vector<T, 3, PackedA>(q * Quaternion<T, QPacked>(vec) * Inverse(q));
|
||||
}
|
||||
|
||||
/// <summary> Rotates (and scales) vector by quaternion. </summary>
|
||||
template <class T, bool QPacked, bool PackedA>
|
||||
Vector<T, 3, PackedA> operator*(const Vector<T, 3, PackedA>& vec, const Quaternion<T, QPacked>& q) {
|
||||
// sandwich product
|
||||
return Vector<T, 3, PackedA>(q * Quaternion<T, QPacked>(vec) * Inverse(q));
|
||||
}
|
||||
|
||||
/// <summary> Rotates (and scales) vector by quaternion. </summary>
|
||||
template <class T, bool QPacked, bool PackedA>
|
||||
Vector<T, 3, PackedA>& operator*=(Vector<T, 3, PackedA>& vec, const Quaternion<T, QPacked>& q) {
|
||||
// sandwich product
|
||||
return vec = Vector<T, 3, PackedA>(q * Quaternion<T, QPacked>(vec) * Inverse(q));
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,3 @@
|
|||
Swizzle<T, Dim, Packed, 0, 0> xx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0> xxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 0> xxxx;
|
|
@ -0,0 +1,28 @@
|
|||
Swizzle<T, Dim, Packed, 0, 0> xx;
|
||||
Swizzle<T, Dim, Packed, 0, 1> xy;
|
||||
Swizzle<T, Dim, Packed, 1, 0> yx;
|
||||
Swizzle<T, Dim, Packed, 1, 1> yy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0> xxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1> xxy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0> xyx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1> xyy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0> yxx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1> yxy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0> yyx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1> yyy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 0> xxxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 1> xxxy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 0> xxyx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 1> xxyy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 0> xyxx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 1> xyxy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 0> xyyx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 1> xyyy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 0> yxxx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 1> yxxy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 0> yxyx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 1> yxyy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 0> yyxx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 1> yyxy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 0> yyyx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 1> yyyy;
|
|
@ -0,0 +1,117 @@
|
|||
Swizzle<T, Dim, Packed, 0, 0> xx;
|
||||
Swizzle<T, Dim, Packed, 0, 1> xy;
|
||||
Swizzle<T, Dim, Packed, 0, 2> xz;
|
||||
Swizzle<T, Dim, Packed, 1, 0> yx;
|
||||
Swizzle<T, Dim, Packed, 1, 1> yy;
|
||||
Swizzle<T, Dim, Packed, 1, 2> yz;
|
||||
Swizzle<T, Dim, Packed, 2, 0> zx;
|
||||
Swizzle<T, Dim, Packed, 2, 1> zy;
|
||||
Swizzle<T, Dim, Packed, 2, 2> zz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0> xxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1> xxy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2> xxz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0> xyx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1> xyy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2> xyz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0> xzx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1> xzy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2> xzz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0> yxx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1> yxy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2> yxz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0> yyx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1> yyy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2> yyz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0> yzx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1> yzy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2> yzz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0> zxx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1> zxy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2> zxz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0> zyx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1> zyy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2> zyz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0> zzx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1> zzy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2> zzz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 0> xxxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 1> xxxy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 2> xxxz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 0> xxyx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 1> xxyy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 2> xxyz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 0> xxzx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 1> xxzy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 2> xxzz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 0> xyxx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 1> xyxy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 2> xyxz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 0> xyyx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 1> xyyy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 2> xyyz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 0> xyzx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 1> xyzy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 2> xyzz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 0> xzxx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 1> xzxy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 2> xzxz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 0> xzyx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 1> xzyy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 2> xzyz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 0> xzzx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 1> xzzy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 2> xzzz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 0> yxxx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 1> yxxy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 2> yxxz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 0> yxyx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 1> yxyy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 2> yxyz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 0> yxzx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 1> yxzy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 2> yxzz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 0> yyxx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 1> yyxy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 2> yyxz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 0> yyyx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 1> yyyy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 2> yyyz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 0> yyzx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 1> yyzy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 2> yyzz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 0> yzxx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 1> yzxy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 2> yzxz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 0> yzyx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 1> yzyy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 2> yzyz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 0> yzzx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 1> yzzy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 2> yzzz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 0> zxxx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 1> zxxy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 2> zxxz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 0> zxyx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 1> zxyy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 2> zxyz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 0> zxzx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 1> zxzy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 2> zxzz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 0> zyxx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 1> zyxy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 2> zyxz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 0> zyyx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 1> zyyy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 2> zyyz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 0> zyzx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 1> zyzy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 2> zyzz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 0> zzxx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 1> zzxy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 2> zzxz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 0> zzyx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 1> zzyy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 2> zzyz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 0> zzzx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 1> zzzy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 2> zzzz;
|
|
@ -0,0 +1,336 @@
|
|||
Swizzle<T, Dim, Packed, 0, 0> xx;
|
||||
Swizzle<T, Dim, Packed, 0, 1> xy;
|
||||
Swizzle<T, Dim, Packed, 0, 2> xz;
|
||||
Swizzle<T, Dim, Packed, 0, 3> xw;
|
||||
Swizzle<T, Dim, Packed, 1, 0> yx;
|
||||
Swizzle<T, Dim, Packed, 1, 1> yy;
|
||||
Swizzle<T, Dim, Packed, 1, 2> yz;
|
||||
Swizzle<T, Dim, Packed, 1, 3> yw;
|
||||
Swizzle<T, Dim, Packed, 2, 0> zx;
|
||||
Swizzle<T, Dim, Packed, 2, 1> zy;
|
||||
Swizzle<T, Dim, Packed, 2, 2> zz;
|
||||
Swizzle<T, Dim, Packed, 2, 3> zw;
|
||||
Swizzle<T, Dim, Packed, 3, 0> wx;
|
||||
Swizzle<T, Dim, Packed, 3, 1> wy;
|
||||
Swizzle<T, Dim, Packed, 3, 2> wz;
|
||||
Swizzle<T, Dim, Packed, 3, 3> ww;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0> xxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1> xxy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2> xxz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 3> xxw;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0> xyx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1> xyy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2> xyz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 3> xyw;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0> xzx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1> xzy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2> xzz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 3> xzw;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 0> xwx;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 1> xwy;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 2> xwz;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 3> xww;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0> yxx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1> yxy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2> yxz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 3> yxw;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0> yyx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1> yyy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2> yyz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 3> yyw;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0> yzx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1> yzy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2> yzz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 3> yzw;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 0> ywx;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 1> ywy;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 2> ywz;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 3> yww;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0> zxx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1> zxy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2> zxz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 3> zxw;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0> zyx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1> zyy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2> zyz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 3> zyw;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0> zzx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1> zzy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2> zzz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 3> zzw;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 0> zwx;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 1> zwy;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 2> zwz;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 3> zww;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 0> wxx;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 1> wxy;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 2> wxz;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 3> wxw;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 0> wyx;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 1> wyy;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 2> wyz;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 3> wyw;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 0> wzx;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 1> wzy;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 2> wzz;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 3> wzw;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 0> wwx;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 1> wwy;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 2> wwz;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 3> www;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 0> xxxx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 1> xxxy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 2> xxxz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 0, 3> xxxw;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 0> xxyx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 1> xxyy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 2> xxyz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 1, 3> xxyw;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 0> xxzx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 1> xxzy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 2> xxzz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 2, 3> xxzw;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 3, 0> xxwx;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 3, 1> xxwy;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 3, 2> xxwz;
|
||||
Swizzle<T, Dim, Packed, 0, 0, 3, 3> xxww;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 0> xyxx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 1> xyxy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 2> xyxz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 0, 3> xyxw;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 0> xyyx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 1> xyyy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 2> xyyz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 1, 3> xyyw;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 0> xyzx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 1> xyzy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 2> xyzz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 2, 3> xyzw;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 3, 0> xywx;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 3, 1> xywy;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 3, 2> xywz;
|
||||
Swizzle<T, Dim, Packed, 0, 1, 3, 3> xyww;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 0> xzxx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 1> xzxy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 2> xzxz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 0, 3> xzxw;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 0> xzyx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 1> xzyy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 2> xzyz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 1, 3> xzyw;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 0> xzzx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 1> xzzy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 2> xzzz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 2, 3> xzzw;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 3, 0> xzwx;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 3, 1> xzwy;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 3, 2> xzwz;
|
||||
Swizzle<T, Dim, Packed, 0, 2, 3, 3> xzww;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 0, 0> xwxx;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 0, 1> xwxy;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 0, 2> xwxz;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 0, 3> xwxw;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 1, 0> xwyx;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 1, 1> xwyy;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 1, 2> xwyz;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 1, 3> xwyw;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 2, 0> xwzx;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 2, 1> xwzy;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 2, 2> xwzz;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 2, 3> xwzw;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 3, 0> xwwx;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 3, 1> xwwy;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 3, 2> xwwz;
|
||||
Swizzle<T, Dim, Packed, 0, 3, 3, 3> xwww;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 0> yxxx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 1> yxxy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 2> yxxz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 0, 3> yxxw;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 0> yxyx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 1> yxyy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 2> yxyz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 1, 3> yxyw;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 0> yxzx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 1> yxzy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 2> yxzz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 2, 3> yxzw;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 3, 0> yxwx;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 3, 1> yxwy;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 3, 2> yxwz;
|
||||
Swizzle<T, Dim, Packed, 1, 0, 3, 3> yxww;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 0> yyxx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 1> yyxy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 2> yyxz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 0, 3> yyxw;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 0> yyyx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 1> yyyy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 2> yyyz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 1, 3> yyyw;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 0> yyzx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 1> yyzy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 2> yyzz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 2, 3> yyzw;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 3, 0> yywx;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 3, 1> yywy;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 3, 2> yywz;
|
||||
Swizzle<T, Dim, Packed, 1, 1, 3, 3> yyww;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 0> yzxx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 1> yzxy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 2> yzxz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 0, 3> yzxw;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 0> yzyx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 1> yzyy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 2> yzyz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 1, 3> yzyw;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 0> yzzx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 1> yzzy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 2> yzzz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 2, 3> yzzw;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 3, 0> yzwx;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 3, 1> yzwy;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 3, 2> yzwz;
|
||||
Swizzle<T, Dim, Packed, 1, 2, 3, 3> yzww;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 0, 0> ywxx;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 0, 1> ywxy;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 0, 2> ywxz;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 0, 3> ywxw;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 1, 0> ywyx;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 1, 1> ywyy;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 1, 2> ywyz;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 1, 3> ywyw;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 2, 0> ywzx;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 2, 1> ywzy;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 2, 2> ywzz;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 2, 3> ywzw;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 3, 0> ywwx;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 3, 1> ywwy;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 3, 2> ywwz;
|
||||
Swizzle<T, Dim, Packed, 1, 3, 3, 3> ywww;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 0> zxxx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 1> zxxy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 2> zxxz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 0, 3> zxxw;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 0> zxyx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 1> zxyy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 2> zxyz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 1, 3> zxyw;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 0> zxzx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 1> zxzy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 2> zxzz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 2, 3> zxzw;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 3, 0> zxwx;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 3, 1> zxwy;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 3, 2> zxwz;
|
||||
Swizzle<T, Dim, Packed, 2, 0, 3, 3> zxww;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 0> zyxx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 1> zyxy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 2> zyxz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 0, 3> zyxw;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 0> zyyx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 1> zyyy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 2> zyyz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 1, 3> zyyw;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 0> zyzx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 1> zyzy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 2> zyzz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 2, 3> zyzw;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 3, 0> zywx;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 3, 1> zywy;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 3, 2> zywz;
|
||||
Swizzle<T, Dim, Packed, 2, 1, 3, 3> zyww;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 0> zzxx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 1> zzxy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 2> zzxz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 0, 3> zzxw;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 0> zzyx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 1> zzyy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 2> zzyz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 1, 3> zzyw;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 0> zzzx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 1> zzzy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 2> zzzz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 2, 3> zzzw;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 3, 0> zzwx;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 3, 1> zzwy;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 3, 2> zzwz;
|
||||
Swizzle<T, Dim, Packed, 2, 2, 3, 3> zzww;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 0, 0> zwxx;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 0, 1> zwxy;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 0, 2> zwxz;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 0, 3> zwxw;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 1, 0> zwyx;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 1, 1> zwyy;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 1, 2> zwyz;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 1, 3> zwyw;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 2, 0> zwzx;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 2, 1> zwzy;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 2, 2> zwzz;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 2, 3> zwzw;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 3, 0> zwwx;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 3, 1> zwwy;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 3, 2> zwwz;
|
||||
Swizzle<T, Dim, Packed, 2, 3, 3, 3> zwww;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 0, 0> wxxx;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 0, 1> wxxy;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 0, 2> wxxz;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 0, 3> wxxw;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 1, 0> wxyx;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 1, 1> wxyy;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 1, 2> wxyz;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 1, 3> wxyw;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 2, 0> wxzx;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 2, 1> wxzy;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 2, 2> wxzz;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 2, 3> wxzw;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 3, 0> wxwx;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 3, 1> wxwy;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 3, 2> wxwz;
|
||||
Swizzle<T, Dim, Packed, 3, 0, 3, 3> wxww;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 0, 0> wyxx;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 0, 1> wyxy;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 0, 2> wyxz;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 0, 3> wyxw;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 1, 0> wyyx;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 1, 1> wyyy;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 1, 2> wyyz;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 1, 3> wyyw;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 2, 0> wyzx;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 2, 1> wyzy;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 2, 2> wyzz;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 2, 3> wyzw;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 3, 0> wywx;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 3, 1> wywy;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 3, 2> wywz;
|
||||
Swizzle<T, Dim, Packed, 3, 1, 3, 3> wyww;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 0, 0> wzxx;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 0, 1> wzxy;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 0, 2> wzxz;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 0, 3> wzxw;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 1, 0> wzyx;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 1, 1> wzyy;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 1, 2> wzyz;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 1, 3> wzyw;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 2, 0> wzzx;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 2, 1> wzzy;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 2, 2> wzzz;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 2, 3> wzzw;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 3, 0> wzwx;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 3, 1> wzwy;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 3, 2> wzwz;
|
||||
Swizzle<T, Dim, Packed, 3, 2, 3, 3> wzww;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 0, 0> wwxx;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 0, 1> wwxy;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 0, 2> wwxz;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 0, 3> wwxw;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 1, 0> wwyx;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 1, 1> wwyy;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 1, 2> wwyz;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 1, 3> wwyw;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 2, 0> wwzx;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 2, 1> wwzy;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 2, 2> wwzz;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 2, 3> wwzw;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 3, 0> wwwx;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 3, 1> wwwy;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 3, 2> wwwz;
|
||||
Swizzle<T, Dim, Packed, 3, 3, 3, 3> wwww;
|
|
@ -0,0 +1,51 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Quaternion/QuaternionImpl.hpp"
|
||||
#include "ZeroBuilder.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
class IdentityBuilder {
|
||||
public:
|
||||
IdentityBuilder() = default;
|
||||
IdentityBuilder& operator=(const IdentityBuilder&) = delete;
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
operator Matrix<T, Rows, Columns, Order, Layout, Packed>() const {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
operator Quaternion<T, Packed>() const {
|
||||
return Quaternion<T, Packed>{ 1, 0, 0, 0 };
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
void Set(Matrix<T, Rows, Columns, Order, Layout, Packed>& m) const {
|
||||
m = Zero();
|
||||
for (int i = 0; i < std::min(Rows, Columns); ++i) {
|
||||
m(i, i) = T(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates an identity matrix or identity quaternion. </summary>
|
||||
/// <remarks> If the matrix is not square, it will look like a truncated larger square identity matrix. </remarks>
|
||||
inline auto Identity() {
|
||||
return IdentityBuilder{};
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,89 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
#include "IdentityBuilder.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class OrthographicBuilder {
|
||||
static_assert(!std::is_integral_v<T>);
|
||||
using VectorT = Vector<T, Dim, Packed>;
|
||||
|
||||
public:
|
||||
OrthographicBuilder(const VectorT& minBounds, const VectorT& maxBounds, T projNearPlane, T projFarPlane)
|
||||
: minBounds(minBounds), maxBounds(maxBounds), projNearPlane(projNearPlane), projFarPlane(projFarPlane) {}
|
||||
OrthographicBuilder& operator=(const OrthographicBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
using VectorT = Vector<T, Dim, Packed>;
|
||||
|
||||
VectorT volumeSize = maxBounds - minBounds;
|
||||
VectorT scale = T(2) / volumeSize;
|
||||
scale[scale.Dimension() - 1] *= T(0.5) * (projFarPlane - projNearPlane);
|
||||
VectorT offset = -(maxBounds + minBounds) / T(2) * scale;
|
||||
offset[offset.Dimension() - 1] += (projFarPlane + projNearPlane) / 2;
|
||||
|
||||
m = Identity();
|
||||
for (int i = 0; i < scale.Dimension(); ++i) {
|
||||
m(i, i) = scale(i);
|
||||
(Order == eMatrixOrder::FOLLOW_VECTOR ? m(scale.Dimension(), i) : m(i, scale.Dimension())) = offset(i);
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<T, Dim, Packed> minBounds, maxBounds;
|
||||
T projNearPlane, projFarPlane;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates an orthographics projection matrix. The volume before projection
|
||||
/// is an axis-aligned hypercube and it is projected onto a unit hypercube. </summary>
|
||||
/// <param name="minBounds"> The "left" corner of the hypercube. </param>
|
||||
/// <param name="maxBounds"> The "right" corner of the hypercube. </param>
|
||||
/// <param name="projNearPlane"> The lower bound of the last axis of the projected volume (Z axis in 3D). </param>
|
||||
/// <param name="projFarPlane"> The upper bound of the last axis of the projected volume (Z axis in 3D). </param>
|
||||
/// <remarks> After projection, all axes range from -1 to 1, except for the last axis, which is specified explicitly. </remarks>
|
||||
template <class T, int Dim, bool Packed>
|
||||
auto Orthographic(const Vector<T, Dim, Packed>& minBounds, const Vector<T, Dim, Packed>& maxBounds, T projNearPlane, T projFarPlane) {
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
using VectorT = Vector<float, Dim, false>;
|
||||
return OrthographicBuilder(VectorT(minBounds), VectorT(maxBounds), float(projNearPlane), float(projFarPlane));
|
||||
}
|
||||
else {
|
||||
return OrthographicBuilder(minBounds, maxBounds, projNearPlane, projFarPlane);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,126 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
#include "IdentityBuilder.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class PerspectiveBuilder {
|
||||
static_assert(!std::is_integral_v<T>);
|
||||
|
||||
public:
|
||||
PerspectiveBuilder(T fovX, const Vector<T, Dim - 1, Packed>& ratios, T nearPlane, T farPlane, T projNearPlane = 0, T projFarPlane = 1)
|
||||
: fovX(fovX), ratios(ratios), nearPlane(nearPlane), farPlane(farPlane), projNearPlane(projNearPlane), projFarPlane(projFarPlane) {}
|
||||
PerspectiveBuilder& operator=(const PerspectiveBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
assert((nearPlane < 0 && farPlane < nearPlane) || (0 < nearPlane && nearPlane < farPlane));
|
||||
|
||||
using UVec = Vector<U, Dim - 1, Packed>;
|
||||
|
||||
m = Zero();
|
||||
// Layout be like (precede_vector):
|
||||
// w 0 0 0
|
||||
// 0 h 0 0
|
||||
// 0 0 A B
|
||||
// 0 0 C 0
|
||||
auto absFovX = std::abs(U(fovX));
|
||||
U N = U(nearPlane);
|
||||
U F = U(farPlane);
|
||||
U n = U(projNearPlane);
|
||||
U f = U(projFarPlane);
|
||||
U C = U(nearPlane) < U(0) ? U(-1) : U(1);
|
||||
U A = C * (f * F - n * N) / (F - N);
|
||||
U B = C * F * N * (n - f) / (F - N);
|
||||
UVec adjRatios = U(ratios(0)) / UVec(ratios);
|
||||
U w = tan(U(0.5) * absFovX);
|
||||
adjRatios /= w;
|
||||
for (int i = 0; i < adjRatios.Dimension(); ++i) {
|
||||
m(i, i) = adjRatios(i);
|
||||
}
|
||||
m(m.RowCount() - 2, m.ColumnCount() - 2) = A;
|
||||
if (Order == eMatrixOrder::FOLLOW_VECTOR) {
|
||||
std::swap(B, C);
|
||||
}
|
||||
m(m.RowCount() - 2, m.ColumnCount() - 1) = B;
|
||||
m(m.RowCount() - 1, m.ColumnCount() - 2) = C;
|
||||
}
|
||||
|
||||
const T fovX;
|
||||
const Vector<T, Dim - 1, Packed> ratios;
|
||||
const T nearPlane;
|
||||
const T farPlane;
|
||||
const T projNearPlane = 0;
|
||||
const T projFarPlane = 1;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a general, n-dimensional perspective projection matrix. </summary>
|
||||
/// <param name="fovX"> Field of view on the first axis (usually denoted X) in radians. </param>
|
||||
/// <param name="ratios"> Aspect ratio (or ratios in higher dimensions). FovX/FovY. </param>
|
||||
/// <param name="nearPlane"> Near bound of the projected volume on the last axis (Z in 3D). </param>
|
||||
/// <param name="farPlane"> Far bound of the projected volume on the last axis (Z in 3D). </param>
|
||||
/// <param name="projNearPlane"> The near plane is taken here after projection. </param>
|
||||
/// <param name="projFarPlane"> The far plane is taken here after projection. </param>
|
||||
/// <remarks> The pre-projection near and far planes must be on the same side of the last axis, i.e. both negative or both positive.
|
||||
/// The post-projection near and far planes can be arbitrary, that is, near larger, near smaller, both positive, both negative,
|
||||
/// one positive the other negative, etc.
|
||||
/// Negative <paramref name="ratios"/> invert image on the axis which has a negative value. </remarks>
|
||||
template <class T, int DimMinus1, bool Packed>
|
||||
auto Perspective(T fovX, const Vector<T, DimMinus1, Packed>& ratios, T nearPlane, T farPlane, T projNearPlane, T projFarPlane) {
|
||||
using NonIntegral = std::conditional_t<std::is_integral_v<T>, float, T>;
|
||||
return PerspectiveBuilder<NonIntegral, DimMinus1 + 1, Packed>{ fovX, ratios, nearPlane, farPlane, projNearPlane, projFarPlane };
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates a 2D projection matrix. </summary>
|
||||
/// <param name="fov"> Field of view. </param>
|
||||
/// <param name="nearPlane"> Lower bound of the volume on the Y axis. </param>
|
||||
/// <param name="farPlane"> Upper bound of the volume on the Y axis. </param>
|
||||
/// <param name="projNearPlane"> Near plane is taken here after projection. </param>
|
||||
/// <param name="projFarPlane"> Far plane is taken here after projection. </param>
|
||||
/// <remarks> The pre-projection near and far planes must be on the same side of the last axis, i.e. both negative or both positive.
|
||||
/// The post-projection near and far planes can be arbitrary, that is, near larger, near smaller, both positive, both negative,
|
||||
/// one positive the other negative, etc. </remarks>
|
||||
template <class T>
|
||||
auto Perspective(T fov, T nearPlane, T farPlane, T projNearPlane, T projFarPlane) {
|
||||
return Perspective(std::abs(fov), Vector<T, 1, false>{ fov < T(0) ? T(-1) : T(1) }, nearPlane, farPlane, projNearPlane, projFarPlane);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates a 3D projection matrix. </summary>
|
||||
/// <param name="fov"/> Field of view. </param>
|
||||
/// <param name="aspectRatio"> FovX/FovY, so 1.777 for a 16:9 screen. </param>
|
||||
/// <param name="nearPlane"/> Lower bound of the volume on the Y axis. </param>
|
||||
/// <param name="farPlane"/> Upper bound of the volume on the Y axis. </param>
|
||||
/// <param name="projNearPlane"/> Near plane is taken here after projection. </param>
|
||||
/// <param name="projFarPlane"/> Far plane is taken here after projection. </param>
|
||||
/// <remarks> The pre-projection near and far planes must be on the same side of the last axis, i.e. both negative or both positive.
|
||||
/// The post-projection near and far planes can be arbitrary, that is, near larger, near smaller, both positive, both negative,
|
||||
/// one positive the other negative, etc.
|
||||
/// The <paramref name="aspectRatio"/> can be negative, in which case the second axis (i.e. Y) is inverted. </remarks>
|
||||
template <class T>
|
||||
auto Perspective(T fov, T aspectRatio, T nearPlane, T farPlane, T projNearPlane, T projFarPlane) {
|
||||
return Perspective(std::abs(fov), Vector<T, 2, false>{ fov < T(0) ? T(-1) : T(1), T(1) / aspectRatio }, nearPlane, farPlane, projNearPlane, projFarPlane);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,85 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T>
|
||||
class Rotation2DBuilder {
|
||||
public:
|
||||
Rotation2DBuilder(const T& angle) : angle(angle) {}
|
||||
Rotation2DBuilder& operator=(const Rotation2DBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 3, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 3, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 2, 2, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 2, 2, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 2, 3, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 2, 3, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 2, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 2, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
T C = cos(angle);
|
||||
T S = sin(angle);
|
||||
|
||||
auto elem = [&m](int i, int j) -> U& {
|
||||
return Order == eMatrixOrder::FOLLOW_VECTOR ? m(i, j) : m(j, i);
|
||||
};
|
||||
|
||||
// Indices according to follow vector order
|
||||
elem(0, 0) = U(C);
|
||||
elem(0, 1) = U(S);
|
||||
elem(1, 0) = U(-S);
|
||||
elem(1, 1) = U(C);
|
||||
|
||||
// Rest
|
||||
for (int j = 0; j < m.ColumnCount(); ++j) {
|
||||
for (int i = (j < 2 ? 2 : 0); i < m.RowCount(); ++i) {
|
||||
m(i, j) = U(j == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const T angle;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a 2D rotation matrix. </summary>
|
||||
/// <param name="angle"> Counter-clockwise angle in radians. </param>
|
||||
template <class T>
|
||||
auto Rotation(const T& angle) {
|
||||
return Rotation2DBuilder{ angle };
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,391 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixFunction.hpp"
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Quaternion/QuaternionImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
#include "IdentityBuilder.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T>
|
||||
class Rotation3DAxisBuilder {
|
||||
public:
|
||||
Rotation3DAxisBuilder(const T& angle, int axis) : angle(angle), axis(axis) {}
|
||||
Rotation3DAxisBuilder& operator=(const Rotation3DAxisBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 4, 4, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 4, 4, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 3, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 3, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, bool QPacked>
|
||||
operator Quaternion<U, QPacked>() const;
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
T C = cos(angle);
|
||||
T S = sin(angle);
|
||||
|
||||
auto elem = [&m](int i, int j) -> U& {
|
||||
return Order == eMatrixOrder::FOLLOW_VECTOR ? m(i, j) : m(j, i);
|
||||
};
|
||||
|
||||
assert(0 <= axis && axis < 3);
|
||||
|
||||
// Indices according to follow vector order
|
||||
if (axis == 0) {
|
||||
// Rotate around X
|
||||
elem(0, 0) = U(1);
|
||||
elem(0, 1) = U(0);
|
||||
elem(0, 2) = U(0);
|
||||
elem(1, 0) = U(0);
|
||||
elem(1, 1) = U(C);
|
||||
elem(1, 2) = U(S);
|
||||
elem(2, 0) = U(0);
|
||||
elem(2, 1) = U(-S);
|
||||
elem(2, 2) = U(C);
|
||||
}
|
||||
else if (axis == 1) {
|
||||
// Rotate around Y
|
||||
elem(0, 0) = U(C);
|
||||
elem(0, 1) = U(0);
|
||||
elem(0, 2) = U(-S);
|
||||
elem(1, 0) = U(0);
|
||||
elem(1, 1) = U(1);
|
||||
elem(1, 2) = U(0);
|
||||
elem(2, 0) = U(S);
|
||||
elem(2, 1) = U(0);
|
||||
elem(2, 2) = U(C);
|
||||
}
|
||||
else {
|
||||
// Rotate around Z
|
||||
elem(0, 0) = U(C);
|
||||
elem(0, 1) = U(S);
|
||||
elem(0, 2) = U(0);
|
||||
elem(1, 0) = U(-S);
|
||||
elem(1, 1) = U(C);
|
||||
elem(1, 2) = U(0);
|
||||
elem(2, 0) = U(0);
|
||||
elem(2, 1) = U(0);
|
||||
elem(2, 2) = U(1);
|
||||
}
|
||||
|
||||
// Rest
|
||||
for (int j = 0; j < m.ColumnCount(); ++j) {
|
||||
for (int i = (j < 3 ? 3 : 0); i < m.RowCount(); ++i) {
|
||||
m(i, j) = U(j == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float angle;
|
||||
const int axis;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// <summary> Rotates around coordinate axis. </summary>
|
||||
/// <param name="axis"> 0 for X, 1 for Y, 2 for Z and so on... </param>
|
||||
/// <param name="angle"> Angle of rotation in radians. </param>
|
||||
/// <remarks> Positive angles rotate according to the right-hand rule in right-handed
|
||||
/// coordinate systems (left-handed according to left-hand rule).
|
||||
template <class T>
|
||||
auto RotationAxis(T angle, int axis) {
|
||||
return Rotation3DAxisBuilder(angle, axis);
|
||||
}
|
||||
|
||||
/// <summary> Rotates around coordinate axis. </summary>
|
||||
/// <typeparam name="Axis"> 0 for X, 1 for Y, 2 for Z and so on... </typeparam>
|
||||
/// <param name="angle"> Angle of rotation in radians. </param>
|
||||
/// <remarks> Positive angles rotate according to the right-hand rule in right-handed
|
||||
/// coordinate systems (left-handed according to left-hand rule).
|
||||
template <int Axis, class T>
|
||||
auto RotationAxis(T angle) {
|
||||
return Rotation3DAxisBuilder(angle, Axis);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Rotates around the X axis according to the right (left) hand rule in right (left) handed systems. </summary>
|
||||
/// <param name="angle"> Angle of rotation in radians. </param>
|
||||
template <class T>
|
||||
auto RotationX(T angle) {
|
||||
return RotationAxis<0>(angle);
|
||||
}
|
||||
|
||||
/// <summary> Rotates around the Y axis according to the right (left) hand rule in right (left) handed systems. </summary>
|
||||
/// <param name="angle"> Angle of rotation in radians. </param>
|
||||
template <class T>
|
||||
auto RotationY(T angle) {
|
||||
return RotationAxis<1>(angle);
|
||||
}
|
||||
|
||||
/// <summary> Rotates around the Z axis according to the right (left) hand rule in right (left) handed systems. </summary>
|
||||
/// <param name="angle"> Angle of rotation in radians. </param>
|
||||
template <class T>
|
||||
auto RotationZ(T angle) {
|
||||
return RotationAxis<2>(angle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T>
|
||||
class Rotation3DTriAxisBuilder {
|
||||
public:
|
||||
Rotation3DTriAxisBuilder(const std::array<T, 3>& angles, std::array<int, 3> axes) : angles(angles), axes(axes) {}
|
||||
Rotation3DTriAxisBuilder& operator=(const Rotation3DTriAxisBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 4, 4, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 4, 4, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 3, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 3, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, bool QPacked>
|
||||
operator Quaternion<U, QPacked>() const;
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
using MatT = Matrix<U, 3, 3, Order, Layout, MPacked>;
|
||||
if constexpr (Order == eMatrixOrder::FOLLOW_VECTOR) {
|
||||
m.template Submatrix<3, 3>(0, 0) = MatT(RotationAxis(angles[0], axes[0])) * MatT(RotationAxis(angles[1], axes[1])) * MatT(RotationAxis(angles[2], axes[2]));
|
||||
}
|
||||
else {
|
||||
m.template Submatrix<3, 3>(0, 0) = MatT(RotationAxis(angles[2], axes[2])) * MatT(RotationAxis(angles[1], axes[1])) * MatT(RotationAxis(angles[0], axes[0]));
|
||||
}
|
||||
|
||||
// Rest
|
||||
for (int j = 0; j < m.ColumnCount(); ++j) {
|
||||
for (int i = (j < 3 ? 3 : 0); i < m.RowCount(); ++i) {
|
||||
m(i, j) = U(j == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<T, 3> angles;
|
||||
const std::array<int, 3> axes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// <summary> Rotates around three axes in succession. </summary>
|
||||
/// <remarks> Axes: 0 for X, 1 for Y and 2 for Z.
|
||||
/// Angles in radians. Each rotation according to the right (and left) hand rule in right (and left) handed systems. </remarks>
|
||||
template <int FirstAxis, int SecondAxis, int ThirdAxis, class T>
|
||||
auto RotationAxis3(T angle0, T angle1, T angle2) {
|
||||
return Rotation3DTriAxisBuilder(std::array<T, 3>{ angle0, angle1, angle2 }, std::array<int, 3>{ FirstAxis, SecondAxis, ThirdAxis });
|
||||
}
|
||||
|
||||
/// <summary> Rotation matrix from Euler angles. Rotations are Z-X-Z. </summary>
|
||||
/// <param name="z1"> Angle of the first rotation around Z in radians. </param>
|
||||
/// <param name="x2"> Angle of the second rotation around X in radians. </param>
|
||||
/// <param name="z3"> Angle of the third rotation around Z in radians. </param>
|
||||
/// <remarks> Each rotation according to the right (and left) hand rule in right (and left) handed systems. </remarks>
|
||||
template <class T>
|
||||
auto RotationEuler(T z1, T x2, T z3) {
|
||||
return RotationAxis3<2, 0, 2>(z1, x2, z3);
|
||||
}
|
||||
|
||||
/// <summary> Rotation matrix from roll-pitch-yaw angles. Rotations are X-Y-Z. </summary>
|
||||
/// <param name="z1"> Angle of the first rotation around X in radians. </param>
|
||||
/// <param name="x2"> Angle of the second rotation around Y in radians. </param>
|
||||
/// <param name="z3"> Angle of the third rotation around Z in radians. </param>
|
||||
/// /// <remarks> Each rotation according to the right (and left) hand rule in right (and left) handed systems. </remarks>
|
||||
template <class T>
|
||||
auto RotationRPY(T x1, T y2, T z3) {
|
||||
return RotationAxis3<0, 1, 2>(x1, y2, z3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, bool Packed>
|
||||
class Rotation3DAxisAngleBuilder {
|
||||
public:
|
||||
Rotation3DAxisAngleBuilder(const Vector<T, 3, Packed>& axis, T angle) : axis(axis), angle(angle) {}
|
||||
Rotation3DAxisAngleBuilder& operator=(const Rotation3DAxisAngleBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 4, 4, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 4, 4, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 3, Order, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 3, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 3, 4, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, 4, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, bool QPacked>
|
||||
operator Quaternion<U, QPacked>() const;
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
assert(IsNormalized(axis));
|
||||
|
||||
T C = cos(angle);
|
||||
T S = sin(angle);
|
||||
|
||||
// 3x3 rotation sub-matrix
|
||||
using RotMat = Matrix<U, 3, 3, eMatrixOrder::FOLLOW_VECTOR, Layout, Packed>;
|
||||
Matrix<U, 3, 1, eMatrixOrder::FOLLOW_VECTOR, Layout, Packed> u(axis(0), axis(1), axis(2));
|
||||
RotMat cross = {
|
||||
U(0), -u(2), u(1),
|
||||
u(2), U(0), -u(0),
|
||||
-u(1), u(0), U(0)
|
||||
};
|
||||
RotMat rot = C * RotMat(Identity()) + S * cross + (1 - C) * (u * Transpose(u));
|
||||
|
||||
|
||||
// Elements
|
||||
auto elem = [&m](int i, int j) -> U& {
|
||||
return Order == eMatrixOrder::PRECEDE_VECTOR ? m(i, j) : m(j, i);
|
||||
};
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
elem(i, j) = rot(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
// Rest
|
||||
for (int j = 0; j < m.Width(); ++j) {
|
||||
for (int i = (j < 3 ? 3 : 0); i < m.Height(); ++i) {
|
||||
m(i, j) = U(j == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<T, 3, Packed> axis;
|
||||
const T angle;
|
||||
};
|
||||
|
||||
/// <summary> Rotates around an arbitrary axis. </summary>
|
||||
/// <param name="axis"> Axis of rotation, must be normalized. </param>
|
||||
/// <param name="angle"> Angle of rotation in radians. </param>
|
||||
/// <remarks> Right-hand (left-hand) rule is followed in right-handed (left-handed) systems. </remarks>
|
||||
template <class T, bool Vpacked, class U>
|
||||
auto RotationAxisAngle(const Vector<T, 3, Vpacked>& axis, U angle) {
|
||||
return Rotation3DAxisAngleBuilder(axis, T(angle));
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Determines if the matrix is a proper rotation matrix. </summary>
|
||||
/// <remarks> Proper rotation matrices are orthogonal and have a determinant of +1. </remarks>
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
bool IsRotationMatrix3D(const Matrix<T, Rows, Columns, Order, Layout, Packed>& m) {
|
||||
static_assert(Rows == 3 || Rows == 4);
|
||||
static_assert(Columns == 3 || Columns == 4);
|
||||
Vector<T, 3> rows[3] = {
|
||||
{ m(0, 0), m(0, 1), m(0, 2) },
|
||||
{ m(1, 0), m(1, 1), m(1, 2) },
|
||||
{ m(2, 0), m(2, 1), m(2, 2) },
|
||||
};
|
||||
return (std::abs(Dot(rows[0], rows[1])) + std::abs(Dot(rows[0], rows[2])) + std::abs(Dot(rows[1], rows[2]))) < T(0.0005) // rows are orthogonal to each other
|
||||
&& IsNormalized(rows[0]) && IsNormalized(rows[1]) && IsNormalized(rows[2]) // all rows are normalized
|
||||
&& Determinant(Matrix<T, 3, 3, Order, Layout, Packed>(m.template Submatrix<3, 3>(0, 0))) > 0; // not an improper rotation
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
template <class U, bool QPacked>
|
||||
Rotation3DAxisBuilder<T>::operator Quaternion<U, QPacked>() const {
|
||||
using QuatT = Quaternion<U, QPacked>;
|
||||
switch (axis) {
|
||||
case 0: return QuatT(RotationAxisAngle(Vector<U, 3, QPacked>(1, 0, 0), angle));
|
||||
case 1: return QuatT(RotationAxisAngle(Vector<U, 3, QPacked>(0, 1, 0), angle));
|
||||
case 2: return QuatT(RotationAxisAngle(Vector<U, 3, QPacked>(0, 0, 1), angle));
|
||||
}
|
||||
assert(false);
|
||||
throw std::invalid_argument("Axis must be 0, 1, or 2.");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <class U, bool QPacked>
|
||||
Rotation3DTriAxisBuilder<T>::operator Quaternion<U, QPacked>() const {
|
||||
using QuatT = Quaternion<U, QPacked>;
|
||||
return QuatT(RotationAxis(angles[2], axes[2])) * QuatT(RotationAxis(angles[1], axes[1])) * QuatT(RotationAxis(angles[0], axes[0]));
|
||||
}
|
||||
|
||||
template <class T, bool Packed>
|
||||
template <class U, bool QPacked>
|
||||
Rotation3DAxisAngleBuilder<T, Packed>::operator Quaternion<U, QPacked>() const {
|
||||
auto halfAngle = U(angle) * U(0.5);
|
||||
return Quaternion(std::cos(halfAngle), Vector<U, 3, QPacked>(axis) * std::sin(halfAngle));
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,88 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
#include "IdentityBuilder.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class ScaleBuilder {
|
||||
public:
|
||||
ScaleBuilder(const Vector<T, Dim, Packed>& scale) : scale(scale) {}
|
||||
ScaleBuilder& operator=(const ScaleBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim, Dim, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Dim, Dim, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
m = Identity();
|
||||
int i;
|
||||
for (i = 0; i < scale.Dimension(); ++i) {
|
||||
m(i, i) = std::move(scale(i));
|
||||
}
|
||||
for (; i < std::min(Rows, Columns); ++i) {
|
||||
m(i, i) = T(1);
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<T, Dim, Packed> scale;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a scaling matrix. </summary>
|
||||
/// <param name="scale"> A vector containing the scales of respective axes. </summary>
|
||||
/// <remarks> The vector's dimension must be less than or equal to the matrix dimension. </remarks>
|
||||
template <class Vt, int Vdim, bool Vpacked>
|
||||
auto Scale(const Vector<Vt, Vdim, Vpacked>& scale) {
|
||||
return ScaleBuilder{ scale };
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates a scaling matrix. </summary>
|
||||
/// <param name="scales"> A list of scalars corresponding to scaling on respective axes. </summary>
|
||||
/// <remarks> The number of arguments must be less than or equal to the matrix dimension. </remarks>
|
||||
template <class... Args, typename std::enable_if<(traits::All<traits::IsScalar, typename std::decay<Args>::type...>::value), int>::type = 0>
|
||||
auto Scale(Args&&... scales) {
|
||||
using PromotedT = decltype((0 + ... + scales));
|
||||
return ScaleBuilder{ Vector<PromotedT, sizeof...(scales)>(scales...) };
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,63 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T>
|
||||
class ShearBuilder {
|
||||
public:
|
||||
ShearBuilder(T slope, int principalAxis, int modulatorAxis) : slope(slope), principalAxis(principalAxis), modulatorAxis(modulatorAxis) {}
|
||||
ShearBuilder& operator=(const ShearBuilder&) = delete;
|
||||
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Rows, Columns, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Rows, Columns, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
assert(principalAxis != modulatorAxis);
|
||||
m = Identity();
|
||||
if constexpr (Order == eMatrixOrder::FOLLOW_VECTOR) {
|
||||
assert(modulatorAxis < Rows);
|
||||
assert(principalAxis < Columns);
|
||||
m(modulatorAxis, principalAxis) = slope;
|
||||
}
|
||||
else {
|
||||
assert(principalAxis < Rows);
|
||||
assert(modulatorAxis < Columns);
|
||||
m(principalAxis, modulatorAxis) = slope;
|
||||
}
|
||||
}
|
||||
|
||||
const T slope;
|
||||
const int principalAxis;
|
||||
const int modulatorAxis;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a shear matrix. </summary>
|
||||
/// <param name="slope"> Strength of the shear. </param>
|
||||
/// <param name="principalAxis"> Points are moved along this axis. </param>
|
||||
/// <param name="modulatorAxis"> The displacement of points is proportional to this coordinate's value. </param>
|
||||
/// <remarks> The formula for displacement along the pricipal axis is
|
||||
/// <paramref name="slope"/>*pos[<paramref name="modulatorAxis"/>]. </remarks>
|
||||
template <class T>
|
||||
auto Shear(T slope, int principalAxis, int modulatorAxis) {
|
||||
return ShearBuilder(slope, principalAxis, modulatorAxis);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,79 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
#include "IdentityBuilder.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class TranslationBuilder {
|
||||
public:
|
||||
TranslationBuilder(const Vector<T, Dim, Packed>& translation) : translation(translation) {}
|
||||
TranslationBuilder& operator=(const TranslationBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& m) const {
|
||||
m = Identity();
|
||||
if constexpr (Order == eMatrixOrder::FOLLOW_VECTOR) {
|
||||
for (int i = 0; i < translation.Dimension(); ++i) {
|
||||
m(Rows - 1, i) = U(translation(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < translation.Dimension(); ++i) {
|
||||
m(i, Columns - 1) = U(translation(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<T, Dim, Packed> translation;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a translation matrix. </summary>
|
||||
/// <param name="translation"> The movement vector. </param>
|
||||
template <class T, int Dim, bool Packed>
|
||||
auto Translation(const Vector<T, Dim, Packed>& translation) {
|
||||
return TranslationBuilder{ translation };
|
||||
}
|
||||
|
||||
/// <summary> Creates a translation matrix. </summary>
|
||||
/// <param name="coordinates"> A list of scalars that specify movement along repsective axes. </param>
|
||||
template <class... Args, typename std::enable_if<(traits::All<traits::IsScalar, typename std::decay<Args>::type...>::value), int>::type = 0>
|
||||
auto Translation(const Args&... coordinates) {
|
||||
using PromotedT = decltype((0 + ... + coordinates));
|
||||
return TranslationBuilder{ Vector<PromotedT, sizeof...(coordinates)>(coordinates...) };
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,159 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "../Vector.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class ViewBuilder {
|
||||
using VectorT = Vector<T, Dim, Packed>;
|
||||
|
||||
public:
|
||||
ViewBuilder(const VectorT& eye, const VectorT& target, const std::array<VectorT, size_t(Dim - 2)>& bases, const std::array<bool, Dim>& flipAxes)
|
||||
: eye(eye), target(target), bases(bases), flipAxes(flipAxes) {}
|
||||
ViewBuilder& operator=(const ViewBuilder&) = delete;
|
||||
|
||||
template <class U, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim + 1, Order, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim, Dim + 1, eMatrixOrder::PRECEDE_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class U, eMatrixLayout Layout, bool MPacked>
|
||||
operator Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked>() const {
|
||||
Matrix<U, Dim + 1, Dim, eMatrixOrder::FOLLOW_VECTOR, Layout, MPacked> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template <class U, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool MPacked>
|
||||
void Set(Matrix<U, Rows, Columns, Order, Layout, MPacked>& matrix) const {
|
||||
VectorT columns[Dim];
|
||||
std::array<const VectorT*, Dim - 1> crossTable = {};
|
||||
for (int i = 0; i < (int)bases.size(); ++i) {
|
||||
crossTable[i] = &bases[i];
|
||||
}
|
||||
crossTable.back() = &columns[Dim - 1];
|
||||
auto elem = [&matrix](int i, int j) -> U& {
|
||||
return Order == eMatrixOrder::FOLLOW_VECTOR ? matrix(i, j) : matrix(j, i);
|
||||
};
|
||||
|
||||
// calculate columns of the rotation matrix
|
||||
int j = Dim - 1;
|
||||
columns[j] = Normalize(eye - target); // right-handed: camera look towards -Z
|
||||
do {
|
||||
--j;
|
||||
|
||||
columns[Dim - j - 2] = Normalize(Cross(crossTable));
|
||||
|
||||
// shift bases
|
||||
for (int s = 0; s < j; ++s) {
|
||||
crossTable[s] = crossTable[s + 1];
|
||||
}
|
||||
crossTable[j] = &columns[Dim - j - 2];
|
||||
} while (j > 0);
|
||||
|
||||
// flip columns
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
if (flipAxes[i]) {
|
||||
columns[i] *= -T(1);
|
||||
}
|
||||
}
|
||||
|
||||
// copy columns to matrix
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
for (int j = 0; j < Dim; ++j) {
|
||||
elem(i, j) = columns[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
// calculate translation of the matrix
|
||||
for (int j = 0; j < Dim; ++j) {
|
||||
elem(Dim, j) = -Dot(eye, columns[j]);
|
||||
}
|
||||
|
||||
// clear additional elements
|
||||
constexpr int AuxDim = Rows < Columns ? Rows : Columns;
|
||||
if (AuxDim > Dim) {
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
elem(i, AuxDim - 1) = 0;
|
||||
}
|
||||
elem(Dim, AuxDim - 1) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const VectorT eye;
|
||||
const VectorT target;
|
||||
const std::array<VectorT, size_t(Dim - 2)> bases;
|
||||
const std::array<bool, Dim> flipAxes;
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a general, n-dimensional camera look-at matrix. </summary>
|
||||
/// <param name="eye"> The camera's position. </param>
|
||||
/// <param name="target"> The camera's target. </param>
|
||||
/// <param name="bases"> Basis vectors fixing the camera's orientation. </param>
|
||||
/// <param name="flipAxis"> Set any element to true to flip an axis in camera space. </param>
|
||||
/// <remarks> The camera looks down the vector going from <paramref name="eye"/> to
|
||||
/// <paramref name="target"/>, but it can still rotate around that vector. To fix the rotation,
|
||||
/// an "up" vector must be provided in 3 dimensions. In higher dimensions,
|
||||
/// we need multiple up vectors. Unfortunately I can't remember how these
|
||||
/// basis vectors are used, but they are orthogonalized to each-other and to the look vector.
|
||||
/// I can't remember the order of orthogonalization. </remarks>
|
||||
template <class T, int Dim, bool Packed, size_t BaseDim, size_t FlipDim>
|
||||
auto LookAt(const Vector<T, Dim, Packed>& eye,
|
||||
const Vector<T, Dim, Packed>& target,
|
||||
const std::array<Vector<T, Dim, Packed>, BaseDim>& bases,
|
||||
const std::array<bool, FlipDim>& flipAxes) {
|
||||
static_assert(BaseDim == Dim - 2, "You must provide 2 fewer bases than the dimension of the transform.");
|
||||
static_assert(Dim == FlipDim, "You must provide the same number of flips as the dimension of the transform.");
|
||||
return ViewBuilder<T, Dim, Packed>(eye, target, bases, flipAxes);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates a 2D look-at matrix. </summary>
|
||||
/// <param name="eye"> The camera's position. </param>
|
||||
/// <param name="target"> The camera's target. </param>
|
||||
/// <param name="positiveYForward"> True if the camera looks towards +Y in camera space, false if -Y. </param>
|
||||
/// <param name="flipX"> True to flip X in camera space. </param>
|
||||
template <class T, bool Packed>
|
||||
auto LookAt(const Vector<T, 2, Packed>& eye, const Vector<T, 2, Packed>& target, bool positiveYForward, bool flipX) {
|
||||
return LookAt(eye, target, std::array<Vector<T, 2, Packed>, 0>{}, std::array{ flipX, positiveYForward });
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates a 3D look-at matrix. </summary>
|
||||
/// <param name="eye"> The camera's position. </param>
|
||||
/// <param name="target"> The camera's target. </param>
|
||||
/// <param name="up"> Up direction in world space. </param>
|
||||
/// <param name="positiveZForward"> True if the camera looks towards +Z in camera space, false if -Z. </param>
|
||||
/// <param name="flipX"> True to flip X in camera space. </param>
|
||||
/// <param name="flipY"> True to flip Y in camera space. </param>
|
||||
/// <remarks> The camera space X is selected to be orthogonal to both the look direction and the <paramref name="up"/> vector.
|
||||
/// Afterwards, the <paramref name="up"/> vector is re-orthogonalized to the camera-space Z and X vectors. </remarks>
|
||||
template <class T, bool Packed>
|
||||
auto LookAt(const Vector<T, 3, Packed>& eye, const Vector<T, 3, Packed>& target, const Vector<T, 3, Packed>& up, bool positiveZForward, bool flipX, bool flipY) {
|
||||
return LookAt(eye, target, std::array<Vector<T, 3, Packed>, 1>{ up }, std::array<bool, 3>{ flipX, flipY, positiveZForward });
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,44 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../Matrix/MatrixImpl.hpp"
|
||||
#include "ZeroBuilder.hpp"
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
class ZeroBuilder {
|
||||
public:
|
||||
ZeroBuilder() = default;
|
||||
ZeroBuilder& operator=(const ZeroBuilder&) = delete;
|
||||
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
operator Matrix<T, Rows, Columns, Order, Layout, Packed>() const {
|
||||
Matrix<T, Rows, Columns, Order, Layout, Packed> m;
|
||||
Set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T, int Rows, int Columns, eMatrixOrder Order, eMatrixLayout Layout, bool Packed>
|
||||
void Set(Matrix<T, Rows, Columns, Order, Layout, Packed>& m) const {
|
||||
for (auto& stripe : m.stripes) {
|
||||
Fill(stripe, T(0));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Creates a matrix with all elements zero. </summary>
|
||||
inline auto Zero() {
|
||||
return ZeroBuilder{};
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,98 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mathter {
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
class Vector;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public utility stuff
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Common mathematical constants
|
||||
|
||||
/// <summary> Accurate mathematical constants. </summary>
|
||||
template <class Scalar>
|
||||
class Constants {
|
||||
public:
|
||||
static constexpr Scalar Pi = (Scalar)3.1415926535897932384626433832795028841971693993751;
|
||||
static constexpr Scalar PiHalf = (Scalar)1.5707963267948966192313216916397514420985846996876;
|
||||
static constexpr Scalar PiFourth = (Scalar)0.78539816339744830961566084581987572104929234984378;
|
||||
static constexpr Scalar E = (Scalar)2.7182818284590452353602874713526624977572470937;
|
||||
static constexpr Scalar Sqrt2 = (Scalar)1.4142135623730950488016887242096980785696718753769;
|
||||
static constexpr Scalar SqrtHalf = (Scalar)0.70710678118654752440084436210484903928483593768847;
|
||||
};
|
||||
|
||||
|
||||
// Radians and degrees
|
||||
|
||||
/// <summary> Converts radians to degrees. </summary>
|
||||
template <class Scalar>
|
||||
auto Rad2Deg(Scalar rad) {
|
||||
using ComputeT = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, long double>::type;
|
||||
return rad / Constants<ComputeT>::Pi * ComputeT(180);
|
||||
}
|
||||
|
||||
/// <summary> Converts degrees to radians. </summary>
|
||||
template <class Scalar>
|
||||
auto Deg2Rad(Scalar deg) {
|
||||
using ComputeT = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, long double>::type;
|
||||
return deg / ComputeT(180) * Constants<ComputeT>::Pi;
|
||||
}
|
||||
|
||||
|
||||
// Clamp and saturate
|
||||
|
||||
/// <summary> Limits arg to the range [lower, upper], making it either lower or upper if out of range. </summary>
|
||||
template <class Scalar>
|
||||
Scalar Clamp(Scalar arg, Scalar lower, Scalar upper) {
|
||||
return std::max(lower, std::min(upper, arg));
|
||||
}
|
||||
|
||||
/// <summary> Clamps all elements of the vector according to the scalar clamp. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Clamp(const Vector<T, Dim, Packed>& arg, T lower, T upper);
|
||||
|
||||
/// <summary> Clamps argument into range [0,1]. </summary>
|
||||
template <class Scalar>
|
||||
Scalar Saturate(Scalar arg) {
|
||||
return Clamp(arg, Scalar(0), Scalar(1));
|
||||
}
|
||||
|
||||
/// <summary> Clamps all elements into range [0,1]. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Saturate(const Vector<T, Dim, Packed>& arg);
|
||||
|
||||
|
||||
} // namespace mathter
|
||||
|
||||
|
||||
#include "Vector.hpp"
|
||||
|
||||
// Implementations of vector clamp functions.
|
||||
namespace mathter {
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Clamp(const Vector<T, Dim, Packed>& arg, T lower, T upper) {
|
||||
Vector<T, Dim, Packed> ret;
|
||||
for (int i = 0; i < arg.Dimension(); ++i) {
|
||||
ret(i) = Clamp(arg(i), lower, upper);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Saturate(const Vector<T, Dim, Packed>& arg) {
|
||||
return Clamp(arg, T(0), T(1));
|
||||
}
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,12 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Vector/VectorArithmetic.hpp"
|
||||
#include "Vector/VectorCompare.hpp"
|
||||
#include "Vector/VectorConcat.hpp"
|
||||
#include "Vector/VectorFunction.hpp"
|
||||
#include "Vector/VectorImpl.hpp"
|
|
@ -0,0 +1,450 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VectorImpl.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Utility
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Dim, bool Packed, class Fun, size_t... Indices>
|
||||
inline auto DoElementwiseOp(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs, Fun&& fun, std::index_sequence<Indices...>) {
|
||||
return Vector<T, Dim, Packed>{ fun(lhs[Indices], rhs[Indices])... };
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, class Fun>
|
||||
inline auto DoBinaryOp(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs, Fun&& fun) {
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
using B = Batch<T, Dim, Packed>;
|
||||
return Vector<T, Dim, Packed>{ fun(B::load_unaligned(lhs.data()), B::load_unaligned(rhs.data())) };
|
||||
}
|
||||
else {
|
||||
return DoElementwiseOp(lhs, rhs, fun, std::make_index_sequence<Dim>{});
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, class S, class Fun, size_t... Indices, std::enable_if_t<!traits::IsVector<S>::value, int> = 0>
|
||||
inline auto DoElementwiseOp(const Vector<T, Dim, Packed>& lhs, const S& rhs, Fun&& fun, std::index_sequence<Indices...>) {
|
||||
using R = std::common_type_t<T, S>;
|
||||
return Vector<R, Dim, Packed>{ fun(R(lhs[Indices]), R(rhs))... };
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, class S, class Fun, std::enable_if_t<!traits::IsVector<S>::value, int> = 0>
|
||||
inline auto DoBinaryOp(const Vector<T, Dim, Packed>& lhs, const S& rhs, Fun&& fun) {
|
||||
using R = std::common_type_t<T, S>;
|
||||
if constexpr (IsBatched<R, Dim, Packed>()
|
||||
&& IsBatched<T, Dim, Packed>()
|
||||
&& std::is_convertible_v<Batch<T, Dim, Packed>, Batch<R, Dim, Packed>>) {
|
||||
using TB = Batch<T, Dim, Packed>;
|
||||
using RB = Batch<T, Dim, Packed>;
|
||||
return Vector<T, Dim, Packed>{ fun(RB(TB::load_unaligned(lhs.data())), RB(R(rhs))) };
|
||||
}
|
||||
else {
|
||||
return DoElementwiseOp(lhs, rhs, fun, std::make_index_sequence<Dim>{});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Vector arithmetic
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Elementwise (Hadamard) vector product. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline auto operator*(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::multiplies{});
|
||||
}
|
||||
|
||||
/// <summary> Elementwise vector division. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed> operator/(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::divides{});
|
||||
}
|
||||
|
||||
/// <summary> Elementwise vector addition. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed> operator+(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::plus{});
|
||||
}
|
||||
|
||||
/// <summary> Elementwise vector subtraction. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed> operator-(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::minus{});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Vector assign arithmetic
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Elementwise (Hadamard) vector product. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed>& operator*=(Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return lhs = lhs * rhs;
|
||||
}
|
||||
|
||||
/// <summary> Elementwise vector division. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed>& operator/=(Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return lhs = lhs / rhs;
|
||||
}
|
||||
|
||||
/// <summary> Elementwise vector addition. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed>& operator+=(Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return lhs = lhs + rhs;
|
||||
}
|
||||
|
||||
/// <summary> Elementwise vector subtraction. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed>& operator-=(Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return lhs = lhs - rhs;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Scalar arithmetic
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Scales the vector by <paramref name="rhs"/>. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline auto operator*(const Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::multiplies{});
|
||||
}
|
||||
|
||||
/// <summary> Scales the vector by 1/<paramref name="rhs"/>. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline auto operator/(const Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::divides{});
|
||||
}
|
||||
|
||||
/// <summary> Adds <paramref name="rhs"/> to each element of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline auto operator+(const Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::plus{});
|
||||
}
|
||||
|
||||
/// <summary> Subtracts <paramref name="rhs"/> from each element of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline auto operator-(const Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return DoBinaryOp(lhs, rhs, std::minus{});
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Scales vector by <paramref name="lhs"/>. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed> operator*(U lhs, const Vector<T, Dim, Packed>& rhs) { return rhs * lhs; }
|
||||
/// <summary> Adds <paramref name="lhs"/> to all elements of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed> operator+(U lhs, const Vector<T, Dim, Packed>& rhs) { return rhs + lhs; }
|
||||
/// <summary> Makes a vector with <paramref name="lhs"/> as all elements, then subtracts <paramref name="rhs"> from it. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed> operator-(U lhs, const Vector<T, Dim, Packed>& rhs) { return Vector<T, Dim, Packed>(lhs) - rhs; }
|
||||
/// <summary> Makes a vector with <paramref name="lhs"/> as all elements, then divides it by <paramref name="rhs">. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed> operator/(U lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
Vector<T, Dim, Packed> copy(lhs);
|
||||
copy /= rhs;
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Scalar assign arithmetic
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Scales the vector by <paramref name="rhs"/>. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed>& operator*=(Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return lhs = lhs * rhs;
|
||||
}
|
||||
|
||||
/// <summary> Scales the vector by 1/<paramref name="rhs"/>. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed>& operator/=(Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return lhs = lhs / rhs;
|
||||
}
|
||||
|
||||
/// <summary> Adds <paramref name="rhs"/> to each element of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed>& operator+=(Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return lhs = lhs + rhs;
|
||||
}
|
||||
|
||||
/// <summary> Subtracts <paramref name="rhs"/> from each element of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
inline Vector<T, Dim, Packed>& operator-=(Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
return lhs = lhs - rhs;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Extra
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// <summary> Return (a*b)+c. Performs MAD or FMA if supported by target architecture. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed> MultiplyAdd(const Vector<T, Dim, Packed>& a, const Vector<T, Dim, Packed>& b, const Vector<T, Dim, Packed>& c) {
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
/// <summary> Negates all elements of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed> operator-(const Vector<T, Dim, Packed>& arg) {
|
||||
return arg * T(-1);
|
||||
}
|
||||
|
||||
/// <summary> Optional plus sign, leaves the vector as is. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
inline Vector<T, Dim, Packed> operator+(const Vector<T, Dim, Packed>& arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Swizzle-vector
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator*(const Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return v * Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator/(const Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return v / Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator+(const Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return v + Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator-(const Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return v - Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator*(const Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return Vector<T, Dim, SwPacked>(s) * v;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator/(const Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return Vector<T, Dim, SwPacked>(s) / v;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator+(const Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return Vector<T, Dim, SwPacked>(s) + v;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator-(const Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>> {
|
||||
return Vector<T, Dim, SwPacked>(s) - v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator*=(Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>>& {
|
||||
return v *= Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator/=(Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>>& {
|
||||
return v /= Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator+=(Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>>& {
|
||||
return v += Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator-=(Vector<T, Dim, Packed>& v, const Swizzle<T, SwDim, SwPacked, Indices...>& s) -> std::enable_if_t<Dim == sizeof...(Indices), Vector<T, Dim, Packed>>& {
|
||||
return v -= Vector<T, Dim, SwPacked>(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator*=(Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Swizzle<T, SwDim, SwPacked, Indices...>>& {
|
||||
return s = Vector<T, Dim, SwPacked>(s) * v;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator/=(Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Swizzle<T, SwDim, SwPacked, Indices...>>& {
|
||||
return s = Vector<T, Dim, SwPacked>(s) / v;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator+=(Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Swizzle<T, SwDim, SwPacked, Indices...>>& {
|
||||
return s = Vector<T, Dim, SwPacked>(s) + v;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int SwDim, bool SwPacked, int... Indices>
|
||||
auto operator-=(Swizzle<T, SwDim, SwPacked, Indices...>& s, const Vector<T, Dim, Packed>& v) -> std::enable_if_t<Dim == sizeof...(Indices), Swizzle<T, SwDim, SwPacked, Indices...>>& {
|
||||
return s = Vector<T, Dim, SwPacked>(s) - v;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Swizzle-swizzle
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto operator*(const Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
static_assert(sizeof...(Indices1) == sizeof...(Indices2));
|
||||
constexpr bool Packed = Packed1 && Packed2;
|
||||
using V1 = Vector<T1, sizeof...(Indices1), Packed>;
|
||||
using V2 = Vector<T2, sizeof...(Indices2), Packed>;
|
||||
return V1(s1) * V2(s2);
|
||||
}
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto operator/(const Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
static_assert(sizeof...(Indices1) == sizeof...(Indices2));
|
||||
constexpr bool Packed = Packed1 && Packed2;
|
||||
using V1 = Vector<T1, sizeof...(Indices1), Packed>;
|
||||
using V2 = Vector<T2, sizeof...(Indices2), Packed>;
|
||||
return V1(s1) / V2(s2);
|
||||
}
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto operator+(const Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
static_assert(sizeof...(Indices1) == sizeof...(Indices2));
|
||||
constexpr bool Packed = Packed1 && Packed2;
|
||||
using V1 = Vector<T1, sizeof...(Indices1), Packed>;
|
||||
using V2 = Vector<T2, sizeof...(Indices2), Packed>;
|
||||
return V1(s1) + V2(s2);
|
||||
}
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto operator-(const Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
static_assert(sizeof...(Indices1) == sizeof...(Indices2));
|
||||
constexpr bool Packed = Packed1 && Packed2;
|
||||
using V1 = Vector<T1, sizeof...(Indices1), Packed>;
|
||||
using V2 = Vector<T2, sizeof...(Indices2), Packed>;
|
||||
return V1(s1) - V2(s2);
|
||||
}
|
||||
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto& operator*=(Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
return s1 = s1 * s2;
|
||||
}
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto& operator/=(Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
return s1 = s1 / s2;
|
||||
}
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto& operator+=(Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
return s1 = s1 + s2;
|
||||
}
|
||||
|
||||
template <class T1, int Dim1, bool Packed1, int... Indices1, class T2, int Dim2, bool Packed2, int... Indices2>
|
||||
auto& operator-=(Swizzle<T1, Dim1, Packed1, Indices1...>& s1, const Swizzle<T2, Dim2, Packed2, Indices2...>& s2) {
|
||||
return s1 = s1 - s2;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Swizzle-scalar
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator*(const Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
return VectorT(lhs) * rhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator/(const Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
return VectorT(lhs) / rhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator+(const Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
return VectorT(lhs) + rhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator-(const Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
return VectorT(lhs) - rhs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator*(U lhs, const Swizzle<T, Dim, Packed, Indices...>& rhs) {
|
||||
return rhs * lhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator/(U lhs, const Swizzle<T, Dim, Packed, Indices...>& rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
return lhs / VectorT(rhs);
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator+(U lhs, const Swizzle<T, Dim, Packed, Indices...>& rhs) {
|
||||
return rhs + lhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto operator-(U lhs, const Swizzle<T, Dim, Packed, Indices...>& rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
return lhs - VectorT(rhs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto& operator*=(Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
lhs = VectorT(lhs) * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto& operator/=(Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
lhs = VectorT(lhs) / rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto& operator+=(Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
lhs = VectorT(lhs) + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices, class U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
auto& operator-=(Swizzle<T, Dim, Packed, Indices...>& lhs, U rhs) {
|
||||
using VectorT = Vector<T, sizeof...(Indices), Packed>;
|
||||
lhs = VectorT(lhs) - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,30 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VectorImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
/// <summary> Exactly compares two vectors. </summary>
|
||||
/// <remarks> <The usual warning about floating point numbers> </remarks>
|
||||
template <class T, int Dim, bool Packed>
|
||||
bool operator==(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
bool same = lhs[0] == rhs[0];
|
||||
for (int i = 1; i < Dim; ++i) {
|
||||
same = same && lhs[i] == rhs[i];
|
||||
}
|
||||
return same;
|
||||
}
|
||||
|
||||
/// <summary> Exactly compares two vectors. </summary>
|
||||
/// <remarks> <The usual warning about floating point numbers> </remarks>
|
||||
template <class T, int Dim, bool Packed>
|
||||
bool operator!=(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,81 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VectorImpl.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U>
|
||||
mathter::Vector<T, Dim + 1, Packed> operator|(const mathter::Vector<T, Dim, Packed>& lhs, U rhs) {
|
||||
mathter::Vector<T, Dim + 1, Packed> ret;
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
ret(i) = lhs(i);
|
||||
}
|
||||
ret(Dim) = rhs;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class T1, int Dim1, class T2, int Dim2, bool Packed>
|
||||
mathter::Vector<T1, Dim1 + Dim2, Packed> operator|(const mathter::Vector<T1, Dim1, Packed>& lhs, const mathter::Vector<T2, Dim2, Packed>& rhs) {
|
||||
mathter::Vector<T1, Dim1 + Dim2, Packed> ret;
|
||||
for (int i = 0; i < Dim1; ++i) {
|
||||
ret(i) = lhs(i);
|
||||
}
|
||||
for (int i = 0; i < Dim2; ++i) {
|
||||
ret(Dim1 + i) = rhs(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class T, int Dim, bool Packed, class U>
|
||||
mathter::Vector<T, Dim + 1, Packed> operator|(U lhs, const mathter::Vector<T, Dim, Packed>& rhs) {
|
||||
mathter::Vector<T, Dim + 1, Packed> ret;
|
||||
ret(0) = lhs;
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
ret(i + 1) = rhs(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class VectorData1, int... Indices1, class VectorData2, int... Indices2>
|
||||
auto operator|(const Swizzle<VectorData1, Indices1...>& lhs, const Swizzle<VectorData2, Indices2...>& rhs) {
|
||||
using TS1 = typename traits::VectorTraits<VectorData1>::Type;
|
||||
using TS2 = typename traits::VectorTraits<VectorData2>::Type;
|
||||
return Vector<TS1, sizeof...(Indices1), false>(lhs) | Vector<TS2, sizeof...(Indices2), false>(rhs);
|
||||
}
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class VectorData1, int... Indices1, class T2, int Dim, bool Packed>
|
||||
auto operator|(const Swizzle<VectorData1, Indices1...>& lhs, const Vector<T2, Dim, Packed>& rhs) {
|
||||
using TS = typename traits::VectorTraits<VectorData1>::Type;
|
||||
return Vector<TS, sizeof...(Indices1), Packed>(lhs) | rhs;
|
||||
}
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class VectorData1, int... Indices1, class T2, int Dim, bool Packed>
|
||||
auto operator|(const Vector<T2, Dim, Packed>& lhs, const Swizzle<VectorData1, Indices1...>& rhs) {
|
||||
using TS = typename traits::VectorTraits<VectorData1>::Type;
|
||||
return lhs | Vector<TS, sizeof...(Indices1), false>(rhs);
|
||||
}
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class VectorData1, int... Indices1, class U>
|
||||
auto operator|(const Swizzle<VectorData1, Indices1...>& lhs, U rhs) {
|
||||
using TS = typename traits::VectorTraits<VectorData1>::Type;
|
||||
return Vector<TS, sizeof...(Indices1), false>(lhs) | rhs;
|
||||
}
|
||||
/// <summary> Concatenates the arguments, and returns the concatenated vector. </summary>
|
||||
template <class VectorData1, int... Indices1, class U>
|
||||
auto operator|(U lhs, const Swizzle<VectorData1, Indices1...>& rhs) {
|
||||
using TS = typename traits::VectorTraits<VectorData1>::Type;
|
||||
return lhs | Vector<TS, sizeof...(Indices1), false>(rhs);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,264 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#if _MSC_VER && defined(min)
|
||||
#pragma push_macro("min")
|
||||
#pragma push_macro("max")
|
||||
#undef min
|
||||
#undef max
|
||||
#define MATHTER_MINMAX
|
||||
#endif
|
||||
|
||||
|
||||
#include "../Common/MathUtil.hpp"
|
||||
#include "VectorImpl.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
/// <summary> Returns true if the vector's length is too small for precise calculations (i.e. normalization). </summary>
|
||||
/// <remarks> "Too small" means smaller than the square root of the smallest number representable by the underlying scalar.
|
||||
/// This value is ~10^-18 for floats and ~10^-154 for doubles. </remarks>
|
||||
template <class T, int Dim, bool Packed>
|
||||
bool IsNullvector(const Vector<T, Dim, Packed>& v) {
|
||||
static constexpr T epsilon = T(1) / impl::ConstexprExp10<T>(impl::ConstexprAbs(std::numeric_limits<T>::min_exponent10) / 2);
|
||||
T length = Length(v);
|
||||
return length < epsilon;
|
||||
}
|
||||
|
||||
/// <summary> Returns the squared length of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
T LengthSquared(const Vector<T, Dim, Packed>& v) {
|
||||
return Dot(v, v);
|
||||
}
|
||||
|
||||
/// <summary> Returns the length of the vector. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
T Length(const Vector<T, Dim, Packed>& v) {
|
||||
return (T)std::sqrt((T)LengthSquared(v));
|
||||
}
|
||||
|
||||
/// <summary> Returns the length of the vector, avoids overflow and underflow, so it's more expensive. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
T LengthPrecise(const Vector<T, Dim, Packed>& v) {
|
||||
T maxElement = std::abs(v(0));
|
||||
for (int i = 1; i < v.Dimension(); ++i) {
|
||||
maxElement = std::max(maxElement, std::abs(v(i)));
|
||||
}
|
||||
if (maxElement == T(0)) {
|
||||
return T(0);
|
||||
}
|
||||
auto scaled = v / maxElement;
|
||||
return std::sqrt(Dot(scaled, scaled)) * maxElement;
|
||||
}
|
||||
|
||||
/// <summary> Returns the euclidean distance between to vectors. </summary>
|
||||
template <class T, class U, int Dim, bool Packed1, bool Packed2>
|
||||
auto Distance(const Vector<T, Dim, Packed1>& lhs, const Vector<U, Dim, Packed2>& rhs) {
|
||||
return Length(lhs - rhs);
|
||||
}
|
||||
|
||||
/// <summary> Makes a unit vector, but keeps direction. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Normalize(const Vector<T, Dim, Packed>& v) {
|
||||
assert(!IsNullvector(v));
|
||||
T l = Length(v);
|
||||
return v / l;
|
||||
}
|
||||
|
||||
/// <summary> Checks if the vector is unit vector. There's some tolerance due to floating points. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
bool IsNormalized(const Vector<T, Dim, Packed>& v) {
|
||||
T n = LengthSquared(v);
|
||||
return T(0.9999) <= n && n <= T(1.0001);
|
||||
}
|
||||
|
||||
/// <summary> Makes a unit vector, but keeps direction. Leans towards (1,0,0...) for nullvectors, costs more. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> SafeNormalize(const Vector<T, Dim, Packed>& v) {
|
||||
Vector<T, Dim, Packed> vmod = v;
|
||||
vmod(0) = std::abs(v(0)) > std::numeric_limits<T>::denorm_min() ? v(0) : std::numeric_limits<T>::denorm_min();
|
||||
T l = LengthPrecise(vmod);
|
||||
return vmod / l;
|
||||
}
|
||||
|
||||
/// <summary> Makes a unit vector, but keeps direction. Leans towards <paramref name="degenerate"/> for nullvectors, costs more. </summary>
|
||||
/// <param name="degenerate"> Must be a unit vector. </param>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> SafeNormalize(const Vector<T, Dim, Packed>& v, const Vector<T, Dim, Packed>& degenerate) {
|
||||
assert(IsNormalized(degenerate));
|
||||
T length = LengthPrecise(v);
|
||||
if (length == 0) {
|
||||
return degenerate;
|
||||
}
|
||||
return v / length;
|
||||
}
|
||||
|
||||
/// <summary> Sets all elements of the vector to the same value. </summary>
|
||||
template <class T, int Dim, bool Packed, class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
void Fill(Vector<T, Dim, Packed>& lhs, U all) {
|
||||
lhs = Vector<T, Dim, Packed>((T)all);
|
||||
}
|
||||
|
||||
/// <summary> Calculates the scalar product (dot product) of the two arguments. </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
T Dot(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
#if MATHTER_USE_XSIMD
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
struct G {
|
||||
static constexpr bool get(unsigned idx, unsigned size) noexcept {
|
||||
return idx < Dim;
|
||||
}
|
||||
};
|
||||
using B = Batch<T, Dim, Packed>;
|
||||
const auto lhsv = B::load_unaligned(lhs.data());
|
||||
const auto rhsv = B::load_unaligned(rhs.data());
|
||||
const auto zeros = B{ T(0) };
|
||||
const auto mask = xsimd::make_batch_bool_constant<B, G>();
|
||||
const auto lhsvm = xsimd::select(mask, lhsv, zeros);
|
||||
const auto rhsvm = xsimd::select(mask, rhsv, zeros);
|
||||
const auto prod = lhsvm * rhsvm;
|
||||
return xsimd::reduce_add(prod);
|
||||
}
|
||||
#endif
|
||||
return std::inner_product(lhs.begin(), lhs.end(), rhs.begin(), T(0));
|
||||
}
|
||||
|
||||
/// <summary> Returns the generalized cross-product in N dimensions. </summary>
|
||||
/// <remarks> You must supply N-1 arguments of type Vector<N>.
|
||||
/// The function returns the generalized cross product as defined by
|
||||
/// https://en.wikipedia.org/wiki/Cross_product#Multilinear_algebra. </remarks>
|
||||
template <class T, int Dim, bool Packed, class... Args>
|
||||
auto Cross(const Vector<T, Dim, Packed>& head, Args&&... args) -> Vector<T, Dim, Packed>;
|
||||
|
||||
|
||||
/// <summary> Returns the generalized cross-product in N dimensions. </summary>
|
||||
/// <remarks> See https://en.wikipedia.org/wiki/Cross_product#Multilinear_algebra for definition. </remarks>
|
||||
template <class T, int Dim, bool Packed>
|
||||
auto Cross(const std::array<const Vector<T, Dim, Packed>*, Dim - 1>& args) -> Vector<T, Dim, Packed>;
|
||||
|
||||
/// <summary> Returns the 2-dimensional cross prodct, which is a vector perpendicular to the argument. </summary>
|
||||
template <class T, bool Packed>
|
||||
Vector<T, 2, Packed> Cross(const Vector<T, 2, Packed>& arg) {
|
||||
return Vector<T, 2, Packed>(-arg.y,
|
||||
arg.x);
|
||||
}
|
||||
/// <summary> Returns the 2-dimensional cross prodct, which is a vector perpendicular to the argument. </summary>
|
||||
template <class T, bool Packed>
|
||||
Vector<T, 2, Packed> Cross(const std::array<const Vector<T, 2, Packed>*, 1>& arg) {
|
||||
return Cross(*(arg[0]));
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the 3-dimensional cross-product. </summary>
|
||||
template <class T, bool Packed>
|
||||
Vector<T, 3, Packed> Cross(const Vector<T, 3, Packed>& lhs, const Vector<T, 3, Packed>& rhs) {
|
||||
using VecT = Vector<T, 3, Packed>;
|
||||
return VecT(lhs.yzx) * VecT(rhs.zxy) - VecT(lhs.zxy) * VecT(rhs.yzx);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the 3-dimensional cross-product. </summary>
|
||||
template <class T, bool Packed>
|
||||
Vector<T, 3, Packed> Cross(const std::array<const Vector<T, 3, Packed>*, 2>& args) {
|
||||
return Cross(*(args[0]), *(args[1]));
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the element-wise minimum of arguments </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Min(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
#if MATHTER_USE_XSIMD
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
using B = Batch<T, Dim, Packed>;
|
||||
const auto lhsv = B::load_unaligned(lhs.data());
|
||||
const auto rhsv = B::load_unaligned(rhs.data());
|
||||
return Vector<T, Dim, Packed>{ xsimd::min(lhsv, rhsv) };
|
||||
}
|
||||
#endif
|
||||
Vector<T, Dim, Packed> res;
|
||||
for (int i = 0; i < lhs.Dimension(); ++i) {
|
||||
res[i] = std::min(lhs[i], rhs[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the element-wise maximum of arguments </summary>
|
||||
template <class T, int Dim, bool Packed>
|
||||
Vector<T, Dim, Packed> Max(const Vector<T, Dim, Packed>& lhs, const Vector<T, Dim, Packed>& rhs) {
|
||||
#if MATHTER_USE_XSIMD
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
using B = Batch<T, Dim, Packed>;
|
||||
const auto lhsv = B::load_unaligned(lhs.data());
|
||||
const auto rhsv = B::load_unaligned(rhs.data());
|
||||
return Vector<T, Dim, Packed>{ xsimd::max(lhsv, rhsv) };
|
||||
}
|
||||
#endif
|
||||
Vector<T, Dim, Packed> res;
|
||||
for (int i = 0; i < lhs.Dimension(); ++i) {
|
||||
res[i] = std::max(lhs[i], rhs[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
} // namespace mathter
|
||||
|
||||
|
||||
|
||||
// Generalized cross-product unfortunately needs matrix determinant.
|
||||
#include "../Matrix.hpp"
|
||||
|
||||
namespace mathter {
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
auto Cross(const std::array<const Vector<T, Dim, Packed>*, Dim - 1>& args) -> Vector<T, Dim, Packed> {
|
||||
Vector<T, Dim, Packed> result;
|
||||
Matrix<T, Dim - 1, Dim - 1, eMatrixOrder::FOLLOW_VECTOR, eMatrixLayout::ROW_MAJOR, false> detCalc;
|
||||
|
||||
// Calculate elements of result on-by-one
|
||||
int sign = 2 * (Dim % 2) - 1;
|
||||
for (int base = 0; base < result.Dimension(); ++base, sign *= -1) {
|
||||
// Fill up sub-matrix the determinant of which yields the coefficient of base-vector.
|
||||
for (int j = 0; j < base; ++j) {
|
||||
for (int i = 0; i < detCalc.RowCount(); ++i) {
|
||||
detCalc(i, j) = (*(args[i]))[j];
|
||||
}
|
||||
}
|
||||
for (int j = base + 1; j < result.Dimension(); ++j) {
|
||||
for (int i = 0; i < detCalc.RowCount(); ++i) {
|
||||
detCalc(i, j - 1) = (*(args[i]))[j];
|
||||
}
|
||||
}
|
||||
|
||||
T coefficient = T(sign) * Determinant(detCalc);
|
||||
result(base) = coefficient;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, class... Args>
|
||||
auto Cross(const Vector<T, Dim, Packed>& head, Args&&... args) -> Vector<T, Dim, Packed> {
|
||||
static_assert(1 + sizeof...(args) == Dim - 1, "Number of arguments must be (Dimension - 1).");
|
||||
|
||||
std::array<const Vector<T, Dim, Packed>*, Dim - 1> vectors = { &head, &args... };
|
||||
return Cross(vectors);
|
||||
}
|
||||
|
||||
} // namespace mathter
|
||||
|
||||
|
||||
#if defined(MATHTER_MINMAX)
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("max")
|
||||
#endif
|
|
@ -0,0 +1,518 @@
|
|||
// L=============================================================================
|
||||
// L This software is distributed under the MIT license.
|
||||
// L Copyright 2021 Péter Kardos
|
||||
// L=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Common/Definitions.hpp"
|
||||
#include "../Common/DeterministicInitializer.hpp"
|
||||
#include "../Common/Traits.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if MATHTER_USE_XSIMD
|
||||
#include <xsimd/xsimd.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
namespace mathter {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Swizzle
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/// <summary> Enables element swizzling (reordering elements) for vectors. </summary>
|
||||
/// <remarks>
|
||||
/// To access swizzlers, use the xx, xy, xyz and similar elements of vectors.
|
||||
/// Swizzlers can be used with assignments, concatenation, casting and constructors.
|
||||
/// To perform arithmetic, cast swizzlers to corresponding vector type.
|
||||
/// </remarks>
|
||||
template <class T, int Dim, bool Packed, int... Indices>
|
||||
class Swizzle {
|
||||
static constexpr int IndexTable[] = { Indices... };
|
||||
T* data() { return reinterpret_cast<T*>(this); }
|
||||
const T* data() const { return reinterpret_cast<const T*>(this); }
|
||||
|
||||
public:
|
||||
Swizzle(const Swizzle&) = delete;
|
||||
Swizzle(Swizzle&&) = delete;
|
||||
|
||||
/// <summary> Builds the swizzled vector object. </summary>
|
||||
template <class T2, bool Packed2>
|
||||
operator Vector<T2, sizeof...(Indices), Packed2>() const;
|
||||
|
||||
/// <summary> Sets the parent vector's elements from the right-side argument. </summary>
|
||||
/// <remarks>
|
||||
/// Example: b = {1,2,3}; a.yxz = b; -> a contains {2,1,3}.
|
||||
/// You don't have to worry about aliasing (a.xyz = a is totally fine).
|
||||
/// </remarks>
|
||||
template <class T2, bool Packed2>
|
||||
Swizzle& operator=(const Vector<T2, sizeof...(Indices), Packed2>& rhs);
|
||||
|
||||
/// <summary> Sets the parent vector's elements from the right-side argument. </summary>
|
||||
/// <remarks>
|
||||
/// Example: b = {1,2,3}; a.yxz = b.xyz; -> a contains {2,1,3}.
|
||||
/// You don't have to worry about aliasing (a.xyz = a.zyx is totally fine).
|
||||
/// </remarks>
|
||||
template <class T2, int Dim2, bool Packed2, int... Indices2, typename std::enable_if<sizeof...(Indices) == sizeof...(Indices2), int>::type = 0>
|
||||
Swizzle& operator=(const Swizzle<T2, Dim2, Packed2, Indices2...>& rhs) {
|
||||
*this = Vector<T, sizeof...(Indices2), false>(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary> Returns the nth element of the swizzled vector. Example: v.zxy[2] returns y. </summary>
|
||||
T& operator[](int idx) {
|
||||
assert(idx < Dim);
|
||||
return data()[IndexTable[idx]];
|
||||
}
|
||||
/// <summary> Returns the nth element of the swizzled vector. Example: v.zxy[2] returns y. </summary>
|
||||
T operator[](int idx) const {
|
||||
assert(idx < Dim);
|
||||
return data()[IndexTable[idx]];
|
||||
}
|
||||
|
||||
/// <summary> Returns the nth element of the swizzled vector. Example: v.zxy(2) returns y. </summary>
|
||||
T& operator()(int idx) {
|
||||
assert(idx < Dim);
|
||||
return data()[IndexTable[idx]];
|
||||
}
|
||||
/// <summary> Returns the nth element of the swizzled vector. Example: v.zxy(2) returns y. </summary>
|
||||
T operator()(int idx) const {
|
||||
assert(idx < Dim);
|
||||
return data()[IndexTable[idx]];
|
||||
}
|
||||
|
||||
/// <summary> Builds the swizzled vector object. </summary>
|
||||
template <bool Packed2 = false>
|
||||
const auto ToVector() const {
|
||||
return Vector<T, Dim, Packed2>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Vectorization utilities
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
constexpr int ExtendedDim() {
|
||||
if constexpr (!Packed) {
|
||||
if (Dim == 3) {
|
||||
return 4;
|
||||
}
|
||||
if (Dim == 6 || Dim == 7) {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
return Dim;
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
constexpr int IsBatched() {
|
||||
#if MATHTER_USE_XSIMD
|
||||
if constexpr (!Packed) {
|
||||
constexpr auto extendedSize = ExtendedDim<T, Dim, Packed>();
|
||||
using BatchT = typename xsimd::make_sized_batch<T, extendedSize>::type;
|
||||
return !std::is_void_v<BatchT>;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
struct BatchTHelper {
|
||||
static auto GetType() {
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
#if MATHTER_USE_XSIMD
|
||||
using B = typename xsimd::make_sized_batch<T, ExtendedDim<T, Dim, Packed>()>::type;
|
||||
return static_cast<B*>(nullptr);
|
||||
#else
|
||||
return static_cast<void*>(nullptr);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
return static_cast<void*>(nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
using Batch = std::decay_t<std::remove_pointer_t<decltype(BatchTHelper<T, Dim, Packed>::GetType())>>;
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
constexpr int Alignment() {
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
using B = Batch<T, Dim, Packed>;
|
||||
static_assert(!std::is_void_v<B>, "IsBatched should prevent this case from ever happening.");
|
||||
return alignof(B);
|
||||
}
|
||||
return alignof(T);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VectorData
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// General case
|
||||
template <class T, int Dim, bool Packed>
|
||||
struct VectorData {
|
||||
VectorData() {}
|
||||
union {
|
||||
/// <summary> A potentially larger array extended to the next SIMD size. </summary>
|
||||
alignas(Alignment<T, Dim, Packed>()) std::array<T, ExtendedDim<T, Dim, Packed>()> extended;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Small vectors with x,y,z,w members
|
||||
template <class T, bool Packed>
|
||||
struct VectorData<T, 2, Packed> {
|
||||
VectorData() {}
|
||||
VectorData(const VectorData& rhs) {
|
||||
extended = rhs.extended;
|
||||
}
|
||||
VectorData& operator=(const VectorData& rhs) {
|
||||
extended = rhs.extended;
|
||||
return *this;
|
||||
}
|
||||
union {
|
||||
/// <summary> A potentially larger array extended to the next SIMD size. </summary>
|
||||
alignas(Alignment<T, 2, Packed>()) std::array<T, ExtendedDim<T, 2, Packed>()> extended;
|
||||
struct {
|
||||
T x, y;
|
||||
};
|
||||
#define Dim 2
|
||||
#include "../Swizzle/Swizzle_2.inc.hpp"
|
||||
#undef Dim
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <class T, bool Packed>
|
||||
struct VectorData<T, 3, Packed> {
|
||||
VectorData() {}
|
||||
VectorData(const VectorData& rhs) {
|
||||
extended = rhs.extended;
|
||||
}
|
||||
VectorData& operator=(const VectorData& rhs) {
|
||||
extended = rhs.extended;
|
||||
return *this;
|
||||
}
|
||||
union {
|
||||
/// <summary> A potentially larger array extended to the next SIMD size. </summary>
|
||||
alignas(Alignment<T, 3, Packed>()) std::array<T, ExtendedDim<T, 3, Packed>()> extended;
|
||||
struct {
|
||||
T x, y, z;
|
||||
};
|
||||
#define Dim 3
|
||||
#include "../Swizzle/Swizzle_4.inc.hpp"
|
||||
#undef Dim
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <class T, bool Packed>
|
||||
struct VectorData<T, 4, Packed> {
|
||||
VectorData() {}
|
||||
VectorData(const VectorData& rhs) {
|
||||
extended = rhs.extended;
|
||||
}
|
||||
VectorData& operator=(const VectorData& rhs) {
|
||||
extended = rhs.extended;
|
||||
return *this;
|
||||
}
|
||||
union {
|
||||
/// <summary> A potentially larger array extended to the next SIMD size. </summary>
|
||||
alignas(Alignment<T, 4, Packed>()) std::array<T, ExtendedDim<T, 4, Packed>()> extended;
|
||||
struct {
|
||||
T x, y, z, w;
|
||||
};
|
||||
#define Dim 4
|
||||
#include "../Swizzle/Swizzle_4.inc.hpp"
|
||||
#undef Dim
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tuple utilities
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Indexable, size_t... Indices>
|
||||
auto AsTuple(const Indexable& value, std::index_sequence<Indices...>) {
|
||||
return std::tuple{ value[Indices]... };
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed>
|
||||
auto AsTuple(const Vector<T, Dim, Packed>& value, std::nullptr_t) {
|
||||
return AsTuple(value, std::make_index_sequence<Dim>());
|
||||
}
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices>
|
||||
auto AsTuple(const Swizzle<T, Dim, Packed, Indices...>& value, std::nullptr_t) {
|
||||
return AsTuple(value, std::make_index_sequence<sizeof...(Indices)>());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto AsTuple(const T& value, const void*) {
|
||||
return std::tuple{ value };
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto AsTuple(const T& value) {
|
||||
return AsTuple(value, nullptr);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// General vector class
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/// <summary> Represents a vector in N-dimensional space. </summary>
|
||||
/// <typeparam name="T"> The scalar type on which the vector is based.
|
||||
/// You can use builtin floating point or integer types. User-defined types and std::complex
|
||||
/// may also work, but are not yet officially supported. </typeparam>
|
||||
/// <typeparam name="Dim"> The dimension of the vector-space. Must be a positive integer.
|
||||
/// Dynamically sized vectors are not supported yet, but you'll have to use
|
||||
/// <see cref="mathter::DYNAMIC"/> to define dynamically sized vectors. </typeparam>
|
||||
/// <typeparam name="Packed"> Set to true to tightly pack vector elements and
|
||||
/// avoid padding of the vector struct. Disables SIMD optimizations. </typeparam>
|
||||
/// <remarks>
|
||||
/// There is not much extraordinary to vectors, they work as you would expect.
|
||||
/// - you can use common vector space airhtmetic
|
||||
/// - you have common function like normalization
|
||||
/// - you can multiply them with <see cref="mathter::Matrix"/> from either side
|
||||
/// - you can concatenate and swizzle them.
|
||||
/// </remarks>
|
||||
template <class T, int Dim, bool Packed = false>
|
||||
class Vector : public VectorData<T, Dim, Packed> {
|
||||
static_assert(Dim >= 1, "Dimension must be positive integer.");
|
||||
|
||||
public:
|
||||
using VectorData<T, Dim, Packed>::extended;
|
||||
|
||||
//--------------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------------
|
||||
|
||||
/// <summary> Returns the number of dimensions of the vector. </summary>
|
||||
constexpr int Dimension() const {
|
||||
return Dim;
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
// Basic constructors
|
||||
//--------------------------------------------
|
||||
|
||||
/// <summary> Constructs the vector. Does NOT zero-initialize elements. </summary>
|
||||
Vector() MATHTER_VECTOR_INITIALIZER(T) {}
|
||||
Vector(const Vector&) = default;
|
||||
Vector& operator=(const Vector&) = default;
|
||||
|
||||
/// <summary> Constructs the vector by converting elements of <paramref name="other"/>. </summary>
|
||||
template <class T2, bool Packed2, std::enable_if_t<std::is_convertible_v<T2, T>, int> = 0>
|
||||
Vector(const Vector<T2, Dim, Packed2>& other) {
|
||||
for (int i = 0; i < Dim; ++i) {
|
||||
data()[i] = (T)other.data()[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Construct the vector using the SIMD batch type as content. </summary>
|
||||
template <class B, std::enable_if_t<std::is_same_v<B, Batch<T, Dim, Packed>>, int> = 0>
|
||||
explicit Vector(const B& batch) {
|
||||
batch.store_unaligned(data());
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
// Homogeneous up- and downcast
|
||||
//--------------------------------------------
|
||||
|
||||
/// <summary> Creates a homogeneous vector by appending a 1. </summary>
|
||||
template <class T2, bool Packed2, class = typename std::enable_if<(Dim >= 2), T2>::type>
|
||||
explicit Vector(const Vector<T2, Dim - 1, Packed2>& rhs) : Vector(rhs, 1) {}
|
||||
|
||||
/// <summary> Truncates last coordinate of homogenous vector to create non-homogeneous. </summary>
|
||||
template <class T2, bool Packed2>
|
||||
explicit Vector(const Vector<T2, Dim + 1, Packed2>& rhs) : Vector(rhs.data()) {}
|
||||
|
||||
|
||||
//--------------------------------------------
|
||||
// Data constructors
|
||||
//--------------------------------------------
|
||||
|
||||
/// <summary> Constructs the vector from an array of elements. </summary>
|
||||
/// <remarks> The number of elements must be the same as the vector's dimension. </remarks>
|
||||
template <class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
explicit Vector(const U* elements) {
|
||||
std::copy(elements, elements + Dimension(), begin());
|
||||
}
|
||||
|
||||
/// <summary> Sets all elements to the same value. </summary>
|
||||
template <class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
|
||||
explicit Vector(U all) {
|
||||
if constexpr (IsBatched<T, Dim, Packed>()) {
|
||||
Batch<T, Dim, Packed>(T(all)).store_unaligned(data());
|
||||
}
|
||||
else {
|
||||
std::fill(begin(), end(), T(all));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Initializes the vector by concatenating given scalar, vector or swizzle arguments. </summary>
|
||||
/// <remarks> Sum of the dimension of arguments must equal vector dimension.
|
||||
/// Types of arguments may differ from vector's underlying type, in which case cast is forced without a warning. </remarks>
|
||||
template <class... Args, typename std::enable_if<(sizeof...(Args) > 1), int>::type = 0>
|
||||
Vector(const Args&... mixed) {
|
||||
auto scalars = std::tuple_cat(AsTuple(mixed)...);
|
||||
auto fun = [this](const auto&... args) {
|
||||
extended = { T(args)... };
|
||||
};
|
||||
std::apply(fun, scalars);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------
|
||||
// Accessors
|
||||
//--------------------------------------------
|
||||
|
||||
/// <summary> Returns the nth element of the vector. </summary>
|
||||
T operator[](int idx) const {
|
||||
return data()[idx];
|
||||
}
|
||||
/// <summary> Returns the nth element of the vector. </summary>
|
||||
T& operator[](int idx) {
|
||||
return data()[idx];
|
||||
}
|
||||
|
||||
/// <summary> Returns the nth element of the vector. </summary>
|
||||
T operator()(int idx) const {
|
||||
return data()[idx];
|
||||
}
|
||||
/// <summary> Returns the nth element of the vector. </summary>
|
||||
T& operator()(int idx) {
|
||||
return data()[idx];
|
||||
}
|
||||
|
||||
/// <summary> Returns an iterator to the first element. </summary>
|
||||
auto cbegin() const {
|
||||
return extended.cbegin();
|
||||
}
|
||||
/// <summary> Returns an iterator to the first element. </summary>
|
||||
auto begin() const {
|
||||
return extended.begin();
|
||||
}
|
||||
/// <summary> Returns an iterator to the first element. </summary>
|
||||
auto begin() {
|
||||
return extended.begin();
|
||||
}
|
||||
/// <summary> Returns an iterator to the end of the vector (works like STL). </summary>
|
||||
auto cend() const {
|
||||
return cbegin() + Dimension();
|
||||
}
|
||||
/// <summary> Returns an iterator to the end of the vector (works like STL). </summary>
|
||||
auto end() const {
|
||||
return begin() + Dimension();
|
||||
}
|
||||
/// <summary> Returns an iterator to the end of the vector (works like STL). </summary>
|
||||
auto end() {
|
||||
return begin() + Dimension();
|
||||
}
|
||||
|
||||
/// <summary> Returns a pointer to the underlying array of elements. </summary>
|
||||
auto Data() const {
|
||||
return data();
|
||||
}
|
||||
/// <summary> Returns a pointer to the underlying array of elements. </summary>
|
||||
auto Data() {
|
||||
return data();
|
||||
}
|
||||
|
||||
/// <summary> Returns a pointer to the underlying array of elements. </summary>
|
||||
auto data() const {
|
||||
return extended.data();
|
||||
}
|
||||
/// <summary> Returns a pointer to the underlying array of elements. </summary>
|
||||
auto data() {
|
||||
return extended.data();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class T, T... Indices>
|
||||
struct SwizzleGenerator {
|
||||
template <T... EmptyList>
|
||||
static constexpr unsigned get_helper(unsigned, unsigned, const void*) {
|
||||
return 0;
|
||||
}
|
||||
template <T First, T... Rest>
|
||||
static constexpr unsigned get_helper(unsigned idx, unsigned size, std::nullptr_t) {
|
||||
return idx == 0 ? First : get_helper<Rest...>(idx - 1, size - 1, nullptr);
|
||||
}
|
||||
static constexpr unsigned get(unsigned idx, unsigned size) {
|
||||
return get_helper<Indices...>(idx, size, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices>
|
||||
template <class T2, bool Packed2>
|
||||
Swizzle<T, Dim, Packed, Indices...>::operator Vector<T2, sizeof...(Indices), Packed2>() const {
|
||||
constexpr auto Dim2 = int(sizeof...(Indices));
|
||||
using V = Vector<T2, Dim2, Packed2>;
|
||||
|
||||
#if MATHTER_USE_XSIMD
|
||||
if constexpr (IsBatched<T, Dim, Packed>() && Dim2 <= Dim) {
|
||||
using TI = traits::same_size_int_t<T>;
|
||||
static_assert(!std::is_void_v<TI> && sizeof(TI) == sizeof(T));
|
||||
using B = Batch<T, Dim, Packed>;
|
||||
constexpr auto batchSize = B::size;
|
||||
using BI = xsimd::batch<TI, typename B::arch_type>;
|
||||
const auto batch = B::load_unaligned(data());
|
||||
using G = SwizzleGenerator<TI, TI(Indices)...>;
|
||||
constexpr auto first = G::get(0, 9);
|
||||
const auto mask = xsimd::make_batch_constant<BI, G>();
|
||||
const auto swizzled = xsimd::swizzle(batch, mask);
|
||||
if constexpr (std::is_convertible_v<decltype(swizzled), Vector<T, Dim2, Packed>>) {
|
||||
return V{ Vector<T, Dim2, Packed>(swizzled) };
|
||||
}
|
||||
else {
|
||||
alignas(decltype(swizzled)) std::array<T, batchSize> extended;
|
||||
swizzled.store_aligned(extended.data());
|
||||
return V{ Vector<T, Dim2, Packed>(extended.data()) };
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return V(data()[Indices]...);
|
||||
}
|
||||
|
||||
|
||||
template <class T, int Dim, bool Packed, int... Indices>
|
||||
template <class T2, bool Packed2>
|
||||
Swizzle<T, Dim, Packed, Indices...>& Swizzle<T, Dim, Packed, Indices...>::operator=(const Vector<T2, sizeof...(Indices), Packed2>& rhs) {
|
||||
if (data() != rhs.data()) {
|
||||
std::tie((*this)[Indices]...) = AsTuple(rhs);
|
||||
}
|
||||
else {
|
||||
Vector<T, sizeof...(Indices), false> tmp = rhs;
|
||||
*this = tmp;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mathter
|
|
@ -0,0 +1,56 @@
|
|||
#include "Camera.h"
|
||||
#include "../shape/Line.h"
|
||||
#include <numbers>
|
||||
|
||||
Camera::Camera() : frustum(1, 1, 0.1, 100) {
|
||||
viewMatrix = mathter::Identity();
|
||||
}
|
||||
|
||||
void Camera::setPosition(Vec3& position) {
|
||||
viewMatrix(0, 3) = -position.x;
|
||||
viewMatrix(1, 3) = -position.y;
|
||||
viewMatrix(2, 3) = -position.z;
|
||||
}
|
||||
|
||||
void Camera::findZPos(std::vector<float>& vertices) {
|
||||
float x = 0.0;
|
||||
float y = 0.0;
|
||||
float z = -1.0;
|
||||
|
||||
//for (int i = 0; i < MAX_NUM_STEPS; i++) {
|
||||
// z += CAMERA_MOVE_INCREMENT;
|
||||
// for (size_t j = 0; j < std::min(VERTEX_SAMPLES, (int) vertices.size()); j++) {
|
||||
// Vec3 vertex{vertices[j * 3], vertices[j * 3 + 1], vertices[j * 3 + 2]};
|
||||
// // check whether it's in the camera frustum
|
||||
// }
|
||||
//}
|
||||
|
||||
viewMatrix = mathter::Translation(x, y, z);
|
||||
}
|
||||
|
||||
Vec3 Camera::toCameraSpace(Vec3& point) {
|
||||
return viewMatrix * point;
|
||||
}
|
||||
|
||||
Vec3 Camera::toWorldSpace(Vec3& point) {
|
||||
return mathter::Inverse(viewMatrix) * point;
|
||||
}
|
||||
|
||||
void Camera::setFocalLength(double focalLength) {
|
||||
frustum.setCameraInternals(focalLength, frustum.ratio, frustum.nearDistance, frustum.farDistance);
|
||||
}
|
||||
|
||||
Vec3 Camera::project(Vec3& pWorld) {
|
||||
Vec3 p = viewMatrix * pWorld;
|
||||
|
||||
frustum.clipToFrustum(p);
|
||||
|
||||
double start = p.x * frustum.focalLength / p.z;
|
||||
double end = p.y * frustum.focalLength / p.z;
|
||||
|
||||
return Vec3(start, end, 0);
|
||||
}
|
||||
|
||||
Frustum Camera::getFrustum() {
|
||||
return frustum;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <JuceHeader.h>
|
||||
#include "../mathter/Matrix.hpp"
|
||||
#include "Frustum.h"
|
||||
|
||||
using Matrix = mathter::Matrix<float, 4, 4, mathter::eMatrixOrder::PRECEDE_VECTOR, mathter::eMatrixLayout::ROW_MAJOR, false>;
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
Camera();
|
||||
|
||||
void setPosition(Vec3& position);
|
||||
void findZPos(std::vector<float>& vertices);
|
||||
Vec3 toCameraSpace(Vec3& point);
|
||||
Vec3 toWorldSpace(Vec3& point);
|
||||
void setFocalLength(double focalLength);
|
||||
Vec3 project(Vec3& p);
|
||||
Frustum getFrustum();
|
||||
private:
|
||||
const double VERTEX_VALUE_THRESHOLD = 1.0;
|
||||
const double CAMERA_MOVE_INCREMENT = -0.1;
|
||||
const int SAMPLE_RENDER_SAMPLES = 50;
|
||||
const int VERTEX_SAMPLES = 1000;
|
||||
const int MAX_NUM_STEPS = 1000;
|
||||
|
||||
Frustum frustum;
|
||||
|
||||
Matrix viewMatrix;
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
void Frustum::setCameraInternals(float focalLength, float ratio, float nearDistance, float farDistance) {
|
||||
// store the information
|
||||
this->focalLength = focalLength;
|
||||
this->ratio = ratio;
|
||||
this->nearDistance = nearDistance;
|
||||
this->farDistance = farDistance;
|
||||
|
@ -15,31 +16,22 @@ void Frustum::setCameraInternals(float focalLength, float ratio, float nearDista
|
|||
width = height * ratio;
|
||||
}
|
||||
|
||||
void Frustum::clipToFrustum(Point &p) {
|
||||
void Frustum::clipToFrustum(Vec3 &p) {
|
||||
float pcz, pcx, pcy, aux;
|
||||
|
||||
// compute vector from camera position to p
|
||||
Point v = p - origin;
|
||||
|
||||
// compute and test the Z coordinate
|
||||
Point negZ = -Z;
|
||||
pcz = v.innerProduct(Z);
|
||||
pcz = juce::jlimit(nearDistance, farDistance, pcz);
|
||||
pcz = p.z;
|
||||
pcz = pcz < nearDistance ? nearDistance : (pcz > farDistance ? farDistance : pcz);
|
||||
|
||||
// compute and test the Y coordinate
|
||||
pcy = v.innerProduct(Y);
|
||||
pcy = p.y;
|
||||
aux = std::abs(pcz * tang);
|
||||
pcy = juce::jlimit(-aux, aux, pcy);
|
||||
pcy = pcy < -aux ? -aux : (pcy > aux ? aux : pcy);
|
||||
|
||||
// compute and test the X coordinate
|
||||
pcx = v.innerProduct(X);
|
||||
pcx = p.x;
|
||||
aux = aux * ratio;
|
||||
pcx = juce::jlimit(-aux, aux, pcx);
|
||||
pcx = pcx < -aux ? -aux : (pcx > aux ? aux : pcx);
|
||||
|
||||
// calculate the clipped point using the referential coordinates
|
||||
Point x = X * pcx;
|
||||
Point y = Y * pcy;
|
||||
Point z = Z * pcz;
|
||||
|
||||
p = x + y + z + origin;
|
||||
p = Vec3(pcx, pcy, pcz);
|
||||
}
|
|
@ -2,27 +2,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "../shape/Point.h"
|
||||
#include "../mathter/Matrix.hpp"
|
||||
#include <cmath>
|
||||
|
||||
using Vec3 = mathter::Vector<float, 3, false>;
|
||||
|
||||
class Frustum {
|
||||
public:
|
||||
float ratio, nearDistance, farDistance, width, height, tang;
|
||||
float ratio, nearDistance, farDistance, width, height, tang, focalLength;
|
||||
|
||||
Point origin = Point(0, 0, -1);
|
||||
|
||||
Point X = Point(1, 0, 0);
|
||||
Point Y = Point(0, 1, 0);
|
||||
Point Z = Point(0, 0, 1);
|
||||
|
||||
Frustum(float fov, float ratio, float nearDistance, float farDistance) {
|
||||
setCameraInternals(fov, ratio, nearDistance, farDistance);
|
||||
Frustum(float focalLength, float ratio, float nearDistance, float farDistance) {
|
||||
setCameraInternals(focalLength, ratio, nearDistance, farDistance);
|
||||
}
|
||||
~Frustum() {};
|
||||
|
||||
void setCameraOrigin(Point& p) {
|
||||
origin = p;
|
||||
void setCameraInternals(float focalLength, float ratio, float nearD, float farD);
|
||||
void clipToFrustum(Vec3 &p);
|
||||
float getFocalLength() {
|
||||
return focalLength;
|
||||
}
|
||||
void setCameraInternals(float fov, float ratio, float nearD, float farD);
|
||||
void clipToFrustum(Point &p);
|
||||
};
|
||||
|
|
|
@ -4,7 +4,101 @@
|
|||
useAppConfig="0" addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1">
|
||||
<MAINGROUP id="ztPpnM" name="osci-render-test">
|
||||
<GROUP id="{4373FA2A-1E20-AAA6-40E0-740C13D88B75}" name="Source">
|
||||
<GROUP id="{DB7C86A4-CC9B-5846-B0C3-6EB553450542}" name="mathter">
|
||||
<GROUP id="{3743CC14-52E9-72AB-1A61-DA053869B50F}" name="Common">
|
||||
<FILE id="aQA6tH" name="Approx.hpp" compile="0" resource="0" file="Source/mathter/Common/Approx.hpp"/>
|
||||
<FILE id="rNR77E" name="Definitions.hpp" compile="0" resource="0" file="Source/mathter/Common/Definitions.hpp"/>
|
||||
<FILE id="CmM7fr" name="DeterministicInitializer.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Common/DeterministicInitializer.hpp"/>
|
||||
<FILE id="O7jKlU" name="MathUtil.hpp" compile="0" resource="0" file="Source/mathter/Common/MathUtil.hpp"/>
|
||||
<FILE id="Ohd8Uj" name="Range.hpp" compile="0" resource="0" file="Source/mathter/Common/Range.hpp"/>
|
||||
<FILE id="cLocYc" name="Traits.hpp" compile="0" resource="0" file="Source/mathter/Common/Traits.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{A0EC283D-AD9A-45D7-CBC9-31DE5C722428}" name="Decompositions">
|
||||
<FILE id="sE3GRp" name="DecomposeLU.hpp" compile="0" resource="0" file="Source/mathter/Decompositions/DecomposeLU.hpp"/>
|
||||
<FILE id="sPN5k7" name="DecomposeQR.hpp" compile="0" resource="0" file="Source/mathter/Decompositions/DecomposeQR.hpp"/>
|
||||
<FILE id="qZhdEq" name="DecomposeSVD.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Decompositions/DecomposeSVD.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{B2E8C8D3-AB72-BD22-371D-B59B7D5EC86E}" name="Matrix">
|
||||
<FILE id="kJ5yQg" name="MatrixArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixArithmetic.hpp"/>
|
||||
<FILE id="aoM61P" name="MatrixCast.hpp" compile="0" resource="0" file="Source/mathter/Matrix/MatrixCast.hpp"/>
|
||||
<FILE id="I55gGV" name="MatrixCompare.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixCompare.hpp"/>
|
||||
<FILE id="Xm6CET" name="MatrixFunction.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixFunction.hpp"/>
|
||||
<FILE id="RbCYlp" name="MatrixImpl.hpp" compile="0" resource="0" file="Source/mathter/Matrix/MatrixImpl.hpp"/>
|
||||
<FILE id="XiaezA" name="MatrixVectorArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixVectorArithmetic.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{1E8F68D7-B4F6-4255-E898-4E41F3212AC3}" name="Quaternion">
|
||||
<FILE id="WiA7Ny" name="QuaternionArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionArithmetic.hpp"/>
|
||||
<FILE id="r6bmOD" name="QuaternionCompare.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionCompare.hpp"/>
|
||||
<FILE id="jNnoRq" name="QuaternionFunction.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionFunction.hpp"/>
|
||||
<FILE id="k1EHXC" name="QuaternionImpl.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionImpl.hpp"/>
|
||||
<FILE id="yhEgiB" name="QuaternionLiterals.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionLiterals.hpp"/>
|
||||
<FILE id="SfyeNM" name="QuaternionVectorArithmetic.hpp" compile="0"
|
||||
resource="0" file="Source/mathter/Quaternion/QuaternionVectorArithmetic.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{801A14AB-9E53-831E-0C28-77920FEBE5F3}" name="Swizzle">
|
||||
<FILE id="e4LuEA" name="Swizzle_1.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_1.inc.hpp"/>
|
||||
<FILE id="oLqh2W" name="Swizzle_2.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_2.inc.hpp"/>
|
||||
<FILE id="HZItGl" name="Swizzle_3.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_3.inc.hpp"/>
|
||||
<FILE id="RGzkGq" name="Swizzle_4.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_4.inc.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{97B1F6D1-6E14-90A3-15FA-B3BDD2BCDC23}" name="Transforms">
|
||||
<FILE id="vFKZjZ" name="IdentityBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/IdentityBuilder.hpp"/>
|
||||
<FILE id="ziIDzi" name="OrthographicBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/OrthographicBuilder.hpp"/>
|
||||
<FILE id="sg9vwi" name="PerspectiveBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/PerspectiveBuilder.hpp"/>
|
||||
<FILE id="V16xRK" name="Rotation2DBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/Rotation2DBuilder.hpp"/>
|
||||
<FILE id="UhewiK" name="Rotation3DBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/Rotation3DBuilder.hpp"/>
|
||||
<FILE id="NtlfeX" name="ScaleBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/ScaleBuilder.hpp"/>
|
||||
<FILE id="e27C5q" name="ShearBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/ShearBuilder.hpp"/>
|
||||
<FILE id="AfgkTC" name="TranslationBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/TranslationBuilder.hpp"/>
|
||||
<FILE id="EKavGG" name="ViewBuilder.hpp" compile="0" resource="0" file="Source/mathter/Transforms/ViewBuilder.hpp"/>
|
||||
<FILE id="UrxQHQ" name="ZeroBuilder.hpp" compile="0" resource="0" file="Source/mathter/Transforms/ZeroBuilder.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{9DB67C44-77FA-E0AD-2B1F-BD067FFC3C49}" name="Vector">
|
||||
<FILE id="APeek2" name="VectorArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorArithmetic.hpp"/>
|
||||
<FILE id="LFTGmB" name="VectorCompare.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorCompare.hpp"/>
|
||||
<FILE id="PZ8ZYU" name="VectorConcat.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorConcat.hpp"/>
|
||||
<FILE id="TNEov6" name="VectorFunction.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorFunction.hpp"/>
|
||||
<FILE id="RlyOmf" name="VectorImpl.hpp" compile="0" resource="0" file="Source/mathter/Vector/VectorImpl.hpp"/>
|
||||
</GROUP>
|
||||
<FILE id="mc09po" name="CMakeLists.txt" compile="0" resource="1" file="Source/mathter/CMakeLists.txt"/>
|
||||
<FILE id="yX3qMf" name="Geometry.hpp" compile="0" resource="0" file="Source/mathter/Geometry.hpp"/>
|
||||
<FILE id="Oyc8dh" name="IoStream.hpp" compile="0" resource="0" file="Source/mathter/IoStream.hpp"/>
|
||||
<FILE id="zx4HZk" name="Mathter.natvis" compile="0" resource="1" file="Source/mathter/Mathter.natvis"/>
|
||||
<FILE id="twSep0" name="Matrix.hpp" compile="0" resource="0" file="Source/mathter/Matrix.hpp"/>
|
||||
<FILE id="tqp13e" name="Quaternion.hpp" compile="0" resource="0" file="Source/mathter/Quaternion.hpp"/>
|
||||
<FILE id="zKZb8o" name="Utility.hpp" compile="0" resource="0" file="Source/mathter/Utility.hpp"/>
|
||||
<FILE id="KdCIwM" name="Vector.hpp" compile="0" resource="0" file="Source/mathter/Vector.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{9D1E8A66-A99C-4333-3CF8-A86432FE09A7}" name="obj">
|
||||
<FILE id="moPMOL" name="Camera.cpp" compile="1" resource="0" file="Source/obj/Camera.cpp"/>
|
||||
<FILE id="QPXpbZ" name="Camera.h" compile="0" resource="0" file="Source/obj/Camera.h"/>
|
||||
<FILE id="V3Q6n2" name="Frustum.cpp" compile="1" resource="0" file="Source/obj/Frustum.cpp"/>
|
||||
<FILE id="m9wauB" name="Frustum.h" compile="0" resource="0" file="Source/obj/Frustum.h"/>
|
||||
</GROUP>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
||||
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
||||
version="2.0.8" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
companyEmail="james@ball.sh">
|
||||
companyEmail="james@ball.sh" defines="NOMINMAX=1">
|
||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
||||
<GROUP id="{C2609827-4F4A-1ADA-8BA1-A40C1D92649C}" name="lua">
|
||||
|
@ -33,6 +33,98 @@
|
|||
</GROUP>
|
||||
</GROUP>
|
||||
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
|
||||
<GROUP id="{50F56072-D264-AC8C-EAC9-A073059CDE27}" name="mathter">
|
||||
<GROUP id="{60CE44FE-26F8-4CE0-3BFF-5475F8E0BD75}" name="Common">
|
||||
<FILE id="XY4AlJ" name="Approx.hpp" compile="0" resource="0" file="Source/mathter/Common/Approx.hpp"/>
|
||||
<FILE id="LeuGwE" name="Definitions.hpp" compile="0" resource="0" file="Source/mathter/Common/Definitions.hpp"/>
|
||||
<FILE id="HcUnQp" name="DeterministicInitializer.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Common/DeterministicInitializer.hpp"/>
|
||||
<FILE id="qvzdH7" name="MathUtil.hpp" compile="0" resource="0" file="Source/mathter/Common/MathUtil.hpp"/>
|
||||
<FILE id="wy5V3L" name="Range.hpp" compile="0" resource="0" file="Source/mathter/Common/Range.hpp"/>
|
||||
<FILE id="hsfx9V" name="Traits.hpp" compile="0" resource="0" file="Source/mathter/Common/Traits.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{AA5719C4-2684-8629-96C2-ACCCC846741E}" name="Decompositions">
|
||||
<FILE id="mqUNTZ" name="DecomposeLU.hpp" compile="0" resource="0" file="Source/mathter/Decompositions/DecomposeLU.hpp"/>
|
||||
<FILE id="bajVyN" name="DecomposeQR.hpp" compile="0" resource="0" file="Source/mathter/Decompositions/DecomposeQR.hpp"/>
|
||||
<FILE id="LVojfW" name="DecomposeSVD.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Decompositions/DecomposeSVD.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{5223A16D-BBB0-7277-2DA2-5F83E36AF6D7}" name="Matrix">
|
||||
<FILE id="NiMdHn" name="MatrixArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixArithmetic.hpp"/>
|
||||
<FILE id="OG3XE0" name="MatrixCast.hpp" compile="0" resource="0" file="Source/mathter/Matrix/MatrixCast.hpp"/>
|
||||
<FILE id="hzHEZU" name="MatrixCompare.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixCompare.hpp"/>
|
||||
<FILE id="TKewDV" name="MatrixFunction.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixFunction.hpp"/>
|
||||
<FILE id="az9Zbs" name="MatrixImpl.hpp" compile="0" resource="0" file="Source/mathter/Matrix/MatrixImpl.hpp"/>
|
||||
<FILE id="ZApObW" name="MatrixVectorArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Matrix/MatrixVectorArithmetic.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{9ED17B79-C8AF-B25C-1B0D-01C8A868E93B}" name="Quaternion">
|
||||
<FILE id="Pg5bBV" name="QuaternionArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionArithmetic.hpp"/>
|
||||
<FILE id="Q40Fdf" name="QuaternionCompare.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionCompare.hpp"/>
|
||||
<FILE id="lnp7Lb" name="QuaternionFunction.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionFunction.hpp"/>
|
||||
<FILE id="qDNOvo" name="QuaternionImpl.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionImpl.hpp"/>
|
||||
<FILE id="eXgZ46" name="QuaternionLiterals.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Quaternion/QuaternionLiterals.hpp"/>
|
||||
<FILE id="oMB7Jr" name="QuaternionVectorArithmetic.hpp" compile="0"
|
||||
resource="0" file="Source/mathter/Quaternion/QuaternionVectorArithmetic.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{D82CFD72-F5BE-1024-94AB-5C2503423E4D}" name="Swizzle">
|
||||
<FILE id="CeOGVV" name="Swizzle_1.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_1.inc.hpp"/>
|
||||
<FILE id="jwhhIz" name="Swizzle_2.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_2.inc.hpp"/>
|
||||
<FILE id="fPNnzc" name="Swizzle_3.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_3.inc.hpp"/>
|
||||
<FILE id="gU43mt" name="Swizzle_4.inc.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Swizzle/Swizzle_4.inc.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{2F08F8FD-A6D7-6DE3-CAD4-564E35A9F0B9}" name="Transforms">
|
||||
<FILE id="pltHW9" name="IdentityBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/IdentityBuilder.hpp"/>
|
||||
<FILE id="g2kgGu" name="OrthographicBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/OrthographicBuilder.hpp"/>
|
||||
<FILE id="DBDXmX" name="PerspectiveBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/PerspectiveBuilder.hpp"/>
|
||||
<FILE id="Gj9MnE" name="Rotation2DBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/Rotation2DBuilder.hpp"/>
|
||||
<FILE id="Hq0ZLD" name="Rotation3DBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/Rotation3DBuilder.hpp"/>
|
||||
<FILE id="An6eDN" name="ScaleBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/ScaleBuilder.hpp"/>
|
||||
<FILE id="xzgGHN" name="ShearBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/ShearBuilder.hpp"/>
|
||||
<FILE id="OVvc6Z" name="TranslationBuilder.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Transforms/TranslationBuilder.hpp"/>
|
||||
<FILE id="TzGfIt" name="ViewBuilder.hpp" compile="0" resource="0" file="Source/mathter/Transforms/ViewBuilder.hpp"/>
|
||||
<FILE id="VTTCpk" name="ZeroBuilder.hpp" compile="0" resource="0" file="Source/mathter/Transforms/ZeroBuilder.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{BA088AC1-AC2B-B70C-582E-EDF9E38ABC20}" name="Vector">
|
||||
<FILE id="KDVsvP" name="VectorArithmetic.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorArithmetic.hpp"/>
|
||||
<FILE id="qBpF7X" name="VectorCompare.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorCompare.hpp"/>
|
||||
<FILE id="IinHSU" name="VectorConcat.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorConcat.hpp"/>
|
||||
<FILE id="aVRyP9" name="VectorFunction.hpp" compile="0" resource="0"
|
||||
file="Source/mathter/Vector/VectorFunction.hpp"/>
|
||||
<FILE id="af10kl" name="VectorImpl.hpp" compile="0" resource="0" file="Source/mathter/Vector/VectorImpl.hpp"/>
|
||||
</GROUP>
|
||||
<FILE id="vOLhwO" name="CMakeLists.txt" compile="0" resource="1" file="Source/mathter/CMakeLists.txt"/>
|
||||
<FILE id="d4t0xm" name="Geometry.hpp" compile="0" resource="0" file="Source/mathter/Geometry.hpp"/>
|
||||
<FILE id="AZxH75" name="IoStream.hpp" compile="0" resource="0" file="Source/mathter/IoStream.hpp"/>
|
||||
<FILE id="ev6gxl" name="Mathter.natvis" compile="0" resource="1" file="Source/mathter/Mathter.natvis"/>
|
||||
<FILE id="VBewvB" name="Matrix.hpp" compile="0" resource="0" file="Source/mathter/Matrix.hpp"/>
|
||||
<FILE id="vlSQrm" name="Quaternion.hpp" compile="0" resource="0" file="Source/mathter/Quaternion.hpp"/>
|
||||
<FILE id="Aos8s3" name="Utility.hpp" compile="0" resource="0" file="Source/mathter/Utility.hpp"/>
|
||||
<FILE id="T6rqIo" name="Vector.hpp" compile="0" resource="0" file="Source/mathter/Vector.hpp"/>
|
||||
</GROUP>
|
||||
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
|
||||
<FILE id="WDV6eI" name="AudioWebSocketServer.cpp" compile="1" resource="0"
|
||||
file="Source/audio/AudioWebSocketServer.cpp"/>
|
||||
|
@ -398,6 +490,8 @@
|
|||
file="Source/MidiComponent.cpp"/>
|
||||
<FILE id="GJqoJa" name="MidiComponent.h" compile="0" resource="0" file="Source/MidiComponent.h"/>
|
||||
<GROUP id="{E6ED85A9-3843-825F-EF48-BCF81E38F8AD}" name="obj">
|
||||
<FILE id="ahOy0q" name="Camera.cpp" compile="1" resource="0" file="Source/obj/Camera.cpp"/>
|
||||
<FILE id="dUDESs" name="Camera.h" compile="0" resource="0" file="Source/obj/Camera.h"/>
|
||||
<FILE id="T6iC8q" name="Frustum.cpp" compile="1" resource="0" file="Source/obj/Frustum.cpp"/>
|
||||
<FILE id="ky5ZfA" name="Frustum.h" compile="0" resource="0" file="Source/obj/Frustum.h"/>
|
||||
<FILE id="Yfpzzn" name="ObjectServer.cpp" compile="1" resource="0"
|
||||
|
|
Ładowanie…
Reference in New Issue