diff --git a/src/python/magnum/math.matrix.h b/src/python/magnum/math.matrix.h index f2654ee..cbc6b44 100644 --- a/src/python/magnum/math.matrix.h +++ b/src/python/magnum/math.matrix.h @@ -68,6 +68,34 @@ template void everyRectangularMatrix(py::class_(), "Construct a matrix with one value for all components") + /* 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"); +} + +/* Separate because it needs to be registered after the type conversion + constructors. Needs to be called also for subclasses. */ +template void everyRectangularMatrixBuffer(py::class_& c) { + c /* Buffer protocol, needed in order to properly detect row-major layouts. Has to be defined *before* the from-tuple constructor so it gets precedence for types that implement the buffer protocol. */ @@ -94,30 +122,7 @@ template void everyRectangularMatrix(py::class_::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"); + }), "Construct from a buffer"); } template bool rectangularMatrixBufferProtocol(T& self, Py_buffer& buffer, int flags) { diff --git a/src/python/magnum/math.matrixdouble.cpp b/src/python/magnum/math.matrixdouble.cpp index 27a99a0..0375016 100644 --- a/src/python/magnum/math.matrixdouble.cpp +++ b/src/python/magnum/math.matrixdouble.cpp @@ -44,18 +44,12 @@ void mathMatrixDouble(py::module& root) { done by the base classes. Moreover, just adding py::buffer_protocol{} would cause it to not find the buffer functions as we don't add them anywhere, thus failing with `pybind11_getbuffer(): Internal error`. */ - py::class_ matrix3d{root, "Matrix3d", "2D double transformation matrix"}; py::class_ matrix4d{root, "Matrix4d", "3D double transformation matrix"}; - matrices( - matrix2x2d, matrix2x3d, matrix2x4d, - matrix3x2d, matrix3x3d, matrix3x4d, - matrix4x2d, matrix4x3d, matrix4x4d, - matrix3d, matrix4d); - - /* At this point we should have both float and double types registered, - so register type conversions */ + /* Register type conversions as soon as possible as those should have a + priority over buffer and list constructors. These need all the types to + be present, so can't be interwinted with the class definitions above. */ convertible(matrix2x2d); convertible(matrix2x3d); convertible(matrix2x4d); @@ -65,9 +59,33 @@ void mathMatrixDouble(py::module& root) { convertible(matrix4x2d); convertible(matrix4x3d); convertible(matrix4x4d); - convertible(matrix3d); convertible(matrix4d); + + /* This needs to be *after* conversion constructors so the type conversion + gets picked before the general buffer constructor (which would then + fail). On the other hand, this needs to be before generic from-list + constructors because buffer protocol is generally faster than + iteration. */ + everyRectangularMatrixBuffer(matrix2x2d); + everyRectangularMatrixBuffer(matrix2x3d); + everyRectangularMatrixBuffer(matrix2x4d); + everyRectangularMatrixBuffer(matrix3x2d); + everyRectangularMatrixBuffer(matrix3x3d); + everyRectangularMatrixBuffer(matrix3x4d); + everyRectangularMatrixBuffer(matrix4x2d); + everyRectangularMatrixBuffer(matrix4x3d); + everyRectangularMatrixBuffer(matrix4x4d); + everyRectangularMatrixBuffer(matrix3d); + everyRectangularMatrixBuffer(matrix4d); + + /* Now register the generic from-list constructors and everything else */ + matrices( + matrix2x2d, matrix2x3d, matrix2x4d, + matrix3x2d, matrix3x3d, matrix3x4d, + matrix4x2d, matrix4x3d, matrix4x4d, + matrix3d, matrix4d); + } } diff --git a/src/python/magnum/math.matrixfloat.cpp b/src/python/magnum/math.matrixfloat.cpp index 75d8a41..de2700d 100644 --- a/src/python/magnum/math.matrixfloat.cpp +++ b/src/python/magnum/math.matrixfloat.cpp @@ -44,20 +44,16 @@ void mathMatrixFloat(py::module& root) { done by the base classes. Moreover, just adding py::buffer_protocol{} would cause it to not find the buffer functions as we don't add them anywhere, thus failing with `pybind11_getbuffer(): Internal error`. */ - py::class_ matrix3{root, "Matrix3", "2D float transformation matrix"}; py::class_ matrix4{root, "Matrix4", "3D float transformation matrix"}; - matrices( - matrix2x2, matrix2x3, matrix2x4, - matrix3x2, matrix3x3, matrix3x4, - matrix4x2, matrix4x3, matrix4x4, - matrix3, matrix4); - /* Register the double types as well, only after that register type conversions because they need all the types */ mathMatrixDouble(root); + /* Register type conversions as soon as possible as those should have a + priority over buffer and list constructors. These need all the types to + be present, so can't be interwinted with the class definitions above. */ convertible(matrix2x2); convertible(matrix2x3); convertible(matrix2x4); @@ -67,9 +63,32 @@ void mathMatrixFloat(py::module& root) { convertible(matrix4x2); convertible(matrix4x3); convertible(matrix4x4); - convertible(matrix3); convertible(matrix4); + + /* This needs to be *after* conversion constructors so the type conversion + gets picked before the general buffer constructor (which would then + fail). On the other hand, this needs to be before generic from-list + constructors because buffer protocol is generally faster than + iteration. */ + everyRectangularMatrixBuffer(matrix2x2); + everyRectangularMatrixBuffer(matrix2x3); + everyRectangularMatrixBuffer(matrix2x4); + everyRectangularMatrixBuffer(matrix3x2); + everyRectangularMatrixBuffer(matrix3x3); + everyRectangularMatrixBuffer(matrix3x4); + everyRectangularMatrixBuffer(matrix4x2); + everyRectangularMatrixBuffer(matrix4x3); + everyRectangularMatrixBuffer(matrix4x4); + everyRectangularMatrixBuffer(matrix3); + everyRectangularMatrixBuffer(matrix4); + + /* Now register the generic from-list constructors and everything else */ + matrices( + matrix2x2, matrix2x3, matrix2x4, + matrix3x2, matrix3x3, matrix3x4, + matrix4x2, matrix4x3, matrix4x4, + matrix3, matrix4); } } diff --git a/src/python/magnum/math.vector.h b/src/python/magnum/math.vector.h index b71837a..c586fc0 100644 --- a/src/python/magnum/math.vector.h +++ b/src/python/magnum/math.vector.h @@ -111,8 +111,8 @@ template void everyVector(py::class_& c) { } /* Separate because it needs to be registered after the type conversion - constructors */ -template void vectorBuffer(py::class_& c) { + constructors. Needs to be called also for subclasses. */ +template void everyVectorBuffer(py::class_& c) { c /* Buffer protocol. If not present, implicit conversion from numpy arrays of non-default types somehow doesn't work. There's also the @@ -437,17 +437,23 @@ template void color3(py::class_, Math::Vector3>& c) .def("value", &Math::Color3::value, "Value"); } +/* Needs to be separate to make it a priority over buffer protocol */ +template void color4from3(py::class_, Math::Vector4>& c) { + py::implicitly_convertible&, Math::Color4>(); + + c + .def(py::init, T>(), "Construct from a three-component color", py::arg("rgb"), py::arg("alpha") = Math::Implementation::fullChannel()) + .def(py::init>(), "Construct from a vector"); +} + template void color4(py::class_, Math::Vector4>& c) { py::implicitly_convertible&, Math::Color4>(); py::implicitly_convertible&, Math::Color4>(); - py::implicitly_convertible&, Math::Color4>(); c /* Constructors */ .def(py::init(), "Constructor", py::arg("r"), py::arg("g"), py::arg("b"), py::arg("a") = Math::Implementation::fullChannel()) .def(py::init(), "Construct with one value for all components", py::arg("rgb"), py::arg("alpha") = Math::Implementation::fullChannel()) - .def(py::init, T>(), "Construct from a vector", py::arg("rgb"), py::arg("alpha") = Math::Implementation::fullChannel()) - .def(py::init>(), "Construct from a vector") .def(py::init([](const std::tuple& value) { return Math::Color4{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; }), "Construct from a RGB tuple") diff --git a/src/python/magnum/math.vectorfloat.cpp b/src/python/magnum/math.vectorfloat.cpp index e31a94d..97907b0 100644 --- a/src/python/magnum/math.vectorfloat.cpp +++ b/src/python/magnum/math.vectorfloat.cpp @@ -86,28 +86,21 @@ void mathVectorFloat(py::module& root, py::module& m) { py::class_ vector2d{root, "Vector2d", "Two-component double vector", py::buffer_protocol{}}; py::class_ vector3d{root, "Vector3d", "Threee-component double vector", py::buffer_protocol{}}; py::class_ vector4d{root, "Vector4d", "Four-component double vector", py::buffer_protocol{}}; - vectorsFloat(m, vector2, vector3, vector4); - vectorsFloat(m, vector2d, vector3d, vector4d); /* The subclasses don't have buffer protocol enabled, as that's already done by the base classes. Moreover, just adding py::buffer_protocol{} would cause it to not find the buffer functions as we don't add them anywhere, thus failing with `pybind11_getbuffer(): Internal error`. */ - py::class_ color3_{root, "Color3", "Color in linear RGB color space"}; - everyVector(color3_); - color(color3_); - color3(color3_); - py::class_ color4_{root, "Color4", "Color in linear RGBA color space"}; - everyVector(color4_); - color(color4_); - color4(color4_); - /* Register the integer types as well, only after that register type + /* Register the integer types first, only after that register type conversions because they need all the types */ mathVectorIntegral(root, m); + /* Register type conversions as soon as possible as those should have a + priority over buffer and list constructors. These need all the types to + be present, so can't be interwinted with the class definitions above. */ convertible(vector2); convertible(vector3); convertible(vector4); @@ -116,17 +109,34 @@ void mathVectorFloat(py::module& root, py::module& m) { convertible(vector4d); /* Colors are float-only at the moment, thus no conversions */ + /* This needs to be before buffer constructors otherwise a buffer + constructor gets picked and it will fail because there are just 3 + elements */ + color4from3(color4_); + /* This needs to be *after* conversion constructors so the type conversion gets picked before the general buffer constructor (which would then - fail) */ - vectorBuffer(vector2); - vectorBuffer(vector3); - vectorBuffer(vector4); - vectorBuffer(vector2d); - vectorBuffer(vector3d); - vectorBuffer(vector4d); - vectorBuffer(color3_); - vectorBuffer(color4_); + fail). On the other hand, this needs to be before generic from-list + constructors because buffer protocol is generally faster than + iteration. */ + everyVectorBuffer(vector2); + everyVectorBuffer(vector3); + everyVectorBuffer(vector4); + everyVectorBuffer(vector2d); + everyVectorBuffer(vector3d); + everyVectorBuffer(vector4d); + everyVectorBuffer(color3_); + everyVectorBuffer(color4_); + + /* Now register the generic from-list constructors and everything else */ + vectorsFloat(m, vector2, vector3, vector4); + vectorsFloat(m, vector2d, vector3d, vector4d); + everyVector(color3_); + color(color3_); + color3(color3_); + everyVector(color4_); + color(color4_); + color4(color4_); } } diff --git a/src/python/magnum/math.vectorintegral.cpp b/src/python/magnum/math.vectorintegral.cpp index ac0daf7..20757dc 100644 --- a/src/python/magnum/math.vectorintegral.cpp +++ b/src/python/magnum/math.vectorintegral.cpp @@ -79,11 +79,9 @@ void mathVectorIntegral(py::module& root, py::module& m) { py::class_ vector2ui{root, "Vector2ui", "Two-component unsigned integral vector", py::buffer_protocol{}}; py::class_ vector3ui{root, "Vector3ui", "Threee-component unsigned integral vector", py::buffer_protocol{}}; py::class_ vector4ui{root, "Vector4ui", "Four-component unsigned integral vector", py::buffer_protocol{}}; - vectorsIntegral(m, vector2i, vector3i, vector4i); - vectorsIntegral(m, vector2ui, vector3ui, vector4ui); - /* At this point we should have both float and integral types registered, - so register type conversions */ + /* First register type conversions as those should have a priority over + buffer and list constructors. */ convertible(vector2i); convertible(vector3i); convertible(vector4i); @@ -93,13 +91,19 @@ void mathVectorIntegral(py::module& root, py::module& m) { /* This needs to be *after* conversion constructors so the type conversion gets picked before the general buffer constructor (which would then - fail) */ - vectorBuffer(vector2i); - vectorBuffer(vector3i); - vectorBuffer(vector4i); - vectorBuffer(vector2ui); - vectorBuffer(vector3ui); - vectorBuffer(vector4ui); + fail). On the other hand, this needs to be before generic from-list + constructors because buffer protocol is generally faster than + iteration. */ + everyVectorBuffer(vector2i); + everyVectorBuffer(vector3i); + everyVectorBuffer(vector4i); + everyVectorBuffer(vector2ui); + everyVectorBuffer(vector3ui); + everyVectorBuffer(vector4ui); + + /* Now register the generic from-list constructors and everything else */ + vectorsIntegral(m, vector2i, vector3i, vector4i); + vectorsIntegral(m, vector2ui, vector3ui, vector4ui); } }