#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 #include #include #include "corrade/PybindExtras.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 initFromBuffer(T& out, const py::buffer_info& info) { for(std::size_t i = 0; i != T::Cols; ++i) for(std::size_t j = 0; j != T::Rows; ++j) out[i][j] = static_cast(*reinterpret_cast(static_cast(info.ptr) + i*info.strides[1] + j*info.strides[0])); } /* Called for both Matrix3x3 and Matrix3 in order to return a proper type / construct correctly from a numpy array, so has to be separate */ template void everyRectangularMatrix(py::class_& c) { /* Matrix is implicitly convertible from a buffer, but not from tuples because there it isn't clear if it's column-major or row-major. */ py::implicitly_convertible(); c .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") /* Buffer protocol, needed in order to make numpy treat the matric correctly as column-major. Has to be defined *before* the from-tuple constructor so it gets precedence for types that implement the buffer protocol. */ .def(py::init([](py::buffer buffer) { py::buffer_info info = buffer.request(); if(info.ndim != 2) throw py::buffer_error{Utility::formatString("expected 2 dimensions but got {}", info.ndim)}; if(info.shape[0] != T::Rows ||info.shape[1] != T::Cols) throw py::buffer_error{Utility::formatString("expected {}x{} elements but got {}x{}", T::Cols, T::Rows, info.shape[1], info.shape[0])}; T out{Math::NoInit}; if(info.format == "f") initFromBuffer(out, info); else if(info.format == "d") initFromBuffer(out, info); else throw py::buffer_error{Utility::formatString("expected format f or d but got {}", info.format)}; return out; }), "Construct from a buffer") /* 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"); } 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 /* Buffer protocol, needed in order to make numpy treat the matric correctly as column-major. The constructor is defined in everyRectangularMatrix(). */ .def_buffer([](const T& self) -> py::buffer_info { // TODO: ownership? return py::buffer_info{ const_cast(self.data()), sizeof(typename T::Type), py::format_descriptor::format(), 2, {T::Rows, T::Cols}, {sizeof(typename T::Type), sizeof(typename T::Type)*T::Rows} }; }) /* 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") .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); } /* Called for both Matrix3x3 and Matrix3 in order to return a proper type, so has to be separate */ template void everyMatrix(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)) /* Methods */ .def("inverted", &T::inverted, "Inverted matrix") .def("inverted_orthogonal", &T::invertedOrthogonal, "Inverted orthogonal matrix") .def("__matmul__", [](const T& self, const T& other) -> T { return self*other; }, "Multiply a matrix") .def("transposed", [](const T& self) -> T { return self.transposed(); }, "Transposed matrix"); } template void matrix(py::class_& c) { c /* 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"); } template void convertible(py::class_& c) { c.def(py::init(), "Construct from different underlying type"); } 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, py::class_, Math::Matrix3x3>& matrix3, py::class_, Math::Matrix4x4>& matrix4 ) { /* Two-column matrices. Buffer constructors need to be *before* tuple constructors so numpy buffer protocol gets extracted correctly. */ everyRectangularMatrix(matrix2x2); everyRectangularMatrix(matrix2x3); everyRectangularMatrix(matrix2x4); rectangularMatrix(matrix2x2); rectangularMatrix(matrix2x3); rectangularMatrix(matrix2x4); everyMatrix(matrix2x2); matrix(matrix2x2); 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(py::init([](const std::tuple, std::tuple>& value) { return Math::Matrix2x2{ Math::Vector2{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value))}, Math::Vector2{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value))} }; }), "Construct from a column tuple") .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"); 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(py::init([](const std::tuple, std::tuple>& value) { return Math::Matrix2x3{ Math::Vector3{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))}, Math::Vector3{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))} }; }), "Construct from a column 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(py::init([](const std::tuple, std::tuple>& value) { return Math::Matrix2x4{ Math::Vector4{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))}, Math::Vector4{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))} }; }), "Construct from a column 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"); /* Three-column matrices. Buffer constructors need to be *before* tuple constructors so numpy buffer protocol gets extracted correctly. */ everyRectangularMatrix(matrix3x2); everyRectangularMatrix(matrix3x3); everyRectangularMatrix(matrix3x4); rectangularMatrix(matrix3x2); rectangularMatrix(matrix3x3); rectangularMatrix(matrix3x4); everyMatrix(matrix3x3); matrix(matrix3x3); 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(py::init([](const std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix3x2{ Math::Vector2{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value))}, Math::Vector2{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value))}, Math::Vector2{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value))} }; }), "Construct from a column 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(py::init([](const std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix3x3{ Math::Vector3{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))}, Math::Vector3{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))}, Math::Vector3{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value))} }; }), "Construct from a column 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::Matrix4x3& other) -> Math::Matrix4x3 { return self*other; }, "Multiply a 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(py::init([](const std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix3x4{ Math::Vector4{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))}, Math::Vector4{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))}, Math::Vector4{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value)), std::get<3>(std::get<2>(value))} }; }), "Construct from a column 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"); /* Four-column matrices. Buffer constructors need to be *before* tuple constructors so numpy buffer protocol gets extracted correctly. */ everyRectangularMatrix(matrix4x2); everyRectangularMatrix(matrix4x3); everyRectangularMatrix(matrix4x4); rectangularMatrix(matrix4x2); rectangularMatrix(matrix4x3); rectangularMatrix(matrix4x4); everyMatrix(matrix4x4); matrix(matrix4x4); 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(py::init([](const std::tuple, std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix4x2{ Math::Vector2{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value))}, Math::Vector2{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value))}, Math::Vector2{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value))}, Math::Vector2{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value))} }; }), "Construct from a column 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(py::init([](const std::tuple, std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix4x3{ Math::Vector3{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))}, Math::Vector3{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))}, Math::Vector3{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value))}, Math::Vector3{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value)), std::get<2>(std::get<3>(value))} }; }), "Construct from a column 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(py::init([](const std::tuple, std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix4x4{ Math::Vector4{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))}, Math::Vector4{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))}, Math::Vector4{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value)), std::get<3>(std::get<2>(value))}, Math::Vector4{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value)), std::get<2>(std::get<3>(value)), std::get<3>(std::get<3>(value))}, }; }), "Construct from a column 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"); /* 3x3 transformation matrix. Buffer constructors need to be *before* tuple constructors so numpy buffer protocol gets extracted correctly. */ py::implicitly_convertible, Math::Matrix3>(); everyRectangularMatrix(matrix3); everyMatrix(matrix3); matrix3 /* Constructors. The scaling() / rotation() are handled below as they conflict with member functions. */ .def_static("translation", static_cast(*)(const Math::Vector2&)>(&Math::Matrix3::translation), "2D translation matrix") .def_static("reflection", &Math::Matrix3::reflection, "2D reflection matrix") .def_static("shearing_x", &Math::Matrix3::shearingX, "2D shearing matrix along the X axis", py::arg("amount")) .def_static("shearing_y", &Math::Matrix3::shearingY, "2D shearning matrix along the Y axis", py::arg("amount")) .def_static("projection", &Math::Matrix3::projection, "2D projection matrix", py::arg("size")) .def_static("from", static_cast(*)(const Math::Matrix2x2&, const Math::Vector2&)>(&Math::Matrix3::from), "Create a matrix from a rotation/scaling part and a translation part", py::arg("rotation_scaling"), py::arg("translation")) .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::Matrix3{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; }), "Construct from a column vector tuple") .def(py::init([](const std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix3{ Math::Vector3{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))}, Math::Vector3{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))}, Math::Vector3{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value))} }; }), "Construct from a column tuple") /* Member functions */ .def("is_rigid_transformation", &Math::Matrix3::isRigidTransformation, "Check whether the matrix represents a rigid transformation") .def("rotation_scaling", &Math::Matrix3::rotationScaling, "2D rotation and scaling part of the matrix") .def("rotation_shear", &Math::Matrix3::rotationShear, "2D rotation and shear part of the matrix") .def("rotation_normalized", &Math::Matrix3::rotationNormalized, "2D rotation part of the matrix assuming there is no scaling") .def("scaling_squared", &Math::Matrix3::scalingSquared, "Non-uniform scaling part of the matrix, squared") .def("uniform_scaling_squared", &Math::Matrix3::uniformScalingSquared, "Uniform scaling part of the matrix, squared") .def("uniform_scaling", &Math::Matrix3::uniformScaling, "Uniform scaling part of the matrix") .def("inverted_rigid", &Math::Matrix3::invertedRigid, "Inverted rigid transformation matrix") .def("transform_vector", &Math::Matrix3::transformVector, "Transform a 2D vector with the matrix") .def("transform_point", &Math::Matrix3::transformPoint, "Transform a 2D point with the matrix") /* Properties */ .def_property("right", static_cast(Math::Matrix3::*)() const>(&Math::Matrix3::right), [](Math::Matrix3& self, const Math::Vector2& value) { self.right() = value; }, "Right-pointing 2D vector") .def_property("up", static_cast(Math::Matrix3::*)() const>(&Math::Matrix3::up), [](Math::Matrix3& self, const Math::Vector2& value) { self.up() = value; }, "Up-pointing 2D vector") .def_property("_translation", // TODO static_cast(Math::Matrix3::*)() const>(&Math::Matrix3::translation), [](Math::Matrix3& self, const Math::Vector2& value) { self.translation() = value; }, "2D translation part of the matrix") /* Static/member scaling(). Pybind doesn't support that natively, so we create a scaling(*args, **kwargs) and dispatch ourselves. */ .def_static("_sscaling", static_cast(*)(const Math::Vector2&)>(&Math::Matrix3::scaling), "2D scaling matrix") .def("_iscaling", static_cast(Math::Matrix3::*)() const>(&Math::Matrix3::scaling), "Non-uniform scaling part of the matrix") .def("scaling", [matrix3](py::args args, py::kwargs kwargs) { if(py::len(args) && py::isinstance>(args[0])) { return matrix3.attr("_iscaling")(*args, **kwargs); } else { return matrix3.attr("_sscaling")(*args, **kwargs); } }) /* Static/member rotation(). Pybind doesn't support that natively, so we create a rotation(*args, **kwargs) and dispatch ourselves. */ .def_static("_srotation", [](Radd angle) { return Math::Matrix3::rotation(Math::Rad(angle)); }, "2D rotation matrix") .def("_irotation", static_cast(Math::Matrix3::*)() const>(&Math::Matrix3::rotation), "2D rotation part of the matrix") .def("rotation", [matrix3](py::args args, py::kwargs kwargs) { if(py::len(args) && py::isinstance>(args[0])) { return matrix3.attr("_irotation")(*args, **kwargs); } else { return matrix3.attr("_srotation")(*args, **kwargs); } }); /* 4x4 transformation matrix. Buffer constructors need to be *before* tuple constructors so numpy buffer protocol gets extracted correctly. */ py::implicitly_convertible, Math::Matrix4>(); everyRectangularMatrix(matrix4); everyMatrix(matrix4); matrix4 /* Constructors. The scaling() / rotation() are handled below as they conflict with member functions. */ .def_static("translation", static_cast(*)(const Math::Vector3&)>(&Math::Matrix4::translation), "3D translation matrix") .def_static("rotation_x", [](Radd angle) { return Math::Matrix4::rotationX(Math::Rad(angle)); }, "3D rotation matrix around the X axis") .def_static("rotation_y", [](Radd angle) { return Math::Matrix4::rotationY(Math::Rad(angle)); }, "3D rotation matrix around the Y axis") .def_static("rotation_z", [](Radd angle) { return Math::Matrix4::rotationZ(Math::Rad(angle)); }, "3D rotation matrix around the Z axis") .def_static("reflection", &Math::Matrix4::reflection, "3D reflection matrix") .def_static("shearing_xy", &Math::Matrix4::shearingXY, "3D shearing matrix along the XY plane", py::arg("amountx"), py::arg("amounty")) .def_static("shearing_xz", &Math::Matrix4::shearingXZ, "3D shearning matrix along the XZ plane", py::arg("amountx"), py::arg("amountz")) .def_static("shearing_yz", &Math::Matrix4::shearingYZ, "3D shearing matrix along the YZ plane", py::arg("amounty"), py::arg("amountz")) .def_static("orthographic_projection", &Math::Matrix4::orthographicProjection, "3D orthographic projection matrix", py::arg("size"), py::arg("near"), py::arg("far")) .def_static("perspective_projection", static_cast(*)(const Math::Vector2&, T, T)>(&Math::Matrix4::perspectiveProjection), "3D perspective projection matrix", py::arg("size"), py::arg("near"), py::arg("far")) .def_static("perspective_projection", [](Radd fov, T aspectRatio, T near, T far) { return Math::Matrix4::perspectiveProjection(Math::Rad(fov), aspectRatio, near, far); }, "3D perspective projection matrix", py::arg("fov"), py::arg("aspect_ratio"), py::arg("near"), py::arg("far")) .def_static("look_at", &Math::Matrix4::lookAt, "Matrix oriented towards a specific point", py::arg("eye"), py::arg("target"), py::arg("up")) .def_static("from", static_cast(*)(const Math::Matrix3x3&, const Math::Vector3&)>(&Math::Matrix4::from), "Create a matrix from a rotation/scaling part and a translation part", py::arg("rotation_scaling"), py::arg("translation")) .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::Matrix4{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)}; }), "Construct from a column vector tuple") .def(py::init([](const std::tuple, std::tuple, std::tuple, std::tuple>& value) { return Math::Matrix4{ Math::Vector4{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))}, Math::Vector4{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))}, Math::Vector4{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value)), std::get<3>(std::get<2>(value))}, Math::Vector4{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value)), std::get<2>(std::get<3>(value)), std::get<3>(std::get<3>(value))}, }; }), "Construct from a column tuple") /* Member functions */ .def("is_rigid_transformation", &Math::Matrix4::isRigidTransformation, "Check whether the matrix represents a rigid transformation") .def("rotation_scaling", &Math::Matrix4::rotationScaling, "3D rotation and scaling part of the matrix") .def("rotation_shear", &Math::Matrix4::rotationShear, "3D rotation and shear part of the matrix") .def("rotation_normalized", &Math::Matrix4::rotationNormalized, "3D rotation part of the matrix assuming there is no scaling") .def("scaling_squared", &Math::Matrix4::scalingSquared, "Non-uniform scaling part of the matrix, squared") .def("uniform_scaling_squared", &Math::Matrix4::uniformScalingSquared, "Uniform scaling part of the matrix, squared") .def("uniform_scaling", &Math::Matrix4::uniformScaling, "Uniform scaling part of the matrix") .def("inverted_rigid", &Math::Matrix4::invertedRigid, "Inverted rigid transformation matrix") .def("transform_vector", &Math::Matrix4::transformVector, "Transform a 3D vector with the matrix") .def("transform_point", &Math::Matrix4::transformPoint, "Transform a 3D point with the matrix") /* Properties */ .def_property("right", static_cast(Math::Matrix4::*)() const>(&Math::Matrix4::right), [](Math::Matrix4& self, const Math::Vector3& value) { self.right() = value; }, "Right-pointing 3D vector") .def_property("up", static_cast(Math::Matrix4::*)() const>(&Math::Matrix4::up), [](Math::Matrix4& self, const Math::Vector3& value) { self.up() = value; }, "Up-pointing 3D vector") .def_property("backward", static_cast(Math::Matrix4::*)() const>(&Math::Matrix4::backward), [](Math::Matrix4& self, const Math::Vector3& value) { self.backward() = value; }, "Backward-pointing 3D vector") .def_property("_translation", // TODO static_cast(Math::Matrix4::*)() const>(&Math::Matrix4::translation), [](Math::Matrix4& self, const Math::Vector3& value) { self.translation() = value; }, "3D translation part of the matrix") /* Static/member scaling(). Pybind doesn't support that natively, so we create a scaling(*args, **kwargs) and dispatch ourselves. */ .def_static("_sscaling", static_cast(*)(const Math::Vector3&)>(&Math::Matrix4::scaling), "3D scaling matrix") .def("_iscaling", static_cast(Math::Matrix4::*)() const>(&Math::Matrix4::scaling), "Non-uniform scaling part of the matrix") .def("scaling", [matrix4](py::args args, py::kwargs kwargs) { if(py::len(args) && py::isinstance>(args[0])) { return matrix4.attr("_iscaling")(*args, **kwargs); } else { return matrix4.attr("_sscaling")(*args, **kwargs); } }) /* Static/member rotation(). Pybind doesn't support that natively, so we create a rotation(*args, **kwargs) and dispatch ourselves. */ .def_static("_srotation", [](Radd angle, const Math::Vector3& axis) { return Math::Matrix4::rotation(Math::Rad(angle), axis); }, "3D rotation matrix around arbitrary axis") .def("_irotation", static_cast(Math::Matrix4::*)() const>(&Math::Matrix4::rotation), "3D rotation part of the matrix") .def("rotation", [matrix4](py::args args, py::kwargs kwargs) { if(py::len(args) && py::isinstance>(args[0])) { return matrix4.attr("_irotation")(*args, **kwargs); } else { return matrix4.attr("_srotation")(*args, **kwargs); } }); } } #endif