diff --git a/Plugin/CMakeLists.txt b/Plugin/CMakeLists.txt index b5bb2817..3d11a754 100644 --- a/Plugin/CMakeLists.txt +++ b/Plugin/CMakeLists.txt @@ -8,6 +8,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/DataCache.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/DataHelper.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/LinkPath.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/PythonFactory.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/Prefab.h ) @@ -19,6 +20,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/DataCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/DataHelper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/LinkPath.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/PythonFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/Prefab.cpp ) diff --git a/Plugin/src/SofaPython3/DataHelper.cpp b/Plugin/src/SofaPython3/DataHelper.cpp index e361afb4..95bafba5 100644 --- a/Plugin/src/SofaPython3/DataHelper.cpp +++ b/Plugin/src/SofaPython3/DataHelper.cpp @@ -25,9 +25,11 @@ #include #include +#include #include #include + using sofa::core::objectmodel::BaseLink; namespace sofapython3 @@ -62,6 +64,12 @@ std::string toSofaParsableString(const py::handle& p) return toSofaParsableString(o); } + // if the object is a link path we set it. + if(py::isinstance(p)) + { + return py::str(p); + } + return py::repr(p); } @@ -463,7 +471,7 @@ BaseData* deriveTypeFromParent(sofa::core::objectmodel::BaseContext* ctx, const bool isProtectedKeyword(const std::string& name) { if (name == "children" || name == "objects" || name == "parents" || - name == "data" || name == "links") + name == "data" || name == "links" || name == "linkpath") { return true; } diff --git a/Plugin/src/SofaPython3/LinkPath.cpp b/Plugin/src/SofaPython3/LinkPath.cpp new file mode 100644 index 00000000..e642577e --- /dev/null +++ b/Plugin/src/SofaPython3/LinkPath.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2021 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include + +namespace sofapython3 +{ + +LinkPath::LinkPath(sofa::core::sptr target) +{ + targetBase = target; + targetData = nullptr; +} + +LinkPath::LinkPath(sofa::core::objectmodel::BaseData* target) +{ + targetBase = target->getOwner(); + targetData = target; +} + +bool LinkPath::isPointingToData() const +{ + return targetData != nullptr; +} + +}/// namespace sofapython3 diff --git a/Plugin/src/SofaPython3/LinkPath.h b/Plugin/src/SofaPython3/LinkPath.h new file mode 100644 index 00000000..7548a398 --- /dev/null +++ b/Plugin/src/SofaPython3/LinkPath.h @@ -0,0 +1,46 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2021 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once + +#include +#include + +namespace sofapython3 +{ + +/// A placeholder class storing a link to a data or a base +/// +/// LinkPath can be used to indicate that a link is needed to be made in place of a string +/// like "@/usr/myobj.position" or "@/usr/myobj" +/// +class LinkPath +{ +public: + sofa::core::sptr targetBase; + sofa::core::objectmodel::BaseData* targetData; + + LinkPath(sofa::core::sptr); + LinkPath(sofa::core::objectmodel::BaseData*); + + bool isPointingToData() const; +}; + +} /// sofapython3 diff --git a/Plugin/src/SofaPython3/PythonFactory.cpp b/Plugin/src/SofaPython3/PythonFactory.cpp index 77031e91..fadb493a 100644 --- a/Plugin/src/SofaPython3/PythonFactory.cpp +++ b/Plugin/src/SofaPython3/PythonFactory.cpp @@ -52,6 +52,7 @@ using sofa::core::objectmodel::ScriptEvent; using sofa::core::objectmodel::Event; #include +#include #include @@ -293,6 +294,12 @@ void copyFromListOf(BaseData& d, const AbstractTypeInfo& nfo, const void PythonFactory::fromPython(BaseData* d, const py::object& o) { + if(py::isinstance(o)) + { + d->setParent(py::cast(o).targetData); + return; + } + const AbstractTypeInfo& nfo{ *(d->getValueTypeInfo()) }; // Is this data field a container ? diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp index 0f88c558..bb32dadd 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp @@ -33,6 +33,7 @@ using sofa::helper::WriteOnlyAccessor; #include +#include #include #include #include @@ -90,6 +91,10 @@ py::object BindingBase::GetAttr(Base* self, const std::string& s, bool doThrowEx if( s == "__data__") return py::cast( DataDict(self) ); + /// Returns the linkpath to the self object. + if(s == "linkpath") + return py::cast(sofapython3::LinkPath(self)); + if(doThrowException) throw py::attribute_error("Missing attribute: "+s); @@ -101,18 +106,34 @@ bool BindingBase::SetData(BaseData* d, py::object value) if(d==nullptr) return false; - const AbstractTypeInfo& nfo{ *(d->getValueTypeInfo()) }; - PythonFactory::fromPython(d, value); return true; } +bool BindingBase::SetLink(BaseLink* link, py::object value) +{ + if(link==nullptr) + return false; + + if(py::isinstance(value)) + return link->read(py::cast(value)); + + if(py::isinstance(value)) + { + auto& target = py::cast(value); + if(target.isPointingToData()) + throw std::runtime_error("Passing a link to a data field instead of an object."); + link->setLinkedBase(target.targetBase.get()); + } + return true; +} + void BindingBase::SetAttr(py::object self, const std::string& s, py::object value) { Base* self_d = py::cast(self); - BaseData* d = self_d->findData(s); + BaseData* d = self_d->findData(s); if(d!=nullptr) { SetData(d, value); @@ -122,6 +143,7 @@ void BindingBase::SetAttr(py::object self, const std::string& s, py::object valu BaseLink* l = self_d->findLink(s); if(l!=nullptr) { + SetLink(l, value); return; } @@ -132,17 +154,8 @@ void BindingBase::SetAttr(py::object self, const std::string& s, py::object valu void BindingBase::SetAttr(Base& self, const std::string& s, py::object value) { BaseData* d = self.findData(s); - if(d!=nullptr) { - const AbstractTypeInfo& nfo{ *(d->getValueTypeInfo()) }; - - /// We go for the container path. - if(nfo.Container()) - { - PythonFactory::fromPython(d,value); - return; - } PythonFactory::fromPython(d, value); return; } @@ -150,6 +163,7 @@ void BindingBase::SetAttr(Base& self, const std::string& s, py::object value) BaseLink* l = self.findLink(s); if(l!=nullptr) { + SetLink(l, value); return; } diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h index 127fc534..670ce54b 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h @@ -48,6 +48,7 @@ class BindingBase /// Set the data field value from the array. static void SetDataFromArray(sofa::core::objectmodel::BaseData* data, const pybind11::array& value); static bool SetData(sofa::core::objectmodel::BaseData* data, pybind11::object value); + static bool SetLink(sofa::core::objectmodel::BaseLink* link, pybind11::object value); static pybind11::object setDataValues(sofa::core::objectmodel::Base& self, pybind11::kwargs kwargs); static pybind11::list getDataFields(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 a6a54072..a77b1d81 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -139,7 +140,7 @@ py::object __getattr__(py::object self, const std::string& s) return PythonFactory::valueToPython_ro(py::cast(self)); if(s == "linkpath") - return py::cast((py::cast(self))->getLinkPath()); + return py::cast(sofapython3::LinkPath(py::cast(self))); /// BaseData does not support dynamic attributes, if you think this is an important feature /// please request for its integration. @@ -151,11 +152,18 @@ void setParent(BaseData* self, BaseData* parent) self->setParent(parent); } -void setParentFromLinkPath(BaseData* self, const std::string& parent) +void setParentFromLinkPathStr(BaseData* self, const std::string& parent) { self->setParent(parent); } +void setParentFromLinkPath(BaseData* self, const LinkPath& parent) +{ + if(!parent.isPointingToData()) + throw std::runtime_error("The provided linkpath is not containing a linkable data"); + self->setParent(parent.targetData); +} + bool hasParent(BaseData *self) { return (self->getParent() != nullptr); @@ -205,6 +213,7 @@ void moduleAddBaseData(py::module& m) data.def("isPersistent", &BaseData::isPersistent, sofapython3::doc::baseData::isPersistent); data.def("setPersistent", &BaseData::setPersistent, sofapython3::doc::baseData::setPersistent); data.def("setParent", setParent, sofapython3::doc::baseData::setParent); + data.def("setParent", setParentFromLinkPathStr, sofapython3::doc::baseData::setParent); data.def("setParent", setParentFromLinkPath, sofapython3::doc::baseData::setParent); data.def("hasParent", hasParent, sofapython3::doc::baseData::hasParent); data.def("read", &BaseData::read, sofapython3::doc::baseData::read); diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath.cpp new file mode 100644 index 00000000..d08b0e57 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath.cpp @@ -0,0 +1,84 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2021 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +using sofa::core::objectmodel::BaseLink; + +#include +using sofa::core::objectmodel::BaseObject; + +#include +#include + +/// Makes an alias for the pybind11 namespace to increase readability. +namespace py { using namespace pybind11; } + +// To bring in the `_a` literal +using namespace pybind11::literals; + +namespace sofapython3 +{ + +LinkPath::LinkPath(sofa::core::sptr target) +{ + targetBase = target; + targetData = nullptr; +} + +LinkPath::LinkPath(sofa::core::objectmodel::BaseData* target) +{ + // If the data is attached to + if(target->getOwner()) + { + targetBase = target->getOwner(); + } + targetData = target; +} + +bool LinkPath::isPointingToData() const +{ + return targetData != nullptr; +} + +std::string __str__(const LinkPath& entry) +{ + std::ostringstream s; + if(entry.targetData != nullptr) + return entry.targetData->getLinkPath(); + else if(entry.targetBase.get() != nullptr) + return "@" + entry.targetBase->getPathName(); + throw std::runtime_error("Empty LinkPath"); +} + +std::string __repr__(const LinkPath& entry) +{ + std::ostringstream s; + s << "LinkPath(\"" << __str__(entry) << "\")"; + return s.str(); +} + +void moduleAddLinkPath(py::module& m) +{ + py::class_ link(m, "LinkPath", sofapython3::doc::linkpath::linkpath); + link.def("__str__", &__str__); + link.def("__repr__", &__repr__); +} + +}/// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath.h new file mode 100644 index 00000000..76dff72a --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath.h @@ -0,0 +1,44 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2021 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace sofapython3 { + +/// A placeholder class storing a link to a data or a base +class LinkPath +{ +public: + sofa::core::sptr targetBase; + sofa::core::objectmodel::BaseData* targetData; + + LinkPath(sofa::core::sptr); + LinkPath(sofa::core::objectmodel::BaseData*); + + bool isPointingToData() const; +}; + +void moduleAddLinkPath(pybind11::module& m); + +} /// sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath_doc.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath_doc.h new file mode 100644 index 00000000..8f026a55 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_LinkPath_doc.h @@ -0,0 +1,35 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2021 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once + +namespace sofapython3::doc::linkpath +{ +static auto linkpath = + R"( + Hold a linkpath to an object or a data. + Example of use: + node.addObject("MechanicalObject", name="o") + node.addObject(position=node.o.position.linkpath) + + str(node.o.linkpath) # prints LinkPath("@/o.position") + repr(node.o.linkpath) # prints "@/o.position" + )"; +} diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp index 92849e2e..315b2c90 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp @@ -38,6 +38,9 @@ using sofa::helper::logging::Message; #include using sofa::core::ExecParams; +#include +using sofapython3::LinkPath; + #include #include #include @@ -185,6 +188,25 @@ py::object getObject(Node &n, const std::string &name, const py::kwargs& kwargs) return py::none(); } +void setFieldsFromPythonValues(Base* self, const py::kwargs& dict) +{ + // For each argument of the addObject function we check if this is an argument we can do a raw conversion from. + // Doing a raw conversion means that we are not converting the argument anymore into a sofa parsable string. + for(auto [key, value] : dict) + { + if(py::isinstance(value)) + { + auto* data = self->findData(py::str(key)); + if(data) + BindingBase::SetData(data, py::cast(value)); + + auto* link = self->findLink(py::str(key)); + if(link) + BindingBase::SetLink(link, py::cast(value)); + } + } +} + /// Implement the addObject function. py::object addObjectKwargs(Node* self, const std::string& type, const py::kwargs& kwargs) { @@ -223,6 +245,8 @@ py::object addObjectKwargs(Node* self, const std::string& type, const py::kwargs object->setName(resolvedName); } + setFieldsFromPythonValues(object.get(), kwargs); + checkParamUsage(desc); // Convert the logged messages in the object's internal logging into python exception. diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index 81bb3d6f..a2d377d7 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -19,6 +19,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_DataEngine_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField_doc.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_LinkPath.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_LinkPath_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Mass.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Mass_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.h @@ -60,6 +62,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Data/Binding_DataLink.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Data/Binding_DataVectorString.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_LinkPath.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Mass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node.cpp diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 932568e5..bf54dd2e 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -35,6 +35,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include #include @@ -93,6 +94,7 @@ PYBIND11_MODULE(Core, core) Sofa.Core.DataContainer Sofa.Core.DataString Sofa.Core.DataVectorString + Sofa.Core.LinkPath Sofa.Core.NodeIterator #Sofa.Core.WriteAccessor )doc"; @@ -117,6 +119,7 @@ PYBIND11_MODULE(Core, core) moduleAddForceField(core); moduleAddMass(core); moduleAddObjectFactory(core); + moduleAddLinkPath(core); moduleAddNode(core); moduleAddNodeIterator(core); moduleAddPrefab(core); diff --git a/bindings/Sofa/tests/Core/Base.py b/bindings/Sofa/tests/Core/Base.py index abfc2cea..faf9446d 100644 --- a/bindings/Sofa/tests/Core/Base.py +++ b/bindings/Sofa/tests/Core/Base.py @@ -99,7 +99,6 @@ def test_addNewDataFromParent(self): # child properly updated from parent value self.assertEqual("test", c1.d2.value) - def test_addNewDataFromParent_brokenLink(self): root = create_scene('root') c1 = root.addObject("MechanicalObject", name="c1") @@ -121,13 +120,21 @@ def test_getClassName(self): def test_getTemplateName(self): root = create_scene("root") c = root.addObject("MechanicalObject", name="t") - self.assertEqual(c.getTemplateName(),"Vec3d") + 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") + self.assertEqual(obj.getPathName(), "/obj") + self.assertEqual(obj.getLinkPath(), "@/obj") + + def test_linkpath(self): + """Accessing the linkpath property should return a LinkPath object with a correctly set 'path'""" + root = create_scene("root") + root.addChild("child") + root.child.addObject("MechanicalObject", name="dofs") + self.assertEqual(type(root.child.dofs.linkpath), Sofa.Core.LinkPath) + self.assertEqual(str(root.child.dofs.linkpath), "@/child/dofs") def test_addExistingDataAsParentOfNewData(self): # TODO(@marques-bruno) @@ -148,4 +155,3 @@ def test_addExistingDataAsParentOfNewData(self): # self.assertEqual(aData.getOwner(), obj1) # self.assertEqual(obj2.aData.value, "pouet") pass - diff --git a/bindings/Sofa/tests/Core/BaseData.py b/bindings/Sofa/tests/Core/BaseData.py index af1f72e6..42336a3e 100644 --- a/bindings/Sofa/tests/Core/BaseData.py +++ b/bindings/Sofa/tests/Core/BaseData.py @@ -293,16 +293,35 @@ def t(c): numpy.testing.assert_array_equal(wa, v*4.0) def test_linkpath(self): + """Accessing the linkpath property should return a LinkPath object with a correct 'path'""" n = create_scene("rootNode") m = n. addObject("MechanicalObject", name="dofs") - self.assertEqual(m.position.linkpath, "@/dofs.position") + self.assertEqual(type(m.position.linkpath), Sofa.Core.LinkPath) + self.assertEqual(str(m.position.linkpath), "@/dofs.position") + + def test_linkpath_in_add_object_compat(self): + """Checks that passing a link path to the addObject function is still working as it was before sofa 21.12""" + root = create_scene("root") + root.addObject("MechanicalObject", name="dofs1", position=[[1.0,2.0,3.0]]) + root.addObject("MechanicalObject", name="dofs2", position=root.dofs1.position.linkpath) + self.assertEqual(str(root.dofs2.position.getParent().linkpath), "@/dofs1.position") + + def test_linkpath_between_two_scenes(self): + """Checks that passing a link path to the addObject function is still working as it was before sofa 21.12""" + root1 = create_scene("root1") + root1.addChild("child1") + root2 = create_scene("root2") + root2.addChild("child2) + root1.child1.addObject("MechanicalObject", name="dofs1", position=[[1.0,2.0,3.0]]) + root2.chdil2.addObject("MechanicalObject", name="dofs2", position=root1.child1.dofs1.position.linkpath) + self.assertEqual(root2.dofs2.position.getParent().name, "dofs1") def test_set_value_from_string(self): n = create_scene("rootNode") - n.gravity.value = [1.0,2.0,3.0] - self.assertEqual(list(n.gravity.value), [1.0,2.0,3.0]) + n.gravity.value = [1.0, 2.0, 3.0] + self.assertEqual(list(n.gravity.value), [1.0, 2.0, 3.0]) n.gravity.value = "4.0 5.0 6.0" - self.assertEqual(list(n.gravity.value), [4.0,5.0,6.0]) + self.assertEqual(list(n.gravity.value), [4.0, 5.0, 6.0]) def test_DataString(self): n = create_scene("rootNode")