Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SofaPython3] Changes how addObject process its arguments when they are of type: numpy & data #198

Merged
Merged
10 changes: 9 additions & 1 deletion Plugin/src/SofaPython3/DataHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,20 @@ std::string toSofaParsableString(const py::handle& p)
if(py::isinstance<py::str>(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<sofa::core::objectmodel::BaseData>(p))
{
sofa::core::objectmodel::BaseData* data = py::cast<sofa::core::objectmodel::BaseData*>(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<py::array>(p))
{
py::object o = p.attr("tolist")();
return toSofaParsableString(o);
}

return py::repr(p);
}

Expand Down
11 changes: 9 additions & 2 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};


Expand Down
4 changes: 4 additions & 0 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<BaseData*>(self);

if(py::isinstance<DataContainer>(value))
Expand All @@ -137,6 +138,9 @@ py::object __getattr__(py::object self, const std::string& s)
if(s == "value")
return PythonFactory::valueToPython_ro(py::cast<BaseData*>(self));

if(s == "linkpath")
return py::cast((py::cast<BaseData*>(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+"'");
Expand Down
16 changes: 16 additions & 0 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 5 additions & 1 deletion bindings/Sofa/tests/Core/Base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions bindings/Sofa/tests/Core/BaseData.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down