Skip to content

Commit 36c826b

Browse files
committed
[Sofa.Core] Add BaseLink forwarding accessor.
Currently accessing: ```python forcefield.mstate.position ``` does not work because forcefield.mstate is a link and thus a link does not have a "position" field. The working version consist to write ```object.mstate.getLinkedBase().position``` which is very cumbersome to write. This PR adds support for attribute forwarding through link allowing the firt approach.
1 parent 443b93a commit 36c826b

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseLink.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ using sofa::core::objectmodel::BaseObject;
2626

2727
#include <sofa/core/objectmodel/BaseNode.h>
2828

29+
#include <SofaPython3/Sofa/Core/Binding_Base.h>
2930
#include <SofaPython3/Sofa/Core/Binding_BaseLink.h>
3031
#include <SofaPython3/Sofa/Core/Binding_BaseLink_doc.h>
3132
#include <SofaPython3/PythonFactory.h>
@@ -67,6 +68,29 @@ std::string getPathName(BaseLink& self)
6768
return (n ? n->getPathName() : o->getPathName()) + "." + self.getName();
6869
}
6970

71+
namespace {
72+
py::object __getattr__(py::object self, const std::string& s)
73+
{
74+
py::object base = getLinkedBase(py::cast<BaseLink&>(self), 0);
75+
if(!base.is_none())
76+
{
77+
return BindingBase::__getattr__(base, s);
78+
}
79+
throw std::runtime_error("Unable to find attribute on an empty link. X");
80+
}
81+
82+
void __setattr__(py::object self, const std::string& s, py::object value)
83+
{
84+
py::object base = getLinkedBase(py::cast<BaseLink&>(self), 0);
85+
if(!base.is_none())
86+
{
87+
BindingBase::__setattr__(base, s, value);
88+
return;
89+
}
90+
throw std::runtime_error("Unable to find and set an attribute on an empty link.");
91+
}
92+
}
93+
7094
void moduleAddBaseLink(py::module& m)
7195
{
7296
py::class_<BaseLink> link(m, "Link", sofapython3::doc::baseLink::baseLinkClass);
@@ -89,11 +113,12 @@ void moduleAddBaseLink(py::module& m)
89113
link.def("getLinkedBase", getLinkedBase, "index"_a = 0, sofapython3::doc::baseLink::getLinkedBase);
90114
link.def("setLinkedBase", &BaseLink::setLinkedBase, sofapython3::doc::baseLink::getLinkedBase);
91115

92-
93116
link.def("getLinkedPath", &BaseLink::getLinkedPath, "index"_a = 0, sofapython3::doc::baseLink::getLinkedPath);
94117
link.def("getPathName", getPathName, sofapython3::doc::baseLink::getLinkedPath);
95118
link.def("read", &BaseLink::read, sofapython3::doc::baseLink::read);
96119

120+
link.def("__getattr__", &__getattr__);
121+
link.def("__setattr__", &__setattr__);
97122
}
98123

99124
} /// namespace sofapython3

bindings/Sofa/tests/Core/BaseLink.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ def test_getLinked_and_Owner(self):
9595
self.assertEqual(link_input.getOwnerBase().getName(), "BarycentricMapping")
9696
self.assertEqual(link_output.getOwnerBase().getName(), "BarycentricMapping")
9797

98+
def test_baselink_attributes_forwarding(self):
99+
root = create_scene("root")
100+
c1 = root.addObject("MechanicalObject", name="t1", position=[[-1,-2,-3],[-4,-5,-6]])
101+
f1 = root.addObject("RestShapeSpringsForceField", name="forcefield", mstate="@t1")
102+
self.assertEqual(len(f1.mstate.position), 2)
103+
f1.mstate.position = [[1.0,2.0,3.0]]
104+
self.assertEqual(len(f1.mstate.position), 1)
105+
98106
@unittest.skip # Segmentation fault on MacOS
99107
def test_read(self):
100108
root = create_scene("root")

0 commit comments

Comments
 (0)