From 1faedb794d83f139df7f5e092696b0355a433734 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Fri, 27 Jun 2025 12:35:35 +0200 Subject: [PATCH 1/6] Merge DAGNode in Node --- .../Core/src/sofa/simulation/Node.cpp | 14 +- .../Core/src/sofa/simulation/Node.h | 196 +++++++++++++---- .../src/sofa/simulation/graph/DAGNode.cpp | 162 +++++++------- .../Graph/src/sofa/simulation/graph/DAGNode.h | 197 +----------------- 4 files changed, 240 insertions(+), 329 deletions(-) diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index ad554c90d04..53a988adaff 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp @@ -71,7 +71,7 @@ namespace sofa::simulation using core::objectmodel::BaseNode; using core::objectmodel::BaseObject; -Node::Node(const std::string& name) +Node::Node(const std::string& name, Node* parent) : core::objectmodel::BaseNode() , sofa::core::objectmodel::Context() , child(initLink("child", "Child nodes")) @@ -109,7 +109,11 @@ Node::Node(const std::string& name) , debug_(false) , initialized(false) + , l_parents(initLink("parents", "Parents nodes in the graph")) { + if( parent ) + parent->addChild(dynamic_cast(this)); + _context = this; setName(name); f_printLog.setValue(DEBUG_LINK); @@ -118,8 +122,14 @@ Node::Node(const std::string& name) Node::~Node() { + for (ChildIterator it = child.begin(), itend = child.end(); it != itend; ++it) + { + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(*it); + dagnode->l_parents.remove(this); + } } + void Node::parse( sofa::core::objectmodel::BaseObjectDescription* arg ) { Inherit1::parse( arg ); @@ -643,7 +653,7 @@ core::topology::Topology* Node::getTopology() const } /// Mesh Topology (unified interface for both static and dynamic topologies) -core::topology::BaseMeshTopology* Node::getMeshTopologyLink(SearchDirection dir) const +core::topology::BaseMeshTopology* Node::NODEgetMeshTopologyLink(SearchDirection dir) const { SOFA_UNUSED(dir); if (this->meshTopology) diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h index 4b07b4713ff..8ef3261dec6 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -149,15 +150,10 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, public: SOFA_ABSTRACT_CLASS2(Node, BaseNode, Context); - typedef sofa::core::visual::DisplayFlags DisplayFlags; -protected: - Node(const std::string& name=""); + Node(const std::string& name="", Node* parent=nullptr); virtual ~Node() override; -public: - /// Create, add, then return the new child of this Node - virtual Node::SPtr createChild(const std::string& nodeName)=0; /// @name High-level interface /// @{ @@ -179,9 +175,6 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// @warning when calling with precomputedOrder=true, the function "precomputeTraversalOrder" must be called before executing the visitor and the user must ensure by himself that the simulation graph has done been modified since the last call to "precomputeTraversalOrder" /// @{ - /// Execute a recursive action starting from this node. - virtual void doExecuteVisitor(Visitor* action, bool precomputedOrder=false)=0; - /// Execute a recursive action starting from this node void executeVisitor(Visitor* action, bool precomputedOrder=false) override; @@ -217,7 +210,7 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, } /// Possible optimization with traversal precomputation, not mandatory and does nothing by default - virtual void precomputeTraversalOrder( const sofa::core::ExecParams* ) {} + void precomputeTraversalOrder( const sofa::core::ExecParams* ); /// @} @@ -276,19 +269,6 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, virtual void removeChild(BaseNode::SPtr node) final; /// Move a node in this from another node virtual void moveChild(BaseNode::SPtr node, BaseNode::SPtr prev_parent) final; - /// Move a node in this from another node - virtual void moveChild(BaseNode::SPtr node) override = 0; - - /// Delegate methods overridden in child classes - /// Add a child node - virtual void doAddChild(BaseNode::SPtr node) = 0; - /// Remove a child node - virtual void doRemoveChild(BaseNode::SPtr node) = 0; - /// Move a node from another node - virtual void doMoveChild(BaseNode::SPtr node, BaseNode::SPtr prev_parent) = 0; - - /// @} - /// @name Set/get objects /// @{ @@ -309,27 +289,13 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// Generic object access, given a set of required tags, possibly searching up or down from the current context /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override = 0; - /// Generic object access, possibly searching up or down from the current context - /// /// Note that the template wrapper method should generally be used to have the correct return type, void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, SearchDirection dir = SearchUp) const override { return getObject(class_info, sofa::core::objectmodel::TagSet(), dir); } - /// Generic object access, given a path from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const override = 0; - - /// Generic list of objects access, given a set of required tags, possibly searching up or down from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void getObjects(const sofa::core::objectmodel::ClassInfo& class_info, GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override = 0; - /// Generic list of objects access, possibly searching up or down from the current context /// /// Note that the template wrapper method should generally be used to have the correct return type, @@ -497,7 +463,7 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, virtual void updateSimulationContext(); /// Called during initialization to correctly propagate the visual context to the children - virtual void initVisualContext() {} + virtual void initVisualContext(); /// Propagate an event void propagateEvent(const sofa::core::ExecParams* params, sofa::core::objectmodel::Event* event) override; @@ -517,9 +483,6 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, template static Node::SPtr create(RealObject*, sofa::core::objectmodel::BaseObjectDescription* arg); - /// return the smallest common parent between this and node2 (returns nullptr if separated sub-graphes) - virtual Node* findCommonParent( simulation::Node* node2 ) = 0; - /// override context setSleeping to add notification. void setSleeping(bool val) override; @@ -607,6 +570,157 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// @} + + /// FROM DAG NODE + + typedef MultiLink LinkParents; + typedef LinkParents::const_iterator ParentIterator; + +public: + static const std::string GetCustomClassName(){ return "Node"; } + + Node::SPtr createChild(const std::string& nodeName); + + /// Remove the current node from the graph: consists in removing the link to its parent + void detachFromGraph() override; + + /// Get a list of parent node + Parents getParents() const override; + + /// returns number of parents + size_t getNbParents() const override; + + /// return the first parent (returns nullptr if no parent) + BaseNode* getFirstParent() const override; + + /// Test if the given node is a parent of this node. + bool hasParent(const BaseNode* node) const override; + + /// Test if the given context is a parent of this context. + bool hasParent(const BaseContext* context) const; + + /// Test if the given context is an ancestor of this context. + /// An ancestor is a parent or (recursively) the parent of an ancestor. + bool hasAncestor(const BaseNode* node) const override + { + return hasAncestor(node->getContext()); + } + + /// Test if the given context is an ancestor of this context. + /// An ancestor is a parent or (recursively) the parent of an ancestor. + bool hasAncestor(const BaseContext* context) const override; + + + /// Generic object access, given a set of required tags, possibly searching up or down from the current context + /// + /// Note that the template wrapper method should generally be used to have the correct return type, + void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; + + /// Generic object access, given a path from the current context + /// + /// Note that the template wrapper method should generally be used to have the correct return type, + void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const override; + + /// Generic list of objects access, given a set of required tags, possibly searching up or down from the current context + /// + /// Note that the template wrapper method should generally be used to have the correct return type, + void getObjects(const sofa::core::objectmodel::ClassInfo& class_info, GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; + + /// Mesh Topology that is relevant for this context + /// (within it or its parents until a mapping is reached that does not preserve topologies). + sofa::core::topology::BaseMeshTopology* NODEgetMeshTopologyLink(SearchDirection dir = SearchUp) const; + + static Node::SPtr create(Node*, sofa::core::objectmodel::BaseObjectDescription* arg) + { + Node::SPtr obj = Node::SPtr(new Node()); + obj->parse(arg); + return obj; + } + + void moveChild(BaseNode::SPtr node) override; + + /// return the smallest common parent between this and node2 (returns nullptr if separated sub-graphes) + Node* findCommonParent( simulation::Node* node2 ); + +protected: + /// bottom-up traversal, returning the first node which have a descendancy containing both node1 & node2 + Node* findCommonParent( Node* node1, Node* node2 ); + + LinkParents l_parents; + + void doAddChild(BaseNode::SPtr node); + void doRemoveChild(BaseNode::SPtr node); + void doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent); + + /// Execute a recursive action starting from this node. + void doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder=false); + + + /// @name @internal stuff related to the DAG traversal + /// @{ + + + /// all child nodes (unordered) + std::set _descendancy; + + /// bottom-up traversal removing descendancy + void setDirtyDescendancy(); + + /// traversal updating the descendancy + void updateDescendancy(); + + /// traversal flags + typedef enum + { + NOT_VISITED=0, + VISITED, + PRUNED + } VisitedStatus; + + + + /// wrapper to use VisitedStatus in a std::map (to ensure the default map insertion will give NOT_VISITED) + struct StatusStruct + { + StatusStruct() : status(NOT_VISITED) {} + StatusStruct( const VisitedStatus& s ) : status(s) {} + inline void operator=( const VisitedStatus& s ) { status=s; } + inline bool operator==( const VisitedStatus& s ) const { return status==s; } + inline bool operator==( const StatusStruct& s ) const { return status==s.status; } + inline bool operator!=( const VisitedStatus& s ) const { return status!=s; } + inline bool operator!=( const StatusStruct& s ) const { return status!=s.status; } + VisitedStatus status; + }; + + /// map structure to store a traversal flag for each Node + typedef std::map StatusMap; + + /// list of Node* + typedef std::list NodeList; + + /// the ordered list of Node to traverse from this Node + NodeList _precomputedTraversalOrder; + + /// @internal performing only the top-down traversal on a DAG + /// @executedNodes will be fill with the Nodes where the top-down action is processed + /// @statusMap the visitor's flag map + /// @visitorRoot node from where the visitor has been run + void executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, Node* visitorRoot ); + void executeVisitorBottomUp(simulation::Visitor* action, NodeList& executedNodes ); + /// @} + + /// @internal tree traversal implementation + void executeVisitorTreeTraversal( Visitor* action, StatusMap& statusMap, Visitor::TreeTraversalRepetition repeat, bool alreadyRepeated=false ); + + /// @name @internal stuff related to getObjects + /// @{ + + /// get node's local objects respecting specified class_info and tags + void getLocalObjects( const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags ) const ; + + friend class GetDownObjectsVisitor ; + friend class GetUpObjectsVisitor ; + /// @} }; } diff --git a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp index bed1314039a..f0d36b47d66 100644 --- a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp +++ b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp @@ -19,12 +19,12 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ -#include +#include #include #include #include -namespace sofa::simulation::graph +namespace sofa::simulation { /// get all down objects respecting specified class_info and tags @@ -32,12 +32,12 @@ class GetDownObjectsVisitor : public Visitor { public: - GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, DAGNode::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); - ~GetDownObjectsVisitor() override; + GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); + ~GetDownObjectsVisitor() ; Result processNodeTopDown(simulation::Node* node) override { - static_cast(node)->getLocalObjects( _class_info, _container, _tags ); + static_cast(node)->getLocalObjects( _class_info, _container, _tags ); return RESULT_CONTINUE; } @@ -51,12 +51,12 @@ class GetDownObjectsVisitor : public Visitor protected: const sofa::core::objectmodel::ClassInfo& _class_info; - DAGNode::GetObjectsCallBack& _container; + Node::GetObjectsCallBack& _container; const sofa::core::objectmodel::TagSet& _tags; }; GetDownObjectsVisitor::GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, - DAGNode::GetObjectsCallBack& container, + Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags) : Visitor( sofa::core::execparams::defaultInstance() ) , _class_info(class_info) @@ -71,12 +71,12 @@ class GetUpObjectsVisitor : public Visitor { public: - GetUpObjectsVisitor(DAGNode* searchNode, const sofa::core::objectmodel::ClassInfo& class_info, DAGNode::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); + GetUpObjectsVisitor(Node* searchNode, const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); ~GetUpObjectsVisitor() override; Result processNodeTopDown(simulation::Node* node) override { - const DAGNode* dagnode = dynamic_cast(node); + const Node* dagnode = dynamic_cast(node); if( dagnode->_descendancy.contains(_searchNode) ) // searchNode is in the current node descendancy, so the current node is a parent of searchNode { dagnode->getLocalObjects( _class_info, _container, _tags ); @@ -99,16 +99,16 @@ class GetUpObjectsVisitor : public Visitor protected: - DAGNode* _searchNode; + Node* _searchNode; const sofa::core::objectmodel::ClassInfo& _class_info; - DAGNode::GetObjectsCallBack& _container; + Node::GetObjectsCallBack& _container; const sofa::core::objectmodel::TagSet& _tags; }; -GetUpObjectsVisitor::GetUpObjectsVisitor(DAGNode* searchNode, +GetUpObjectsVisitor::GetUpObjectsVisitor(Node* searchNode, const sofa::core::objectmodel::ClassInfo& class_info, - DAGNode::GetObjectsCallBack& container, + Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags) : Visitor( sofa::core::execparams::defaultInstance() ) , _searchNode( searchNode ) @@ -119,27 +119,10 @@ GetUpObjectsVisitor::GetUpObjectsVisitor(DAGNode* searchNode, GetUpObjectsVisitor::~GetUpObjectsVisitor(){} -DAGNode::DAGNode(const std::string& name, DAGNode* parent) - : simulation::Node(name) - , l_parents(initLink("parents", "Parents nodes in the graph")) -{ - if( parent ) - parent->addChild(dynamic_cast(this)); -} - -DAGNode::~DAGNode() -{ - for (ChildIterator it = child.begin(), itend = child.end(); it != itend; ++it) - { - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(*it); - dagnode->l_parents.remove(this); - } -} - /// Create, add, then return the new child of this Node -Node::SPtr DAGNode::createChild(const std::string& nodeName) +Node::SPtr Node::createChild(const std::string& nodeName) { - DAGNode::SPtr newchild; + Node::SPtr newchild; if (nodeName.empty()) { int i = 0; @@ -167,18 +150,18 @@ Node::SPtr DAGNode::createChild(const std::string& nodeName) } msg_error("Node::createChild()") << "Empty string given to property 'name': Forcefully setting an empty name is forbidden.\n" "Renaming to " + newName + " to avoid unexpected behaviors."; - newchild = sofa::core::objectmodel::New(newName); + newchild = sofa::core::objectmodel::New(newName); } else - newchild = sofa::core::objectmodel::New(nodeName); + newchild = sofa::core::objectmodel::New(nodeName); this->addChild(newchild); newchild->updateSimulationContext(); return newchild; } -void DAGNode::moveChild(BaseNode::SPtr node) +void Node::moveChild(BaseNode::SPtr node) { - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); for (const auto& parent : dagnode->getParents()) { Node::moveChild(node, parent); } @@ -186,9 +169,9 @@ void DAGNode::moveChild(BaseNode::SPtr node) /// Add a child node -void DAGNode::doAddChild(BaseNode::SPtr node) +void Node::doAddChild(BaseNode::SPtr node) { - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); setDirtyDescendancy(); child.add(dagnode); dagnode->l_parents.add(this); @@ -196,18 +179,18 @@ void DAGNode::doAddChild(BaseNode::SPtr node) } /// Remove a child -void DAGNode::doRemoveChild(BaseNode::SPtr node) +void Node::doRemoveChild(BaseNode::SPtr node) { - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); setDirtyDescendancy(); child.remove(dagnode); dagnode->l_parents.remove(this); } /// Move a node from another node -void DAGNode::doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent) +void Node::doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent) { - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); if (!dagnode) return; setDirtyDescendancy(); @@ -217,9 +200,9 @@ void DAGNode::doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent) } /// Remove a child -void DAGNode::detachFromGraph() +void Node::detachFromGraph() { - DAGNode::SPtr me = this; // make sure we don't delete ourself before the end of this method + Node::SPtr me = this; // make sure we don't delete ourself before the end of this method const LinkParents::Container& parents = l_parents.getValue(); while(!parents.empty()) parents.back()->removeChild(this); @@ -228,7 +211,7 @@ void DAGNode::detachFromGraph() /// Generic object access, possibly searching up or down from the current context /// /// Note that the template wrapper method should generally be used to have the correct return type, -void* DAGNode::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir) const +void* Node::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir) const { if (dir == SearchRoot) { @@ -277,7 +260,7 @@ void* DAGNode::getObject(const sofa::core::objectmodel::ClassInfo& class_info, c } break; case SearchRoot: - dmsg_error("DAGNode") << "SearchRoot SHOULD NOT BE POSSIBLE HERE."; + dmsg_error("Node") << "SearchRoot SHOULD NOT BE POSSIBLE HERE."; break; } } @@ -288,7 +271,7 @@ void* DAGNode::getObject(const sofa::core::objectmodel::ClassInfo& class_info, c /// Generic object access, given a path from the current context /// /// Note that the template wrapper method should generally be used to have the correct return type, -void* DAGNode::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const +void* Node::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const { if (path.empty()) { @@ -355,7 +338,7 @@ void* DAGNode::getObject(const sofa::core::objectmodel::ClassInfo& class_info, c void* result = class_info.dynamicCast(obj); if (result == nullptr) { - dmsg_error("DAGNode") << "Object "<(this), class_info, container, tags); + GetUpObjectsVisitor vis( const_cast(this), class_info, container, tags); getRootContext()->executeVisitor(&vis); } break; @@ -406,7 +389,7 @@ void DAGNode::getObjects(const sofa::core::objectmodel::ClassInfo& class_info, G { // a regular visitor is enforcing the selected object unicity GetDownObjectsVisitor vis(class_info, container, tags); - (const_cast(this))->executeVisitor(&vis); + (const_cast(this))->executeVisitor(&vis); break; } default: @@ -415,7 +398,7 @@ void DAGNode::getObjects(const sofa::core::objectmodel::ClassInfo& class_info, G } /// Get a list of parent node -sofa::core::objectmodel::BaseNode::Parents DAGNode::getParents() const +sofa::core::objectmodel::BaseNode::Parents Node::getParents() const { Parents p; @@ -428,13 +411,13 @@ sofa::core::objectmodel::BaseNode::Parents DAGNode::getParents() const /// returns number of parents -size_t DAGNode::getNbParents() const +size_t Node::getNbParents() const { return l_parents.getValue().size(); } /// return the first parent (returns nullptr if no parent) -sofa::core::objectmodel::BaseNode* DAGNode::getFirstParent() const +sofa::core::objectmodel::BaseNode* Node::getFirstParent() const { const LinkParents::Container& parents = l_parents.getValue(); if( parents.empty() ) return nullptr; @@ -443,7 +426,7 @@ sofa::core::objectmodel::BaseNode* DAGNode::getFirstParent() const /// Test if the given node is a parent of this node. -bool DAGNode::hasParent(const BaseNode* node) const +bool Node::hasParent(const BaseNode* node) const { const LinkParents::Container& parents = l_parents.getValue(); for ( unsigned int i = 0; i < parents.size() ; ++i) @@ -454,7 +437,7 @@ bool DAGNode::hasParent(const BaseNode* node) const } /// Test if the given context is a parent of this context. -bool DAGNode::hasParent(const BaseContext* context) const +bool Node::hasParent(const BaseContext* context) const { if (context == nullptr) return !getNbParents(); @@ -469,7 +452,7 @@ bool DAGNode::hasParent(const BaseContext* context) const /// Test if the given context is an ancestor of this context. /// An ancestor is a parent or (recursively) the parent of an ancestor. -bool DAGNode::hasAncestor(const BaseContext* context) const +bool Node::hasAncestor(const BaseContext* context) const { const LinkParents::Container& parents = l_parents.getValue(); for ( unsigned int i = 0; i < parents.size() ; ++i) @@ -482,7 +465,7 @@ bool DAGNode::hasAncestor(const BaseContext* context) const /// Mesh Topology that is relevant for this context /// (within it or its parents until a mapping is reached that does not preserve topologies). -sofa::core::topology::BaseMeshTopology* DAGNode::getMeshTopologyLink(SearchDirection dir) const +sofa::core::topology::BaseMeshTopology* Node::getMeshTopologyLink(SearchDirection dir) const { if (this->meshTopology) return this->meshTopology; @@ -519,8 +502,7 @@ sofa::core::topology::BaseMeshTopology* DAGNode::getMeshTopologyLink(SearchDirec return nullptr; // not found in any parents } - -void DAGNode::precomputeTraversalOrder( const sofa::core::ExecParams* params ) +void Node::precomputeTraversalOrder( const sofa::core::ExecParams* params ) { // accumulating traversed Nodes class TraversalOrderVisitor : public Visitor @@ -536,7 +518,7 @@ void DAGNode::precomputeTraversalOrder( const sofa::core::ExecParams* params ) Result processNodeTopDown(Node* node) override { - _orderList.push_back( static_cast(node) ); + _orderList.push_back( static_cast(node) ); return RESULT_CONTINUE; } @@ -550,7 +532,7 @@ void DAGNode::precomputeTraversalOrder( const sofa::core::ExecParams* params ) /// Execute a recursive action starting from this node -void DAGNode::doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder) +void Node::doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder) { if( precomputedOrder && !_precomputedTraversalOrder.empty() ) { @@ -568,7 +550,7 @@ void DAGNode::doExecuteVisitor(simulation::Visitor* action, bool precomputedOrde } else { - // WARNING: do not store the traversal infos in the DAGNode, as several visitors could traversed the graph simultaneously + // WARNING: do not store the traversal infos in the Node, as several visitors could traversed the graph simultaneously // These infos are stored in a StatusMap per visitor. updateDescendancy(); @@ -612,7 +594,7 @@ void DAGNode::doExecuteVisitor(simulation::Visitor* action, bool precomputedOrde } -void DAGNode::executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, DAGNode* visitorRoot ) +void Node::executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, Node* visitorRoot ) { if ( statusMap[this] != NOT_VISITED ) { @@ -674,10 +656,10 @@ void DAGNode::executeVisitorTopDown(simulation::Visitor* action, NodeList& execu // ... but continue the recursion anyway! if( action->childOrderReversed(this) ) for(unsigned int i = unsigned(child.size()); i>0;) - static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); else for(unsigned int i = 0; i(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + static_cast(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); } else { @@ -692,10 +674,10 @@ void DAGNode::executeVisitorTopDown(simulation::Visitor* action, NodeList& execu // ... and continue the recursion if( action->childOrderReversed(this) ) for(unsigned int i = unsigned(child.size()); i>0;) - static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); else for(unsigned int i = 0; i(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + static_cast(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); } } @@ -704,7 +686,7 @@ void DAGNode::executeVisitorTopDown(simulation::Visitor* action, NodeList& execu // warning nodes that are dynamically created during the traversal, but that have not been traversed during the top-down, won't be traversed during the bottom-up // TODO is it what we want? // otherwise it is possible to restart from top, go to leaves and running bottom-up action while going up -void DAGNode::executeVisitorBottomUp( simulation::Visitor* action, NodeList& executedNodes ) +void Node::executeVisitorBottomUp( simulation::Visitor* action, NodeList& executedNodes ) { for( NodeList::reverse_iterator it = executedNodes.rbegin(), itend = executedNodes.rend() ; it != itend ; ++it ) { @@ -714,7 +696,7 @@ void DAGNode::executeVisitorBottomUp( simulation::Visitor* action, NodeList& exe } -void DAGNode::setDirtyDescendancy() +void Node::setDirtyDescendancy() { _descendancy.clear(); const LinkParents::Container &parents = l_parents.getValue(); @@ -724,13 +706,13 @@ void DAGNode::setDirtyDescendancy() } } -void DAGNode::updateDescendancy() +void Node::updateDescendancy() { if( _descendancy.empty() && !child.empty() ) { for(unsigned int i = 0; i(child[i].get()); + Node* dagnode = static_cast(child[i].get()); dagnode->updateDescendancy(); _descendancy.insert( dagnode->_descendancy.begin(), dagnode->_descendancy.end() ); _descendancy.insert( dagnode ); @@ -740,7 +722,7 @@ void DAGNode::updateDescendancy() -void DAGNode::executeVisitorTreeTraversal( simulation::Visitor* action, StatusMap& statusMap, Visitor::TreeTraversalRepetition repeat, bool alreadyRepeated ) +void Node::executeVisitorTreeTraversal( simulation::Visitor* action, StatusMap& statusMap, Visitor::TreeTraversalRepetition repeat, bool alreadyRepeated ) { if( !this->isActive() ) { @@ -768,10 +750,10 @@ void DAGNode::executeVisitorTreeTraversal( simulation::Visitor* action, StatusMa statusMap[this] = VISITED; if( action->childOrderReversed(this) ) for(unsigned int i = unsigned(child.size()); i>0;) - static_cast(child[--i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); + static_cast(child[--i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); else for(unsigned int i = 0; i(child[i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); + static_cast(child[i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); } else { @@ -782,7 +764,7 @@ void DAGNode::executeVisitorTreeTraversal( simulation::Visitor* action, StatusMa } -void DAGNode::initVisualContext() +void Node::initVisualContext() { if (getNbParents()) { @@ -790,7 +772,7 @@ void DAGNode::initVisualContext() } } -void DAGNode::updateContext() +void Node::updateContext() { sofa::core::objectmodel::BaseNode* firstParent = getFirstParent(); @@ -798,16 +780,16 @@ void DAGNode::updateContext() { if( debug_ ) { - msg_info()<<"DAGNode::updateContext, node = "< DAGNodeClass("DAGNode"); +//helper::Creator NodeDefaultClass("default"); +static helper::Creator NodeClass("Node"); } // namespace sofa::simulation::graph diff --git a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.h b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.h index d258e206705..16042bd4764 100644 --- a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.h +++ b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.h @@ -22,203 +22,8 @@ #pragma once #include #include -#include -#include namespace sofa::simulation::graph { - -/** Define the structure of the scene as a Directed Acyclic Graph. Contains component objects (as pointer lists) and parents/childs (as DAGNode objects). - * - * The visitor traversal is performed in two passes: - * - a complete top-down traversal - * - then a complete bottom-up traversal in the exact invert order than the top-down traversal - * NB: contrary to the "tree" traversal, there are no interlinked forward/backward callbacks. There are only forward then only backward callbacks. - * - * Note that nodes created during a traversal are not traversed if they are created upper than the current node during the top-down traversal or if they are created during the bottom-up traversal. - */ -class SOFA_SIMULATION_GRAPH_API DAGNode : public simulation::Node -{ -public: - typedef Node::DisplayFlags DisplayFlags; - SOFA_CLASS(DAGNode, simulation::Node); - - typedef MultiLink LinkParents; - typedef LinkParents::const_iterator ParentIterator; - - -protected: - DAGNode( const std::string& name="", DAGNode* parent=nullptr ); - - virtual ~DAGNode() override; - -public: - static const std::string GetCustomClassName(){ return "Node"; } - - /// Pure Virtual method from Node - virtual Node::SPtr createChild(const std::string& nodeName) override; - - /// Remove the current node from the graph: consists in removing the link to its parent - void detachFromGraph() override; - - /// Get a list of parent node - Parents getParents() const override; - - /// returns number of parents - size_t getNbParents() const override; - - /// return the first parent (returns nullptr if no parent) - BaseNode* getFirstParent() const override; - - /// Test if the given node is a parent of this node. - bool hasParent(const BaseNode* node) const override; - - /// Test if the given context is a parent of this context. - bool hasParent(const BaseContext* context) const; - - /// Test if the given context is an ancestor of this context. - /// An ancestor is a parent or (recursively) the parent of an ancestor. - bool hasAncestor(const BaseNode* node) const override - { - return hasAncestor(node->getContext()); - } - - /// Test if the given context is an ancestor of this context. - /// An ancestor is a parent or (recursively) the parent of an ancestor. - bool hasAncestor(const BaseContext* context) const override; - - - /// Generic object access, given a set of required tags, possibly searching up or down from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; - - /// Generic object access, given a path from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const override; - - /// Generic list of objects access, given a set of required tags, possibly searching up or down from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void getObjects(const sofa::core::objectmodel::ClassInfo& class_info, GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; - - - /// Mesh Topology that is relevant for this context - /// (within it or its parents until a mapping is reached that does not preserve topologies). - sofa::core::topology::BaseMeshTopology* getMeshTopologyLink(SearchDirection dir = SearchUp) const override; - - - /// Called during initialization to correctly propagate the visual context to the children - void initVisualContext() override; - - /// Update the whole context values, based on parent and local ContextObjects - void updateContext() override; - - /// Update the simulation context values(gravity, time...), based on parent and local ContextObjects - void updateSimulationContext() override; - - static DAGNode::SPtr create(DAGNode*, sofa::core::objectmodel::BaseObjectDescription* arg) - { - DAGNode::SPtr obj = DAGNode::SPtr(new DAGNode()); - obj->parse(arg); - return obj; - } - - - /// return the smallest common parent between this and node2 (returns nullptr if separated sub-graphes) - /// it assumes that the DAG node is a tree node. In case of multiple parents it returns any of the parents. - /// it uses the node descendancy information. - Node* findCommonParent( Node* node2 ) override; - - /// compute the traversal order from this Node - void precomputeTraversalOrder( const sofa::core::ExecParams* params ) override; - - virtual void moveChild(BaseNode::SPtr node) override; - -protected: - - /// bottom-up traversal, returning the first node which have a descendancy containing both node1 & node2 - DAGNode* findCommonParent( DAGNode* node1, DAGNode* node2 ); - - - LinkParents l_parents; - - - virtual void doAddChild(BaseNode::SPtr node) override; - virtual void doRemoveChild(BaseNode::SPtr node) override; - virtual void doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent) override; - - - /// Execute a recursive action starting from this node. - void doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder=false) override; - - - /// @name @internal stuff related to the DAG traversal - /// @{ - - - /// all child nodes (unordered) - std::set _descendancy; - - /// bottom-up traversal removing descendancy - void setDirtyDescendancy(); - - /// traversal updating the descendancy - void updateDescendancy(); - - /// traversal flags - typedef enum - { - NOT_VISITED=0, - VISITED, - PRUNED - } VisitedStatus; - - - - /// wrapper to use VisitedStatus in a std::map (to ensure the default map insertion will give NOT_VISITED) - struct StatusStruct - { - StatusStruct() : status(NOT_VISITED) {} - StatusStruct( const VisitedStatus& s ) : status(s) {} - inline void operator=( const VisitedStatus& s ) { status=s; } - inline bool operator==( const VisitedStatus& s ) const { return status==s; } - inline bool operator==( const StatusStruct& s ) const { return status==s.status; } - inline bool operator!=( const VisitedStatus& s ) const { return status!=s; } - inline bool operator!=( const StatusStruct& s ) const { return status!=s.status; } - VisitedStatus status; - }; - - /// map structure to store a traversal flag for each DAGNode - typedef std::map StatusMap; - - /// list of DAGNode* - typedef std::list NodeList; - - /// the ordered list of Node to traverse from this Node - NodeList _precomputedTraversalOrder; - - /// @internal performing only the top-down traversal on a DAG - /// @executedNodes will be fill with the DAGNodes where the top-down action is processed - /// @statusMap the visitor's flag map - /// @visitorRoot node from where the visitor has been run - void executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, DAGNode* visitorRoot ); - void executeVisitorBottomUp(simulation::Visitor* action, NodeList& executedNodes ); - /// @} - - /// @internal tree traversal implementation - void executeVisitorTreeTraversal( Visitor* action, StatusMap& statusMap, Visitor::TreeTraversalRepetition repeat, bool alreadyRepeated=false ); - - /// @name @internal stuff related to getObjects - /// @{ - - /// get node's local objects respecting specified class_info and tags - void getLocalObjects( const sofa::core::objectmodel::ClassInfo& class_info, DAGNode::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags ) const ; - - friend class GetDownObjectsVisitor ; - friend class GetUpObjectsVisitor ; - /// @} -}; - + using DAGNode = sofa::simulation::Node; } // namespace sofa::simulation::graph From 8f897b8d010d6fccb49adc640c98b15e7decd013 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Fri, 27 Jun 2025 12:59:06 +0200 Subject: [PATCH 2/6] Use Node in place of DAGNode in sofa's code. --- .../Collision/Geometry/tests/Sphere_test.cpp | 16 +- .../Geometry/tests/Triangle_test.cpp | 6 +- .../Response/Mapper/tests/BaryMapper_test.cpp | 6 +- .../LinearSystem/tests/MappingGraph_test.cpp | 10 +- .../tests/MatrixLinearSystem_test.cpp | 8 +- .../Helper/test/system/PluginManager_test.cpp | 4 +- .../src/sofa/simpleapi/SimpleApi.cpp | 6 +- .../Core/src/sofa/simulation/Node.h | 155 +++++++++--------- .../framework/Simulation/Graph/CMakeLists.txt | 2 +- .../sofa/simulation/graph/DAGSimulation.cpp | 2 +- .../graph/{DAGNode.cpp => NodeDagImpl.cpp} | 0 .../Simulation/Graph/test/CMakeLists.txt | 2 +- .../Simulation/Graph/test/DAGNode_test.cpp | 36 ++-- .../Simulation/Graph/test/DAG_test.cpp | 16 +- .../Graph/test/MutationListener_test.cpp | 32 ++-- .../SceneCreatorBenchmarks.cpp | 2 +- 16 files changed, 148 insertions(+), 155 deletions(-) rename Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/{DAGNode.cpp => NodeDagImpl.cpp} (100%) diff --git a/Sofa/Component/Collision/Geometry/tests/Sphere_test.cpp b/Sofa/Component/Collision/Geometry/tests/Sphere_test.cpp index 9f5e300a56c..8f14b6da125 100644 --- a/Sofa/Component/Collision/Geometry/tests/Sphere_test.cpp +++ b/Sofa/Component/Collision/Geometry/tests/Sphere_test.cpp @@ -58,7 +58,7 @@ using sofa::helper::logging::MessageDispatcher ; #include using sofa::helper::logging::ClangMessageHandler ; -#include +#include #include using sofa::testing::BaseSimulationTest; @@ -101,7 +101,7 @@ bool TestSphere::rigidRigid1(){ angles[1] = 0; angles[2] = 0; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -157,7 +157,7 @@ bool TestSphere::rigidRigid2(){ angles_2[1] = 0; angles_2[2] = 0; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -204,7 +204,7 @@ bool TestSphere::rigidSoft2(){ angles[1] = M_PI/4; angles[2] = M_PI/3; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -251,7 +251,7 @@ bool TestSphere::rigidSoft1(){ angles[1] = 0; angles[2] = 0; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -299,7 +299,7 @@ bool TestSphere::rigidSoft3(){ angles[1] = M_PI/4; angles[2] = M_PI/3; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -346,7 +346,7 @@ bool TestSphere::rigidSoft4(){ angles[1] = 0; angles[2] = 0; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -384,7 +384,7 @@ bool TestSphere::rigidSoft4(){ bool TestSphere::softSoft1(){ - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere diff --git a/Sofa/Component/Collision/Geometry/tests/Triangle_test.cpp b/Sofa/Component/Collision/Geometry/tests/Triangle_test.cpp index e6f8567a262..0727f317683 100644 --- a/Sofa/Component/Collision/Geometry/tests/Triangle_test.cpp +++ b/Sofa/Component/Collision/Geometry/tests/Triangle_test.cpp @@ -47,7 +47,7 @@ using sofa::helper::logging::MessageDispatcher; #include using sofa::testing::BaseTest; -#include +#include #include #include @@ -82,7 +82,7 @@ bool TestTriangle::rigidTriangle(sofa::component::collision::detection::intersec angles[1] = 0; angles[2] = 0; - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere @@ -121,7 +121,7 @@ bool TestTriangle::rigidTriangle(sofa::component::collision::detection::intersec template bool TestTriangle::softTriangle(sofa::component::collision::detection::intersection::BaseProximityIntersection::SPtr intersectionMethod, Intersector& bi) { - Node::SPtr scn = New(); + Node::SPtr scn = New(); //the center of this OBB is (0,0,-1) and its extent is 1 //we construct the falling sphere diff --git a/Sofa/Component/Collision/Response/Mapper/tests/BaryMapper_test.cpp b/Sofa/Component/Collision/Response/Mapper/tests/BaryMapper_test.cpp index aa69dc976da..08d4c4b4866 100644 --- a/Sofa/Component/Collision/Response/Mapper/tests/BaryMapper_test.cpp +++ b/Sofa/Component/Collision/Response/Mapper/tests/BaryMapper_test.cpp @@ -20,7 +20,7 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ -#include +#include #include #include #include @@ -84,7 +84,7 @@ MeshTopology* BaryMapperTest::initMesh(NodePtr &father){ bool BaryMapperTest::test_inside(SReal alpha,SReal beta){ initTriPts(); - sofa::simulation::Node::SPtr father = New(); + sofa::simulation::Node::SPtr father = New(); MeshTopology * topo = initMesh(father); //makeTri() const component::mapping::linear::BarycentricMapperMeshTopology::SPtr mapper = sofa::core::objectmodel::New >(topo, (component::topology::container::dynamic::PointSetTopologyContainer*)0x0); @@ -103,7 +103,7 @@ bool BaryMapperTest::test_inside(SReal alpha,SReal beta){ bool BaryMapperTest::test_outside(int index){ initTriPts(); - sofa::simulation::Node::SPtr father = New(); + sofa::simulation::Node::SPtr father = New(); MeshTopology * topo = initMesh(father); //makeTri() const component::mapping::linear::BarycentricMapperMeshTopology::SPtr mapper = sofa::core::objectmodel::New >(topo,(component::topology::container::dynamic::PointSetTopologyContainer*)0x0); diff --git a/Sofa/Component/LinearSystem/tests/MappingGraph_test.cpp b/Sofa/Component/LinearSystem/tests/MappingGraph_test.cpp index e0c21d2a512..7de0406d2b5 100644 --- a/Sofa/Component/LinearSystem/tests/MappingGraph_test.cpp +++ b/Sofa/Component/LinearSystem/tests/MappingGraph_test.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -65,7 +65,7 @@ TEST(MappingGraph, nullRootNode) TEST(MappingGraph, emptyRootNode) { - const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); sofa::component::linearsystem::MappingGraph graph; graph.build(sofa::core::MechanicalParams::defaultInstance(), root.get()); @@ -85,7 +85,7 @@ TEST(MappingGraph, emptyRootNode) TEST(MappingGraph, oneMechanicalObject) { - const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); const auto mstate = sofa::core::objectmodel::New >(); root->addObject(mstate); @@ -107,7 +107,7 @@ TEST(MappingGraph, oneMechanicalObject) TEST(MappingGraph, twoMechanicalObject) { - const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); const auto mstate1 = sofa::core::objectmodel::New >(); root->addObject(mstate1); @@ -135,7 +135,7 @@ TEST(MappingGraph, twoMechanicalObject) TEST(MappingGraph, oneMapping) { - const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); const auto mstate1 = sofa::core::objectmodel::New >(); root->addObject(mstate1); diff --git a/Sofa/Component/LinearSystem/tests/MatrixLinearSystem_test.cpp b/Sofa/Component/LinearSystem/tests/MatrixLinearSystem_test.cpp index c8163bfdb3d..104a062297a 100644 --- a/Sofa/Component/LinearSystem/tests/MatrixLinearSystem_test.cpp +++ b/Sofa/Component/LinearSystem/tests/MatrixLinearSystem_test.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -62,7 +62,7 @@ TEST(LinearSystem, MatrixSystem_noContext) TEST(LinearSystem, MatrixSystem) { - const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); using MatrixType = sofa::linearalgebra::CompressedRowSparseMatrix; using MatrixSystem = sofa::component::linearsystem::MatrixLinearSystem >; @@ -92,7 +92,7 @@ TEST(LinearSystem, MatrixSystem_springForceField) // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; - sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); using MatrixType = sofa::linearalgebra::FullMatrix; using MatrixSystem = sofa::component::linearsystem::MatrixLinearSystem >; @@ -332,7 +332,7 @@ TEST(LinearSystem, MatrixSystem_buggyForceField) // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; - const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); + const sofa::simulation::Node::SPtr root = sofa::core::objectmodel::New(); using MatrixSystem = sofa::component::linearsystem::MatrixLinearSystem >; const MatrixSystem::SPtr linearSystem = sofa::core::objectmodel::New(); diff --git a/Sofa/framework/Helper/test/system/PluginManager_test.cpp b/Sofa/framework/Helper/test/system/PluginManager_test.cpp index c27aee28fe9..453c9ccb243 100644 --- a/Sofa/framework/Helper/test/system/PluginManager_test.cpp +++ b/Sofa/framework/Helper/test/system/PluginManager_test.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include using sofa::testing::BaseTest; @@ -319,7 +319,7 @@ TEST_F(PluginManager_test, failingPlugin) EXPECT_TRUE(sofa::core::ObjectFactory::getInstance()->hasCreator("ComponentFailingPlugin")); sofa::core::objectmodel::BaseObjectDescription description("ComponentFailingPlugin", "ComponentFailingPlugin"); - const auto tmpNode = sofa::core::objectmodel::New("tmp"); + const auto tmpNode = sofa::core::objectmodel::New("tmp"); EXPECT_EQ(sofa::core::ObjectFactory::getInstance()->createObject(tmpNode.get(), &description), nullptr); EXPECT_FALSE(description.getErrors().empty()); diff --git a/Sofa/framework/SimpleApi/src/sofa/simpleapi/SimpleApi.cpp b/Sofa/framework/SimpleApi/src/sofa/simpleapi/SimpleApi.cpp index 4d0935ab8c9..63555fcb1d4 100644 --- a/Sofa/framework/SimpleApi/src/sofa/simpleapi/SimpleApi.cpp +++ b/Sofa/framework/SimpleApi/src/sofa/simpleapi/SimpleApi.cpp @@ -29,8 +29,8 @@ using sofa::core::ObjectFactory ; #include using sofa::simulation::graph::DAGSimulation ; -#include -using sofa::simulation::graph::DAGNode; +#include +using sofa::simulation::Node; using sofa::core::objectmodel::BaseObjectDescription ; #include @@ -144,7 +144,7 @@ Node::SPtr createChild(Node::SPtr node, BaseObjectDescription& desc) Node::SPtr createNode(const std::string& name) { - return core::objectmodel::New(name); + return core::objectmodel::New(name); } } // namespace sofa::simpleapi diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h index 8ef3261dec6..46c640679c2 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.h @@ -290,6 +290,7 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// Generic object access, given a set of required tags, possibly searching up or down from the current context /// + /// Note that the template wrapper method should generally be used to have the correct return type, void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, SearchDirection dir = SearchUp) const override { @@ -304,6 +305,9 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, getObjects(class_info, container, sofa::core::objectmodel::TagSet(), dir); } + /// get node's local objects respecting specified class_info and tags + void getLocalObjects( const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags ) const ; + /// List all objects of this node deriving from a given class template void getNodeObjects(Container* list) @@ -486,6 +490,75 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// override context setSleeping to add notification. void setSleeping(bool val) override; +public: + virtual void addListener(MutationListener* obj); + virtual void removeListener(MutationListener* obj); + + static const std::string GetCustomClassName(){ return "Node"; } + + Node::SPtr createChild(const std::string& nodeName); + + /// Remove the current node from the graph: consists in removing the link to its parent + void detachFromGraph() override; + + /// Get a list of parent node + Parents getParents() const override; + + /// returns number of parents + size_t getNbParents() const override; + + /// return the first parent (returns nullptr if no parent) + BaseNode* getFirstParent() const override; + + /// Test if the given node is a parent of this node. + bool hasParent(const BaseNode* node) const override; + + /// Test if the given context is a parent of this context. + bool hasParent(const BaseContext* context) const; + + /// Test if the given context is an ancestor of this context. + /// An ancestor is a parent or (recursively) the parent of an ancestor. + bool hasAncestor(const BaseNode* node) const override + { + return hasAncestor(node->getContext()); + } + + /// Test if the given context is an ancestor of this context. + /// An ancestor is a parent or (recursively) the parent of an ancestor. + bool hasAncestor(const BaseContext* context) const override; + + + /// Generic object access, given a set of required tags, possibly searching up or down from the current context + /// + /// Note that the template wrapper method should generally be used to have the correct return type, + void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; + + /// Generic object access, given a path from the current context + /// + /// Note that the template wrapper method should generally be used to have the correct return type, + void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const override; + + /// Generic list of objects access, given a set of required tags, possibly searching up or down from the current context + /// + /// Note that the template wrapper method should generally be used to have the correct return type, + void getObjects(const sofa::core::objectmodel::ClassInfo& class_info, GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; + + /// Mesh Topology that is relevant for this context + /// (within it or its parents until a mapping is reached that does not preserve topologies). + sofa::core::topology::BaseMeshTopology* NODEgetMeshTopologyLink(SearchDirection dir = SearchUp) const; + + static Node::SPtr create(Node*, sofa::core::objectmodel::BaseObjectDescription* arg) + { + Node::SPtr obj = Node::SPtr(new Node()); + obj->parse(arg); + return obj; + } + + void moveChild(BaseNode::SPtr node) override; + + /// return the smallest common parent between this and node2 (returns nullptr if separated sub-graphes) + Node* findCommonParent( simulation::Node* node2 ); + protected: bool debug_; bool initialized; @@ -495,6 +568,7 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, virtual void doMoveObject(sofa::core::objectmodel::BaseObject::SPtr sobj, Node* prev_parent); std::stack actionStack; + private: virtual void notifyBeginAddChild(Node::SPtr parent, Node::SPtr child) const; virtual void notifyBeginRemoveChild(Node::SPtr parent, Node::SPtr child) const; @@ -522,11 +596,6 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, type::vector listener; - -public: - virtual void addListener(MutationListener* obj); - virtual void removeListener(MutationListener* obj); - /// @name virtual functions to add/remove some special components directly in the right Sequence /// @{ @@ -576,73 +645,7 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, typedef MultiLink LinkParents; typedef LinkParents::const_iterator ParentIterator; -public: - static const std::string GetCustomClassName(){ return "Node"; } - - Node::SPtr createChild(const std::string& nodeName); - - /// Remove the current node from the graph: consists in removing the link to its parent - void detachFromGraph() override; - - /// Get a list of parent node - Parents getParents() const override; - - /// returns number of parents - size_t getNbParents() const override; - - /// return the first parent (returns nullptr if no parent) - BaseNode* getFirstParent() const override; - - /// Test if the given node is a parent of this node. - bool hasParent(const BaseNode* node) const override; - - /// Test if the given context is a parent of this context. - bool hasParent(const BaseContext* context) const; - - /// Test if the given context is an ancestor of this context. - /// An ancestor is a parent or (recursively) the parent of an ancestor. - bool hasAncestor(const BaseNode* node) const override - { - return hasAncestor(node->getContext()); - } - - /// Test if the given context is an ancestor of this context. - /// An ancestor is a parent or (recursively) the parent of an ancestor. - bool hasAncestor(const BaseContext* context) const override; - - - /// Generic object access, given a set of required tags, possibly searching up or down from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; - - /// Generic object access, given a path from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void* getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const override; - - /// Generic list of objects access, given a set of required tags, possibly searching up or down from the current context - /// - /// Note that the template wrapper method should generally be used to have the correct return type, - void getObjects(const sofa::core::objectmodel::ClassInfo& class_info, GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir = SearchUp) const override; - - /// Mesh Topology that is relevant for this context - /// (within it or its parents until a mapping is reached that does not preserve topologies). - sofa::core::topology::BaseMeshTopology* NODEgetMeshTopologyLink(SearchDirection dir = SearchUp) const; - - static Node::SPtr create(Node*, sofa::core::objectmodel::BaseObjectDescription* arg) - { - Node::SPtr obj = Node::SPtr(new Node()); - obj->parse(arg); - return obj; - } - - void moveChild(BaseNode::SPtr node) override; - - /// return the smallest common parent between this and node2 (returns nullptr if separated sub-graphes) - Node* findCommonParent( simulation::Node* node2 ); - -protected: +private: /// bottom-up traversal, returning the first node which have a descendancy containing both node1 & node2 Node* findCommonParent( Node* node1, Node* node2 ); @@ -655,11 +658,9 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// Execute a recursive action starting from this node. void doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder=false); - /// @name @internal stuff related to the DAG traversal /// @{ - /// all child nodes (unordered) std::set _descendancy; @@ -677,8 +678,6 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, PRUNED } VisitedStatus; - - /// wrapper to use VisitedStatus in a std::map (to ensure the default map insertion will give NOT_VISITED) struct StatusStruct { @@ -714,10 +713,6 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// @name @internal stuff related to getObjects /// @{ - - /// get node's local objects respecting specified class_info and tags - void getLocalObjects( const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags ) const ; - friend class GetDownObjectsVisitor ; friend class GetUpObjectsVisitor ; /// @} diff --git a/Sofa/framework/Simulation/Graph/CMakeLists.txt b/Sofa/framework/Simulation/Graph/CMakeLists.txt index b3d07bd5a51..31d593432f0 100644 --- a/Sofa/framework/Simulation/Graph/CMakeLists.txt +++ b/Sofa/framework/Simulation/Graph/CMakeLists.txt @@ -14,7 +14,7 @@ set(HEADER_FILES set(SOURCE_FILES ${SOFASIMULATIONGRAPH_SRC}/init.cpp ${SOFASIMULATIONGRAPH_SRC}/initSofaSimulationGraph.cpp - ${SOFASIMULATIONGRAPH_SRC}/DAGNode.cpp + ${SOFASIMULATIONGRAPH_SRC}/NodeDagImpl.cpp ${SOFASIMULATIONGRAPH_SRC}/DAGSimulation.cpp ) diff --git a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGSimulation.cpp b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGSimulation.cpp index 7a351f1075a..baa0bbf1a22 100644 --- a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGSimulation.cpp +++ b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGSimulation.cpp @@ -54,7 +54,7 @@ Node::SPtr DAGSimulation::createNewGraph(const std::string& name) Node::SPtr DAGSimulation::createNewNode(const std::string& name) { - return sofa::core::objectmodel::New(name); + return sofa::core::objectmodel::New(name); } } // namespace sofa::simulation::graph diff --git a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/NodeDagImpl.cpp similarity index 100% rename from Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp rename to Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/NodeDagImpl.cpp diff --git a/Sofa/framework/Simulation/Graph/test/CMakeLists.txt b/Sofa/framework/Simulation/Graph/test/CMakeLists.txt index de06e21da16..7c8fcc01857 100644 --- a/Sofa/framework/Simulation/Graph/test/CMakeLists.txt +++ b/Sofa/framework/Simulation/Graph/test/CMakeLists.txt @@ -7,7 +7,7 @@ set(HEADER_FILES set(SOURCE_FILES DAG_test.cpp - DAGNode_test.cpp + Node_test.cpp MutationListener_test.cpp Node_test.cpp Simulation_test.cpp diff --git a/Sofa/framework/Simulation/Graph/test/DAGNode_test.cpp b/Sofa/framework/Simulation/Graph/test/DAGNode_test.cpp index 3c629dc074b..3fd22f68dff 100644 --- a/Sofa/framework/Simulation/Graph/test/DAGNode_test.cpp +++ b/Sofa/framework/Simulation/Graph/test/DAGNode_test.cpp @@ -22,24 +22,24 @@ #include using sofa::testing::BaseTest; -#include +#include using namespace sofa; using namespace simulation::graph; -struct DAGNode_test : public BaseTest +struct Node_test : public BaseTest { - DAGNode_test() {} + Node_test() {} void test_findCommonParent() { - const DAGNode::SPtr root = core::objectmodel::New("root"); - const DAGNode::SPtr node1 = core::objectmodel::New("node1"); - const DAGNode::SPtr node2 = core::objectmodel::New("node2"); - const DAGNode::SPtr node3 = core::objectmodel::New("node3"); - const DAGNode::SPtr node11 = core::objectmodel::New("node11"); - const DAGNode::SPtr node12 = core::objectmodel::New("node12"); - const DAGNode::SPtr node31 = core::objectmodel::New("node31"); + const Node::SPtr root = core::objectmodel::New("root"); + const Node::SPtr node1 = core::objectmodel::New("node1"); + const Node::SPtr node2 = core::objectmodel::New("node2"); + const Node::SPtr node3 = core::objectmodel::New("node3"); + const Node::SPtr node11 = core::objectmodel::New("node11"); + const Node::SPtr node12 = core::objectmodel::New("node12"); + const Node::SPtr node31 = core::objectmodel::New("node31"); root->addChild(node1); root->addChild(node2); @@ -62,12 +62,12 @@ struct DAGNode_test : public BaseTest void test_findCommonParent_MultipleParents() { - const DAGNode::SPtr root = core::objectmodel::New("root"); - const DAGNode::SPtr node1 = core::objectmodel::New("node1"); - const DAGNode::SPtr node2 = core::objectmodel::New("node2"); - const DAGNode::SPtr node11 = core::objectmodel::New("node11"); - const DAGNode::SPtr node22 = core::objectmodel::New("node22"); - const DAGNode::SPtr node23 = core::objectmodel::New("node23"); + const Node::SPtr root = core::objectmodel::New("root"); + const Node::SPtr node1 = core::objectmodel::New("node1"); + const Node::SPtr node2 = core::objectmodel::New("node2"); + const Node::SPtr node11 = core::objectmodel::New("node11"); + const Node::SPtr node22 = core::objectmodel::New("node22"); + const Node::SPtr node23 = core::objectmodel::New("node23"); root->addChild(node1); root->addChild(node2); @@ -93,5 +93,5 @@ struct DAGNode_test : public BaseTest } }; -TEST_F(DAGNode_test, test_findCommonParent) { test_findCommonParent(); } -TEST_F(DAGNode_test, test_findCommonParent_MultipleParents) { test_findCommonParent_MultipleParents(); } +TEST_F(Node_test, test_findCommonParent) { test_findCommonParent(); } +TEST_F(Node_test, test_findCommonParent_MultipleParents) { test_findCommonParent_MultipleParents(); } diff --git a/Sofa/framework/Simulation/Graph/test/DAG_test.cpp b/Sofa/framework/Simulation/Graph/test/DAG_test.cpp index 3cf4a4ea1a5..5984c42a507 100644 --- a/Sofa/framework/Simulation/Graph/test/DAG_test.cpp +++ b/Sofa/framework/Simulation/Graph/test/DAG_test.cpp @@ -377,29 +377,29 @@ TEST_F( DAG_test, traverse ) traverse_morecomplex2(); } -TEST(DAGNodeTest, objectDestruction_singleObject) +TEST(NodeTest, objectDestruction_singleObject) { EXPECT_MSG_NOEMIT(Error) ; - Node_test_objectDestruction_singleObject(); + Node_test_objectDestruction_singleObject(); } -TEST(DAGNodeTest, objectDestruction_multipleObjects) +TEST(NodeTest, objectDestruction_multipleObjects) { EXPECT_MSG_NOEMIT(Error) ; - Node_test_objectDestruction_multipleObjects(); + Node_test_objectDestruction_multipleObjects(); } -TEST(DAGNodeTest, objectDestruction_childNode_singleObject) +TEST(NodeTest, objectDestruction_childNode_singleObject) { EXPECT_MSG_NOEMIT(Error) ; - Node_test_objectDestruction_childNode_singleObject(); + Node_test_objectDestruction_childNode_singleObject(); } -TEST(DAGNodeTest, objectDestruction_childNode_complexChild) +TEST(NodeTest, objectDestruction_childNode_complexChild) { EXPECT_MSG_NOEMIT(Error) ; - Node_test_objectDestruction_childNode_complexChild(); + Node_test_objectDestruction_childNode_complexChild(); } diff --git a/Sofa/framework/Simulation/Graph/test/MutationListener_test.cpp b/Sofa/framework/Simulation/Graph/test/MutationListener_test.cpp index c2d20e7bc9c..bca065e28a5 100644 --- a/Sofa/framework/Simulation/Graph/test/MutationListener_test.cpp +++ b/Sofa/framework/Simulation/Graph/test/MutationListener_test.cpp @@ -23,15 +23,13 @@ using sofa::testing::BaseTest; #include -#include +#include #include #include using sofa::simulation::MutationListener; using sofa::core::objectmodel::BaseObject; using sofa::simulation::Simulation; using sofa::simulation::Node; -using sofa::simulation::graph::DAGNode; - class TestMutationListener : public MutationListener { @@ -291,8 +289,8 @@ struct MutationListener_test : public BaseTest void test_addChildWithDescendency() { - const DAGNode::SPtr node1 = sofa::core::objectmodel::New("node1"); - const DAGNode::SPtr node2 = sofa::core::objectmodel::New("node2"); + const Node::SPtr node1 = sofa::core::objectmodel::New("node1"); + const Node::SPtr node2 = sofa::core::objectmodel::New("node2"); node1->addChild(node2); sofa::core::objectmodel::BaseObjectDescription bod1("obj1", "BaseObject"); obj1 = sofa::core::objectmodel::New(); @@ -313,8 +311,8 @@ struct MutationListener_test : public BaseTest void test_removeChildWithDescendency() { - const DAGNode::SPtr node1 = sofa::core::objectmodel::New("node1"); - const DAGNode::SPtr node2 = sofa::core::objectmodel::New("node2"); + const Node::SPtr node1 = sofa::core::objectmodel::New("node1"); + const Node::SPtr node2 = sofa::core::objectmodel::New("node2"); node1->addChild(node2); sofa::core::objectmodel::BaseObjectDescription bod1("obj1", "BaseObject"); obj1 = sofa::core::objectmodel::New(); @@ -336,8 +334,8 @@ struct MutationListener_test : public BaseTest void test_moveChildWithDescendency() { - const DAGNode::SPtr node1 = sofa::core::objectmodel::New("node1"); - const DAGNode::SPtr node2 = sofa::core::objectmodel::New("node2"); + const Node::SPtr node1 = sofa::core::objectmodel::New("node1"); + const Node::SPtr node2 = sofa::core::objectmodel::New("node2"); node1->addChild(node2); sofa::core::objectmodel::BaseObjectDescription bod1("obj1", "BaseObject"); obj1 = sofa::core::objectmodel::New(); @@ -362,14 +360,14 @@ struct MutationListener_test : public BaseTest void test_moveChildWithDescendencyAndMultipleParents() { - const DAGNode::SPtr node1 = sofa::core::objectmodel::New("node1"); - const DAGNode::SPtr node2 = sofa::core::objectmodel::New("node2"); - const DAGNode::SPtr node3 = sofa::core::objectmodel::New("node3"); - const DAGNode::SPtr node4 = sofa::core::objectmodel::New("node4"); - const DAGNode::SPtr node5 = sofa::core::objectmodel::New("node5"); - const DAGNode::SPtr node6 = sofa::core::objectmodel::New("node6"); - const DAGNode::SPtr node7 = sofa::core::objectmodel::New("node7"); - const DAGNode::SPtr node8 = sofa::core::objectmodel::New("node8"); + const Node::SPtr node1 = sofa::core::objectmodel::New("node1"); + const Node::SPtr node2 = sofa::core::objectmodel::New("node2"); + const Node::SPtr node3 = sofa::core::objectmodel::New("node3"); + const Node::SPtr node4 = sofa::core::objectmodel::New("node4"); + const Node::SPtr node5 = sofa::core::objectmodel::New("node5"); + const Node::SPtr node6 = sofa::core::objectmodel::New("node6"); + const Node::SPtr node7 = sofa::core::objectmodel::New("node7"); + const Node::SPtr node8 = sofa::core::objectmodel::New("node8"); sofa::core::objectmodel::BaseObjectDescription bod1("obj1", "BaseObject"); obj1 = sofa::core::objectmodel::New(); diff --git a/applications/plugins/SceneCreator/sceneCreatorExamples/SceneCreatorBenchmarks.cpp b/applications/plugins/SceneCreator/sceneCreatorExamples/SceneCreatorBenchmarks.cpp index 15fab358841..5b856a67269 100644 --- a/applications/plugins/SceneCreator/sceneCreatorExamples/SceneCreatorBenchmarks.cpp +++ b/applications/plugins/SceneCreator/sceneCreatorExamples/SceneCreatorBenchmarks.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include From 4d0de258e5855bf8ec345339b6a2b618f64f04f8 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Fri, 27 Jun 2025 15:54:54 +0200 Subject: [PATCH 3/6] Copy past content of DAGNode in Node --- .../Core/src/sofa/simulation/Node.cpp | 842 ++++++++++++++++- .../framework/Simulation/Graph/CMakeLists.txt | 2 +- .../src/sofa/simulation/graph/DAGNode.cpp | 31 + .../src/sofa/simulation/graph/NodeDagImpl.cpp | 851 ------------------ 4 files changed, 857 insertions(+), 869 deletions(-) create mode 100644 Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp delete mode 100644 Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/NodeDagImpl.cpp diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index 53a988adaff..b35300735fc 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp @@ -850,23 +850,6 @@ void Node::initialize() updateSimulationContext(); } -void Node::updateContext() -{ - updateSimulationContext(); - updateVisualContext(); - if ( debug_ ) - msg_info()<<"Node::updateContext, node = "<(node); + if( dagnode->_descendancy.contains(_searchNode) ) // searchNode is in the current node descendancy, so the current node is a parent of searchNode + { + dagnode->getLocalObjects( _class_info, _container, _tags ); + return RESULT_CONTINUE; + } + else // the current node is NOT a parent of searchNode, stop here + { + return RESULT_PRUNE; + } + } + + /// Specify whether this action can be parallelized. + bool isThreadSafe() const override { return false; } + + /// Return a category name for this action. + /// Only used for debugging / profiling purposes + const char* getCategoryName() const override { return "GetUpObjectsVisitor"; } + const char* getClassName() const override { return "GetUpObjectsVisitor"; } + + +protected: + + Node* _searchNode; + const sofa::core::objectmodel::ClassInfo& _class_info; + Node::GetObjectsCallBack& _container; + const sofa::core::objectmodel::TagSet& _tags; + +}; + +GetUpObjectsVisitor::GetUpObjectsVisitor(Node* searchNode, + const sofa::core::objectmodel::ClassInfo& class_info, + Node::GetObjectsCallBack& container, + const sofa::core::objectmodel::TagSet& tags) + : Visitor( sofa::core::execparams::defaultInstance() ) + , _searchNode( searchNode ) + , _class_info(class_info) + , _container(container) + , _tags(tags) +{} + +GetUpObjectsVisitor::~GetUpObjectsVisitor(){} + +/// Create, add, then return the new child of this Node +Node::SPtr Node::createChild(const std::string& nodeName) +{ + Node::SPtr newchild; + if (nodeName.empty()) + { + int i = 0; + std::string newName = "unnamed"; + bool uid_found = false; + while (!uid_found) + { + uid_found = true; + for (const auto& c : this->child) + { + if (c->getName() == newName) + { + newName = "unnamed" + std::to_string(++i); + uid_found = true; + } + } + for (const auto& o : this->object) + { + if (o->getName() == newName) + { + newName = "unnamed" + std::to_string(++i); + uid_found = true; + } + } + } + msg_error("Node::createChild()") << "Empty string given to property 'name': Forcefully setting an empty name is forbidden.\n" + "Renaming to " + newName + " to avoid unexpected behaviors."; + newchild = sofa::core::objectmodel::New(newName); + } + else + newchild = sofa::core::objectmodel::New(nodeName); + this->addChild(newchild); newchild->updateSimulationContext(); + return newchild; +} + + +void Node::moveChild(BaseNode::SPtr node) +{ + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + for (const auto& parent : dagnode->getParents()) { + Node::moveChild(node, parent); + } +} + + +/// Add a child node +void Node::doAddChild(BaseNode::SPtr node) +{ + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + setDirtyDescendancy(); + child.add(dagnode); + dagnode->l_parents.add(this); + dagnode->l_parents.updateLinks(); // to fix load-time unresolved links +} + +/// Remove a child +void Node::doRemoveChild(BaseNode::SPtr node) +{ + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + setDirtyDescendancy(); + child.remove(dagnode); + dagnode->l_parents.remove(this); +} + +/// Move a node from another node +void Node::doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent) +{ + const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); + if (!dagnode) return; + + setDirtyDescendancy(); + previous_parent->removeChild(node); + + addChild(node); +} + +/// Remove a child +void Node::detachFromGraph() +{ + Node::SPtr me = this; // make sure we don't delete ourself before the end of this method + const LinkParents::Container& parents = l_parents.getValue(); + while(!parents.empty()) + parents.back()->removeChild(this); +} + +/// Generic object access, possibly searching up or down from the current context +/// +/// Note that the template wrapper method should generally be used to have the correct return type, +void* Node::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir) const +{ + if (dir == SearchRoot) + { + if (getNbParents()) return getRootContext()->getObject(class_info, tags, dir); + else dir = SearchDown; // we are the root, search down from here. + } + void *result = nullptr; + + if (dir != SearchParents) + for (ObjectIterator it = this->object.begin(); it != this->object.end(); ++it) + { + sofa::core::objectmodel::BaseObject* obj = it->get(); + if (tags.empty() || (obj)->getTags().includes(tags)) + { + + result = class_info.dynamicCast(obj); + if (result != nullptr) + { + + break; + } + } + } + + if (result == nullptr) + { + switch(dir) + { + case Local: + break; + case SearchParents: + case SearchUp: + { + const LinkParents::Container& parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; ++i){ + result = parents[i]->getObject(class_info, tags, SearchUp); + if (result != nullptr) break; + } + } + break; + case SearchDown: + for(ChildIterator it = child.begin(); it != child.end(); ++it) + { + result = (*it)->getObject(class_info, tags, dir); + if (result != nullptr) break; + } + break; + case SearchRoot: + dmsg_error("Node") << "SearchRoot SHOULD NOT BE POSSIBLE HERE."; + break; + } + } + + return result; +} + +/// Generic object access, given a path from the current context +/// +/// Note that the template wrapper method should generally be used to have the correct return type, +void* Node::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const +{ + if (path.empty()) + { + // local object + return Node::getObject(class_info, Local); + } + else if (path[0] == '/') + { + // absolute path; let's start from root + if (!getNbParents()) return getObject(class_info,std::string(path,1)); + else return getRootContext()->getObject(class_info,path); + } + else if (std::string(path,0,2)==std::string("./")) + { + std::string newpath = std::string(path, 2); + while (!newpath.empty() && path[0] == '/') + newpath.erase(0); + return getObject(class_info,newpath); + } + else if (std::string(path,0,3)==std::string("../")) + { + // tricky case: + // let's test EACH parent and return the first object found (if any) + std::string newpath = std::string(path, 3); + while (!newpath.empty() && path[0] == '/') + newpath.erase(0); + if (getNbParents()) + { + const LinkParents::Container& parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; ++i) + { + void* obj = parents[i]->getObject(class_info,newpath); + if (obj) return obj; + } + return nullptr; // not found in any parent node at all + } + else return getObject(class_info,newpath); + } + else + { + std::string::size_type pend = path.find('/'); + if (pend == std::string::npos) pend = path.length(); + const std::string name ( path, 0, pend ); + const Node* child = getChild(name); + if (child) + { + while (pend < path.length() && path[pend] == '/') + ++pend; + return child->getObject(class_info, std::string(path, pend)); + } + else if (pend < path.length()) + { + return nullptr; + } + else + { + sofa::core::objectmodel::BaseObject* obj = simulation::Node::getObject(name); + if (obj == nullptr) + { + return nullptr; + } + else + { + void* result = class_info.dynamicCast(obj); + if (result == nullptr) + { + dmsg_error("Node") << "Object "<getObjects( class_info, container, tags, dir ); + return; + } + else dir = SearchDown; // we are the root, search down from here. + } + + + switch( dir ) + { + case Local: + this->getLocalObjects( class_info, container, tags ); + break; + + case SearchUp: + this->getLocalObjects( class_info, container, tags ); // add locals then SearchParents + // no break here, we want to execute the SearchParents code. + [[fallthrough]]; + case SearchParents: + { + // a visitor executed from top but only run for this' parents will enforce the selected object unicity due even with diamond graph setups + GetUpObjectsVisitor vis( const_cast(this), class_info, container, tags); + getRootContext()->executeVisitor(&vis); + } + break; + + case SearchDown: + { + // a regular visitor is enforcing the selected object unicity + GetDownObjectsVisitor vis(class_info, container, tags); + (const_cast(this))->executeVisitor(&vis); + break; + } + default: + break; + } +} + +/// Get a list of parent node +sofa::core::objectmodel::BaseNode::Parents Node::getParents() const +{ + Parents p; + + const LinkParents::Container& parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; ++i) + p.push_back(parents[i]); + + return p; +} + + +/// returns number of parents +size_t Node::getNbParents() const +{ + return l_parents.getValue().size(); +} + +/// return the first parent (returns nullptr if no parent) +sofa::core::objectmodel::BaseNode* Node::getFirstParent() const +{ + const LinkParents::Container& parents = l_parents.getValue(); + if( parents.empty() ) return nullptr; + else return l_parents.getValue()[0]; +} + + +/// Test if the given node is a parent of this node. +bool Node::hasParent(const BaseNode* node) const +{ + const LinkParents::Container& parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; ++i) + { + if (parents[i]==node) return true; + } + return false; +} + +/// Test if the given context is a parent of this context. +bool Node::hasParent(const BaseContext* context) const +{ + if (context == nullptr) return !getNbParents(); + + const LinkParents::Container& parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; ++i) + if (context == parents[i]->getContext()) return true; + return false; + +} + + + +/// Test if the given context is an ancestor of this context. +/// An ancestor is a parent or (recursively) the parent of an ancestor. +bool Node::hasAncestor(const BaseContext* context) const +{ + const LinkParents::Container& parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; ++i) + if (context == parents[i]->getContext() + || parents[i]->hasAncestor(context)) + return true; + return false; +} + + +/// Mesh Topology that is relevant for this context +/// (within it or its parents until a mapping is reached that does not preserve topologies). +sofa::core::topology::BaseMeshTopology* Node::getMeshTopologyLink(SearchDirection dir) const +{ + if (this->meshTopology) + return this->meshTopology; + + if (dir != Local) + return Node::getMeshTopologyLink(dir); + + //local case similar to getActiveMeshTopology ... + + // Check if a local mapping stops the search + if (this->mechanicalMapping && !this->mechanicalMapping->sameTopology()) + { + return nullptr; + } + for ( Sequence::iterator i=this->mapping.begin(), iend=this->mapping.end(); i!=iend; ++i ) + { + if (!(*i)->sameTopology()) + { + return nullptr; + } + } + // No mapping with a different topology, continue on to the parents + const LinkParents::Container &parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; i++ ) + { + // if the visitor is run from a sub-graph containing a multinode linked with a node outside of the subgraph, do not consider the outside node by looking on the sub-graph descendancy + if ( parents[i] ) + { + sofa::core::topology::BaseMeshTopology* res = parents[i]->getMeshTopologyLink(Local); + if (res) + return res; + } + } + return nullptr; // not found in any parents +} + +void Node::precomputeTraversalOrder( const sofa::core::ExecParams* params ) +{ + // accumulating traversed Nodes + class TraversalOrderVisitor : public Visitor + { + NodeList& _orderList; + public: + TraversalOrderVisitor(const sofa::core::ExecParams* params, NodeList& orderList ) + : Visitor(params) + , _orderList( orderList ) + { + _orderList.clear(); + } + + Result processNodeTopDown(Node* node) override + { + _orderList.push_back( static_cast(node) ); + return RESULT_CONTINUE; + } + + const char* getClassName() const override {return "TraversalOrderVisitor";} + }; + + TraversalOrderVisitor tov( params, _precomputedTraversalOrder ); + executeVisitor( &tov, false ); +} + + + +/// Execute a recursive action starting from this node +void Node::doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder) +{ + if( precomputedOrder && !_precomputedTraversalOrder.empty() ) + { + for( NodeList::iterator it = _precomputedTraversalOrder.begin(), itend = _precomputedTraversalOrder.end() ; it != itend ; ++it ) + { + if ( action->canAccessSleepingNode || !(*it)->getContext()->isSleeping() ) + action->processNodeTopDown( *it ); + } + + for( NodeList::reverse_iterator it = _precomputedTraversalOrder.rbegin(), itend = _precomputedTraversalOrder.rend() ; it != itend ; ++it ) + { + if ( action->canAccessSleepingNode || !(*it)->getContext()->isSleeping() ) + action->processNodeBottomUp( *it ); + } + } + else + { + // WARNING: do not store the traversal infos in the Node, as several visitors could traversed the graph simultaneously + // These infos are stored in a StatusMap per visitor. + updateDescendancy(); + + Visitor::TreeTraversalRepetition repeat; + if( action->treeTraversal(repeat) ) + { + // Tree traversal order + // + // Diamond shapes are ignored, a child node is visited as soon as a parent node has been visited. + // The multi-nodes (with several parents) are visited either: only once, only twice or for every times + // depending on the visitor's 'repeat' + // + // Some particular visitors such as a flat graph display or VisualVisitors must follow such a traversal order. + + StatusMap statusMap; + executeVisitorTreeTraversal( action, statusMap, repeat ); + } + else + { + // Direct acyclic graph traversal order + // + // This is the default order, used for mechanics. + // + // A child node is visited only when all its parents have been visited. + // A child node is 'pruned' only if all its parents are 'pruned'. + // Every executed node in the forward traversal are stored in 'executedNodes', + // its reverse order is used for the backward traversal. + + // Note that a newly 'pruned' node is still traversed (w/o execution) to be sure to execute its child nodes, + // that can have ancestors in another branch that is not pruned... + // An already pruned node is ignored. + + NodeList executedNodes; + { + StatusMap statusMap; + executeVisitorTopDown( action, executedNodes, statusMap, this ); + } + executeVisitorBottomUp( action, executedNodes ); + } + } +} + + +void Node::executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, Node* visitorRoot ) +{ + if ( statusMap[this] != NOT_VISITED ) + { + return; // skipped (already visited) + } + + if( !this->isActive() ) + { + // do not execute the visitor on this node + statusMap[this] = PRUNED; + + // in that case we can considerer if some child are activated, the graph is not valid, so no need to continue the recursion + return; + } + + if( this->isSleeping() && !action->canAccessSleepingNode ) + { + // do not execute the visitor on this node + statusMap[this] = PRUNED; + + return; + } + + // pour chaque noeud "prune" on continue à parcourir quand même juste pour marquer le noeud comme parcouru + + // check du "visitedStatus" des parents: + // un enfant n'est pruné que si tous ses parents le sont + // on ne passe à un enfant que si tous ses parents ont été visités + bool allParentsPruned = true; + bool hasParent = false; + + if( visitorRoot != this ) + { + // the graph structure is generally modified during an action anterior to the traversal but can possibly be modified during the current traversal + visitorRoot->updateDescendancy(); + + const LinkParents::Container &parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; i++ ) + { + // if the visitor is run from a sub-graph containing a multinode linked with a node outside of the subgraph, do not consider the outside node by looking on the sub-graph descendancy + if ( visitorRoot->_descendancy.contains(parents[i]) || parents[i]==visitorRoot ) + { + // all parents must have been visited before + if ( statusMap[parents[i]] == NOT_VISITED ) + return; // skipped for now... the other parent should come later + + allParentsPruned = allParentsPruned && ( statusMap[parents[i]] == PRUNED ); + hasParent = true; + } + } + } + + // all parents have been visited, let's go with the visitor + if ( allParentsPruned && hasParent ) + { + // do not execute the visitor on this node + statusMap[this] = PRUNED; + + // ... but continue the recursion anyway! + if( action->childOrderReversed(this) ) + for(unsigned int i = unsigned(child.size()); i>0;) + static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + else + for(unsigned int i = 0; i(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + } + else + { + // execute the visitor on this node + const Visitor::Result result = action->processNodeTopDown(this); + + // update status + statusMap[this] = ( result == simulation::Visitor::RESULT_PRUNE ? PRUNED : VISITED ); + + executedNodes.push_back(this); + + // ... and continue the recursion + if( action->childOrderReversed(this) ) + for(unsigned int i = unsigned(child.size()); i>0;) + static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + else + for(unsigned int i = 0; i(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); + + } +} + + +// warning nodes that are dynamically created during the traversal, but that have not been traversed during the top-down, won't be traversed during the bottom-up +// TODO is it what we want? +// otherwise it is possible to restart from top, go to leaves and running bottom-up action while going up +void Node::executeVisitorBottomUp( simulation::Visitor* action, NodeList& executedNodes ) +{ + for( NodeList::reverse_iterator it = executedNodes.rbegin(), itend = executedNodes.rend() ; it != itend ; ++it ) + { + (*it)->updateDescendancy(); + action->processNodeBottomUp( *it ); + } +} + + +void Node::setDirtyDescendancy() +{ + _descendancy.clear(); + const LinkParents::Container &parents = l_parents.getValue(); + for ( unsigned int i = 0; i < parents.size() ; i++ ) + { + parents[i]->setDirtyDescendancy(); + } +} + +void Node::updateDescendancy() +{ + if( _descendancy.empty() && !child.empty() ) + { + for(unsigned int i = 0; i(child[i].get()); + dagnode->updateDescendancy(); + _descendancy.insert( dagnode->_descendancy.begin(), dagnode->_descendancy.end() ); + _descendancy.insert( dagnode ); + } + } +} + + + +void Node::executeVisitorTreeTraversal( simulation::Visitor* action, StatusMap& statusMap, Visitor::TreeTraversalRepetition repeat, bool alreadyRepeated ) +{ + if( !this->isActive() ) + { + // do not execute the visitor on this node + statusMap[this] = PRUNED; + return; + } + + if( this->isSleeping() && !action->canAccessSleepingNode ) + { + // do not execute the visitor on this node + statusMap[this] = PRUNED; + return; + } + + // node already visited and repetition must be avoid + if( statusMap[this] != NOT_VISITED ) + { + if( repeat==Visitor::NO_REPETITION || ( alreadyRepeated && repeat==Visitor::REPEAT_ONCE ) ) return; + else alreadyRepeated = true; + } + + if( action->processNodeTopDown(this) != simulation::Visitor::RESULT_PRUNE ) + { + statusMap[this] = VISITED; + if( action->childOrderReversed(this) ) + for(unsigned int i = unsigned(child.size()); i>0;) + static_cast(child[--i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); + else + for(unsigned int i = 0; i(child[i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); + } + else + { + statusMap[this] = PRUNED; + } + + action->processNodeBottomUp(this); +} + + +void Node::initVisualContext() +{ + if (getNbParents()) + { + this->setDisplayWorldGravity(false); //only display gravity for the root: it will be propagated at each time step + } +} + +void Node::updateContext() +{ + sofa::core::objectmodel::BaseNode* firstParent = getFirstParent(); + + if ( firstParent ) + { + if( debug_ ) + { + msg_info()<<"Node::updateContext, node = "< NodeClass("Node"); + +} diff --git a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/NodeDagImpl.cpp b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/NodeDagImpl.cpp deleted file mode 100644 index f0d36b47d66..00000000000 --- a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/NodeDagImpl.cpp +++ /dev/null @@ -1,851 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 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 . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include -#include - -namespace sofa::simulation -{ - -/// get all down objects respecting specified class_info and tags -class GetDownObjectsVisitor : public Visitor -{ -public: - - GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); - ~GetDownObjectsVisitor() ; - - Result processNodeTopDown(simulation::Node* node) override - { - static_cast(node)->getLocalObjects( _class_info, _container, _tags ); - return RESULT_CONTINUE; - } - - /// Specify whether this action can be parallelized. - bool isThreadSafe() const override { return false; } - - /// Return a category name for this action. - /// Only used for debugging / profiling purposes - const char* getCategoryName() const override { return "GetDownObjectsVisitor"; } - const char* getClassName() const override { return "GetDownObjectsVisitor"; } - -protected: - const sofa::core::objectmodel::ClassInfo& _class_info; - Node::GetObjectsCallBack& _container; - const sofa::core::objectmodel::TagSet& _tags; -}; - -GetDownObjectsVisitor::GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, - Node::GetObjectsCallBack& container, - const sofa::core::objectmodel::TagSet& tags) - : Visitor( sofa::core::execparams::defaultInstance() ) - , _class_info(class_info) - , _container(container) - , _tags(tags) -{} - -GetDownObjectsVisitor::~GetDownObjectsVisitor(){} - -/// get all up objects respecting specified class_info and tags -class GetUpObjectsVisitor : public Visitor -{ -public: - - GetUpObjectsVisitor(Node* searchNode, const sofa::core::objectmodel::ClassInfo& class_info, Node::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); - ~GetUpObjectsVisitor() override; - - Result processNodeTopDown(simulation::Node* node) override - { - const Node* dagnode = dynamic_cast(node); - if( dagnode->_descendancy.contains(_searchNode) ) // searchNode is in the current node descendancy, so the current node is a parent of searchNode - { - dagnode->getLocalObjects( _class_info, _container, _tags ); - return RESULT_CONTINUE; - } - else // the current node is NOT a parent of searchNode, stop here - { - return RESULT_PRUNE; - } - } - - /// Specify whether this action can be parallelized. - bool isThreadSafe() const override { return false; } - - /// Return a category name for this action. - /// Only used for debugging / profiling purposes - const char* getCategoryName() const override { return "GetUpObjectsVisitor"; } - const char* getClassName() const override { return "GetUpObjectsVisitor"; } - - -protected: - - Node* _searchNode; - const sofa::core::objectmodel::ClassInfo& _class_info; - Node::GetObjectsCallBack& _container; - const sofa::core::objectmodel::TagSet& _tags; - -}; - -GetUpObjectsVisitor::GetUpObjectsVisitor(Node* searchNode, - const sofa::core::objectmodel::ClassInfo& class_info, - Node::GetObjectsCallBack& container, - const sofa::core::objectmodel::TagSet& tags) - : Visitor( sofa::core::execparams::defaultInstance() ) - , _searchNode( searchNode ) - , _class_info(class_info) - , _container(container) - , _tags(tags) -{} - -GetUpObjectsVisitor::~GetUpObjectsVisitor(){} - -/// Create, add, then return the new child of this Node -Node::SPtr Node::createChild(const std::string& nodeName) -{ - Node::SPtr newchild; - if (nodeName.empty()) - { - int i = 0; - std::string newName = "unnamed"; - bool uid_found = false; - while (!uid_found) - { - uid_found = true; - for (const auto& c : this->child) - { - if (c->getName() == newName) - { - newName = "unnamed" + std::to_string(++i); - uid_found = true; - } - } - for (const auto& o : this->object) - { - if (o->getName() == newName) - { - newName = "unnamed" + std::to_string(++i); - uid_found = true; - } - } - } - msg_error("Node::createChild()") << "Empty string given to property 'name': Forcefully setting an empty name is forbidden.\n" - "Renaming to " + newName + " to avoid unexpected behaviors."; - newchild = sofa::core::objectmodel::New(newName); - } - else - newchild = sofa::core::objectmodel::New(nodeName); - this->addChild(newchild); newchild->updateSimulationContext(); - return newchild; -} - - -void Node::moveChild(BaseNode::SPtr node) -{ - const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); - for (const auto& parent : dagnode->getParents()) { - Node::moveChild(node, parent); - } -} - - -/// Add a child node -void Node::doAddChild(BaseNode::SPtr node) -{ - const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); - setDirtyDescendancy(); - child.add(dagnode); - dagnode->l_parents.add(this); - dagnode->l_parents.updateLinks(); // to fix load-time unresolved links -} - -/// Remove a child -void Node::doRemoveChild(BaseNode::SPtr node) -{ - const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); - setDirtyDescendancy(); - child.remove(dagnode); - dagnode->l_parents.remove(this); -} - -/// Move a node from another node -void Node::doMoveChild(BaseNode::SPtr node, BaseNode::SPtr previous_parent) -{ - const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); - if (!dagnode) return; - - setDirtyDescendancy(); - previous_parent->removeChild(node); - - addChild(node); -} - -/// Remove a child -void Node::detachFromGraph() -{ - Node::SPtr me = this; // make sure we don't delete ourself before the end of this method - const LinkParents::Container& parents = l_parents.getValue(); - while(!parents.empty()) - parents.back()->removeChild(this); -} - -/// Generic object access, possibly searching up or down from the current context -/// -/// Note that the template wrapper method should generally be used to have the correct return type, -void* Node::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const sofa::core::objectmodel::TagSet& tags, SearchDirection dir) const -{ - if (dir == SearchRoot) - { - if (getNbParents()) return getRootContext()->getObject(class_info, tags, dir); - else dir = SearchDown; // we are the root, search down from here. - } - void *result = nullptr; - - if (dir != SearchParents) - for (ObjectIterator it = this->object.begin(); it != this->object.end(); ++it) - { - sofa::core::objectmodel::BaseObject* obj = it->get(); - if (tags.empty() || (obj)->getTags().includes(tags)) - { - - result = class_info.dynamicCast(obj); - if (result != nullptr) - { - - break; - } - } - } - - if (result == nullptr) - { - switch(dir) - { - case Local: - break; - case SearchParents: - case SearchUp: - { - const LinkParents::Container& parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; ++i){ - result = parents[i]->getObject(class_info, tags, SearchUp); - if (result != nullptr) break; - } - } - break; - case SearchDown: - for(ChildIterator it = child.begin(); it != child.end(); ++it) - { - result = (*it)->getObject(class_info, tags, dir); - if (result != nullptr) break; - } - break; - case SearchRoot: - dmsg_error("Node") << "SearchRoot SHOULD NOT BE POSSIBLE HERE."; - break; - } - } - - return result; -} - -/// Generic object access, given a path from the current context -/// -/// Note that the template wrapper method should generally be used to have the correct return type, -void* Node::getObject(const sofa::core::objectmodel::ClassInfo& class_info, const std::string& path) const -{ - if (path.empty()) - { - // local object - return Node::getObject(class_info, Local); - } - else if (path[0] == '/') - { - // absolute path; let's start from root - if (!getNbParents()) return getObject(class_info,std::string(path,1)); - else return getRootContext()->getObject(class_info,path); - } - else if (std::string(path,0,2)==std::string("./")) - { - std::string newpath = std::string(path, 2); - while (!newpath.empty() && path[0] == '/') - newpath.erase(0); - return getObject(class_info,newpath); - } - else if (std::string(path,0,3)==std::string("../")) - { - // tricky case: - // let's test EACH parent and return the first object found (if any) - std::string newpath = std::string(path, 3); - while (!newpath.empty() && path[0] == '/') - newpath.erase(0); - if (getNbParents()) - { - const LinkParents::Container& parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; ++i) - { - void* obj = parents[i]->getObject(class_info,newpath); - if (obj) return obj; - } - return nullptr; // not found in any parent node at all - } - else return getObject(class_info,newpath); - } - else - { - std::string::size_type pend = path.find('/'); - if (pend == std::string::npos) pend = path.length(); - const std::string name ( path, 0, pend ); - const Node* child = getChild(name); - if (child) - { - while (pend < path.length() && path[pend] == '/') - ++pend; - return child->getObject(class_info, std::string(path, pend)); - } - else if (pend < path.length()) - { - return nullptr; - } - else - { - sofa::core::objectmodel::BaseObject* obj = simulation::Node::getObject(name); - if (obj == nullptr) - { - return nullptr; - } - else - { - void* result = class_info.dynamicCast(obj); - if (result == nullptr) - { - dmsg_error("Node") << "Object "<getObjects( class_info, container, tags, dir ); - return; - } - else dir = SearchDown; // we are the root, search down from here. - } - - - switch( dir ) - { - case Local: - this->getLocalObjects( class_info, container, tags ); - break; - - case SearchUp: - this->getLocalObjects( class_info, container, tags ); // add locals then SearchParents - // no break here, we want to execute the SearchParents code. - [[fallthrough]]; - case SearchParents: - { - // a visitor executed from top but only run for this' parents will enforce the selected object unicity due even with diamond graph setups - GetUpObjectsVisitor vis( const_cast(this), class_info, container, tags); - getRootContext()->executeVisitor(&vis); - } - break; - - case SearchDown: - { - // a regular visitor is enforcing the selected object unicity - GetDownObjectsVisitor vis(class_info, container, tags); - (const_cast(this))->executeVisitor(&vis); - break; - } - default: - break; - } -} - -/// Get a list of parent node -sofa::core::objectmodel::BaseNode::Parents Node::getParents() const -{ - Parents p; - - const LinkParents::Container& parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; ++i) - p.push_back(parents[i]); - - return p; -} - - -/// returns number of parents -size_t Node::getNbParents() const -{ - return l_parents.getValue().size(); -} - -/// return the first parent (returns nullptr if no parent) -sofa::core::objectmodel::BaseNode* Node::getFirstParent() const -{ - const LinkParents::Container& parents = l_parents.getValue(); - if( parents.empty() ) return nullptr; - else return l_parents.getValue()[0]; -} - - -/// Test if the given node is a parent of this node. -bool Node::hasParent(const BaseNode* node) const -{ - const LinkParents::Container& parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; ++i) - { - if (parents[i]==node) return true; - } - return false; -} - -/// Test if the given context is a parent of this context. -bool Node::hasParent(const BaseContext* context) const -{ - if (context == nullptr) return !getNbParents(); - - const LinkParents::Container& parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; ++i) - if (context == parents[i]->getContext()) return true; - return false; - -} - - - -/// Test if the given context is an ancestor of this context. -/// An ancestor is a parent or (recursively) the parent of an ancestor. -bool Node::hasAncestor(const BaseContext* context) const -{ - const LinkParents::Container& parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; ++i) - if (context == parents[i]->getContext() - || parents[i]->hasAncestor(context)) - return true; - return false; -} - - -/// Mesh Topology that is relevant for this context -/// (within it or its parents until a mapping is reached that does not preserve topologies). -sofa::core::topology::BaseMeshTopology* Node::getMeshTopologyLink(SearchDirection dir) const -{ - if (this->meshTopology) - return this->meshTopology; - - if (dir != Local) - return Node::getMeshTopologyLink(dir); - - //local case similar to getActiveMeshTopology ... - - // Check if a local mapping stops the search - if (this->mechanicalMapping && !this->mechanicalMapping->sameTopology()) - { - return nullptr; - } - for ( Sequence::iterator i=this->mapping.begin(), iend=this->mapping.end(); i!=iend; ++i ) - { - if (!(*i)->sameTopology()) - { - return nullptr; - } - } - // No mapping with a different topology, continue on to the parents - const LinkParents::Container &parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; i++ ) - { - // if the visitor is run from a sub-graph containing a multinode linked with a node outside of the subgraph, do not consider the outside node by looking on the sub-graph descendancy - if ( parents[i] ) - { - sofa::core::topology::BaseMeshTopology* res = parents[i]->getMeshTopologyLink(Local); - if (res) - return res; - } - } - return nullptr; // not found in any parents -} - -void Node::precomputeTraversalOrder( const sofa::core::ExecParams* params ) -{ - // accumulating traversed Nodes - class TraversalOrderVisitor : public Visitor - { - NodeList& _orderList; - public: - TraversalOrderVisitor(const sofa::core::ExecParams* params, NodeList& orderList ) - : Visitor(params) - , _orderList( orderList ) - { - _orderList.clear(); - } - - Result processNodeTopDown(Node* node) override - { - _orderList.push_back( static_cast(node) ); - return RESULT_CONTINUE; - } - - const char* getClassName() const override {return "TraversalOrderVisitor";} - }; - - TraversalOrderVisitor tov( params, _precomputedTraversalOrder ); - executeVisitor( &tov, false ); -} - - - -/// Execute a recursive action starting from this node -void Node::doExecuteVisitor(simulation::Visitor* action, bool precomputedOrder) -{ - if( precomputedOrder && !_precomputedTraversalOrder.empty() ) - { - for( NodeList::iterator it = _precomputedTraversalOrder.begin(), itend = _precomputedTraversalOrder.end() ; it != itend ; ++it ) - { - if ( action->canAccessSleepingNode || !(*it)->getContext()->isSleeping() ) - action->processNodeTopDown( *it ); - } - - for( NodeList::reverse_iterator it = _precomputedTraversalOrder.rbegin(), itend = _precomputedTraversalOrder.rend() ; it != itend ; ++it ) - { - if ( action->canAccessSleepingNode || !(*it)->getContext()->isSleeping() ) - action->processNodeBottomUp( *it ); - } - } - else - { - // WARNING: do not store the traversal infos in the Node, as several visitors could traversed the graph simultaneously - // These infos are stored in a StatusMap per visitor. - updateDescendancy(); - - Visitor::TreeTraversalRepetition repeat; - if( action->treeTraversal(repeat) ) - { - // Tree traversal order - // - // Diamond shapes are ignored, a child node is visited as soon as a parent node has been visited. - // The multi-nodes (with several parents) are visited either: only once, only twice or for every times - // depending on the visitor's 'repeat' - // - // Some particular visitors such as a flat graph display or VisualVisitors must follow such a traversal order. - - StatusMap statusMap; - executeVisitorTreeTraversal( action, statusMap, repeat ); - } - else - { - // Direct acyclic graph traversal order - // - // This is the default order, used for mechanics. - // - // A child node is visited only when all its parents have been visited. - // A child node is 'pruned' only if all its parents are 'pruned'. - // Every executed node in the forward traversal are stored in 'executedNodes', - // its reverse order is used for the backward traversal. - - // Note that a newly 'pruned' node is still traversed (w/o execution) to be sure to execute its child nodes, - // that can have ancestors in another branch that is not pruned... - // An already pruned node is ignored. - - NodeList executedNodes; - { - StatusMap statusMap; - executeVisitorTopDown( action, executedNodes, statusMap, this ); - } - executeVisitorBottomUp( action, executedNodes ); - } - } -} - - -void Node::executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, Node* visitorRoot ) -{ - if ( statusMap[this] != NOT_VISITED ) - { - return; // skipped (already visited) - } - - if( !this->isActive() ) - { - // do not execute the visitor on this node - statusMap[this] = PRUNED; - - // in that case we can considerer if some child are activated, the graph is not valid, so no need to continue the recursion - return; - } - - if( this->isSleeping() && !action->canAccessSleepingNode ) - { - // do not execute the visitor on this node - statusMap[this] = PRUNED; - - return; - } - - // pour chaque noeud "prune" on continue à parcourir quand même juste pour marquer le noeud comme parcouru - - // check du "visitedStatus" des parents: - // un enfant n'est pruné que si tous ses parents le sont - // on ne passe à un enfant que si tous ses parents ont été visités - bool allParentsPruned = true; - bool hasParent = false; - - if( visitorRoot != this ) - { - // the graph structure is generally modified during an action anterior to the traversal but can possibly be modified during the current traversal - visitorRoot->updateDescendancy(); - - const LinkParents::Container &parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; i++ ) - { - // if the visitor is run from a sub-graph containing a multinode linked with a node outside of the subgraph, do not consider the outside node by looking on the sub-graph descendancy - if ( visitorRoot->_descendancy.contains(parents[i]) || parents[i]==visitorRoot ) - { - // all parents must have been visited before - if ( statusMap[parents[i]] == NOT_VISITED ) - return; // skipped for now... the other parent should come later - - allParentsPruned = allParentsPruned && ( statusMap[parents[i]] == PRUNED ); - hasParent = true; - } - } - } - - // all parents have been visited, let's go with the visitor - if ( allParentsPruned && hasParent ) - { - // do not execute the visitor on this node - statusMap[this] = PRUNED; - - // ... but continue the recursion anyway! - if( action->childOrderReversed(this) ) - for(unsigned int i = unsigned(child.size()); i>0;) - static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); - else - for(unsigned int i = 0; i(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); - } - else - { - // execute the visitor on this node - const Visitor::Result result = action->processNodeTopDown(this); - - // update status - statusMap[this] = ( result == simulation::Visitor::RESULT_PRUNE ? PRUNED : VISITED ); - - executedNodes.push_back(this); - - // ... and continue the recursion - if( action->childOrderReversed(this) ) - for(unsigned int i = unsigned(child.size()); i>0;) - static_cast(child[--i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); - else - for(unsigned int i = 0; i(child[i].get())->executeVisitorTopDown(action,executedNodes,statusMap,visitorRoot); - - } -} - - -// warning nodes that are dynamically created during the traversal, but that have not been traversed during the top-down, won't be traversed during the bottom-up -// TODO is it what we want? -// otherwise it is possible to restart from top, go to leaves and running bottom-up action while going up -void Node::executeVisitorBottomUp( simulation::Visitor* action, NodeList& executedNodes ) -{ - for( NodeList::reverse_iterator it = executedNodes.rbegin(), itend = executedNodes.rend() ; it != itend ; ++it ) - { - (*it)->updateDescendancy(); - action->processNodeBottomUp( *it ); - } -} - - -void Node::setDirtyDescendancy() -{ - _descendancy.clear(); - const LinkParents::Container &parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; i++ ) - { - parents[i]->setDirtyDescendancy(); - } -} - -void Node::updateDescendancy() -{ - if( _descendancy.empty() && !child.empty() ) - { - for(unsigned int i = 0; i(child[i].get()); - dagnode->updateDescendancy(); - _descendancy.insert( dagnode->_descendancy.begin(), dagnode->_descendancy.end() ); - _descendancy.insert( dagnode ); - } - } -} - - - -void Node::executeVisitorTreeTraversal( simulation::Visitor* action, StatusMap& statusMap, Visitor::TreeTraversalRepetition repeat, bool alreadyRepeated ) -{ - if( !this->isActive() ) - { - // do not execute the visitor on this node - statusMap[this] = PRUNED; - return; - } - - if( this->isSleeping() && !action->canAccessSleepingNode ) - { - // do not execute the visitor on this node - statusMap[this] = PRUNED; - return; - } - - // node already visited and repetition must be avoid - if( statusMap[this] != NOT_VISITED ) - { - if( repeat==Visitor::NO_REPETITION || ( alreadyRepeated && repeat==Visitor::REPEAT_ONCE ) ) return; - else alreadyRepeated = true; - } - - if( action->processNodeTopDown(this) != simulation::Visitor::RESULT_PRUNE ) - { - statusMap[this] = VISITED; - if( action->childOrderReversed(this) ) - for(unsigned int i = unsigned(child.size()); i>0;) - static_cast(child[--i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); - else - for(unsigned int i = 0; i(child[i].get())->executeVisitorTreeTraversal(action,statusMap,repeat,alreadyRepeated); - } - else - { - statusMap[this] = PRUNED; - } - - action->processNodeBottomUp(this); -} - - -void Node::initVisualContext() -{ - if (getNbParents()) - { - this->setDisplayWorldGravity(false); //only display gravity for the root: it will be propagated at each time step - } -} - -void Node::updateContext() -{ - sofa::core::objectmodel::BaseNode* firstParent = getFirstParent(); - - if ( firstParent ) - { - if( debug_ ) - { - msg_info()<<"Node::updateContext, node = "< NodeClass("Node"); - -} // namespace sofa::simulation::graph From a3a1734036fc859e64da876fd80d4745574f7ad0 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Fri, 27 Jun 2025 23:38:16 +0200 Subject: [PATCH 4/6] FIXUP forward declaration of BaseMechanicalstate --- Sofa/framework/Core/src/sofa/core/fwd.h | 1 + .../Simulation/Core/src/sofa/simulation/Visitor.h | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/fwd.h b/Sofa/framework/Core/src/sofa/core/fwd.h index 622a30e1394..33b99fdc89c 100644 --- a/Sofa/framework/Core/src/sofa/core/fwd.h +++ b/Sofa/framework/Core/src/sofa/core/fwd.h @@ -110,6 +110,7 @@ class BaseInteractionForceField; class BaseProjectiveConstraintSet; class BaseInteractionProjectiveConstraintSet; class BaseInteractionConstraint; +class BaseMechanicalState; class LinearSolver; class MultiMatrixAccessor; diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Visitor.h b/Sofa/framework/Simulation/Core/src/sofa/simulation/Visitor.h index f64eb8e801f..f24d6173b09 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Visitor.h +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Visitor.h @@ -190,11 +190,11 @@ class SOFA_SIMULATION_CORE_API Visitor static void printNode(const char* type); static void printCloseNode(const char* type); - static void printVector(core::behavior::BaseMechanicalState *mm, core::ConstVecId id); + static void printVector(sofa::core::behavior::BaseMechanicalState *mm, sofa::core::ConstVecId id); - virtual void printInfo(const core::objectmodel::BaseContext* context, bool dirDown); + virtual void printInfo(const sofa::core::objectmodel::BaseContext* context, bool dirDown); - void setNode(core::objectmodel::Base* c); + void setNode(sofa::core::objectmodel::Base* c); static void EnableExportStateVector(bool activation) {outputStateVector=activation;} static void SetFirstIndexStateVector(unsigned int first) {firstIndexStateVector=first;} @@ -213,7 +213,7 @@ class SOFA_SIMULATION_CORE_API Visitor static ctime_t initDumpTime; static std::vector< ctime_t > initNodeTime; - core::objectmodel::Base* enteringBase; + sofa::core::objectmodel::Base* enteringBase; bool infoPrinted; private: From d3f502bd8131e8f3286844de2debe1779001aa36 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 14 Jul 2025 08:36:18 +0200 Subject: [PATCH 5/6] FIX Node refactoring invalid merge. --- Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index b35300735fc..df9c4e7d209 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp @@ -1469,7 +1469,7 @@ sofa::core::topology::BaseMeshTopology* Node::getMeshTopologyLink(SearchDirectio return this->meshTopology; if (dir != Local) - return Node::getMeshTopologyLink(dir); + return NODEgetMeshTopologyLink(dir); //local case similar to getActiveMeshTopology ... From 94fc28d54f0a1322db9bd491b1d3139808444355 Mon Sep 17 00:00:00 2001 From: Damien Marchal Date: Mon, 14 Jul 2025 11:00:20 +0200 Subject: [PATCH 6/6] Copy-paste Context into BaseContext --- .../src/sofa/core/objectmodel/BaseContext.cpp | 90 +++++++++++-- .../src/sofa/core/objectmodel/BaseContext.h | 34 +++-- .../src/sofa/core/objectmodel/Context.cpp | 122 +----------------- .../Core/src/sofa/core/objectmodel/Context.h | 5 + .../Core/src/sofa/simulation/Node.cpp | 4 +- .../Core/src/sofa/simulation/Node.h | 4 +- 6 files changed, 111 insertions(+), 148 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.cpp index 0f35bda09e5..3d2e868e50a 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.cpp @@ -33,7 +33,19 @@ namespace sofa::core::objectmodel { BaseContext::BaseContext() -{} + : is_activated(initData(&is_activated, true, "activated", "To Activate a node")) + , worldGravity_(initData(&worldGravity_, Vec3(SReal(0),SReal(-9.81),SReal(0)),"gravity","Gravity in the world coordinate system")) + , dt_(initData(&dt_,SReal(0.01),"dt","Time step")) + , time_(initData(&time_,SReal(0.),"time","Current time")) + , animate_(initData(&animate_,false,"animate","Animate the Simulation(applied at initialization only)")) + , d_isSleeping(initData(&d_isSleeping, false, "sleeping", "The node is sleeping, and thus ignored by visitors.")) + , d_canChangeSleepingState(initData(&d_canChangeSleepingState, false, "canChangeSleepingState", "The node can change its sleeping state.")) +{ + animate_.setReadOnly(true); + dt_.setReadOnly(true); + time_.setReadOnly(true); +} + BaseContext::~BaseContext() {} @@ -44,53 +56,109 @@ BaseContext* BaseContext::getDefault() return &defaultContext; } - - //////////////// // Parameters // //////////////// +/// State of the context +void BaseContext::setActive(bool val) { is_activated.setValue(val); } + /// The Context is active -bool BaseContext::isActive() const { return true; } +bool BaseContext::isActive() const { return is_activated.getValue(); } + + +/// Sleeping state of the context +void BaseContext::setSleeping(bool val){ d_isSleeping.setValue(val); } /// The Context is not sleeping by default -bool BaseContext::isSleeping() const { return false; } +bool BaseContext::isSleeping() const { return d_isSleeping.getValue(); } /// The Context can not change its sleeping state by default -bool BaseContext::canChangeSleepingState() const { return false; } +bool BaseContext::canChangeSleepingState() const { return d_canChangeSleepingState.getValue(); } +/// Sleeping state change of the context +void BaseContext::setChangeSleepingState(bool val) +{ + d_canChangeSleepingState.setValue(val); +} + +/// Gravity vector +void BaseContext::setGravity(const Vec3& g) +{ + worldGravity_ .setValue(g); +} /// Gravity in the world coordinate system const BaseContext::Vec3& BaseContext::getGravity() const { - static const Vec3 G(SReal(0),SReal(-9.81), SReal(0)); - return G; + return worldGravity_.getValue(); +} + +/// Simulation timestep +void BaseContext::setDt(SReal dt) +{ + dt_.setValue(dt); } /// Simulation timestep SReal BaseContext::getDt() const { - return 0.01; + return dt_.getValue(); +} + +/// Simulation time +void BaseContext::setTime(SReal t) +{ + time_.setValue(t); } /// Simulation time SReal BaseContext::getTime() const { - return 0.0; + return time_.getValue(); +} + +/// Animation flag +void BaseContext::setAnimate(const bool val) +{ + animate_.setValue(val); } /// Animation flag bool BaseContext::getAnimate() const { - return true; + return animate_.getValue(); } +/// Display flags: Gravity +void BaseContext::setDisplayWorldGravity(bool val) +{ + worldGravity_.setDisplayed(val); +} BaseContext* BaseContext::getRootContext() const { return const_cast(this); } +//====================== +void BaseContext::copyContext(const BaseContext& c) +{ + // BUGFIX 12/01/06 (Jeremie A.): Can't use operator= on the class as it will copy other data in the BaseContext class (such as name)... + // *this = c; + copySimulationContext(c); +} + + +void BaseContext::copySimulationContext(const BaseContext& c) +{ + worldGravity_.setValue(c.getGravity()); ///< Gravity IN THE WORLD COORDINATE SYSTEM. + setDt(c.getDt()); + setTime(c.getTime()); + setAnimate(c.getAnimate()); +} + + //////////////// // Containers // //////////////// diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.h b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.h index e0ae2ddc670..7e2ca0d843a 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseContext.h @@ -77,7 +77,7 @@ class SOFA_CORE_API BaseContext : public virtual Base virtual bool isActive() const; /// State of the context - virtual void setActive(bool) {} + virtual void setActive(bool); /// Sleeping state of the context virtual bool isSleeping() const; @@ -95,12 +95,14 @@ class SOFA_CORE_API BaseContext : public virtual Base virtual bool getAnimate() const; /// @} - /// Gravity in local coordinates virtual const Vec3& getGravity() const; + /// Gravity in local coordinates - virtual void setGravity( const Vec3& ) - { } + virtual void setGravity( const Vec3& ); + + /// Display flags: Gravity + void setDisplayWorldGravity(bool val); /// Get the root context of the graph virtual BaseContext* getRootContext() const; @@ -318,20 +320,18 @@ class SOFA_CORE_API BaseContext : public virtual Base /// Simulation timestep - virtual void setDt( SReal /*dt*/ ) - { } + virtual void setDt( SReal /*dt*/ ); + + void setTime(SReal t); /// Animation flag - virtual void setAnimate(bool /*val*/) - { } + virtual void setAnimate(bool /*val*/); /// Sleeping state of the context - virtual void setSleeping(bool /*val*/) - { } + virtual void setSleeping(bool /*val*/); /// Sleeping state change of the context - virtual void setChangeSleepingState(bool /*val*/) - { } + virtual void setChangeSleepingState(bool /*val*/); /// @} /// @name Variables Setters @@ -389,6 +389,8 @@ class SOFA_CORE_API BaseContext : public virtual Base /// @} + void copyContext(const BaseContext& c); + void copySimulationContext(const BaseContext& c); /// @name Notifications for graph change listeners /// @{ @@ -403,6 +405,14 @@ class SOFA_CORE_API BaseContext : public virtual Base protected: ComponentNameHelper m_nameHelper; + + Data is_activated; ///< To Activate a node + Data worldGravity_; ///< Gravity in the world coordinate system + Data dt_; ///< Time step + Data time_; ///< Current time + Data animate_; ///< Animate the Simulation(applied at initialization only) + Data d_isSleeping; ///< The node is sleeping, and thus ignored by visitors. + Data d_canChangeSleepingState; ///< The node can change its sleeping state. }; template diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Context.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/Context.cpp index 1bc2b619271..98f015c0d55 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/Context.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Context.cpp @@ -21,128 +21,8 @@ ******************************************************************************/ #include -namespace sofa::core::objectmodel +namespace sofa::core::objectmodel::deprecated { -Context::Context() - : is_activated(initData(&is_activated, true, "activated", "To Activate a node")) - , worldGravity_(initData(&worldGravity_, Vec3(SReal(0),SReal(-9.81),SReal(0)),"gravity","Gravity in the world coordinate system")) - , dt_(initData(&dt_,SReal(0.01),"dt","Time step")) - , time_(initData(&time_,SReal(0.),"time","Current time")) - , animate_(initData(&animate_,false,"animate","Animate the Simulation(applied at initialization only)")) - , d_isSleeping(initData(&d_isSleeping, false, "sleeping", "The node is sleeping, and thus ignored by visitors.")) - , d_canChangeSleepingState(initData(&d_canChangeSleepingState, false, "canChangeSleepingState", "The node can change its sleeping state.")) -{ - animate_.setReadOnly(true); - dt_.setReadOnly(true); - time_.setReadOnly(true); -} - -/// The Context is active -bool Context::isActive() const {return is_activated.getValue();} - -/// State of the context -void Context::setActive(bool val) -{ - is_activated.setValue(val); -} - -/// The Context is sleeping -bool Context::isSleeping() const -{ - return d_isSleeping.getValue(); -} - -/// Sleeping state of the context -void Context::setSleeping(bool val) -{ - d_isSleeping.setValue(val); -} - -/// The Context can change its sleeping state -bool Context::canChangeSleepingState() const -{ - return d_canChangeSleepingState.getValue(); -} - -/// Sleeping state change of the context -void Context::setChangeSleepingState(bool val) -{ - d_canChangeSleepingState.setValue(val); -} - - - -/// Simulation timestep -SReal Context::getDt() const -{ - return dt_.getValue(); -} - -/// Simulation time -SReal Context::getTime() const -{ - return time_.getValue(); -} - -/// Gravity vector in world coordinates -const Context::Vec3& Context::getGravity() const -{ - return worldGravity_.getValue(); -} - -/// Animation flag -bool Context::getAnimate() const -{ - return animate_.getValue(); -} - -//=============================================================================== - -/// Simulation timestep -void Context::setDt(SReal dt) -{ - dt_.setValue(dt); -} - -/// Simulation time -void Context::setTime(SReal t) -{ - time_.setValue(t); -} - -/// Gravity vector -void Context::setGravity(const Vec3& g) -{ - worldGravity_ .setValue(g); -} - -/// Animation flag -void Context::setAnimate(const bool val) -{ - animate_.setValue(val); -} - -//====================== -void Context::copyContext(const Context& c) -{ - // BUGFIX 12/01/06 (Jeremie A.): Can't use operator= on the class as it will copy other data in the BaseContext class (such as name)... - // *this = c; - - copySimulationContext(c); - -} - - -void Context::copySimulationContext(const Context& c) -{ - worldGravity_.setValue(c.getGravity()); ///< Gravity IN THE WORLD COORDINATE SYSTEM. - setDt(c.getDt()); - setTime(c.getTime()); - setAnimate(c.getAnimate()); - - - -} } // namespace sofa::core::objectmodel diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Context.h b/Sofa/framework/Core/src/sofa/core/objectmodel/Context.h index 758af6fbd40..045afe98007 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/Context.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Context.h @@ -24,6 +24,11 @@ #include namespace sofa::core::objectmodel +{ + using Context = BaseContext; +} + +namespace sofa::core::objectmodel::deprecated { /** diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index df9c4e7d209..77c570721cd 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp @@ -1782,7 +1782,7 @@ void Node::updateContext() } // TODO // ahem.... not sure here... which parent should I copy my context from exactly ? - copyContext(*static_cast(static_cast(firstParent))); + copyContext(*(firstParent->getContext())); } updateSimulationContext(); @@ -1803,7 +1803,7 @@ void Node::updateSimulationContext() } // TODO // ahem.... not sure here... which parent should I copy my simulation context from exactly ? - copySimulationContext(*static_cast(static_cast(firstParent))); + copySimulationContext(*firstParent->getContext()); } for ( unsigned i=0; i; @author The SOFA team */ -class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, public sofa::core::objectmodel::Context +class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, public sofa::core::objectmodel::BaseContext { public: - SOFA_ABSTRACT_CLASS2(Node, BaseNode, Context); + SOFA_ABSTRACT_CLASS2(Node, BaseNode, BaseContext); typedef sofa::core::visual::DisplayFlags DisplayFlags; Node(const std::string& name="", Node* parent=nullptr);