diff --git a/Plugin/src/SofaPython3/DataHelper.cpp b/Plugin/src/SofaPython3/DataHelper.cpp index 3d108284..80aaa449 100644 --- a/Plugin/src/SofaPython3/DataHelper.cpp +++ b/Plugin/src/SofaPython3/DataHelper.cpp @@ -48,12 +48,20 @@ std::string toSofaParsableString(const py::handle& p) if(py::isinstance(p)) return py::str(p); - // Insure compatibility with data field code returning value instead of data. + // If the object is a data field we link the data field if(py::isinstance(p)) { sofa::core::objectmodel::BaseData* data = py::cast(p); return data->getValueString(); } + + // If the object is a numpy array we convert it to a list then to a sofa string. + if(py::isinstance(p)) + { + py::object o = p.attr("tolist")(); + return toSofaParsableString(o); + } + return py::repr(p); } diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp index 3eafdd5e..ef7f14d3 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp @@ -387,7 +387,13 @@ std::string BindingBase::getPathName(Base& self) { return self.toBaseNode() ? self.toBaseNode()->getPathName() : self.toBaseObject()->getPathName(); } - + +std::string BindingBase::getLinkPath(Base& self) +{ + return "@"+getPathName(self); +} + + py::object BindingBase::setDataValues(Base& self, py::kwargs kwargs) { for(auto key : kwargs) @@ -435,7 +441,8 @@ void moduleAddBase(py::module &m) base.def("getLoggedMessagesAsString", &BindingBase::getLoggedMessagesAsString, sofapython3::doc::base::getLoggedMessagesAsString); base.def("countLoggedMessages", &BindingBase::countLoggedMessages, sofapython3::doc::base::countLoggedMessages); base.def("clearLoggedMessages", &BindingBase::clearLoggedMessages, sofapython3::doc::base::clearLoggedMessages); - base.def("getPathName", &BindingBase::getPathName); + base.def("getPathName", &BindingBase::getPathName, sofapython3::doc::base::getPathName); + base.def("getLinkPath", &BindingBase::getLinkPath, sofapython3::doc::base::getLinkPath); base.def("setDataValues", &BindingBase::setDataValues, sofapython3::doc::base::setDataValues); } diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h index 7f5a6c15..127fc534 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h @@ -64,6 +64,7 @@ class BindingBase static pybind11::object countLoggedMessages(sofa::core::objectmodel::Base& self); static pybind11::object clearLoggedMessages(sofa::core::objectmodel::Base& self); static std::string getPathName(sofa::core::objectmodel::Base& self); + static std::string getLinkPath(sofa::core::objectmodel::Base& self); }; diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp index c61cc9a9..9997f56c 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp @@ -112,6 +112,7 @@ py::object writeableArray(BaseData* self) void __setattr__(py::object self, const std::string& s, py::object value) { + SOFA_UNUSED(s); BaseData* selfdata = py::cast(self); if(py::isinstance(value)) @@ -137,6 +138,9 @@ py::object __getattr__(py::object self, const std::string& s) if(s == "value") return PythonFactory::valueToPython_ro(py::cast(self)); + if(s == "linkpath") + return py::cast((py::cast(self))->getLinkPath()); + /// BaseData does not support dynamic attributes, if you think this is an important feature /// please request for its integration. throw py::attribute_error("There is no attribute '"+s+"'"); diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base_doc.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base_doc.h index 2086ac27..76a37638 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base_doc.h +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base_doc.h @@ -63,6 +63,22 @@ static auto findData = :type name: string :return: the data field )"; +static auto getLinkPath = + R"( + returns the path name as a parsable string. + eg: + if object.getPathName() is "/child1/object" + then the linkPath is @"/child1/object" + )"; +static auto getPathName = + R"( + returns the path name as a string. + .. code-block:: python + a = Sofa.Core.Node("root") + b.addObject("Camera", name="camera") + b.getPathName() # should returns "/root/camera" + ``` + )"; static auto setDataValues = R"( Set values for a the given data field, multiple pairs of args are allowed. diff --git a/bindings/Sofa/tests/Core/Base.py b/bindings/Sofa/tests/Core/Base.py index e7be0f0a..01a8daf9 100644 --- a/bindings/Sofa/tests/Core/Base.py +++ b/bindings/Sofa/tests/Core/Base.py @@ -123,7 +123,11 @@ def test_getTemplateName(self): c = root.addObject("MechanicalObject", name="t") self.assertEqual(c.getTemplateName(),"Vec3d") - + def test_getLinkPath(self): + root = create_scene("root") + obj = root.addObject("MechanicalObject", name="obj") + self.assertEqual(obj.getPathName(),"/obj") + self.assertEqual(obj.getLinkPath(),"@/obj") def test_addExistingDataAsParentOfNewData(self): # TODO(@marques-bruno) diff --git a/bindings/Sofa/tests/Core/BaseData.py b/bindings/Sofa/tests/Core/BaseData.py index 34d99d9a..f429360d 100644 --- a/bindings/Sofa/tests/Core/BaseData.py +++ b/bindings/Sofa/tests/Core/BaseData.py @@ -292,6 +292,11 @@ def t(c): self.assertEqual(wa[2, 2], 8.0) numpy.testing.assert_array_equal(wa, v*4.0) + def test_linkpath(self): + n = create_scene("rootNode") + m = n. addObject("MechanicalObject", name="dofs") + self.assertEqual(m.position.linkpath, "@/dofs.position") + def test_set_value_from_string(self): n = create_scene("rootNode") n.gravity.value = [1.0,2.0,3.0]