Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ py::object addKwargs(Node* self, const py::object& callable, const py::kwargs& k
{
BaseObject* obj = py::cast<BaseObject*>(callable);

self->addObject(obj);
self->addObject(obj);
return py::cast(obj);
}

Expand Down Expand Up @@ -326,24 +326,41 @@ py::object removeChildByName(Node& n, const std::string name)

NodeIterator* property_children(Node* node)
{
return new NodeIterator(node, [](Node* n) -> size_t { return n->child.size(); },
[](Node* n, unsigned int index) -> Base::SPtr { return n->child[index]; });
return new NodeIterator(node,
[](Node* n) -> size_t { return n->child.size(); },
[](Node* n, unsigned int index) -> Base::SPtr { return n->child[index]; },
[](const Node* n, const std::string& name) { return n->getChild(name); },
[](Node* n, unsigned int index) { n->removeChild(n->child[index]); }
);
}

NodeIterator* property_parents(Node* node)
{
return new NodeIterator(node, [](Node* n) -> size_t { return n->getNbParents(); },
[](Node* n, unsigned int index) -> Node::SPtr {
auto p = n->getParents();
return static_cast<Node*>(p[index]);
});
return new NodeIterator(node,
[](Node* n) -> size_t { return n->getNbParents(); },
[](Node* n, unsigned int index) -> Node::SPtr {
auto p = n->getParents();
return static_cast<Node*>(p[index]);
},
[](const Node* n, const std::string& name) -> sofa::core::Base* {
const auto& parents = n->getParents();
return *std::find_if(parents.begin(),
parents.end(),
[name](BaseNode* child){ return child->getName() == name; });
},
[](Node*, unsigned int) {
throw std::runtime_error("Removing a parent is not a supported operation. Please detach the node from the corresponding graph node.");
});
}

NodeIterator* property_objects(Node* node)
{
return new NodeIterator(node, [](Node* n) -> size_t { return n->object.size(); },
[](Node* n, unsigned int index) -> Base::SPtr { return (n->object[index]);
});
return new NodeIterator(node,
[](Node* n) -> size_t { return n->object.size(); },
[](Node* n, unsigned int index) -> Base::SPtr { return (n->object[index]);},
[](const Node* n, const std::string& name) { return n->getObject(name); },
[](Node* n, unsigned int index) { n->removeObject(n->object[index]);}
);
}

py::object __getattr__(Node& self, const std::string& name)
Expand Down
11 changes: 6 additions & 5 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void moduleAddNodeIterator(py::module &m)

d.def("__getitem__", [](NodeIterator& d, const std::string& name) -> py::object
{
BaseObject* obj =d.owner->getObject(name);
sofa::core::objectmodel::Base* obj = d.get_by_name(d.owner.get(), name);
if(obj==nullptr)
throw py::index_error("No existing object '"+name+"'");
return PythonFactory::toPython(obj);
Expand Down Expand Up @@ -71,10 +71,11 @@ void moduleAddNodeIterator(py::module &m)
});
d.def("remove_at", [](NodeIterator& d, size_t index)
{
sofa::core::sptr<sofa::core::objectmodel::BaseNode> n(
dynamic_cast<sofa::core::objectmodel::BaseNode *>(d.get(d.owner.get(), index).get())
);
d.owner->removeChild(n);
return d.remove_at(d.owner.get(), index);
});
d.def("__contains__", [](NodeIterator& d, const std::string& name)
{
return d.get_by_name(d.owner.get(), name) != nullptr;
});
}

Expand Down
27 changes: 25 additions & 2 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,42 @@

