Use mathter library for Camera and Frustum, and add viewMatrix-based camera

pull/218/head
James Ball 2024-02-11 22:06:35 +00:00
rodzic 8ff283aba8
commit eaf5a3eb9c
58 zmienionych plików z 7518 dodań i 100 usunięć

Wyświetl plik

@ -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),
}
);

Wyświetl plik

@ -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]));
}
}
};

Wyświetl plik

@ -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
);
}

Wyświetl plik

@ -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;
};

Wyświetl plik

@ -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()

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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 &lt;= 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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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&lt;*&gt;">
<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&gt;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&lt;*&gt;">
<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&lt;*&gt;">
<DisplayString>[{w} + {i} i + {j} j + {k} k]</DisplayString>
</Type>
<Type Name="mathter::Hyperplane&lt;*&gt;">
<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&lt;*&gt;">
<DisplayString>[v] = {base} + {direction} t</DisplayString>
</Type>
</AutoVisualizer>

Wyświetl plik

@ -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"

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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"

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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"/>&ast;pos[<paramref name="modulatorAxis"/>]. </remarks>
template <class T>
auto Shear(T slope, int principalAxis, int modulatorAxis) {
return ShearBuilder(slope, principalAxis, modulatorAxis);
}
} // namespace mathter

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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"

Wyświetl plik

@ -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

Wyświetl plik

@ -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> &lt;The usual warning about floating point numbers&gt; </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> &lt;The usual warning about floating point numbers&gt; </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

Wyświetl plik

@ -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

Wyświetl plik

@ -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&lt;N&gt;.
/// 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

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
};

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);
};

Wyświetl plik

@ -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>

Wyświetl plik

@ -6,7 +6,7 @@
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
cppLanguageStandard="20" projectLineFeed="&#10;" 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"