From 44503539e756f02b8f42a28bc8dd3f0078af9f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 7 May 2019 08:26:55 +0200 Subject: [PATCH] python: expose matrix types. Not the Matrix4 / Matrix3 transformation types yet, though. --- src/python/magnum/CMakeLists.txt | 2 + src/python/magnum/__init__.py | 7 + src/python/magnum/bootstrap.h | 2 + src/python/magnum/magnum.cpp | 2 + src/python/magnum/math.matrix.h | 337 ++++++++++++++++++++++++ src/python/magnum/math.matrixdouble.cpp | 49 ++++ src/python/magnum/math.matrixfloat.cpp | 49 ++++ src/python/magnum/test/test_math.py | 96 +++++++ 8 files changed, 544 insertions(+) create mode 100644 src/python/magnum/math.matrix.h create mode 100644 src/python/magnum/math.matrixdouble.cpp create mode 100644 src/python/magnum/math.matrixfloat.cpp diff --git a/src/python/magnum/CMakeLists.txt b/src/python/magnum/CMakeLists.txt index c3a5ef6..5017980 100644 --- a/src/python/magnum/CMakeLists.txt +++ b/src/python/magnum/CMakeLists.txt @@ -26,6 +26,8 @@ set(magnum_SRCS magnum.cpp math.cpp + math.matrixfloat.cpp + math.matrixdouble.cpp math.vectorfloat.cpp math.vectorintegral.cpp) diff --git a/src/python/magnum/__init__.py b/src/python/magnum/__init__.py index aaedb30..782e557 100644 --- a/src/python/magnum/__init__.py +++ b/src/python/magnum/__init__.py @@ -35,4 +35,11 @@ __all__ = [ 'Vector2d', 'Vector3d', 'Vector4d', 'Vector2i', 'Vector3i', 'Vector4i', 'Vector2ui', 'Vector3ui', 'Vector4ui', + + 'Matrix2x2', 'Matrix2x3', 'Matrix2x4', + 'Matrix3x2', 'Matrix3x3', 'Matrix3x4', + 'Matrix4x2', 'Matrix4x3', 'Matrix4x4', + 'Matrix2x2d', 'Matrix2x3d', 'Matrix2x4d', + 'Matrix3x2d', 'Matrix3x3d', 'Matrix3x4d', + 'Matrix4x2d', 'Matrix4x3d', 'Matrix4x4d', ] diff --git a/src/python/magnum/bootstrap.h b/src/python/magnum/bootstrap.h index ad0fe99..498a500 100644 --- a/src/python/magnum/bootstrap.h +++ b/src/python/magnum/bootstrap.h @@ -36,6 +36,8 @@ namespace py = pybind11; void math(py::module& root, py::module& m); void mathVectorFloat(py::module& root, py::module& m); void mathVectorIntegral(py::module& root, py::module& m); +void mathMatrixFloat(py::module& root); +void mathMatrixDouble(py::module& root); } diff --git a/src/python/magnum/magnum.cpp b/src/python/magnum/magnum.cpp index 4345c64..b235dcb 100644 --- a/src/python/magnum/magnum.cpp +++ b/src/python/magnum/magnum.cpp @@ -36,4 +36,6 @@ PYBIND11_MODULE(_magnum, m) { magnum::math(m, math); magnum::mathVectorFloat(m, math); magnum::mathVectorIntegral(m, math); + magnum::mathMatrixFloat(m); + magnum::mathMatrixDouble(m); } diff --git a/src/python/magnum/math.matrix.h b/src/python/magnum/math.matrix.h new file mode 100644 index 0000000..cd364af --- /dev/null +++ b/src/python/magnum/math.matrix.h @@ -0,0 +1,337 @@ +#ifndef magnum_math_matrix_h +#define magnum_math_matrix_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Math/Matrix.h" +#include "Magnum/Math/Vector4.h" + +#include "magnum/math.h" + +namespace magnum { + +/* A variant of Magnum's own DimensionTraits, but working for 2/3/4 dimensions + instead of 1/2/3 dimensions */ +template struct VectorTraits; +template struct VectorTraits<2, T> { typedef Math::Vector2 Type; }; +template struct VectorTraits<3, T> { typedef Math::Vector3 Type; }; +template struct VectorTraits<4, T> { typedef Math::Vector4 Type; }; + +template void rectangularMatrix(py::class_& c) { + /* + Missing APIs: + + from(T*) + fromVector() (would need Vector6,...Vector16 for that) + Type + construction from different types + construction by slicing or expanding differently sized matrices + row() / setRow() (function? that's ugly. property? not sure how) + component-wise operations (would need BoolVector6 ... BoolVector16) + ij() (doesn't make sense in generic code as we don't have Matrix1) + */ + + c + /* Constructors */ + .def_static("from_diagonal", [](const typename VectorTraits::Type& vector) { + return T::fromDiagonal(vector); + }, "Construct a diagonal matrix") + .def_static("zero_init", []() { + return T{Math::ZeroInit}; + }, "Construct a zero-filled matrix") + .def(py::init(), "Default constructor") + .def(py::init(), "Construct a matrix with one value for all components") + + /* Comparison */ + .def(py::self == py::self, "Equality comparison") + .def(py::self != py::self, "Non-equality comparison") + + /* Set / get. Need to throw IndexError in order to allow iteration: + https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */ + .def("__setitem__", [](T& self, std::size_t i, const typename VectorTraits::Type& value) { + if(i >= T::Cols) throw pybind11::index_error{}; + self[i] = value; + }, "Set a column at given position") + .def("__getitem__", [](const T& self, std::size_t i) -> typename VectorTraits::Type { + if(i >= T::Cols) throw pybind11::index_error{}; + return self[i]; + }, "Column at given position") + /* Set / get for direct elements, because [a][b] = 2.5 won't work + without involving shared pointers */ + .def("__setitem__", [](T& self, const std::pair& i, typename T::Type value) { + if(i.first >= T::Cols || i.second >= T::Rows) throw pybind11::index_error{}; + self[i.first][i.second] = value; + }, "Set a value at given col/row") + .def("__getitem__", [](const T& self, const std::pair& i) { + if(i.first >= T::Cols || i.second >= T::Rows) throw pybind11::index_error{}; + return self[i.first][i.second]; + }, "Value at given col/row") + + /* Operators */ + .def(-py::self, "Negated matrix") + .def(py::self += py::self, "Add and assign a matrix") + .def(py::self + py::self, "Add a matrix") + .def(py::self -= py::self, "Subtract and assign a matrix") + .def(py::self - py::self, "Subtract a matrix") + .def(py::self *= typename T::Type{}, "Multiply with a scalar and assign") + .def(py::self * typename T::Type{}, "Multiply with a scalar") + .def(py::self /= typename T::Type{}, "Divide with a scalar and assign") + .def(py::self / typename T::Type{}, "Divide with a scalar") + .def("__mul__", [](const T& self, const typename VectorTraits::Type& vector) -> typename VectorTraits::Type { + return self*vector; + }, "Multiply a vector") + .def(typename T::Type{} * py::self, "Multiply a scalar with a matrix") + .def(typename T::Type{} / py::self, "Divide a matrix with a scalar and invert") + + /* Member functions that don't return a size-dependent type */ + .def("flipped_cols", &T::flippedCols, "Matrix with flipped cols") + .def("flipped_rows", &T::flippedRows, "Matrix with flipped rows") + .def("diagonal", [](const T& self) -> typename VectorTraits::Type { + return self.diagonal(); + }, "Values on diagonal") + + .def("__repr__", repr, "Object representation"); + + /* Matrix column count */ + char lenDocstring[] = "Matrix column count. Returns _."; + lenDocstring[sizeof(lenDocstring) - 3] = '0' + T::Cols; + c.def_static("__len__", []() { return int(T::Cols); }, lenDocstring); +} + +template void matrix(py::class_& c) { + c + /* Constructors */ + .def_static("identity_init", [](typename T::Type value) { + return T{Math::IdentityInit, value}; + }, "Construct an identity matrix", py::arg("value") = typename T::Type(1)) + + /* Member functions for square matrices only */ + .def("is_orthogonal", &T::isOrthogonal, "Whether the matrix is orthogonal") + .def("trace", &T::trace, "Trace of the matrix") + .def("determinant", &T::determinant, "Determinant") + .def("inverted", &T::inverted, "Inverted matrix") + .def("inverted_orthogonal", &T::invertedOrthogonal, "Inverted orthogonal matrix"); +} + +template void matrices( + py::class_>& matrix2x2, + py::class_>& matrix2x3, + py::class_>& matrix2x4, + + py::class_>& matrix3x2, + py::class_>& matrix3x3, + py::class_>& matrix3x4, + + py::class_>& matrix4x2, + py::class_>& matrix4x3, + py::class_>& matrix4x4 +) { + /* Two-column matrices */ + matrix2x2 + .def(py::init&, const Math::Vector2&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector2>& value) { + return Math::Matrix2x2{std::get<0>(value), std::get<1>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix2x2& self, const Math::Matrix2x2& other) -> Math::Matrix2x2 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix2x2& self, const Math::Matrix3x2& other) -> Math::Matrix3x2 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix2x2& self, const Math::Matrix4x2& other) -> Math::Matrix4x2 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix2x2& self) -> Math::Matrix2x2 { + return self.transposed(); + }, "Transposed matrix"); + matrix2x3 + .def(py::init&, const Math::Vector3&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector3>& value) { + return Math::Matrix2x3{std::get<0>(value), std::get<1>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix2x3& self, const Math::Matrix2x2& other) -> Math::Matrix2x3 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix2x3& self, const Math::Matrix3x2& other) -> Math::Matrix3x3 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix2x3& self, const Math::Matrix4x2& other) -> Math::Matrix4x3 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix2x3& self) -> Math::Matrix3x2 { + return self.transposed(); + }, "Transposed matrix"); + matrix2x4 + .def(py::init&, const Math::Vector4&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector4>& value) { + return Math::Matrix2x4{std::get<0>(value), std::get<1>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix2x4& self, const Math::Matrix2x2& other) -> Math::Matrix2x4 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix2x4& self, const Math::Matrix3x2& other) -> Math::Matrix3x4 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix2x4& self, const Math::Matrix4x2& other) -> Math::Matrix4x4 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix2x4& self) -> Math::Matrix4x2 { + return self.transposed(); + }, "Transposed matrix"); + rectangularMatrix(matrix2x2); + rectangularMatrix(matrix2x3); + rectangularMatrix(matrix2x4); + matrix(matrix2x2); + + /* Three-column matrices */ + matrix3x2 + .def(py::init&, const Math::Vector2&, const Math::Vector2&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector2, Math::Vector2>& value) { + return Math::Matrix3x2{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix3x2& self, const Math::Matrix2x3& other) -> Math::Matrix2x2 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix3x2& self, const Math::Matrix3x3& other) -> Math::Matrix3x2 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix3x2& self, const Math::Matrix4x3& other) -> Math::Matrix4x2 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix3x2& self) -> Math::Matrix2x3 { + return self.transposed(); + }, "Transposed matrix"); + matrix3x3 + .def(py::init&, const Math::Vector3&, const Math::Vector3&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector3, Math::Vector3>& value) { + return Math::Matrix3x3{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix3x3& self, const Math::Matrix2x3& other) -> Math::Matrix2x3 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix3x3& self, const Math::Matrix3x3& other) -> Math::Matrix3x3 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix3x3& self, const Math::Matrix4x3& other) -> Math::Matrix4x3 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix3x3& self) -> Math::Matrix3x3 { + return self.transposed(); + }, "Transposed matrix"); + matrix3x4 + .def(py::init&, const Math::Vector4&, const Math::Vector4&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector4, Math::Vector4>& value) { + return Math::Matrix3x4{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix3x4& self, const Math::Matrix2x3& other) -> Math::Matrix2x4 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix3x4& self, const Math::Matrix3x3& other) -> Math::Matrix3x4 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix3x4& self, const Math::Matrix4x3& other) -> Math::Matrix4x4 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix3x4& self) -> Math::Matrix4x3 { + return self.transposed(); + }, "Transposed matrix"); + rectangularMatrix(matrix3x2); + rectangularMatrix(matrix3x3); + rectangularMatrix(matrix3x4); + matrix(matrix3x3); + + /* Four-column matrices */ + matrix4x2 + .def(py::init&, const Math::Vector2&, const Math::Vector2&, const Math::Vector2&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector2, Math::Vector2, Math::Vector2>& value) { + return Math::Matrix4x2{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix4x2& self, const Math::Matrix2x4& other) -> Math::Matrix2x2 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix4x2& self, const Math::Matrix3x4& other) -> Math::Matrix3x2 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix4x2& self, const Math::Matrix4x4& other) -> Math::Matrix4x2 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix4x2& self) -> Math::Matrix2x4 { + return self.transposed(); + }, "Transposed matrix"); + matrix4x3 + .def(py::init&, const Math::Vector3&, const Math::Vector3&, const Math::Vector3&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector3, Math::Vector3, Math::Vector3>& value) { + return Math::Matrix4x3{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix4x3& self, const Math::Matrix2x4& other) -> Math::Matrix2x3 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix4x3& self, const Math::Matrix3x4& other) -> Math::Matrix3x3 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix4x3& self, const Math::Matrix4x4& other) -> Math::Matrix4x3 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix4x3& self) -> Math::Matrix3x4 { + return self.transposed(); + }, "Transposed matrix"); + matrix4x4 + .def(py::init&, const Math::Vector4&, const Math::Vector4&, const Math::Vector4&>(), + "Construct from column vectors") + .def(py::init([](const std::tuple, Math::Vector4, Math::Vector4, Math::Vector4>& value) { + return Math::Matrix4x4{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)}; + }), "Construct from a column vector tuple") + .def("__matmul__", [](const Math::Matrix4x4& self, const Math::Matrix2x4& other) -> Math::Matrix2x4 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix4x4& self, const Math::Matrix3x4& other) -> Math::Matrix3x4 { + return self*other; + }, "Multiply a matrix") + .def("__matmul__", [](const Math::Matrix4x4& self, const Math::Matrix4x4& other) -> Math::Matrix4x4 { + return self*other; + }, "Multiply a matrix") + .def("transposed", [](const Math::Matrix4x4& self) -> Math::Matrix4x4 { + return self.transposed(); + }, "Transposed matrix"); + rectangularMatrix(matrix4x2); + rectangularMatrix(matrix4x3); + rectangularMatrix(matrix4x4); + matrix(matrix4x4); +} + +} + +#endif diff --git a/src/python/magnum/math.matrixdouble.cpp b/src/python/magnum/math.matrixdouble.cpp new file mode 100644 index 0000000..047a1bb --- /dev/null +++ b/src/python/magnum/math.matrixdouble.cpp @@ -0,0 +1,49 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "magnum/math.matrix.h" + +namespace magnum { + +void mathMatrixDouble(py::module& root) { + py::class_ matrix2x2d{root, "Matrix2x2d", "2x2 double matrix"}; + py::class_ matrix2x3d{root, "Matrix2x3d", "2x3 double matrix"}; + py::class_ matrix2x4d{root, "Matrix2x4d", "2x4 double matrix"}; + + py::class_ matrix3x2d{root, "Matrix3x2d", "3x2 double matrix"}; + py::class_ matrix3x3d{root, "Matrix3x3d", "3x3 double matrix"}; + py::class_ matrix3x4d{root, "Matrix3x4d", "3x4 double matrix"}; + + py::class_ matrix4x2d{root, "Matrix4x2d", "4x2 double matrix"}; + py::class_ matrix4x3d{root, "Matrix4x3d", "4x3 double matrix"}; + py::class_ matrix4x4d{root, "Matrix4x4d", "4x4 double matrix"}; + + matrices( + matrix2x2d, matrix2x3d, matrix2x4d, + matrix3x2d, matrix3x3d, matrix3x4d, + matrix4x2d, matrix4x3d, matrix4x4d); +} + +} diff --git a/src/python/magnum/math.matrixfloat.cpp b/src/python/magnum/math.matrixfloat.cpp new file mode 100644 index 0000000..2abc750 --- /dev/null +++ b/src/python/magnum/math.matrixfloat.cpp @@ -0,0 +1,49 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "magnum/math.matrix.h" + +namespace magnum { + +void mathMatrixFloat(py::module& root) { + py::class_ matrix2x2{root, "Matrix2x2", "2x2 float matrix"}; + py::class_ matrix2x3{root, "Matrix2x3", "2x3 float matrix"}; + py::class_ matrix2x4{root, "Matrix2x4", "2x4 float matrix"}; + + py::class_ matrix3x2{root, "Matrix3x2", "3x2 float matrix"}; + py::class_ matrix3x3{root, "Matrix3x3", "3x3 float matrix"}; + py::class_ matrix3x4{root, "Matrix3x4", "3x4 float matrix"}; + + py::class_ matrix4x2{root, "Matrix4x2", "4x2 float matrix"}; + py::class_ matrix4x3{root, "Matrix4x3", "4x3 float matrix"}; + py::class_ matrix4x4{root, "Matrix4x4", "4x4 float matrix"}; + + matrices( + matrix2x2, matrix2x3, matrix2x4, + matrix3x2, matrix3x3, matrix3x4, + matrix4x2, matrix4x3, matrix4x4); +} + +} diff --git a/src/python/magnum/test/test_math.py b/src/python/magnum/test/test_math.py index 9450356..c71e5b4 100644 --- a/src/python/magnum/test/test_math.py +++ b/src/python/magnum/test/test_math.py @@ -249,3 +249,99 @@ class Vector(unittest.TestCase): def test_repr(self): self.assertEqual(repr(Vector3(1.0, 3.14, -13.37)), 'Vector(1, 3.14, -13.37)') + +class Matrix(unittest.TestCase): + def test_init(self): + a = Matrix3x2() + self.assertEqual(a[0], Vector2(0)) + self.assertEqual(a[1], Vector2(0)) + self.assertEqual(a[2], Vector2(0)) + + b = Matrix4x4.zero_init() + self.assertEqual(b[0], Vector4(0)) + self.assertEqual(b[1], Vector4(0)) + self.assertEqual(b[2], Vector4(0)) + self.assertEqual(b[3], Vector4(0)) + + c1 = Matrix3x3.identity_init() + self.assertEqual(c1[0], Vector3.x_axis()) + self.assertEqual(c1[1], Vector3.y_axis()) + self.assertEqual(c1[2], Vector3.z_axis()) + + c3 = Matrix3x3.identity_init(3.0) + self.assertEqual(c3[0], Vector3.x_axis(3.0)) + self.assertEqual(c3[1], Vector3.y_axis(3.0)) + self.assertEqual(c3[2], Vector3.z_axis(3.0)) + + d = Matrix2x3(Vector3(1.0, 2.0, 3.0), + Vector3(4.0, 5.0, 6.0)) + self.assertEqual(d[0], Vector3(1.0, 2.0, 3.0)) + self.assertEqual(d[1], Vector3(4.0, 5.0, 6.0)) + + e = Matrix2x3((1.0, 2.0, 3.0), + (4.0, 5.0, 6.0)) + self.assertEqual(e[0], Vector3(1.0, 2.0, 3.0)) + self.assertEqual(e[1], Vector3(4.0, 5.0, 6.0)) + + f = Matrix3x2(((1.0, 2.0), + (3.0, 4.0), + (5.0, 6.0))) + self.assertEqual(f[0], Vector2(1.0, 2.0)) + self.assertEqual(f[1], Vector2(3.0, 4.0)) + self.assertEqual(f[2], Vector2(5.0, 6.0)) + + def test_length(self): + self.assertEqual(Matrix3x4.__len__(), 3) + #self.assertEqual(len(Matrix4x3), 4) TODO: Y not? + self.assertEqual(len(Matrix4x3()), 4) + + def test_set_get(self): + a = Matrix2x3((1.0, 2.0, 3.0), + (4.0, 5.0, 6.0)) + self.assertEqual(a[1][2], 6.0) + + a[1] = (4.5, 5.5, 6.5) + self.assertEqual(a[1], Vector3(4.5, 5.5, 6.5)) + + a[0, 1] = 2.5 + self.assertEqual(a[0], Vector3(1.0, 2.5, 3.0)) # yes, 2.5 + + @unittest.expectedFailure + def test_set_two_brackets(self): + a = Matrix2x3((1.0, 2.0, 3.0), + (4.0, 5.0, 6.0)) + a[0][1] = 2.5 + self.assertEqual(a[0], Vector3(1.0, 2.5, 3.0)) + + def test_iterate(self): + a = Matrix3x2((1.0, 2.0), + (3.0, 4.0), + (5.0, 6.0)) + self.assertEqual([i.sum() for i in a], [3.0, 7.0, 11.0]) + + def test_ops(self): + a = Matrix2x3((1.0, 2.0, 3.0), + (4.0, 5.0, 6.0)) + b = Matrix3x2((1.0, 2.0), + (3.0, 4.0), + (5.0, 6.0)) + c = Matrix3x3((9.0, 12.0, 15.0), + (19.0, 26.0, 33.0), + (29.0, 40.0, 51.0)) + self.assertEqual(a @ b, c) + + def test_ops_number_on_the_left(self): + a = Matrix2x3((1.0, 2.0, 3.0), + (4.0, 5.0, 6.0)) + + self.assertEqual(2.0*a, Matrix2x3((2.0, 4.0, 6.0), + (8.0, 10.0, 12.0))) + self.assertEqual(6.0/a, Matrix2x3((6.0, 3.0, 2.0), + (1.5, 1.2, 1.0))) + + def test_repr(self): + a = Matrix2x3((1.0, 2.0, 3.0), + (4.0, 5.0, 6.0)) + self.assertEqual(repr(a), 'Matrix(1, 4,\n' + ' 2, 5,\n' + ' 3, 6)')