namespace sofapython3 {

class NodeIterator {
/// Generic implementation to iterator over the different structures of a Node.
/// This allows to implement pythonic solution as in:
/// for object in node.objects:
/// print(object.name)
///
/// or
/// for child in node.children:
/// print(child.name)
///
/// there is also support for several function like:
/// len(node.children)
/// if "MyName" in node.children
/// node.children.remove_at(index)
///
/// The implementation is generic by the use of lamda function allowing to specify which
/// data structure is actually iterated.
class NodeIterator
{
public:
sofa::core::sptr<sofa::simulation::Node> owner;
size_t index=0;
std::function<size_t (sofa::simulation::Node *)> size ;
std::function<sofa::core::sptr<sofa::core::Base> (sofa::simulation::Node *, size_t)> get ;
std::function<sofa::core::Base* (const sofa::simulation::Node *, const std::string&)> get_by_name ;
std::function<void (sofa::simulation::Node*, unsigned int)> remove_at;

NodeIterator(sofa::core::sptr<sofa::simulation::Node> owner_,
std::function<size_t (sofa::simulation::Node *)> size_,
std::function<sofa::core::sptr<sofa::core::Base> (sofa::simulation::Node *, size_t)> get_)
std::function<sofa::core::sptr<sofa::core::Base> (sofa::simulation::Node *, size_t)> get_,
std::function<sofa::core::Base* (const sofa::simulation::Node *, const std::string&)> get_by_name_,
std::function<void (sofa::simulation::Node*, unsigned int)> remove_at_)
{
size = size_;
get = get_;
get_by_name = get_by_name_;
remove_at = remove_at_;
owner=owner_;
index=0;
}
Expand Down
27 changes: 25 additions & 2 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,29 +245,52 @@ static auto getLinkPath =
static auto children =
R"(
Field interface to acces the children of a node.
The returned object is a iteratable featuring the following operations:
len, remove_at, __contains__, get_at

:Example:
>>> n = Sofa.Core.Node("MyNode")
>>> n.addChild("child1")
>>> for child in n.children:
>>> print(child.name)
>>>
>>> if "child1" in n.children:
>>> print("Yes")
>>> print(len(n.children))
)";

static auto parents =
R"(
Field interface to acces the parents of a node.
The returned object is a iteratable featuring the following operations:
len, remove_at, __contains__, get_at

:Example:
>>> n = Sofa.Core.Node("MyNode")
>>> for parent in n.parents:
>>> n = Sofa.Core.Node("parent1")
>>> c = n.addChild("child1")
>>> for parent in c.parents:
>>> print(parent.name)
>>>
>>> if "parent1" in c.parents:
>>> print("Yes")
>>> print(len(n.parents))
)";
static auto objects =
R"(
Field interface to acces the objects of a node.
The returned object is a iteratable featuring the following operations:
len, remove_at, __contains__, get_at

:Example:
>>> n = Sofa.Core.Node("MyNode")
>>> n.addObject("MechanicalObject", name="object1")
>>> n.addObject("MechanicalObject", name="object2")
>>> for object in n.objects:
>>> print(object.name)
>>>
>>> if "object2" in c.objects:
>>> print("Yes")
>>> print(len(n.objects))
)";
static auto removeObject =
R"(
Expand Down
24 changes: 24 additions & 0 deletions bindings/Sofa/tests/Simulation/Node.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,30 @@ def test_objects_property(self):
child.addObject("MechanicalObject", name="name2")
self.assertEqual(len(child.objects), 3)

def test_objects_property_contains_method(self):
root = Sofa.Core.Node("rootNode")
root.addObject("RequiredPlugin", name="SofaBaseMechanics")
child = root.addChild(Sofa.Core.Node("child1"))
child.addObject("MechanicalObject", name="name1")
child.addObject("MechanicalObject", name="name2")
self.assertTrue("child1" in root.children)
self.assertFalse("child2" in root.children)
self.assertTrue("name1" in child.objects)
self.assertTrue("name2" in child.objects)
self.assertFalse("name3" in child.objects)

def test_objects_property_remove_at_method(self):
root = Sofa.Core.Node("rootNode")
root.addObject("RequiredPlugin", name="SofaBaseMechanics")
child = root.addChild(Sofa.Core.Node("child1"))
child.addObject("MechanicalObject", name="name1")
child.addObject("MechanicalObject", name="name2")
self.assertTrue("name1" in child.objects)
self.assertTrue("name2" in child.objects)
child.objects.remove_at(1)
self.assertTrue("name1" in child.objects)
self.assertFalse("name2" in child.objects)

def test_data_property(self):
root = Sofa.Core.Node("rootNode")
self.assertTrue(hasattr(root, "__data__"))
Expand Down