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/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/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/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.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index ad554c90d04..77c570721cd 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) @@ -840,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 NODEgetMeshTopologyLink(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 = "< void getNodeObjects(Container* list) @@ -497,7 +467,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,12 +487,78 @@ 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; +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; @@ -532,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; @@ -559,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 /// @{ @@ -607,6 +639,83 @@ class SOFA_SIMULATION_CORE_API Node : public sofa::core::objectmodel::BaseNode, /// @} + + /// FROM DAG NODE + + typedef MultiLink LinkParents; + typedef LinkParents::const_iterator ParentIterator; + +private: + /// 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 + /// @{ + friend class GetDownObjectsVisitor ; + friend class GetUpObjectsVisitor ; + /// @} }; } 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: 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..4507024ebd5 100644 --- a/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp +++ b/Sofa/framework/Simulation/Graph/src/sofa/simulation/graph/DAGNode.cpp @@ -19,851 +19,13 @@ * * * 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 -class GetDownObjectsVisitor : public Visitor -{ -public: - - GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, DAGNode::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); - ~GetDownObjectsVisitor() override; - - 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; - DAGNode::GetObjectsCallBack& _container; - const sofa::core::objectmodel::TagSet& _tags; -}; - -GetDownObjectsVisitor::GetDownObjectsVisitor(const sofa::core::objectmodel::ClassInfo& class_info, - DAGNode::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(DAGNode* searchNode, const sofa::core::objectmodel::ClassInfo& class_info, DAGNode::GetObjectsCallBack& container, const sofa::core::objectmodel::TagSet& tags); - ~GetUpObjectsVisitor() override; - - Result processNodeTopDown(simulation::Node* node) override - { - const DAGNode* 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: - - DAGNode* _searchNode; - const sofa::core::objectmodel::ClassInfo& _class_info; - DAGNode::GetObjectsCallBack& _container; - const sofa::core::objectmodel::TagSet& _tags; - -}; - -GetUpObjectsVisitor::GetUpObjectsVisitor(DAGNode* searchNode, - const sofa::core::objectmodel::ClassInfo& class_info, - DAGNode::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(){} - -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) -{ - DAGNode::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 DAGNode::moveChild(BaseNode::SPtr node) -{ - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); - for (const auto& parent : dagnode->getParents()) { - Node::moveChild(node, parent); - } -} - - -/// Add a child node -void DAGNode::doAddChild(BaseNode::SPtr node) -{ - const DAGNode::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 DAGNode::doRemoveChild(BaseNode::SPtr node) -{ - const DAGNode::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) -{ - const DAGNode::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); - if (!dagnode) return; - - setDirtyDescendancy(); - previous_parent->removeChild(node); - - addChild(node); -} - -/// Remove a child -void DAGNode::detachFromGraph() -{ - DAGNode::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* DAGNode::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("DAGNode") << "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* DAGNode::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("DAGNode") << "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 DAGNode::getParents() const -{ - Parents p; +//helper::Creator NodeDefaultClass("default"); +static helper::Creator NodeClass("Node"); - 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 DAGNode::getNbParents() const -{ - return l_parents.getValue().size(); -} - -/// return the first parent (returns nullptr if no parent) -sofa::core::objectmodel::BaseNode* DAGNode::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 DAGNode::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 DAGNode::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 DAGNode::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* DAGNode::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 DAGNode::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 DAGNode::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 DAGNode, 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 DAGNode::executeVisitorTopDown(simulation::Visitor* action, NodeList& executedNodes, StatusMap& statusMap, DAGNode* 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 DAGNode::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 DAGNode::setDirtyDescendancy() -{ - _descendancy.clear(); - const LinkParents::Container &parents = l_parents.getValue(); - for ( unsigned int i = 0; i < parents.size() ; i++ ) - { - parents[i]->setDirtyDescendancy(); - } -} - -void DAGNode::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 DAGNode::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 DAGNode::initVisualContext() -{ - if (getNbParents()) - { - this->setDisplayWorldGravity(false); //only display gravity for the root: it will be propagated at each time step - } -} - -void DAGNode::updateContext() -{ - sofa::core::objectmodel::BaseNode* firstParent = getFirstParent(); - - if ( firstParent ) - { - if( debug_ ) - { - msg_info()<<"DAGNode::updateContext, node = "< DAGNodeClass("DAGNode"); - -} // 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 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/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