From fa40138d87339e0f6924cc98b9cccedf6f8f81e0 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 17 Jun 2026 14:56:34 +0200 Subject: [PATCH] Fix/add/test Mat bindings --- .../src/SofaPython3/SofaTypes/Binding_Mat.cpp | 29 ++++++++- .../src/SofaPython3/SofaTypes/Binding_Vec.cpp | 44 +++++++------ bindings/SofaTypes/tests/CMakeLists.txt | 1 + bindings/SofaTypes/tests/pyfiles/mat_test.py | 64 +++++++++++++++++++ .../SofaTypes/tests/pyfiles/vector_test.py | 1 + 5 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 bindings/SofaTypes/tests/pyfiles/mat_test.py diff --git a/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Mat.cpp b/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Mat.cpp index 158dbd331..bbe053614 100644 --- a/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Mat.cpp +++ b/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Mat.cpp @@ -49,6 +49,7 @@ template static void bindSquaredMat(py::class_> p.def("transpose", (void (MatClass::*)()) & MatClass::transpose); p.def("inverted", [](MatClass &mat){ return mat.inverted(); }); p.def("invert", [](MatClass &dest, MatClass &from){ return dest.invert(from); }); + p.def("trace", [](const MatClass& mat){ return sofa::type::trace(mat); }); } template @@ -150,8 +151,14 @@ static void addMat(py::module & /*m*/, py::class_> &p) { p.def(py::self * double()); p.def(double() * py::self); p.def(py::self / double()); - p.def(py::self *= double()); - p.def(py::self /= double()); + p.def("__imul__", [](MatClass& self, double value) -> MatClass& { + self *= value; + return self; + }, py::return_value_policy::reference_internal); + p.def("__itruediv__", [](MatClass& self, double value) -> MatClass& { + self /= value; + return self; + }, py::return_value_policy::reference_internal); p.def(py::self += py::self); p.def(py::self -= py::self); @@ -163,6 +170,16 @@ static void addMat(py::module & /*m*/, py::class_> &p) { p.def("__repr__", [](MatClass &self) { return pyMat::__str__(self, true); }); } +template +static void addMatProduct(py::module & /*m*/, py::class_> &p) +{ + using LHS = Mat; + using RHS = Mat; + p.def("__mul__", [](const LHS &self, const RHS &m) { + return self * m; + }); +} + // Generic bindings for Matrices template struct MATRIX { static void addMat(py::module &m) { @@ -205,6 +222,8 @@ template <> struct MATRIX<1, 1> { return std::unique_ptr(mat); })); ::addMat(m, p); + + addMatProduct<1,1, 1,1>(m, p); } }; @@ -234,6 +253,8 @@ template <> struct MATRIX<2, 2> { return std::unique_ptr(mat); })); ::addMat(m, p); + + addMatProduct<2,2, 2,2>(m, p); } }; @@ -263,6 +284,8 @@ template <> struct MATRIX<3, 3> { return std::unique_ptr(mat); })); ::addMat(m, p); + + addMatProduct<3,3, 3,3>(m, p); } }; @@ -292,6 +315,8 @@ template <> struct MATRIX<4, 4> { return std::unique_ptr(mat); })); ::addMat(m, p); + + addMatProduct<4,4, 4,4>(m, p); } }; diff --git a/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Vec.cpp b/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Vec.cpp index 26476cd69..f2012063e 100644 --- a/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Vec.cpp +++ b/bindings/SofaTypes/src/SofaPython3/SofaTypes/Binding_Vec.cpp @@ -87,11 +87,11 @@ void setFromPartialSequence(const VecClass& s, py::list t) for(unsigned int i=0;i -py::class_> addVec(py::module &m) +template class V = sofa::type::Vec> +py::class_> addVec(py::module &m) { - typedef Vec VecClass; - py::class_> p(m, sofa::defaulttype::DataTypeInfo< Vec >::name().c_str()); + typedef V VecClass; + py::class_> p(m, sofa::defaulttype::DataTypeInfo< V >::name().c_str()); p.def(py::init<>()); // empty ctor p.def(py::init()); // copy ctor @@ -230,26 +230,30 @@ T addCross(T p) return p; } -template +template class V = sofa::type::Vec> void addVectorsFor(py::module& m) { - addVec<1, T>(m); - addCross( addVec<2, T>(m) ); - addCross( addVec<3, T>(m) ); - addVec<4, T>(m); - addVec<5, T>(m); - addVec<6, T>(m); - addVec<7, T>(m); - addVec<8, T>(m); - addVec<9, T>(m); - addVec<10, T>(m); - addVec<11, T>(m); - addVec<12, T>(m); + addVec<1, T, V>(m); + addCross( addVec<2, T, V>(m) ); + addCross( addVec<3, T, V>(m) ); + addVec<4, T, V>(m); + addVec<5, T, V>(m); + addVec<6, T, V>(m); + addVec<7, T, V>(m); + addVec<8, T, V>(m); + addVec<9, T, V>(m); + addVec<10, T, V>(m); + addVec<11, T, V>(m); + addVec<12, T, V>(m); } void moduleAddVec(py::module &m) { - addVectorsFor(m); - addVectorsFor(m); - addVectorsFor(m); + addVectorsFor(m); + addVectorsFor(m); + addVectorsFor(m); + + addVectorsFor(m); + addVectorsFor(m); + addVectorsFor(m); } diff --git a/bindings/SofaTypes/tests/CMakeLists.txt b/bindings/SofaTypes/tests/CMakeLists.txt index 2f9ffc6e5..7c2c6a30f 100644 --- a/bindings/SofaTypes/tests/CMakeLists.txt +++ b/bindings/SofaTypes/tests/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCE_FILES set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/pyfiles/vector_test.py + ${CMAKE_CURRENT_SOURCE_DIR}/pyfiles/mat_test.py ) find_package(Sofa.Testing REQUIRED) diff --git a/bindings/SofaTypes/tests/pyfiles/mat_test.py b/bindings/SofaTypes/tests/pyfiles/mat_test.py new file mode 100644 index 000000000..a45b5657e --- /dev/null +++ b/bindings/SofaTypes/tests/pyfiles/mat_test.py @@ -0,0 +1,64 @@ +import math +import unittest + +import Sofa +from SofaTypes import Mat1x1, Mat2x2, Mat3x3, Vec2d + +class TestMaterialMatrix(unittest.TestCase): + + def test_Constructors(self): + # --- Mat1x1 (Scalar/Row Vector) Construction --- + m = Mat1x1() # Empty constructor + self.assertEqual(m[0][0], 0.0) + + m = Mat1x1([[math.pi]]) # list ctor (or direct assignment) + self.assertEqual(m[0][0], math.pi) + + # --- Mat2x2 Construction --- + m = Mat2x2([[1.0, 2.0], [3.0, 4.0]]) # list of lists ctor + self.assertEqual(m[0][0], 1.0) + self.assertEqual(m[0][1], 2.0) + self.assertEqual(m[1][0], 3.0) + self.assertEqual(m[1][1], 4.0) + + # --- Mat3x3 Construction --- + m = Mat3x3([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]) + self.assertEqual(m[0][0], 1.0) + self.assertEqual(m[0][1], 2.0) + self.assertEqual(m[0][2], 3.0) + self.assertEqual(m[1][0], 4.0) + self.assertEqual(m[1][1], 5.0) + self.assertEqual(m[1][2], 6.0) + self.assertEqual(m[2][0], 7.0) + self.assertEqual(m[2][1], 8.0) + self.assertEqual(m[2][2], 9.0) + + def test_Operators(self): + m1 = Mat2x2([[1.0, 2.0], [3.0, 4.0]]) + m2 = Mat2x2([[5.0, 6.0], [7.0, 8.0]]) + + # Matrix addition + sum_mat = m1 + m2 + expected_sum = Mat2x2([[6.0, 8.0], [10.0, 12.0]]) + self.assertEqual(sum_mat, expected_sum) + + # Matrix subtraction + diff_mat = m1 - m2 + expected_diff = Mat2x2([[-4.0, -4.0], [-4.0, -4.0]]) + self.assertEqual(diff_mat, expected_diff) + + # Scalar multiplication (A * 2.0) + scaled_mat = m1 * 2.0 + expected_scale = Mat2x2([[2.0, 4.0], [6.0, 8.0]]) + self.assertEqual(scaled_mat, expected_scale) + + # Matrix multiplication (A * B) + mat_product = m1 * m2 + expected_mat_product = Mat2x2([[1.0 * 5.0 + 2.0 * 7.0, 1.0 * 6.0 + 2.0 * 8.0], [3.0 * 5.0 + 4.0 * 7.0, 3.0 * 6.0 + 4.0 * 8.0]]) + self.assertEqual(mat_product, expected_mat_product) + + # Testing assignment/in-place modification if available (e.g., /=) + m_test = Mat2x2([[1.0, 2.0], [3.0, 4.0]]) + m_test /= 2.0 + expected_inplace_div = Mat2x2([[0.5, 1.0], [1.5, 2.0]]) + self.assertEqual(m_test, expected_inplace_div) diff --git a/bindings/SofaTypes/tests/pyfiles/vector_test.py b/bindings/SofaTypes/tests/pyfiles/vector_test.py index 32f938a1f..262d6c354 100644 --- a/bindings/SofaTypes/tests/pyfiles/vector_test.py +++ b/bindings/SofaTypes/tests/pyfiles/vector_test.py @@ -1,6 +1,7 @@ import math import unittest +import Sofa import SofaTypes from SofaTypes import Vec1d, Vec2d, Vec3d, Vec4d, Vec4i