diff --git a/Sofa/Component/Collision/Geometry/src/sofa/component/collision/geometry/TriangleCollisionModel.h b/Sofa/Component/Collision/Geometry/src/sofa/component/collision/geometry/TriangleCollisionModel.h index 712c6db49f6..80a541cc26c 100644 --- a/Sofa/Component/Collision/Geometry/src/sofa/component/collision/geometry/TriangleCollisionModel.h +++ b/Sofa/Component/Collision/Geometry/src/sofa/component/collision/geometry/TriangleCollisionModel.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace sofa::component::collision::geometry { @@ -193,6 +194,37 @@ class TriangleCollisionModel : public core::CollisionModel const VecDeriv& getNormals() const { return m_normals; } int getTriangleFlags(sofa::core::topology::BaseMeshTopology::TriangleID i); + void saveInternalStateIn(sofa::core::objectmodel::Snapshot::SnapshotObject& snapshot) const override + { + sofa::core::objectmodel::Snapshot::DataInfo dataInfo; + + std::stringstream ss; + this->m_triangles->write(ss); + + dataInfo.name = "m_triangles"; + dataInfo.type = "vector"; + dataInfo.value = ss.str(); + + snapshot.m_dataContainer.push_back(dataInfo); + + } + + void loadInternalStateFrom(const core::objectmodel::Snapshot::SnapshotObject &snapshot) override + { + m_needsUpdate = true; + + for (const auto& dataInfo : snapshot.m_dataContainer) + { + if (dataInfo.name == "m_triangles") + { + m_triangles = &m_topology->getTriangles(); + resize(m_topology->getNbTriangles()); + updateNormals(); + } + + } + } + Deriv velocity(sofa::Index index)const; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.h index 8f9b3992c4b..e3171614c6b 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.h @@ -290,6 +290,12 @@ class TetrahedronFEMForceField : public BaseLinearElasticityFEMForceFieldl_topology->getTetrahedra()); + } + protected: void computeStrainDisplacement( StrainDisplacement &J, Coord a, Coord b, Coord c, Coord d ); Real peudo_determinant_for_coef ( const type::Mat<2, 3, Real>& M ); diff --git a/Sofa/framework/Core/CMakeLists.txt b/Sofa/framework/Core/CMakeLists.txt index 5d049ec19bf..439e7724da2 100644 --- a/Sofa/framework/Core/CMakeLists.txt +++ b/Sofa/framework/Core/CMakeLists.txt @@ -160,6 +160,7 @@ set(HEADER_FILES ${SRC_ROOT}/objectmodel/BaseLink.h ${SRC_ROOT}/objectmodel/BaseNode.h ${SRC_ROOT}/objectmodel/BaseObjectDescription.h + ${SRC_ROOT}/objectmodel/Snapshot.h ${SRC_ROOT}/objectmodel/ClassInfo.h ${SRC_ROOT}/objectmodel/ComponentState.h ${SRC_ROOT}/objectmodel/ConfigurationSetting.h @@ -178,6 +179,7 @@ set(HEADER_FILES ${SRC_ROOT}/objectmodel/HapticDeviceEvent.h ${SRC_ROOT}/objectmodel/IdleEvent.h ${SRC_ROOT}/objectmodel/JoystickEvent.h + ${SRC_ROOT}/objectmodel/SnapshotJSONExporter.h ${SRC_ROOT}/objectmodel/KeypressedEvent.h ${SRC_ROOT}/objectmodel/KeyreleasedEvent.h ${SRC_ROOT}/objectmodel/lifecycle/DeprecatedData.h @@ -310,6 +312,7 @@ set(SOURCE_FILES ${SRC_ROOT}/objectmodel/BaseLink.cpp ${SRC_ROOT}/objectmodel/BaseNode.cpp ${SRC_ROOT}/objectmodel/BaseObjectDescription.cpp + ${SRC_ROOT}/objectmodel/Snapshot.cpp ${SRC_ROOT}/objectmodel/ClassInfo.cpp ${SRC_ROOT}/objectmodel/ComponentState.cpp ${SRC_ROOT}/objectmodel/ConfigurationSetting.cpp @@ -328,6 +331,7 @@ set(SOURCE_FILES ${SRC_ROOT}/objectmodel/HapticDeviceEvent.cpp ${SRC_ROOT}/objectmodel/IdleEvent.cpp ${SRC_ROOT}/objectmodel/JoystickEvent.cpp + ${SRC_ROOT}/objectmodel/SnapshotJSONExporter.cpp ${SRC_ROOT}/objectmodel/KeypressedEvent.cpp ${SRC_ROOT}/objectmodel/KeyreleasedEvent.cpp ${SRC_ROOT}/objectmodel/lifecycle/DeprecatedData.cpp diff --git a/Sofa/framework/Core/simutest/CMakeLists.txt b/Sofa/framework/Core/simutest/CMakeLists.txt index 5ec8342f6b6..6095de998c2 100644 --- a/Sofa/framework/Core/simutest/CMakeLists.txt +++ b/Sofa/framework/Core/simutest/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCE_FILES objectmodel/BaseContext_test.cpp objectmodel/BaseLink_simutest.cpp objectmodel/PathResolver_simutest.cpp + objectmodel/Snapshot_test.cpp ) add_executable(${PROJECT_NAME} ${SOURCE_FILES}) diff --git a/Sofa/framework/Core/simutest/objectmodel/Base_test.cpp b/Sofa/framework/Core/simutest/objectmodel/Base_test.cpp index e6a54bce469..4802c593704 100644 --- a/Sofa/framework/Core/simutest/objectmodel/Base_test.cpp +++ b/Sofa/framework/Core/simutest/objectmodel/Base_test.cpp @@ -36,6 +36,9 @@ using sofa::defaulttype::Rigid3Types; #include using sofa::defaulttype::Vec3Types; +#include +using sofa::simulation::SaveSnapshotVisitor; + namespace customns { class CustomBaseObject : public sofa::core::objectmodel::BaseComponent @@ -121,4 +124,3 @@ TEST_F(Base_test , testGetClassName) EXPECT_EQ(o.getTypeName(), "CustomBaseObject"); EXPECT_EQ(o.getClass()->className, "CustomBaseObject"); } - diff --git a/Sofa/framework/Core/simutest/objectmodel/Snapshot_test.cpp b/Sofa/framework/Core/simutest/objectmodel/Snapshot_test.cpp new file mode 100644 index 00000000000..f5af64820dc --- /dev/null +++ b/Sofa/framework/Core/simutest/objectmodel/Snapshot_test.cpp @@ -0,0 +1,393 @@ +/****************************************************************************** +* 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 "gtest/gtest.h" +using sofa::core::objectmodel::Base ; +using sofa::core::objectmodel::ComponentState; + +#include +using sofa::testing::BaseSimulationTest ; +using sofa::simulation::Node ; + +#include +using sofa::core::objectmodel::BaseComponent; + +#include +using sofa::core::objectmodel::Snapshot; + +#include +using sofa::simulation::SaveSnapshotVisitor; + +#include + +#include +using sofa::core::objectmodel::Data; +using sofa::core::objectmodel::BaseLink; +using sofa::core::objectmodel::SingleLink; +using sofa::core::objectmodel::MultiLink; +using sofa::core::objectmodel::Snapshot; +using sofa::core::objectmodel::BaseNode; + +#include +#include + +class TestComponent : public BaseComponent +{ +public: + SOFA_CLASS(TestComponent, BaseComponent); + + Data d_value; + sofa::MultiLink l_target; + + TestComponent() + : d_value(initData(&d_value, 3.14f, "pi", "test value")) + , l_target(initLink("target","target test")) + { + this->setName("TestComponent"); + } + + void saveData(Snapshot::SnapshotObject& snapshot) + { + this->saveDataIn(snapshot); + } + + void saveLinks(Snapshot::SnapshotObject& snapshot) + { + this->saveLinksIn(snapshot); + } + + std::shared_ptr createSnapshotObjectTest(std::vector>& parents) const + { + + return this->createSnapshotObject(parents); + } + +}; + +class Snapshot_test: public BaseSimulationTest +{ +public: + Snapshot_test() {} + ~Snapshot_test() override {} +}; + +/** + * @brief Test of saveDataIn + * + * This test verifies that saveDataIn save data correctly in a SnapshotObject. + * + * Test steps: + * 1. Create a component (Component) and a snapshot + * 2. Save component's data in the snapshot + * 3. Check if the snapshot contains the component with expected data + * + */ +TEST_F(Snapshot_test, saveDataIn) +{ + TestComponent Component; + auto snapshot = std::make_shared(); + Component.saveData(*snapshot); + for (auto& data : snapshot->m_dataContainer) + { + if (data.name == "name") + { + EXPECT_EQ(data.value, "TestComponent"); + } + if(data.name == "pi") + { + EXPECT_EQ(data.value, "3.14"); + } + } +} + +/** + * @brief Test of saveLinksIn + * + * This test verifies that saveLinksIn save links correctly in a SnapshotObject. + * + * Test steps: + * 1. Create a component (Component) and a snapshot + * 2. Save component's links in the snapshot + * 3. Check if the snapshot contains the component with expected links + * + */ +TEST_F(Snapshot_test, saveLinkIn) +{ + + TestComponent tComponent; + auto snapshot = std::make_shared(); + + tComponent.saveLinks(*snapshot); + for (auto& link : snapshot->m_linkContainer) + { + if (link.name == "name") + { + EXPECT_EQ(link.value, "@./"); + } + if (link.name == "slaves") + { + EXPECT_EQ(link.value, ""); + } + if (link.name == "master") + { + EXPECT_EQ(link.value, ""); + } + } +} + +/** + * @brief Test of createSnapshotObject + * + * This test verifies that createSnapshotObject can find the SnapshotObject in a Snapshot with the component's name. + * + * Test steps: + * 1. Create a component (Component) + * 2. Create a SnapshotObject with the function createSnapshotObject + * 3. Save Component's data in the SnapshotObject + * 4. Verify if every data is correctly saved in the SnapshotObject + * + */ +TEST_F(Snapshot_test, createSnapshotObject) +{ + TestComponent Component; + + std::vector> snapshotParents; + auto snapshotObject = Component.createSnapshotObjectTest(snapshotParents); + + snapshotObject->m_name = "snapshotObject"; + Component.saveData(*snapshotObject); + + EXPECT_NE(snapshotObject, nullptr); + EXPECT_EQ(snapshotObject->m_name, "snapshotObject"); + for (auto& data : snapshotObject->m_dataContainer) + { + if(data.name == "pi") + { + EXPECT_EQ(data.value, "3.14"); + } + } +} + +/** + * @brief Test of findSnapshotObject + * + * This test verifies that findSnapshotObject can find the SnapshotObject in a Snapshot with the component's name. + * + * Test steps: + * 1. Create a component (Component) and a graph of with a SnapshotNode + * 2. Save Component in the SnapshotNode (as a SnapshotObject) + * 3. Use findSnapshotObject to find the SnapshotObject corresponding to Component + * 4. Verify if the SnapshotObject has been correctly found + * + */ +TEST_F(Snapshot_test, findSnapshotObject) +{ + TestComponent Component; + auto snapshotNode = std::make_shared("root"); + std::vector> snapshotParents; + snapshotParents.push_back(snapshotNode); + + auto snapshot = Component.saveSnapshot(snapshotParents); + snapshotNode->components.push_back(*snapshot); + + auto expectedObject = Component.findSnapshotObject(snapshotNode, "TestComponent"); + + EXPECT_NE(expectedObject, nullptr); + EXPECT_EQ(Component.getName(), expectedObject->m_name); +} + +/** + * @brief Test of saveSnapshot + * + * This test verifies that saveSnapshot save the data to a previously saved snapshot. + * + * Test steps: + * 1. Create a component (Component) and a snapshot + * 2. Save Component1's data in the snapshot + * 3. Verify if the snapshot contains all the data from Component1 + * + */ +TEST_F(Snapshot_test, saveSnapshot) +{ + TestComponent Component; + auto snapshot = std::make_shared(); + std::vector> snapshotParents; + + snapshot = Component.saveSnapshot(snapshotParents); + + EXPECT_EQ(snapshot->m_name, "TestComponent"); + EXPECT_EQ(snapshot->m_dataContainer.size(), 7); + EXPECT_EQ(snapshot->m_dataContainer[0].name, "name"); + EXPECT_EQ(snapshot->m_dataContainer[0].value, "TestComponent"); + EXPECT_EQ(snapshot->m_dataContainer.back().name, "pi"); + EXPECT_EQ(snapshot->m_dataContainer.back().value, "3.14"); + EXPECT_EQ(snapshot->m_linkContainer[0].name, "context"); + EXPECT_EQ(snapshot->m_linkContainer[0].value, "@./"); + EXPECT_EQ(snapshot->m_linkContainer.back().name, "target"); + EXPECT_EQ(snapshot->m_linkContainer.back().value, ""); + +} + +/** + * @brief Test of loadDataSnapshot + * + * This test verifies that loadLinkSnapshot restores the state of data to a previously saved snapshot + * + * Test steps: + * 1. Create a component (Component1) and a snapshot + * 2. Save Component1's data in the snapshot + * 3. Create another component (Component2) and change the data d_value + * 4. Load Component1's data into Component2 with loadDataSnapshot + * 5. Verify if Component2 has same value as Component1 + * + */ +TEST_F(Snapshot_test, loadDataSnapshot) +{ + TestComponent Component1; + auto snapshot = std::make_shared(); + std::vector> snapshotParents; + + snapshot = Component1.saveSnapshot(snapshotParents); + + TestComponent Component2; + Component2.d_value.setValue(0.0f); + Component2.loadDataSnapshot(snapshot); + + EXPECT_EQ(Component2.d_value.getValue(), 3.14f); +} + +/** + * @brief Test of loadLinkSnapshot + * + * This test verifies that loadLinkSnapshot restores the state of a link to a previously saved snapshot. + * First, it set up a graph "root" with 3 components: Component1, Component2 and Component3 + * Each component holds a multi-link l_target pointing to other components + * + * Test steps: + * 1. Add Component2 to Component1's l_target link. + * 2. Save a snapshot of Component1 (l_target points to @Component2 only). + * 3. Add Component3 to Component1's l_target link. + * 4. Verify that l_target now points to both @Component2 and@Component3. + * 5. Restore Component1 from the snapshot. + * 6. Verify that l_target is back to pointing to @Component2 only, + * confirming that it @Component3 was correctly removed by loadLinkSnapshot. + * + */ +TEST_F(Snapshot_test, loadLinkSnapshot) +{ + const SceneInstance scene("root"); + auto Component1 = sofa::core::objectmodel::New(); + Component1->setName("Component1"); + auto Component2 = sofa::core::objectmodel::New(); + Component2->setName("Component2"); + auto Component3 = sofa::core::objectmodel::New(); + Component3->setName("Component3"); + scene.root->addObject(Component1); + scene.root->addObject(Component2); + scene.root->addObject(Component3); + + auto snapshotNode = std::make_shared("root"); + std::vector> snapshotParents; + snapshotParents.push_back(snapshotNode); + + TestComponent* ptr = Component2.get(); + Component1->l_target.add(ptr); + + EXPECT_EQ(Component1->l_target.getValueString(), "@Component2"); + + auto snapshotObject1 = std::make_shared(); + snapshotObject1 = Component1->saveSnapshot(snapshotParents); + + ptr = Component3.get(); + Component1->l_target.add(ptr); + + EXPECT_EQ(Component1->l_target.getValueString(), "@Component2 @Component3"); + + Component1->loadLinkSnapshot(snapshotObject1); + + EXPECT_EQ(Component1->l_target.getValueString(), "@Component2"); +} + +/** + * @brief Test of SnapshotJSONExporter + * + * This test verifies the behavior of the export and the import with SnapshotJSONExporter + * + * Test steps: + * 1. Init the scene + * 2. Create a snapshot (snapshot) + * 3. Run SaveSnapshotVisitor to save the state in m_snapshot + * 4. Export m_snapshot to a JSON file and check if the file is valid + * 5. Create another snapshot (snapshot_import) + * 6. Import the JSON file in snapshot_import + * 7. Compare m_snapshot and snapshot_import + * + */ +TEST_F(Snapshot_test, SnapshotJSONExporter) +{ + const std::string scene = R"( + + + + + + + + + + )"; + + SceneInstance c("xml", scene) ; + c.initScene() ; + + Node* root = c.root.get() ; + + std::filesystem::path path = std::filesystem::temp_directory_path() / "test_file.json"; + auto snapshot = std::make_shared(); + + auto visitor = SaveSnapshotVisitor(nullptr, *snapshot); + root->execute(visitor); + + exportToJSON(*snapshot,path); + + std::ifstream checkFile(path); + EXPECT_TRUE(checkFile.good()); + checkFile.close(); + + auto snapshot_import = std::make_shared(); + importFrom(*snapshot_import, path); + + EXPECT_NE(snapshot_import->m_graphRoot,nullptr); + + EXPECT_EQ(snapshot_import->m_graphRoot->m_name,"Root"); + EXPECT_EQ(snapshot_import->m_graphRoot->components[1].m_name,"DefaultAnimationLoop1"); + EXPECT_EQ(snapshot_import->m_graphRoot->components[2].m_name,"DefaultVisualManagerLoop1"); + EXPECT_EQ(snapshot_import->m_graphRoot->children[0]->m_name,"child1"); + EXPECT_EQ(snapshot_import->m_graphRoot->children[0]->components[0].m_name,"MechanicalObject1"); + + std::filesystem::remove(path); +} + + diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Base.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/Base.cpp index ed3a4d942de..80ac272ddd7 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/Base.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Base.cpp @@ -20,8 +20,8 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #define SOFA_CORE_OBJECTMODEL_BASE_CPP -#include +#include #include #include #include @@ -37,11 +37,15 @@ using sofa::helper::logging::Message ; #include using sofa::helper::getClosestMatch; +#include + #include #include #include #include + + #define ERROR_LOG_SIZE 100 namespace sofa::core::objectmodel @@ -680,7 +684,113 @@ int Base::getInstanciationSourceFilePos() const return m_instanciationSourceFilePos; } -} // namespace sofa::core::objectmodel +void Base::saveDataIn(Snapshot::SnapshotObject& snapshot) const +{ + for (const auto& dataFields = this->getDataFields(); const auto& data : dataFields) + { + Snapshot::DataInfo dataInfo; + dataInfo.name = data->getName(); + dataInfo.type = data->getValueTypeString(); + dataInfo.value = data->getValueString(); + + snapshot.m_dataContainer.push_back(dataInfo); + } +} + +void Base::saveLinksIn(Snapshot::SnapshotObject& snapshot) const +{ + for (const auto& links = this->getLinks(); const auto& link : links) + { + Snapshot::LinkInfo linkInfo; + linkInfo.name = link->getName(); + linkInfo.type = link->getValueTypeString(); + linkInfo.value = link->getValueString(); + + std::string search = "//"; + sofa::helper::replaceAll(linkInfo.value, search,""); + snapshot.m_linkContainer.push_back(linkInfo); + } +} + +void Base::saveInternalStateIn(Snapshot::SnapshotObject& snapshot) const +{ + SOFA_UNUSED(snapshot); +} + +std::shared_ptr +Base::createSnapshotObject(std::vector>& parents) const +{ + auto object = std::make_shared(); + for (const auto& p : parents) + { + if (p) + { + p->components.push_back(*object); + } + } + return object; +} + +std::shared_ptr Base::saveSnapshot(std::vector>& parents) const +{ + const auto snapshotObject = createSnapshotObject(parents); + snapshotObject->m_name = this->getName(); + saveDataIn(*snapshotObject); + saveLinksIn(*snapshotObject); + saveInternalStateIn(*snapshotObject); + return snapshotObject; +} + + +std::shared_ptr +Base::findSnapshotObject(const std::shared_ptr& parents, const std::string& objectname) +{ + for (const auto& p : parents->components) + { + if (p.m_name == objectname) + { + auto snapshotObject = std::make_shared(p); + return snapshotObject; + } + } + msg_error() << "SnapshotObject "<< objectname << " not found"; + auto defaultObject = std::make_shared(); + defaultObject->m_name = "Unknown object"; + + return defaultObject; +} + +void Base::loadInternalStateFrom(const Snapshot::SnapshotObject& snapshot) +{ + SOFA_UNUSED(snapshot); + +} + +void Base::loadDataSnapshot(const std::shared_ptr& snapshotObject) const +{ + for (const auto& dataInfo : snapshotObject->m_dataContainer) + { + if (const auto data = this->findData(dataInfo.name)) + { + if(data->read(dataInfo.value) == 0 ) + msg_error() << "Failed to read " << dataInfo.name << " in " << this->getName() << " from the snapshot " << dataInfo.value; + } + } +} + +void Base::loadLinkSnapshot(const std::shared_ptr& snapshotObject) const +{ + for (const auto& linkInfo : snapshotObject->m_linkContainer) { + if (const auto link = this->findLink(linkInfo.name)) { + + if (link->readFromSnapshot(linkInfo.value) == 0 ) + msg_error() << "Failed to read " << linkInfo.name << " in " << this->getName() << " from the snapshot " << linkInfo.value; + } + } +} + + +}// namespace sofa::core::objectmodel namespace sofa::helper::logging diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Base.h b/Sofa/framework/Core/src/sofa/core/objectmodel/Base.h index ce62107d239..aa3a1d52a1a 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/Base.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Base.h @@ -40,6 +40,10 @@ #include #include +#include + +#include + #define SOFA_BASE_CAST_IMPLEMENTATION(CLASSNAME) \ virtual const CLASSNAME* to##CLASSNAME() const override { return this; } \ virtual CLASSNAME* to##CLASSNAME() override { return this; } @@ -47,6 +51,8 @@ virtual CLASSNAME* to##CLASSNAME() override { return this; } namespace sofa::core::objectmodel { + + /** * \brief Base class for everything * @@ -356,7 +362,35 @@ class SOFA_CORE_API Base : public IntrusiveObject ///@} + /// Save a SnapshotObject full of data and link into the snapshot + std::shared_ptr saveSnapshot(std::vector>& parents) const; + + /// Load data from the snapshot to the scene + void loadDataSnapshot(const std::shared_ptr& snapshotObject) const; + + /// Load link from the snapshot to the scene + void loadLinkSnapshot(const std::shared_ptr& snapshotObject) const; + +protected: + + /// Save data from an object to the SnapshotObject + void saveDataIn(Snapshot::SnapshotObject& snapshot) const; + + /// Save link from an object to the SnapshotObject + void saveLinksIn(Snapshot::SnapshotObject& snapshot) const; + + /// Create a SnapshotObject that will contain data and link + virtual std::shared_ptr createSnapshotObject(std::vector>& parents) const; +public : + + /// Find a SnapshotObject corresponding to the object from the scene + virtual std::shared_ptr findSnapshotObject(const std::shared_ptr& parents, const std::string& objectname); + + /// Save Internal State to the SnapshotObject + virtual void saveInternalStateIn(Snapshot::SnapshotObject& snapshot) const; + ///Load Internal State from a snapshot to the scene + virtual void loadInternalStateFrom(const Snapshot::SnapshotObject& snapshot) ; protected: /// List of fields (Data instances) VecData m_vecData; diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp index 656955cdd71..52b37938401 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp @@ -224,8 +224,8 @@ void BaseComponent::addSlave(BaseComponent::SPtr s) const BaseComponent::SPtr previous = s->getMaster(); if (previous == this) return; if (previous) - previous->l_slaves.remove(s); - l_slaves.add(s); + previous->l_slaves.remove(s.get()); + l_slaves.add(s.get()); if (previous) this->getContext()->notifyMoveSlave(previous.get(), this, s.get()); else @@ -234,7 +234,7 @@ void BaseComponent::addSlave(BaseComponent::SPtr s) void BaseComponent::removeSlave(BaseComponent::SPtr s) { - if (l_slaves.remove(s)) + if (l_slaves.remove(s.get())) { this->getContext()->notifyRemoveSlave(this, s.get()); } diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseData.h b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseData.h index 0e3a4c7d894..343b7124b40 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseData.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseData.h @@ -317,6 +317,8 @@ class SOFA_CORE_API BaseData : public DDGNode virtual void* doBeginEditVoidPtr() = 0; virtual void doEndEditVoidPtr() = 0; virtual void doOnUpdate() {} + + }; /** A WriteAccessWithRawPtr is a RAII class, holding a reference to a given container diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.cpp index c056c2ef233..09304bfe439 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.cpp @@ -323,6 +323,83 @@ bool BaseLink::read( const std::string& str ) return ok; } +bool BaseLink::readFromSnapshot( const std::string& str ) +{ + this->updateLinks(); + bool result = true; + + if (str.empty()) + return true; + + const auto owner = getOwner(); + if (owner == nullptr) + return false; + + /// From the snapshot + std::vector linkPathsFromSnapshot; + std::istringstream istr(str); + std::string linkString; + + while (istr >> linkString) + { + if (!linkString.empty() && linkString[0] == '@') + linkPathsFromSnapshot.push_back(linkString); + } + + /// From the link + std::vector linkPathsFromLink; + + std::string linkPath = this->getValueString(); + + std::string search = "//"; + sofa::helper::replaceAll(linkPath, search,""); + + std::istringstream istrLink(linkPath); + std::string linkStringBis; + + while (istrLink >> linkStringBis) + { + if (!linkStringBis.empty() && linkStringBis[0] == '@') + linkPathsFromLink.push_back(linkStringBis); + } + + std::set mixedLink; + mixedLink.insert(linkPathsFromSnapshot.begin(), linkPathsFromSnapshot.end()); + mixedLink.insert(linkPathsFromLink.begin(), linkPathsFromLink.end()); + + for (const auto& linkPath : mixedLink) + { + bool inLinkPathsFromSnapshot = std::find(linkPathsFromSnapshot.begin(), linkPathsFromSnapshot.end(), linkPath) != linkPathsFromSnapshot.end(); + bool inLinkPathsFromLink = std::find(linkPathsFromLink.begin(), linkPathsFromLink.end(), linkPath) != linkPathsFromLink.end(); + + if (inLinkPathsFromLink && inLinkPathsFromSnapshot) + { + result = true; + } + else if (inLinkPathsFromLink) + { + Base *ptr = PathResolver::FindBaseFromClassAndPath(owner, getDestClass(), linkPath); + if (ptr == nullptr) + { + msg_warning(owner) << "readFromSnapshot: could not resolve link path " << linkPath << "\" for link " << getName() << ". Skipping."; + result = false; + } + this->remove(ptr); + } + else if (inLinkPathsFromSnapshot) + { + Base *ptr = PathResolver::FindBaseFromClassAndPath(owner, getDestClass(), linkPath); + if (ptr == nullptr) + { + msg_warning(owner) << "readFromSnapshot: could not resolve link path " << linkPath << "\" for link " << getName() << ". Skipping."; + result = false; + } + this->add(ptr); + } + } + return result; +} + std::string BaseLink::getLinkedPath(const std::size_t index) const { if(index >= getSize()) diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.h b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.h index d5c71fc6d57..cbedb7f3e2b 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseLink.h @@ -134,6 +134,9 @@ class SOFA_CORE_API BaseLink /// Read the command line bool read( const std::string& str ); + /// Read the command line from a snapshot + bool readFromSnapshot( const std::string& str ); + /// Update pointers in case the pointed-to objects have appeared /// @return false if there are broken links bool updateLinks(); @@ -171,18 +174,23 @@ class SOFA_CORE_API BaseLink /// Add a new target to the link. bool add(Base* baseptr, const std::string& path) { return _doAdd_(baseptr, path); } + bool add(Base* baseptr) { return _doAdd_(baseptr); } /// Change the link's target at the provided index. bool set(Base* baseptr, size_t index=0) { return _doSet_(baseptr, index); } + bool remove(Base* baseptr) {return _doRemove_(baseptr); } + protected: virtual bool _doSet_(Base* target, const size_t index=0) = 0; virtual Base* _doGetOwner_() const = 0 ; virtual void _doSetOwner_(Base* owner) = 0; virtual Base* _doGet_(const size_t=0) const = 0; virtual bool _doAdd_(Base* target, const std::string&) = 0; + virtual bool _doAdd_(Base*) = 0; virtual void _doClear_() = 0; virtual std::string _doGetLinkedPath_(const size_t=0) const = 0; + virtual bool _doRemove_(Base* target) = 0; unsigned int m_flags; std::string m_name; diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.cpp index 5b6b0af4954..2c4d082967d 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.cpp @@ -25,7 +25,7 @@ #include #include #include - +#include namespace sofa::core::objectmodel { @@ -63,6 +63,41 @@ core::visual::VisualLoop* BaseNode::getVisualLoop() const return this->getContext()->get(); } +std::shared_ptr +BaseNode::createSnapshotObject(std::vector>& parents) const +{ + auto nodeObject = std::make_shared(); + for (auto p : parents) + { + if (p) + { + p->children.push_back(nodeObject); + } + } + + return nodeObject; +} + +std::shared_ptr +BaseNode::findSnapshotObject( const std::shared_ptr& parents, const std::string& objectname) +{ + if (!parents) return nullptr; + + if(parents->m_name == objectname) + { + return parents; + } + + for (const auto& child : parents->children) + { + if (auto result = this->findSnapshotObject(child, objectname)) + return result; + + } + + return nullptr; +} + /// Set the context of an object to this void BaseNode::setObjectContext(BaseComponent::SPtr obj) { diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.h b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.h index 2913373a309..e9f5c4a416b 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseNode.h @@ -24,10 +24,11 @@ #include #include #include - +#include namespace sofa::core::objectmodel { + /** * \brief Base class for simulation nodes. * @@ -128,6 +129,15 @@ class SOFA_CORE_API BaseNode : public virtual Base virtual core::collision::Pipeline* getCollisionPipeline() const; virtual core::visual::VisualLoop* getVisualLoop() const; +protected: + + /// Create a SnapshotObject that will contain data and link + std::shared_ptr createSnapshotObject(std::vector>& parents) const override; +public: + + /// Find a SnapshotObject corresponding to the object from the scene + std::shared_ptr findSnapshotObject(const std::shared_ptr& parents, const std::string& objectname) override; + private: virtual std::string internalGetPathName() const; diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Data.h b/Sofa/framework/Core/src/sofa/core/objectmodel/Data.h index 797c76f4cea..0103ffaa5e1 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/Data.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Data.h @@ -236,6 +236,8 @@ class Data : public BaseData const void* doGetValueVoidPtr() const override { return &getValue(); } void* doBeginEditVoidPtr() override { return beginEdit(); } void doEndEditVoidPtr() override { endEdit(); } + + }; class EmptyData : public Data {}; diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Link.h b/Sofa/framework/Core/src/sofa/core/objectmodel/Link.h index b1519d4bc89..bc8061ac8d6 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/Link.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Link.h @@ -377,27 +377,6 @@ class TLink : public BaseLink return true; } - bool add(DestPtr v) - { - if (!v) - return false; - const std::size_t index = TraitsContainer::add(m_value,v); - updateCounter(); - added(v, index); - return true; - } - - bool add(DestPtr v, const std::string& path) - { - if (!v && path.empty()) - return false; - std::size_t index = TraitsContainer::add(m_value,v); - TraitsValueType::setPath(m_value[index],path); - updateCounter(); - added(v, index); - return true; - } - bool addPath(const std::string& path) { if (path.empty()) @@ -408,11 +387,11 @@ class TLink : public BaseLink return add(ptr, path); } - bool remove(DestPtr v) + bool _doRemove_(Base* target) override { - if (!v) + if (!target) return false; - return removeAt(TraitsContainer::find(m_value,v)); + return removeAt(TraitsContainer::find(m_value,castTo(target))); } bool removeAt(std::size_t index) @@ -503,7 +482,32 @@ class TLink : public BaseLink } /// TLink:adding accepts nullptr (for a not yet resolved link). - return TLink::add(destptr, path); + std::size_t index = TraitsContainer::add(m_value, destptr); + TraitsValueType::setPath(m_value[index], path); + updateCounter(); + added(destptr, index); + return true; + } + + bool _doAdd_(Base* baseptr) override + { + /// If the pointer is null and the path empty we do nothing + if(!baseptr) + return false; + + /// Downcast the pointer to a compatible type and + /// If the types are not compatible with the Link we returns false + auto destptr = castTo(baseptr); + if(!destptr) + { + return false; + } + + /// TLink:adding accepts nullptr (for a not yet resolved link). + const std::size_t index = TraitsContainer::add(m_value, destptr); + updateCounter(); + added(destptr, index); + return true;; } /// Returns false on type mismatch @@ -577,7 +581,7 @@ class MultiLink : public TLink& init, DestPtr val) : Inherit(init), m_validator(nullptr) { - if (val) this->add(val); + if (val) this->_doAdd_(sofa::core::castToBase(TraitsDestPtr::get(val))); } virtual ~MultiLink() @@ -654,7 +658,7 @@ class SingleLink : public TLink& init, DestPtr val) : Inherit(init), m_validator(nullptr) { - if (val) this->add(val); + if (val) this->_doAdd_(sofa::core::castToBase(TraitsDestPtr::get(val))); } virtual ~SingleLink() diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Snapshot.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/Snapshot.cpp new file mode 100644 index 00000000000..a28a3897eee --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Snapshot.cpp @@ -0,0 +1,30 @@ +/****************************************************************************** +* 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 + +namespace sofa::core::objectmodel +{ + +Snapshot::Snapshot() = default; +Snapshot::~Snapshot() = default; + +} // namespace sofa::core::objectmodel diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/Snapshot.h b/Sofa/framework/Core/src/sofa/core/objectmodel/Snapshot.h new file mode 100644 index 00000000000..a3505ddd706 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/Snapshot.h @@ -0,0 +1,125 @@ +/****************************************************************************** +* 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 * +******************************************************************************/ +#pragma once +#include +#include +#include +#include +#include + +#include + +namespace sofa::core::objectmodel +{ + +/** +* \brief Class for snapshot +* +* This class contains the structure for a snapshot of a simulation in SOFA. +* The snapshot contains data and link, and keep the shape of a scene graph +*/ +class SOFA_CORE_API Snapshot +{ + +public: + struct DataInfo + { + std::string name; + std::string type; + std::string value; + }; + + struct LinkInfo + { + std::string name; + std::string type; + std::string value; + }; + + struct SnapshotObject + { + std::string m_name; + std::vector m_dataContainer; + std::vector m_linkContainer; + void* m_internalState { nullptr }; + + + virtual void clear() + { + m_dataContainer.clear(); + m_linkContainer.clear(); + } + + void push_back(const DataInfo& data) + { + m_dataContainer.push_back(data); + } + + void push_back(const LinkInfo& link) + { + m_linkContainer.push_back(link); + } + + SnapshotObject() = default; + explicit SnapshotObject(std::string name) : m_name(std::move(name)){} + + virtual ~SnapshotObject() = default; + }; + + struct SnapshotNode : public SnapshotObject + { + std::vector components; + std::vector> children; + + void clear() override + { + components.clear(); + children.clear(); + m_dataContainer.clear(); + m_linkContainer.clear(); + } + + void push_back(const std::shared_ptr& child) + { + children.push_back(child); + } + + void push_back(const SnapshotObject& component) + { + components.push_back(component); + } + + SnapshotNode() = default; + explicit SnapshotNode(const std::string& name) : SnapshotObject(name) {} + explicit SnapshotNode(const SnapshotObject& obj) : SnapshotObject(obj) {} + + ~SnapshotNode() noexcept override = default; + }; + + std::shared_ptr m_graphRoot {nullptr}; + + Snapshot(); + virtual ~Snapshot(); + +}; + +} // namespace sofa::core::objectmodel diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/SnapshotJSONExporter.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/SnapshotJSONExporter.cpp new file mode 100644 index 00000000000..a64ebaaae8d --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/SnapshotJSONExporter.cpp @@ -0,0 +1,309 @@ +/****************************************************************************** +* 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 +#include +#include +using sofa::helper::logging::MessageDispatcher ; +using sofa::helper::logging::Message ; + +#define ERROR_LOG_SIZE 100 + +namespace sofa::core::objectmodel +{ + + + +void to_json(nlohmann::json& j, const Snapshot::DataInfo& di ) +{ + j.clear(); + j["name"] = di.name; + j["type"] = di.type; + j["value"] = di.value; +} + +void to_json(nlohmann::json& j, const Snapshot::LinkInfo& li ) +{ + j.clear(); + j["name"] = li.name; + j["type"] = li.type; + j["value"] = li.value; +} + +void to_json(nlohmann::json& j, const Snapshot::SnapshotObject& so ) +{ + j.clear(); + j["name"] = so.m_name; + j["datas"] = so.m_dataContainer; + j["links"] = so.m_linkContainer; +} + +void to_json(nlohmann::json& j, const Snapshot::SnapshotNode& sn) +{ + j.clear(); + j["name"] = sn.m_name; + j["datas"] = sn.m_dataContainer; + j["links"] = sn.m_linkContainer; + j["components"] = sn.components; + + j["children"] = nlohmann::json::array(); + for (const auto& childPtr : sn.children) + { + if(childPtr) + { + j["children"].push_back(*childPtr); + } + else + { + j["children"].push_back(nullptr); + } + } +} + +void to_json(nlohmann::json& j, const std::shared_ptr& sn) +{ + j.clear(); + j["name"] = sn->m_name; + j["datas"] = sn->m_dataContainer; + j["links"] = sn->m_linkContainer; + j["components"] = sn->components; + j["children"] = nlohmann::json::array(); + for (const auto& childPtr : sn->children) + { + if(childPtr) + { + j["children"].push_back(*childPtr); + } + else + { + j["children"].push_back(nullptr); + } + } +} + +void exportToJSON(Snapshot& snapshot, const std::string& filename) +{ + nlohmann::json j = snapshot.m_graphRoot ; + + std::ofstream file(filename); + file << j.dump(5); + file.close(); +} + +void from_json(const nlohmann::json& j, Snapshot::DataInfo& di) +{ + di.name = j.value("name", ""); + di.type = j.value("type", ""); + di.value = j.value("value", ""); +} + +void from_json(const nlohmann::json& j, Snapshot::LinkInfo& li) +{ + li.name = j.value("name", ""); + li.type = j.value("type", ""); + li.value = j.value("value", ""); +} + +void from_json(const nlohmann::json& j, Snapshot::SnapshotObject& so) +{ + so.m_name = j.value("name", ""); + + if (j.contains("datas") && j["datas"].is_array()) + { + so.m_dataContainer.clear(); + for (const auto& dataJson : j["datas"]) + { + Snapshot::DataInfo di; + from_json(dataJson, di); + so.m_dataContainer.push_back(di); + } + } + + if (j.contains("links") && j["links"].is_array()) + { + so.m_linkContainer.clear(); + for (const auto& linkJson : j["links"]) + { + Snapshot::LinkInfo li; + from_json(linkJson, li); + so.m_linkContainer.push_back(li); + } + } +} + +void from_json(const nlohmann::json& j, Snapshot::SnapshotNode& sn) +{ + sn.m_name = j.value("name", ""); + + if (j.contains("datas") && j["datas"].is_array()) + { + sn.m_dataContainer.clear(); + for (const auto& dataJson : j["datas"]) + { + Snapshot::DataInfo di; + from_json(dataJson, di); + sn.m_dataContainer.push_back(di); + } + } + + if (j.contains("links") && j["links"].is_array()) + { + sn.m_linkContainer.clear(); + for (const auto& linkJson : j["links"]) + { + Snapshot::LinkInfo li; + from_json(linkJson, li); + sn.m_linkContainer.push_back(li); + } + } + + if (j.contains("components") && j["components"].is_array()) + { + sn.components.clear(); + for (const auto& compJson : j["components"]) + { + Snapshot::SnapshotObject so; + from_json(compJson, so); + sn.components.push_back(so); + } + } + + sn.children.clear(); + if (j.contains("children") && j["children"].is_array()) + { + for (const auto& childJson : j["children"]) + { + if (!childJson.is_null()) + { + auto child = std::make_shared(); + from_json(childJson, *child); + sn.children.push_back(child); + } + } + } +} + +void importFrom(Snapshot& snapshot, const std::string& filename) +{ + std::ifstream file(filename); + if (!file.is_open()) + { + msg_error("SnapshotJSONExporter") << "ERROR: Cannot open file " << filename << " for reading"; + return; + } + + nlohmann::json jsonRoot; + file >> jsonRoot; + file.close(); + + if (!snapshot.m_graphRoot) + { + snapshot.m_graphRoot = std::make_shared(); + } + + if (jsonRoot.is_object() && !jsonRoot.empty()) + { + from_json(jsonRoot, *snapshot.m_graphRoot); + } + else + { + msg_error("SnapshotJSONExporter") << "Invalid JSON format in " << filename ; + return; + } + + msg_info("SnapshotJSONExporter") << "JSON imported successfully from: " << filename; +} + +std::string fileToString(const std::string& filename) +{ + std::ifstream file(filename); + if (!file.is_open()) + { + msg_error("SnapshotJSONExporter") << "Cannot open file " << filename << " for reading"; + return ""; + } + + nlohmann::json jsonRoot; + file >> jsonRoot; + file.close(); + return to_string(jsonRoot); +} + +std::string snapshotToString(const Snapshot& snapshot) +{ + nlohmann::json j = snapshot.m_graphRoot ; + return to_string(j); +} + +void exportToJSON(std::map>& snapshots, const std::string& filename) +{ + std::ofstream file(filename); + + nlohmann::json j_all = nlohmann::json::array(); + + for (const auto& snapshotJson : snapshots) + { + j_all.push_back(snapshotJson.second->m_graphRoot); + } + file << j_all.dump(5); + file.close(); +} + +void importFrom(std::map>& snapshots, const std::string& filename) +{ + std::ifstream file(filename); + + if (!file.is_open()) + { + msg_error("SnapshotJSONExporter") << "Cannot open file " << filename << " for reading"; + return; + } + + nlohmann::json j_all = nlohmann::json::array(); + file >> j_all; + file.close(); + + snapshots.clear(); + + int index = 0; + + for (const auto& snapshotJson : j_all) + { + auto snapshot = std::make_shared(); + + from_json(snapshotJson, *snapshot->m_graphRoot); + + std::string id; + + if (snapshot->m_graphRoot) + id = snapshot->m_graphRoot->m_name; + + if (id.empty()) + id="snapshot_"+ std::to_string(index++); + snapshot->m_graphRoot->m_name = id; + snapshots[id] = snapshot; + } +} + +} // namespace sofa::core::objectmodel diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/SnapshotJSONExporter.h b/Sofa/framework/Core/src/sofa/core/objectmodel/SnapshotJSONExporter.h new file mode 100644 index 00000000000..679375ba4b9 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/SnapshotJSONExporter.h @@ -0,0 +1,47 @@ +/****************************************************************************** +* 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 * +******************************************************************************/ +#pragma once +#include +#include + +namespace sofa::core::objectmodel +{ + + /// Export a single Snapshot to a JSON file + void exportToJSON(Snapshot& snapshot, const std::string& filename); + + /// Import a single Snapshot from a JSON file + void importFrom(Snapshot& snapshot, const std::string& filename); + + /// Read a JSON file and returns its content as a string + std::string fileToString(const std::string& filename); + + /// Serialize a Snapshot to a JSON string + std::string snapshotToString(const Snapshot& snapshot); + + /// Export a collection of Snapshots to a single JSON file + void exportToJSON(std::map>& snapshots, const std::string& filename); + + /// Import a collection of Snapshots from a single JSON file + void importFrom(std::map>& snapshots, const std::string& filename); + +} // namespace sofa::core::objectmodel diff --git a/Sofa/framework/Simulation/Core/CMakeLists.txt b/Sofa/framework/Simulation/Core/CMakeLists.txt index 22699a67eeb..5a825de3ffe 100644 --- a/Sofa/framework/Simulation/Core/CMakeLists.txt +++ b/Sofa/framework/Simulation/Core/CMakeLists.txt @@ -27,6 +27,7 @@ set(HEADER_FILES ${SRC_ROOT}/InitVisitor.h ${SRC_ROOT}/IntegrateBeginEvent.h ${SRC_ROOT}/IntegrateEndEvent.h + ${SRC_ROOT}/LoadSnapshotVisitor.h ${SRC_ROOT}/MechanicalOperations.h ${SRC_ROOT}/MechanicalVPrintVisitor.h ${SRC_ROOT}/MechanicalVisitor.h @@ -44,6 +45,8 @@ set(HEADER_FILES ${SRC_ROOT}/SceneLoaderFactory.h ${SRC_ROOT}/Simulation.h ${SRC_ROOT}/SimulationLoop.h + ${SRC_ROOT}/SaveSnapshotVisitor.h + ${SRC_ROOT}/SnapshotManager.h ${SRC_ROOT}/SolveVisitor.h ${SRC_ROOT}/StateChangeVisitor.h ${SRC_ROOT}/TopologyChangeVisitor.h @@ -179,6 +182,7 @@ set(SOURCE_FILES ${SRC_ROOT}/InitVisitor.cpp ${SRC_ROOT}/IntegrateBeginEvent.cpp ${SRC_ROOT}/IntegrateEndEvent.cpp + ${SRC_ROOT}/LoadSnapshotVisitor.cpp ${SRC_ROOT}/MappingGraph.cpp ${SRC_ROOT}/MappingGraphMechanicalOperations.cpp ${SRC_ROOT}/MechanicalOperations.cpp @@ -198,6 +202,8 @@ set(SOURCE_FILES ${SRC_ROOT}/SceneCheckRegistry.cpp ${SRC_ROOT}/SceneCheckMainRegistry.cpp ${SRC_ROOT}/Simulation.cpp + ${SRC_ROOT}/SaveSnapshotVisitor.cpp + ${SRC_ROOT}/SnapshotManager.cpp ${SRC_ROOT}/SolveVisitor.cpp ${SRC_ROOT}/StateChangeVisitor.cpp ${SRC_ROOT}/TopologyChangeVisitor.cpp @@ -322,6 +328,7 @@ sofa_find_package(Sofa.Core REQUIRED) add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES}) target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.Core) +target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/LoadSnapshotVisitor.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/LoadSnapshotVisitor.cpp new file mode 100644 index 00000000000..57c6c7e98e7 --- /dev/null +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/LoadSnapshotVisitor.cpp @@ -0,0 +1,60 @@ +/****************************************************************************** +* 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 +{ + +void LoadSnapshotVisitor::processObject( + core::objectmodel::BaseObject* obj, + const std::shared_ptr& parent +) +{ + auto snapshotObject = obj->findSnapshotObject(parent, obj->getName()); + if (snapshotObject) + { + obj->loadDataSnapshot(snapshotObject); + obj->loadLinkSnapshot(snapshotObject); + obj->loadInternalStateFrom(*snapshotObject); + } +} + +Visitor::Result LoadSnapshotVisitor::processNodeTopDown(simulation::Node* node) +{ + const auto snapshotObject = node->findSnapshotObject(m_snapshotContainer.m_graphRoot, node->getName()); + if (snapshotObject) + { + const auto SnapshotNode = std::dynamic_pointer_cast(snapshotObject); + node->loadDataSnapshot(SnapshotNode); + node->loadLinkSnapshot(SnapshotNode); + for (simulation::Node::ObjectIterator it = node->object.begin(); it != node->object.end(); ++it) + { + this->processObject(it->get(), SnapshotNode); + } + } + return RESULT_CONTINUE; +} + +} // namespace sofa::simulation diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/LoadSnapshotVisitor.h b/Sofa/framework/Simulation/Core/src/sofa/simulation/LoadSnapshotVisitor.h new file mode 100644 index 00000000000..fe0e2154c67 --- /dev/null +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/LoadSnapshotVisitor.h @@ -0,0 +1,45 @@ +/****************************************************************************** +* 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 * +******************************************************************************/ +#pragma once +#include +#include + +namespace sofa::simulation +{ + +class SOFA_SIMULATION_CORE_API LoadSnapshotVisitor : public Visitor +{ +protected: + core::objectmodel::Snapshot& m_snapshotContainer; + +public: + LoadSnapshotVisitor(const sofa::core::ExecParams* eparams, core::objectmodel::Snapshot& snapshot) : Visitor(eparams), m_snapshotContainer(snapshot) {} + + void processObject(core::objectmodel::BaseObject* obj, const std::shared_ptr& parent); + + Result processNodeTopDown(simulation::Node* node) override; + const char* getClassName() const override { return "LoadDataSnapshotVisitor"; } + +}; + +} // namespace sofa::simulation + diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index 70a0674af2f..23303e6ea6b 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp @@ -635,7 +635,7 @@ bool Node::doAddObject(sofa::core::objectmodel::BaseComponent::SPtr sobj, sofa:: { this->setObjectContext(sobj); if(insertionLocation == sofa::core::objectmodel::TypeOfInsertion::AtEnd) - object.add(sobj); + object.add(sobj.get()); else object.addBegin(sobj); @@ -654,7 +654,7 @@ bool Node::doRemoveObject(sofa::core::objectmodel::BaseComponent::SPtr sobj) dmsg_warning_when(sobj == nullptr) << "Trying to remove a nullptr object"; this->clearObjectContext(sobj); - object.remove(sobj); + object.remove(sobj.get()); sofa::core::objectmodel::BaseComponent* obj = sobj.get(); if(obj != nullptr && !obj->removeInNode( this ) ) @@ -1182,7 +1182,7 @@ void Node::doAddChild(BaseNode::SPtr node) { const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); setDirtyDescendancy(); - child.add(dagnode); + child.add(dagnode.get()); dagnode->l_parents.add(this); dagnode->l_parents.updateLinks(); // to fix load-time unresolved links } @@ -1192,7 +1192,7 @@ void Node::doRemoveChild(BaseNode::SPtr node) { const Node::SPtr dagnode = sofa::core::objectmodel::SPtr_static_cast(node); setDirtyDescendancy(); - child.remove(dagnode); + child.remove(dagnode.get()); dagnode->l_parents.remove(this); } diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/SaveSnapshotVisitor.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/SaveSnapshotVisitor.cpp new file mode 100644 index 00000000000..4e6c0e98412 --- /dev/null +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/SaveSnapshotVisitor.cpp @@ -0,0 +1,75 @@ +/****************************************************************************** +* 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 + + +namespace sofa::simulation +{ + +void SaveSnapshotVisitor::processObject( + core::objectmodel::BaseObject* obj, + std::shared_ptr parent) +{ + std::vector> parents; + const auto snapshot = obj->saveSnapshot(parents); + parent->components.push_back(*snapshot); +} + +Visitor::Result SaveSnapshotVisitor::processNodeTopDown(simulation::Node* node) +{ + const auto parents = node->getParents(); + + std::vector> snapshotParents; + for (auto* p : parents) + { + const auto it = m_snapshotNodeMap.find(p); + if (it != m_snapshotNodeMap.end()) + { + snapshotParents.push_back(it->second); + } + } + + const auto snapshot = node->saveSnapshot(snapshotParents); + const auto SnapshotNode = std::dynamic_pointer_cast(snapshot); + if (SnapshotNode) + { + m_snapshotNodeMap[node] = SnapshotNode; + } + + if (m_snapshotContainer.m_graphRoot == nullptr) + { + m_snapshotContainer.m_graphRoot = SnapshotNode; + } + + for (const auto& it : node->object) + { + this->processObject(it.get(), SnapshotNode); + } + + return RESULT_CONTINUE; +} + +} // namespace sofa::simulation + + + diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/SaveSnapshotVisitor.h b/Sofa/framework/Simulation/Core/src/sofa/simulation/SaveSnapshotVisitor.h new file mode 100644 index 00000000000..1aa4716753f --- /dev/null +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/SaveSnapshotVisitor.h @@ -0,0 +1,50 @@ +/****************************************************************************** +* 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 + +namespace sofa::simulation +{ + + +class SOFA_SIMULATION_CORE_API SaveSnapshotVisitor : public Visitor +{ +protected: + core::objectmodel::Snapshot& m_snapshotContainer; + + std::unordered_map> m_snapshotNodeMap; + +public: + SaveSnapshotVisitor(const sofa::core::ExecParams* eparams, core::objectmodel::Snapshot& snapshot) : Visitor(eparams), m_snapshotContainer(snapshot) + { + } + + void processObject(core::objectmodel::BaseObject* obj, std::shared_ptr parent); + + Result processNodeTopDown(simulation::Node* node) override; + const char* getClassName() const override { return "SaveSnapshotVisitor"; } + +}; + +} // namespace sofa::simulation + diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/SnapshotManager.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/SnapshotManager.cpp new file mode 100644 index 00000000000..5e525c84217 --- /dev/null +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/SnapshotManager.cpp @@ -0,0 +1,145 @@ +/****************************************************************************** +* 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 +#include +#include +#include +#include +#include +using sofa::helper::system::FileSystem; + + + +namespace sofa::simulation +{ + +SnapshotManager::SnapshotManager() = default; + +SnapshotManager::~SnapshotManager() = default; + +void SnapshotManager::addRecentFile(const std::string& path) +{ + m_recentSnapshotsFromFiles.erase(std::remove(m_recentSnapshotsFromFiles.begin(), m_recentSnapshotsFromFiles.end(), path), m_recentSnapshotsFromFiles.end()); + m_recentSnapshotsFromFiles.push_back(path); +} + +void SnapshotManager::addRecentSnapshot(std::shared_ptr snapshot, + double snapshotTime) +{ + static int index = 0; + m_recentSnapshotsFromMemory["Memory_Snapshot " + std::to_string(index++) + " at " + std::to_string(snapshotTime)] = std::move(snapshot); +} + +void SnapshotManager::doMemorySave(sofa::core::sptr& groot) +{ + auto snapshot = std::make_shared(); + auto visitor = SaveSnapshotVisitor(nullptr, *snapshot); + groot->execute(visitor); + addRecentSnapshot(snapshot, groot->getTime()); +} + +void SnapshotManager::doMemoryLoad(sofa::core::sptr& groot) +{ + if (m_recentSnapshotsFromMemory.empty()) + { + msg_warning("MemoryLoad") << "No Snapshot in memory"; + return; + } + + const auto& snapshot = m_recentSnapshotsFromMemory.rbegin()->second; + auto visitor = LoadSnapshotVisitor(nullptr, *snapshot); + groot->execute(visitor); +} + +void SnapshotManager::doSaveTo(sofa::core::sptr& groot,std::string savePath, bool isGroup) +{ + auto m_snapshot = std::make_shared(); + auto visitor = SaveSnapshotVisitor(nullptr,*m_snapshot); + groot->execute(visitor); + + + std::string FileExtension = FileSystem::getExtension(savePath); + if (FileExtension == "json" && !isGroup) + exportToJSON(*m_snapshot,savePath); + else if (FileExtension == "json" && isGroup) + exportToJSON(m_recentSnapshotsFromMemory,savePath); + else + msg_error("SaveSnapshot") << "Snapshot " << savePath << " not supported"; + + addRecentFile(savePath); + msg_info("SaveSnapshot") << "Snapshot " << savePath << " saved"; +} + +void SnapshotManager::doLoadTo(sofa::core::sptr& groot, std::string outPath) +{ + auto m_snapshot = std::make_shared(); + if (FileSystem::exists(outPath)) + { + importFrom(*m_snapshot,outPath); + auto visitor = LoadSnapshotVisitor(nullptr,*m_snapshot); + groot->execute(visitor); + } + addRecentFile(outPath); + msg_info("LoadSnapshot") << "Snapshot " << outPath << " loaded"; +} + +void SnapshotManager::doLoadToGroup(const std::string& filename) +{ + if (!FileSystem::exists(filename)) + return; + + std::ifstream file(filename); + + nlohmann::json jSnapshot; + + if (!file.is_open()) + { + msg_error("SnapshotJSONExporter") << "Cannot open file " << filename << " for reading"; + return; + } + + file >> jSnapshot; + file.close(); + + for (const auto& snapshotJson : jSnapshot) + { + auto snapshot = std::make_shared(); + snapshot->m_graphRoot = std::make_shared(); + sofa::core::objectmodel::from_json(snapshotJson, *snapshot->m_graphRoot); + std::string snapshotTime = "0"; + for (const auto& data : snapshot->m_graphRoot->m_dataContainer) + { + if (data.name == "time") + snapshotTime = data.value; + } + + addRecentSnapshot(snapshot, std::stod(snapshotTime)); + } + + msg_info("LoadSnapshot") << "Snapshot " << filename << " loaded"; +} + +} // namespace sofa::simulation diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/SnapshotManager.h b/Sofa/framework/Simulation/Core/src/sofa/simulation/SnapshotManager.h new file mode 100644 index 00000000000..64077d63e66 --- /dev/null +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/SnapshotManager.h @@ -0,0 +1,84 @@ +/****************************************************************************** +* 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 * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace sofa::simulation +{ + +/** + * \brief Snapshot container and memory save/load helper. + * + * Actually, this class: + * - Store snapshots (from files or memory) + * - Do save/load in memory + * - Do save/load in a file + * + */ +class SOFA_SIMULATION_CORE_API SnapshotManager +{ +public: + SnapshotManager(); + ~SnapshotManager(); + + /// Container of snapshot from Files + std::vector m_recentSnapshotsFromFiles; + + /// Container of snapshot from Memory + std::map> m_recentSnapshotsFromMemory; + + /// Store every snapshot from files in m_recentSnapshotsFromFiles and sort them by filename + void addRecentFile(const std::string& path); + + /// Store every snapshot in memory in m_recentSnapshotsFromMemory and sort them by simulation time + void addRecentSnapshot(std::shared_ptr snapshot, + double snapshotTime); + + /// Save and store a Snapshot in memory + void doMemorySave(sofa::core::sptr& groot); + + /// Load a snapshot from memory + void doMemoryLoad(sofa::core::sptr& groot); + + /// Save and store a Snapshot to a file (or Save and store a group of Snapshot to a file when isGroup is true) + void doSaveTo(sofa::core::sptr& groot,std::string savePath, bool isGroup); + + /// Load a snapshot from a file + void doLoadTo(sofa::core::sptr& groot, std::string outPath); + + /// Load a group of snapshots from a file + void doLoadToGroup(const std::string& filename); + + +}; + +} // namespace sofa::simulation diff --git a/Sofa/framework/Simulation/Graph/test/Link_test.cpp b/Sofa/framework/Simulation/Graph/test/Link_test.cpp index 3d904bfa8ef..a47bbb0a698 100644 --- a/Sofa/framework/Simulation/Graph/test/Link_test.cpp +++ b/Sofa/framework/Simulation/Graph/test/Link_test.cpp @@ -19,6 +19,8 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ +#include +#include #include using sofa::testing::BaseSimulationTest ; diff --git a/examples/Demos/collisionMultiple.scn b/examples/Demos/collisionMultiple.scn index cd2ea9d47cd..fbee01a00fd 100644 --- a/examples/Demos/collisionMultiple.scn +++ b/examples/Demos/collisionMultiple.scn @@ -32,7 +32,7 @@ - + @@ -54,7 +54,7 @@ - + @@ -76,7 +76,7 @@ - + diff --git a/examples/Demos/liver.scn b/examples/Demos/liver.scn index 6b93edffdef..eddef65ad19 100644 --- a/examples/Demos/liver.scn +++ b/examples/Demos/liver.scn @@ -19,30 +19,30 @@ - + - + - + - + - + diff --git a/examples/Tutorials/Basic/TutorialBasicPendulum.scn b/examples/Tutorials/Basic/TutorialBasicPendulum.scn index ba50111ce2e..3488f8e33b9 100644 --- a/examples/Tutorials/Basic/TutorialBasicPendulum.scn +++ b/examples/Tutorials/Basic/TutorialBasicPendulum.scn @@ -13,8 +13,8 @@ - - + + - + - - - + + + - - - + + + diff --git a/examples/Tutorials/Collision/MultipleObjectsTwoCubes.scn b/examples/Tutorials/Collision/MultipleObjectsTwoCubes.scn index 8ab98c70dff..ec48437afbf 100644 --- a/examples/Tutorials/Collision/MultipleObjectsTwoCubes.scn +++ b/examples/Tutorials/Collision/MultipleObjectsTwoCubes.scn @@ -25,31 +25,31 @@ - - - - - - + + + + + + - + - - - - - - + + + + + + - - - + + + - - - + + +