diff --git a/CMake/External/CMakeLists.txt b/CMake/External/CMakeLists.txt index f668d532d918a6a8596ae18fc1b7fd30e744de4d..f72aa4fb7bd0e60cb92104c6c4d18513c6a495b9 100644 --- a/CMake/External/CMakeLists.txt +++ b/CMake/External/CMakeLists.txt @@ -95,6 +95,7 @@ ExternalProject_Add( ${PROJECT_NAME} -D${PROJECT_NAME}_BUILD_EXAMPLES:BOOL=${${PROJECT_NAME}_BUILD_EXAMPLES} -D${PROJECT_NAME}_BUILD_TESTING:BOOL=${${PROJECT_NAME}_BUILD_TESTING} -D${PROJECT_NAME}_USE_OpenHaptics:BOOL=${${PROJECT_NAME}_USE_OpenHaptics} + -D${PROJECT_NAME}_USE_MODEL_REDUCTION:BOOL={${PROJECT_NAME}_USE_MODEL_REDUCTION} -D${PROJECT_NAME}_USE_Vulkan:BOOL=${${PROJECT_NAME}_USE_Vulkan} -D${PROJECT_NAME}_ENABLE_AUDIO:BOOL=${${PROJECT_NAME}_ENABLE_AUDIO} -D${PROJECT_NAME}_ENABLE_VR:BOOL=${${PROJECT_NAME}_ENABLE_VR} diff --git a/CMake/External/External_VTK.cmake b/CMake/External/External_VTK.cmake index f410c3f6d994dba3c2c10a8eb5489238c625125f..3fcbe0425a8b83590c1d5b9e694d7b636ed4ee5e 100644 --- a/CMake/External/External_VTK.cmake +++ b/CMake/External/External_VTK.cmake @@ -16,6 +16,7 @@ set_property(CACHE ${PROJECT_NAME}_VTK_REPO_SOURCE PROPERTY STRINGS ${VTK_SOURCE if(${PROJECT_NAME}_VTK_REPO_SOURCE EQUAL "8.2") set(VTK_MODULE_SETTINGS + -DModule_vtkChartsCore:BOOL=ON -DModule_vtkRenderingOpenGL2:BOOL=ON -DModule_vtkIOXML:BOOL=ON -DModule_vtkIOLegacy:BOOL=ON @@ -28,6 +29,7 @@ if(${PROJECT_NAME}_VTK_REPO_SOURCE EQUAL "8.2") -DModule_vtkglew:BOOL=ON -DModule_vtkRenderingContext2D:BOOL=ON -DModule_vtkRenderingVolumeOpenGL2:BOOL=ON + -DModule_vtkViewsContext2D:BOOL=ON -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_TESTING:BOOL=OFF -DVTK_Group_StandAlone:BOOL=OFF @@ -42,6 +44,7 @@ else() set(VTK_OPENVR "DONT_WANT") endif() set(VTK_MODULE_SETTINGS + -DVTK_MODULE_ENABLE_VTK_ChartsCore:STRING=YES -DVTK_MODULE_ENABLE_VTK_RenderingOpenGL2:STRING=YES -DVTK_MODULE_ENABLE_VTK_IOExport:STRING=YES -DVTK_MODULE_ENABLE_VTK_IOImport:STRING=YES @@ -58,6 +61,7 @@ else() -DVTK_MODULE_ENABLE_VTK_glew:STRING=YES -DVTK_MODULE_ENABLE_VTK_RenderingContext2D:STRING=YES -DVTK_MODULE_ENABLE_VTK_RenderingVolumeOpenGL2:STRING=YES + -DVTK_MODULE_ENABLE_VTK_ViewsContext2D:STRING=YES -DVTK_BUILD_EXAMPLES:STRING=DONT_WANT -DVTK_BUILD_TESTING:STRING=OFF -DVTK_GROUP_ENABLE_StandAlone:STRING=DONT_WANT diff --git a/CMake/External/External_VegaFEM.cmake b/CMake/External/External_VegaFEM.cmake index a5e30bedc047eab7d110ad97d3de17a54b841e58..27b616ce183dc977365dd5a6b0349e0b80a5c3aa 100644 --- a/CMake/External/External_VegaFEM.cmake +++ b/CMake/External/External_VegaFEM.cmake @@ -8,12 +8,12 @@ endif(WIN32) include(imstkAddExternalProject) imstk_add_external_project( VegaFEM - GIT_REPOSITORY https://gitlab.kitware.com/iMSTK/vegafemv4.0 - GIT_TAG f9c96c4128437a559e5fb9a93830ef3c9a627d5e + URL https://gitlab.kitware.com/iMSTK/vegafemv4.0/-/archive/build_model_reduction/vegafemv4.0-build_model_reduction.zip + URL_MD5 3f04bb7c2ba080785bcadf44d1a462a3 CMAKE_CACHE_ARGS -DVegaFEM_ENABLE_PTHREADS_SUPPORT:BOOL=ON -DVegaFEM_ENABLE_OpenGL_SUPPORT:BOOL=OFF - -DVegaFEM_BUILD_MODEL_REDUCTION:BOOL=OFF + -DVegaFEM_BUILD_MODEL_REDUCTION:BOOL=${${PROJECT_NAME}_USE_MODEL_REDUCTION} -DVegaFEM_BUILD_UTILITIES:BOOL=OFF DEPENDENCIES ${VegaFEM_DEPENDENCIES} RELATIVE_INCLUDE_PATH "" diff --git a/CMake/Utilities/iMSTKInnerbuildConfig.cmake.in b/CMake/Utilities/iMSTKInnerbuildConfig.cmake.in deleted file mode 100644 index 1158fcca3b166ab8756c9198e43c651ad6849004..0000000000000000000000000000000000000000 --- a/CMake/Utilities/iMSTKInnerbuildConfig.cmake.in +++ /dev/null @@ -1,97 +0,0 @@ -# Include and library paths -set(CMAKE_INCLUDE_PATH "@CMAKE_INCLUDE_PATH@") -set(CMAKE_LIBRARY_PATH "@CMAKE_LIBRARY_PATH@") - -# Path to iMSTK source directory -set(iMSTK_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@") -set(iMSTK_BINARY_DIR "@CMAKE_BINARY_DIR@") -set(iMSTK_INSTALL_ROOT "@iMSTK_INSTALL_ROOT@") - -# Update CMake module path -list(INSERT CMAKE_MODULE_PATH 0 "${iMSTK_SOURCE_DIR}/CMake") -list(INSERT CMAKE_MODULE_PATH 1 "${iMSTK_SOURCE_DIR}/CMake/Utilities") - -# The Find scripts use cmake variables so overwrite them -set(CMAKE_INSTALL_PREFIX_CACHE ${CMAKE_INSTALL_PREFIX}) -set(CMAKE_BINARY_DIR_CACHE ${CMAKE_BINARY_DIR}) -set(CMAKE_DEBUG_POSTFIX_CACHE ${CMAKE_DEBUG_POSTFIX}) - -set(CMAKE_INSTALL_PREFIX ${iMSTK_INSTALL_ROOT} CACHE STRING "" FORCE) -mark_as_advanced(CMAKE_INSTALL_PREFIX) -set(CMAKE_BINARY_DIR ${iMSTK_BINARY_DIR} CACHE STRING "" FORCE) -mark_as_advanced(CMAKE_BINARY_DIR) -set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "" FORCE) -mark_as_advanced(CMAKE_DEBUG_POSTFIX) - -# iMSTK settings -set(iMSTK_USE_OpenHaptics @iMSTK_USE_OpenHaptics@) -set(iMSTK_USE_Vulkan @iMSTK_USE_Vulkan@) -set(VegaFEM_DIR "@VegaFEM_DIR@") -set(VTK_DIR "@VTK_DIR@") - -# Assimp -find_package( Assimp REQUIRED ) - -# g3log -find_package( g3log REQUIRED ) -include_directories( ${g3log_INCLUDE_DIR} ) - -# glm -find_package( glm REQUIRED ) - -if(iMSTK_USE_Vulkan) - # glfw - find_package( glfw REQUIRED ) - - # gli - find_package( gli REQUIRED ) -endif() - -# Eigen -find_package( Eigen3 3.3 REQUIRED ) -include_directories( ${Eigen_INCLUDE_DIR} ) - -# imgui -find_package( imgui REQUIRED ) -include_directories( ${imgui_INCLUDE_DIR} ) - -# PhysX -find_package(PhysX REQUIRED) -include_directories(${PHYSX_INCLUDE_DIRS}) - -# SCCD -find_package( SCCD REQUIRED ) -include_directories( ${SCCD_INCLUDE_DIR} ) - -# tbb -find_package(tbb REQUIRED) -include_directories(${TBB_INCLUDE_DIR}) -if (MSVC) - add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE=1) -endif() - -# VegaFEM -find_package( VegaFEM REQUIRED CONFIG ) - -# VTK -find_package( VTK REQUIRED CONFIG ) -include( ${VTK_USE_FILE} ) - -# VRPN -find_package( VRPN REQUIRED ) -include_directories( ${VRPN_INCLUDE_DIRS} ) -add_definitions( -DVRPN_USE_LIBNIFALCON ) -if(iMSTK_USE_OpenHaptics) - find_package(OpenHapticsSDK REQUIRED) - add_definitions( -DiMSTK_USE_OPENHAPTICS ) -else() - remove_definitions( -DiMSTK_USE_OPENHAPTICS ) -endif() - -# iMSTK -#link_directories(@CMAKE_LIBRARY_PATH@) -include("@CMAKE_CURRENT_BINARY_DIR@/iMSTKTargets.cmake") - -set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX_CACHE} CACHE STRING "" FORCE) -set(CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR_CACHE} CACHE STRING "" FORCE) -set(CMAKE_DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX_CACHE} CACHE STRING "" FORCE) \ No newline at end of file diff --git a/CMake/Utilities/imstkAddLibrary.cmake b/CMake/Utilities/imstkAddLibrary.cmake index a56adf11e47974face309b7515050d29513af559..f124ed858ef0cdad7c997f7ee9997fd1e6b5ddfb 100644 --- a/CMake/Utilities/imstkAddLibrary.cmake +++ b/CMake/Utilities/imstkAddLibrary.cmake @@ -14,7 +14,7 @@ function(imstk_add_library target) set(options VERBOSE) set(oneValueArgs) - set(multiValueArgs H_FILES CPP_FILES SUBDIR_LIST DEPENDS) + set(multiValueArgs H_FILES CPP_FILES SUBDIR_LIST EXCLUDE_FILES DEPENDS) include(CMakeParseArguments) cmake_parse_arguments(target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) @@ -42,6 +42,13 @@ function(imstk_add_library target) endif() endif() + # Exclude files + foreach(file ${target_EXCLUDE_FILES}) + list(REMOVE_ITEM target_H_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${file}") + list(REMOVE_ITEM target_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${file}") + list(REMOVE_ITEM testing_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${file}") + endforeach() + if( NOT target_SUBDIR_LIST ) imstk_subdir_list(target_SUBDIR_LIST ${CMAKE_CURRENT_SOURCE_DIR}) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index b7b817a9b8bad83a156f90c46f69f759272cf6e0..bac23b70716265dff58228959d5d4d392461969a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.9) -project(iMSTK VERSION 2.0.0 LANGUAGES C CXX) +project(iMSTK VERSION 3.0.0 LANGUAGES C CXX) if(UNIX AND NOT APPLE) set(LINUX TRUE) @@ -88,6 +88,7 @@ set(BUILD_TESTING OFF) if (${PROJECT_NAME}_BUILD_TESTING) set(BUILD_TESTING ON) endif () +option(${PROJECT_NAME}_USE_MODEL_REDUCTION "Build with model reduction, requires a VegaFEM built with Intel MKL and arpack" OFF) #----------------------------------------------------------------------------- # CTest/Dashboards @@ -282,6 +283,7 @@ else() # modules are linked via `VTK::CommonCore` # vtk_module_autoinit is needed find_package(VTK COMPONENTS + ChartsCore CommonCore CommonDataModel FiltersGeneral @@ -300,6 +302,7 @@ else() RenderingOpenGL2 RenderingVolume RenderingVolumeOpenGL2 + ViewsContext2D ) endif() diff --git a/Data/asianDragon/asianDragon.URendering.float.sha512 b/Data/asianDragon/asianDragon.URendering.float.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..c1f4df24c51ae47d0fcb8fadeb2f89cf0f8b4c27 --- /dev/null +++ b/Data/asianDragon/asianDragon.URendering.float.sha512 @@ -0,0 +1 @@ +b598cc7b55f34c79eb2fc87505cec2e6069d7f3a8113710f6013eae88bd16ab0cf1fbbd699c83986b0ef6a7a4959dcc82afa68c4a9aff5ad4bb40a9a911e74fd \ No newline at end of file diff --git a/Data/asianDragon/asianDragon.cub.sha512 b/Data/asianDragon/asianDragon.cub.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..e158f1fe59e6a12d061ed7036f1e893cd3a8eb00 --- /dev/null +++ b/Data/asianDragon/asianDragon.cub.sha512 @@ -0,0 +1 @@ +b904fa19e85c7c59e3007ff4c6f80ac9876e0fb26fcc4db93ff13d7b723e2ea67b80ccd7d1171271e8166fa40f1fb730147b91c2bb6a1e04193542d13af4c608 \ No newline at end of file diff --git a/Docs/source/documentation.rst b/Docs/source/documentation.rst index 162bc1edd489604c482e204de245f85eed79f262..42beb8e981e5a7def5724b4357c688a19070a1a4 100644 --- a/Docs/source/documentation.rst +++ b/Docs/source/documentation.rst @@ -36,7 +36,7 @@ Setup for Development ===================== iMSTK and its external dependencies can be configured and built from -scratch Cmake to create a super-build on UNIX (MAC, Linux) and Windows +scratch with Cmake to create a super-build on UNIX (MAC, Linux) and Windows platforms. The instructions below describe this process in detail. Configuration and Build @@ -1011,7 +1011,7 @@ An example code on how to instantiate a haptic device is shown below sdk->addModule(server); -Paralle Support +Parallel Support =============== iMSTK allows CPU based shared memory parallelization using Intel TBB library. @@ -1041,6 +1041,99 @@ options to parallelize over a dimension of choice. Miscellaneous Topics ==================== +Task Graphs +------------------ +iMSTK provides Task Graphs. These are used internally but may also be used by the user. +A TaskGraph contains a set of TaskNode's. Each node accomplishes some computation. +The TaskGraph also contains edges establishing successor/predeccessor relationships between +the tasks. For example, one could specify that a task may only happen after two others have +completed. Additionally a TaskGraph will always contain a source and sink node. + +This TaskGraph may be then executed by the user using one of the TaskGraphControllers. +Currently, there are two provided TaskGraphControllers. SequentialTaskGraphController and +TbbTaskGraphController. Sequential executes the tasks one by one in topological order while +Tbb executes them in parallel. + +To use a TaskGraph, create one and begin adding functions/nodes to it. One can either +create a TaskNode or use the overloaded function in TaskGraph to directly supply a function +pointer. Then we can establish edges between the nodes and execute. + +:: + + // Inputs + const int x = 0; + const int y = 2; + const int z = 3; + const int w = 5; + + // Results + int a = 0; + int b = 0; + int c = 0; + + std::shared_ptr<TaskGraph> graph = std::make_shared<TaskGraph>(); + + // Setup Nodes + std::shared_ptr<TaskNode> addXYNode = graph->addFunction([&]() { a = x + y; }); + std::shared_ptr<TaskNode> multZWNode = graph->addFunction([&]() { b = z * w; }); + std::shared_ptr<TaskNode> addABNode = graph->addFunction([&]() { c = a + b; }); + + // Setup Edges + graph->addEdge(graph->getSource(), addXYNode); + graph->addEdge(graph->getSource(), multZWNode); + graph->addEdge(addXYNode, addABNode); + graph->addEdge(multZWNode, addABNode); + graph->addEdge(addABNode, graph->getSink()); + + // Finally, execute + std::shared_ptr<TbbTaskGraphController> controller = std::make_shared<TbbTaskGraphController>(); + controller->setInput(graph); + controller->init(); // Possible preprocessing steps + controller->execute(); + +TaskGraphs are also used internally in iMSTK's physics loop. This allows one to reconfigure +iMSTK's sequence of events. The Scene, SceneObject, and AbstractDynamicalModel classes all contain +member TaskGraphs which are composited into each other. To modify the main physics loop one should +modify the top level TaskGraph, that would be the Scene TaskGraph. Keep in mind that the Scene's TaskGraph +is rebuilt on initialize. One can have their own step within initialization like so: + +:: + + scene->setComputeGraphConfigureCallback([&](Scene* scene) + { + // Insert nodes or edges + }); + +Additionally many of the classes in iMSTK expose getters for their nodes. This is useful, for example, +if one wanted to insert a step after PbdModel does its position integration. + +:: + + std::shared_ptr<PbdModel> pbdModel = clothObj->getPbdModel(); + ... + scene->setComputeGraphConfigureCallback([&](Scene* scene) + { + std::shared_ptr<ComputeGraph> graph = scene->getComputeGraph(); + + // A TaskNode to print all velocities + std::shared_ptr<TaskNode> printVelocities = std::make_shared<TaskNode>([&]() + { + const StdVectorOfVec3d& velocities = *pbdModel->getCurrentState()->getVelocities(); + for (size_t i = 0; i < velocities.size(); i++) + { + printf("Velocity: %f, %f, %f\n", velocities[i].x(), velocities[i].y(), velocities[i].z()); + } + }); + + // Add to the graph, after positions are integrated + graph->insertAfter(pbdModel->getIntegratePositionNode(), printVelocities); + }); + +Lastly, the TaskGraph comes with a number of graph functions. Static functions such as sum, nest, topological sort, +transitive reduction, cyclic check, etc. Member functions such as addNode, addFunction, addEdge, insertAfter, +insertBefore, ... The graph sum and nest are especially useful when maintaining multiple decoupled graphs with identical +nodes between them that you would then like to combine. + Object Controllers ------------------ diff --git a/Examples/BoneShaving/BoneShavingExample.cpp b/Examples/BoneShaving/BoneShavingExample.cpp index ce8e3912fed516af4b1dd408759ab7c95a281626..1c1e80fd08caeb6a862d810a8b744fc9bd98ea59 100644 --- a/Examples/BoneShaving/BoneShavingExample.cpp +++ b/Examples/BoneShaving/BoneShavingExample.cpp @@ -105,13 +105,9 @@ main() auto objController = std::make_shared<SceneObjectController>(drill, deviceTracker); scene->addObjectController(objController); - // Create a collision graph - auto graph = scene->getCollisionGraph(); - auto pair = graph->addInteractionPair(bone, - drill, - CollisionDetection::Type::PointSetToSphere, - CollisionHandling::Type::BoneDrilling, - CollisionHandling::Type::None); + // Add interaction + scene->getCollisionGraph()->addInteraction(makeObjectInteractionPair(bone, drill, + InteractionType::FemObjToCollidingObjBoneDrilling, CollisionDetection::Type::PointSetToSphere)); #endif diff --git a/Examples/CollisionDetection/ManualCDWithOctree/ManualCDWithOctreeExample.cpp b/Examples/CollisionDetection/ManualCDWithOctree/ManualCDWithOctreeExample.cpp index 1e27dfc2ebb6bd7d121e98654ef881a67b4a8b28..73d2c198927d1b7d58102ab6136b6c92b019aaf7 100644 --- a/Examples/CollisionDetection/ManualCDWithOctree/ManualCDWithOctreeExample.cpp +++ b/Examples/CollisionDetection/ManualCDWithOctree/ManualCDWithOctreeExample.cpp @@ -33,7 +33,6 @@ #include "imstkCollidingObject.h" #include "imstkSceneManager.h" #include "imstkSurfaceMesh.h" -#include "imstkCollisionGraph.h" #include "imstkScene.h" #include "imstkVTKRenderer.h" diff --git a/Examples/CreateEnclosingMesh/CreateEnclosingMesh.cpp b/Examples/CreateEnclosingMesh/CreateEnclosingMesh.cpp index 8130adb1417910bf97584269466a21bfca79cfcc..75206d18c752a6c81fc4e2b022875bc21f3cae35 100644 --- a/Examples/CreateEnclosingMesh/CreateEnclosingMesh.cpp +++ b/Examples/CreateEnclosingMesh/CreateEnclosingMesh.cpp @@ -22,7 +22,6 @@ #include "imstkSimulationManager.h" #include "imstkLight.h" #include "imstkCamera.h" -#include "imstkCollisionGraph.h" #include "imstkAPIUtilities.h" #include "imstkSurfaceMesh.h" #include "imstkTetrahedralMesh.h" diff --git a/Examples/DebugRendering/DebugRenderingExample.cpp b/Examples/DebugRendering/DebugRenderingExample.cpp index db5a9b8531f971f55b4dbda584bda7e06fde3d7f..f5d03ce135977416bb154fbe5b1becb151905987 100644 --- a/Examples/DebugRendering/DebugRenderingExample.cpp +++ b/Examples/DebugRendering/DebugRenderingExample.cpp @@ -24,7 +24,6 @@ #include "imstkLight.h" #include "imstkCamera.h" #include "imstkSceneManager.h" -#include "imstkCollisionGraph.h" #include "imstkDebugRenderGeometry.h" #include "imstkAPIUtilities.h" #include "imstkVTKViewer.h" diff --git a/Examples/DeformableBody/DeformableBodyExample.cpp b/Examples/DeformableBody/DeformableBodyExample.cpp index 3093eee8e20e670afea6d73b4e375470f0fe595c..6b2f5092dd4e4f2db818d03573e877f639f8b451 100644 --- a/Examples/DeformableBody/DeformableBodyExample.cpp +++ b/Examples/DeformableBody/DeformableBodyExample.cpp @@ -35,7 +35,6 @@ #include "imstkLight.h" #include "imstkCamera.h" #include "imstkFEMDeformableBodyModel.h" -#include "imstkCollisionGraph.h" #include "imstkSurfaceMesh.h" #include "imstkScene.h" @@ -68,25 +67,20 @@ main() volTetMesh->extractSurfaceMesh(surfMesh, true); - // Construct a map - // Construct one to one nodal map based on the above meshes auto oneToOneNodalMap = std::make_shared<OneToOneMap>(tetMesh, surfMesh); - // Scene object 1: Dragon - // Configure dynamic model auto dynaModel = std::make_shared<FEMDeformableBodyModel>(); - //dynaModel->configure(iMSTK_DATA_ROOT "/asianDragon/asianDragon.config"); - auto config = std::make_shared<FEMModelConfig>(); - config->m_fixedNodeIds = { 51, 127, 178 }; + config->m_fixedNodeIds = { 50, 126, 177 }; dynaModel->configure(config); + //dynaModel->configure(iMSTK_DATA_ROOT "/asianDragon/asianDragon.config"); dynaModel->setTimeStepSizeType(TimeSteppingType::Fixed); dynaModel->setModelGeometry(volTetMesh); - auto timeIntegrator = std::make_shared<BackwardEuler>(0.05);// Create and add Backward Euler time integrator + auto timeIntegrator = std::make_shared<BackwardEuler>(0.01);// Create and add Backward Euler time integrator dynaModel->setTimeIntegrator(timeIntegrator); auto material = std::make_shared<RenderMaterial>(); @@ -97,7 +91,7 @@ main() auto surfMeshModel = std::make_shared<VisualModel>(surfMesh); surfMeshModel->setRenderMaterial(material); - // Scene Object + // Scene object 1: Dragon auto deformableObj = std::make_shared<FeDeformableObject>("Dragon"); deformableObj->addVisualModel(surfMeshModel); deformableObj->setPhysicsGeometry(volTetMesh); @@ -114,30 +108,6 @@ main() planeObj->setCollidingGeometry(planeGeom); scene->addSceneObject(planeObj); - // create a nonlinear system - auto nlSystem = std::make_shared<NonLinearSystem<SparseMatrixd>>( - dynaModel->getFunction(), - dynaModel->getFunctionGradient()); - - nlSystem->setUnknownVector(dynaModel->getUnknownVec()); - nlSystem->setUpdateFunction(dynaModel->getUpdateFunction()); - nlSystem->setUpdatePreviousStatesFunction(dynaModel->getUpdatePrevStateFunction()); - - // create a linear solver - auto linSolver = std::make_shared<ConjugateGradient>(); - - if (linSolver->getType() == imstk::LinearSolver<imstk::SparseMatrixd>::Type::GaussSeidel - && dynaModel->isFixedBCImplemented()) - { - LOG(WARNING) << "The GS solver may not be viable!"; - } - - // create a non-linear solver and add to the scene - auto nlSolver = std::make_shared<NewtonSolver<SparseMatrixd>>(); - nlSolver->setLinearSolver(linSolver); - nlSolver->setSystem(nlSystem); - scene->addNonlinearSolver(nlSolver); - // Light auto light = std::make_shared<DirectionalLight>("light"); light->setFocalPoint(Vec3d(5, -8, -5)); diff --git a/Examples/GeometryProcessing/GeometryProcessingExample.cpp b/Examples/GeometryProcessing/GeometryProcessingExample.cpp index 49cab445e4764141a987c9b0486134061de4af6f..5a050fb534c2aed0e2fb9882bb9154c8ffe4b1ab 100644 --- a/Examples/GeometryProcessing/GeometryProcessingExample.cpp +++ b/Examples/GeometryProcessing/GeometryProcessingExample.cpp @@ -31,7 +31,6 @@ #include "imstkMeshIO.h" #include "imstkSurfaceMesh.h" #include "imstkTetrahedralMesh.h" -#include "imstkCollisionGraph.h" #include "imstkScene.h" using namespace imstk; diff --git a/Examples/GeometryTransforms/GeometryTransformsExample.cpp b/Examples/GeometryTransforms/GeometryTransformsExample.cpp index 4417b0123ae67f7249580812307473983cb10bce..f1f69870900cd42b47d1e540c0edecb5c2aca512 100644 --- a/Examples/GeometryTransforms/GeometryTransformsExample.cpp +++ b/Examples/GeometryTransforms/GeometryTransformsExample.cpp @@ -21,7 +21,6 @@ #include "imstkSimulationManager.h" #include "imstkSceneManager.h" -#include "imstkCollisionGraph.h" #include "imstkSceneObject.h" #include "imstkCamera.h" #include "imstkLight.h" diff --git a/Examples/LineMesh/LineMeshExample.cpp b/Examples/LineMesh/LineMeshExample.cpp index 7a8c6cf3de7ea5d97a7e721bf8a68c2bdf78b33b..89b8fa8f91ca5cee6b7c50fb17e8253b4d97d3f5 100644 --- a/Examples/LineMesh/LineMeshExample.cpp +++ b/Examples/LineMesh/LineMeshExample.cpp @@ -20,7 +20,6 @@ =========================================================================*/ #include "imstkSimulationManager.h" -#include "imstkCollisionGraph.h" #include "imstkAPIUtilities.h" #include "imstkCamera.h" #include "imstkLineMesh.h" diff --git a/Examples/MultipleScenes/multipleScenes.cpp b/Examples/MultipleScenes/multipleScenes.cpp index ca7f47f6260aa6a56494be66b41aedbb6ace7fbd..8b5cd383bc0ab66fc08555a8da941cc08f20ffaf 100644 --- a/Examples/MultipleScenes/multipleScenes.cpp +++ b/Examples/MultipleScenes/multipleScenes.cpp @@ -28,7 +28,6 @@ #include "imstkSurfaceMesh.h" #include "imstkMeshIO.h" #include "imstkTetrahedralMesh.h" -#include "imstkCollisionGraph.h" #include "imstkPlane.h" #include "imstkScene.h" @@ -66,15 +65,15 @@ createSoftBodyScene(std::shared_ptr<SimulationManager> simManager, const char* s auto pbdParams = std::make_shared<PBDModelConfig>(); // FEM constraint - pbdParams->m_YoungModulus = 100.0; - pbdParams->m_PoissonRatio = 0.3; + pbdParams->m_femParams->m_YoungModulus = 100.0; + pbdParams->m_femParams->m_PoissonRatio = 0.3; pbdParams->m_fixedNodeIds = { 51, 127, 178 }; pbdParams->enableFEMConstraint(PbdConstraint::Type::FEMTet, PbdFEMConstraint::MaterialType::StVK); // Other parameters pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -9.8, 0); - pbdParams->m_maxIter = 45; + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_iterations = 45; // Set the parameters pbdModel->configure(pbdParams); @@ -164,9 +163,9 @@ createClothScene(std::shared_ptr<SimulationManager> simManager, const char* scen // Other parameters pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -9.8, 0); - pbdParams->m_DefaultDt = 0.03; - pbdParams->m_maxIter = 5; + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_defaultDt = 0.03; + pbdParams->m_iterations = 5; // Set the parameters pbdModel->configure(pbdParams); diff --git a/Examples/NonlinearSolver/nonlinearSolver.cpp b/Examples/NonlinearSolver/nonlinearSolver.cpp index 1725f74c8351bfb07f27fed3fbb7c273434782d9..1e50fa5a3f7ba643b2ef9eb0466e52ce2ab20dd6 100644 --- a/Examples/NonlinearSolver/nonlinearSolver.cpp +++ b/Examples/NonlinearSolver/nonlinearSolver.cpp @@ -34,40 +34,40 @@ using namespace imstk; int main(int argc, char** argv) { - const int N = 2; - auto x = Vectord(N); + const int N = 2; + auto x = Vectord(N); auto xe = Vectord(N); - auto y = Vectord(N); - auto A = Matrixd(N, N); + auto y = Vectord(N); + auto A = Matrixd(N, N); - x[0] = 100.0; - x[1] = 100.0; + x[0] = 100.0; + x[1] = 100.0; xe[0] = 1.0; xe[1] = 10.0; auto func = [&y](const Vectord& x, const bool isSemiImplicit) -> const Vectord& { - // auto y = Vectord(x.size()); - y[0] = x[0] * x[0] - 1.0; - y[1] = x[1] * x[1] - 100.0; + // auto y = Vectord(x.size()); + y[0] = x[0] * x[0] - 1.0; + y[1] = x[1] * x[1] - 100.0; - return y; - }; + return y; + }; auto jac = [&A](const Vectord& x) -> const Matrixd& { - // auto A = Matrixd(x.size(), x.size()); - A(0, 0) = 2 * x[0]; - A(0, 1) = 0.0; - A(1, 0) = 0.0; - A(1, 1) = 2 * x[1]; + // auto A = Matrixd(x.size(), x.size()); + A(0, 0) = 2 * x[0]; + A(0, 1) = 0.0; + A(1, 0) = 0.0; + A(1, 1) = 2 * x[1]; - return A; - }; + return A; + }; auto updateX = [&x](const Vectord& du, const bool isSemiImplicit) - { - x -= du; - return; - }; + { + x -= du; + return; + }; auto updateXold = [](void) {}; @@ -84,8 +84,8 @@ main(int argc, char** argv) nlSolver->setSystem(nlSystem); nlSolver->setLinearSolver(linSolver); - std::cout << "init_error = " << std::setprecision(12) << std::scientific << (x-xe).norm() << std::endl; + std::cout << "init_error = " << std::setprecision(12) << std::scientific << (x - xe).norm() << std::endl; nlSolver->solve(); - std::cout << "final_error = " << std::setprecision(12) << std::scientific << (x-xe).norm() << std::endl; + std::cout << "final_error = " << std::setprecision(12) << std::scientific << (x - xe).norm() << std::endl; } diff --git a/Examples/ObjectControllerDummyClient/ObjectCtrlDummyClientExample.cpp b/Examples/ObjectControllerDummyClient/ObjectCtrlDummyClientExample.cpp index 87b0344db7294d62036c282e0f9aa48bbfc39362..632019b2aaa2cf9b8f9506b6c88b0ca63c1102f5 100644 --- a/Examples/ObjectControllerDummyClient/ObjectCtrlDummyClientExample.cpp +++ b/Examples/ObjectControllerDummyClient/ObjectCtrlDummyClientExample.cpp @@ -21,7 +21,6 @@ #include "imstkSimulationManager.h" #include "imstkCollidingObject.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" #include "imstkLight.h" #include "imstkSceneManager.h" diff --git a/Examples/PBD/PBDCloth/pbdClothExample.cpp b/Examples/PBD/PBDCloth/pbdClothExample.cpp index b1f99326c073190d53920319e6c88bbed074af63..30289cab718e0246749e2ef0f2df24ed285233da 100644 --- a/Examples/PBD/PBDCloth/pbdClothExample.cpp +++ b/Examples/PBD/PBDCloth/pbdClothExample.cpp @@ -24,7 +24,6 @@ #include "imstkPbdObject.h" #include "imstkAPIUtilities.h" #include "imstkSurfaceMesh.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" #include "imstkLight.h" #include "imstkScene.h" @@ -100,20 +99,19 @@ main() auto pbdParams = std::make_shared<PBDModelConfig>(); // Constraints - pbdParams->enableConstraint(PbdConstraint::Type::Distance, 0.1); - pbdParams->enableConstraint(PbdConstraint::Type::Dihedral, 0.001); - std::vector<size_t> fixedNodes(nCols); - for (size_t i = 0; i < fixedNodes.size(); i++) - { - fixedNodes[i] = i; - } + // pbdParams->enableConstraint(PbdConstraint::Type::Distance, 0.1); + // pbdParams->enableConstraint(PbdConstraint::Type::Dihedral, 1e2); + // pbdParams->m_solverType = PbdConstraint::SolverType::PBD; + pbdParams->enableConstraint(PbdConstraint::Type::Distance, 1e2); + pbdParams->enableConstraint(PbdConstraint::Type::Dihedral, 1e1); + std::vector<size_t> fixedNodes = { 0, nCols - 1 }; pbdParams->m_fixedNodeIds = fixedNodes; // Other parameters - pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -9.8, 0); - pbdParams->m_DefaultDt = 0.005; - pbdParams->m_maxIter = 5; + pbdParams->m_uniformMassValue = width * height / (nRows * nCols); + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_defaultDt = 0.005; + pbdParams->m_iterations = 5; // Set the parameters pbdModel->configure(pbdParams); diff --git a/Examples/PBD/PBDCollisionMultipleObjects/PBDCollisionMultipleObjectsExample.cpp b/Examples/PBD/PBDCollisionMultipleObjects/PBDCollisionMultipleObjectsExample.cpp index e620279b58b856420c1cfdea800110c2b56ec541..ed1e6083bdab7216fe910523c411c34c906dbf94 100644 --- a/Examples/PBD/PBDCollisionMultipleObjects/PBDCollisionMultipleObjectsExample.cpp +++ b/Examples/PBD/PBDCollisionMultipleObjects/PBDCollisionMultipleObjectsExample.cpp @@ -19,22 +19,18 @@ =========================================================================*/ -#include "imstkSimulationManager.h" -#include "imstkLight.h" #include "imstkCamera.h" #include "imstkCollisionGraph.h" -#include "imstkSceneManager.h" -#include "imstkPbdModel.h" -#include "imstkPbdObject.h" -#include "imstkTetrahedralMesh.h" +#include "imstkLight.h" #include "imstkMeshIO.h" +#include "imstkObjectInteractionFactory.h" #include "imstkOneToOneMap.h" -#include "imstkSurfaceMeshToSurfaceMeshCD.h" -#include "imstkPBDCollisionHandling.h" -#include "imstkVTKTextStatusManager.h" -#include "imstkCollisionData.h" -#include "imstkSurfaceMesh.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" #include "imstkScene.h" +#include "imstkSimulationManager.h" +#include "imstkSurfaceMesh.h" +#include "imstkTetrahedralMesh.h" // Enable this macro to generate many dragons #define BIG_SCENE @@ -124,17 +120,16 @@ generateDragon(const std::shared_ptr<imstk::Scene>& scene, auto pbdParams = std::make_shared<PBDModelConfig>(); // FEM constraint - pbdParams->m_YoungModulus = 1000.0; - pbdParams->m_PoissonRatio = 0.3; + pbdParams->m_femParams->m_YoungModulus = 1000.0; + pbdParams->m_femParams->m_PoissonRatio = 0.3; pbdParams->enableFEMConstraint(PbdConstraint::Type::FEMTet, PbdFEMConstraint::MaterialType::StVK); // Other parameters pbdParams->m_uniformMassValue = 5.0; - pbdParams->m_gravity = Vec3d(0, -1.0, 0); - pbdParams->m_DefaultDt = 0.01; - pbdParams->m_maxIter = 20; - pbdParams->m_proximity = 0.5; - pbdParams->m_contactStiffness = 0.1; + pbdParams->m_gravity = Vec3d(0, -1.0, 0); + pbdParams->m_defaultDt = 0.01; + pbdParams->m_iterations = 20; + pbdParams->collisionParams->m_proximity = 0.5; pbdModel->configure(pbdParams); deformableObj->setDynamicalModel(pbdModel); @@ -156,10 +151,6 @@ main() auto viewer = std::dynamic_pointer_cast<VTKViewer>(simManager->getViewer()); viewer->getVtkRenderWindow()->SetSize(1920, 1080); - auto statusManager = viewer->getTextStatusManager(); - statusManager->setStatusFontSize(VTKTextStatusManager::Custom, 25); - statusManager->setStatusFontColor(VTKTextStatusManager::Custom, Color::Orange); - // Build floor geometry const double width = 100.0; const double height = 100.0; @@ -194,48 +185,45 @@ main() } } - auto floorMesh = std::make_shared<SurfaceMesh>(); - floorMesh->initialize(vertList, triangles); - - auto materialFloor = std::make_shared<RenderMaterial>(); - materialFloor->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); - auto floorMeshModel = std::make_shared<VisualModel>(floorMesh); - floorMeshModel->setRenderMaterial(materialFloor); - - auto floor = std::make_shared<PbdObject>("Floor"); - floor->setCollidingGeometry(floorMesh); - floor->setVisualGeometry(floorMesh); - floor->setPhysicsGeometry(floorMesh); - - auto pbdModel2 = std::make_shared<PbdModel>(); - pbdModel2->setModelGeometry(floorMesh); - - // configure model - auto pbdParams2 = std::make_shared<PBDModelConfig>(); - pbdParams2->m_uniformMassValue = 0.0; - pbdParams2->m_proximity = 0.1; - pbdParams2->m_contactStiffness = 0.1; - pbdParams2->m_maxIter = 0; - - // Set the parameters - pbdModel2->configure(pbdParams2); - floor->setDynamicalModel(pbdModel2); - scene->addSceneObject(floor); + auto floorObj = std::make_shared<PbdObject>("Floor"); + { + auto floorMesh = std::make_shared<SurfaceMesh>(); + floorMesh->initialize(vertList, triangles); + + auto materialFloor = std::make_shared<RenderMaterial>(); + materialFloor->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); + auto floorMeshModel = std::make_shared<VisualModel>(floorMesh); + floorMeshModel->setRenderMaterial(materialFloor); + + floorObj->setCollidingGeometry(floorMesh); + floorObj->setVisualGeometry(floorMesh); + floorObj->setPhysicsGeometry(floorMesh); + + auto pbdFloorModel = std::make_shared<PbdModel>(); + pbdFloorModel->setModelGeometry(floorMesh); + + // Configure floor + auto pbdFloorConfig = std::make_shared<PBDModelConfig>(); + pbdFloorConfig->m_uniformMassValue = 0.0; + pbdFloorConfig->collisionParams->m_proximity = -0.1; + pbdFloorConfig->m_iterations = 0; + + // Set the parameters + pbdFloorModel->configure(pbdFloorConfig); + floorObj->setDynamicalModel(pbdFloorModel); + scene->addSceneObject(floorObj); + } #ifdef BIG_SCENE const int expandsXZ = 1; - const int expandsY = 4; + const int expandsY = 2; #endif const double shiftX = 5.0; const double distanceXZ = 10.0; const double distanceY = 5.0; const double minHeight = -5.0; - std::vector<std::shared_ptr<SurfaceMesh>> surfaceMeshes; - std::vector<std::shared_ptr<PbdObject>> pbdObjs; - //std::vector<std::shared_ptr<PbdSolver>> pbdSolvers; - std::vector<std::shared_ptr<CollisionData>> allCollisionData; - size_t numTriangles = 0; + std::vector<std::shared_ptr<PbdObject>> pbdObjs; #ifdef BIG_SCENE for (int i = -expandsXZ; i < expandsXZ; ++i) @@ -260,59 +248,23 @@ main() std::shared_ptr<PbdSolver> solver; Vec3d translation(shiftX + i * distanceXZ, minHeight + j * distanceY, k * distanceXZ); generateDragon(scene, translation, mesh, pbdObj, solver); - surfaceMeshes.push_back(mesh); pbdObjs.push_back(pbdObj); - //pbdSolvers.push_back(solver); - numTriangles += mesh->getNumTriangles(); - - // Collision between dragon with floor - const auto colData = std::make_shared<CollisionData>(); - const auto CD = std::make_shared<SurfaceMeshToSurfaceMeshCD>(mesh, floorMesh, colData); - const auto CH = std::make_shared<PBDCollisionHandling>(CollisionHandling::Side::A, - colData, pbdObj, floor); - scene->getCollisionGraph()->addInteractionPair(pbdObj, floor, CD, CH, nullptr); - allCollisionData.push_back(colData); + + scene->getCollisionGraph()->addInteraction(makeObjectInteractionPair(pbdObj, floorObj, + InteractionType::PbdObjToPbdObjCollision, CollisionDetection::Type::SurfaceMeshToSurfaceMesh)); } } } - for (size_t i = 0; i < surfaceMeshes.size(); ++i) + for (size_t i = 0; i < pbdObjs.size(); ++i) { - for (size_t j = 0; j < surfaceMeshes.size(); ++j) + for (size_t j = i + 1; j < pbdObjs.size(); ++j) { - if (i == j) - { - continue; - } - - // Collision between two dragons - const auto colData = std::make_shared<CollisionData>(); - const auto CD = std::make_shared<SurfaceMeshToSurfaceMeshCD>(surfaceMeshes[i], surfaceMeshes[j], colData); - const auto CH = std::make_shared<PBDCollisionHandling>(CollisionHandling::Side::A, - colData, pbdObjs[i], pbdObjs[j]); - scene->getCollisionGraph()->addInteractionPair(pbdObjs[i], pbdObjs[j], CD, CH, nullptr); + scene->getCollisionGraph()->addInteraction(makeObjectInteractionPair(pbdObjs[i], pbdObjs[j], + InteractionType::PbdObjToPbdObjCollision, CollisionDetection::Type::SurfaceMeshToSurfaceMesh)); } } - auto updateFunc = - [&](Module*) { - size_t numVTCollisions = 0; - size_t numEECollisions = 0; - for (const auto& colData: allCollisionData) - { - numVTCollisions += colData->VTColData.getSize(); - numEECollisions += colData->EEColData.getSize(); - } - - std::stringstream ss; - ss << "Num. dragons: " << surfaceMeshes.size() - << "\nNum. surface triangles: " << numTriangles - << "\nNum. collision: " << numVTCollisions << " (VT) | " - << numEECollisions << " (EE)"; - statusManager->setCustomStatus(ss.str()); - }; - simManager->getSceneManager(scene)->setPostUpdateCallback(updateFunc); - // Light auto light = std::make_shared<DirectionalLight>("light"); light->setFocalPoint(Vec3d(5, -8, -5)); diff --git a/Examples/PBD/PBDCollisionOneObject/PBDCollisionOneObjectExample.cpp b/Examples/PBD/PBDCollisionOneObject/PBDCollisionOneObjectExample.cpp index bff74bf450b6e20285e5fd7ca717a812695faff7..6e07581e8058f2fc50066c8f65682da82af48f28 100644 --- a/Examples/PBD/PBDCollisionOneObject/PBDCollisionOneObjectExample.cpp +++ b/Examples/PBD/PBDCollisionOneObject/PBDCollisionOneObjectExample.cpp @@ -19,20 +19,19 @@ =========================================================================*/ -#include "imstkSimulationManager.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" +#include "imstkCollisionGraph.h" #include "imstkLight.h" -#include "imstkPbdModel.h" -#include "imstkPbdObject.h" -#include "imstkTetrahedralMesh.h" #include "imstkMeshIO.h" +#include "imstkObjectInteractionFactory.h" #include "imstkOneToOneMap.h" -#include "imstkMeshToMeshBruteForceCD.h" -#include "imstkPBDCollisionHandling.h" -#include "imstkTetraTriangleMap.h" -#include "imstkSurfaceMesh.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" #include "imstkScene.h" +#include "imstkSimulationManager.h" +#include "imstkSurfaceMesh.h" +#include "imstkTaskGraphVizWriter.h" +#include "imstkTetraTriangleMap.h" using namespace imstk; @@ -44,8 +43,7 @@ int main() { auto simManager = std::make_shared<SimulationManager>(); - auto scene = simManager->createNewScene("PbdCollision"); - + auto scene = simManager->createNewScene("PbdCollisionOneDragon"); scene->getCamera()->setPosition(0, 3.0, 20.0); scene->getCamera()->setFocalPoint(0.0, -10.0, 0.0); @@ -84,18 +82,18 @@ main() auto pbdParams = std::make_shared<PBDModelConfig>(); // FEM constraint - pbdParams->m_YoungModulus = 1000.0; - pbdParams->m_PoissonRatio = 0.3; + pbdParams->m_femParams->m_YoungModulus = 1000.0; + pbdParams->m_femParams->m_PoissonRatio = 0.3; pbdParams->enableFEMConstraint(PbdConstraint::Type::FEMTet, PbdFEMConstraint::MaterialType::Corotation); // Other parameters pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -10.0, 0); - pbdParams->m_DefaultDt = 0.01; - pbdParams->m_maxIter = 5; - pbdParams->m_proximity = 0.3; - pbdParams->m_contactStiffness = 0.1; + pbdParams->m_gravity = Vec3d(0, -10.0, 0); + pbdParams->m_defaultDt = 0.01; + pbdParams->m_iterations = 5; + pbdParams->collisionParams->m_proximity = 0.3; + pbdParams->collisionParams->m_stiffness = 0.1; pbdModel->configure(pbdParams); deformableObj->setDynamicalModel(pbdModel); @@ -155,9 +153,8 @@ main() // configure model auto pbdParams2 = std::make_shared<PBDModelConfig>(); pbdParams2->m_uniformMassValue = 0.0; - pbdParams2->m_proximity = 0.1; - pbdParams2->m_contactStiffness = 1.0; - pbdParams2->m_maxIter = 0; + pbdParams2->m_iterations = 0; + pbdParams2->collisionParams->m_proximity = -0.1; // Set the parameters pbdModel2->configure(pbdParams2); @@ -166,10 +163,8 @@ main() scene->addSceneObject(floor); // Collision - scene->getCollisionGraph()->addInteractionPair(deformableObj, floor, - CollisionDetection::Type::MeshToMeshBruteForce, - CollisionHandling::Type::PBD, - CollisionHandling::Type::None); + scene->getCollisionGraph()->addInteraction(makeObjectInteractionPair(deformableObj, floor, + InteractionType::PbdObjToPbdObjCollision, CollisionDetection::Type::MeshToMeshBruteForce)); // Light auto light = std::make_shared<DirectionalLight>("light"); diff --git a/Examples/PBD/PBDCollisionStairs/PBDCollisionStairsExample.cpp b/Examples/PBD/PBDCollisionStairs/PBDCollisionStairsExample.cpp index c6b99cbd37d7d093f9704d354dcb22f08be9fca4..f14c7df8d337bc6187ba9ac0a515d049c55637bc 100644 --- a/Examples/PBD/PBDCollisionStairs/PBDCollisionStairsExample.cpp +++ b/Examples/PBD/PBDCollisionStairs/PBDCollisionStairsExample.cpp @@ -20,6 +20,8 @@ //=========================================================================*/ #include "imstkCamera.h" +#include "imstkCollisionGraph.h" +#include "imstkLight.h" #include "imstkMeshIO.h" #include "imstkMeshToMeshBruteForceCD.h" #include "imstkOneToOneMap.h" @@ -27,13 +29,12 @@ #include "imstkPbdModel.h" #include "imstkPbdObject.h" #include "imstkPbdSolver.h" +#include "imstkScene.h" #include "imstkSimulationManager.h" +#include "imstkSurfaceMesh.h" #include "imstkTetrahedralMesh.h" #include "imstkTetraTriangleMap.h" -#include "imstkCollisionGraph.h" -#include "imstkSurfaceMesh.h" -#include "imstkLight.h" -#include "imstkScene.h" +#include "imstkObjectInteractionFactory.h" using namespace imstk; @@ -92,6 +93,87 @@ buildStairs(int nSteps, double width, double height, double depth) return stairMesh; } +static std::shared_ptr<PbdObject> +makeDragonPbdObject(const std::string& name) +{ + auto pbdObj = std::make_shared<PbdObject>(name); + + // Read in the dragon mesh + auto highResSurfMesh = std::dynamic_pointer_cast<SurfaceMesh>(MeshIO::read(iMSTK_DATA_ROOT "/asianDragon/asianDragon.obj")); + auto coarseTetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(MeshIO::read(iMSTK_DATA_ROOT "/asianDragon/asianDragon.veg")); + highResSurfMesh->translate(Vec3d(0.0f, 10.0f, 0.0f), Geometry::TransformType::ApplyToData); + coarseTetMesh->translate(Vec3d(0.0f, 10.0f, 0.0f), Geometry::TransformType::ApplyToData); + auto coarseSurfMesh = std::make_shared<SurfaceMesh>(); + coarseTetMesh->extractSurfaceMesh(coarseSurfMesh, true); + + // Setup the Parameters + auto pbdParams = std::make_shared<PBDModelConfig>(); + pbdParams->m_femParams->m_YoungModulus = 1000.0; + pbdParams->m_femParams->m_PoissonRatio = 0.3; + pbdParams->enableFEMConstraint(PbdConstraint::Type::FEMTet, + PbdFEMConstraint::MaterialType::StVK); + pbdParams->m_uniformMassValue = 1.0; + pbdParams->m_gravity = Vec3d(0, -10.0, 0); + pbdParams->m_defaultDt = 0.01; + pbdParams->m_iterations = 10; + pbdParams->collisionParams->m_proximity = 0.3; + pbdParams->collisionParams->m_stiffness = 0.1; + + // Setup the Model + auto pbdModel = std::make_shared<PbdModel>(); + pbdModel->setModelGeometry(coarseTetMesh); + pbdModel->configure(pbdParams); + + // Setup the VisualModel + auto material = std::make_shared<RenderMaterial>(); + material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); + auto surfMeshModel = std::make_shared<VisualModel>(highResSurfMesh); + surfMeshModel->setRenderMaterial(material); + + // Setup the Object + pbdObj->addVisualModel(surfMeshModel); + pbdObj->setCollidingGeometry(coarseSurfMesh); + pbdObj->setPhysicsGeometry(coarseTetMesh); + pbdObj->setPhysicsToCollidingMap(std::make_shared<OneToOneMap>(coarseTetMesh, coarseSurfMesh)); + pbdObj->setPhysicsToVisualMap(std::make_shared<TetraTriangleMap>(coarseTetMesh, highResSurfMesh)); + pbdObj->setDynamicalModel(pbdModel); + + return pbdObj; +} + +static std::shared_ptr<PbdObject> +makeStairsPbdObject(const std::string& name, int numSteps, double width, double height, double depth) +{ + auto stairObj = std::make_shared<PbdObject>(name); + + std::shared_ptr<SurfaceMesh> stairMesh(std::move(buildStairs(numSteps, width, height, depth))); + + // Setup the parameters + auto pbdParams = std::make_shared<PBDModelConfig>(); + pbdParams->m_uniformMassValue = 0.0; + pbdParams->collisionParams->m_proximity = -0.1; + pbdParams->m_iterations = 0; + + // Setup the model + auto pbdModel = std::make_shared<PbdModel>(); + pbdModel->setModelGeometry(stairMesh); + pbdModel->configure(pbdParams); + + // Setup the VisualModel + auto stairMaterial = std::make_shared<RenderMaterial>(); + stairMaterial->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); + auto stairMeshModel = std::make_shared<VisualModel>(stairMesh); + stairMeshModel->setRenderMaterial(stairMaterial); + + stairObj->addVisualModel(stairMeshModel); + stairObj->setDynamicalModel(pbdModel); + stairObj->setCollidingGeometry(stairMesh); + stairObj->setVisualGeometry(stairMesh); + stairObj->setPhysicsGeometry(stairMesh); + + return stairObj; +} + /// /// \brief This example demonstrates the collision interaction /// using Position based dynamic on a more complex mesh @@ -100,97 +182,25 @@ int main() { auto simManager = std::make_shared<SimulationManager>(); - auto scene = simManager->createNewScene("PbdCollision"); + auto scene = simManager->createNewScene("PbdStairsCollision"); scene->getCamera()->setPosition(0.0, 0.0, -30.0); scene->getCamera()->setFocalPoint(0.0, 0.0, 0.0); - auto deformableObj = std::make_shared<PbdObject>("DeformableObj"); - { - // Read in the dragon mesh - auto highResSurfMesh = std::dynamic_pointer_cast<SurfaceMesh>(MeshIO::read(iMSTK_DATA_ROOT "/asianDragon/asianDragon.obj")); - auto coarseTetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(MeshIO::read(iMSTK_DATA_ROOT "/asianDragon/asianDragon.veg")); - highResSurfMesh->translate(Vec3d(0.0f, 10.0f, 0.0f), Geometry::TransformType::ApplyToData); - coarseTetMesh->translate(Vec3d(0.0f, 10.0f, 0.0f), Geometry::TransformType::ApplyToData); - auto coarseSurfMesh = std::make_shared<SurfaceMesh>(); - coarseTetMesh->extractSurfaceMesh(coarseSurfMesh, true); - - // Setup parameters - auto pbdParams = std::make_shared<PBDModelConfig>(); - pbdParams->m_YoungModulus = 1000.0; - pbdParams->m_PoissonRatio = 0.3; - pbdParams->enableFEMConstraint(PbdConstraint::Type::FEMTet, PbdFEMConstraint::MaterialType::StVK); - pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -10.0, 0); - pbdParams->m_DefaultDt = 0.01; - pbdParams->m_maxIter = 5; - pbdParams->m_proximity = 0.3; - pbdParams->m_contactStiffness = 0.08; - - // Setup Model - auto pbdModel = std::make_shared<PbdModel>(); - pbdModel->setModelGeometry(coarseTetMesh); - pbdModel->configure(pbdParams); - - // Setup VisualModel - auto material = std::make_shared<RenderMaterial>(); - material->setDisplayMode(RenderMaterial::DisplayMode::Surface); - auto surfMeshModel = std::make_shared<VisualModel>(highResSurfMesh); - surfMeshModel->setRenderMaterial(material); - - // Setup Object - deformableObj->addVisualModel(surfMeshModel); - deformableObj->setCollidingGeometry(coarseSurfMesh); - deformableObj->setPhysicsGeometry(coarseTetMesh); - deformableObj->setPhysicsToCollidingMap(std::make_shared<OneToOneMap>(coarseTetMesh, coarseSurfMesh)); - deformableObj->setPhysicsToVisualMap(std::make_shared<TetraTriangleMap>(coarseTetMesh, highResSurfMesh)); - deformableObj->setDynamicalModel(pbdModel); - - // Add to scene - scene->addSceneObject(deformableObj); - } + // Create and add the dragon to the scene + auto pbdDragon1 = makeDragonPbdObject("PbdDragon1"); + scene->addSceneObject(pbdDragon1); - auto stairObj = std::make_shared<PbdObject>("Floor"); - { - std::shared_ptr<SurfaceMesh> stairMesh(std::move(buildStairs(15, 20.0, 10.0, 20.0))); - - // Setup parameters - auto pbdParams2 = std::make_shared<PBDModelConfig>(); - pbdParams2->m_uniformMassValue = 0.0; - pbdParams2->m_proximity = 0.1; - pbdParams2->m_contactStiffness = 0.08; - pbdParams2->m_maxIter = 0; - - // Setup model - auto pbdModel2 = std::make_shared<PbdModel>(); - pbdModel2->setModelGeometry(stairMesh); - pbdModel2->configure(pbdParams2); - - // Setup VisualModel - auto stairMaterial = std::make_shared<RenderMaterial>(); - stairMaterial->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); - stairMaterial->setEdgeColor(Color::Black); - auto stairMeshModel = std::make_shared<VisualModel>(stairMesh); - stairMeshModel->setRenderMaterial(stairMaterial); - - stairObj->addVisualModel(stairMeshModel); - stairObj->setDynamicalModel(pbdModel2); - stairObj->setCollidingGeometry(stairMesh); - stairObj->setVisualGeometry(stairMesh); - stairObj->setPhysicsGeometry(stairMesh); - - scene->addSceneObject(stairObj); - } + auto stairObj = makeStairsPbdObject("PbdStairs", 12, 20.0, 10.0, 20.0); + scene->addSceneObject(stairObj); // Collision - scene->getCollisionGraph()->addInteractionPair(deformableObj, stairObj, - CollisionDetection::Type::SurfaceMeshToSurfaceMesh, - CollisionHandling::Type::PBD, - CollisionHandling::Type::None); + scene->getCollisionGraph()->addInteraction(makeObjectInteractionPair(pbdDragon1, stairObj, + InteractionType::PbdObjToPbdObjCollision, CollisionDetection::Type::MeshToMeshBruteForce)); // Light - auto light = std::make_shared<DirectionalLight>("light"); - light->setFocalPoint(Vec3d(5, -8, 5)); - light->setIntensity(1.2); + auto light = std::make_shared<DirectionalLight>("Light"); + light->setFocalPoint(Vec3d(5.0, -8.0, 5.0)); + light->setIntensity(1.0); scene->addLight(light); auto light2 = std::make_shared<DirectionalLight>("light 2"); diff --git a/Examples/PBD/PBDDeformableObject/PBD3DDeformableObject.cpp b/Examples/PBD/PBDDeformableObject/PBD3DDeformableObject.cpp index aedde8a7dcc6b6a878977a39b19dfbaefc9fb28e..925ae79cce40699377c0c54e960e9ecf21e727f9 100644 --- a/Examples/PBD/PBDDeformableObject/PBD3DDeformableObject.cpp +++ b/Examples/PBD/PBDDeformableObject/PBD3DDeformableObject.cpp @@ -27,7 +27,6 @@ #include "imstkOneToOneMap.h" #include "imstkAPIUtilities.h" #include "imstkTetraTriangleMap.h" -#include "imstkCollisionGraph.h" #include "imstkSurfaceMesh.h" #include "imstkCamera.h" #include "imstkPlane.h" @@ -67,15 +66,16 @@ main() auto pbdParams = std::make_shared<PBDModelConfig>(); // FEM constraint - pbdParams->m_YoungModulus = 100.0; - pbdParams->m_PoissonRatio = 0.3; - pbdParams->m_fixedNodeIds = { 51, 127, 178 }; + pbdParams->m_femParams->m_YoungModulus = 1000.0; + pbdParams->m_femParams->m_PoissonRatio = 0.3; + pbdParams->m_fixedNodeIds = { 50, 126, 177 }; pbdParams->enableFEMConstraint(PbdConstraint::Type::FEMTet, PbdFEMConstraint::MaterialType::StVK); // Other parameters pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -9.8, 0); - pbdParams->m_maxIter = 45; + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_iterations = 15; + // pbdParams->m_solverType = PbdConstraint::SolverType::PBD; // Set the parameters pbdModel->configure(pbdParams); @@ -87,8 +87,6 @@ main() deformableObj->setPhysicsGeometry(tetMesh); deformableObj->setPhysicsToVisualMap(map); //assign the computed map - deformableObj->setDynamicalModel(pbdModel); - scene->addSceneObject(deformableObj); // Setup plane diff --git a/Examples/PBD/PBDFluids/PBDFluidsExample.cpp b/Examples/PBD/PBDFluids/PBDFluidsExample.cpp index d7eaf3870a28f85c1ca3abb92daf121883e6fbde..4af5e4ce16d5db25090b6df7732351326983afaa 100644 --- a/Examples/PBD/PBDFluids/PBDFluidsExample.cpp +++ b/Examples/PBD/PBDFluids/PBDFluidsExample.cpp @@ -19,18 +19,19 @@ =========================================================================*/ -#include "imstkSimulationManager.h" -#include "imstkCollisionGraph.h" +#include "imstkAPIUtilities.h" #include "imstkCamera.h" +#include "imstkCollisionGraph.h" #include "imstkLight.h" #include "imstkMeshIO.h" -#include "imstkPbdModel.h" -#include "imstkPbdObject.h" -#include "imstkAPIUtilities.h" #include "imstkMeshToMeshBruteForceCD.h" +#include "imstkObjectInteractionFactory.h" #include "imstkPBDCollisionHandling.h" -#include "imstkSurfaceMesh.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" #include "imstkScene.h" +#include "imstkSimulationManager.h" +#include "imstkSurfaceMesh.h" using namespace imstk; @@ -76,11 +77,10 @@ main() // Other parameters pbdParams->m_uniformMassValue = 1.0; - pbdParams->m_gravity = Vec3d(0, -9.8, 0); - pbdParams->m_DefaultDt = 0.01; - pbdParams->m_maxIter = 2; - pbdParams->m_proximity = 0.01; - pbdParams->m_contactStiffness = 0.2; + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_defaultDt = 0.01; + pbdParams->m_iterations = 2; + pbdParams->collisionParams->m_proximity = 0.01; // Set the parameters pbdModel->configure(pbdParams); @@ -212,8 +212,7 @@ main() // Configure model auto pbdParams2 = std::make_shared<PBDModelConfig>(); pbdParams2->m_uniformMassValue = 0.0; - pbdParams2->m_proximity = 0.1; - pbdParams2->m_contactStiffness = 1.0; + pbdParams2->collisionParams->m_proximity = 0.1; pbdModel2->configure(pbdParams2); floor->setDynamicalModel(pbdModel2); @@ -221,11 +220,8 @@ main() scene->addSceneObject(floor); // Collisions - auto colGraph = scene->getCollisionGraph(); - auto CD = std::make_shared<MeshToMeshBruteForceCD>(fluidMesh, floorMeshColliding, nullptr); - auto CH = std::make_shared<PBDCollisionHandling>(CollisionHandling::Side::A, - CD->getCollisionData(), deformableObj, floor); - colGraph->addInteractionPair(deformableObj, floor, CD, CH, nullptr); + scene->getCollisionGraph()->addInteraction(makeObjectInteractionPair(deformableObj, floor, + InteractionType::PbdObjToPbdObjCollision, CollisionDetection::Type::MeshToMeshBruteForce)); // Light (white) auto whiteLight = std::make_shared<DirectionalLight>("whiteLight"); diff --git a/Examples/PBD/PBDString/pbdStringExample.cpp b/Examples/PBD/PBDString/pbdStringExample.cpp index 563eb90797418dbdd834fe2d53af45000ef926c2..6295471f383bbb1f5a123767a8b6cb9a39a93a6f 100644 --- a/Examples/PBD/PBDString/pbdStringExample.cpp +++ b/Examples/PBD/PBDString/pbdStringExample.cpp @@ -25,7 +25,6 @@ #include "imstkPbdObject.h" #include "imstkSimulationManager.h" #include "imstkSceneManager.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" #include "imstkScene.h" @@ -92,13 +91,13 @@ main() // Configure the parameters with bend stiffnesses varying from 0.001 to ~0.1 sims[i].params = std::make_shared<PBDModelConfig>(); - sims[i].params->enableConstraint(PbdConstraint::Type::Distance, 0.001); - sims[i].params->enableConstraint(PbdConstraint::Type::Bend, static_cast<double>(i) * 0.1 / numStrings + 0.001); + sims[i].params->enableConstraint(PbdConstraint::Type::Distance, 1e7); + sims[i].params->enableConstraint(PbdConstraint::Type::Bend, (static_cast<double>(i) * 0.1 / numStrings + 0.001) * 1e6); sims[i].params->m_fixedNodeIds = { 0 }; sims[i].params->m_uniformMassValue = 5.0; - sims[i].params->m_gravity = Vec3d(0, -9.8, 0); - sims[i].params->m_DefaultDt = 0.0005; - sims[i].params->m_maxIter = 5; + sims[i].params->m_gravity = Vec3d(0, -9.8, 0); + sims[i].params->m_defaultDt = 0.0005; + sims[i].params->m_iterations = 5; // Set the parameters sims[i].model->configure(sims[i].params); diff --git a/Examples/ReducedFEM/CMakeLists.txt b/Examples/ReducedFEM/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f7cb1579910ced2759b1fb92c82b46f9a7169bf4 --- /dev/null +++ b/Examples/ReducedFEM/CMakeLists.txt @@ -0,0 +1,36 @@ +########################################################################### +# +# Copyright (c) Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +project(Example-ReducedFEM) + +if (${iMSTK_USE_MODEL_REDUCTION}) + #----------------------------------------------------------------------------- + # Create executable + #----------------------------------------------------------------------------- + imstk_add_executable(${PROJECT_NAME} ReducedFEM.cpp) + + #----------------------------------------------------------------------------- + # Add the target to Examples folder + #----------------------------------------------------------------------------- + SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples) + + #----------------------------------------------------------------------------- + # Link libraries to executable + #----------------------------------------------------------------------------- + target_link_libraries(${PROJECT_NAME} SimulationManager apiUtilities) +endif() \ No newline at end of file diff --git a/Examples/ReducedFEM/ReducedFEM.cpp b/Examples/ReducedFEM/ReducedFEM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f8c9bfea57234fb6bf4274634e4df150266ec9e --- /dev/null +++ b/Examples/ReducedFEM/ReducedFEM.cpp @@ -0,0 +1,129 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkTimer.h" +#include "imstkSimulationManager.h" +#include "imstkDeformableObject.h" +#include "imstkBackwardEuler.h" +#include "imstkNonLinearSystem.h" +#include "imstkNewtonSolver.h" +#include "imstkGaussSeidel.h" +#include "imstkPlane.h" +#include "imstkTetrahedralMesh.h" +#include "imstkMeshIO.h" +#include "imstkOneToOneMap.h" +#include "imstkAPIUtilities.h" +#include "imstkConjugateGradient.h" +#include "imstkLight.h" +#include "imstkCamera.h" +#include "imstkReducedStVKBodyModel.h" +#include "imstkReducedFeDeformableObject.h" +#include "imstkCollisionGraph.h" +#include "imstkSurfaceMesh.h" +#include "imstkScene.h" + +using namespace imstk; + +/// +/// \brief This example demonstrates the soft body simulation +/// using Finite elements +/// +int +main() +{ + // simManager and Scene + auto simConfig = std::make_shared<SimManagerConfig>(); + simConfig->simulationMode = SimulationMode::Rendering; + auto simManager = std::make_shared<SimulationManager>(simConfig); + auto scene = simManager->createNewScene("DeformableBodyFEM"); + scene->getCamera()->setPosition(0, 2.0, 15.0); + + // Load a tetrahedral mesh + // auto tetMesh = MeshIO::read(iMSTK_DATA_ROOT "/asianDragon/asianDragon.veg"); + auto tetMesh = MeshIO::read(iMSTK_DATA_ROOT "/asianDragon/asianDragon.veg"); + + CHECK(tetMesh != nullptr) << "Could not read mesh from file."; + + // Extract the surface mesh + auto surfMesh = std::make_shared<SurfaceMesh>(); + auto volTetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(tetMesh); + + CHECK(volTetMesh != nullptr) << "Dynamic pointer cast from PointSet to TetrahedralMesh failed!"; + + volTetMesh->extractSurfaceMesh(surfMesh, true); + + // Construct a map + + // Construct one to one nodal map based on the above meshes + auto oneToOneNodalMap = std::make_shared<OneToOneMap>(tetMesh, surfMesh); + + // Scene object 1: Dragon + + // Configure dynamic model + auto dynaModel = std::make_shared<ReducedStVK>(); + + //dynaModel->configure(iMSTK_DATA_ROOT "/asianDragon/asianDragon.config"); + + auto config = std::make_shared<ReducedStVKConfig>(); + // config->m_fixedNodeIds = { 51, 127, 178 }; + config->m_cubicPolynomialFilename = iMSTK_DATA_ROOT "/asianDragon/asianDragon.cub"; + config->m_modesFileName = iMSTK_DATA_ROOT "/asianDragon/asianDragon.URendering.float"; + dynaModel->configure(config); + + dynaModel->setTimeStepSizeType(TimeSteppingType::Fixed); + dynaModel->setModelGeometry(volTetMesh); + auto timeIntegrator = std::make_shared<BackwardEuler>(0.01);// Create and add Backward Euler time integrator + dynaModel->setTimeIntegrator(timeIntegrator); + + auto material = std::make_shared<RenderMaterial>(); + material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); + auto surfMeshModel = std::make_shared<VisualModel>(surfMesh); + surfMeshModel->setRenderMaterial(material); + + // Scene Object + auto deformableObj = std::make_shared<ReducedFeDeformableObject>("Dragon"); + deformableObj->addVisualModel(surfMeshModel); + deformableObj->setPhysicsGeometry(volTetMesh); + deformableObj->setPhysicsToVisualMap(oneToOneNodalMap); //assign the computed map + deformableObj->setDynamicalModel(dynaModel); + scene->addSceneObject(deformableObj); + + // Scene object 2: Plane + auto planeGeom = std::make_shared<Plane>(); + planeGeom->setWidth(40); + planeGeom->setPosition(0, -6, 0); + auto planeObj = std::make_shared<CollidingObject>("Plane"); + planeObj->setVisualGeometry(planeGeom); + planeObj->setCollidingGeometry(planeGeom); + scene->addSceneObject(planeObj); + + // Light + auto light = std::make_shared<DirectionalLight>("light"); + light->setFocalPoint(Vec3d(5, -8, -5)); + light->setIntensity(1); + scene->addLight(light); + + // Run the simulation + simManager->setActiveScene(scene); + simManager->start(SimulationStatus::Paused); + + return 0; +} diff --git a/Examples/Rendering/RenderingExample.cpp b/Examples/Rendering/RenderingExample.cpp index f2e9d4d41e3b250fe442dd69265a21d8f79988db..736aa9553ac80d89a71faa5c20398929b0ece2e0 100644 --- a/Examples/Rendering/RenderingExample.cpp +++ b/Examples/Rendering/RenderingExample.cpp @@ -22,7 +22,6 @@ #include "imstkSimulationManager.h" #include "imstkAPIUtilities.h" #include "imstkVisualObjectImporter.h" -#include "imstkCollisionGraph.h" #include "imstkIBLProbe.h" #include "imstkCamera.h" #include "imstkMeshIO.h" diff --git a/Examples/SPHFluid/Fluid.hpp b/Examples/SPHFluid/Fluid.hpp index 7f3c47183426bfd323e4800c4fce09a926f8510e..04a4fc3410b14ddec024020139585343e157281c 100644 --- a/Examples/SPHFluid/Fluid.hpp +++ b/Examples/SPHFluid/Fluid.hpp @@ -21,7 +21,6 @@ #include "imstkSimulationManager.h" #include "imstkSPHObject.h" -#include "imstkSPHSolver.h" #include "imstkSPHModel.h" #include "imstkPointSet.h" #include "imstkScene.h" @@ -176,10 +175,5 @@ generateFluid(const std::shared_ptr<Scene>& scene, const double particleRadius) fluidObj->setPhysicsGeometry(fluidGeometry); // TODO: Look into API duplication and resulting conflicts scene->addSceneObject(fluidObj); - // Configure the solver - auto sphSolver = std::make_shared<SPHSolver>(); - sphSolver->setSPHObject(fluidObj); - scene->addNonlinearSolver(sphSolver); - return fluidObj; } diff --git a/Examples/SPHFluid/SPHFluidExample.hpp b/Examples/SPHFluid/SPHFluidExample.hpp index 8ca080ff2b16c484e1b62982cf2d662ff00cec62..b69c04ea0674eb321ccc70c0225e9fdb8ce97ebf 100644 --- a/Examples/SPHFluid/SPHFluidExample.hpp +++ b/Examples/SPHFluid/SPHFluidExample.hpp @@ -31,6 +31,8 @@ #include "imstkCollisionGraph.h" #include "imstkSceneManager.h" #include "imstkCamera.h" +#include "imstkObjectInteractionFactory.h" +#include "imstkCollisionDetection.h" #include "Fluid.hpp" #include "Solid.hpp" @@ -101,23 +103,18 @@ main(int argc, char* argv[]) }); // Collision between fluid and solid objects - auto colGraph = scene->getCollisionGraph(); - + std::shared_ptr<CollisionGraph> collisionGraph = scene->getCollisionGraph(); for (auto& solid: solids) { if (std::dynamic_pointer_cast<Plane>(solid->getCollidingGeometry())) { - colGraph->addInteractionPair(fluidObj, solid, - CollisionDetection::Type::PointSetToPlane, - CollisionHandling::Type::SPH, - CollisionHandling::Type::None); + collisionGraph->addInteraction(makeObjectInteractionPair(fluidObj, solid, + InteractionType::SphObjToCollidingObjCollision, CollisionDetection::Type::PointSetToPlane)); } else if (std::dynamic_pointer_cast<Sphere>(solid->getCollidingGeometry())) { - colGraph->addInteractionPair(fluidObj, solid, - CollisionDetection::Type::PointSetToSphere, - CollisionHandling::Type::SPH, - CollisionHandling::Type::None); + collisionGraph->addInteraction(makeObjectInteractionPair(fluidObj, solid, + InteractionType::SphObjToCollidingObjCollision, CollisionDetection::Type::PointSetToSphere)); } else { diff --git a/Examples/SceneManagement/SceneManagementExample.cpp b/Examples/SceneManagement/SceneManagementExample.cpp index 45418a5f9d8cd4ec668ac39e7da2267528f3ed41..f0c4668a32b388d3994f06cae739ec753519ccd0 100644 --- a/Examples/SceneManagement/SceneManagementExample.cpp +++ b/Examples/SceneManagement/SceneManagementExample.cpp @@ -19,10 +19,10 @@ =========================================================================*/ -#include "imstkSimulationManager.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" +#include "imstkCollisionGraph.h" #include "imstkScene.h" +#include "imstkSimulationManager.h" using namespace imstk; diff --git a/Examples/Screenshot/ScreenshotExample.cpp b/Examples/Screenshot/ScreenshotExample.cpp index b34ce731ddc63cf47514588b4766097ef0c6e3cb..9b88ed1ac828d23e7b2d9f4cca453a3d41cb9f6b 100644 --- a/Examples/Screenshot/ScreenshotExample.cpp +++ b/Examples/Screenshot/ScreenshotExample.cpp @@ -21,7 +21,6 @@ #include "imstkSimulationManager.h" #include "imstkSceneObject.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" #include "imstkLight.h" #include "imstkCube.h" diff --git a/Examples/TaskGraph/CMakeLists.txt b/Examples/TaskGraph/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..964a9fe46555e89800ccc1ca30875fc8ac896dab --- /dev/null +++ b/Examples/TaskGraph/CMakeLists.txt @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Add Example subdirectories +#----------------------------------------------------------------------------- +macro(listOfSubDir result curdir) + file(GLOB children RELATIVE ${curdir} ${curdir}/*) + set(dirlist "") + foreach(child ${children}) + if(IS_DIRECTORY ${curdir}/${child}) + list(APPEND dirlist ${child}) + endif() + endforeach() + set(${result} ${dirlist}) +endmacro() + +listOfSubDir(subDirs ${CMAKE_CURRENT_SOURCE_DIR}) + +foreach(subdir ${subDirs}) + add_subdirectory(${subdir}) +endforeach() + + diff --git a/Examples/TaskGraph/Configuration/CMakeLists.txt b/Examples/TaskGraph/Configuration/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..09512ba38eb3a3f85dd128ed4793b9d215eafa96 --- /dev/null +++ b/Examples/TaskGraph/Configuration/CMakeLists.txt @@ -0,0 +1,34 @@ +########################################################################### +# +# Copyright (c) Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +project(Example-TaskGraphConfigure) + +#----------------------------------------------------------------------------- +# Create executable +#----------------------------------------------------------------------------- +imstk_add_executable(${PROJECT_NAME} taskGraphConfigureExample.cpp) + +#----------------------------------------------------------------------------- +# Add the target to Examples folder +#----------------------------------------------------------------------------- +SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples/TaskGraph) + +#----------------------------------------------------------------------------- +# Link libraries to executable +#----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} SimulationManager apiUtilities) diff --git a/Examples/TaskGraph/Configuration/taskGraphConfigureExample.cpp b/Examples/TaskGraph/Configuration/taskGraphConfigureExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cbfc8178d9da8e1d46a25d73a70e60df1bc9230 --- /dev/null +++ b/Examples/TaskGraph/Configuration/taskGraphConfigureExample.cpp @@ -0,0 +1,206 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkAPIUtilities.h" +#include "imstkCamera.h" +#include "imstkLight.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" +#include "imstkScene.h" +#include "imstkSimulationManager.h" +#include "imstkSurfaceMesh.h" +#include "imstkTaskGraph.h" +#include "imstkTaskGraphVizWriter.h" + +using namespace imstk; + +static std::unique_ptr<SurfaceMesh> +makeCloth( + const double width, const double height, const int nRows, const int nCols) +{ + // Create surface mesh + std::unique_ptr<SurfaceMesh> clothMesh = std::make_unique<SurfaceMesh>(); + StdVectorOfVec3d vertList; + + vertList.resize(nRows * nCols); + const double dy = width / (double)(nCols - 1); + const double dx = height / (double)(nRows - 1); + for (int i = 0; i < nRows; ++i) + { + for (int j = 0; j < nCols; j++) + { + vertList[i * nCols + j] = Vec3d((double)dx * i, 1.0, (double)dy * j); + } + } + clothMesh->setInitialVertexPositions(vertList); + clothMesh->setVertexPositions(vertList); + + // Add connectivity data + std::vector<SurfaceMesh::TriangleArray> triangles; + for (std::size_t i = 0; i < nRows - 1; ++i) + { + for (std::size_t j = 0; j < nCols - 1; j++) + { + SurfaceMesh::TriangleArray tri[2]; + const size_t index1 = i * nCols + j; + const size_t index2 = index1 + nCols; + const size_t index3 = index1 + 1; + const size_t index4 = index2 + 1; + + // Interleave [/][\] + if (i % 2 ^ j % 2) + { + tri[0] = { { index1, index2, index3 } }; + tri[1] = { { index4, index3, index2 } }; + } + else + { + tri[0] = { { index2, index4, index1 } }; + tri[1] = { { index4, index3, index1 } }; + } + triangles.push_back(tri[0]); + triangles.push_back(tri[1]); + } + } + + clothMesh->setTrianglesVertices(triangles); + + return clothMesh; +} + +static std::shared_ptr<PbdObject> +makeClothObj(const std::string& name, double width, double height, int nRows, int nCols) +{ + auto clothObj = std::make_shared<PbdObject>(name); + + std::shared_ptr<SurfaceMesh> clothMesh(std::move(makeCloth(width, height, nRows, nCols))); + + // Setup the Parameters + auto pbdParams = std::make_shared<PBDModelConfig>(); + pbdParams->m_uniformMassValue = 1.0; + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_defaultDt = 0.005; + pbdParams->m_iterations = 5; + pbdParams->m_viscousDampingCoeff = 0.05; + pbdParams->enableConstraint(PbdConstraint::Type::Distance, 0.1); + pbdParams->enableConstraint(PbdConstraint::Type::Dihedral, 0.001); + std::vector<size_t> fixedNodes(nCols); + for (size_t i = 0; i < fixedNodes.size(); i++) + { + fixedNodes[i] = i; + } + pbdParams->m_fixedNodeIds = fixedNodes; + + // Setup the Model + auto pbdModel = std::make_shared<PbdModel>(); + pbdModel->setModelGeometry(clothMesh); + pbdModel->configure(pbdParams); + + // Setup the VisualModel + auto material = std::make_shared<RenderMaterial>(); + material->setBackFaceCulling(false); + material->setColor(Color::LightGray); + material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); + + auto clothVisualModel = std::make_shared<VisualModel>(clothMesh); + clothVisualModel->setRenderMaterial(material); + + // Setup the Object + clothObj->addVisualModel(clothVisualModel); + clothObj->setPhysicsGeometry(clothMesh); + clothObj->setDynamicalModel(pbdModel); + + return clothObj; +} + +/// +/// \brief This example demonstrates how to modify the task graph +/// +int +main() +{ + auto simManager = std::make_shared<SimulationManager>(); + auto scene = simManager->createNewScene("PBDCloth"); + + const double width = 10.0; + const double height = 10.0; + const int nRows = 16; + const int nCols = 16; + std::shared_ptr<PbdObject> clothObj = makeClothObj("Cloth", width, height, nRows, nCols); + scene->addSceneObject(clothObj); + + // Light (white) + auto whiteLight = std::make_shared<DirectionalLight>("whiteLight"); + whiteLight->setFocalPoint(Vec3d(5, -8, -5)); + whiteLight->setIntensity(7); + scene->addLight(whiteLight); + + // Light (red) + auto colorLight = std::make_shared<SpotLight>("colorLight"); + colorLight->setPosition(Vec3d(-5, -3, 5)); + colorLight->setFocalPoint(Vec3d(0, -5, 5)); + colorLight->setIntensity(100); + colorLight->setColor(Color::Red); + colorLight->setSpotAngle(30); + scene->addLight(colorLight); + + // Adjust camera + scene->getCamera()->setFocalPoint(0, -5, 5); + scene->getCamera()->setPosition(-15., -5.0, 15.0); + + // Adds a custom physics step to print out intermediate velocities + { + std::shared_ptr<PbdModel> pbdModel = clothObj->getPbdModel(); + scene->setTaskGraphConfigureCallback([&](Scene* scene) + { + // Get the graph + std::shared_ptr<TaskGraph> graph = scene->getTaskGraph(); + + // First write the graph before we make modifications, just to show the changes + TaskGraphVizWriter writer; + writer.setInput(graph); + writer.setFileName("taskGraphConfigureExampleOld.svg"); + writer.write(); + + std::shared_ptr<TaskNode> printVelocities = std::make_shared<TaskNode>([&]() + { + const StdVectorOfVec3d& velocities = *pbdModel->getCurrentState()->getVelocities(); + for (size_t i = 0; i < velocities.size(); i++) + { + printf("Velocity: %f, %f, %f\n", velocities[i].x(), velocities[i].y(), velocities[i].z()); + } + }, "PrintVelocities"); + + // After IntegratePosition + graph->insertAfter(pbdModel->getIntegratePositionNode(), printVelocities); + + // Write the modified graph + writer.setFileName("taskGraphConfigureExampleNew.svg"); + writer.write(); + }); + } + + // Start + simManager->setActiveScene(scene); + simManager->start(SimulationStatus::Paused); + + return 0; +} diff --git a/Examples/TaskGraph/TaskGraph/CMakeLists.txt b/Examples/TaskGraph/TaskGraph/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e51e2e76989b6317c48160e5dfc264cd977697b0 --- /dev/null +++ b/Examples/TaskGraph/TaskGraph/CMakeLists.txt @@ -0,0 +1,34 @@ +########################################################################### +# +# Copyright (c) Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +project(Example-TaskGraph) + +#----------------------------------------------------------------------------- +# Create executable +#----------------------------------------------------------------------------- +imstk_add_executable(${PROJECT_NAME} taskGraphExample.cpp) + +#----------------------------------------------------------------------------- +# Add the target to Examples folder +#----------------------------------------------------------------------------- +SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples/TaskGraph) + +#----------------------------------------------------------------------------- +# Link libraries to executable +#----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} SimulationManager apiUtilities) diff --git a/Examples/TaskGraph/TaskGraph/taskGraphExample.cpp b/Examples/TaskGraph/TaskGraph/taskGraphExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c05cf60888a790e16c442768eef771f22410ca16 --- /dev/null +++ b/Examples/TaskGraph/TaskGraph/taskGraphExample.cpp @@ -0,0 +1,107 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkParallelFor.h" +#include "imstkSequentialTaskGraphController.h" +#include "imstkTaskGraph.h" +#include "imstkTbbTaskGraphController.h" +#include <iostream> + +using namespace imstk; + +/// +/// \brief This example how to use a TaskGraph standalone +/// +int +main() +{ + const bool runExampleInParallel = true; + + // Initialize some data arrays + const int countA = 100; + const int countB = 255; + int x[countA]; + int y[countA]; + int w[countB]; + int z[countB]; + + for (int i = 0; i < 100; i++) + { + x[i] = i + 5; + y[i] = i * 6 + 1; + } + for (int i = 0; i < 255; i++) + { + w[i] = i % 10; + z[i] = i; + } + + int sumA = 0; + int sumB = 0; + + // Now create a graph that computes results = sum_i(x_i + y_i) * sum_i(w_i * z_i) + std::shared_ptr<TaskGraph> graph = std::make_shared<TaskGraph>(); + + // Create and add the nodes + std::shared_ptr<TaskNode> addNode = graph->addFunction("Add Step", [&]() + { + for (int i = 0; i < countA; i++) + { + sumA += (x[i] + y[i]); + } + }); + std::shared_ptr<TaskNode> multNode = graph->addFunction("Mult Step", [&]() + { + for (int i = 0; i < countB; i++) + { + sumB += (w[i] * z[i]); + } + }); + + // Define the edges, add and mult steps will be done in parallel + graph->addEdge(graph->getSource(), addNode); + graph->addEdge(graph->getSource(), multNode); + graph->addEdge(addNode, graph->getSink()); + graph->addEdge(multNode, graph->getSink()); + + // Set which controller to use + std::shared_ptr<TaskGraphController> controller = nullptr; + if (runExampleInParallel) + { + controller = std::make_shared<TbbTaskGraphController>(); + } + else + { + controller = std::make_shared<SequentialTaskGraphController>(); + } + + // Compute + controller->setTaskGraph(graph); + if (!controller->initialize()) + { + std::cout << "TaskGraph failed to initialize" << std::endl; + return 1; + } + controller->execute(); + std::cout << "Results: " << sumA + sumB << std::endl; + + return 0; +} diff --git a/Examples/TaskGraph/Timing/CMakeLists.txt b/Examples/TaskGraph/Timing/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ea4991855fcfe14cb594f62e1370b62de19b2eff --- /dev/null +++ b/Examples/TaskGraph/Timing/CMakeLists.txt @@ -0,0 +1,34 @@ +########################################################################### +# +# Copyright (c) Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +project(Example-TaskGraphTiming) + +#----------------------------------------------------------------------------- +# Create executable +#----------------------------------------------------------------------------- +imstk_add_executable(${PROJECT_NAME} taskGraphTimingExample.cpp) + +#----------------------------------------------------------------------------- +# Add the target to Examples folder +#----------------------------------------------------------------------------- +SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples/TaskGraph) + +#----------------------------------------------------------------------------- +# Link libraries to executable +#----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} SimulationManager apiUtilities) diff --git a/Examples/TaskGraph/Timing/taskGraphTimingExample.cpp b/Examples/TaskGraph/Timing/taskGraphTimingExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9f7862660cb6417b2f594442b9f75020a27b88a --- /dev/null +++ b/Examples/TaskGraph/Timing/taskGraphTimingExample.cpp @@ -0,0 +1,160 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkAPIUtilities.h" +#include "imstkCamera.h" +#include "imstkLineMesh.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" +#include "imstkScene.h" +#include "imstkSceneManager.h" +#include "imstkSimulationManager.h" +#include "imstkTaskGraphVizWriter.h" + +using namespace imstk; + +/// +/// \brief This examples uses the timing features of the task graph This allows one +/// to see the elapsed time of every step of iMSTK as well as export the computational +/// graph and show information such as the critical path +/// +int +main() +{ + auto simManager = std::make_shared<SimulationManager>(); + auto scene = simManager->createNewScene("PBDString"); + scene->getConfig()->taskTimingEnabled = true; + + // Setup N separate string simulations with varying bend stiffnesses + const unsigned int numStrings = 8; + const unsigned int numVerts = 30; + const double stringSpacing = 2.0; // How far each string is apart + const double stringLength = 10.0; // Total length of string + const Color startColor = Color::Red; // Color of first string + const Color endColor = Color::Green; // Color of last string + + struct PbdSim + { + std::shared_ptr<LineMesh> geometry; + std::shared_ptr<PbdObject> object; + std::shared_ptr<PbdModel> model; + std::shared_ptr<PBDModelConfig> params; + std::shared_ptr<VisualModel> visualModel; + }; + std::vector<PbdSim> sims(numStrings); + + const double size = stringSpacing * (numStrings - 1); + const double vertexSpacing = stringLength / numVerts; + for (unsigned int i = 0; i < numStrings; i++) + { + // Setup the line mesh + sims[i].geometry = std::make_shared<LineMesh>(); + StdVectorOfVec3d vertList; + vertList.resize(numVerts); + for (size_t j = 0; j < numVerts; j++) + { + vertList[j] = Vec3d( + static_cast<double>(i) * stringSpacing - size * 0.5, + stringLength * 0.5 - static_cast<double>(j) * vertexSpacing, 0.0); + } + sims[i].geometry->setInitialVertexPositions(vertList); + sims[i].geometry->setVertexPositions(vertList); + + // Add connectivity data + std::vector<LineMesh::LineArray> segments; + for (size_t j = 0; j < numVerts - 1; j++) + { + LineMesh::LineArray seg = { j, j + 1 }; + segments.push_back(seg); + } + + sims[i].geometry->setLinesVertices(segments); + + sims[i].object = std::make_shared<PbdObject>("String " + std::to_string(i)); + sims[i].model = std::make_shared<PbdModel>(); + sims[i].model->setModelGeometry(sims[i].geometry); + + // Configure the parameters with bend stiffnesses varying from 0.001 to ~0.1 + sims[i].params = std::make_shared<PBDModelConfig>(); + sims[i].params->enableConstraint(PbdConstraint::Type::Distance, 0.001); + sims[i].params->enableConstraint(PbdConstraint::Type::Bend, static_cast<double>(i) * 0.1 / numStrings + 0.001); + sims[i].params->m_fixedNodeIds = { 0 }; + sims[i].params->m_uniformMassValue = 5.0; + sims[i].params->m_gravity = Vec3d(0, -9.8, 0); + sims[i].params->m_defaultDt = 0.0005; + sims[i].params->m_iterations = 5; + + // Set the parameters + sims[i].model->configure(sims[i].params); + sims[i].object->setDynamicalModel(sims[i].model); + sims[i].object->setPhysicsGeometry(sims[i].geometry); + + sims[i].visualModel = std::make_shared<VisualModel>(sims[i].geometry); + std::shared_ptr<RenderMaterial> material = std::make_shared<RenderMaterial>(); + material->setDisplayMode(RenderMaterial::DisplayMode::Wireframe); + material->setDebugColor(Color::lerpRgb(startColor, endColor, static_cast<double>(i) / (numStrings - 1))); + material->setLineWidth(2.0f); + sims[i].visualModel->setRenderMaterial(material); + sims[i].object->addVisualModel(sims[i].visualModel); + + // Add in scene + scene->addSceneObject(sims[i].object); + } + + // Adjust the camera + scene->getCamera()->setFocalPoint(0.0, 0.0, 0.0); + scene->getCamera()->setPosition(0.0, 0.0, 15.0); + + // Move the points every frame + double t = 0.0; + const double dt = 0.0005; + const double radius = 1.5; + auto movePoints = + [&sims, &t, dt, radius](Module* module) + { + for (unsigned int i = 0; i < sims.size(); i++) + { + Vec3d pos = sims[i].model->getCurrentState()->getVertexPosition(0); + // Move in circle, derivatives of parametric eq of circle + sims[i].model->getCurrentState()->setVertexPosition(0, imstk::Vec3d( + pos.x() + -std::sin(t) * radius * dt, + pos.y(), + pos.z() + std::cos(t) * radius * dt)); + } + t += dt; + }; + simManager->getSceneManager(scene)->setPostUpdateCallback(movePoints); + + // Start + simManager->setActiveScene(scene); + simManager->start(); + + // Write the graph, highlighting the critical path and putting the completion time in the name + TaskGraphVizWriter writer; + writer.setInput(scene->getTaskGraph()); + writer.setFileName("taskGraphBenchmarkExample.svg"); + writer.setHighlightCriticalPath(true); + writer.setWriteNodeComputeTimesColor(true); + writer.setWriteNodeComputeTimesText(true); + writer.write(); + + return 0; +} diff --git a/Examples/VirtualCoupling/VirtualCouplingExample.cpp b/Examples/VirtualCoupling/VirtualCouplingExample.cpp index cce2bcbab236876fa738c15987234067b53a59e6..a59c63db9860f6db1cfe48c6c89c9416a80b61ee 100644 --- a/Examples/VirtualCoupling/VirtualCouplingExample.cpp +++ b/Examples/VirtualCoupling/VirtualCouplingExample.cpp @@ -94,17 +94,20 @@ main() auto objController = std::make_shared<SceneObjectController>(obj, deviceTracker); scene->addObjectController(objController); - // Create a collision graph - auto graph = scene->getCollisionGraph(); - auto pair = graph->addInteractionPair(planeObj, obj, - CollisionDetection::Type::UnidirectionalPlaneToSphere, - CollisionHandling::Type::None, - CollisionHandling::Type::VirtualCoupling); - - // Customize collision handling algorithm - auto colHandlingAlgo = std::dynamic_pointer_cast<VirtualCouplingCH>(pair->getCollisionHandlingB()); - colHandlingAlgo->setStiffness(5e-01); - colHandlingAlgo->setDamping(0.005); + { + // Setup CD, and collision data + auto colData = std::make_shared<CollisionData>(); + std::shared_ptr<CollisionDetection> colDetect = makeCollisionDetectionObject(CollisionDetection::Type::UnidirectionalPlaneToSphere, + planeObj, obj, colData); + + // Setup the handler + auto colHandler = std::make_shared<VirtualCouplingCH>(CollisionHandling::Side::B, colData, planeObj, obj); + colHandler->setStiffness(5e-01); + colHandler->setDamping(0.005); + + auto pair = std::make_shared<CollisionPair>(planeObj, obj, colDetect, nullptr, colHandler); + scene->getCollisionGraph()->addInteractionPair(pair); + } // Camera auto cam = scene->getCamera(); diff --git a/Examples/VolumeRendering/VolumeRenderingExample.cpp b/Examples/VolumeRendering/VolumeRenderingExample.cpp index f5d5dff507d36730c65873e7fdc5b5ec83dd89a8..97e96ed9d5527c3da31378a4784be155fa2cfc97 100644 --- a/Examples/VolumeRendering/VolumeRenderingExample.cpp +++ b/Examples/VolumeRendering/VolumeRenderingExample.cpp @@ -28,7 +28,6 @@ #include "imstkVolumeRenderMaterialPresets.h" #include "imstkVTKTextStatusManager.h" #include "imstkSceneManager.h" -#include "imstkCollisionGraph.h" #include "imstkScene.h" #include "imstkVTKRenderer.h" diff --git a/Source/CollisionDetection/CollisionDetection/imstkBidirectionalPlaneToSphereCD.h b/Source/CollisionDetection/CollisionDetection/imstkBidirectionalPlaneToSphereCD.h index 3c081937650d6a0e26c37970575d7112e5c40b11..7a8460f22a83b7bf7fe71617de3db8f68d09b39c 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkBidirectionalPlaneToSphereCD.h +++ b/Source/CollisionDetection/CollisionDetection/imstkBidirectionalPlaneToSphereCD.h @@ -21,7 +21,6 @@ #pragma once -#include <memory> #include "imstkCollisionDetection.h" namespace imstk diff --git a/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.cpp b/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.cpp index 231430dddb4aaf9ce99a7f82c03c134254bc4a39..f6999298191a1c413775e78ab8eb9f8504a16493 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.cpp +++ b/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.cpp @@ -21,15 +21,16 @@ #include "imstkCollisionDetection.h" #include "imstkCollisionData.h" +#include "imstkTaskNode.h" +#include "imstkLogger.h" #include "imstkOctreeBasedCD.h" #include "imstkSurfaceMesh.h" -#include <g3log/g3log.hpp> - namespace imstk { CollisionDetection::CollisionDetection(const CollisionDetection::Type& type, std::shared_ptr<CollisionData> colData) : m_type(type), - m_colData((colData == nullptr) ? std::make_shared<CollisionData>() : colData) + m_colData((colData == nullptr) ? std::make_shared<CollisionData>() : colData), + m_taskNode(std::make_shared<TaskNode>(std::bind(&CollisionDetection::computeCollisionData, this), "CollisionDetection")) { } diff --git a/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h b/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h index ed8c7bcde19590d53231893748fcfa321e057731..a1be7c2f1761c7a040545f7c88f63c60ec538426 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h +++ b/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h @@ -25,10 +25,12 @@ namespace imstk { +struct CollisionData; + class CollidingObject; -class OctreeBasedCD; class Geometry; -struct CollisionData; +class OctreeBasedCD; +class TaskNode; /// /// \class CollisionDetection @@ -63,6 +65,9 @@ public: SphereToCylinder, SphereToSphere, + // Image based CD + SignedDistanceField, + Custom }; @@ -92,6 +97,11 @@ public: /// const std::shared_ptr<CollisionData> getCollisionData() const { return m_colData; } + /// + /// \brief Returns computational node + /// + std::shared_ptr<TaskNode> getTaskNode() const { return m_taskNode; } + /// /// \brief Update the intrernal octree, preparing for collision detection /// @@ -112,8 +122,9 @@ public: const std::shared_ptr<CollisionData>& collisionData); protected: - Type m_type = Type::Custom; ///< Collision detection algorithm type - std::shared_ptr<CollisionData> m_colData; ///< Collision data + Type m_type = Type::Custom; ///> Collision detection algorithm type + std::shared_ptr<CollisionData> m_colData = nullptr; ///> Collision data + std::shared_ptr<TaskNode> m_taskNode = nullptr; ///> Computational node to execute the detection /// Static octree for collision detection /// This octree is valid throughout the lifetime of the program diff --git a/Source/CollisionDetection/CollisionDetection/imstkSurfaceMeshToSurfaceMeshCCD.cpp b/Source/CollisionDetection/CollisionDetection/imstkSurfaceMeshToSurfaceMeshCCD.cpp index aa5ab7477f14c03434be2256290f414f4294630e..49ed12f5936fbb85171110419d46c7802de0d221 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkSurfaceMeshToSurfaceMeshCCD.cpp +++ b/Source/CollisionDetection/CollisionDetection/imstkSurfaceMeshToSurfaceMeshCCD.cpp @@ -35,9 +35,6 @@ SurfaceMeshToSurfaceMeshCCD::SurfaceMeshToSurfaceMeshCCD(std::shared_ptr<Surface m_modelA(std::make_shared<DeformModel>(meshA->getVertexPositions(), meshA->getTrianglesVertices())), m_modelB(std::make_shared<DeformModel>(meshB->getVertexPositions(), meshB->getTrianglesVertices())) { - //m_modelA = std::make_shared<DeformModel>(meshA->getVertexPositions(), meshA->getTrianglesVertices()); - //m_modelB = std::make_shared<DeformModel>(meshB->getVertexPositions(), meshB->getTrianglesVertices()); - // Setup Callbacks m_modelA->SetEECallBack(SurfaceMeshToSurfaceMeshCCD::EECallback, this); m_modelA->SetVFCallBack(SurfaceMeshToSurfaceMeshCCD::VFCallbackA, this); diff --git a/Source/Scene/imstkCDObjectFactory.h b/Source/CollisionDetection/imstkCDObjectFactory.cpp similarity index 53% rename from Source/Scene/imstkCDObjectFactory.h rename to Source/CollisionDetection/imstkCDObjectFactory.cpp index 58cd39ec7e5ca13dfea5570a5e78cd16b556ad97..668990d220005b1e0974f328ba32820441ed2c1e 100644 --- a/Source/Scene/imstkCDObjectFactory.h +++ b/Source/CollisionDetection/imstkCDObjectFactory.cpp @@ -1,54 +1,53 @@ /*========================================================================= - Library: iMSTK +Library: iMSTK - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0.txt +http://www.apache.org/licenses/LICENSE-2.0.txt - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. =========================================================================*/ -#pragma once - -#include "imstkCollisionDetection.h" +#include "imstkCDObjectFactory.h" #include "imstkCollisionData.h" #include "imstkOctreeBasedCD.h" -#include "imstkCollidingObject.h" // Points to objects #include "imstkPointSetToCapsuleCD.h" -#include "imstkPointSetToSphereCD.h" #include "imstkPointSetToPlaneCD.h" -#include "imstkPointSetToSurfaceMeshCD.h" +#include "imstkPointSetToSphereCD.h" #include "imstkPointSetToSpherePickingCD.h" +#include "imstkPointSetToSurfaceMeshCD.h" // Mesh to mesh -#include "imstkSurfaceMeshToSurfaceMeshCD.h" -#include "imstkSurfaceMeshToSurfaceMeshCCD.h" #include "imstkMeshToMeshBruteForceCD.h" +#include "imstkSurfaceMeshToSurfaceMeshCCD.h" +#include "imstkSurfaceMeshToSurfaceMeshCD.h" #include "imstkTetraToTetraCD.h" // Analytical object to analytical object -#include "imstkUnidirectionalPlaneToSphereCD.h" #include "imstkBidirectionalPlaneToSphereCD.h" #include "imstkSphereToCylinderCD.h" #include "imstkSphereToSphereCD.h" +#include "imstkUnidirectionalPlaneToSphereCD.h" +// Geometry +#include "imstkCapsule.h" +#include "imstkCylinder.h" +#include "imstkImageData.h" #include "imstkPlane.h" #include "imstkSphere.h" -#include "imstkCylinder.h" -#include "imstkCapsule.h" #include "imstkSurfaceMesh.h" #include "imstkTetrahedralMesh.h" @@ -60,40 +59,33 @@ namespace imstk { -/// -/// \brief Static factory for collision detection sub classes -/// If the collision pair is PointSet to SurfaceMesh, or SurfaceMesh to SurfaceMesh, -/// it will be added to an internal static octree for detecting collision -/// \todo Other collision pair may be considered to use octree too -/// -static std::shared_ptr<CollisionDetection> -makeCollisionDetectionObject(const CollisionDetection::Type type, - std::shared_ptr<CollidingObject> objA, - std::shared_ptr<CollidingObject> objB, - std::shared_ptr<CollisionData> colData) +makeCollisionDetectionObject(const CollisionDetection::Type type, + std::shared_ptr<Geometry> collidingGeometryA, + std::shared_ptr<Geometry> collidingGeometryB, + std::shared_ptr<CollisionData> colData) { switch (type) { // Points to objects case CollisionDetection::Type::PointSetToSphere: { - auto pointset = std::dynamic_pointer_cast<PointSet>(objA->getCollidingGeometry()); - auto sphere = std::dynamic_pointer_cast<Sphere>(objB->getCollidingGeometry()); + auto pointset = std::dynamic_pointer_cast<PointSet>(collidingGeometryA); + auto sphere = std::dynamic_pointer_cast<Sphere>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(pointset, sphere) return std::make_shared<PointSetToSphereCD>(pointset, sphere, colData); } case CollisionDetection::Type::PointSetToPlane: { - auto pointset = std::dynamic_pointer_cast<PointSet>(objA->getCollidingGeometry()); - auto plane = std::dynamic_pointer_cast<Plane>(objB->getCollidingGeometry()); + auto pointset = std::dynamic_pointer_cast<PointSet>(collidingGeometryA); + auto plane = std::dynamic_pointer_cast<Plane>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(pointset, plane) return std::make_shared<PointSetToPlaneCD>(pointset, plane, colData); } case CollisionDetection::Type::PointSetToCapsule: { - auto pointset = std::dynamic_pointer_cast<PointSet>(objA->getCollidingGeometry()); - auto capsule = std::dynamic_pointer_cast<Capsule>(objB->getCollidingGeometry()); + auto pointset = std::dynamic_pointer_cast<PointSet>(collidingGeometryA); + auto capsule = std::dynamic_pointer_cast<Capsule>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(pointset, capsule) return std::make_shared<PointSetToCapsuleCD>(pointset, capsule, colData); } @@ -108,37 +100,33 @@ makeCollisionDetectionObject(const CollisionDetection::Type type, #endif case CollisionDetection::Type::PointSetToSurfaceMesh: { - const auto& geomA = objA->getCollidingGeometry(); - const auto& geomB = objB->getCollidingGeometry(); - auto pointset = std::dynamic_pointer_cast<PointSet>(geomA); - auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(geomB); + auto pointset = std::dynamic_pointer_cast<PointSet>(collidingGeometryA); + auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(pointset, triMesh) - CollisionDetection::addCollisionPairToOctree(geomA, geomB, type, colData); + CollisionDetection::addCollisionPairToOctree(collidingGeometryA, collidingGeometryB, type, colData); return std::make_shared<PointSetToSurfaceMeshCD>(pointset, triMesh, colData); } // Mesh to mesh case CollisionDetection::Type::SurfaceMeshToSurfaceMesh: { - const auto& geomA = objA->getCollidingGeometry(); - const auto& geomB = objB->getCollidingGeometry(); - auto meshA = std::dynamic_pointer_cast<SurfaceMesh>(geomA); - auto meshB = std::dynamic_pointer_cast<SurfaceMesh>(geomB); + auto meshA = std::dynamic_pointer_cast<SurfaceMesh>(collidingGeometryA); + auto meshB = std::dynamic_pointer_cast<SurfaceMesh>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(meshA, meshB) - CollisionDetection::addCollisionPairToOctree(geomA, geomB, type, colData); + CollisionDetection::addCollisionPairToOctree(collidingGeometryA, collidingGeometryB, type, colData); return std::make_shared<SurfaceMeshToSurfaceMeshCD>(meshA, meshB, colData); } case CollisionDetection::Type::SurfaceMeshToSurfaceMeshCCD: { - auto meshA = std::dynamic_pointer_cast<SurfaceMesh>(objA->getCollidingGeometry()); - auto meshB = std::dynamic_pointer_cast<SurfaceMesh>(objB->getCollidingGeometry()); + auto meshA = std::dynamic_pointer_cast<SurfaceMesh>(collidingGeometryA); + auto meshB = std::dynamic_pointer_cast<SurfaceMesh>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(meshA, meshB) return std::make_shared<SurfaceMeshToSurfaceMeshCCD>(meshA, meshB, colData); } case CollisionDetection::Type::VolumeMeshToVolumeMesh: { - auto tet1 = std::dynamic_pointer_cast<TetrahedralMesh>(objA->getCollidingGeometry()); - auto tet2 = std::dynamic_pointer_cast<TetrahedralMesh>(objB->getCollidingGeometry()); + auto tet1 = std::dynamic_pointer_cast<TetrahedralMesh>(collidingGeometryA); + auto tet2 = std::dynamic_pointer_cast<TetrahedralMesh>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(tet1, tet2) return std::make_shared<TetraToTetraCD>(tet1, tet2, colData); } @@ -146,38 +134,37 @@ makeCollisionDetectionObject(const CollisionDetection::Type type, // Analytical object to analytical object case CollisionDetection::Type::UnidirectionalPlaneToSphere: { - auto plane = std::dynamic_pointer_cast<Plane>(objA->getCollidingGeometry()); - auto sphere = std::dynamic_pointer_cast<Sphere>(objB->getCollidingGeometry()); + auto plane = std::dynamic_pointer_cast<Plane>(collidingGeometryA); + auto sphere = std::dynamic_pointer_cast<Sphere>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(plane, sphere) return std::make_shared<UnidirectionalPlaneToSphereCD>(plane, sphere, colData); } case CollisionDetection::Type::BidirectionalPlaneToSphere: { - auto plane = std::dynamic_pointer_cast<Plane>(objA->getCollidingGeometry()); - auto sphere = std::dynamic_pointer_cast<Sphere>(objB->getCollidingGeometry()); + auto plane = std::dynamic_pointer_cast<Plane>(collidingGeometryA); + auto sphere = std::dynamic_pointer_cast<Sphere>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(plane, sphere) return std::make_shared<BidirectionalPlaneToSphereCD>(plane, sphere, colData); } case CollisionDetection::Type::SphereToSphere: { - auto sphereA = std::dynamic_pointer_cast<Sphere>(objA->getCollidingGeometry()); - auto sphereB = std::dynamic_pointer_cast<Sphere>(objB->getCollidingGeometry()); + auto sphereA = std::dynamic_pointer_cast<Sphere>(collidingGeometryA); + auto sphereB = std::dynamic_pointer_cast<Sphere>(collidingGeometryB); IMSTK_CHECK_FOR_VALID_GEOMETRIES(sphereA, sphereB) return std::make_shared<SphereToSphereCD>(sphereA, sphereB, colData); } case CollisionDetection::Type::SphereToCylinder: { - auto sphere = std::dynamic_pointer_cast<Sphere>(objB->getCollidingGeometry()); - auto cylinder = std::dynamic_pointer_cast<Cylinder>(objA->getCollidingGeometry()); + auto sphere = std::dynamic_pointer_cast<Sphere>(collidingGeometryB); + auto cylinder = std::dynamic_pointer_cast<Cylinder>(collidingGeometryA); IMSTK_CHECK_FOR_VALID_GEOMETRIES(sphere, cylinder) return std::make_shared<SphereToCylinderCD>(sphere, cylinder, colData); } case CollisionDetection::Type::MeshToMeshBruteForce: { - auto meshA = std::dynamic_pointer_cast<SurfaceMesh>(objA->getCollidingGeometry()); - auto meshB = std::dynamic_pointer_cast<SurfaceMesh>(objB->getCollidingGeometry()); - IMSTK_CHECK_FOR_VALID_GEOMETRIES(meshA, meshB) - return std::make_shared<MeshToMeshBruteForceCD>(meshA, meshB, colData); + auto meshB = std::dynamic_pointer_cast<SurfaceMesh>(collidingGeometryB); + IMSTK_CHECK_FOR_VALID_GEOMETRIES(collidingGeometryA, meshB) + return std::make_shared<MeshToMeshBruteForceCD>(collidingGeometryA, meshB, colData); } default: { @@ -186,4 +173,4 @@ makeCollisionDetectionObject(const CollisionDetection::Type type, } } } -} +} \ No newline at end of file diff --git a/Source/CollisionDetection/imstkCDObjectFactory.h b/Source/CollisionDetection/imstkCDObjectFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..1f27972361706d8a9771a9a721057d8c7cabc54c --- /dev/null +++ b/Source/CollisionDetection/imstkCDObjectFactory.h @@ -0,0 +1,40 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkCollisionDetection.h" + +namespace imstk +{ +class Geometry; + +/// +/// \brief Static factory for collision detection sub classes +/// If the collision pair is PointSet to SurfaceMesh, or SurfaceMesh to SurfaceMesh, +/// it will be added to an internal static octree for detecting collision +/// \todo Other collision pair may be considered to use octree too +/// +extern std::shared_ptr<CollisionDetection> makeCollisionDetectionObject(const CollisionDetection::Type type, + std::shared_ptr<Geometry> collidingGeometryA, + std::shared_ptr<Geometry> collidingGeometryB, + std::shared_ptr<CollisionData> colData); +} diff --git a/Source/CollisionHandling/imstkBoneDrillingCH.cpp b/Source/CollisionHandling/imstkBoneDrillingCH.cpp index 23578cc8e044cba3503441cedd47f2ea1b279762..04877d74a5ea42d9ad926e97c0b71391ec0281ab 100644 --- a/Source/CollisionHandling/imstkBoneDrillingCH.cpp +++ b/Source/CollisionHandling/imstkBoneDrillingCH.cpp @@ -21,13 +21,11 @@ #include "imstkBoneDrillingCH.h" #include "imstkCollidingObject.h" -#include "imstkTetrahedralMesh.h" #include "imstkCollisionData.h" #include "imstkDeviceTracker.h" -#include "imstkMath.h" +#include "imstkLogger.h" #include "imstkParallelUtils.h" - -#include <g3log/g3log.hpp> +#include "imstkTetrahedralMesh.h" namespace imstk { diff --git a/Source/CollisionHandling/imstkBoneDrillingCH.h b/Source/CollisionHandling/imstkBoneDrillingCH.h index 9f1719cd985beff64eae2f50ce9986738abffeb8..25bfe35077f74ee10a35943d019d78d16560c8fe 100644 --- a/Source/CollisionHandling/imstkBoneDrillingCH.h +++ b/Source/CollisionHandling/imstkBoneDrillingCH.h @@ -23,6 +23,7 @@ // imstk #include "imstkCollisionHandling.h" +#include "imstkMath.h" namespace imstk { diff --git a/Source/CollisionHandling/imstkCollisionHandling.cpp b/Source/CollisionHandling/imstkCollisionHandling.cpp index 1fd29c45812fbc950261ce2f82485721d68127e0..2cb17bd165141f8e23e9208a8fbde67d925ece60 100644 --- a/Source/CollisionHandling/imstkCollisionHandling.cpp +++ b/Source/CollisionHandling/imstkCollisionHandling.cpp @@ -1,82 +1,33 @@ /*========================================================================= - Library: iMSTK +Library: iMSTK - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0.txt +http://www.apache.org/licenses/LICENSE-2.0.txt - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. =========================================================================*/ #include "imstkCollisionHandling.h" - -#include "imstkPenaltyCH.h" -#include "imstkVirtualCouplingCH.h" -#include "imstkPickingCH.h" -#include "imstkDeformableObject.h" -#include "imstkBoneDrillingCH.h" -#include "imstkSPHCollisionHandling.h" -#include "imstkPBDCollisionHandling.h" -#include "imstkCollisionData.h" - -#include <g3log/g3log.hpp> +#include "imstkTaskNode.h" namespace imstk { -std::shared_ptr<CollisionHandling> -CollisionHandling::make_collision_handling(const Type& type, - const Side& side, - const std::shared_ptr<CollisionData> colData, - std::shared_ptr<CollidingObject> objA, - std::shared_ptr<CollidingObject> objB) +CollisionHandling::CollisionHandling(const Type& type, const Side& side, + const std::shared_ptr<CollisionData> colData) : + m_type(type), m_side(side), m_colData(colData), + m_taskNode(std::make_shared<TaskNode>(std::bind(&CollisionHandling::processCollisionData, this), "CollisionHandling", true)) { - if (type != Type::None - && objA->getType() == SceneObject::Type::Visual) - { - LOG(FATAL) << "CollisionHandling::make_collision_handling error: " - << "collision handling only implemented for colliding objects."; - return nullptr; - } - - switch (type) - { - case Type::None: - return nullptr; - - case Type::Penalty: - - return std::make_shared<PenaltyCH>(side, colData, objA); - - case Type::VirtualCoupling: - - return std::make_shared<VirtualCouplingCH>(side, colData, objA); - - case Type::NodalPicking: - return std::make_shared<PickingCH>(side, colData, objA); - - case Type::BoneDrilling: - return std::make_shared<BoneDrillingCH>(side, colData, objA, objB); - - case Type::PBD: - return std::make_shared<PBDCollisionHandling>(side, colData, objA, objB); - - case Type::SPH: - return std::make_shared<SPHCollisionHandling>(side, colData, objA); - - default: - LOG(FATAL) << "CollisionHandling::make_collision_handling error: type not implemented."; - return nullptr; - } -} } +} \ No newline at end of file diff --git a/Source/CollisionHandling/imstkCollisionHandling.h b/Source/CollisionHandling/imstkCollisionHandling.h index 15aba621f4ef2df47931d51a5e9d82988e719cc2..ea9e05a2d77e4617a022d7ab855cb7018585c0e0 100644 --- a/Source/CollisionHandling/imstkCollisionHandling.h +++ b/Source/CollisionHandling/imstkCollisionHandling.h @@ -21,14 +21,15 @@ #pragma once -// imstk -#include "imstkCollidingObject.h" +#include <memory> namespace imstk { -class InteractionPair; struct CollisionData; -class PbdObject; + +class CollidingObject; +class InteractionPair; +class TaskNode; /// /// \class CollisionHandling @@ -62,32 +63,13 @@ public: AB }; - /// - /// \brief Static factory for collision handling sub classes - /// - static std::shared_ptr<CollisionHandling> make_collision_handling( - const Type& type, - const Side& side, - const std::shared_ptr<CollisionData> colData, - std::shared_ptr<CollidingObject> objA, - std::shared_ptr<CollidingObject> objB = nullptr); - - /// - /// \brief Constructor - /// - CollisionHandling(const Type& type, - const Side& side, - const std::shared_ptr<CollisionData> colData) : - m_type(type), - m_side(side), - m_colData(colData) {} +public: + CollisionHandling(const Type& type, const Side& side, const std::shared_ptr<CollisionData> colData); CollisionHandling() = delete; - /// - /// \brief Destructor - /// virtual ~CollisionHandling() = default; +public: /// /// \brief Compute forces based on collision data (pure virtual) /// @@ -96,12 +78,15 @@ public: /// /// \brief Returns collision handling type /// - const Type& getType() const { return m_type; }; + const Type& getType() const { return m_type; } + const Side& getSide() const { return m_side; } -protected: + std::shared_ptr<TaskNode> getTaskNode() const { return m_taskNode; } - Type m_type; ///< Collision handling algorithm type - Side m_side; ///< Direction of the collisionData - const std::shared_ptr<CollisionData> m_colData; ///< Collision data +protected: + Type m_type; ///< Collision handling algorithm type + Side m_side; ///< Direction of the collisionData + const std::shared_ptr<CollisionData> m_colData = nullptr; ///< Collision data + std::shared_ptr<TaskNode> m_taskNode = nullptr; }; } diff --git a/Source/CollisionHandling/imstkPBDCollisionHandling.cpp b/Source/CollisionHandling/imstkPBDCollisionHandling.cpp index 42bec2550ad03d92b7549bc822661ea4efe46d98..31a2f57a37f9ffdf83f10da556ed648caf9bcfa6 100644 --- a/Source/CollisionHandling/imstkPBDCollisionHandling.cpp +++ b/Source/CollisionHandling/imstkPBDCollisionHandling.cpp @@ -21,28 +21,24 @@ #include "imstkPBDCollisionHandling.h" #include "imstkCollisionData.h" -#include "imstkDeformableObject.h" +#include "imstkGeometryMap.h" +#include "imstkPbdEdgeEdgeCollisionConstraint.h" #include "imstkPbdModel.h" #include "imstkPbdObject.h" -#include "imstkPbdEdgeEdgeCollisionConstraint.h" #include "imstkPbdPointTriCollisionConstraint.h" #include "imstkPbdSolver.h" -#include "imstkParallelUtils.h" -#include "imstkGeometryMap.h" #include "imstkSurfaceMesh.h" -#include <memory> -#include <g3log/g3log.hpp> - namespace imstk { PBDCollisionHandling::PBDCollisionHandling(const Side& side, const std::shared_ptr<CollisionData> colData, - std::shared_ptr<CollidingObject> obj1, - std::shared_ptr<CollidingObject> obj2) : + std::shared_ptr<PbdObject> pbdObject1, + std::shared_ptr<PbdObject> pbdObject2) : CollisionHandling(Type::PBD, side, colData), - m_pbdObject1(std::dynamic_pointer_cast<PbdObject>(obj1)), - m_pbdObject2(std::dynamic_pointer_cast<PbdObject>(obj2)) + m_PbdObject1(pbdObject1), + m_PbdObject2(pbdObject2), + m_pbdCollisionSolver(std::make_shared<PbdCollisionSolver>()) { } @@ -61,36 +57,30 @@ PBDCollisionHandling::~PBDCollisionHandling() void PBDCollisionHandling::processCollisionData() { - /*if (auto deformableObj = std::dynamic_pointer_cast<DeformableObject>(m_object)) - { - this->computeContactForcesDiscreteDeformable(deformableObj); - } - else if (auto analyticObj = std::dynamic_pointer_cast<CollidingObject>(m_object)) - { - this->computeContactForcesAnalyticRigid(analyticObj); - } - else - { - LOG(WARNING) << "PenaltyRigidCH::computeContactForces error: " - << "no penalty collision handling available for " << m_object->getName() - << " (rigid mesh not yet supported)."; - }*/ this->generatePBDConstraints(); - CHECK(m_PBDSolver != nullptr) << "No PbdSolver found to handle the Collision constraints!"; + if (m_PBDConstraints.size() == 0) + { + return; + } - m_PBDSolver->addCollisionConstraints(&m_PBDConstraints); + m_pbdCollisionSolver->addCollisionConstraints(&m_PBDConstraints, + m_PbdObject1->getPbdModel()->getCurrentState()->getPositions(), + m_PbdObject1->getPbdModel()->getInvMasses(), + m_PbdObject2->getPbdModel()->getCurrentState()->getPositions(), + m_PbdObject2->getPbdModel()->getInvMasses()); } void PBDCollisionHandling::generatePBDConstraints() { - const auto dynaModel1 = std::static_pointer_cast<PbdModel>(m_pbdObject1->getDynamicalModel()); - const auto dynaModel2 = std::static_pointer_cast<PbdModel>(m_pbdObject2->getDynamicalModel()); - const auto colGeo2 = std::static_pointer_cast<SurfaceMesh>(m_pbdObject2->getCollidingGeometry()); + std::shared_ptr<PbdCollisionConstraintConfig> config1 = m_PbdObject1->getPbdModel()->getParameters()->collisionParams; + std::shared_ptr<PbdCollisionConstraintConfig> config2 = m_PbdObject2->getPbdModel()->getParameters()->collisionParams; + + const auto colGeo2 = std::static_pointer_cast<SurfaceMesh>(m_PbdObject2->getCollidingGeometry()); - const auto map1 = m_pbdObject1->getPhysicsToCollidingMap(); - const auto map2 = m_pbdObject2->getPhysicsToCollidingMap(); + const auto map1 = m_PbdObject1->getPhysicsToCollidingMap(); + const auto map2 = m_PbdObject2->getPhysicsToCollidingMap(); // std::cout << "EE: " << m_colData->EEColData.getSize() << "TV: " << m_colData->VTColData.getSize() << std::endl; @@ -133,7 +123,7 @@ PBDCollisionHandling::generatePBDConstraints() } const auto constraint = m_EEConstraintPool[idx]; - constraint->initConstraint(dynaModel1, edgeA1, edgeA2, dynaModel2, edgeB1, edgeB2); + constraint->initConstraint(edgeA1, edgeA2, edgeB1, edgeB2, config1, config2); }); // Generate vertex-triangle pbd constraints @@ -166,7 +156,10 @@ PBDCollisionHandling::generatePBDConstraints() } const auto constraint = m_VTConstraintPool[idx]; - constraint->initConstraint(dynaModel1, colData.vertexIdx, dynaModel2, v1, v2, v3); + constraint->initConstraint( + colData.vertexIdx, + v1, v2, v3, + config1, config2); }); // Copy constraints diff --git a/Source/CollisionHandling/imstkPBDCollisionHandling.h b/Source/CollisionHandling/imstkPBDCollisionHandling.h index 39b81c847f0bb2d1dc04bf3fc74bdfec3dbd07be..3935e0db3328f3f4c93a77482f7b6fbae6a81cbe 100644 --- a/Source/CollisionHandling/imstkPBDCollisionHandling.h +++ b/Source/CollisionHandling/imstkPBDCollisionHandling.h @@ -24,14 +24,15 @@ // imstk #include "imstkCollisionHandling.h" +#include <vector> + namespace imstk { -class CollidingObject; class PbdObject; class PbdCollisionConstraint; class PbdEdgeEdgeConstraint; class PbdPointTriangleConstraint; -class PbdSolver; +class PbdCollisionSolver; struct CollisionData; /// @@ -41,7 +42,6 @@ struct CollisionData; /// class PBDCollisionHandling : public CollisionHandling { -typedef std::vector<PbdCollisionConstraint*> PBDConstraintVector; public: /// @@ -49,8 +49,8 @@ public: /// PBDCollisionHandling(const Side& side, const std::shared_ptr<CollisionData> colData, - std::shared_ptr<CollidingObject> obj1, - std::shared_ptr<CollidingObject> obj2); + std::shared_ptr<PbdObject> pbdObject1, + std::shared_ptr<PbdObject> pbdObject2); PBDCollisionHandling() = delete; @@ -69,18 +69,14 @@ public: /// void generatePBDConstraints(); - /// - /// \brief Get/Set pbd solver - /// - void setSolver(std::shared_ptr<PbdSolver> solver) { m_PBDSolver = solver; }; - std::shared_ptr<PbdSolver> getSolver() { return m_PBDSolver; }; + std::shared_ptr<PbdCollisionSolver> getCollisionSolver() const { return m_pbdCollisionSolver; } private: + std::shared_ptr<PbdObject> m_PbdObject1 = nullptr; ///> PBD object + std::shared_ptr<PbdObject> m_PbdObject2 = nullptr; ///> PBD object + std::shared_ptr<PbdCollisionSolver> m_pbdCollisionSolver = nullptr; - std::shared_ptr<PbdObject> m_pbdObject1; ///> PBD object - std::shared_ptr<PbdObject> m_pbdObject2; ///> PBD object - PBDConstraintVector m_PBDConstraints; ///> List of PBD constraints - std::shared_ptr<PbdSolver> m_PBDSolver; /// The Solver for the collision constraints + std::vector<PbdCollisionConstraint*> m_PBDConstraints; ///> List of PBD constraints std::vector<PbdEdgeEdgeConstraint*> m_EEConstraintPool; std::vector<PbdPointTriangleConstraint*> m_VTConstraintPool; diff --git a/Source/CollisionHandling/imstkPenaltyCH.cpp b/Source/CollisionHandling/imstkPenaltyCH.cpp index d19f90068678929ad63a1b40ceff9905b3824d32..b3a482040a49f1323be2fd7876eb9ac0e05a8b03 100644 --- a/Source/CollisionHandling/imstkPenaltyCH.cpp +++ b/Source/CollisionHandling/imstkPenaltyCH.cpp @@ -20,14 +20,13 @@ =========================================================================*/ #include "imstkPenaltyCH.h" - #include "imstkCollidingObject.h" #include "imstkCollisionData.h" #include "imstkDeformableObject.h" +#include "imstkFEMDeformableBodyModel.h" +#include "imstkLogger.h" #include "imstkParallelUtils.h" -#include <g3log/g3log.hpp> - namespace imstk { PenaltyCH::PenaltyCH(const Side& side, @@ -98,8 +97,9 @@ PenaltyCH::computeContactForcesDiscreteDeformable(const std::shared_ptr<FeDeform << m_object->getName() << " is not a deformable object."; // Get current force vector - auto& force = deformableObj->getContactForce(); - const auto& velVector = deformableObj->getVelocities(); + std::shared_ptr<FEMDeformableBodyModel> model = deformableObj->getFEMModel(); + auto& force = model->getContactForce(); + const auto& velVector = model->getCurrentState()->getQDot(); // If collision data, append forces ParallelUtils::SpinLock lock; diff --git a/Source/CollisionHandling/imstkPenaltyCH.h b/Source/CollisionHandling/imstkPenaltyCH.h index a75938f13e91902a49b3b5e47800afa99e835062..35b3724034bc90c0cb26bd141eb20640b372f121 100644 --- a/Source/CollisionHandling/imstkPenaltyCH.h +++ b/Source/CollisionHandling/imstkPenaltyCH.h @@ -38,7 +38,6 @@ struct CollisionData; class PenaltyCH : public CollisionHandling { public: - /// /// \brief Constructor /// @@ -78,10 +77,9 @@ public: } private: + std::shared_ptr<CollidingObject> m_object = nullptr; ///> - std::shared_ptr<CollidingObject> m_object; ///> - - double m_stiffness = 5.0e5; ///> Stiffness of contact - double m_damping = 0.5; ///> Damping of the contact + double m_stiffness = 5.0e5; ///> Stiffness of contact + double m_damping = 0.5; ///> Damping of the contact }; } diff --git a/Source/CollisionHandling/imstkPickingCH.cpp b/Source/CollisionHandling/imstkPickingCH.cpp index eca40104ca43190738e656c0f3f28af5c934b56a..ac70bb54389fc2dd923b6be4e8be87c827db9739 100644 --- a/Source/CollisionHandling/imstkPickingCH.cpp +++ b/Source/CollisionHandling/imstkPickingCH.cpp @@ -32,9 +32,9 @@ namespace imstk { PickingCH::PickingCH(const CollisionHandling::Side& side, const std::shared_ptr<CollisionData> colData, - std::shared_ptr<CollidingObject> obj) : + std::shared_ptr<FeDeformableObject> obj) : CollisionHandling(Type::NodalPicking, side, colData), - m_object(std::dynamic_pointer_cast<FeDeformableObject>(obj)) + m_object(obj) { } @@ -60,8 +60,9 @@ PickingCH::addPickConstraints(std::shared_ptr<FeDeformableObject> deformableObj) CHECK(deformableObj != nullptr) << "PenaltyRigidCH::addPickConstraints error: " << " not a deformable object."; - const auto& Uprev = deformableObj->getDisplacements(); - const auto& Vprev = deformableObj->getVelocities(); + std::shared_ptr<FEMDeformableBodyModel> model = deformableObj->getFEMModel(); + const Vectord& Uprev = model->getCurrentState()->getQ(); + const Vectord& Vprev = model->getCurrentState()->getQDot(); auto PhysTetMesh = std::dynamic_pointer_cast<PointSet>(deformableObj->getPhysicsGeometry()); auto dT = std::dynamic_pointer_cast<FEMDeformableBodyModel>(m_object->getDynamicalModel())->getTimeIntegrator()->getTimestepSize(); diff --git a/Source/CollisionHandling/imstkPickingCH.h b/Source/CollisionHandling/imstkPickingCH.h index 49934c063d2c4b795a440b509fd1e27dace84eae..2614dde37f9ad5adc8ec3e0feb8bce1919fdbf8e 100644 --- a/Source/CollisionHandling/imstkPickingCH.h +++ b/Source/CollisionHandling/imstkPickingCH.h @@ -24,6 +24,8 @@ // imstk #include "imstkCollisionHandling.h" +#include <vector> + namespace imstk { class CollidingObject; @@ -45,7 +47,7 @@ public: /// PickingCH(const Side& side, const std::shared_ptr<CollisionData> colData, - std::shared_ptr<CollidingObject> obj); + std::shared_ptr<FeDeformableObject> obj); PickingCH() = delete; diff --git a/Source/CollisionHandling/imstkSPHCollisionHandling.cpp b/Source/CollisionHandling/imstkSPHCollisionHandling.cpp index 5c34f080fa9af109b70347a297025abcc12f34a4..99670ce38a06b5cb74183a108335ebe09ccb8034 100644 --- a/Source/CollisionHandling/imstkSPHCollisionHandling.cpp +++ b/Source/CollisionHandling/imstkSPHCollisionHandling.cpp @@ -20,19 +20,18 @@ =========================================================================*/ #include "imstkSPHCollisionHandling.h" -#include "imstkParallelUtils.h" #include "imstkCollisionData.h" -#include "imstkSPHObject.h" +#include "imstkLogger.h" +#include "imstkParallelUtils.h" #include "imstkSPHModel.h" - -#include <g3log/g3log.hpp> +#include "imstkSPHObject.h" namespace imstk { -SPHCollisionHandling::SPHCollisionHandling(const CollisionHandling::Side& side, - const std::shared_ptr<CollisionData>& colData, - const std::shared_ptr<CollidingObject>& obj) : - CollisionHandling(Type::SPH, side, colData), m_SPHObject(std::dynamic_pointer_cast<SPHObject>(obj)) +SPHCollisionHandling::SPHCollisionHandling(const CollisionHandling::Side& side, + std::shared_ptr<CollisionData> colData, + std::shared_ptr<SPHObject> obj) : + CollisionHandling(Type::SPH, side, colData), m_SPHObject(obj) { LOG_IF(FATAL, (!m_SPHObject)) << "Invalid SPH object"; } @@ -55,9 +54,9 @@ SPHCollisionHandling::processCollisionData() ParallelUtils::parallelFor(m_colData->MAColData.getSize(), [&](const size_t idx) { - const auto& cd = m_colData->MAColData[idx]; - const auto pidx = cd.nodeIdx; // Fluid particle index - auto n = cd.penetrationVector; // This vector should point into solid object + const MeshToAnalyticalCollisionDataElement& cd = m_colData->MAColData[idx]; + const uint32_t pidx = cd.nodeIdx; // Fluid particle index + Vec3d n = cd.penetrationVector; // This vector should point into solid object // Correct particle position state.getPositions()[pidx] -= n; diff --git a/Source/CollisionHandling/imstkSPHCollisionHandling.h b/Source/CollisionHandling/imstkSPHCollisionHandling.h index 27a819f09668e4609b1c97dc48768c64b3060f0d..e024c18d70375230690c0267cccf1c05a0c8e120 100644 --- a/Source/CollisionHandling/imstkSPHCollisionHandling.h +++ b/Source/CollisionHandling/imstkSPHCollisionHandling.h @@ -30,9 +30,9 @@ class SPHObject; class SPHCollisionHandling : public CollisionHandling { public: - SPHCollisionHandling(const Side& side, - const std::shared_ptr<CollisionData>& colData, - const std::shared_ptr<CollidingObject>& obj); + SPHCollisionHandling(const Side& side, + std::shared_ptr<CollisionData> colData, + std::shared_ptr<SPHObject> obj); SPHCollisionHandling() = delete; @@ -42,6 +42,6 @@ public: virtual void processCollisionData() override; private: - std::shared_ptr<SPHObject> m_SPHObject; + std::shared_ptr<SPHObject> m_SPHObject = nullptr; }; } // end namespace imstk diff --git a/Source/CollisionHandling/imstkVirtualCouplingCH.h b/Source/CollisionHandling/imstkVirtualCouplingCH.h index 7fdbcd391a7a243875ed352003c7468ab48439c8..3ad40c87f577256666822a6c9362931296c99bad 100644 --- a/Source/CollisionHandling/imstkVirtualCouplingCH.h +++ b/Source/CollisionHandling/imstkVirtualCouplingCH.h @@ -23,6 +23,7 @@ // imstk #include "imstkCollisionHandling.h" +#include "imstkMath.h" namespace imstk { diff --git a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.cpp b/Source/Common/TaskGraph/imstkSequentialTaskGraphController.cpp similarity index 69% rename from Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.cpp rename to Source/Common/TaskGraph/imstkSequentialTaskGraphController.cpp index a70731e37ffd09107a38272930b3f615c45515e4..f9cccef2ad552d2db107bf901b2b2783084f7fd5 100644 --- a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.cpp +++ b/Source/Common/TaskGraph/imstkSequentialTaskGraphController.cpp @@ -19,20 +19,25 @@ =========================================================================*/ -#include <memory> - -#include "imstkPbdRigidObject.h" -#include "imstkGeometry.h" -#include "imstkGeometryMap.h" -#include "imstkPbdModel.h" - -#include <g3log/g3log.hpp> +#include "imstkSequentialTaskGraphController.h" +#include "imstkTaskGraph.h" +#include "imstkTaskNode.h" namespace imstk { void -PbdRigidObject::updatePbdStates() +SequentialTaskGraphController::init() +{ + m_executionOrderedNodes = TaskGraph::topologicalSort(m_graph); +} + +void +SequentialTaskGraphController::execute() { - m_pbdModel->updatePbdStateFromPhysicsGeometry(); + // Sequential + for (std::shared_ptr<TaskNode> node : *m_executionOrderedNodes) + { + node->execute(); + } } -} //imstk +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkSequentialTaskGraphController.h b/Source/Common/TaskGraph/imstkSequentialTaskGraphController.h new file mode 100644 index 0000000000000000000000000000000000000000..b645df75204427d9bd2b86d5473c19565c9d2923 --- /dev/null +++ b/Source/Common/TaskGraph/imstkSequentialTaskGraphController.h @@ -0,0 +1,51 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkTaskGraphController.h" +#include <list> + +namespace imstk +{ +class TaskNode; + +/// +/// \class SequentialTaskGraphController +/// +/// \brief This class executes a TaskGraph by first topologically sorting them (Kahn's algorithm) +/// then sequentially running them +/// +class SequentialTaskGraphController : public TaskGraphController +{ +public: + /// + /// \brief Sorts the computational nodes + /// + void init() override; + + void execute() override; + +private: + // The current nodes to execute, ordered + std::shared_ptr<std::list<std::shared_ptr<TaskNode>>> m_executionOrderedNodes; +}; +}; \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskGraph.cpp b/Source/Common/TaskGraph/imstkTaskGraph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04a9ce5f09b5c85fbdd68e1cfe27746bf2042aef --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskGraph.cpp @@ -0,0 +1,843 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkTaskGraph.h" +#include "imstkLogger.h" +#include "imstkTaskNode.h" +#include <queue> +#include <set> +#include <stack> + +namespace imstk +{ +TaskGraph::TaskGraph(std::string sourceName, std::string sinkName) : + m_source(std::make_shared<TaskNode>()), + m_sink(std::make_shared<TaskNode>()) +{ + m_source->m_name = sourceName; + m_sink->m_name = sinkName; + addNode(m_source); + addNode(m_sink); +} + +TaskNodeVector::iterator +TaskGraph::findNode(std::string name) +{ + return std::find_if(m_nodes.begin(), m_nodes.end(), + [&name](const std::shared_ptr<TaskNode>& x) { return x->m_name == name; }); +} + +TaskNodeVector::const_iterator +TaskGraph::findNode(std::string name) const +{ + return std::find_if(m_nodes.begin(), m_nodes.end(), + [&name](const std::shared_ptr<TaskNode>& x) { return x->m_name == name; }); +} + +TaskNodeVector::iterator +TaskGraph::findNode(std::shared_ptr<TaskNode> node) +{ + return std::find(m_nodes.begin(), m_nodes.end(), node); +} + +TaskNodeVector::const_iterator +TaskGraph::findNode(std::shared_ptr<TaskNode> node) const +{ + return std::find(m_nodes.begin(), m_nodes.end(), node); +} + +bool +TaskGraph::containsNode(std::shared_ptr<TaskNode> node) const +{ + return findNode(node) != endNode(); +} + +bool +TaskGraph::containsEdge(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode) +{ + return (m_adjList.count(srcNode) != 0 && m_adjList[srcNode].count(destNode) != 0); +} + +void +TaskGraph::addEdge(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode) +{ + m_adjList[srcNode].insert(destNode); + m_invAdjList[destNode].insert(srcNode); +} + +void +TaskGraph::nestGraph(std::shared_ptr<TaskGraph> subgraph, std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) +{ + // Ensure source and sink are in the graph + if (findNode(source) == endNode()) + { + LOG(WARNING) << "Tried to nest a graph using source, but source does not exist in this"; + return; + } + if (findNode(sink) == endNode()) + { + LOG(WARNING) << "Tried to nest a graph using sink, but sink does not exist in this"; + return; + } + + // Copy all of the nodes into this graph (check duplicates) + for (TaskNodeVector::iterator it = subgraph->getNodes().begin(); it != subgraph->getNodes().end(); it++) + { + addNode(*it); + } + // Copy all the edges into the graph (no need to check for duplicates) + const TaskNodeAdjList& adjList = subgraph->getAdjList(); + for (TaskNodeAdjList::const_iterator it = adjList.begin(); it != adjList.end(); it++) + { + const TaskNodeSet& outputNodes = it->second; + for (TaskNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) + { + addEdge(it->first, *jt); + } + } + // Add source and sink edges + addEdge(source, subgraph->getSource()); + addEdge(subgraph->getSink(), sink); +} + +void +TaskGraph::removeEdge(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode) +{ + if (m_adjList.count(srcNode) == 0) + { + return; + } + if (m_adjList[srcNode].count(destNode) == 0) + { + return; + } + m_adjList[srcNode].erase(destNode); + m_invAdjList[destNode].erase(srcNode); + if (m_adjList[srcNode].size() == 0) + { + m_adjList.erase(srcNode); + } + if (m_invAdjList[destNode].size() == 0) + { + m_invAdjList.erase(destNode); + } +} + +bool +TaskGraph::addNode(std::shared_ptr<TaskNode> node) +{ + // If the node already exists return false + if (!containsNode(node)) + { + // Put it in this graph + m_nodes.push_back(node); + return true; + } + else + { + return false; + } +} + +std::shared_ptr<TaskNode> +TaskGraph::addFunction(std::string name, std::function<void()> func) +{ + std::shared_ptr<TaskNode> node = std::make_shared<TaskNode>(func, name); + m_nodes.push_back(node); + return node; +} + +bool +TaskGraph::removeNode(std::shared_ptr<TaskNode> node) +{ + if (!containsNode(node)) + { + LOG(INFO) << "Tried to remove node " + node->m_name + " from graph but it doesn't contain the node."; + return false; + } + + // Erase edges + TaskNodeSet inputs = m_invAdjList[node]; + TaskNodeSet outputs = m_adjList[node]; + for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + removeEdge(*i, node); + } + for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + removeEdge(node, *i); + } + + // Finally remove the node from the graph + TaskNodeVector::iterator it = findNode(node); + if (it != endNode()) + { + m_nodes.erase(it); + } + return true; +} + +bool +TaskGraph::removeNodeAndRedirect(std::shared_ptr<TaskNode> node) +{ + if (!containsNode(node)) + { + LOG(INFO) << "Tried to remove node " + node->m_name + " from graph but it doesn't contain the node."; + return false; + } + + // Erase edges + TaskNodeSet inputs = m_invAdjList[node]; + TaskNodeSet outputs = m_adjList[node]; + for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + removeEdge(*i, node); + } + for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + removeEdge(node, *i); + } + + // Fix the edges + for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + for (TaskNodeSet::iterator j = outputs.begin(); j != outputs.end(); j++) + { + addEdge(*i, *j); + } + } + + // Finally remove the node from the graph + TaskNodeVector::iterator it = findNode(node); + if (it != endNode()) + { + m_nodes.erase(it); + } + + return true; +} + +void +TaskGraph::insertAfter(std::shared_ptr<TaskNode> refNode, std::shared_ptr<TaskNode> newNode) +{ + // Try to add to graph, if already exists, exit + if (!addNode(newNode)) + { + return; + } + + // Remove output edges + TaskNodeSet outputs = m_adjList[refNode]; // Copy (since we are modifying) + for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + removeEdge(refNode, *i); + } + + // Add refNode->newNode + addEdge(refNode, newNode); + + // Add newNode->outputs[i] + for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + addEdge(newNode, *i); + } +} + +void +TaskGraph::insertBefore(std::shared_ptr<TaskNode> refNode, std::shared_ptr<TaskNode> newNode) +{ + // Try to add to graph, if already exists, exit + if (!addNode(newNode)) + { + return; + } + + // Remove input edges + TaskNodeSet inputs = m_invAdjList[refNode]; // Copy (since we are modifying) + for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + removeEdge(*i, refNode); + } + + // inputs[i]->newNode + for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + addEdge(*i, newNode); + } + + // refNode->newNode + addEdge(newNode, refNode); +} + +bool +TaskGraph::isReachable(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode) +{ + const TaskNodeAdjList& adjList = getAdjList(); + + // Simple BFS + std::unordered_set<std::shared_ptr<TaskNode>> visitedNodes; + + // It inserts itself as well + std::queue<std::shared_ptr<TaskNode>> nodeStack; + nodeStack.push(srcNode); + while (!nodeStack.empty()) + { + std::shared_ptr<TaskNode> currNode = nodeStack.front(); + nodeStack.pop(); + + // If we've arrived at the desired node + if (destNode == currNode) + { + return true; + } + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(currNode); + for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<TaskNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + } + return false; +} + +void +TaskGraph::clear() +{ + m_nodes.clear(); + clearEdges(); + addNode(m_source); + addNode(m_sink); +} + +//std::shared_ptr<TaskGraph> +//TaskGraph::sum(std::shared_ptr<TaskGraph> graphA, std::shared_ptr<TaskGraph> graphB) +//{ +// std::shared_ptr<TaskGraph> results = std::make_shared<TaskGraph>(); +// // Get rid of the results source/sink +// results->m_source = nullptr; +// results->m_sink = nullptr; +// results->m_nodes.clear(); +// +// // Sum the nodes +// TaskNodeVector& nodesA = graphA->getNodes(); +// for (size_t i = 0; i < nodesA.size(); i++) +// { +// results->addNode(nodesA[i]); +// } +// TaskNodeVector& nodesB = graphB->getNodes(); +// for (size_t i = 0; i < nodesB.size(); i++) +// { +// results->addNode(nodesB[i]); +// } +// +// // Now sum the edges +// const TaskNodeAdjList& adjListA = graphA->getAdjList(); +// for (TaskNodeAdjList::const_iterator it = adjListA.begin(); it != adjListA.end(); it++) +// { +// const TaskNodeSet& outputNodes = it->second; +// for (TaskNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) +// { +// results->addEdge(it->first, *jt); +// } +// } +// const TaskNodeAdjList& adjListB = graphB->getAdjList(); +// for (TaskNodeAdjList::const_iterator it = adjListB.begin(); it != adjListB.end(); it++) +// { +// const TaskNodeSet& outputNodes = it->second; +// for (TaskNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) +// { +// results->addEdge(it->first, *jt); +// } +// } +// +// return results; +//} + +std::shared_ptr<TaskNodeList> +TaskGraph::topologicalSort(std::shared_ptr<TaskGraph> graph) +{ + // Compute the number of inputs to each node (we will remove these as we go) + std::unordered_map<std::shared_ptr<TaskNode>, size_t> numInputs; + + const TaskNodeAdjList& adjList = graph->getAdjList(); + const TaskNodeAdjList& invAdjList = graph->getInvAdjList(); + + for (TaskNodeAdjList::const_iterator i = invAdjList.begin(); i != invAdjList.end(); i++) + { + numInputs[i->first] = invAdjList.size(); + } + + // Create an edge blacklist for edge removal during algorithm + std::unordered_map<std::shared_ptr<TaskNode>, std::shared_ptr<TaskNode>> removedEdges; + + auto edgeHasBeenRemoved = [&](const std::shared_ptr<TaskNode>& node1, const std::shared_ptr<TaskNode>& node2) + { + return (removedEdges.count(node1) != 0) && (removedEdges[node1] == node2); + }; + + // Kahns algorithm (BFS/queue) + // iterate through all nodes (BFS or DFS) removing edges + // nodes are accepted when all input edges have been removed + std::queue<std::shared_ptr<TaskNode>> sources; + sources.push(graph->m_source); + std::shared_ptr<TaskNodeList> results = std::make_shared<TaskNodeList>(); + + while (!sources.empty()) + { + // Remove a node + std::shared_ptr<TaskNode> node = sources.front(); + sources.pop(); + + results->push_back(node); + + // As long as there are children + if (adjList.count(node) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(node); + for (TaskNodeSet::const_iterator it = outputNodes.begin(); it != outputNodes.end(); it++) + { + std::shared_ptr<TaskNode> childNode = *it; + // If edge is present + if (!edgeHasBeenRemoved(node, childNode)) + { + // Mark edge as removed + removedEdges[node] = childNode; + + // Decrement amount of inputs + numInputs[childNode]--; + if (numInputs[childNode] <= 0) + { + sources.push(childNode); + } + } + } + } + } + return results; +} + +std::shared_ptr<TaskGraph> +TaskGraph::resolveCriticalNodes(std::shared_ptr<TaskGraph> graph) +{ + std::shared_ptr<TaskGraph> results = std::make_shared<TaskGraph>(*graph); + + const TaskNodeAdjList& adjList = graph->getAdjList(); + const TaskNodeVector& nodes = graph->getNodes(); + + // Compute the levels of each node via DFS + std::unordered_map<std::shared_ptr<TaskNode>, int> depths; + { + std::unordered_set<std::shared_ptr<TaskNode>> visitedNodes; + + // DFS for the dependencies + std::stack<std::shared_ptr<TaskNode>> nodeStack; + depths[graph->getSource()] = 0; + nodeStack.push(graph->getSource()); + while (!nodeStack.empty()) + { + std::shared_ptr<TaskNode> currNode = nodeStack.top(); + int currLevel = depths[currNode]; + nodeStack.pop(); + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(currNode); + for (TaskNodeSet::const_iterator i = outputNodes.begin(); i != outputNodes.end(); i++) + { + std::shared_ptr<TaskNode> childNode = *i; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + depths[childNode] = currLevel + 1; + nodeStack.push(childNode); + } + } + } + } + } + + // Identify the set of critical nodes + TaskNodeVector critNodes; + for (size_t i = 0; i < nodes.size(); i++) + { + if (nodes[i]->m_isCritical) + { + critNodes.push_back(nodes[i]); + } + } + + // Compute the critical adjacency list + TaskNodeAdjList critAdjList; + for (size_t i = 0; i < critNodes.size(); i++) + { + std::unordered_set<std::shared_ptr<TaskNode>> visitedNodes; + + // DFS for the dependencies + std::stack<std::shared_ptr<TaskNode>> nodeStack; + nodeStack.push(critNodes[i]); + while (!nodeStack.empty()) + { + std::shared_ptr<TaskNode> currNode = nodeStack.top(); + nodeStack.pop(); + + // If you can reach one critical node from the other then they are adjacent + if (currNode->m_isCritical) + { + critAdjList[critNodes[i]].insert(currNode); + } + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(currNode); + for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<TaskNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + } + } + + // Now we know which critical nodes depend on each other (we are interested in those that aren't) + // For every critical pair + for (size_t i = 0; i < critNodes.size(); i++) + { + std::shared_ptr<TaskNode> srcNode = critNodes[i]; + for (size_t j = i + 1; j < critNodes.size(); j++) + { + std::shared_ptr<TaskNode> destNode = critNodes[j]; + // If the edge doesn't exist, either way + if ((critAdjList.count(srcNode) == 0 || critAdjList.at(srcNode).count(destNode) == 0) + && (critAdjList.count(destNode) == 0 || critAdjList.at(destNode).count(srcNode) == 0)) + { + // Add an edge between the critical nodes in the direction of levels + int leveli = depths[srcNode]; + int levelj = depths[destNode]; + // If i is below j, then j->i + if (leveli > levelj) + { + results->addEdge(destNode, srcNode); + } + else + { + results->addEdge(srcNode, destNode); + } + } + } + } + + return results; +} + +std::shared_ptr<TaskGraph> +TaskGraph::transitiveReduce(std::shared_ptr<TaskGraph> graph) +{ + // It's a bad idea to do this method if the graph is cyclic + if (isCyclic(graph)) + { + return nullptr; + } + + std::shared_ptr<TaskGraph> results = std::make_shared<TaskGraph>(*graph); + + // Copy the adjList + const TaskNodeAdjList adjList = results->getAdjList(); + + // For every edge in the graph + for (TaskNodeAdjList::const_iterator i = adjList.begin(); i != adjList.end(); i++) + { + std::shared_ptr<TaskNode> inputNode = i->first; + const TaskNodeSet& outputNodes = i->second; + for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<TaskNode> outputNode = *j; + // Edge inputNode->outputNode + + // Remove the edge and see if it still reaches + results->removeEdge(inputNode, outputNode); + + // If there still exists a path inputNode->outputNode, leave it removed + if (!results->isReachable(inputNode, outputNode)) + { + results->addEdge(inputNode, outputNode); + } + } + } + + return results; +} + +std::shared_ptr<TaskGraph> +TaskGraph::removeRedundantNodes(std::shared_ptr<TaskGraph> graph) +{ + std::shared_ptr<TaskGraph> results = std::make_shared<TaskGraph>(*graph); + + const TaskNodeAdjList& adjList = results->getAdjList(); + const TaskNodeAdjList& invAdjList = results->getInvAdjList(); + TaskNodeVector& nodes = results->getNodes(); + + for (size_t i = 0; i < nodes.size(); i++) + { + std::shared_ptr<TaskNode> node = nodes[i]; + + // These can't be removed (following code would break as they have no inputs/outputs) + if (node == graph->getSource() || node == graph->getSink()) + { + continue; + } + + // If node not functional and has single input/output it is removeable + if (!node->isFunctional()) + { + // Get copies of the inputs and outputs of the node + TaskNodeSet inputs = invAdjList.at(node); + TaskNodeSet outputs = adjList.at(node); + if (inputs.size() == 1 && outputs.size() == 1) + { + // Remove inputs/outputs of node + for (TaskNodeSet::iterator j = inputs.begin(); j != inputs.end(); j++) + { + results->removeEdge(*j, node); + } + for (TaskNodeSet::iterator j = outputs.begin(); j != outputs.end(); j++) + { + results->removeEdge(node, *j); + } + + // Fix the edges + for (TaskNodeSet::iterator j = inputs.begin(); j != inputs.end(); j++) + { + for (TaskNodeSet::iterator k = outputs.begin(); k != outputs.end(); k++) + { + results->addEdge(*j, *k); + } + } + + // Finally remove the node from the graph + nodes.erase(nodes.begin() + i); + + // If node was removed, don't advance + i--; + } + } + } + return results; +} + +bool +TaskGraph::isCyclic(std::shared_ptr<TaskGraph> graph) +{ + // Brute force, DFS every node to find it again + const TaskNodeAdjList& adjList = graph->getAdjList(); + const TaskNodeVector& nodes = graph->getNodes(); + for (size_t i = 0; i < nodes.size(); i++) + { + std::unordered_set<std::shared_ptr<TaskNode>> visitedNodes; + + // DFS for the dependencies (start at children instead of itself) + std::stack<std::shared_ptr<TaskNode>> nodeStack; + // Add children to stack if not yet visited + if (adjList.count(nodes[i]) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(nodes[i]); + for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<TaskNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + while (!nodeStack.empty()) + { + std::shared_ptr<TaskNode> currNode = nodeStack.top(); + nodeStack.pop(); + + // If we revisit a node then it must be cyclic + if (nodes[i] == currNode) + { + return true; + } + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(currNode); + for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<TaskNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + } + } + + return false; +} + +std::unordered_map<std::shared_ptr<TaskNode>, std::string> +TaskGraph::getUniqueNodeNames(std::shared_ptr<TaskGraph> graph, bool apply) +{ + // Produce non colliding names + std::unordered_map<std::shared_ptr<TaskNode>, std::string> nodeNames; + std::unordered_map<std::string, int> names; + const TaskNodeVector& nodes = graph->getNodes(); + for (size_t i = 0; i < nodes.size(); i++) + { + nodeNames[nodes[i]] = nodes[i]->m_name; + names[nodes[i]->m_name]++; + } + // Adjust names + for (std::unordered_map<std::shared_ptr<TaskNode>, std::string>::iterator it = nodeNames.begin(); + it != nodeNames.end(); it++) + { + int nameIter = 0; + std::string currName = it->second; + // If we can find a node with this name, increment name counter and try again + while (names[currName] > 1) + { + names[currName]--; + currName = it->second + std::to_string(nameIter); + names[currName]++; + nameIter++; + } + nodeNames[it->first] = currName; + } + if (apply) + { + for (std::shared_ptr<TaskNode> node : nodes) + { + node->m_name = nodeNames[node]; + } + } + return nodeNames; +} + +std::unordered_map<std::shared_ptr<TaskNode>, double> +TaskGraph::getNodeStartTimes(std::shared_ptr<TaskGraph> graph) +{ + const TaskNodeAdjList& adjList = graph->getAdjList(); + + // Setup a map for total elapsed times at each node + std::unordered_map<std::shared_ptr<TaskNode>, double> startTimes; + + { + std::unordered_set<std::shared_ptr<TaskNode>> visitedNodes; + + // BFS down the tree computing total times + std::stack<std::shared_ptr<TaskNode>> nodeStack; + startTimes[graph->getSource()] = 0.0; + nodeStack.push(graph->getSource()); + while (!nodeStack.empty()) + { + std::shared_ptr<TaskNode> currNode = nodeStack.top(); + nodeStack.pop(); + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const TaskNodeSet& outputNodes = adjList.at(currNode); + for (TaskNodeSet::const_iterator i = outputNodes.begin(); i != outputNodes.end(); i++) + { + std::shared_ptr<TaskNode> childNode = *i; + + // Determine the time it would take to start this child + const double childStartTime = startTimes[currNode] + currNode->m_computeTime; + if (childStartTime > startTimes[childNode]) + { + // Accept the longest time as nodes can't continue until all child inputs complete + startTimes[childNode] = childStartTime; + } + // If not visited yet add child to stack + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + //times[childNode] = times[currNode] + childNode->m_computeTime; + nodeStack.push(childNode); + } + } + } + } + } + return startTimes; +} + +TaskNodeList +TaskGraph::getCriticalPath(std::shared_ptr<TaskGraph> graph) +{ + std::unordered_map<std::shared_ptr<TaskNode>, double> nodeStartTimes = getNodeStartTimes(graph); + + // Now backtrack to acquire the path of longest duration + const TaskNodeAdjList& invAdjList = graph->getInvAdjList(); + std::list<std::shared_ptr<TaskNode>> results; + { + std::shared_ptr<TaskNode> currNode = graph->getSink(); + // Starting from the sink, always choose the input with the longer start time + while (currNode != graph->getSource()) + { + results.push_front(currNode); + std::shared_ptr<TaskNode> longestNode = nullptr; + double maxTime = 0.0; + + // For every parent + if (invAdjList.count(currNode) != 0) + { + const TaskNodeSet& inputNodes = invAdjList.at(currNode); + for (TaskNodeSet::const_iterator i = inputNodes.begin(); i != inputNodes.end(); i++) + { + std::shared_ptr<TaskNode> parentNode = *i; + if (nodeStartTimes[parentNode] >= maxTime) + { + maxTime = nodeStartTimes[parentNode]; + longestNode = parentNode; + } + } + } + + // Move to the node with the longest time + currNode = longestNode; + } + } + results.push_front(graph->getSource()); + return results; +} +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskGraph.h b/Source/Common/TaskGraph/imstkTaskGraph.h new file mode 100644 index 0000000000000000000000000000000000000000..79c2d99c5a0e8a4176c6a085217c003dba877b12 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskGraph.h @@ -0,0 +1,239 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkTaskNode.h" +#include <list> +#include <memory> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +namespace imstk +{ +using TaskNodeVector = std::vector<std::shared_ptr<TaskNode>>; +using TaskNodeList = std::list<std::shared_ptr<TaskNode>>; +using TaskNodeSet = std::unordered_set<std::shared_ptr<TaskNode>>; +using TaskNodeAdjList = std::unordered_map<std::shared_ptr<TaskNode>, TaskNodeSet>; + +/// +/// \class TaskGraph +/// +/// \brief Base class for TaskGraph, a collection of TaskNode's. Maintains nodes, +/// adjacencyList, and invAdjacencyList. +/// +class TaskGraph +{ +public: + TaskGraph(std::string sourceName = "Source", std::string sinkName = "Sink"); + virtual ~TaskGraph() = default; + +public: + std::shared_ptr<TaskNode> getSource() { return m_source; } + std::shared_ptr<TaskNode> getSink() { return m_sink; } + + /// + /// \brief Get the nodes belonging to this graph + /// + TaskNodeVector& getNodes() { return m_nodes; } + + /// + /// \brief Get the edges belonging to this graph + /// + TaskNodeAdjList& getAdjList() { return m_adjList; } + + /// + /// \brief Get the inverse edges belonging to this graph + /// + TaskNodeAdjList& getInvAdjList() { return m_invAdjList; } + +// Node operations +public: + /// + /// \brief Linear search for node by name within this graph + /// + TaskNodeVector::iterator findNode(std::string name); + TaskNodeVector::const_iterator findNode(std::string name) const; + /// + /// \brief Linear search for node within this graph + /// + TaskNodeVector::iterator findNode(std::shared_ptr<TaskNode> node); + TaskNodeVector::const_iterator findNode(std::shared_ptr<TaskNode> node) const; + + /// + /// \brief Check if node exists in this graph + /// + bool containsNode(std::shared_ptr<TaskNode> node) const; + + /// + /// \brief Returns the start of the node container + /// + TaskNodeVector::iterator endNode() { return m_nodes.end(); } + TaskNodeVector::const_iterator endNode() const { return m_nodes.end(); } + /// + /// \brief Returns the start of the node container + /// + TaskNodeVector::iterator beginNode() { return m_nodes.begin(); } + TaskNodeVector::const_iterator beginNode() const { return m_nodes.begin(); } + + /// + /// \brief Adds a node to the graph, returns if successful. + /// Returns false and fails if node already exists in graph + /// + bool addNode(std::shared_ptr<TaskNode> node); + + /// + /// \brief Creates a node for the function, adds it to the graph + /// + std::shared_ptr<TaskNode> addFunction(std::string name, std::function<void()> func); + + /// + /// \brief Removes node from the graph and all relevant edges + /// Returns false and fails if node is not present in graph + /// + bool removeNode(std::shared_ptr<TaskNode> node); + + /// + /// \brief Removes node from the graph and all relevant edges. + /// Any incoming edges to the node are redirected to all its outputs + /// + bool removeNodeAndRedirect(std::shared_ptr<TaskNode> node); + + /// + /// \brief newNode gets placed after refNode & added to the graph. newNode takes on refNode's outputs + /// + void insertAfter(std::shared_ptr<TaskNode> refNode, std::shared_ptr<TaskNode> newNode); + + /// + /// \brief newNode gets placed before refNode & added to the graph. newNode takes on refNode's inputs + /// + void insertBefore(std::shared_ptr<TaskNode> refNode, std::shared_ptr<TaskNode> newNode); + +// Edge operations +public: + /// + /// \brief Returns whether or not this graph contains the given directed edge + /// + bool containsEdge(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode); + + /// + /// \brief Adds a directed edge to the graph (doesn't check if nodes are in graph) + /// + void addEdge(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode); + + /// + /// \brief Attachs another TaskGraph as a subgraph (copies nodes and edges, then connects source->subgraph::source, subgraph::sink->sink), + /// source and sink must exist in this graph. Also serves as a graph sum between this and subgraph + /// + void nestGraph(std::shared_ptr<TaskGraph> subgraph, std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink); + + /// + /// \brief Removes an edge from the graph (removes from adjList and invAdjList, cleans) + /// + void removeEdge(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode); + + /// + /// \brief Returns true if srcNode reaches destNode + /// + bool isReachable(std::shared_ptr<TaskNode> srcNode, std::shared_ptr<TaskNode> destNode); + +public: + /// + /// \brief Removes all nodes & edges from the graph. Source and sink nodes maintained. + /// + void clear(); + + /// + /// \brief Removes all edges from the graph + /// + void clearEdges() + { + m_adjList.clear(); + m_invAdjList.clear(); + } + +// Graph algorithms, todo: Move into filtering module +public: + /// + /// \brief Graph sum, shared references are considered identical nodes, source/sink of results invalidated/nullptr + /// + //static std::shared_ptr<TaskGraph> sum(std::shared_ptr<TaskGraph> graphA, std::shared_ptr<TaskGraph> graphB); + + /// + /// \brief Topological sort of all nodes within graph + /// + static std::shared_ptr<TaskNodeList> topologicalSort(std::shared_ptr<TaskGraph> graph); + + /// + /// \brief Makes sure no two *critical nodes* run at the same time by establishing an edge between them. + /// + static std::shared_ptr<TaskGraph> resolveCriticalNodes(std::shared_ptr<TaskGraph> graph); + + /// + /// \brief Remove redudant edges. Removal is such that all vertices are still reachable and graph goes from source->sink + /// returns nullptr if failed. Only fails if graph is cyclic. + /// + static std::shared_ptr<TaskGraph> transitiveReduce(std::shared_ptr<TaskGraph> graph); + + /// + /// \brief Removes nullptr/nonfunctional TaskNode's that don't split/join + /// + static std::shared_ptr<TaskGraph> removeRedundantNodes(std::shared_ptr<TaskGraph> graph); + + /// + /// \brief Simplifies the graph in a way that retains functionality + /// Performs transitive reduction then redundant node removal to remove redundant nodes and edges + /// + static std::shared_ptr<TaskGraph> reduce(std::shared_ptr<TaskGraph> graph) + { + return removeRedundantNodes(transitiveReduce(graph)); + } + + /// + /// \brief Returns if Graph is cyclic or not + /// + static bool isCyclic(std::shared_ptr<TaskGraph> graph); + + /// + /// \brief Nodes may not have unique names. Iterates the names with numeric postfix to generate uniques + /// + static std::unordered_map<std::shared_ptr<TaskNode>, std::string> getUniqueNodeNames(std::shared_ptr<TaskGraph> graph, bool apply = false); + + /// + /// \brief Gets the completion times of each node (source = ~0s) + /// + static std::unordered_map<std::shared_ptr<TaskNode>, double> getNodeStartTimes(std::shared_ptr<TaskGraph> graph); + + /// + /// \brief Computes the critical path + /// + static TaskNodeList getCriticalPath(std::shared_ptr<TaskGraph> graph); + +protected: + TaskNodeVector m_nodes; + TaskNodeAdjList m_adjList; ///> This gives the outputs of every node + TaskNodeAdjList m_invAdjList; ///> This gives the inputs of every node + + std::shared_ptr<TaskNode> m_source = nullptr; + std::shared_ptr<TaskNode> m_sink = nullptr; +}; +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskGraphController.cpp b/Source/Common/TaskGraph/imstkTaskGraphController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..faa7d9b5729d4eb2c90407542d4a6be16b027d26 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskGraphController.cpp @@ -0,0 +1,48 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkTaskGraphController.h" +#include "imstkTaskGraph.h" +#include "imstkLogger.h" + +namespace imstk +{ +bool +TaskGraphController::initialize() +{ + // Ensure the source is reachable from the sink and the graph is not cyclic + // todo: Safer check would be to ensure all nodes reach sink + if (!m_graph->isReachable(m_graph->getSource(), m_graph->getSink())) + { + LOG(WARNING) << "TaskGraph Sink not reachable from source. Graph initialization failed."; + return false; + } + + if (TaskGraph::isCyclic(m_graph)) + { + LOG(WARNING) << "TaskGraph is cyclic. Graph initialization failed."; + return false; + } + + init(); + return true; +} +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskGraphController.h b/Source/Common/TaskGraph/imstkTaskGraphController.h new file mode 100644 index 0000000000000000000000000000000000000000..29786544b84eece69a16306c54f574f543666445 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskGraphController.h @@ -0,0 +1,67 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <memory> + +namespace imstk +{ +class TaskGraph; + +/// +/// \class TaskGraphController +/// +/// \brief Base class for TaskGraph controllers which are responsible for executing the TaskGraph +/// +class TaskGraphController +{ +public: + TaskGraphController() = default; + virtual ~TaskGraphController() = default; + +public: + virtual void setTaskGraph(std::shared_ptr<TaskGraph> graph) { this->m_graph = graph; } + + std::shared_ptr<TaskGraph> getTaskGraph() const { return m_graph; } + + /// + /// \brief Initialization of the TaskGraphController, good for anything the controller may need + /// to do after it recieves input, before execution, but not do everytime execution is called. + /// Returns if successful + /// + bool initialize(); + + /// + /// \brief Executes the TaskGraph + /// + virtual void execute() = 0; + +protected: + /// + /// \brief Subclass initialization call + /// + virtual void init() { } + +protected: + std::shared_ptr<TaskGraph> m_graph = nullptr; +}; +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskGraphVizWriter.cpp b/Source/Common/TaskGraph/imstkTaskGraphVizWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bd010b468cb5e2e5361d7661209c010c3fee0d8 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskGraphVizWriter.cpp @@ -0,0 +1,151 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkTaskGraphVizWriter.h" +#include "imstkLogger.h" +#include "imstkTaskGraph.h" +#include "imstkColor.h" +#include <fstream> + +namespace imstk +{ +void +TaskGraphVizWriter::write() +{ + if (m_inputGraph == nullptr) + { + LOG(WARNING) << "No input set, unable to write ComputeGraph"; + return; + } + + // Compute range of compute times for color function + double maxTime = std::numeric_limits<double>::min(); + if (m_writeNodeComputeTimesColor) + { + for (auto node : m_inputGraph->getNodes()) + { + if (node->m_computeTime > maxTime) + { + maxTime = node->m_computeTime; + } + } + } + + // Hardcoded color function + std::vector<Color> colorFunc = std::vector<Color>(3); + colorFunc[0] = Color::Blue; + colorFunc[1] = Color::Green; + colorFunc[2] = Color::Red; + const int colorFuncExtent = static_cast<int>(colorFunc.size() - 1); + + // Compute the critical path (ie: longest path in duration) + TaskNodeList critPath; + if (m_highlightCriticalPath) + { + critPath = TaskGraph::getCriticalPath(m_inputGraph); + } + // Test if edge exists in critical path by linear searching + auto edgeExists = [&](const std::shared_ptr<TaskNode>& a, const std::shared_ptr<TaskNode>& b) + { + TaskNodeList::iterator srcNode = std::find(critPath.begin(), critPath.end(), a); + // If srcNode was found and the next node is b + return (srcNode != critPath.end() && *std::next(srcNode) == b); + }; + + // Write the file + { + std::ofstream file; + file.open(m_fileName); + + if (!file.is_open() || file.fail()) + { + return; + } + + file << + "digraph imstkDependency\n" + "{\n" + "style=filled;\n" + "color=lightgrey;\n" + "edge[arrowhead=vee, arrowtail=inv, arrowsize=.7, color=grey20];\n"; + + // Write the node section + const TaskNodeVector& nodes = m_inputGraph->getNodes(); + std::unordered_map<std::shared_ptr<TaskNode>, std::string> nodeIds; + for (size_t i = 0; i < nodes.size(); i++) + { + const std::string nodeUniqueName = "node" + std::to_string(i); + nodeIds[nodes[i]] = nodeUniqueName; + + file << "\"" << nodeUniqueName << "\" ["; + + // Write label property + if (m_writeNodeComputeTimesText) + { + file << " label=\"" << nodes[i]->m_name << " (" << nodes[i]->m_computeTime << "ms)" << "\""; + } + else + { + file << " label=\"" << nodes[i]->m_name << '\"'; + } + + // Write style property + file << " style=filled"; + + // Write color property + if (m_writeNodeComputeTimesColor) + { + const double t = nodes[i]->m_computeTime / maxTime; + const int i1 = static_cast<int>(t * colorFuncExtent); + const int i2 = std::min(colorFuncExtent, i1 + 1); + Color color = Color::lerpRgb(colorFunc[i1], colorFunc[i2], t); + file << " color=\"#" << color.rgbHex() << "\""; + } + else + { + file << " color=cornflowerblue"; + } + file << "];" << std::endl; + } + + // Write out all the edges + const TaskNodeAdjList& adjList = m_inputGraph->getAdjList(); + for (TaskNodeAdjList::const_iterator it = adjList.begin(); it != adjList.end(); it++) + { + std::shared_ptr<TaskNode> srcNode = it->first; + const TaskNodeSet& outputNodes = it->second; + for (TaskNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) + { + std::shared_ptr<TaskNode> destNode = *jt; + file << '\"' << nodeIds[srcNode] << '\"' << " -> " << '\"' << nodeIds[destNode] << '\"'; + + if (m_highlightCriticalPath && edgeExists(srcNode, destNode)) + { + file << "[color=red]"; + } + file << std::endl; + } + } + file << "}\n"; + file.close(); + } +} +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskGraphVizWriter.h b/Source/Common/TaskGraph/imstkTaskGraphVizWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..1f913b7f597b9926e35b7276ff3dc7082a61c313 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskGraphVizWriter.h @@ -0,0 +1,87 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <memory> +#include <string> + +namespace imstk +{ +class TaskGraph; + +/// +/// \class TaskGraphVizWriter +/// +/// \brief Writes a TaskGraph to an svg file. Produces unique node names from duplicates with postfix. +/// Can also color by node compute time and highlight the critical path +/// +class TaskGraphVizWriter +{ +public: + TaskGraphVizWriter() = default; + virtual ~TaskGraphVizWriter() = default; + +public: + /// + /// \brief The graph to write + /// + void setInput(std::shared_ptr<TaskGraph> graph) { this->m_inputGraph = graph; } + + /// + /// \brief The filename and path to write too + /// + void setFileName(std::string fileName) { this->m_fileName = fileName; } + + /// + /// \brief If on, will highlight the critical path in red + /// + void setHighlightCriticalPath(bool highlightCriticalPath) { this->m_highlightCriticalPath = highlightCriticalPath; } + + /// + /// \brief If on, will write the time the node took to complete as a color + /// + void setWriteNodeComputeTimesColor(bool writeNodeComputeTimesColor) { this->m_writeNodeComputeTimesColor = writeNodeComputeTimesColor; } + + /// + /// \brief If on, will write the time the node took to complete in name as text + /// + void setWriteNodeComputeTimesText(bool writeNodeComputeTimesText) { this->m_writeNodeComputeTimesText = writeNodeComputeTimesText; } + + std::shared_ptr<TaskGraph> getInput() const { return m_inputGraph; } + const std::string& getFileName() const { return m_fileName; } + bool getHighlightCriticalPath() const { return m_highlightCriticalPath; } + bool getWriteNodeComputeTimesColor() const { return m_writeNodeComputeTimesColor; } + bool getWriteNodeComputeTimesText() const { return m_writeNodeComputeTimesText; } + + /// + /// \brief Writes the graph to a file given the filename + /// + void write(); + +private: + std::shared_ptr<TaskGraph> m_inputGraph = nullptr; + std::string m_fileName = ""; + bool m_highlightCriticalPath = false; + bool m_writeNodeComputeTimesColor = false; + bool m_writeNodeComputeTimesText = false; +}; +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskNode.cpp b/Source/Common/TaskGraph/imstkTaskNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e401244ed3a842c91f8176c1b134d0e0476c54c5 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskNode.cpp @@ -0,0 +1,50 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkTaskNode.h" +#include "imstkTimer.h" + +namespace imstk +{ +void +TaskNode::execute() +{ + if (m_enabled && m_func != nullptr) + { + if (!m_enableTiming) + { + m_func(); + } + else + { + StopWatch timer; + timer.start(); + m_func(); + m_computeTime = timer.getTimeElapsed(); + timer.stop(); + } + } + else + { + m_computeTime = 0.0; + } +} +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTaskNode.h b/Source/Common/TaskGraph/imstkTaskNode.h new file mode 100644 index 0000000000000000000000000000000000000000..10bdccb27e74e95685e4c6f8fcab976febf36ecb --- /dev/null +++ b/Source/Common/TaskGraph/imstkTaskNode.h @@ -0,0 +1,69 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <functional> +#include <string> + +namespace imstk +{ +/// +/// \class TaskNode +/// +/// \brief Base class for TaskGraph nodes +/// +class TaskNode +{ +public: + TaskNode() = default; + TaskNode(std::function<void()> func, std::string name = "none", bool isCritical = false) : + m_func(func), m_name(name), m_isCritical(isCritical), m_computeTime(0.0) + { + } + + virtual ~TaskNode() = default; + +public: + void setFunction(std::function<void()> func) { this->m_func = func; } + void setEnabled(bool enabled) { this->m_enabled = enabled; } + + /// + /// \brief Returns true if function is nullptr + /// + bool isFunctional() const { return m_func != nullptr; } + + /// + /// \brief Calls the function pointer provided if node enabled + /// + virtual void execute(); + +public: + std::string m_name = "none"; + bool m_enabled = true; + bool m_isCritical = false; + double m_computeTime = 0.0; + bool m_enableTiming = false; + +protected: + std::function<void()> m_func = nullptr; ///> Don't allow user to call directly (must use execute) +}; +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp b/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15581278657cd1de25eca99c46b5d7accbf8fec6 --- /dev/null +++ b/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp @@ -0,0 +1,103 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkTbbTaskGraphController.h" +#include "imstkTaskGraph.h" +#include <tbb/tbb.h> + +namespace imstk +{ +class NodeTbbTask : public tbb::task +{ +public: + NodeTbbTask(std::shared_ptr<TaskNode> node) : m_node(node) { } + +public: + task* execute() override + { + __TBB_ASSERT(ref_count() == 0, NULL); + + m_node->execute(); + + for (size_t i = 0; i < successors.size(); i++) + { + if (successors[i]->decrement_ref_count() == 0) + { + spawn(*successors[i]); + } + } + return NULL; + } + +public: + std::shared_ptr<TaskNode> m_node = nullptr; + std::vector<NodeTbbTask*> successors; +}; + +void +TbbTaskGraphController::execute() +{ + // Create a Task for every node + const TaskNodeVector& nodes = m_graph->getNodes(); + if (nodes.size() == 0) + { + return; + } + + // Create a task for every node + std::unordered_map<std::shared_ptr<TaskNode>, NodeTbbTask*> tasks; + tasks.reserve(nodes.size()); + + for (size_t i = 0; i < nodes.size(); i++) + { + std::shared_ptr<TaskNode> node = nodes[i]; + tasks[node] = new (tbb::task::allocate_root())NodeTbbTask(node); + } + // Increment successor reference counts + const TaskNodeAdjList& adjList = m_graph->getAdjList(); + // For every node in graph + for (size_t i = 0; i < nodes.size(); i++) + { + // If it contains outputs + if (adjList.count(nodes[i]) != 0) + { + // For every output + const TaskNodeSet& outputNodes = adjList.at(nodes[i]); + for (TaskNodeSet::const_iterator it = outputNodes.begin(); it != outputNodes.end(); it++) + { + // Lookup the task of the node + NodeTbbTask* successor = tasks[*it]; + tasks[nodes[i]]->successors.push_back(successor); + successor->increment_ref_count(); + } + } + } + + NodeTbbTask* startTask = tasks[m_graph->getSource()]; + NodeTbbTask* finalTask = tasks[m_graph->getSink()]; + + // Extra ref count on the final task + finalTask->increment_ref_count(); + finalTask->spawn_and_wait_for_all(*startTask); + finalTask->execute(); // Execute final task explicitly + tbb::task::destroy(*finalTask); +} +} \ No newline at end of file diff --git a/Source/Common/TaskGraph/imstkTbbTaskGraphController.h b/Source/Common/TaskGraph/imstkTbbTaskGraphController.h new file mode 100644 index 0000000000000000000000000000000000000000..d17155958d75ef681ab7fcb371fbf1e183fb20ab --- /dev/null +++ b/Source/Common/TaskGraph/imstkTbbTaskGraphController.h @@ -0,0 +1,38 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkTaskGraphController.h" + +namespace imstk +{ +/// +/// \class TbbTaskGraphController +/// +/// \brief This class runs an input TaskGraph in parallel using tbb tasks +/// +class TbbTaskGraphController : public TaskGraphController +{ +public: + void execute() override; +}; +}; \ No newline at end of file diff --git a/Source/Common/imstkColor.cpp b/Source/Common/imstkColor.cpp index 8a8cc79284da498befb60c8693d2a000eee12497..1bd6848732073986ab915ea04c6c6084d97f9f81 100644 --- a/Source/Common/imstkColor.cpp +++ b/Source/Common/imstkColor.cpp @@ -20,8 +20,9 @@ =========================================================================*/ #include "imstkColor.h" - -#include <g3log/g3log.hpp> +#include "imstkLogger.h" +#include <iomanip> +#include <sstream> namespace imstk { @@ -166,6 +167,19 @@ Color::lerpRgb(const Color& start, const Color& end, const double t) return Color(start + (end - start) * t, 1.0); } +std::string +Color::rgbHex() +{ + std::stringstream ss; + const int red = static_cast<int>(r * 255.0); + const int green = static_cast<int>(g * 255.0); + const int blue = static_cast<int>(b * 255.0); + ss << std::setfill('0') << std::setw(2) << std::hex << red; + ss << std::setfill('0') << std::setw(2) << std::hex << green; + ss << std::setfill('0') << std::setw(2) << std::hex << blue; + return ss.str(); +} + Color operator*(const Color& color_lhs, const Color& color_rhs) { diff --git a/Source/Common/imstkColor.h b/Source/Common/imstkColor.h index 7b7ca429731a51690be6df8b15094f4eb1833933..052519e51e4f58ef5251204d641517b3fbd82100 100644 --- a/Source/Common/imstkColor.h +++ b/Source/Common/imstkColor.h @@ -101,6 +101,8 @@ struct Color /// const double* getValue() const; + std::string rgbHex(); + /// /// \brief interpolate between two colors by ratio t /// diff --git a/Source/Constraint/CMakeLists.txt b/Source/Constraint/CMakeLists.txt index aab16ab80c97ec608d61a539b678e2b7b4f9b759..df474dc4b0d6e0c0fc3f540bd351fe3b70751061 100644 --- a/Source/Constraint/CMakeLists.txt +++ b/Source/Constraint/CMakeLists.txt @@ -3,8 +3,11 @@ #----------------------------------------------------------------------------- include(imstkAddLibrary) imstk_add_library( Constraints + SUBDIR_LIST + PbdConstraints DEPENDS Common + DataStructures Geometry ) diff --git a/Source/Constraint/PbdConstraints/imstkPbdAreaConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdAreaConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6121b15e54634b7d15e52f26976341c327a21ed0 --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdAreaConstraint.cpp @@ -0,0 +1,81 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdAreaConstraint.h" + +namespace imstk +{ +void +PbdAreaConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, + const size_t& pIdx1, + const size_t& pIdx2, + const double k) +{ + m_vertexIds[0] = pIdx0; + m_vertexIds[1] = pIdx1; + m_vertexIds[2] = pIdx2; + + this->m_stiffness = k; + this->m_compliance = 1.0 / k; + + const Vec3d& p0 = initVertexPositions[pIdx0]; + const Vec3d& p1 = initVertexPositions[pIdx1]; + const Vec3d& p2 = initVertexPositions[pIdx2]; + + m_restArea = 0.5 * (p1 - p0).cross(p2 - p0).norm(); +} + +bool +PbdAreaConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const +{ + const auto i1 = m_vertexIds[0]; + const auto i2 = m_vertexIds[1]; + const auto i3 = m_vertexIds[2]; + + const Vec3d& p0 = currVertexPositions[i1]; + const Vec3d& p1 = currVertexPositions[i2]; + const Vec3d& p2 = currVertexPositions[i3]; + + const Vec3d e0 = p0 - p1; + const Vec3d e1 = p1 - p2; + const Vec3d e2 = p2 - p0; + + Vec3d n = e0.cross(e1); + c = 0.5 * n.norm(); + + if (c < m_epsilon) + { + return false; + } + + n /= 2 * c; + c -= m_restArea; + + dcdx[0] = e1.cross(n); + dcdx[1] = e2.cross(n); + dcdx[2] = e0.cross(n); + + return true; +} +} diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdAreaConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdAreaConstraint.h similarity index 74% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdAreaConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdAreaConstraint.h index 02d5099dbc31eea0da2ac13e1c06d9d4a47c67bb..94c7d57b9f82ba56d02a4eeabf6c7cdcbd0ec595 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdAreaConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdAreaConstraint.h @@ -46,19 +46,16 @@ public: /// /// \brief Initializes the area constraint /// - void initConstraint(PbdModel& model, - const size_t& pIdx1, - const size_t& pIdx2, - const size_t& pIdx3, - const double k = 2.5); + void initConstraint( + const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx1, const size_t& pIdx2, const size_t& pIdx3, + const double k = 2.5); - /// - /// \brief Solves the area constraint - /// - bool solvePositionConstraint(PbdModel& model) override; + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const override; public: - double m_restArea = 0.; ///> Area at the rest position - double m_stiffness = 0.; ///> Stiffness of the area constraint + double m_restArea = 0.; ///> Area at the rest position }; } // imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdBendConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdBendConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9bb0315a17ef4617bd539746ca0feeda88b6947f --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdBendConstraint.cpp @@ -0,0 +1,82 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdBendConstraint.h" + +namespace imstk +{ +void +PbdBendConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, + const size_t& pIdx1, + const size_t& pIdx2, + const double k) +{ + m_vertexIds[0] = pIdx0; + m_vertexIds[1] = pIdx1; + m_vertexIds[2] = pIdx2; + + m_stiffness = k; + m_compliance = 1.0 / k; + + const Vec3d& p0 = initVertexPositions[pIdx0]; + const Vec3d& p1 = initVertexPositions[pIdx1]; + const Vec3d& p2 = initVertexPositions[pIdx2]; + + // Instead of using the angle between the segments we can use the distance + // from the center of the triangle + const Vec3d& center = (p0 + p1 + p2) / 3.0; + m_restLength = (p1 - center).norm(); +} + +bool +PbdBendConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const +{ + const size_t i0 = m_vertexIds[0]; + const size_t i1 = m_vertexIds[1]; + const size_t i2 = m_vertexIds[2]; + + const Vec3d& p0 = currVertexPositions[i0]; + const Vec3d& p1 = currVertexPositions[i1]; + const Vec3d& p2 = currVertexPositions[i2]; + + // Move towards triangle center + const Vec3d& center = (p0 + p1 + p2) / 3.0; + const Vec3d& diff = p1 - center; + const double dist = diff.norm(); + + if (dist < m_epsilon) + { + return false; + } + + c = dist - m_restLength; + + dcdx.resize(3); + dcdx[0] = (-2.0 / dist) * diff; + dcdx[1] = -2.0 * dcdx[0]; + dcdx[2] = dcdx[0]; + + return true; +} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdBendConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdBendConstraint.h similarity index 77% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdBendConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdBendConstraint.h index c51f9efc11d98f8a0385e49c627177ad11e0c6e0..c2e68dc2f8702512a4f14127834fe361994831f2 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdBendConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdBendConstraint.h @@ -53,22 +53,23 @@ public: / p2 \param model - \param pIdx1 index of p0 + \param pIdx0 index of p0 \param pIdx2 index of p1 \param pIdx3 index of p2 \param k stiffness */ - void initConstraint(PbdModel& model, - const size_t& pIdx1, const size_t& pIdx2, - const size_t& pIdx3, const double k); + void initConstraint( + const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx1, const size_t& pIdx2, + const size_t& pIdx3, const double k); /// - /// \brief Solves the bend constraint + /// \brief Compute the value and gradient of constraint /// - bool solvePositionConstraint(PbdModel& model) override; - + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPosition, + double& c, + StdVectorOfVec3d& dcdx) const override; public: double m_restLength = 0.; ///> Rest length - double m_stiffness = 0.; ///> Bend stiffness }; } //imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdCollisionConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdCollisionConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dda274ce9101a8d8b14404fd0ee0bd6062378fa5 --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdCollisionConstraint.cpp @@ -0,0 +1,83 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdCollisionConstraint.h" +#include "imstkLogger.h" + +namespace imstk +{ +PbdCollisionConstraint::PbdCollisionConstraint(const unsigned int& n1, const unsigned int& n2) +{ + m_bodiesFirst.resize(n1); + m_bodiesSecond.resize(n2); +} + +void +PbdCollisionConstraint::projectConstraint(const StdVectorOfReal& invMassA, + const StdVectorOfReal& invMassB, + StdVectorOfVec3d& posA, + StdVectorOfVec3d& posB) +{ + double c; + StdVectorOfVec3d dcdxA; + StdVectorOfVec3d dcdxB; + + bool update = this->computeValueAndGradient(posA, posB, c, dcdxA, dcdxB); + if (!update) + { + return; + } + + double lambda = 0.0; + + for (size_t i = 0; i < m_bodiesFirst.size(); ++i) + { + lambda += invMassA[m_bodiesFirst[i]] * dcdxA[i].squaredNorm(); + } + + for (size_t i = 0; i < m_bodiesSecond.size(); ++i) + { + lambda += invMassB[m_bodiesSecond[i]] * dcdxB[i].squaredNorm(); + } + + lambda = c / lambda; + + for (size_t i = 0, vid = 0; i < m_bodiesFirst.size(); ++i) + { + vid = m_bodiesFirst[i]; + if (invMassA[vid] > 0.0) + { + posA[vid] -= invMassA[vid] * lambda * dcdxA[i] * m_configA->m_stiffness; + } + } + + for (size_t i = 0, vid = 0; i < m_bodiesSecond.size(); ++i) + { + vid = m_bodiesSecond[i]; + if (invMassB[vid] > 0.0) + { + posB[vid] -= invMassB[vid] * lambda * dcdxB[i] * m_configB->m_stiffness; + } + } + + return; +} +} // imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdCollisionConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdCollisionConstraint.h new file mode 100644 index 0000000000000000000000000000000000000000..bd5270f7224acfaa8362fa49001b2e5bac916a5a --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdCollisionConstraint.h @@ -0,0 +1,103 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkMath.h" + +namespace imstk +{ +/// +/// \struct PbdCollisionConstraintConfig +/// \brief Parameters for PBD collision constraints +/// +struct PbdCollisionConstraintConfig +{ + PbdCollisionConstraintConfig(double proximity, double stiffness) : m_proximity(proximity), m_stiffness(stiffness) { } + + double m_proximity = 0.1; ///> Proximity for static collision + double m_stiffness = 1.0; ///> Stiffness for collision +}; + +/// +/// \class PbdCollisionConstraint +/// +/// \brief +/// +class PbdCollisionConstraint +{ +public: + enum class Type + { + EdgeEdge, + PointTriangle + }; + + /// + /// \brief + /// + PbdCollisionConstraint(const unsigned int& nA, const unsigned int& nB); + + /// + /// \brief Destructor + /// + virtual ~PbdCollisionConstraint() = default; + + /// + /// \brief Get vertex indices of first object + /// + const std::vector<size_t>& getVertexIdsFirst() const { return m_bodiesFirst; } + const std::vector<size_t>& getVertexIdsSecond() const { return m_bodiesSecond; } + + /// + /// \brief Get config + /// + const PbdCollisionConstraintConfig& getConfigFirst() const { return *m_configA; } + const PbdCollisionConstraintConfig& getConfigSecond() const { return *m_configB; } + + /// + /// \brief compute value and gradient of constraint function + /// + /// \param[in] currVertexPositionsA current positions from object A + /// \param[in] currVertexPositionsA current positions from object B + /// \param[inout] c constraint value + /// \param[inout] dcdx constraint gradient + /// + virtual bool computeValueAndGradient(const StdVectorOfVec3d& posA, + const StdVectorOfVec3d& posB, + double& c, + StdVectorOfVec3d& dcdxA, + StdVectorOfVec3d& dcdxB) const = 0; + + virtual void projectConstraint(const StdVectorOfReal& invMassA, + const StdVectorOfReal& invMassB, + StdVectorOfVec3d& posA, + StdVectorOfVec3d& posB); +protected: + std::vector<size_t> m_bodiesFirst; ///> index of points for the first object + std::vector<size_t> m_bodiesSecond; ///> index of points for the second object + + std::shared_ptr<PbdCollisionConstraintConfig> m_configA = nullptr; ///> parameters of the collision constraint + std::shared_ptr<PbdCollisionConstraintConfig> m_configB = nullptr; ///> parameters of the collision constraint +}; + +using PBDCollisionConstraintVector = std::vector<PbdCollisionConstraint*>; +} diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstantDensityConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdConstantDensityConstraint.cpp similarity index 82% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstantDensityConstraint.cpp rename to Source/Constraint/PbdConstraints/imstkPbdConstantDensityConstraint.cpp index 8875e63f5727cb082f25c449c579bec1bcbe9d99..03fe3fd841019825d024481ad38f58ffb441640d 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstantDensityConstraint.cpp +++ b/Source/Constraint/PbdConstraints/imstkPbdConstantDensityConstraint.cpp @@ -20,16 +20,14 @@ limitations under the License. =========================================================================*/ #include "imstkPbdConstantDensityConstraint.h" -#include "imstkPbdModel.h" #include "imstkParallelUtils.h" namespace imstk { void -PbdConstantDensityConstraint::initConstraint(PbdModel& model, const double) +PbdConstantDensityConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, const double) { - const auto& state = model.getCurrentState(); - const auto numParticles = state->getPositions().size(); + const size_t numParticles = initVertexPositions.size(); // constraint parameters // Refer: Miller, et al 2003 "Particle-Based Fluid Simulation for Interactive Applications." @@ -46,32 +44,31 @@ PbdConstantDensityConstraint::initConstraint(PbdModel& model, const double) m_NeighborSearcher = std::make_shared<NeighborSearch>(m_NeighborSearchMethod, m_maxDist); } -bool -PbdConstantDensityConstraint::solvePositionConstraint(PbdModel& model) +void +PbdConstantDensityConstraint::projectConstraint(const StdVectorOfReal& currInvMasses, + const double dt, + const PbdConstraint::SolverType& type, + StdVectorOfVec3d& currVertexPositions) { - const auto& state = model.getCurrentState(); - auto& positions = state->getPositions(); - const auto numParticles = positions.size(); + const size_t numParticles = currVertexPositions.size(); // Search neighbor for each particle - m_NeighborSearcher->getNeighbors(m_neighborList, positions); + m_NeighborSearcher->getNeighbors(m_neighborList, currVertexPositions); ParallelUtils::parallelFor(numParticles, [&](const size_t idx) { - computeDensity(positions[idx], idx, positions); + computeDensity(currVertexPositions[idx], idx, currVertexPositions); }); ParallelUtils::parallelFor(numParticles, [&](const size_t idx) { - computeLambdaScalingFactor(positions[idx], idx, positions); + computeLambdaScalingFactor(currVertexPositions[idx], idx, currVertexPositions); }); ParallelUtils::parallelFor(numParticles, [&](const size_t idx) { - updatePositions(positions[idx], idx, positions); + updatePositions(currVertexPositions[idx], idx, currVertexPositions); }); - - return true; } double diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstantDensityConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdConstantDensityConstraint.h similarity index 86% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstantDensityConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdConstantDensityConstraint.h index 9c5bccc7952b47b4b5859817d4aa025f53ad14ef..81f97b656f2a54f98b8c07ad9640af60e30f50e6 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstantDensityConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdConstantDensityConstraint.h @@ -49,7 +49,7 @@ public: /// /// \Constant Density Constraint Initialization /// - void initConstraint(PbdModel& model, const double k); + void initConstraint(const StdVectorOfVec3d& initVertexPositions, const double k); /// /// \brief Returns PBD constraint of type Type::ConstantDensity @@ -59,7 +59,17 @@ public: /// /// \brief Solves the constant density constraint /// - bool solvePositionConstraint(PbdModel& model) override; + void projectConstraint(const StdVectorOfReal& currInvMasses, + const double dt, + const PbdConstraint::SolverType& type, + StdVectorOfVec3d& currVertexPositions) override; + + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const + { + return true; + } private: /// diff --git a/Source/Constraint/PbdConstraints/imstkPbdConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..443c2a17ad140350c8ed738534e314f6a40b1d09 --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdConstraint.cpp @@ -0,0 +1,79 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdConstraint.h" + +namespace imstk +{ +void +PbdConstraint::projectConstraint(const StdVectorOfReal& invMasses, const double dt, const SolverType& solverType, StdVectorOfVec3d& pos) +{ + double c; + StdVectorOfVec3d dcdx; + + bool update = this->computeValueAndGradient(pos, c, dcdx); + if (!update) + { + return; + } + + double dcMidc = 0.0; + double lambda = 0.0; + double alpha; + + for (size_t i = 0; i < m_vertexIds.size(); ++i) + { + dcMidc += invMasses[m_vertexIds[i]] * dcdx[i].squaredNorm(); + } + + if (dcMidc < VERY_SMALL_EPSILON) + { + return; + } + + switch (solverType) + { + case (SolverType::xPBD): + alpha = m_compliance / (dt * dt); + lambda = -(c + alpha * m_lambda) / (dcMidc + alpha); + m_lambda += lambda; + break; + case (SolverType::PBD): + lambda = -c * m_stiffness / dcMidc; + break; + default: + alpha = m_compliance / (dt * dt); + lambda = -(c + alpha * m_lambda) / (dcMidc + alpha); + m_lambda += lambda; + } + + for (size_t i = 0, vid = 0; i < m_vertexIds.size(); ++i) + { + vid = m_vertexIds[i]; + if (invMasses[vid] > 0.0) + { + pos[vid] += invMasses[vid] * lambda * dcdx[i]; + } + } + + return; +} +} diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdConstraint.h similarity index 54% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdConstraint.h index 37e96681c01879a3be04de0c4ab322c798622772..316ae0309a05d1808ab0022ab33ab88b65b23ad6 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdConstraint.h @@ -21,12 +21,10 @@ #pragma once -#include <vector> +#include "imstkMath.h" namespace imstk { -class PbdModel; - /// /// \class PbdConstraint /// @@ -51,6 +49,14 @@ public: None }; + /// + /// \brief Type of solvers + enum class SolverType + { + xPBD = 0, + PBD, + GCD + }; /// /// \brief Constructor /// @@ -68,11 +74,15 @@ public: virtual Type getType() const = 0; /// - /// \brief compute delta position from the constraint function - /// \param model \class PbdModel - /// \return true if succeeded + /// \brief Compute value and gradient of the constraint + /// + /// \param[in] currVertexPositions vector of current positions + /// \param[inout] c constraint value + /// \param[inout] dcdx constraint gradient /// - virtual bool solvePositionConstraint(PbdModel& model) = 0; + virtual bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const = 0; /// /// \brief Get the vertex indices of the constraint @@ -84,8 +94,41 @@ public: /// void setTolerance(const double eps) { m_epsilon = eps; } + /// + /// \brief Get the tolerance used for pbd constraints + /// + double getTolerance() const { return m_epsilon; } + + /// + /// \brief Set the stiffness + /// + void setStiffness(const double stiffness) + { + m_stiffness = stiffness; + m_compliance = 1.0 / stiffness; + } + + /// + /// \brief Get the stiffness + /// + double getStiffness() const { return m_stiffness; } + + /// + /// \brief Use PBD + /// + void zeroOutLambda() { m_lambda = 0.0; } + + /// + /// \brief Update positions by projecting constraints. + /// + virtual void projectConstraint(const StdVectorOfReal& currInvMasses, const double dt, const SolverType& type, StdVectorOfVec3d& pos); protected: std::vector<size_t> m_vertexIds; ///> index of points for the constraint - double m_epsilon = 1.0e-16; ///> Tolerance used for the costraints + double m_epsilon = 1.0e-16; ///> Tolerance used for the costraints + double m_stiffness = 1.0; ///> used in PBD, [0, 1] + double m_compliance = 1e-7; ///> used in xPBD, inverse of Young's Modulus + mutable double m_lambda = 0.0; ///> Lagrange multiplier }; + +using PBDConstraintVector = std::vector<std::shared_ptr<PbdConstraint>>; } diff --git a/Source/Constraint/PbdConstraints/imstkPbdDihedralConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdDihedralConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d45e2edd87341aa2e7d24fe589cef5e3411168b4 --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdDihedralConstraint.cpp @@ -0,0 +1,98 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdDihedralConstraint.h" +#include <iostream> + +namespace imstk +{ +void +PbdDihedralConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, + const size_t& pIdx1, + const size_t& pIdx2, + const size_t& pIdx3, + const double k) +{ + m_vertexIds[0] = pIdx0; + m_vertexIds[1] = pIdx1; + m_vertexIds[2] = pIdx2; + m_vertexIds[3] = pIdx3; + + m_stiffness = k; + m_compliance = 1.0 / k; + + const Vec3d& p0 = initVertexPositions[pIdx0]; + const Vec3d& p1 = initVertexPositions[pIdx1]; + const Vec3d& p2 = initVertexPositions[pIdx2]; + const Vec3d& p3 = initVertexPositions[pIdx3]; + + const Vec3d n1 = (p2 - p0).cross(p3 - p0).normalized(); + const Vec3d n2 = (p3 - p1).cross(p2 - p1).normalized(); + + m_restAngle = atan2(n1.cross(n2).dot(p3 - p2), (p3 - p2).norm() * n1.dot(n2)); +} + +bool +PbdDihedralConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const +{ + const auto i0 = m_vertexIds[0]; + const auto i1 = m_vertexIds[1]; + const auto i2 = m_vertexIds[2]; + const auto i3 = m_vertexIds[3]; + + const Vec3d& p0 = currVertexPositions[i0]; + const Vec3d& p1 = currVertexPositions[i1]; + const Vec3d& p2 = currVertexPositions[i2]; + const Vec3d& p3 = currVertexPositions[i3]; + + const auto e = p3 - p2; + const auto e1 = p3 - p0; + const auto e2 = p0 - p2; + const auto e3 = p3 - p1; + const auto e4 = p1 - p2; + // To accelerate, all normal (area) vectors and edge length should be precomputed in parallel + auto n1 = e1.cross(e); + auto n2 = e.cross(e3); + const auto A1 = n1.norm(); + const auto A2 = n2.norm(); + n1 /= A1; + n2 /= A2; + + const double l = e.norm(); + if (l < m_epsilon) + { + return false; + } + + dcdx.resize(4); + dcdx[0] = -(l / A1) * n1; + dcdx[1] = -(l / A2) * n2; + dcdx[2] = (e.dot(e1) / (A1 * l)) * n1 + (e.dot(e3) / (A2 * l)) * n2; + dcdx[3] = (e.dot(e2) / (A1 * l)) * n1 + (e.dot(e4) / (A2 * l)) * n2; + + c = atan2(n1.cross(n2).dot(e), l * n1.dot(n2)) - m_restAngle; + + return true; +} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDihedralConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdDihedralConstraint.h similarity index 71% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDihedralConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdDihedralConstraint.h index 500b6f6f64e92f8369990f8d986699bf65345cc5..98ad174c203726cf03ff14263504481b79aa5ca4 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDihedralConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdDihedralConstraint.h @@ -59,18 +59,24 @@ public: \param pIdx4 index of p3 \param k stiffness */ - void initConstraint(PbdModel& model, - const size_t& pIdx1, const size_t& pIdx2, - const size_t& pIdx3, const size_t& pIdx4, - const double k); + void initConstraint( + const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, const size_t& pIdx1, + const size_t& pIdx2, const size_t& pIdx3, + const double k); /// - /// \brief Solves the dihedral angular constraint + /// \brief Compute value and gradient of the constraint /// - bool solvePositionConstraint(PbdModel& model) override; + /// \param[in] currVertexPositions vector of current positions + /// \param[inout] c constraint value + /// \param[inout] dcdx constraint gradient + /// + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const override; public: - double m_restAngle = 0.; ///> Rest angle - double m_stiffness = 0.; ///> Angular stiffness + double m_restAngle = 0.0; ///> Rest angle }; } //imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdDistanceConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdDistanceConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0815882793e062cb43330b643a06f8264a4bf77 --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdDistanceConstraint.cpp @@ -0,0 +1,60 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdDistanceConstraint.h" + +namespace imstk +{ +void +PbdDistanceConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, + const size_t& pIdx1, + const double k) +{ + m_vertexIds[0] = pIdx0; + m_vertexIds[1] = pIdx1; + m_stiffness = k; + m_compliance = 1.0 / k; + + const Vec3d& p0 = initVertexPositions[pIdx0]; + const Vec3d& p1 = initVertexPositions[pIdx1]; + + m_restLength = (p0 - p1).norm(); +} + +bool +PbdDistanceConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const +{ + const Vec3d& p0 = currVertexPositions[m_vertexIds[0]]; + const Vec3d& p1 = currVertexPositions[m_vertexIds[1]]; + dcdx.resize(m_vertexIds.size()); + + dcdx[0] = p0 - p1; + const double len = dcdx[0].norm(); + dcdx[0] /= len; + dcdx[1] = -dcdx[0]; + c = len - m_restLength; + + return true; +} +} diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDistanceConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdDistanceConstraint.h similarity index 72% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDistanceConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdDistanceConstraint.h index 4d4c2f706abf4e17d12a902104afbeb25e12e2df..12a7516718723d43461778b97f7ef90b6c3be1b5 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDistanceConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdDistanceConstraint.h @@ -46,16 +46,16 @@ public: /// /// \brief Initializes the distance constraint /// - void initConstraint(PbdModel& model, const size_t& pIdx1, - const size_t& pIdx2, const double k = 1e-1); + void initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, + const size_t& pIdx1, + const double k = 1e5); - /// - /// \brief Solves the Distance constraint - /// - bool solvePositionConstraint(PbdModel& model) override; + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const override; public: - double m_restLength = 0.; ///> Rest length between the nodes - double m_stiffness = 0.; ///> Stiffness of the constaint + double m_restLength = 0.0; ///> Rest length between the nodes }; -} // imstk \ No newline at end of file +} // imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85986e9963e3690f7977c2f6e1854dd387c8b30f --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.cpp @@ -0,0 +1,111 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdEdgeEdgeCollisionConstraint.h" +#include "imstkLogger.h" + +namespace imstk +{ +void +PbdEdgeEdgeConstraint::initConstraint(const size_t& pIdxA1, + const size_t& pIdxA2, + const size_t& pIdxB1, + const size_t& pIdxB2, + std::shared_ptr<PbdCollisionConstraintConfig> configA, + std::shared_ptr<PbdCollisionConstraintConfig> configB) +{ + m_configA = configA; + m_configB = configB; + m_bodiesFirst[0] = pIdxA1; + m_bodiesFirst[1] = pIdxA2; + m_bodiesSecond[0] = pIdxB1; + m_bodiesSecond[1] = pIdxB2; +} + +bool +PbdEdgeEdgeConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositionsA, + const StdVectorOfVec3d& currVertexPositionsB, + double& cc, + StdVectorOfVec3d& dcdxA, + StdVectorOfVec3d& dcdxB) const +{ + const auto i0 = m_bodiesFirst[0]; + const auto i1 = m_bodiesFirst[1]; + const auto i2 = m_bodiesSecond[0]; + const auto i3 = m_bodiesSecond[1]; + + const Vec3d& x0 = currVertexPositionsA[i0]; + const Vec3d& x1 = currVertexPositionsA[i1]; + const Vec3d& x2 = currVertexPositionsB[i2]; + const Vec3d& x3 = currVertexPositionsB[i3]; + + auto a = (x3 - x2).dot(x1 - x0); + auto b = (x1 - x0).dot(x1 - x0); + auto c = (x0 - x2).dot(x1 - x0); + auto d = (x3 - x2).dot(x3 - x2); + auto e = a; + auto f = (x0 - x2).dot(x3 - x2); + + auto det = a * e - d * b; + double s = 0.5; + double t = 0.5; + if (fabs(det) > 1e-12) + { + s = (c * e - b * f) / det; + t = (c * d - a * f) / det; + if (s < 0 || s > 1.0 || t < 0 || t > 1.0) + { + c = 0.0; + return false; + } + } + else + { + //LOG(WARNING) << "det is null"; + } + + Vec3d P = x0 + t * (x1 - x0); + Vec3d Q = x2 + s * (x3 - x2); + + Vec3d n = Q - P; + auto l = n.norm(); + n /= l; + + const auto dist = m_configA->m_proximity + m_configB->m_proximity; + + if (l > dist) + { + c = 0.0; + return false; + } + + dcdxA.resize(2); + dcdxB.resize(2); + dcdxA[0] = -(1 - t) * n; + dcdxA[1] = -(t) * n; + dcdxB[0] = (1 - s) * n; + dcdxB[1] = (s) * n; + + cc = l - dist; + + return true; +} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.h similarity index 63% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.h index 967c31df18cf163c2094b77c338aaa41c2bf1c03..e07dc618c39aa32d3a2e6f091ec39fe0d0d291b4 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.h @@ -48,14 +48,24 @@ public: /// \param pIdx4 second point of the edge from object2 /// \return true if succeeded /// - void initConstraint(std::shared_ptr<PbdModel> model1, - const size_t& pIdx1, const size_t& pIdx2, - std::shared_ptr<PbdModel> model2, - const size_t& pIdx3, const size_t& pIdx4); + void initConstraint( + const size_t& pIdxA1, const size_t& pIdxA2, + const size_t& pIdxB1, const size_t& pIdxB2, + std::shared_ptr<PbdCollisionConstraintConfig> configA, + std::shared_ptr<PbdCollisionConstraintConfig> configB); /// - /// \brief Solve edge-edge collision constraint + /// \brief compute value and gradient of constraint function /// - bool solvePositionConstraint() override; + /// \param[in] currVertexPositionsA current positions from object A + /// \param[in] currVertexPositionsA current positions from object B + /// \param[inout] c constraint value + /// \param[inout] dcdx constraint gradient + /// + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositionsA, + const StdVectorOfVec3d& currVertexPositionsB, + double& c, + StdVectorOfVec3d& dcdxA, + StdVectorOfVec3d& dcdxB) const override; }; -} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEMConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdFEMConstraint.cpp similarity index 99% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEMConstraint.cpp rename to Source/Constraint/PbdConstraints/imstkPbdFEMConstraint.cpp index f9acfad1d5e7291c29bba6812e08ec843c25ec59..6a9dfe926781091c7cccb3c0d78629180ceae9d9 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEMConstraint.cpp +++ b/Source/Constraint/PbdConstraints/imstkPbdFEMConstraint.cpp @@ -32,4 +32,4 @@ PbdFEMConstraint::PbdFEMConstraint(const unsigned int cardinality, { m_vertexIds.resize(cardinality); } -} // imstk \ No newline at end of file +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEMConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdFEMConstraint.h similarity index 62% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEMConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdFEMConstraint.h index 404cbc9f8fbcd7ca9a81e53386b2608d65e7b29d..d06dea03362a70a519005fff7082e62c5613da91 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEMConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdFEMConstraint.h @@ -22,10 +22,28 @@ #pragma once #include "imstkPbdConstraint.h" -#include "imstkMath.h" namespace imstk { +/// +/// \class PbdFEMConstraintConfig +/// +/// \brief Either mu/lambda used, may be computed from youngs modulus and poissons ratio +/// +struct PbdFEMConstraintConfig +{ + PbdFEMConstraintConfig(double mu, double lambda, double youngModulus, double poissonRatio) : + m_mu(mu), m_lambda(lambda), m_YoungModulus(youngModulus), m_PoissonRatio(poissonRatio) + { + } + + double m_mu = 0.0; ///> Lame constant, if constraint type is FEM + double m_lambda = 0.0; ///> Lame constant, if constraint type is FEM + + double m_YoungModulus = 1000; ///> FEM parameter, if constraint type is FEM + double m_PoissonRatio = 0.2; ///> FEM parameter, if constraint type is FEM +}; + /// /// \class PbdFEMConstraint /// @@ -52,8 +70,10 @@ public: explicit PbdFEMConstraint(const unsigned int cardinality, MaterialType mtype = MaterialType::StVK); public: - double m_elementVolume; ///> Volume of the element - MaterialType m_material; ///> Material type - Mat3d m_invRestMat; ///> + double m_elementVolume = 0.0; ///> Volume of the element + MaterialType m_material; ///> Material type + Mat3d m_invRestMat; + + std::shared_ptr<PbdFEMConstraintConfig> m_config = nullptr; }; -} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFETetConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdFETetConstraint.cpp similarity index 68% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFETetConstraint.cpp rename to Source/Constraint/PbdConstraints/imstkPbdFETetConstraint.cpp index 6d1b0e87c8af19e6af062591a44e30cc7409bc14..4763b2f29c6d5d94db7561aec5eac5eb262a230d 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFETetConstraint.cpp +++ b/Source/Constraint/PbdConstraints/imstkPbdFETetConstraint.cpp @@ -20,30 +20,30 @@ =========================================================================*/ #include "imstkPbdFETetConstraint.h" -#include "imstkPbdModel.h" #include "imstkLogger.h" -#include "imstkMath.h" +#include <iostream> namespace imstk { bool -PbdFEMTetConstraint::initConstraint(PbdModel& model, - const size_t& pIdx1, const size_t& pIdx2, - const size_t& pIdx3, const size_t& pIdx4) +PbdFEMTetConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, const size_t& pIdx1, + const size_t& pIdx2, const size_t& pIdx3, + std::shared_ptr<PbdFEMConstraintConfig> config) { - m_vertexIds[0] = pIdx1; - m_vertexIds[1] = pIdx2; - m_vertexIds[2] = pIdx3; - m_vertexIds[3] = pIdx4; + m_vertexIds[0] = pIdx0; + m_vertexIds[1] = pIdx1; + m_vertexIds[2] = pIdx2; + m_vertexIds[3] = pIdx3; - auto state = model.getInitialState(); - - const Vec3d& p0 = state->getVertexPosition(pIdx1); - const Vec3d& p1 = state->getVertexPosition(pIdx2); - const Vec3d& p2 = state->getVertexPosition(pIdx3); - const Vec3d& p3 = state->getVertexPosition(pIdx4); + const Vec3d& p0 = initVertexPositions[pIdx0]; + const Vec3d& p1 = initVertexPositions[pIdx1]; + const Vec3d& p2 = initVertexPositions[pIdx2]; + const Vec3d& p3 = initVertexPositions[pIdx3]; m_elementVolume = (1.0 / 6.0) * (p3 - p0).dot((p1 - p0).cross(p2 - p0)); + m_config = config; + m_compliance = 1.0 / (config->m_lambda + 2 * config->m_mu); Mat3d m; m.col(0) = p0 - p3; @@ -61,21 +61,19 @@ PbdFEMTetConstraint::initConstraint(PbdModel& model, } bool -PbdFEMTetConstraint::solvePositionConstraint(PbdModel& model) +PbdFEMTetConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& cval, + StdVectorOfVec3d& dcdx) const { - const auto i1 = m_vertexIds[0]; - const auto i2 = m_vertexIds[1]; - const auto i3 = m_vertexIds[2]; - const auto i4 = m_vertexIds[3]; - - auto state = model.getCurrentState(); - - Vec3d& p0 = state->getVertexPosition(i1); - Vec3d& p1 = state->getVertexPosition(i2); - Vec3d& p2 = state->getVertexPosition(i3); - Vec3d& p3 = state->getVertexPosition(i4); + const auto i0 = m_vertexIds[0]; + const auto i1 = m_vertexIds[1]; + const auto i2 = m_vertexIds[2]; + const auto i3 = m_vertexIds[3]; - //double currentVolume = (1.0 / 6.0) * (p3 - p0).dot((p1 - p0).cross(p2 - p0)); + const Vec3d& p0 = currVertexPositions[i0]; + const Vec3d& p1 = currVertexPositions[i1]; + const Vec3d& p2 = currVertexPositions[i2]; + const Vec3d& p3 = currVertexPositions[i3]; Mat3d m; m.col(0) = p0 - p3; @@ -89,8 +87,8 @@ PbdFEMTetConstraint::solvePositionConstraint(PbdModel& model) // energy constraint double C = 0; - const auto mu = model.getParameters()->m_mu; - const auto lambda = model.getParameters()->m_lambda; + const auto mu = m_config->m_mu; + const auto lambda = m_config->m_lambda; switch (m_material) { @@ -176,46 +174,14 @@ PbdFEMTetConstraint::solvePositionConstraint(PbdModel& model) } } - const double im1 = model.getInvMass(i1); - const double im2 = model.getInvMass(i2); - const double im3 = model.getInvMass(i3); - const double im4 = model.getInvMass(i4); - Mat3d gradC = m_elementVolume * P * m_invRestMat.transpose(); - - double sum = im1 * gradC.col(0).squaredNorm() - + im2 * gradC.col(1).squaredNorm() - + im3 * gradC.col(2).squaredNorm() - + im4 * (gradC.col(0) + gradC.col(1) + gradC.col(2)).squaredNorm(); - - if (sum < m_epsilon) - { - return false; - } - - C = C * m_elementVolume; - - const double s = C / sum; - - if (im1 > 0) - { - p0 += -s* im1* gradC.col(0); - } - - if (im2 > 0) - { - p1 += -s* im2* gradC.col(1); - } - - if (im3 > 0) - { - p2 += -s* im3* gradC.col(2); - } - - if (im4 > 0) - { - p3 += s * im4 * (gradC.col(0) + gradC.col(1) + gradC.col(2)); - } + cval = C; + cval *= m_elementVolume; + dcdx.resize(m_vertexIds.size()); + dcdx[0] = gradC.col(0); + dcdx[1] = gradC.col(1); + dcdx[2] = gradC.col(2); + dcdx[3] = -dcdx[0] - dcdx[1] - dcdx[2]; return true; } diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFETetConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdFETetConstraint.h similarity index 80% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFETetConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdFETetConstraint.h index 1c1b41eca3fc43d073640f9d7609974bc5980e4f..e098bef1872fb467f9a2b1ffd64f76ab0e85604a 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFETetConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdFETetConstraint.h @@ -48,13 +48,16 @@ public: /// /// \brief Initialize the tetrahedral FEM constraint /// - bool initConstraint(PbdModel& model, + bool initConstraint(const StdVectorOfVec3d& initVertexPositions, const size_t& pIdx1, const size_t& pIdx2, - const size_t& pIdx3, const size_t& pIdx4); + const size_t& pIdx3, const size_t& pIdx4, + std::shared_ptr<PbdFEMConstraintConfig> config); /// - /// \brief Solve the tetrahedral FEM constraint + /// \brief Compute the value and gradient of constraint /// - bool solvePositionConstraint(PbdModel& model) override; + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPosition, + double& c, + StdVectorOfVec3d& dcdx) const override; }; } // imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdPointTriCollisionConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdPointTriCollisionConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75b13a8d9e49a1a6ab776c8a3cd7b9e55464758a --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdPointTriCollisionConstraint.cpp @@ -0,0 +1,99 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdPointTriCollisionConstraint.h" + +#include "imstkLogger.h" + +namespace imstk +{ +void +PbdPointTriangleConstraint::initConstraint(const size_t& pIdxA1, + const size_t& pIdxB1, + const size_t& pIdxB2, + const size_t& pIdxB3, + std::shared_ptr<PbdCollisionConstraintConfig> configA, + std::shared_ptr<PbdCollisionConstraintConfig> configB) +{ + m_bodiesFirst[0] = pIdxA1; + m_bodiesSecond[0] = pIdxB1; + m_bodiesSecond[1] = pIdxB2; + m_bodiesSecond[2] = pIdxB3; + m_configA = configA; + m_configB = configB; +} + +bool +PbdPointTriangleConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositionsA, + const StdVectorOfVec3d& currVertexPositionsB, + double& c, + StdVectorOfVec3d& dcdxA, + StdVectorOfVec3d& dcdxB) const +{ + const size_t i0 = m_bodiesFirst[0]; + const size_t i1 = m_bodiesSecond[0]; + const size_t i2 = m_bodiesSecond[1]; + const size_t i3 = m_bodiesSecond[2]; + + const Vec3d& x0 = currVertexPositionsA[i0]; + const Vec3d& x1 = currVertexPositionsB[i1]; + const Vec3d& x2 = currVertexPositionsB[i2]; + const Vec3d& x3 = currVertexPositionsB[i3]; + + const Vec3d x12 = x2 - x1; + const Vec3d x13 = x3 - x1; + Vec3d n = x12.cross(x13); + const Vec3d x01 = x0 - x1; + + double alpha = n.dot(x12.cross(x01)) / (n.dot(n)); + double beta = n.dot(x01.cross(x13)) / (n.dot(n)); + + if (alpha < 0 || beta < 0 || alpha + beta > 1) + { + //LOG(WARNING) << "Projection point not inside the triangle"; + c = 0.0; + return false; + } + + const double dist = m_configA->m_proximity + m_configB->m_proximity; + + n.normalize(); + + double l = x01.dot(n); + + if (l > dist) + { + c = 0.0; + return false; + } + + double gamma = 1.0 - alpha - beta; + dcdxA.resize(1); + dcdxB.resize(3); + dcdxA[0] = n; + dcdxB[0] = -alpha * n; + dcdxB[1] = -beta * n; + dcdxB[2] = -gamma * n; + + c = l - dist; + return true; +} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdPointTriCollisionConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdPointTriCollisionConstraint.h similarity index 54% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdPointTriCollisionConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdPointTriCollisionConstraint.h index ba1335e4927542b9acbdc3c111f34e876932e49a..6351c1dae540ccf23b56b7e071589f67880f7b35 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdPointTriCollisionConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdPointTriCollisionConstraint.h @@ -46,19 +46,29 @@ public: /// /// \brief initialize constraint - /// \param pIdx1 index of the point from object1 - /// \param pIdx2 first point of the triangle from object2 - /// \param pIdx3 second point of the triangle from object2 - /// \param pIdx4 third point of the triangle from object2 + /// \param pIdxA1 index of the point from object1 + /// \param pIdxB1 first point of the triangle from object2 + /// \param pIdxB2 second point of the triangle from object2 + /// \param pIdxB3 third point of the triangle from object2 /// \return /// - void initConstraint(std::shared_ptr<PbdModel> model1, const size_t& pIdx1, - std::shared_ptr<PbdModel> model2, const size_t& pIdx2, - const size_t& pIdx3, const size_t& pIdx4); + void initConstraint(const size_t& pIdxA1, + const size_t& pIdxB1, const size_t& pIdxB2, const size_t& pIdxB3, + std::shared_ptr<PbdCollisionConstraintConfig> configA, + std::shared_ptr<PbdCollisionConstraintConfig> configB); /// - /// \brief + /// \brief compute value and gradient of constraint function /// - bool solvePositionConstraint() override; + /// \param[in] currVertexPositionsA current positions from object A + /// \param[in] currVertexPositionsA current positions from object B + /// \param[inout] c constraint value + /// \param[inout] dcdx constraint gradient + /// + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPositionsA, + const StdVectorOfVec3d& currVertexPositionsB, + double& c, + StdVectorOfVec3d& dcdxA, + StdVectorOfVec3d& dcdxB) const override; }; -} +} // imstk diff --git a/Source/Constraint/PbdConstraints/imstkPbdVolumeConstraint.cpp b/Source/Constraint/PbdConstraints/imstkPbdVolumeConstraint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef63215ebcfa6e58be138a90c170eb9f9012941f --- /dev/null +++ b/Source/Constraint/PbdConstraints/imstkPbdVolumeConstraint.cpp @@ -0,0 +1,73 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkPbdVolumeConstraint.h" + +namespace imstk +{ +void +PbdVolumeConstraint::initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx0, const size_t& pIdx1, + const size_t& pIdx2, const size_t& pIdx3, + const double k) +{ + m_vertexIds[0] = pIdx0; + m_vertexIds[1] = pIdx1; + m_vertexIds[2] = pIdx2; + m_vertexIds[3] = pIdx3; + + m_stiffness = k; + m_compliance = 1.0 / k; + + const Vec3d& p0 = initVertexPositions[pIdx0]; + const Vec3d& p1 = initVertexPositions[pIdx1]; + const Vec3d& p2 = initVertexPositions[pIdx2]; + const Vec3d& p3 = initVertexPositions[pIdx3]; + + m_restVolume = (1.0 / 6.0) * ((p1 - p0).cross(p2 - p0)).dot(p3 - p0); +} + +bool +PbdVolumeConstraint::computeValueAndGradient(const StdVectorOfVec3d& currVertexPositions, + double& c, + StdVectorOfVec3d& dcdx) const +{ + const auto i0 = m_vertexIds[0]; + const auto i1 = m_vertexIds[1]; + const auto i2 = m_vertexIds[2]; + const auto i3 = m_vertexIds[3]; + + const Vec3d& x0 = currVertexPositions[i0]; + const Vec3d& x1 = currVertexPositions[i1]; + const Vec3d& x2 = currVertexPositions[i2]; + const Vec3d& x3 = currVertexPositions[i3]; + + const double onesixth = 1.0 / 6.0; + + dcdx[0] = onesixth * (x1 - x2).cross(x3 - x1); + dcdx[1] = onesixth * (x2 - x0).cross(x3 - x0); + dcdx[2] = onesixth * (x3 - x0).cross(x1 - x0); + dcdx[3] = onesixth * (x1 - x0).cross(x2 - x0); + + c = dcdx[3].dot(x3 - x0); + return true; +} +} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdVolumeConstraint.h b/Source/Constraint/PbdConstraints/imstkPbdVolumeConstraint.h similarity index 67% rename from Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdVolumeConstraint.h rename to Source/Constraint/PbdConstraints/imstkPbdVolumeConstraint.h index 0fb646cb953facf8c3fac8ad09a5235ee0b60164..71a5e48ecda4a933cef788bc737be021efa73cfc 100644 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdVolumeConstraint.h +++ b/Source/Constraint/PbdConstraints/imstkPbdVolumeConstraint.h @@ -36,7 +36,7 @@ public: /// /// \brief constructor /// - PbdVolumeConstraint() : PbdConstraint() { m_vertexIds.resize(4); } + PbdVolumeConstraint() { m_vertexIds.resize(4); } /// /// \brief Returns PBD constraint of type Type::Volume @@ -46,17 +46,19 @@ public: /// /// \brief Initializes the volume constraint /// - void initConstraint(PbdModel& model, const size_t& pIdx1, - const size_t& pIdx2, const size_t& pIdx3, - const size_t& pIdx4, const double k = 2.0); + void initConstraint(const StdVectorOfVec3d& initVertexPositions, + const size_t& pIdx1, const size_t& pIdx2, + const size_t& pIdx3, const size_t& pIdx4, + const double k = 2.0); /// - /// \brief Solves the volume constraint + /// \brief Compute the value and gradient of constraint /// - bool solvePositionConstraint(PbdModel& model) override; + bool computeValueAndGradient(const StdVectorOfVec3d& currVertexPosition, + double& c, + StdVectorOfVec3d& dcdx) const override; -public: - double m_restVolume = 0.; ///> Rest volume - double m_stiffness = 0.; ///> Stiffness of the volume constraint +protected: + double m_restVolume = 0.0; ///> Rest volume }; -} +} // imstk diff --git a/Source/Controllers/imstkSceneObjectController.cpp b/Source/Controllers/imstkSceneObjectController.cpp index 7056dcb7f7f92d77da5286d338714f0715a88895..1b6a94a8e151a4a2d0c8d20ad62bbec0f0306c62 100644 --- a/Source/Controllers/imstkSceneObjectController.cpp +++ b/Source/Controllers/imstkSceneObjectController.cpp @@ -25,10 +25,7 @@ #include "imstkDeviceClient.h" #include "imstkCollidingObject.h" #include "imstkGeometry.h" - -#include <utility> - -#include <g3log/g3log.hpp> +#include "imstkLogger.h" namespace imstk { diff --git a/Source/Devices/CMakeLists.txt b/Source/Devices/CMakeLists.txt index 74242f5af6beaecd5987721d569330b85b3b349a..83e5e995fa7283bcf8e360a999df99158e1adf68 100644 --- a/Source/Devices/CMakeLists.txt +++ b/Source/Devices/CMakeLists.txt @@ -1,17 +1,33 @@ #----------------------------------------------------------------------------- # Create target #----------------------------------------------------------------------------- + +list(APPEND Dependencies + Common + VRPN) + +list(APPEND ExclusionFiles "") +if(${PROJECT_NAME}_USE_OpenHaptics) + list(APPEND Dependencies + ${HAPTIC_DEVICE_LIBS}) +else() + list(APPEND ExclusionFiles + imstkHDAPIDeviceClient.h + imstkHDAPIDeviceClient.cpp + imstkHDAPIDeviceServer.h + imstkHDAPIDeviceServer.cpp) +endif() + include(imstkAddLibrary) set(HAPTIC_DEVICE_LIBS) if(${PROJECT_NAME}_USE_OpenHaptics) set(HAPTIC_DEVICE_LIBS OpenHapticsSDK) endif() -imstk_add_library( Devices +imstk_add_library(Devices + EXCLUDE_FILES + ${ExclusionFiles} DEPENDS - Common - VRPN - ${HAPTIC_DEVICE_LIBS} - ) + ${Dependencies}) #----------------------------------------------------------------------------- # Testing diff --git a/Source/Devices/imstkHDAPIDeviceClient.cpp b/Source/Devices/imstkHDAPIDeviceClient.cpp index da70ba044d450770a84c2e76575ab628a7fe213f..654e22acd62afa36e2c73b704e284351705c4e5b 100644 --- a/Source/Devices/imstkHDAPIDeviceClient.cpp +++ b/Source/Devices/imstkHDAPIDeviceClient.cpp @@ -19,8 +19,6 @@ =========================================================================*/ -#ifdef iMSTK_USE_OPENHAPTICS - #include "imstkHDAPIDeviceClient.h" #include <HDU/hduVector.h> @@ -93,4 +91,3 @@ HDAPIDeviceClient::hapticCallback(void* pData) return HD_CALLBACK_DONE; } } // imstk -#endif // ifdef iMSTK_USE_OPENHAPTICS diff --git a/Source/Devices/imstkHDAPIDeviceClient.h b/Source/Devices/imstkHDAPIDeviceClient.h index c4d2ab3ae5a8dd8739356cccb864eb8fc33ffe56..525054168d34ed7d5a03656e4a8360dc0513b68c 100644 --- a/Source/Devices/imstkHDAPIDeviceClient.h +++ b/Source/Devices/imstkHDAPIDeviceClient.h @@ -19,10 +19,7 @@ =========================================================================*/ -#ifdef iMSTK_USE_OPENHAPTICS - -#ifndef imstkHDAPIDeviceClient_h -#define imstkHDAPIDeviceClient_h +#pragma once #include "imstkDeviceClient.h" @@ -81,6 +78,3 @@ private: HD_state m_state; ///< device reading state }; } - -#endif // ifndef imstkHDAPIDeviceClient_h -#endif // ifdef iMSTK_USE_OpenHaptics diff --git a/Source/Devices/imstkHDAPIDeviceServer.cpp b/Source/Devices/imstkHDAPIDeviceServer.cpp index 380a3a2a3ffcc7ab42b649d672d84d9eff3e4390..302d076e64a89ee3a20f9a7ab78a7050831ac009 100644 --- a/Source/Devices/imstkHDAPIDeviceServer.cpp +++ b/Source/Devices/imstkHDAPIDeviceServer.cpp @@ -19,8 +19,6 @@ =========================================================================*/ -#ifdef iMSTK_USE_OPENHAPTICS - #include "imstkHDAPIDeviceServer.h" #include "imstkHDAPIDeviceClient.h" @@ -65,5 +63,3 @@ HDAPIDeviceServer::cleanUpModule() } } } // imstk - -#endif // ifdef iMSTK_USE_OpenHaptics diff --git a/Source/Devices/imstkHDAPIDeviceServer.h b/Source/Devices/imstkHDAPIDeviceServer.h index 9d25aeece66471cb127818292ef24116952ee542..0638b75dbd28e17ffb7b8cbad52f868bb09b4202 100644 --- a/Source/Devices/imstkHDAPIDeviceServer.h +++ b/Source/Devices/imstkHDAPIDeviceServer.h @@ -19,10 +19,7 @@ =========================================================================*/ -#ifdef iMSTK_USE_OPENHAPTICS - -#ifndef imstkHDAPIDeviceServer_h -#define imstkHDAPIDeviceServer_h +#pragma once #include <vector> @@ -78,6 +75,3 @@ private: std::vector<std::shared_ptr<HDAPIDeviceClient>> m_deviceClients; ///< list of OpenHaptics }; } // imstk - -#endif // ifndef imstkHDAPIDeviceServer_h -#endif // ifdef iMSTK_USE_OpenHaptics diff --git a/Source/DynamicalModels/CMakeLists.txt b/Source/DynamicalModels/CMakeLists.txt index b69970a4c2ad14e919c332a40d157f5059c74a5a..db13a0b3ff196d398aa8577a727a4c0544cab352 100644 --- a/Source/DynamicalModels/CMakeLists.txt +++ b/Source/DynamicalModels/CMakeLists.txt @@ -3,31 +3,48 @@ #----------------------------------------------------------------------------- include(imstkAddLibrary) -imstk_add_library( DynamicalModels +list(APPEND Dependencies + Common + DataStructures + Constraints + Geometry + Solvers + MeshIO + VegaFEM::massSpringSystem + VegaFEM::corotationalLinearFEM + VegaFEM::isotropicHyperelasticFEM + VegaFEM::forceModel + VegaFEM::stvk + VegaFEM::graph + VegaFEM::volumetricMesh + PhysX) +list(APPEND ExclusionFiles "") + +# If using model reduction link to these libs +if (${${PROJECT_NAME}_USE_MODEL_REDUCTION}) + list(APPEND Dependencies + VegaFEM::reducedStvk + VegaFEM::reducedElasticForceModel) +else() + list(APPEND ExclusionFiles + ObjectModels/imstkReducedStVKBodyModel.h + ObjectModels/imstkReducedStVKBodyModel.cpp) +endif() + +imstk_add_library(DynamicalModels SUBDIR_LIST ObjectStates TimeIntegrators ObjectModels - ObjectModels/PbdConstraints InternalForceModel - DEPENDS - DataStructures - Constraints - Geometry - Solvers - VegaFEM::massSpringSystem - VegaFEM::corotationalLinearFEM - VegaFEM::isotropicHyperelasticFEM - VegaFEM::forceModel - VegaFEM::stvk - VegaFEM::graph - VegaFEM::volumetricMesh - PhysX - ) + EXCLUDE_FILES + ${ExclusionFiles} + DEPENDS + ${Dependencies}) #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- #if( ${PROJECT_NAME}_BUILD_TESTING ) # add_subdirectory( Testing ) -#endif() \ No newline at end of file +#endif() diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdAreaConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdAreaConstraint.cpp deleted file mode 100644 index 18d71d89575468daa5b36f0423e852d8171f98ba..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdAreaConstraint.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdAreaConstraint.h" -#include "imstkPbdModel.h" - -namespace imstk -{ -void -PbdAreaConstraint::initConstraint(PbdModel& model, const size_t& pIdx1, - const size_t& pIdx2, const size_t& pIdx3, - const double k) -{ - m_vertexIds[0] = pIdx1; - m_vertexIds[1] = pIdx2; - m_vertexIds[2] = pIdx3; - - m_stiffness = k; - - auto state = model.getInitialState(); - - const Vec3d& p0 = state->getVertexPosition(pIdx1); - const Vec3d& p1 = state->getVertexPosition(pIdx2); - const Vec3d& p2 = state->getVertexPosition(pIdx3); - - m_restArea = 0.5 * (p1 - p0).cross(p2 - p0).norm(); -} - -bool -PbdAreaConstraint::solvePositionConstraint(PbdModel& model) -{ - const auto i1 = m_vertexIds[0]; - const auto i2 = m_vertexIds[1]; - const auto i3 = m_vertexIds[2]; - - auto state = model.getCurrentState(); - - Vec3d& p0 = state->getVertexPosition(i1); - Vec3d& p1 = state->getVertexPosition(i2); - Vec3d& p2 = state->getVertexPosition(i3); - - const auto im0 = model.getInvMass(i1); - const auto im1 = model.getInvMass(i2); - const auto im2 = model.getInvMass(i3); - - const auto e1 = p0 - p1; - const auto e2 = p1 - p2; - const auto e3 = p2 - p0; - - auto n = e1.cross(e2); - const auto A = 0.5 * n.norm(); - - if (A < m_epsilon) - { - return false; - } - - n /= 2 * A; - - const Vec3d grad0 = e2.cross(n); - const Vec3d grad1 = e3.cross(n); - const Vec3d grad2 = e1.cross(n); - - auto lambda = im0 * grad0.squaredNorm() + im1 * grad1.squaredNorm() + im2 * grad2.squaredNorm(); - - lambda = (A - m_restArea) / lambda * m_stiffness; - - if (im0 > 0) - { - p0 += -im0 * lambda * grad0; - } - - if (im1 > 0) - { - p1 += -im1 * lambda * grad1; - } - - if (im2 > 0) - { - p2 += -im2 * lambda * grad2; - } - - return true; -} -} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdBendConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdBendConstraint.cpp deleted file mode 100644 index cde7439e76c8b05a798b17a8b6af634404d6736f..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdBendConstraint.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdBendConstraint.h" -#include "imstkPbdModel.h" - -namespace imstk -{ -void -PbdBendConstraint::initConstraint(PbdModel& model, - const size_t& pIdx1, const size_t& pIdx2, - const size_t& pIdx3, const double k) -{ - m_vertexIds[0] = pIdx1; - m_vertexIds[1] = pIdx2; - m_vertexIds[2] = pIdx3; - - m_stiffness = k; - auto state = model.getInitialState(); - - const Vec3d& p0 = state->getVertexPosition(pIdx1); - const Vec3d& p1 = state->getVertexPosition(pIdx2); - const Vec3d& p2 = state->getVertexPosition(pIdx3); - - // Instead of using the angle between the segments we can use the distance - // from the center of the triangle - const Vec3d& center = (p0 + p1 + p2) / 3.0; - m_restLength = (p1 - center).norm(); -} - -bool -PbdBendConstraint::solvePositionConstraint(PbdModel& model) -{ - const auto i1 = m_vertexIds[0]; - const auto i2 = m_vertexIds[1]; - const auto i3 = m_vertexIds[2]; - - auto state = model.getCurrentState(); - - Vec3d& p0 = state->getVertexPosition(i1); - Vec3d& p1 = state->getVertexPosition(i2); - Vec3d& p2 = state->getVertexPosition(i3); - - const auto im0 = model.getInvMass(i1); - const auto im1 = model.getInvMass(i2); - const auto im2 = model.getInvMass(i3); - - const auto m0 = (im0 > 0.0) ? 1.0 / im0 : 0.0; - const auto m1 = (im1 > 0.0) ? 1.0 / im1 : 0.0; - const auto m2 = (im2 > 0.0) ? 1.0 / im2 : 0.0; - - // Move towards triangle center - const Vec3d& center = (p0 + p1 + p2) / 3.0; - const Vec3d& diff = p1 - center; - const double dist = diff.norm(); - if (dist < m_epsilon) - { - return false; - } - const Vec3d& dir = diff / dist; - const double c = (dist - m_restLength) * m_stiffness; - - // Now Apply movement weighted by masses - if (im0 > 0.0) - { - p0 += c * dir * 2.0 * m0 / (m0 + m1 + m2); - } - - if (im1 > 0.0) - { - p1 -= c * dir * 4.0 * m1 / (m0 + m1 + m2); - } - - if (im2 > 0.0) - { - p2 += c * dir * 2.0 * m2 / (m0 + m1 + m2); - } - - return true; -} -} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdCollisionConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdCollisionConstraint.cpp deleted file mode 100644 index 529c8103cdb64f2193a53a45cd13be91f296449b..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdCollisionConstraint.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdCollisionConstraint.h" -#include "imstkLogger.h" - -namespace imstk -{ -PbdCollisionConstraint::PbdCollisionConstraint(const unsigned int& n1, const unsigned int& n2) -{ - m_bodiesFirst.resize(n1); - m_bodiesSecond.resize(n2); -} -} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdCollisionConstraint.h b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdCollisionConstraint.h deleted file mode 100644 index 854c1fe65c590f5dc015ea6ed0f2e099c454ebac..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdCollisionConstraint.h +++ /dev/null @@ -1,67 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include <vector> -#include <memory> - -namespace imstk -{ -class PbdModel; - -/// -/// \class PbdCollisionConstraint -/// -/// \brief -/// -class PbdCollisionConstraint -{ -public: - enum class Type - { - EdgeEdge, - PointTriangle - }; - - /// - /// \brief - /// - PbdCollisionConstraint(const unsigned int& n1, const unsigned int& n2); - - /// - /// \brief Destructor - /// - virtual ~PbdCollisionConstraint() = default; - - /// - /// \brief - /// - virtual bool solvePositionConstraint() = 0; - -public: - std::vector<size_t> m_bodiesFirst; ///> index of points for the first object - std::vector<size_t> m_bodiesSecond; ///> index of points for the second object - - std::shared_ptr<PbdModel> m_model1; - std::shared_ptr<PbdModel> m_model2; -}; -} diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDihedralConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDihedralConstraint.cpp deleted file mode 100644 index ed327ad64ed545897f96d7cef41d5aaef8daa535..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDihedralConstraint.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdDihedralConstraint.h" -#include "imstkPbdModel.h" - -namespace imstk -{ -void -PbdDihedralConstraint::initConstraint(PbdModel& model, - const size_t& pIdx1, const size_t& pIdx2, - const size_t& pIdx3, const size_t& pIdx4, - const double k) -{ - m_vertexIds[0] = pIdx1; - m_vertexIds[1] = pIdx2; - m_vertexIds[2] = pIdx3; - m_vertexIds[3] = pIdx4; - - m_stiffness = k; - auto state = model.getInitialState(); - - const Vec3d& p0 = state->getVertexPosition(pIdx1); - const Vec3d& p1 = state->getVertexPosition(pIdx2); - const Vec3d& p2 = state->getVertexPosition(pIdx3); - const Vec3d& p3 = state->getVertexPosition(pIdx4); - - const Vec3d n1 = (p2 - p0).cross(p3 - p0).normalized(); - const Vec3d n2 = (p3 - p1).cross(p2 - p1).normalized(); - - m_restAngle = atan2(n1.cross(n2).dot(p3 - p2), (p3 - p2).norm() * n1.dot(n2)); -} - -bool -PbdDihedralConstraint::solvePositionConstraint(PbdModel& model) -{ - const auto i1 = m_vertexIds[0]; - const auto i2 = m_vertexIds[1]; - const auto i3 = m_vertexIds[2]; - const auto i4 = m_vertexIds[3]; - - auto state = model.getCurrentState(); - - Vec3d& p0 = state->getVertexPosition(i1); - Vec3d& p1 = state->getVertexPosition(i2); - Vec3d& p2 = state->getVertexPosition(i3); - Vec3d& p3 = state->getVertexPosition(i4); - - const auto im0 = model.getInvMass(i1); - const auto im1 = model.getInvMass(i2); - const auto im2 = model.getInvMass(i3); - const auto im3 = model.getInvMass(i4); - - if (im0 == 0.0 && im1 == 0.0) - { - return false; - } - - const auto e = p3 - p2; - const auto e1 = p3 - p0; - const auto e2 = p0 - p2; - const auto e3 = p3 - p1; - const auto e4 = p1 - p2; - // To accelerate, all normal (area) vectors and edge length should be precomputed in parallel - auto n1 = e1.cross(e); - auto n2 = e.cross(e3); - const auto A1 = n1.norm(); - const auto A2 = n2.norm(); - n1 /= A1; - n2 /= A2; - - const double l = e.norm(); - if (l < m_epsilon) - { - return false; - } - - const Vec3d grad0 = -(l / A1) * n1; - const Vec3d grad1 = -(l / A2) * n2; - const Vec3d grad2 = (e.dot(e1) / (A1 * l)) * n1 + (e.dot(e3) / (A2 * l)) * n2; - const Vec3d grad3 = (e.dot(e2) / (A1 * l)) * n1 + (e.dot(e4) / (A2 * l)) * n2; - - auto lambda = im0 * grad0.squaredNorm() + - im1 * grad1.squaredNorm() + - im2 * grad2.squaredNorm() + - im3 * grad3.squaredNorm(); - - // huge difference if use acos instead of atan2 - lambda = (atan2(n1.cross(n2).dot(e), l * n1.dot(n2)) - m_restAngle) / lambda * m_stiffness; - - if (im0 > 0) - { - p0 += -im0 * lambda * grad0; - } - - if (im1 > 0) - { - p1 += -im1 * lambda * grad1; - } - - if (im2 > 0) - { - p2 += -im2 * lambda * grad2; - } - - if (im3 > 0) - { - p3 += -im3 * lambda * grad3; - } - - return true; -} -} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDistanceConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDistanceConstraint.cpp deleted file mode 100644 index a989446063f7cb62cbc4dfed88eb7aadc8aade66..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdDistanceConstraint.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdDistanceConstraint.h" -#include "imstkPbdModel.h" - -namespace imstk -{ -void -PbdDistanceConstraint::initConstraint(PbdModel& model, const size_t& pIdx1, - const size_t& pIdx2, const double k) -{ - m_vertexIds[0] = pIdx1; - m_vertexIds[1] = pIdx2; - m_stiffness = k; - - auto state = model.getInitialState(); - const Vec3d& p1 = state->getVertexPosition(pIdx1); - const Vec3d& p2 = state->getVertexPosition(pIdx2); - - m_restLength = (p1 - p2).norm(); -} - -bool -PbdDistanceConstraint::solvePositionConstraint(PbdModel& model) -{ - const auto i1 = m_vertexIds[0]; - const auto i2 = m_vertexIds[1]; - - auto state = model.getCurrentState(); - - Vec3d& p0 = state->getVertexPosition(i1); - Vec3d& p1 = state->getVertexPosition(i2); - - const auto im1 = model.getInvMass(i1); - const auto im2 = model.getInvMass(i2); - - const auto wsum = im1 + im2; - - if (wsum == 0.0) - { - return false; - } - - Vec3d n = p1 - p0; - const auto len = n.norm(); - n /= len; - - auto gradC = n * m_stiffness * (len - m_restLength) / wsum; - - if (im1 > 0) - { - p0 += im1 * gradC; - } - - if (im2 > 0) - { - p1 += -im2 * gradC; - } - return true; -} -} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.cpp deleted file mode 100644 index 38401eec48d23588835940325f5e7acd831cde4f..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdEdgeEdgeCollisionConstraint.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdEdgeEdgeCollisionConstraint.h" -#include "imstkPbdModel.h" - -#include "imstkLogger.h" - -namespace imstk -{ -void -PbdEdgeEdgeConstraint::initConstraint(std::shared_ptr<PbdModel> model1, - const size_t& pIdx1, const size_t& pIdx2, - std::shared_ptr<PbdModel> model2, - const size_t& pIdx3, const size_t& pIdx4) -{ - m_model1 = model1; - m_model2 = model2; - m_bodiesFirst[0] = pIdx1; - m_bodiesFirst[1] = pIdx2; - m_bodiesSecond[0] = pIdx3; - m_bodiesSecond[1] = pIdx4; -} - -bool -PbdEdgeEdgeConstraint::solvePositionConstraint() -{ - const auto i0 = m_bodiesFirst[0]; - const auto i1 = m_bodiesFirst[1]; - const auto i2 = m_bodiesSecond[0]; - const auto i3 = m_bodiesSecond[1]; - - auto state1 = m_model1->getCurrentState(); - auto state2 = m_model2->getCurrentState(); - - Vec3d& x0 = state1->getVertexPosition(i0); - Vec3d& x1 = state1->getVertexPosition(i1); - Vec3d& x2 = state2->getVertexPosition(i2); - Vec3d& x3 = state2->getVertexPosition(i3); - - auto a = (x3 - x2).dot(x1 - x0); - auto b = (x1 - x0).dot(x1 - x0); - auto c = (x0 - x2).dot(x1 - x0); - auto d = (x3 - x2).dot(x3 - x2); - auto e = a; - auto f = (x0 - x2).dot(x3 - x2); - - auto det = a * e - d * b; - double s = 0.5; - double t = 0.5; - if (fabs(det) > 1e-12) - { - s = (c * e - b * f) / det; - t = (c * d - a * f) / det; - if (s < 0 || s > 1.0 || t < 0 || t > 1.0) - { - return false; - } - } - else - { - //LOG(WARNING) << "det is null"; - } - - Vec3d P = x0 + t * (x1 - x0); - Vec3d Q = x2 + s * (x3 - x2); - - Vec3d n = Q - P; - auto l = n.norm(); - n /= l; - - const auto dist = m_model1->getParameters()->m_proximity + m_model2->getParameters()->m_proximity; - - if (l > dist) - { - return false; - } - - Vec3d grad0 = -(1 - t) * n; - Vec3d grad1 = -(t) * n; - Vec3d grad2 = (1 - s) * n; - Vec3d grad3 = (s) * n; - - const auto im0 = m_model1->getInvMass(i0); - const auto im1 = m_model1->getInvMass(i1); - const auto im2 = m_model2->getInvMass(i2); - const auto im3 = m_model2->getInvMass(i3); - - auto lambda = im0 * grad0.squaredNorm() + - im1 * grad1.squaredNorm() + - im2 * grad2.squaredNorm() + - im3 * grad3.squaredNorm(); - - lambda = (l - dist) / lambda; - -// LOG(INFO) << "Lambda:" << lambda <<" Normal:" << n[0] <<" " << n[1] <<" "<<n[2]; - - if (im0 > 0) - { - x0 += -im0* lambda* grad0* m_model1->getParameters()->m_contactStiffness; - } - - if (im1 > 0) - { - x1 += -im1* lambda* grad1* m_model1->getParameters()->m_contactStiffness; - } - - if (im2 > 0) - { - x2 += -im2* lambda* grad2* m_model2->getParameters()->m_contactStiffness; - } - - if (im3 > 0) - { - x3 += -im3* lambda* grad3* m_model2->getParameters()->m_contactStiffness; - } - - return true; -} -} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEHexConstraint.h b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEHexConstraint.h deleted file mode 100644 index 6d772d6675c75b8a65522063b3c7519fd59fda1a..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdFEHexConstraint.h +++ /dev/null @@ -1,62 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include "imstkPbdFEMConstraint.h" - -namespace imstk -{ -/// -/// \class PbdFEMHexConstraint -/// -/// \brief The FEMHexConstraint class class for constraint as the elastic energy -/// computed by linear shape functions with hexahedral mesh. -/// -class PbdFEMHexConstraint : public PbdFEMConstraint -{ -public: - /// - /// \brief Constructor - /// - explicit PbdFEMHexConstraint(MaterialType mtype = MaterialType::StVK) : - PbdFEMConstraint(8, mtype) {} - - /// - /// \brief Get the type of FEM constraint - /// - inline Type getType() const override { return Type::FEMHex; } - - /// - /// \brief Initializes the FEM hexahedral element constraint - /// - bool initConstraint(PbdModel& model, const unsigned int& pIdx1, - const unsigned int& pIdx2, const unsigned int& pIdx3, - const unsigned int& pIdx4, const unsigned int& pIdx5, - const unsigned int& pIdx6, const unsigned int& pIdx7, - const unsigned int& pIdx8); - - /// - /// \brief Solves the FEM hexahedral element constraint - /// - bool solvePositionConstraint(PbdModel& model) override; -}; -} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdPointTriCollisionConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdPointTriCollisionConstraint.cpp deleted file mode 100644 index f263d1f6a733c4ce5cc266bb77fd81474792f824..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdPointTriCollisionConstraint.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdPointTriCollisionConstraint.h" -#include "imstkPbdModel.h" - -#include "imstkLogger.h" - -namespace imstk -{ -void -PbdPointTriangleConstraint::initConstraint(std::shared_ptr<PbdModel> model1, const size_t& pIdx1, - std::shared_ptr<PbdModel> model2, const size_t& pIdx2, - const size_t& pIdx3, const size_t& pIdx4) -{ - m_model1 = model1; - m_model2 = model2; - m_bodiesFirst[0] = pIdx1; - m_bodiesSecond[0] = pIdx2; - m_bodiesSecond[1] = pIdx3; - m_bodiesSecond[2] = pIdx4; -} - -bool -PbdPointTriangleConstraint::solvePositionConstraint() -{ - const auto i0 = m_bodiesFirst[0]; - const auto i1 = m_bodiesSecond[0]; - const auto i2 = m_bodiesSecond[1]; - const auto i3 = m_bodiesSecond[2]; - - auto state1 = m_model1->getCurrentState(); - auto state2 = m_model2->getCurrentState(); - - Vec3d& x0 = state1->getVertexPosition(i0); - Vec3d& x1 = state2->getVertexPosition(i1); - Vec3d& x2 = state2->getVertexPosition(i2); - Vec3d& x3 = state2->getVertexPosition(i3); - - Vec3d x12 = x2 - x1; - Vec3d x13 = x3 - x1; - Vec3d n = x12.cross(x13); - Vec3d x01 = x0 - x1; - - double alpha = n.dot(x12.cross(x01)) / (n.dot(n)); - double beta = n.dot(x01.cross(x13)) / (n.dot(n)); - - if (alpha < 0 || beta < 0 || alpha + beta > 1) - { - //LOG(WARNING) << "Projection point not inside the triangle"; - return false; - } - - const double dist = m_model1->getParameters()->m_proximity + m_model2->getParameters()->m_proximity; - - n.normalize(); - - double l = x01.dot(n); - - if (l > dist) - { - return false; - } - - double gamma = 1.0 - alpha - beta; - Vec3d grad0 = n; - Vec3d grad1 = -alpha * n; - Vec3d grad2 = -beta * n; - Vec3d grad3 = -gamma * n; - - const auto im0 = m_model1->getInvMass(i0); - const auto im1 = m_model2->getInvMass(i1); - const auto im2 = m_model2->getInvMass(i2); - const auto im3 = m_model2->getInvMass(i3); - - double lambda = im0 * grad0.squaredNorm() + - im1 * grad1.squaredNorm() + - im2 * grad2.squaredNorm() + - im3 * grad3.squaredNorm(); - - lambda = (l - dist) / lambda; - - //LOG(INFO) << "Lambda:" << lambda <<" Normal:" << n[0] <<" " << n[1] <<" "<<n[2]; - - if (im0 > 0) - { - x0 += -im0* lambda* grad0* m_model1->getParameters()->m_contactStiffness; - } - - if (im1 > 0) - { - x1 += -im1* lambda* grad1* m_model2->getParameters()->m_contactStiffness; - } - - if (im2 > 0) - { - x2 += -im2* lambda* grad2* m_model2->getParameters()->m_contactStiffness; - } - - if (im3 > 0) - { - x3 += -im3* lambda* grad3* m_model2->getParameters()->m_contactStiffness; - } - - return true; -} -} // imstk diff --git a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdVolumeConstraint.cpp b/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdVolumeConstraint.cpp deleted file mode 100644 index b6dce29fd6d1ffa45a05836416fb20f20dd04fcc..0000000000000000000000000000000000000000 --- a/Source/DynamicalModels/ObjectModels/PbdConstraints/imstkPbdVolumeConstraint.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdVolumeConstraint.h" -#include "imstkPbdModel.h" - -namespace imstk -{ -void -PbdVolumeConstraint::initConstraint(PbdModel& model, const size_t& pIdx1, - const size_t& pIdx2, const size_t& pIdx3, - const size_t& pIdx4, const double k) -{ - m_vertexIds[0] = pIdx1; - m_vertexIds[1] = pIdx2; - m_vertexIds[2] = pIdx3; - m_vertexIds[3] = pIdx4; - - m_stiffness = k; - - auto state = model.getInitialState(); - - const Vec3d& p0 = state->getVertexPosition(pIdx1); - const Vec3d& p1 = state->getVertexPosition(pIdx2); - const Vec3d& p2 = state->getVertexPosition(pIdx3); - const Vec3d& p3 = state->getVertexPosition(pIdx4); - - m_restVolume = (1.0 / 6.0) * ((p1 - p0).cross(p2 - p0)).dot(p3 - p0); -} - -bool -PbdVolumeConstraint::solvePositionConstraint(PbdModel& model) -{ - const auto i1 = m_vertexIds[0]; - const auto i2 = m_vertexIds[1]; - const auto i3 = m_vertexIds[2]; - const auto i4 = m_vertexIds[3]; - - auto state = model.getCurrentState(); - - Vec3d& x1 = state->getVertexPosition(i1); - Vec3d& x2 = state->getVertexPosition(i2); - Vec3d& x3 = state->getVertexPosition(i3); - Vec3d& x4 = state->getVertexPosition(i4); - - const auto im1 = model.getInvMass(i1); - const auto im2 = model.getInvMass(i2); - const auto im3 = model.getInvMass(i3); - const auto im4 = model.getInvMass(i4); - - const double onesixth = 1.0 / 6.0; - - const Vec3d grad1 = onesixth * (x2 - x3).cross(x4 - x2); - const Vec3d grad2 = onesixth * (x3 - x1).cross(x4 - x1); - const Vec3d grad3 = onesixth * (x4 - x1).cross(x2 - x1); - const Vec3d grad4 = onesixth * (x2 - x1).cross(x3 - x1); - - const auto V = grad4.dot(x4 - x1); - - double lambda = im1 * grad1.squaredNorm() + - im2 * grad2.squaredNorm() + - im3 * grad3.squaredNorm() + - im4 * grad4.squaredNorm(); - - lambda = (V - m_restVolume) / lambda * m_stiffness; - - if (im1 > 0) - { - x1 += -im1 * lambda * grad1; - } - - if (im2 > 0) - { - x2 += -im2 * lambda * grad2; - } - - if (im3 > 0) - { - x3 += -im3 * lambda * grad3; - } - - if (im4 > 0) - { - x4 += -im4 * lambda * grad4; - } - - return true; -} -} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp index 436d8d5e5d1442b235e2dbe883637100b8efd326..7264f71fa8c33601aa358b3d8d99751b19bcd454 100644 --- a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp @@ -21,9 +21,15 @@ #include "imstkAbstractDynamicalModel.h" #include "imstkLogger.h" +#include "imstkTaskGraph.h" namespace imstk { +AbstractDynamicalModel::AbstractDynamicalModel(DynamicalModelType type) : + m_type(type), m_numDOF(0), m_taskGraph(std::make_shared<TaskGraph>("AbstractDynamicalModel_Source", "AbstractDynamicalModel_Sink")) +{ +} + bool AbstractDynamicalModel::isGeometryValid(const std::shared_ptr<Geometry> geometry) { @@ -65,4 +71,17 @@ AbstractDynamicalModel::setModelGeometry(std::shared_ptr<Geometry> geometry) LOG(WARNING) << "Invalid geometry for Model"; } } + +void +AbstractDynamicalModel::initGraphEdges() +{ + m_taskGraph->clearEdges(); + initGraphEdges(m_taskGraph->getSource(), m_taskGraph->getSink()); +} + +void +AbstractDynamicalModel::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) +{ + m_taskGraph->addEdge(source, sink); +} } // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h index 6d7a547292831a79e2f71e79d414da4e3cd3b5cb..8201dde345b65c1dc7b37e7e1f2c629440afc5a8 100644 --- a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h @@ -26,9 +26,13 @@ #include <set> #include <string> +#include <unordered_map> namespace imstk { +class TaskGraph; +class TaskNode; + /// /// \brief Type of the time dependent mathematical model /// @@ -58,7 +62,6 @@ enum class TimeSteppingType class AbstractDynamicalModel { public: - /// /// \brief Type of the update of the state of the body /// @@ -71,17 +74,13 @@ public: None }; -public: - /// - /// \brief Constructor - /// - AbstractDynamicalModel(DynamicalModelType type = DynamicalModelType::None) : m_type(type), m_numDOF(0) {} +protected: + AbstractDynamicalModel(DynamicalModelType type = DynamicalModelType::None); - /// - /// \brief Destructor - /// +public: virtual ~AbstractDynamicalModel() = default; +public: /// /// \brief Reset the current state to the initial state /// @@ -93,6 +92,8 @@ public: std::size_t getNumDegreeOfFreedom() const { return m_numDOF; } void setNumDegreeOfFreedom(const size_t nDof) { m_numDOF = nDof; } + std::shared_ptr<TaskGraph> getTaskGraph() const { return m_taskGraph; } + /// /// \brief Get the type of the object /// @@ -104,9 +105,9 @@ public: virtual void updateBodyStates(const Vectord& q, const StateUpdateType updateType = StateUpdateType::Displacement) = 0; /// - /// \brief + /// \brief Update the geometry of the model /// - virtual void updatePhysicsGeometry() {} + virtual void updatePhysicsGeometry() { } /// /// \brief Set the time step size @@ -138,20 +139,33 @@ public: /// virtual bool initialize() = 0; + /// + /// \brief Initializes the edges of the graph + /// + void initGraphEdges(); + /// /// \brief Set the type of approach used to update the time step size after every frame /// virtual void setTimeStepSizeType(const TimeSteppingType type) { m_timeStepSizeType = type; } TimeSteppingType getTimeStepSizeType() { return m_timeStepSizeType; } +protected: + /// + /// \brief Setup connectivity of the compute graph + /// + virtual void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink); + protected: DynamicalModelType m_type; ///> Mathematical model type std::size_t m_numDOF; ///> Total number of degree of freedom - std::shared_ptr<Geometry> m_geometry; ///> Physics geometry of the model + std::shared_ptr<Geometry> m_geometry = nullptr; ///> Physics geometry of the model std::set<Geometry::Type> m_validGeometryTypes; ///> Valid geometry types of this model TimeSteppingType m_timeStepSizeType = TimeSteppingType::Fixed; + + std::shared_ptr<TaskGraph> m_taskGraph = nullptr; }; } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkDynamicalModel.h b/Source/DynamicalModels/ObjectModels/imstkDynamicalModel.h index e6b8ee325a45672956f2c12b40c35981ece876c6..b1b79d3556403de0ca74a1e604586454ebea3100 100644 --- a/Source/DynamicalModels/ObjectModels/imstkDynamicalModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkDynamicalModel.h @@ -62,7 +62,7 @@ public: /// /// \brief Reset the current state to the initial state /// - void resetToInitialState() override + virtual void resetToInitialState() override { m_currentState->setState(m_initialState); m_previousState->setState(m_initialState); diff --git a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp index 02d42bdfcfbfad82ad859c45fbcf1eb9547f8c2e..00c523e8bac8825ffcfe97c79632a2cdbe3bb178 100644 --- a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp @@ -24,17 +24,18 @@ //imstk #include "imstkFEMDeformableBodyModel.h" -#include "imstkStVKForceModel.h" -#include "imstkLinearFEMForceModel.h" +#include "imstkConjugateGradient.h" #include "imstkCorotationalFEMForceModel.h" +#include "imstkInternalForceModel.h" #include "imstkIsotropicHyperelasticFEMForceModel.h" -#include "imstkVegaMeshIO.h" -#include "imstkVolumetricMesh.h" +#include "imstkLinearFEMForceModel.h" +#include "imstkMath.h" +#include "imstkSolverBase.h" +#include "imstkStVKForceModel.h" +#include "imstkTaskGraph.h" #include "imstkTimeIntegrator.h" #include "imstkVegaMeshIO.h" -#include "imstkNewtonSolver.h" -#include "imstkInternalForceModel.h" -#include "imstkMath.h" +#include "imstkVolumetricMesh.h" // vega #include "generateMassMatrix.h" @@ -56,6 +57,8 @@ FEMDeformableBodyModel::FEMDeformableBodyModel() : Geometry::Type::TetrahedralMesh, Geometry::Type::HexahedralMesh }; + + m_solveNode = m_taskGraph->addFunction("FEMModel_Solve", [&]() { getSolver()->solve(); }); } FEMDeformableBodyModel::~FEMDeformableBodyModel() @@ -86,7 +89,6 @@ FEMDeformableBodyModel::configure(const std::string& configFileName) vegaConfigFileOptions.addOptionOptional("deformationCompliance", &m_FEModelConfig->m_deformationCompliance, m_FEModelConfig->m_deformationCompliance); vegaConfigFileOptions.addOptionOptional("compressionResistance", &m_FEModelConfig->m_compressionResistance, m_FEModelConfig->m_compressionResistance); vegaConfigFileOptions.addOptionOptional("inversionThreshold", &m_FEModelConfig->m_inversionThreshold, m_FEModelConfig->m_inversionThreshold); - vegaConfigFileOptions.addOptionOptional("numberOfThreads", &m_FEModelConfig->m_numberOfThreads, m_FEModelConfig->m_numberOfThreads); vegaConfigFileOptions.addOptionOptional("gravity", &m_FEModelConfig->m_gravity, m_FEModelConfig->m_gravity); // Parse the configuration file @@ -153,47 +155,37 @@ FEMDeformableBodyModel::configure(std::shared_ptr<FEMModelConfig> config) m_FEModelConfig = config; } -void -FEMDeformableBodyModel::setForceModelConfiguration(std::shared_ptr<FEMModelConfig> fmConfig) -{ - m_FEModelConfig = fmConfig; -} - -std::shared_ptr<imstk::FEMModelConfig> -FEMDeformableBodyModel::getForceModelConfiguration() const +bool +FEMDeformableBodyModel::initialize() { - return m_FEModelConfig; -} + // prerequisite of for successfully initializing + CHECK(m_geometry != nullptr && m_FEModelConfig != nullptr) << "DeformableBodyModel::initialize: Physics mesh or force model configuration not set yet!"; -void -FEMDeformableBodyModel::setInternalForceModel(std::shared_ptr<InternalForceModel> fm) -{ - m_internalForceModel = fm; -} + // Setup default solver if model doesn't yet have one + if (m_solver == nullptr) + { + // Create a nonlinear system + auto nlSystem = std::make_shared<NonLinearSystem<SparseMatrixd>>(getFunction(), getFunctionGradient()); -std::shared_ptr<imstk::InternalForceModel> -FEMDeformableBodyModel::getInternalForceModel() const -{ - return m_internalForceModel; -} + nlSystem->setUnknownVector(getUnknownVec()); + nlSystem->setUpdateFunction(getUpdateFunction()); + nlSystem->setUpdatePreviousStatesFunction(getUpdatePrevStateFunction()); -void -FEMDeformableBodyModel::setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator) -{ - m_timeIntegrator = timeIntegrator; -} + // Create a linear solver + auto linSolver = std::make_shared<ConjugateGradient>(); -std::shared_ptr<imstk::TimeIntegrator> -FEMDeformableBodyModel::getTimeIntegrator() const -{ - return m_timeIntegrator; -} + if (linSolver->getType() == imstk::LinearSolver<imstk::SparseMatrixd>::Type::GaussSeidel + && isFixedBCImplemented()) + { + LOG(WARNING) << "The GS solver may not be viable!"; + } -bool -FEMDeformableBodyModel::initialize() -{ - // prerequisite of for successfully initializing - CHECK(m_geometry != nullptr && m_FEModelConfig != nullptr) << "DeformableBodyModel::initialize: Physics mesh or force model configuration not set yet!"; + // Create a non-linear solver and add to the scene + auto nlSolver = std::make_shared<NewtonSolver<SparseMatrixd>>(); + nlSolver->setLinearSolver(linSolver); + nlSolver->setSystem(nlSystem); + setSolver(nlSolver); + } auto physicsMesh = std::dynamic_pointer_cast<imstk::VolumetricMesh>(this->getModelGeometry()); m_vegaPhysicsMesh = VegaMeshIO::convertVolumetricMeshToVegaMesh(physicsMesh); @@ -293,7 +285,9 @@ bool FEMDeformableBodyModel::initializeForceModel() { const double g = m_FEModelConfig->m_gravity; - const bool isGravityPresent = (g > 0) ? true : false; + // Since vega 4.0 doesn't add gravity correcntly in all cases, we do it ourselves; see \ref initializeGravityForce + // const bool isGravityPresent = (g > 0) ? true : false; + const bool isGravityPresent = false; m_numDOF = (size_t)m_vegaPhysicsMesh->getNumVertices() * 3; @@ -775,12 +769,6 @@ FEMDeformableBodyModel::initializeEigenMatrixFromVegaMatrix(const vega::SparseMa eigenMatrix.makeCompressed(); } -Vectord& -FEMDeformableBodyModel::getContactForce() -{ - return m_Fcontact; -} - void FEMDeformableBodyModel::setFixedSizeTimeStepping() { @@ -799,4 +787,12 @@ FEMDeformableBodyModel::getTimeStep() const { return m_timeIntegrator->getTimestepSize(); }; + +void +FEMDeformableBodyModel::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) +{ + // Setup graph connectivity + m_taskGraph->addEdge(source, m_solveNode); + m_taskGraph->addEdge(m_solveNode, sink); +} } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h index 9641e0cba7dfb8409c037f238f0619cd75070c82..01c1b9ea94fd57850b15fff607979ac2a1a487ae 100644 --- a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h @@ -25,6 +25,7 @@ #include "imstkDynamicalModel.h" #include "imstkInternalForceModelTypes.h" #include "imstkNonLinearSystem.h" +#include "imstkNewtonSolver.h" // vega #include "sparseMatrix.h" @@ -36,12 +37,10 @@ class VolumetricMesh; namespace imstk { +class InternalForceModel; class TimeIntegrator; +class SolverBase; class VegaMeshIO; -// class NewtonSolver<SparseMatrixd>; -// template <typename SystemMatrix> -// class NewtonSolver; -class InternalForceModel; struct FEMModelConfig { @@ -59,9 +58,6 @@ struct FEMModelConfig double m_compressionResistance = 500.0; double m_inversionThreshold = -std::numeric_limits<double>::max(); double m_gravity = 9.81; - - // \todo remove from here ? - int m_numberOfThreads = 4; }; /// @@ -87,6 +83,7 @@ public: /// ~FEMDeformableBodyModel(); +public: /// /// \brief Configure the force model from external file /// @@ -101,20 +98,20 @@ public: /// /// \brief Set/Get force model configuration /// - void setForceModelConfiguration(std::shared_ptr<FEMModelConfig> fmConfig); - std::shared_ptr<FEMModelConfig> getForceModelConfiguration() const; + void setForceModelConfiguration(std::shared_ptr<FEMModelConfig> fmConfig) { this->m_FEModelConfig = fmConfig; } + std::shared_ptr<FEMModelConfig> getForceModelConfiguration() const { return m_FEModelConfig; } /// /// \brief Set/Get internal force model /// - void setInternalForceModel(std::shared_ptr<InternalForceModel> fm); - std::shared_ptr<InternalForceModel> getInternalForceModel() const; + void setInternalForceModel(std::shared_ptr<InternalForceModel> fm) { m_internalForceModel = fm; } + std::shared_ptr<InternalForceModel> getInternalForceModel() const { return m_internalForceModel; } /// /// \brief Set/Get time integrator /// - void setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator); - std::shared_ptr<TimeIntegrator> getTimeIntegrator() const; + void setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator) { this->m_timeIntegrator = timeIntegrator; } + std::shared_ptr<TimeIntegrator> getTimeIntegrator() const { return m_timeIntegrator; } /// /// \brief Load the initial conditions of the deformable object @@ -230,7 +227,7 @@ public: /// /// \brief Get the contact force vector /// - Vectord& getContactForce(); + Vectord& getContactForce() { return m_Fcontact; } /// /// \brief Returns the unknown vectors @@ -269,39 +266,54 @@ public: void disableFixedBC() { m_implementFixedBC = false; }; bool isFixedBCImplemented() const { return m_implementFixedBC; }; + std::shared_ptr<TaskNode> getSolveNode() const { return m_solveNode; } + + std::shared_ptr<SolverBase> getSolver() const { return m_solver; } + void setSolver(std::shared_ptr<SolverBase> solver) { this->m_solver = solver; } + protected: - std::shared_ptr<InternalForceModel> m_internalForceModel; ///> Mathematical model for intenal forces - std::shared_ptr<TimeIntegrator> m_timeIntegrator; ///> Time integrator - std::shared_ptr<System> m_nonLinearSystem; ///> Nonlinear system resulting from TI and force model + /// + /// \brief Setup the computational graph of FEM + /// + void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) override; - std::shared_ptr<FEMModelConfig> m_FEModelConfig; +protected: + std::shared_ptr<SolverBase> m_solver = nullptr; + std::shared_ptr<InternalForceModel> m_internalForceModel = nullptr; ///> Mathematical model for intenal forces + std::shared_ptr<TimeIntegrator> m_timeIntegrator = nullptr; ///> Time integrator + std::shared_ptr<NonLinearSystem<SparseMatrixd>> m_nonLinearSystem = nullptr; ///> Nonlinear system resulting from TI and force model + + std::shared_ptr<FEMModelConfig> m_FEModelConfig = nullptr; /// Matrices typical to a elastodynamics and 2nd order analogous systems - SparseMatrixd m_M; ///> Mass matrix - SparseMatrixd m_C; ///> Damping coefficient matrix - SparseMatrixd m_K; ///> Tangent (derivative of internal force w.r.t displacements) stiffness matrix - SparseMatrixd m_Keff; ///> Effective stiffness matrix (dependent on internal force model and time integrator) + SparseMatrixd m_M; ///> Mass matrix + SparseMatrixd m_C; ///> Damping coefficient matrix + SparseMatrixd m_K; ///> Tangent (derivative of internal force w.r.t displacements) stiffness matrix + SparseMatrixd m_Keff; ///> Effective stiffness matrix (dependent on internal force model and time integrator) - Vectord m_Finternal; ///> Vector of internal forces - Vectord m_Feff; ///> Vector of effective forces - Vectord m_Fcontact; ///> Vector of contact forces - Vectord m_Fgravity; ///> Vector of gravity forces - Vectord m_FexplicitExternal; ///> Vector of explicitly defined external forces - Vectord m_qSol; ///> Vector to maintain solution at each iteration of nonlinear solver + Vectord m_Finternal; ///> Vector of internal forces + Vectord m_Feff; ///> Vector of effective forces + Vectord m_Fcontact; ///> Vector of contact forces + Vectord m_Fgravity; ///> Vector of gravity forces + Vectord m_FexplicitExternal; ///> Vector of explicitly defined external forces + Vectord m_qSol; ///> Vector to maintain solution at each iteration of nonlinear solver - std::shared_ptr<vega::VolumetricMesh> m_vegaPhysicsMesh; ///> Mesh used for Physics - std::shared_ptr<vega::SparseMatrix> m_vegaMassMatrix; ///> Vega mass matrix - std::shared_ptr<vega::SparseMatrix> m_vegaTangentStiffnessMatrix; ///> Vega Tangent stiffness matrix - std::shared_ptr<vega::SparseMatrix> m_vegaDampingMatrix; ///> Vega Laplacian damping matrix + std::shared_ptr<vega::VolumetricMesh> m_vegaPhysicsMesh = nullptr; ///> Mesh used for Physics + std::shared_ptr<vega::SparseMatrix> m_vegaMassMatrix = nullptr; ///> Vega mass matrix + std::shared_ptr<vega::SparseMatrix> m_vegaTangentStiffnessMatrix = nullptr; ///> Vega Tangent stiffness matrix + std::shared_ptr<vega::SparseMatrix> m_vegaDampingMatrix = nullptr; ///> Vega Laplacian damping matrix - std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed + std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed - StateUpdateType m_updateType = StateUpdateType::DeltaVelocity; ///> Update type of the model + StateUpdateType m_updateType = StateUpdateType::DeltaVelocity; ///> Update type of the model - bool m_damped = false; ///> Viscous or structurally damped system + bool m_damped = false; ///> Viscous or structurally damped system // If this is true, the tangent stiffness and force vector will be modified to // accommodate (the rows and columns will be nullified) the fixed boundary conditions bool m_implementFixedBC = true; + +private: + std::shared_ptr<TaskNode> m_solveNode = nullptr; }; } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp b/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp index 188949934a7743c086eb10660386b07ba27b1fda..a5a583654b712f1d1df16e669756a55adaa4e6d8 100644 --- a/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp @@ -19,34 +19,54 @@ =========================================================================*/ -#include "imstkGraph.h" #include "imstkPbdModel.h" -#include "imstkTetrahedralMesh.h" -#include "imstkSurfaceMesh.h" +#include "imstkGeometryUtilities.h" +#include "imstkGraph.h" #include "imstkLineMesh.h" -#include "imstkPbdVolumeConstraint.h" -#include "imstkPbdDistanceConstraint.h" -#include "imstkPbdDihedralConstraint.h" #include "imstkPbdAreaConstraint.h" #include "imstkPbdBendConstraint.h" -#include "imstkPbdFETetConstraint.h" -#include "imstkPbdFEHexConstraint.h" #include "imstkPbdConstantDensityConstraint.h" -#include "imstkParallelUtils.h" -#include "imstkGeometryUtilities.h" -#include "imstkMeshIO.h" -//#include "imstkColor.h" +#include "imstkPbdDihedralConstraint.h" +#include "imstkPbdDistanceConstraint.h" +#include "imstkPbdFETetConstraint.h" +#include "imstkPbdSolver.h" +#include "imstkPbdVolumeConstraint.h" +#include "imstkSurfaceMesh.h" +#include "imstkTaskGraph.h" +#include "imstkTetrahedralMesh.h" #include <unordered_map> namespace imstk { +PbdModel::PbdModel() : DynamicalModel(DynamicalModelType::PositionBasedDynamics), + m_constraints(std::make_shared<PBDConstraintVector>()), + m_partitionedConstraints(std::make_shared<std::vector<PBDConstraintVector>>()), + m_mass(std::make_shared<StdVectorOfReal>()), + m_invMass(std::make_shared<StdVectorOfReal>()), + m_parameters(std::make_shared<PBDModelConfig>()) +{ + m_validGeometryTypes = { + Geometry::Type::PointSet, + Geometry::Type::LineMesh, + Geometry::Type::SurfaceMesh, + Geometry::Type::TetrahedralMesh, + Geometry::Type::HexahedralMesh + }; + + // Setup PBD compute nodes + m_integrationPositionNode = m_taskGraph->addFunction("PbdModel_IntegratePosition", std::bind(&PbdModel::integratePosition, this)); + m_updateCollisionGeometryNode = m_taskGraph->addFunction("PbdModel_UpdateCollisionGeometry", std::bind(&PbdModel::updatePhysicsGeometry, this)); + m_solveConstraintsNode = m_taskGraph->addFunction("PbdModel_SolveConstraints", [&]() { m_pbdSolver->solve(); }); // Avoids rebinding on solver swap + m_updateVelocityNode = m_taskGraph->addFunction("PbdModel_UpdateVelocity", std::bind(&PbdModel::updateVelocity, this)); +} + void PBDModelConfig::enableConstraint(PbdConstraint::Type type, double stiffness) { LOG_IF(FATAL, (type == PbdConstraint::Type::FEMTet || type == PbdConstraint::Type::FEMHex)) << "FEM constraint should be enabled by the enableFEMConstraint function"; - m_RegularConstraints.push_back({ type, stiffness }); + m_regularConstraints.push_back({ type, stiffness }); } void @@ -58,11 +78,11 @@ PBDModelConfig::enableFEMConstraint(PbdConstraint::Type type, PbdFEMConstraint:: } void -PbdModel::configure(const std::shared_ptr<PBDModelConfig>& params) +PbdModel::configure(std::shared_ptr<PBDModelConfig> params) { LOG_IF(FATAL, (!this->getModelGeometry())) << "PbdModel::configure - Set PBD Model geometry before configuration!"; - m_Parameters = params; + m_parameters = params; this->setNumDegreeOfFreedom(std::dynamic_pointer_cast<PointSet>(m_geometry)->getNumVertices() * 3); } @@ -76,31 +96,38 @@ PbdModel::initialize() m_previousState = std::make_shared<PbdState>(); m_currentState = std::make_shared<PbdState>(); - bool option[3] = { 1, 0, 0 }; - m_initialState->initialize(m_mesh, option); - m_previousState->initialize(m_mesh, option); - - option[1] = option[2] = 1; - m_currentState->initialize(m_mesh, option); - - m_initialState->setPositions(m_mesh->getVertexPositions()); - m_currentState->setPositions(m_mesh->getVertexPositions()); + m_initialState->initialize(m_mesh->getVertexPositions()); + m_previousState->initialize(m_mesh->getVertexPositions()); + m_currentState->initialize(m_mesh->getVertexPositions()); auto numParticles = m_mesh->getNumVertices(); - m_mass.resize(numParticles, 0); - m_invMass.resize(numParticles, 0); - setUniformMass(m_Parameters->m_uniformMassValue); + m_mass->resize(numParticles, 0); + m_invMass->resize(numParticles, 0); + setUniformMass(m_parameters->m_uniformMassValue); - for (auto i : m_Parameters->m_fixedNodeIds) + for (auto i : m_parameters->m_fixedNodeIds) { setFixedPoint(i); } bool bOK = true; // Return immediately if some constraint failed to initialize + // Setup the default pbd solver if none exists + if (m_pbdSolver == nullptr) + { + m_pbdSolver = std::make_shared<PbdSolver>(); + m_pbdSolver->setIterations(m_parameters->m_iterations); + m_pbdSolver->setSolverType(m_parameters->m_solverType); + } + m_pbdSolver->setPositions(getCurrentState()->getPositions()); + m_pbdSolver->setInvMasses(getInvMasses()); + m_pbdSolver->setConstraints(getConstraints()); + m_pbdSolver->setPartitionedConstraints(getPartitionedConstraints()); + m_pbdSolver->setTimeStep(m_parameters->m_dt); + // Initialize FEM constraints - for (auto& constraint: m_Parameters->m_FEMConstraints) + for (auto& constraint: m_parameters->m_FEMConstraints) { computeElasticConstants(); if (!initializeFEMConstraints(constraint.second)) @@ -110,8 +137,17 @@ PbdModel::initialize() } // Initialize other constraints - for (auto& constraint: m_Parameters->m_RegularConstraints) + for (auto& constraint: m_parameters->m_regularConstraints) { + if (m_parameters->m_solverType == PbdConstraint::SolverType::PBD && constraint.second > 1.0) + { + LOG(WARNING) << "for PBD, k should be between [0, 1]"; + } + else if (m_parameters->m_solverType == PbdConstraint::SolverType::xPBD && constraint.second <= 1.0) + { + LOG(WARNING) << "for xPBD, k is Young's Modulu, and should be much larger than 1"; + } + if (!bOK) { return false; @@ -148,30 +184,45 @@ PbdModel::initialize() } // Partition constraints for parallel computation - partitionConstraints(); + if (!m_partitioned) + { + this->partitionConstraints(); + m_partitioned = true; + } this->setTimeStepSizeType(m_timeStepSizeType); return bOK; } +void +PbdModel::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) +{ + // Setup graph connectivity + m_taskGraph->addEdge(source, m_integrationPositionNode); + m_taskGraph->addEdge(m_integrationPositionNode, m_updateCollisionGeometryNode); + m_taskGraph->addEdge(m_updateCollisionGeometryNode, m_solveConstraintsNode); + m_taskGraph->addEdge(m_solveConstraintsNode, m_updateVelocityNode); + m_taskGraph->addEdge(m_updateVelocityNode, sink); +} + void PbdModel::computeElasticConstants() { - if (std::abs(m_Parameters->m_mu) < MIN_REAL - && std::abs(m_Parameters->m_lambda) < MIN_REAL) + if (std::abs(m_parameters->m_femParams->m_mu) < MIN_REAL + && std::abs(m_parameters->m_femParams->m_lambda) < MIN_REAL) { - const auto E = m_Parameters->m_YoungModulus; - const auto nu = m_Parameters->m_PoissonRatio; - m_Parameters->m_mu = E / Real(2.0) / (Real(1.0) + nu); - m_Parameters->m_lambda = E * nu / ((Real(1.0) + nu) * (Real(1.0) - Real(2.0) * nu)); + const auto E = m_parameters->m_femParams->m_YoungModulus; + const auto nu = m_parameters->m_femParams->m_PoissonRatio; + m_parameters->m_femParams->m_mu = E / Real(2.0) / (Real(1.0) + nu); + m_parameters->m_femParams->m_lambda = E * nu / ((Real(1.0) + nu) * (Real(1.0) - Real(2.0) * nu)); } else { - const auto mu = m_Parameters->m_mu; - const auto lambda = m_Parameters->m_lambda; - m_Parameters->m_YoungModulus = mu * (Real(3.0) * lambda + Real(2.0) * mu) / (lambda + mu); - m_Parameters->m_PoissonRatio = lambda / Real(2.0) / (lambda + mu); + const auto mu = m_parameters->m_femParams->m_mu; + const auto lambda = m_parameters->m_femParams->m_lambda; + m_parameters->m_femParams->m_YoungModulus = mu * (Real(3.0) * lambda + Real(2.0) * mu) / (lambda + mu); + m_parameters->m_femParams->m_PoissonRatio = lambda / Real(2.0) / (lambda + mu); } } @@ -192,9 +243,10 @@ PbdModel::initializeFEMConstraints(PbdFEMConstraint::MaterialType type) { auto& tet = elements[k]; auto c = std::make_shared<PbdFEMTetConstraint>(type); - c->initConstraint(*this, tet[0], tet[1], tet[2], tet[3]); + c->initConstraint(*m_initialState->getPositions(), + tet[0], tet[1], tet[2], tet[3], m_parameters->m_femParams); lock.lock(); - m_constraints.push_back(std::move(c)); + m_constraints->push_back(std::move(c)); lock.unlock(); }); return true; @@ -216,9 +268,10 @@ PbdModel::initializeVolumeConstraints(const double stiffness) { auto& tet = elements[k]; auto c = std::make_shared<PbdVolumeConstraint>(); - c->initConstraint(*this, tet[0], tet[1], tet[2], tet[3], stiffness); + c->initConstraint(*m_initialState->getPositions(), + tet[0], tet[1], tet[2], tet[3], stiffness); lock.lock(); - m_constraints.push_back(std::move(c)); + m_constraints->push_back(std::move(c)); lock.unlock(); }); return true; @@ -238,8 +291,8 @@ PbdModel::initializeDistanceConstraints(const double stiffness) { E[i1][i2] = 0; auto c = std::make_shared<PbdDistanceConstraint>(); - c->initConstraint(*this, i1, i2, stiffness); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), i1, i2, stiffness); + m_constraints->push_back(std::move(c)); } }; @@ -310,9 +363,9 @@ PbdModel::initializeAreaConstraints(const double stiffness) { auto& tri = elements[k]; auto c = std::make_shared<PbdAreaConstraint>(); - c->initConstraint(*this, tri[0], tri[1], tri[2], stiffness); + c->initConstraint(*m_initialState->getPositions(), tri[0], tri[1], tri[2], stiffness); lock.lock(); - m_constraints.push_back(std::move(c)); + m_constraints->push_back(std::move(c)); lock.unlock(); }); return true; @@ -338,8 +391,8 @@ PbdModel::initializeBendConstraints(const double stiffness) } auto c = std::make_shared<PbdBendConstraint>(); - c->initConstraint(*this, i1, i2, i3, k); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), i1, i2, i3, k); + m_constraints->push_back(std::move(c)); }; // Create constraints @@ -411,8 +464,8 @@ PbdModel::initializeDihedralConstraints(const double stiffness) } } auto c = std::make_shared<PbdDihedralConstraint>(); - c->initConstraint(*this, tri[2], tri[idx], tri[0], tri[1], stiffness); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), tri[2], tri[idx], tri[0], tri[1], stiffness); + m_constraints->push_back(std::move(c)); } } }; @@ -448,8 +501,8 @@ PbdModel::initializeConstantDensityConstraint(const double stiffness) << "Constant constraint should come with a mesh!"; auto c = std::make_shared<PbdConstantDensityConstraint>(); - c->initConstraint(*this, stiffness); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), stiffness); + m_constraints->push_back(std::move(c)); return true; } @@ -458,10 +511,14 @@ void PbdModel::partitionConstraints(const bool print) { // Form the map { vertex : list_of_constraints_involve_vertex } + PBDConstraintVector& allConstraints = *m_constraints; + + //std::cout << "---------partitionConstraints: " << allConstraints.size() << std::endl; + std::unordered_map<size_t, std::vector<size_t>> vertexConstraints; - for (size_t constrIdx = 0; constrIdx < m_constraints.size(); ++constrIdx) + for (size_t constrIdx = 0; constrIdx < allConstraints.size(); ++constrIdx) { - const auto& constr = m_constraints[constrIdx]; + const auto& constr = allConstraints[constrIdx]; for (const auto& vIds : constr->getVertexIds()) { vertexConstraints[vIds].push_back(constrIdx); @@ -470,7 +527,7 @@ PbdModel::partitionConstraints(const bool print) // Add edges to the constraint graph // Each edge represent a shared vertex between two constraints - Graph constraintGraph(m_constraints.size()); + Graph constraintGraph(allConstraints.size()); for (const auto& kv : vertexConstraints) { const auto& constraints = kv.second; // the list of constraints for a vertex @@ -485,89 +542,68 @@ PbdModel::partitionConstraints(const bool print) vertexConstraints.clear(); // do graph coloring for the constraint graph - const auto coloring = constraintGraph.doColoring(); + const auto coloring = constraintGraph.doColoring(Graph::ColoringMethod::WelshPowell); const auto& partitionIndices = coloring.first; const auto numPartitions = coloring.second; - assert(partitionIndices.size() == m_constraints.size()); + assert(partitionIndices.size() == allConstraints.size()); - m_partitionedConstraints.resize(0); - m_partitionedConstraints.resize(static_cast<size_t>(numPartitions)); + std::vector<PBDConstraintVector>& partitionedConstraints = *m_partitionedConstraints; + partitionedConstraints.resize(0); + partitionedConstraints.resize(static_cast<size_t>(numPartitions)); for (size_t constrIdx = 0; constrIdx < partitionIndices.size(); ++constrIdx) { const auto partitionIdx = partitionIndices[constrIdx]; - m_partitionedConstraints[partitionIdx].push_back(std::move(m_constraints[constrIdx])); + partitionedConstraints[partitionIdx].push_back(std::move(allConstraints[constrIdx])); } // If a partition has size smaller than the partition threshold, then move its constraints back // These constraints will be processed sequentially // Because small size partitions yield bad performance upon running in parallel - m_constraints.resize(0); - for (const auto& constraints : m_partitionedConstraints) + allConstraints.resize(0); + for (const auto& constraints : partitionedConstraints) { if (constraints.size() < m_partitionThreshold) { for (size_t constrIdx = 0; constrIdx < constraints.size(); ++constrIdx) { - m_constraints.push_back(std::move(constraints[constrIdx])); + allConstraints.push_back(std::move(constraints[constrIdx])); } } } // Remove all empty partitions size_t writeIdx = 0; - for (size_t readIdx = 0; readIdx < m_partitionedConstraints.size(); ++readIdx) + for (size_t readIdx = 0; readIdx < partitionedConstraints.size(); ++readIdx) { - if (m_partitionedConstraints[readIdx].size() >= m_partitionThreshold) + if (partitionedConstraints[readIdx].size() >= m_partitionThreshold) { - m_partitionedConstraints[writeIdx++] = std::move(m_partitionedConstraints[readIdx]); + partitionedConstraints[writeIdx++] = std::move(partitionedConstraints[readIdx]); } } - m_partitionedConstraints.resize(writeIdx); + partitionedConstraints.resize(writeIdx); // Print if (print) { size_t numConstraints = 0; int idx = 0; - for (const auto& constraints : m_partitionedConstraints) + for (const auto& constraints : partitionedConstraints) { std::cout << "Partition # " << idx++ << " | # nodes: " << constraints.size() << std::endl; numConstraints += constraints.size(); } - std::cout << "Sequential processing # nodes: " << m_constraints.size() << std::endl; - numConstraints += m_constraints.size(); + std::cout << "Sequential processing # nodes: " << allConstraints.size() << std::endl; + numConstraints += allConstraints.size(); std::cout << "Total constraints: " << numConstraints << " | Graph size: " << constraintGraph.size() << std::endl; } } -void -PbdModel::projectConstraints() -{ - unsigned int i = 0; - while (++i < m_Parameters->m_maxIter) - { - for (auto c: m_constraints) - { - c->solvePositionConstraint(*this); - } - - for (auto& partitionConstraints : m_partitionedConstraints) - { - ParallelUtils::parallelFor(partitionConstraints.size(), - [&](const size_t idx) - { - partitionConstraints[idx]->solvePositionConstraint(*this); - }); - } - } -} - void PbdModel::updatePhysicsGeometry() { - m_mesh->setVertexPositions(m_currentState->getPositions()); + m_mesh->setVertexPositions(*m_currentState->getPositions()); } void @@ -582,7 +618,7 @@ PbdModel::setTimeStepSizeType(const TimeSteppingType type) m_timeStepSizeType = type; if (type == TimeSteppingType::Fixed) { - m_Parameters->m_dt = m_Parameters->m_DefaultDt; + m_parameters->m_dt = m_parameters->m_defaultDt; } } @@ -591,57 +627,55 @@ PbdModel::setUniformMass(const double val) { if (val != 0.0) { - std::fill(m_mass.begin(), m_mass.end(), val); - std::fill(m_invMass.begin(), m_invMass.end(), 1 / val); + std::fill(m_mass->begin(), m_mass->end(), val); + std::fill(m_invMass->begin(), m_invMass->end(), 1.0 / val); } else { - std::fill(m_invMass.begin(), m_invMass.end(), 0.0); - std::fill(m_mass.begin(), m_mass.end(), 0.0); + std::fill(m_invMass->begin(), m_invMass->end(), 0.0); + std::fill(m_mass->begin(), m_mass->end(), 0.0); } } void PbdModel::setParticleMass(const double val, const size_t idx) { + StdVectorOfReal& masses = *m_mass; + StdVectorOfReal& invMasses = *m_invMass; if (idx < m_mesh->getNumVertices()) { - m_mass[idx] = val; - m_invMass[idx] = 1.0 / val; + masses[idx] = val; + invMasses[idx] = 1.0 / val; } } void PbdModel::setFixedPoint(const size_t idx) { + StdVectorOfReal& invMasses = *m_invMass; if (idx < m_mesh->getNumVertices()) { - m_invMass[idx] = 0; + invMasses[idx] = 0.0; } } -double -PbdModel::getInvMass(const size_t idx) const -{ - return m_invMass[idx]; -} - void PbdModel::integratePosition() { - const auto& accn = m_currentState->getAccelerations(); - auto& prevPos = m_previousState->getPositions(); - auto& pos = m_currentState->getPositions(); - auto& vel = m_currentState->getVelocities(); + StdVectorOfVec3d& prevPos = *m_previousState->getPositions(); + StdVectorOfVec3d& pos = *m_currentState->getPositions(); + StdVectorOfVec3d& vel = *m_currentState->getVelocities(); + const StdVectorOfVec3d& accn = *m_currentState->getAccelerations(); + const StdVectorOfReal& invMasses = *m_invMass; ParallelUtils::parallelFor(m_mesh->getNumVertices(), [&](const size_t i) { - if (std::abs(m_invMass[i]) > MIN_REAL) + if (std::abs(invMasses[i]) > MIN_REAL) { - vel[i] += (accn[i] + m_Parameters->m_gravity) * m_Parameters->m_dt; + vel[i] += (accn[i] + m_parameters->m_gravity) * m_parameters->m_dt; prevPos[i] = pos[i]; - pos[i] += (1.0 - m_Parameters->m_viscousDampingCoeff) * vel[i] * m_Parameters->m_dt; + pos[i] += (1.0 - m_parameters->m_viscousDampingCoeff) * vel[i] * m_parameters->m_dt; } }); } @@ -649,16 +683,17 @@ PbdModel::integratePosition() void PbdModel::updateVelocity() { - const auto& prevPos = m_previousState->getPositions(); - const auto& pos = m_currentState->getPositions(); - auto& vel = m_currentState->getVelocities(); + const StdVectorOfVec3d& prevPos = *m_previousState->getPositions(); + const StdVectorOfVec3d& pos = *m_currentState->getPositions(); + StdVectorOfVec3d& vel = *m_currentState->getVelocities(); + const StdVectorOfReal& invMasses = *m_invMass; ParallelUtils::parallelFor(m_mesh->getNumVertices(), [&](const size_t i) { - if (std::abs(m_invMass[i]) > MIN_REAL && m_Parameters->m_dt > 0.) + if (std::abs(invMasses[i]) > MIN_REAL && m_parameters->m_dt > 0.0) { - vel[i] = (pos[i] - prevPos[i]) / m_Parameters->m_dt; + vel[i] = (pos[i] - prevPos[i]) / m_parameters->m_dt; } }); } diff --git a/Source/DynamicalModels/ObjectModels/imstkPbdModel.h b/Source/DynamicalModels/ObjectModels/imstkPbdModel.h index 6d882c25601e33e8e4191a6cd5183f997a66a73e..3cb8f5f587db0eb0be2026b6763c9edc72606856 100644 --- a/Source/DynamicalModels/ObjectModels/imstkPbdModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkPbdModel.h @@ -21,18 +21,16 @@ #pragma once -#include <vector> -#include <Eigen/Dense> - -#include "imstkPbdConstraint.h" -#include "imstkPbdFEMConstraint.h" #include "imstkDynamicalModel.h" +#include "imstkPbdCollisionConstraint.h" +#include "imstkPbdFEMConstraint.h" #include "imstkPbdState.h" -#include "imstkMath.h" +#include "imstkLogger.h" namespace imstk { class PointSet; +class PbdSolver; /// /// \struct PBDModelConfig @@ -40,28 +38,35 @@ class PointSet; /// struct PBDModelConfig { - double m_uniformMassValue = 1.0; ///> Mass properties - double m_viscousDampingCoeff = 0.01; ///> Viscous damping coefficient [0, 1] + double m_uniformMassValue = 1.0; ///> Mass properties + double m_viscousDampingCoeff = 0.01; ///> Viscous damping coefficient [0, 1] - double m_contactStiffness = 1.0; ///> Contact stiffness for collisions - double m_proximity = 0.1; ///> Proximity for collisions + std::shared_ptr<PbdCollisionConstraintConfig> collisionParams = + std::make_shared<PbdCollisionConstraintConfig>(PbdCollisionConstraintConfig + { + 0.1, // Proximity + 1.0 // Contact stiffness + }); ///> Info shared between the collision constraints - unsigned int m_maxIter = 10; ///> Max. pbd iterations - double m_dt = 0.0; ///> Time step size - double m_DefaultDt = 0.01; ///> Default Time step size + unsigned int m_iterations = 10; ///> Pbd iterations + unsigned int m_collisionIterations = 5; ///> Pbd collision iterations + double m_dt = 0.0; ///> Time step size + double m_defaultDt = 0.01; ///> Default Time step size - std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed - Vec3r m_gravity = Vec3r(0, -9.81, 0); ///> Gravity + std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed + Vec3r m_gravity = Vec3r(0, -9.81, 0); ///> Gravity - double m_mu = 0; ///> Lame constant, if constraint type is FEM - double m_lambda = 0; ///> Lame constant, if constraint type is FEM + std::shared_ptr<PbdFEMConstraintConfig> m_femParams = + std::make_shared<PbdFEMConstraintConfig>(PbdFEMConstraintConfig + { + 0.0, // Lame constant, if constraint type is FEM + 0.0, // Lame constant, if constraint type is FEM + 1000.0, // FEM parameter, if constraint type is FEM + 0.2 // FEM parameter, if constraint type is FEM + }); ///> Info shared between the fem constraints - double m_YoungModulus = 1000; ///> FEM parameter, if constraint type is FEM - double m_PoissonRatio = 0.2; ///> FEM parameter, if constraint type is FEM - - std::vector<std::pair<PbdConstraint::Type, double>> m_RegularConstraints; ///> Constraints except FEM - std::vector<std::pair<PbdConstraint::Type, - PbdFEMConstraint::MaterialType>> m_FEMConstraints; ///> FEM constraints + std::vector<std::pair<PbdConstraint::Type, double>> m_regularConstraints; ///> Constraints except FEM + std::vector<std::pair<PbdConstraint::Type, PbdFEMConstraint::MaterialType>> m_FEMConstraints; ///> Constraints except FEM /// /// \brief Enable a regular constraint (constraint that is not FEM constraint) @@ -70,9 +75,26 @@ struct PBDModelConfig void enableConstraint(PbdConstraint::Type type, double stiffness); /// - /// \brief Enable a FEM constraint with a given FEM material + /// \brief Enable a FEM constraint with mu, lambda /// void enableFEMConstraint(PbdConstraint::Type type, PbdFEMConstraint::MaterialType material); + + /// + /// \brief Set the PBD solver type + /// + void setSolverType(const PbdConstraint::SolverType& type) + { + if (type == PbdConstraint::SolverType::GCD) + { + LOG(WARNING) << "GCD is NOT implemented yet, use xPBD instead"; + m_solverType = PbdConstraint::SolverType::xPBD; + return; + } + + m_solverType = type; + } + + PbdConstraint::SolverType m_solverType = PbdConstraint::SolverType::xPBD; }; /// @@ -86,16 +108,7 @@ public: /// /// \brief Constructor /// - PbdModel() : DynamicalModel(DynamicalModelType::PositionBasedDynamics) - { - m_validGeometryTypes = { - Geometry::Type::PointSet, - Geometry::Type::LineMesh, - Geometry::Type::SurfaceMesh, - Geometry::Type::TetrahedralMesh, - Geometry::Type::HexahedralMesh - }; - } + PbdModel(); /// /// \brief Destructor @@ -105,12 +118,12 @@ public: /// /// \brief Set simulation parameters /// - void configure(const std::shared_ptr<PBDModelConfig>& params); + void configure(std::shared_ptr<PBDModelConfig> params); /// /// \brief Get the simulation parameters /// - const std::shared_ptr<PBDModelConfig>& getParameters() const { assert(m_Parameters); return m_Parameters; } + const std::shared_ptr<PBDModelConfig>& getParameters() const { assert(m_parameters); return m_parameters; } /// /// \brief Compute elastic constants: Young Modulus, Poisson Ratio, first and second Lame @@ -154,11 +167,6 @@ public: /// bool initializeConstantDensityConstraint(const double stiffness); - /// - /// \brief compute delta x (position) and update position - /// - void projectConstraints(); - /// /// \brief Update the model geometry from the newest PBD state /// @@ -172,13 +180,13 @@ public: /// /// \brief Returns true if there is at least one constraint /// - bool hasConstraints() const { return !m_constraints.empty() || !m_partitionedConstraints.empty(); } + bool hasConstraints() const { return !m_constraints->empty() || !m_partitionedConstraints->empty(); } /// /// \brief Set the time step size /// - virtual void setTimeStep(const Real timeStep) override { m_Parameters->m_dt = timeStep; } - void setDefaultTimeStep(const Real timeStep) { m_Parameters->m_DefaultDt = static_cast<Real>(timeStep); } + virtual void setTimeStep(const Real timeStep) override { m_parameters->m_dt = timeStep; } + void setDefaultTimeStep(const Real timeStep) { m_parameters->m_defaultDt = static_cast<Real>(timeStep); } /// /// \brief Set the time step size to fixed size @@ -188,8 +196,18 @@ public: /// /// \brief Returns the time step size /// - virtual double getTimeStep() const override { return m_Parameters->m_dt; } - double getDefaultTimeStep() const { return m_Parameters->m_DefaultDt; } + virtual double getTimeStep() const override { return m_parameters->m_dt; } + double getDefaultTimeStep() const { return m_parameters->m_defaultDt; } + + /// + /// \brief + /// + std::shared_ptr<PBDConstraintVector> getConstraints() { return m_constraints; } + + /// + /// \brief + /// + std::shared_ptr<std::vector<PBDConstraintVector>> getPartitionedConstraints() { return m_partitionedConstraints; } /// /// \brief Set uniform mass to all the nodes @@ -209,7 +227,9 @@ public: /// /// \brief Get the inverse of mass of a certain node /// - double getInvMass(const size_t idx) const; + double getInvMass(const size_t idx) const { return (*m_invMass)[idx]; } + + std::shared_ptr<StdVectorOfReal> getInvMasses() { return m_invMass; } /// /// \brief Time integrate the position @@ -237,22 +257,47 @@ public: /// void setConstraintPartitionThreshold(size_t threshold) { m_partitionThreshold = threshold; } + std::shared_ptr<PbdSolver> getSolver() const { return m_pbdSolver; } + + void setSolver(std::shared_ptr<PbdSolver> solver) { this->m_pbdSolver = solver; } + + std::shared_ptr<TaskNode> getIntegratePositionNode() const { return m_integrationPositionNode; } + + std::shared_ptr<TaskNode> getUpdateCollisionGeometryNode() const { return m_updateCollisionGeometryNode; } + + std::shared_ptr<TaskNode> getSolveNode() const { return m_solveConstraintsNode; } + + std::shared_ptr<TaskNode> getUpdateVelocityNode() const { return m_updateVelocityNode; } + protected: /// /// \brief Partition constraints for parallelization /// void partitionConstraints(const bool print = false); - size_t m_partitionThreshold = 16; ///> Threshold for constraint partitioning + /// + /// \brief Setup the computational graph of PBD + /// + void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) override; + +protected: + bool m_partitioned = false; /// \todo this is used in initialize() as a temporary fix to problems on linux + size_t m_partitionThreshold = 16; ///> Threshold for constraint partitioning - std::shared_ptr<PointSet> m_mesh; ///> PointSet on which the pbd model operates on - std::vector<double> m_mass; ///> Mass of nodes - std::vector<double> m_invMass; ///> Inverse of mass of nodes + std::shared_ptr<PbdSolver> m_pbdSolver = nullptr; ///> PBD solver + std::shared_ptr<PointSet> m_mesh = nullptr; ///> PointSet on which the pbd model operates on + std::shared_ptr<StdVectorOfReal> m_mass = nullptr; ///> Mass of nodes + std::shared_ptr<StdVectorOfReal> m_invMass = nullptr; ///> Inverse of mass of nodes - using PBDConstraintVector = std::vector<std::shared_ptr<PbdConstraint>>; + std::shared_ptr<PBDConstraintVector> m_constraints = nullptr; ///> List of pbd constraints + std::shared_ptr<std::vector<PBDConstraintVector>> m_partitionedConstraints = nullptr; ///> List of pbd constraints + std::shared_ptr<PBDModelConfig> m_parameters = nullptr; ///> Model parameters, must be set before simulation - PBDConstraintVector m_constraints; ///> List of pbd constraints - std::vector<PBDConstraintVector> m_partitionedConstraints; ///> List of pbd constraints - std::shared_ptr<PBDModelConfig> m_Parameters; ///> Model parameters, must be set before simulation +protected: + // Computational Nodes + std::shared_ptr<TaskNode> m_integrationPositionNode = nullptr; + std::shared_ptr<TaskNode> m_updateCollisionGeometryNode = nullptr; + std::shared_ptr<TaskNode> m_solveConstraintsNode = nullptr; + std::shared_ptr<TaskNode> m_updateVelocityNode = nullptr; }; } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkReducedStVKBodyModel.cpp b/Source/DynamicalModels/ObjectModels/imstkReducedStVKBodyModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26fa5f05d9752db528933007b12e8708ca33ffda --- /dev/null +++ b/Source/DynamicalModels/ObjectModels/imstkReducedStVKBodyModel.cpp @@ -0,0 +1,693 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// std lib +#include <fstream> +#include <ios> +#include <iomanip> + +// imstk +#include "imstkMath.h" +#include "imstkNewtonSolver.h" +#include "imstkReducedStVKBodyModel.h" +#include "imstkTimeIntegrator.h" +#include "imstkVegaMeshIO.h" +#include "imstkVolumetricMesh.h" +#include "imstkTaskGraph.h" +#include "imstkNewtonSolver.h" + +// vega +#include "StVKReducedInternalForces.h" +#include "matrixIO.h" +#include "modalMatrix.h" +#include "reducedStVKForceModel.h" + +#pragma warning(push) +#pragma warning(disable : 4458) +#include "configFile.h" +#pragma warning(pop) + +namespace imstk +{ +ReducedStVK::ReducedStVK() : DynamicalModel(DynamicalModelType::ElastoDynamics) +{ + // m_fixedNodeIds.reserve(1000); + + m_validGeometryTypes = { Geometry::Type::TetrahedralMesh, Geometry::Type::HexahedralMesh }; + + m_solveNode = m_taskGraph->addFunction("FEMModel_Solve", [&]() { getSolver()->solve(); }); +} + +ReducedStVK::~ReducedStVK() +{ + // Get vega to destruct first (before the shared pointer to the vega mesh is cleaned up) + m_internalForceModel = nullptr; +} + +void +ReducedStVK::configure(const std::string& configFileName) +{ + std::cerr << "not implemented yet" << std::endl; + // Configure the ReducedStVKConfig + m_config = std::make_shared<ReducedStVKConfig>(); + + // char femMethod[256]; + // char invertibleMaterial[256]; + // char fixedDOFFilename[256]; + + vega::ConfigFile vegaConfigFileOptions; + + // configure the options + vegaConfigFileOptions.addOptionOptional("dampingMassCoefficient", + &m_config->m_dampingMassCoefficient, + m_config->m_dampingMassCoefficient); + vegaConfigFileOptions.addOptionOptional("dampingStiffnessCoefficient", + &m_config->m_dampingStiffnessCoefficient, + m_config->m_dampingStiffnessCoefficient); + vegaConfigFileOptions.addOptionOptional("dampingLaplacianCoefficient", + &m_config->m_dampingLaplacianCoefficient, + m_config->m_dampingLaplacianCoefficient); + vegaConfigFileOptions.addOptionOptional("numberOfThreads", + &m_config->m_numberOfThreads, + m_config->m_numberOfThreads); + vegaConfigFileOptions.addOptionOptional("gravity", &m_config->m_gravity, m_config->m_gravity); + + // Parse the configuration file + CHECK(vegaConfigFileOptions.parseOptions(configFileName.data()) == 0) + << "ForceModelConfig::parseConfig - Unable to load the configuration file"; + + // get the root directory of the boundary file name + // std::string rootDir; + // const size_t last_slash_idx = configFileName.rfind('/'); + // if (std::string::npos != last_slash_idx) + // { + // rootDir = configFileName.substr(0, last_slash_idx); + // } + + // file names (remove from here?) + // m_config->m_fixedDOFFilename = rootDir + std::string("/") + std::string(fixedDOFFilename); +} + +void +ReducedStVK::configure(std::shared_ptr<ReducedStVKConfig> config) +{ + m_config = config; +} + +void +ReducedStVK::setForceModelConfiguration(std::shared_ptr<ReducedStVKConfig> fmConfig) +{ + m_config = fmConfig; +} + +std::shared_ptr<imstk::ReducedStVKConfig> +ReducedStVK::getForceModelConfiguration() const +{ + return m_config; +} + +void +// ReducedStVK::setInternalForceModel(std::shared_ptr<InternalForceModel> fm) +ReducedStVK::setInternalForceModel(std::shared_ptr<vega::StVKReducedInternalForces> fm) +{ + m_internalForceModel = fm; +} + +std::shared_ptr<imstk::InternalForceModel> +ReducedStVK::getInternalForceModel() const +{ + // return m_internalForceModel; + LOG(FATAL) << "current implementation can not return an imstk::InternalForceModel."; + // todo: to implement this function, we have to + // 1) template InternalForceModel with SystemMatrix + // 2) define ReducedStVKForceModel by inherit from InternalForceModel + // 3) replace vega::StVKReducedInternalForces with ReducedStVKForceModel + return nullptr; +} + +void +ReducedStVK::setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator) +{ + m_timeIntegrator = timeIntegrator; +} + +std::shared_ptr<imstk::TimeIntegrator> +ReducedStVK::getTimeIntegrator() const +{ + return m_timeIntegrator; +} + +bool +ReducedStVK::initialize() +{ + // prerequisite of for successfully initializing + CHECK(m_geometry != nullptr && m_config != nullptr) + << "DeformableBodyModel::initialize: Physics mesh or force model configuration not set " + "yet!"; + + // Setup default solver if model wasn't assigned one + if (m_solver == nullptr) + { + // create a nonlinear system + auto nlSystem = std::make_shared<NonLinearSystem<Matrixd>>(getFunction(), getFunctionGradient()); + + nlSystem->setUnknownVector(getUnknownVec()); + nlSystem->setUpdateFunction(getUpdateFunction()); + nlSystem->setUpdatePreviousStatesFunction(getUpdatePrevStateFunction()); + + // create a non-linear solver and add to the scene + auto nlSolver = std::make_shared<NewtonSolver<Matrixd>>(); + // nlSolver->setLinearSolver(linSolver); + nlSolver->setMaxIterations(1); + nlSolver->setSystem(nlSystem); + setSolver(nlSolver); + } + + // This will specify \p m_numDOF and \p m_numDOFReduced + this->readModalMatrix(m_config->m_modesFileName); + this->loadInitialStates(); + + auto physicsMesh = std::dynamic_pointer_cast<imstk::VolumetricMesh>(this->getModelGeometry()); + m_vegaPhysicsMesh = VegaMeshIO::convertVolumetricMeshToVegaMesh(physicsMesh); + CHECK(m_numDOF == m_vegaPhysicsMesh->getNumVertices() * 3); + + if (!this->initializeForceModel() || !this->initializeMassMatrix() + || !this->initializeTangentStiffness() || !this->initializeDampingMatrix() + || !this->initializeGravityForce() || !this->initializeExplicitExternalForces()) + { + return false; + } + + // reduced-space + m_Feff.resize(m_numDOFReduced); + m_Finternal.resize(m_numDOFReduced); + m_Finternal.setConstant(0.0); + m_qSolReduced.resize(m_numDOFReduced); + m_qSolReduced.setConstant(0.0); + m_FcontactReduced.resize(m_numDOFReduced); + m_FgravityReduced.resize(m_numDOFReduced); + m_FexplicitExternalReduced.resize(m_numDOFReduced); + + // full-space variable + m_Fcontact.resize(m_numDOF); + m_Fcontact.setConstant(0.0); + m_qSol.resize(m_numDOF); + m_qSol.setConstant(0.0); + + return true; +} + +void +ReducedStVK::readModalMatrix(const std::string& fname) +{ + std::vector<float> Ufloat; + int m, n; + vega::ReadMatrixFromDisk_(fname.c_str(), m, n, Ufloat); + m_numDOF = m; + m_numDOFReduced = n; + std::vector<double> Udouble(Ufloat.size()); + + for (size_t i = 0; i < Ufloat.size(); ++i) + { + Udouble[i] = Ufloat[i]; + } + + m_modalMatrix = + std::make_shared<vega::ModalMatrix>(m_numDOF / 3, m_numDOFReduced, Udouble.data()); + return; +} + +void +ReducedStVK::loadInitialStates() +{ + if (m_numDOF == 0 || m_numDOFReduced == 0) + { + LOG(WARNING) << "ReducedStVK::loadInitialStates() - Num. of degree of freedom is zero!"; + } + + // For now the initial states are set to zero + m_initialState = std::make_shared<kinematicState>(m_numDOF); + m_previousState = std::make_shared<kinematicState>(m_numDOF); + m_currentState = std::make_shared<kinematicState>(m_numDOF); + + m_initialStateReduced = std::make_shared<kinematicState>(m_numDOFReduced); + m_previousStateReduced = std::make_shared<kinematicState>(m_numDOFReduced); + m_currentStateReduced = std::make_shared<kinematicState>(m_numDOFReduced); +} + +bool +ReducedStVK::initializeForceModel() +{ + const double g = m_config->m_gravity; + const bool isGravityPresent = (g > 0) ? true : false; + + // m_numDOFReduced = m_config->r; + m_internalForceModel = std::make_shared<vega::StVKReducedInternalForces>( + m_config->m_cubicPolynomialFilename.c_str(), + m_numDOFReduced); + m_forceModel = std::make_shared<vega::ReducedStVKForceModel>(m_internalForceModel.get()); + + return true; +} + +bool +ReducedStVK::initializeMassMatrix() +{ + CHECK(m_geometry != nullptr) + << "DeformableBodyModel::initializeMassMatrix Force model geometry not set!"; + this->m_massMatrix.resize(m_numDOFReduced * m_numDOFReduced, 0.0); + + // set M to identity + for (size_t i = 0; i < m_numDOFReduced; ++i) + { + this->m_massMatrix[i * m_numDOFReduced + i] = 1.0; + } + this->m_M.resize(m_numDOFReduced, m_numDOFReduced); + this->initializeEigenMatrixFromStdVector(m_massMatrix, m_M); + + return true; +} + +bool +ReducedStVK::initializeDampingMatrix() +{ + // \todo: only mass dampings are considered here. + this->m_dampingMatrix.resize(m_numDOFReduced * m_numDOFReduced, 0.0); + auto cM = m_config->m_dampingMassCoefficient; + auto cK = m_config->m_dampingStiffnessCoefficient; + for (size_t i = 0; i < m_dampingMatrix.size(); ++i) + { + m_dampingMatrix[i] = m_massMatrix[i] * cM + m_stiffnessMatrix[i] * cK; + } + this->m_C.resize(m_numDOFReduced, m_numDOFReduced); + this->initializeEigenMatrixFromStdVector(m_massMatrix, m_C); + m_damped = true; + + return true; +} + +bool +ReducedStVK::initializeTangentStiffness() +{ + CHECK(m_forceModel != nullptr) + << "DeformableBodyModel::initializeTangentStiffness: Tangent stiffness cannot be " + "initialized without force model"; + + this->m_stiffnessMatrix.resize(m_numDOFReduced * m_numDOFReduced, 0.0); + m_forceModel->GetTangentStiffnessMatrix(m_initialStateReduced->getQ().data(), + m_stiffnessMatrix.data()); + this->m_K.resize(m_numDOFReduced, m_numDOFReduced); + this->initializeEigenMatrixFromStdVector(m_stiffnessMatrix, m_K); + return true; +} + +bool +ReducedStVK::initializeGravityForce() +{ + m_Fgravity.resize(m_numDOF); + const double gravity = m_config->m_gravity; + + m_vegaPhysicsMesh->computeGravity(m_Fgravity.data(), gravity); + m_FgravityReduced.resize(m_numDOFReduced); + this->project(m_Fgravity, m_FgravityReduced); + + return true; +} + +void +ReducedStVK::computeImplicitSystemRHS(kinematicState& stateAtT, + kinematicState& newState, + const StateUpdateType updateType) +{ + const auto& uPrev = stateAtT.getQ(); + const auto& vPrev = stateAtT.getQDot(); + auto& u = newState.getQ(); + const auto& v = newState.getQDot(); + + // Do checks if there are uninitialized matrices + m_forceModel->GetTangentStiffnessMatrix(u.data(), m_stiffnessMatrix.data()); + this->initializeEigenMatrixFromStdVector(m_stiffnessMatrix, m_K); + + const double dT = m_timeIntegrator->getTimestepSize(); + + switch (updateType) + { + case StateUpdateType::DeltaVelocity: + + m_Feff = m_K * -(uPrev - u + v * dT); + + if (m_damped) + { + m_Feff -= m_C * v; + } + + m_forceModel->GetInternalForce(u.data(), m_Finternal.data()); + m_Feff -= m_Finternal; + this->project(m_FexplicitExternal, m_FexplicitExternalReduced); + m_Feff += m_FexplicitExternalReduced; + // the reduced gravity has alredy been initialized in initializeGravityForce + // this->project(m_Fgravity, m_FgravityReduced); + m_Feff += m_FgravityReduced; + this->project(m_Fcontact, m_FcontactReduced); + m_Feff += m_FcontactReduced; + m_Feff *= dT; + m_Feff += m_M * (vPrev - v); + + break; + default: + LOG(WARNING) << "ReducedStVK::computeImplicitSystemRHS: Update type not supported"; + } +} + +void +ReducedStVK::computeSemiImplicitSystemRHS(kinematicState& stateAtT, + kinematicState& newState, + const StateUpdateType updateType) +{ + // auto& uPrev = stateAtT.getQ(); + const auto& vPrev = stateAtT.getQDot(); + auto& u = newState.getQ(); + // auto& v = newState.getQDot(); + + // Do checks if there are uninitialized matrices + m_forceModel->GetTangentStiffnessMatrix(u.data(), m_stiffnessMatrix.data()); + this->initializeEigenMatrixFromStdVector(m_stiffnessMatrix, m_K); + + const double dT = m_timeIntegrator->getTimestepSize(); + + switch (updateType) + { + case StateUpdateType::DeltaVelocity: + + m_Feff = m_K * (vPrev * -dT); + + if (m_damped) + { + m_Feff -= m_C * vPrev; + } + + m_forceModel->GetInternalForce(u.data(), m_Finternal.data()); + m_Feff -= m_Finternal; + this->project(m_FexplicitExternal, m_FexplicitExternalReduced); + m_Feff += m_FexplicitExternalReduced; + this->project(m_Fgravity, m_FgravityReduced); + m_Feff += m_FgravityReduced; + this->project(m_Fcontact, m_FcontactReduced); + m_Feff += m_FcontactReduced; + m_Feff *= dT; + + break; + + default: + LOG(FATAL) << "ReducedStVK::computeSemiImplicitSystemRHS: Update type not supported"; + } +} + +void +ReducedStVK::computeImplicitSystemLHS(const kinematicState& stateAtT, + kinematicState& newState, + const StateUpdateType updateType) +{ + const double dT = m_timeIntegrator->getTimestepSize(); + + switch (updateType) + { + case StateUpdateType::DeltaVelocity: + + stateAtT; // supress warning (state is not used in this update type hence can be + // ignored) + + this->updateMassMatrix(); + m_forceModel->GetTangentStiffnessMatrix(newState.getQ().data(), + m_stiffnessMatrix.data()); + this->initializeEigenMatrixFromStdVector(m_stiffnessMatrix, m_K); + this->updateDampingMatrix(); + + m_Keff = m_M; + if (m_damped) + { + m_Keff += dT * m_C; + } + m_Keff += (dT * dT) * m_K; + + break; + + default: + LOG(FATAL) << "ReducedStVK::computeImplicitSystemLHS: Update type not supported"; + } +} + +bool +ReducedStVK::initializeExplicitExternalForces() +{ + m_FexplicitExternal.resize(m_numDOF); + m_FexplicitExternal.setZero(); + m_FexplicitExternalReduced.resize(m_numDOFReduced); + m_FexplicitExternalReduced.setZero(); + + return true; +} + +void +ReducedStVK::updateDampingMatrix() +{ + if (!m_damped) + { + return; + } + + const auto& dampingStiffnessCoefficient = m_config->m_dampingStiffnessCoefficient; + const auto& dampingMassCoefficient = m_config->m_dampingMassCoefficient; + + if (dampingMassCoefficient > 0) + { + m_C = dampingMassCoefficient * m_M; + + if (dampingStiffnessCoefficient > 0) + { + m_C += m_K * dampingStiffnessCoefficient; + } + } + else if (dampingStiffnessCoefficient > 0) + { + m_C = m_K * dampingStiffnessCoefficient; + } +} + +void +ReducedStVK::applyBoundaryConditions(Matrixd& M, const bool withCompliance) const +{ + // I believe there is nothing to do here since the full space has already been constrained + // before the reduction. + return; +} + +void +ReducedStVK::applyBoundaryConditions(Vectord& x) const +{ + LOG(WARNING) << "Boundary conditions are not not allowed to change"; +} + +void +ReducedStVK::updateMassMatrix() +{ + // Do nothing for now as topology changes are not supported yet! +} + +void +ReducedStVK::updatePhysicsGeometry() +{ + auto volMesh = std::static_pointer_cast<VolumetricMesh>(m_geometry); + auto& uReduced = m_currentStateReduced->getQ(); + auto& uFull = m_currentState->getQ(); + this->prolongate(uReduced, uFull); + volMesh->setVertexDisplacements(uFull); +} + +void +ReducedStVK::updateBodyPreviousStates() +{ + // full-space + m_previousStateReduced->setU(m_currentStateReduced->getQ()); + m_previousStateReduced->setV(m_currentStateReduced->getQDot()); + prolongate(*m_previousStateReduced, *m_previousState); +} + +void +ReducedStVK::updateBodyStates(const Vectord& solution, const StateUpdateType updateType) +{ + this->updateBodyPreviousStates(); + this->updateBodyIntermediateStates(solution, updateType); +} + +void +ReducedStVK::updateBodyIntermediateStates(const Vectord& solution, const StateUpdateType updateType) +{ + const auto& uPrev = m_previousStateReduced->getQ(); + // auto& u = m_currentState->getQ(); + const auto& v = m_currentStateReduced->getQDot(); + const double dT = m_timeIntegrator->getTimestepSize(); + + switch (updateType) + { + case StateUpdateType::DeltaVelocity: + m_currentStateReduced->setV(v + solution); + m_currentStateReduced->setU(uPrev + dT * v); + + break; + + case StateUpdateType::Velocity: + m_currentStateReduced->setV(solution); + m_currentStateReduced->setU(uPrev + dT * v); + + break; + + default: + LOG(FATAL) << "DeformableBodyModel::updateBodyIntermediateStates: Unknown state update " + "type"; + } + prolongate(*m_currentStateReduced, *m_currentState); + m_qSolReduced = m_currentStateReduced->getQ(); +} + +NonLinearSystem<Matrixd>::VectorFunctionType +ReducedStVK::getFunction() +{ +#pragma warning(push) +#pragma warning(disable : 4100) + + // Function to evaluate the nonlinear objective function given the current state + return [&, this](const Vectord& q, const bool semiImplicit) -> const Vectord& { + (semiImplicit) ? this->computeSemiImplicitSystemRHS(*m_previousStateReduced, + *m_currentStateReduced, + m_updateType) + : this->computeImplicitSystemRHS(*m_previousStateReduced, + *m_currentStateReduced, + m_updateType); + return m_Feff; + }; + +#pragma warning(pop) +} + +NonLinearSystem<Matrixd>::MatrixFunctionType +ReducedStVK::getFunctionGradient() +{ +#pragma warning(push) +#pragma warning(disable : 4100) + // Gradient of the nonlinear objective function given the current state + return [&, this](const Vectord& q) -> const Matrixd& { + this->computeImplicitSystemLHS(*m_previousStateReduced, + *m_currentStateReduced, + m_updateType); + return m_Keff; + }; + +#pragma warning(pop) +} + +NonLinearSystem<Matrixd>::UpdateFunctionType +ReducedStVK::getUpdateFunction() +{ + // Function to evaluate the nonlinear objective function given the current state + return [&, this](const Vectord& q, const bool fullyImplicit) -> void { + (fullyImplicit) ? this->updateBodyIntermediateStates(q, m_updateType) + : this->updateBodyStates(q, m_updateType); + }; +} + +NonLinearSystem<Matrixd>::UpdatePrevStateFunctionType +ReducedStVK::getUpdatePrevStateFunction() +{ + // Function to evaluate the nonlinear objective function given the current state + return [&, this]() -> void { this->updateBodyPreviousStates(); }; +} + +void +ReducedStVK::initializeEigenMatrixFromStdVector(const std::vector<double>& A, Matrixd& eigenMatrix) +{ + CHECK(eigenMatrix.rows() == eigenMatrix.cols()); + CHECK(A.size() == eigenMatrix.rows() * eigenMatrix.cols()); + + // A is column-major + for (size_t j = 0; j < eigenMatrix.cols(); ++j) + { + size_t offset = j * eigenMatrix.rows(); + for (size_t i = 0; i < eigenMatrix.rows(); ++i) + { + eigenMatrix(i, j) = A[i + offset]; + } + } +} + +Vectord& +ReducedStVK::getContactForce() +{ + return m_Fcontact; +} + +void +ReducedStVK::setFixedSizeTimeStepping() +{ + m_timeStepSizeType = TimeSteppingType::Fixed; + m_timeIntegrator->setTimestepSizeToDefault(); +} + +void +ReducedStVK::setTimeStep(const double timeStep) +{ + m_timeIntegrator->setTimestepSize(timeStep); +} + +double +ReducedStVK::getTimeStep() const +{ + return m_timeIntegrator->getTimestepSize(); +}; + +void +ReducedStVK::prolongate(const Vectord& uReduced, Vectord& u) const +{ + //\todo: check sizes + m_modalMatrix->AssembleVector(const_cast<double*>(uReduced.data()), u.data()); +} + +void +ReducedStVK::prolongate(kinematicState& uReduced, kinematicState& u) const +{ + this->prolongate(uReduced.getQ(), u.getQ()); + this->prolongate(uReduced.getQDot(), u.getQDot()); +} + +void +ReducedStVK::project(const Vectord& u, Vectord& uReduced) const +{ + m_modalMatrix->ProjectVector(const_cast<double*>(u.data()), uReduced.data()); +} + +void +ReducedStVK::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) +{ + // Setup graph connectivity + m_taskGraph->addEdge(source, m_solveNode); + m_taskGraph->addEdge(m_solveNode, sink); +} +} // namespace imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkReducedStVKBodyModel.h b/Source/DynamicalModels/ObjectModels/imstkReducedStVKBodyModel.h new file mode 100644 index 0000000000000000000000000000000000000000..b8f55955b2676b86608c8d7bf861c99eedc82a9a --- /dev/null +++ b/Source/DynamicalModels/ObjectModels/imstkReducedStVKBodyModel.h @@ -0,0 +1,369 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <memory> + +#include "imstkDynamicalModel.h" +#include "imstkNonLinearSystem.h" +#include "imstkLogger.h" + +namespace vega +{ +class VolumetricMesh; +class ReducedStVKForceModel; +class StVKReducedInternalForces; +class ModalMatrix; +} + +namespace imstk +{ +class InternalForceModel; +class SolverBase; +class TaskNode; +class TimeIntegrator; +class VegaMeshIO; + +struct ReducedStVKConfig +{ + std::string m_cubicPolynomialFilename; + std::string m_modesFileName; + // int r; + + double m_dampingMassCoefficient = 0.1; + double m_dampingStiffnessCoefficient = 0.01; + double m_dampingLaplacianCoefficient = 0.0; + double m_deformationCompliance = 1.0; + double m_gravity = 9.81; + + int m_numberOfThreads = 4; +}; + +class ReducedStVK : public DynamicalModel<FeDeformBodyState> +{ +using kinematicState = FeDeformBodyState; +// using Matrixd = Eigen::MatrixXd; +public: + /// + /// \brief Constructor + /// + ReducedStVK(); + /// + /// \brief Destructor + /// + ~ReducedStVK(); + + /// + /// \brief Configure the force model from external file + /// + void configure(const std::string& configFileName); + void configure(std::shared_ptr<ReducedStVKConfig> config = std::make_shared<ReducedStVKConfig>()); + + /// + /// \brief Initialize the deformable body model + /// + bool initialize() override; + + /// + /// \brief Set/Get internal force model + /// + void setForceModelConfiguration(std::shared_ptr<ReducedStVKConfig> config); + std::shared_ptr<ReducedStVKConfig> getForceModelConfiguration() const; + + /// + /// \brief Set/Get time integrator + /// + /// \note the return type is different from FEMDeformableBodyModel::setInternalForceModel + void setInternalForceModel(std::shared_ptr<vega::StVKReducedInternalForces> fm); + std::shared_ptr<imstk::InternalForceModel> getInternalForceModel() const; + + /// + /// \brief Set/Get time integrator + /// + void setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator); + std::shared_ptr<TimeIntegrator> getTimeIntegrator() const; + + /// + /// \brief Load the initial conditions of the deformable object + /// + void loadInitialStates(); + + /// + /// \brief Initialize the force model + /// + bool initializeForceModel(); + + /// + /// \brief Initialize the mass matrix from the mesh + /// + bool initializeMassMatrix(); + + /// + /// \brief Initialize the damping (combines structural and viscous damping) matrix + /// + bool initializeDampingMatrix(); + + /// + /// \brief Initialize the tangent stiffness matrix + /// + bool initializeTangentStiffness(); + + /// + /// \brief Initialize the gravity force + /// + bool initializeGravityForce(); + + /// + /// \brief Initialize explicit external forces + /// + bool initializeExplicitExternalForces(); + + /// + /// \brief Initialize the Eigen matrix with data inside vega sparse matrix + /// + static void initializeEigenMatrixFromStdVector(const std::vector<double>& v, + Matrixd& eigenMatrix); + + /// + /// \brief Compute the RHS of the resulting linear system + /// + void computeImplicitSystemRHS(kinematicState& prevState, + kinematicState& newState, + const StateUpdateType updateType); + + /// + /// \brief Compute the RHS of the resulting linear system using semi-implicit scheme + /// + void computeSemiImplicitSystemRHS(kinematicState& stateAtT, + kinematicState& newState, + const StateUpdateType updateType); + + /// + /// \brief Compute the LHS of the resulting linear system + /// + void computeImplicitSystemLHS(const kinematicState& prevState, + kinematicState& newState, + const StateUpdateType updateType); + + /// + /// \brief Update damping Matrix + /// + void updateDampingMatrix(); + + /// + /// \brief Update mass matrix + /// Note: Not supported yet! + /// + void updateMassMatrix(); + + /// + /// \brief Applies boundary conditions to matrix and a vector + /// + void applyBoundaryConditions(Matrixd& M, const bool withCompliance = false) const; + void applyBoundaryConditions(Vectord& x) const; + + /// + /// \brief Updates the Physics geometry + /// + void updatePhysicsGeometry() override; + + /// + /// \brief Update states + /// + void updateBodyStates(const Vectord& solution, const StateUpdateType updateType) override; + void updateBodyIntermediateStates(const Vectord& solution, const StateUpdateType updateType); + + /// + /// \brief Update the previous states given the current state + /// + void updateBodyPreviousStates(); + + /// + /// \brief Returns the "function" that evaluates the nonlinear function given + /// the state vector + /// + NonLinearSystem<Matrixd>::VectorFunctionType getFunction(); + + /// + /// \brief Get the function that updates the model given the solution + /// + NonLinearSystem<Matrixd>::UpdateFunctionType getUpdateFunction(); + NonLinearSystem<Matrixd>::UpdatePrevStateFunctionType getUpdatePrevStateFunction(); + + /// + /// \brief Returns the "function" that evaluates the gradient of the nonlinear + /// function given the state vector + /// + NonLinearSystem<Matrixd>::MatrixFunctionType getFunctionGradient(); + + /// + /// \brief Get the contact force vector + /// + Vectord& getContactForce(); + + /// + /// \brief Returns the unknown vectors + /// + Vectord& getUnknownVec() + { + return m_qSol; + } + + /// + /// \brief Set/Get the update type + /// + void setUpdateType(const StateUpdateType& updateType) + { + m_updateType = updateType; + } + + const StateUpdateType& getUpdateType() const + { + return m_updateType; + } + + /// \brief Returns the unknown vectors + /// + std::vector<std::size_t>& getFixNodeIds() + { + return m_fixedNodeIds; + } + + /// + /// \brief Set the time step size + /// + virtual void setTimeStep(const double timeStep); + + /// + /// \brief Returns the time step size + /// + virtual double getTimeStep() const; + + /// + /// \brief Set the time step size to fixed size + /// + void setFixedSizeTimeStepping(); + + /// + /// \brief Set the fixed BC implementation state + /// + void enableFixedBC() + { + LOG(WARNING) << "Boundary conditions are not allowed to change"; + m_implementFixedBC = true; + }; + void disableFixedBC() + { + // LOG(FATAL, "Boundary conditions are not allowed to change"); + m_implementFixedBC = false; + }; + bool isFixedBCImplemented() const + { + return m_implementFixedBC; + }; + + /// + /// \brief prolongate reduced vector into full space, ie, u = U * uReduced + /// + void prolongate(const Vectord& uReduced, Vectord& u) const; + + /// + /// \brief prolongate reduced state into full space + /// + void prolongate(kinematicState& uReduced, kinematicState& u) const; + + /// + /// \brief project full-space vector into reduced space, uReduced = U^T u + /// + void project(const Vectord& u, Vectord& uReduced) const; + /// + /// \brief Read in the basis file and create m_modalMatrix + /// \todo: make it private/protected? + /// + void readModalMatrix(const std::string& fname); + + std::shared_ptr<TaskNode> getSolveNode() const { return m_solveNode; } + + std::shared_ptr<SolverBase> getSolver() const { return m_solver; } + void setSolver(std::shared_ptr<SolverBase> solver) { this->m_solver = solver; } + +protected: + /// + /// \brief Setup the computational graph of FEM + /// + void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) override; + +protected: + std::shared_ptr<SolverBase> m_solver = nullptr; + std::shared_ptr<vega::StVKReducedInternalForces> m_internalForceModel; ///> Mathematical model for intenal forces + std::shared_ptr<vega::ReducedStVKForceModel> m_forceModel; + std::shared_ptr<TimeIntegrator> m_timeIntegrator; ///> Time integrator + std::shared_ptr<NonLinearSystem<Matrixd>> m_nonLinearSystem; ///> Nonlinear system resulting from TI and force model + std::shared_ptr<vega::ModalMatrix> m_modalMatrix; + + std::shared_ptr<ReducedStVKConfig> m_config; + + /// Matrices typical to a elastodynamics and 2nd order analogous systems + Matrixd m_M; ///> Mass matrix + Matrixd m_C; ///> Damping coefficient matrix + Matrixd m_K; ///> Tangent (derivative of internal force w.r.t displacements) stiffness matrix + Matrixd m_Keff; ///> Effective stiffness matrix (dependent on internal force model and time + /// integrator) + + // full-space vectors + Vectord m_Fcontact; ///> Vector of contact forces + Vectord m_Fgravity; ///> Vector of gravity forces + Vectord m_FexplicitExternal; ///> Vector of explicitly defined external forces + Vectord m_qSol; ///> Vector to maintain solution at each iteration of nonlinear solver + + // reduced-space vectors + Vectord m_Feff; ///> Vector of effective forces + Vectord m_Finternal; ///> Vector of internal forces + Vectord m_qSolReduced; ///> Vector to maintain solution at each iteration of nonlinear solver + Vectord m_FcontactReduced; + Vectord m_FgravityReduced; ///> Vector of gravity forces + Vectord m_FexplicitExternalReduced; ///> Vector of explicitly defined external forces + size_t m_numDOFReduced; + + std::shared_ptr<vega::VolumetricMesh> m_vegaPhysicsMesh; ///> Mesh used for Physics + std::vector<double> m_massMatrix; ///> Vega mass matrix + std::vector<double> m_stiffnessMatrix; ///> Vega Tangent stiffness matrix + std::vector<double> m_dampingMatrix; ///> Vega Laplacian damping matrix + + std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed + + StateUpdateType m_updateType = StateUpdateType::DeltaVelocity; ///> Update type of the model + + bool m_damped = false; ///> Viscous or structurally damped system + + // If this is true, the tangent stiffness and force vector will be modified to + // accommodate (the rows and columns will be nullified) the fixed boundary conditions + bool m_implementFixedBC = false; + + std::shared_ptr<kinematicState> m_initialStateReduced; + std::shared_ptr<kinematicState> m_previousStateReduced; + std::shared_ptr<kinematicState> m_currentStateReduced; + +private: + std::shared_ptr<TaskNode> m_solveNode = nullptr; +}; +} // namespace imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h b/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h index b9d377d7bd34a1ece03e01dfd423c71ada8bbdf4..7c9e020342cfe46e1bfbcbff817a80e89916bcd7 100644 --- a/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h @@ -19,8 +19,7 @@ =========================================================================*/ -#ifndef imstkRigidBodyModel_h -#define imstkRigidBodyModel_h +#pragma once #include "imstkDynamicalModel.h" #include "imstkRigidBodyState.h" @@ -175,6 +174,4 @@ private: void createCube(); void createMesh(); }; -} // imstk - -#endif \ No newline at end of file +} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp b/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp index ca91fa161179157371ec49e46d519426f85c2c32..1ff32db59d7db2d415b1ab2395e33ae7095ba13c 100644 --- a/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp @@ -1,27 +1,29 @@ /*========================================================================= - Library: iMSTK +Library: iMSTK - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0.txt +http://www.apache.org/licenses/LICENSE-2.0.txt - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. =========================================================================*/ #include "imstkSPHModel.h" +#include "imstkLogger.h" #include "imstkParallelUtils.h" #include "imstkPointSet.h" +#include "imstkTaskGraph.h" namespace imstk { @@ -54,6 +56,34 @@ SPHModelConfig::initialize() m_kernelRadiusSqr = m_kernelRadius * m_kernelRadius; } +SPHModel::SPHModel() : DynamicalModel<SPHKinematicState>(DynamicalModelType::SmoothedParticleHydrodynamics) +{ + m_validGeometryTypes = { Geometry::Type::PointSet }; + + m_findParticleNeighborsNode = m_taskGraph->addFunction("SPHModel_Partition", std::bind(&SPHModel::findParticleNeighbors, this)); + m_computeDensityNode = m_taskGraph->addFunction("SPHModel_ComputeDensity", [&]() + { + computeNeighborRelativePositions(); + computeDensity(); + normalizeDensity(); + collectNeighborDensity(); + }); + m_computePressureAccelNode = + m_taskGraph->addFunction("SPHModel_ComputePressureAccel", std::bind(&SPHModel::computePressureAcceleration, this)); + m_computeSurfaceTensionNode = + m_taskGraph->addFunction("SPHModel_ComputeSurfaceTensionAccel", std::bind(&SPHModel::computeSurfaceTension, this)); + m_computeTimeStepSizeNode = + m_taskGraph->addFunction("SPHModel_ComputeTimestep", std::bind(&SPHModel::computeTimeStepSize, this)); + m_integrateNode = + m_taskGraph->addFunction("SPHModel_Integrate", [&]() + { + sumAccels(); + updateVelocity(getTimeStep()); + computeViscosity(); + moveParticles(getTimeStep()); + }); +} + bool SPHModel::initialize() { @@ -80,7 +110,10 @@ SPHModel::initialize() // Initialize neighbor searcher m_neighborSearcher = std::make_shared<NeighborSearch>(m_modelParameters->m_NeighborSearchMethod, - m_modelParameters->m_kernelRadius); + m_modelParameters->m_kernelRadius); + + m_pressureAccels = std::make_shared<StdVectorOfVec3d>(getState().getNumParticles()); + m_surfaceTensionAccels = std::make_shared<StdVectorOfVec3d>(getState().getNumParticles()); return true; } @@ -93,19 +126,22 @@ SPHModel::updatePhysicsGeometry() } void -SPHModel::advanceTimeStep() +SPHModel::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) { - findParticleNeighbors(); - computeNeighborRelativePositions(); - computeDensity(); - normalizeDensity(); - collectNeighborDensity(); - computePressureAcceleration(); - computeSurfaceTension(); - computeTimeStepSize(); - updateVelocity(getTimeStep()); - computeViscosity(); - moveParticles(getTimeStep()); + // Setup graph connectivity + m_taskGraph->addEdge(source, m_findParticleNeighborsNode); + m_taskGraph->addEdge(m_findParticleNeighborsNode, m_computeDensityNode); + + // Pressure, Surface Tension, and time step size can be done in parallel + m_taskGraph->addEdge(m_computeDensityNode, m_computePressureAccelNode); + m_taskGraph->addEdge(m_computeDensityNode, m_computeSurfaceTensionNode); + m_taskGraph->addEdge(m_computeDensityNode, m_computeTimeStepSizeNode); + + m_taskGraph->addEdge(m_computePressureAccelNode, m_integrateNode); + m_taskGraph->addEdge(m_computeSurfaceTensionNode, m_integrateNode); + m_taskGraph->addEdge(m_computeTimeStepSizeNode, m_integrateNode); + + m_taskGraph->addEdge(m_integrateNode, sink); } void @@ -282,6 +318,7 @@ SPHModel::computePressureAcceleration() return error > Real(0) ? error : Real(0); }; + StdVectorOfVec3d& pressureAccels = *m_pressureAccels; ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { Vec3r accel(0, 0, 0); @@ -307,7 +344,19 @@ SPHModel::computePressureAcceleration() } accel *= m_modelParameters->m_pressureStiffness * m_modelParameters->m_particleMass; - getState().getAccelerations()[p] = accel; + //getState().getAccelerations()[p] = accel; + pressureAccels[p] = accel; + }); +} + +void +SPHModel::sumAccels() +{ + const StdVectorOfVec3d& pressureAccels = *m_pressureAccels; + const StdVectorOfVec3d& surfaceTensionAccels = *m_surfaceTensionAccels; + ParallelUtils::parallelFor(getState().getNumParticles(), + [&](const size_t p) { + getState().getAccelerations()[p] = pressureAccels[p] + surfaceTensionAccels[p]; }); } @@ -366,7 +415,7 @@ SPHModel::computeViscosity() ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { getState().getVelocities()[p] += getState().getDiffuseVelocities()[p]; - }); + }); } void @@ -396,6 +445,7 @@ SPHModel::computeSurfaceTension() }); // Second, compute surface tension acceleration + StdVectorOfVec3d& surfaceTensionAccels = *m_surfaceTensionAccels; ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { const auto& fluidNeighborList = getState().getFluidNeighborLists()[p]; @@ -436,7 +486,8 @@ SPHModel::computeSurfaceTension() } accel *= m_modelParameters->m_surfaceTensionStiffness; - getState().getAccelerations()[p] += accel; + //getState().getAccelerations()[p] += accel; + surfaceTensionAccels[p] = accel; }); } @@ -446,6 +497,6 @@ SPHModel::moveParticles(Real timestep) ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { getState().getPositions()[p] += getState().getVelocities()[p] * timestep; - }); + }); } } // end namespace imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkSPHModel.h b/Source/DynamicalModels/ObjectModels/imstkSPHModel.h index d5cedcf23b8a2eabe9996d56bbaaed58cc5bae2d..56895525b9f7a9fdfe3601b48e816a6d47e3d669 100644 --- a/Source/DynamicalModels/ObjectModels/imstkSPHModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkSPHModel.h @@ -28,6 +28,7 @@ namespace imstk { +class ComputeNode; class PointSet; /// @@ -92,10 +93,7 @@ public: /// /// \brief Constructor /// - SPHModel() : DynamicalModel<SPHKinematicState>(DynamicalModelType::SmoothedParticleHydrodynamics) - { - m_validGeometryTypes = { Geometry::Type::PointSet }; - } + SPHModel(); /// /// \brief Destructor @@ -175,10 +173,19 @@ public: virtual double getTimeStep() const override { return static_cast<double>(m_dt); } + std::shared_ptr<TaskNode> getFindParticleNeighborsNode() const { return m_findParticleNeighborsNode; } + std::shared_ptr<TaskNode> getComputeDensityNode() const { return m_computeDensityNode; } + std::shared_ptr<TaskNode> getComputePressureNode() const { return m_computePressureAccelNode; } + std::shared_ptr<TaskNode> getComputeSurfaceTensionNode() const { return m_computeSurfaceTensionNode; } + std::shared_ptr<TaskNode> getComputeTimeStepSizeNode() const { m_computeTimeStepSizeNode; } + std::shared_ptr<TaskNode> getSumAccelsNode() const { m_sumAccelsNode; } + std::shared_ptr<TaskNode> getIntegrateNode() const { m_integrateNode; } + +protected: /// - /// \brief Do one time step simulation + /// \brief Setup SPH compute graph connectivity /// - void advanceTimeStep(); + virtual void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) override; private: /// @@ -222,6 +229,11 @@ private: /// void computePressureAcceleration(); + /// + /// \brief Sum the forces computed in parallel + /// + void sumAccels(); + /// /// \brief Update particle velocities due to pressure /// @@ -244,6 +256,16 @@ private: /// void moveParticles(const Real timestep); +protected: + std::shared_ptr<TaskNode> m_findParticleNeighborsNode = nullptr; + std::shared_ptr<TaskNode> m_computeDensityNode = nullptr; + std::shared_ptr<TaskNode> m_computePressureAccelNode = nullptr; + std::shared_ptr<TaskNode> m_computeSurfaceTensionNode = nullptr; + std::shared_ptr<TaskNode> m_computeTimeStepSizeNode = nullptr; + std::shared_ptr<TaskNode> m_sumAccelsNode = nullptr; + std::shared_ptr<TaskNode> m_integrateNode = nullptr; + +private: std::shared_ptr<PointSet> m_pointSetGeometry; SPHSimulationState m_simulationState; @@ -253,5 +275,8 @@ private: SPHSimulationKernels m_kernels; ///> SPH kernels (must be initialized during model initialization) std::shared_ptr<SPHModelConfig> m_modelParameters; ///> SPH Model parameters (must be set before simulation) std::shared_ptr<NeighborSearch> m_neighborSearcher; ///> Neighbor Search (must be initialized during model initialization) + + std::shared_ptr<StdVectorOfVec3d> m_pressureAccels = nullptr; + std::shared_ptr<StdVectorOfVec3d> m_surfaceTensionAccels = nullptr; }; } // end namespace imstk diff --git a/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp b/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp index 4c06dc9f5a2b0df9df9bf4474a7e259c10f30f5a..13c84c079c919daf9bc67d76a1d167534e89e937 100644 --- a/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp +++ b/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp @@ -25,36 +25,25 @@ namespace imstk { void -PbdState::initialize(const size_t numNodes, const bool (& options)[3]) +PbdState::initialize(const size_t numNodes) { - // CHECKBACK : m_pos could actually not be another copy - if (options[0]) - { - m_pos.resize(numNodes, Vec3d(0, 0, 0)); - } - - if (options[1]) - { - m_vel.resize(numNodes, Vec3d(0, 0, 0)); - } - - if (options[2]) - { - m_acc.resize(numNodes, Vec3d(0, 0, 0)); - } + m_pos->resize(numNodes, Vec3d(0.0, 0.0, 0.0)); + m_vel->resize(numNodes, Vec3d(0.0, 0.0, 0.0)); + m_acc->resize(numNodes, Vec3d(0.0, 0.0, 0.0)); } void -PbdState::initialize(const std::shared_ptr<PointSet>& m, const bool (& options)[3]) +PbdState::initialize(const StdVectorOfVec3d& vertices) { - this->initialize(m->getNumVertices(), options); + this->initialize(vertices.size()); + setPositions(vertices); } void PbdState::setState(std::shared_ptr<PbdState> rhs) { - m_pos = rhs->getPositions(); - m_vel = rhs->getVelocities(); - m_acc = rhs->getAccelerations(); + *m_pos = *rhs->getPositions(); + *m_vel = *rhs->getVelocities(); + *m_acc = *rhs->getAccelerations(); } } // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectStates/imstkPbdState.h b/Source/DynamicalModels/ObjectStates/imstkPbdState.h index bcc2884edf0417daf9cf185002813c922f46a4e4..65f5019e57a89f52c6b0097059741b7a5bdf3223 100644 --- a/Source/DynamicalModels/ObjectStates/imstkPbdState.h +++ b/Source/DynamicalModels/ObjectStates/imstkPbdState.h @@ -43,30 +43,40 @@ public: /// /// \brief Initialize the pbd state /// - void initialize(const size_t numNodes, const bool (& options)[3]); - void initialize(const std::shared_ptr<PointSet>& m, const bool (& options)[3]); + void initialize(const size_t numNodes); + void initialize(const StdVectorOfVec3d& vertices); /// /// \brief Get/Set nodal position given the index /// - void setVertexPosition(const size_t& idx, const Vec3d& pos) { m_pos.at(idx) = pos; } - Vec3d& getVertexPosition(const size_t& idx) { return m_pos.at(idx); } + void setVertexPosition(const size_t& idx, const Vec3d& pos) { m_pos->at(idx) = pos; } + Vec3d& getVertexPosition(const size_t& idx) { return m_pos->at(idx); } /// /// \brief Returns the vector of current nodal positions /// - StdVectorOfVec3d& getPositions() { return m_pos; } - void setPositions(const StdVectorOfVec3d& p) { m_pos = p; } + std::shared_ptr<StdVectorOfVec3d> getPositions() { return m_pos; } + void setPositions(std::shared_ptr<StdVectorOfVec3d> p) + { + m_pos->resize(p->size()); + std::copy(p->begin(), p->end(), m_pos->begin()); + } + + void setPositions(const StdVectorOfVec3d& p) + { + m_pos->resize(p.size()); + std::copy(p.begin(), p.end(), m_pos->begin()); + } /// /// \brief Returns the vector of current nodal velocities /// - StdVectorOfVec3d& getVelocities() { return m_vel; } + std::shared_ptr<StdVectorOfVec3d> getVelocities() { return m_vel; } /// /// \brief Returns the vector of current nodal accelerations /// - StdVectorOfVec3d& getAccelerations() { return m_acc; } + std::shared_ptr<StdVectorOfVec3d> getAccelerations() { return m_acc; } /// /// \brief Set the state to a given one @@ -74,8 +84,8 @@ public: void setState(std::shared_ptr<PbdState> rhs); private: - StdVectorOfVec3d m_pos; ///> Nodal positions - StdVectorOfVec3d m_vel; ///> Nodal velocities - StdVectorOfVec3d m_acc; ///> Nodal acelerations + std::shared_ptr<StdVectorOfVec3d> m_pos = std::make_shared<StdVectorOfVec3d>(); ///> Nodal positions + std::shared_ptr<StdVectorOfVec3d> m_vel = std::make_shared<StdVectorOfVec3d>(); ///> Nodal velocities + std::shared_ptr<StdVectorOfVec3d> m_acc = std::make_shared<StdVectorOfVec3d>(); ///> Nodal acelerations }; } // imstk diff --git a/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h b/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h index 2f0764a6d07e0a0016081a0b14f60138a38db3ea..b67ce30c98cce95d020b5a7b7080949d4f3efad8 100644 --- a/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h +++ b/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h @@ -19,8 +19,7 @@ =========================================================================*/ -#ifndef imstkRigidBodyState_h -#define imstkRigidBodyState_h +#pragma once #include <Eigen/Dense> #include <memory> @@ -77,6 +76,4 @@ private: //Can add linear velocity and angular velocity too. }; -} // imstk - -#endif // imstkPbdState_h \ No newline at end of file +} // imstk \ No newline at end of file diff --git a/Source/Geometry/Mesh/imstkTetrahedralMesh.h b/Source/Geometry/Mesh/imstkTetrahedralMesh.h index b4881a889acec38508f97390f464c720611e6ebd..0324193bddb08b788bcfb24b495d6abfc8c2df74 100644 --- a/Source/Geometry/Mesh/imstkTetrahedralMesh.h +++ b/Source/Geometry/Mesh/imstkTetrahedralMesh.h @@ -21,8 +21,8 @@ #pragma once -// imstk #include "imstkVolumetricMesh.h" +#include <array> namespace imstk { diff --git a/Source/MeshIO/imstkVegaMeshIO.cpp b/Source/MeshIO/imstkVegaMeshIO.cpp index 77bdb4dff607042008450b584100e51ad31b085f..98a61e6f3e72dc0847740115163438cdfd751bc9 100644 --- a/Source/MeshIO/imstkVegaMeshIO.cpp +++ b/Source/MeshIO/imstkVegaMeshIO.cpp @@ -159,8 +159,8 @@ VegaMeshIO::convertVolumetricMeshToVegaMesh(const std::shared_ptr<imstk::Volumet if (imstkVolMesh->getType() == Geometry::Type::TetrahedralMesh) { // Using default material properties to append to the .veg file - const double E = 1E6; - const double nu = 0.45; + const double E = 1E7; + const double nu = 0.4; const double density = 1000.0; auto imstkVolTetMesh = std::dynamic_pointer_cast<imstk::TetrahedralMesh>(imstkVolMesh); diff --git a/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp b/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp index 64ea662dde19fcdfd0e30cd51a201a7463e5721d..4b1fa596e0c3d8db22c34216cac857ab7080fc06 100644 --- a/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp +++ b/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp @@ -24,7 +24,6 @@ #include "imstkScene.h" #include "imstkSceneObject.h" #include "imstkCamera.h" -#include "imstkCollisionGraph.h" #include "imstkVTKRenderDelegate.h" #include "imstkVTKSurfaceMeshRenderDelegate.h" #include "imstkLight.h" @@ -38,6 +37,18 @@ #include <vtkProp.h> #include <vtkLight.h> #include <vtkRenderer.h> +#include <vtkRenderWindow.h> + +#include <vtkAxis.h> +#include <vtkChartXY.h> +#include <vtkContextActor.h> +#include <vtkContextScene.h> +#include <vtkDoubleArray.h> +#include <vtkIntArray.h> +#include <vtkPlotBar.h> +#include <vtkStringArray.h> +#include <vtkTable.h> +#include <vtkTextProperty.h> namespace imstk { @@ -171,7 +182,6 @@ VTKRenderer::VTKRenderer(std::shared_ptr<Scene> scene, const bool enableVR) : m_ m_defaultVtkCamera = vtkSmartPointer<vtkOpenVRCamera>::New(); m_vtkRenderer->SetActiveCamera(m_defaultVtkCamera); } - #endif ///TODO : based on scene properties // Customize background colors @@ -231,6 +241,49 @@ VTKRenderer::VTKRenderer(std::shared_ptr<Scene> scene, const bool enableVR) : m_ m_camPos[1].Translation[2] = 0.0; } #endif + + { + // Add the benchmarking chart + m_timeTableChart = vtkSmartPointer<vtkChartXY>::New(); + vtkSmartPointer<vtkContextScene> m_benchmarkChartScene = vtkSmartPointer<vtkContextScene>::New(); + m_timeTableChartActor = vtkSmartPointer<vtkContextActor>::New(); + m_vtkRenderer->AddActor(m_timeTableChartActor); + m_benchmarkChartScene->SetRenderer(m_vtkRenderer); + + m_timeTableChart->SetAutoSize(true); + m_timeTableChart->SetSize(vtkRectf(0.0, 0.0, 600.0, 600.0)); + + m_benchmarkChartScene->AddItem(m_timeTableChart); + m_timeTableChartActor->SetScene(m_benchmarkChartScene); + m_timeTableChartActor->SetVisibility(false); + + m_timeTablePlot = vtkPlotBar::SafeDownCast(m_timeTableChart->AddPlot(vtkChart::BAR)); + m_timeTablePlot->SetColor(0.6, 0.1, 0.1); + m_timeTablePlot->SetOrientation(vtkPlotBar::HORIZONTAL); + m_timeTableChart->GetAxis(vtkAxis::BOTTOM)->SetTitle("ms"); + m_timeTableChart->GetAxis(vtkAxis::LEFT)->SetTitle(""); + m_timeTableChart->GetAxis(vtkAxis::LEFT)->GetLabelProperties()->SetVerticalJustification(VTK_TEXT_CENTERED); + m_timeTableChart->GetAxis(vtkAxis::LEFT)->GetLabelProperties()->SetJustification(VTK_TEXT_RIGHT); + + m_timeTable = vtkSmartPointer<vtkTable>::New(); + vtkSmartPointer<vtkDoubleArray> xIndices = vtkSmartPointer<vtkDoubleArray>::New(); + xIndices->SetName("Indices"); + xIndices->SetNumberOfValues(0); + vtkSmartPointer<vtkDoubleArray> yElapsedTimes = vtkSmartPointer<vtkDoubleArray>::New(); + yElapsedTimes->SetName("Elapsed Times"); + yElapsedTimes->SetNumberOfValues(0); + vtkSmartPointer<vtkStringArray> labels = vtkSmartPointer<vtkStringArray>::New(); + labels->SetName("Labels"); + labels->SetNumberOfValues(0); + m_timeTable->AddColumn(xIndices); + m_timeTable->AddColumn(yElapsedTimes); + m_timeTable->AddColumn(labels); + m_timeTablePlot->SetInputData(m_timeTable, 0, 1); + + vtkAxis* axisY = m_timeTableChart->GetAxis(vtkAxis::LEFT); + //axisY->SetRange(xIndices->GetRange()); + axisY->SetCustomTickPositions(xIndices, labels); + } } vtkSmartPointer<vtkRenderer> @@ -360,15 +413,83 @@ VTKRenderer::setAxesVisibility(const bool visible) m_AxesActor->SetVisibility(visible); } -/// -/// \brief Returns whether the debug axes is visible or not -/// bool VTKRenderer::getAxesVisibility() const { return m_AxesActor->GetVisibility(); } +void +VTKRenderer::setTimeTable(const std::unordered_map<std::string, double>& timeTable) +{ + // Sort by elapsed times + std::vector<std::pair<std::string, double>> nameToTimesVec(timeTable.begin(), timeTable.end()); + std::sort(nameToTimesVec.begin(), nameToTimesVec.end(), + [](const std::pair<std::string, double>& a, const std::pair<std::string, double>& b) { return a.second < b.second; }); + + // Construct vtkTable from provided data + vtkSmartPointer<vtkDoubleArray> xIndices = vtkDoubleArray::SafeDownCast(m_timeTable->GetColumn(0)); + vtkSmartPointer<vtkDoubleArray> yElapsedTimes = vtkDoubleArray::SafeDownCast(m_timeTable->GetColumn(1)); + vtkSmartPointer<vtkStringArray> labels = vtkStringArray::SafeDownCast(m_timeTable->GetColumn(2)); + + labels->SetNumberOfValues(nameToTimesVec.size()); + xIndices->SetNumberOfValues(nameToTimesVec.size()); + yElapsedTimes->SetNumberOfValues(nameToTimesVec.size()); + for (size_t i = 0; i < nameToTimesVec.size(); i++) + { + labels->SetValue(i, nameToTimesVec[i].first.c_str()); + xIndices->SetValue(i, i + 1); + yElapsedTimes->SetValue(i, nameToTimesVec[i].second); + } + + // The range for the x axis is based on history of the elapsed times + vtkAxis* botAxis = m_timeTableChart->GetAxis(vtkAxis::BOTTOM); + + // Get the previous and current range + double newMaxElapsed = yElapsedTimes->GetRange()[1]; + yElapsedTimes->Modified(); + double currMaxElapsed = botAxis->GetMaximum(); + + // Always respect the max as all information should be shown + if (newMaxElapsed > currMaxElapsed) + { + botAxis->SetRange(0.0, newMaxElapsed); + } + // But if current elapsed is less than the existing one we can lag + else + { + // Lag downscaling by 400 iterations + if (m_timeTableIter % 400 == 0) + { + botAxis->SetRange(0.0, newMaxElapsed); + } + else + { + botAxis->SetRange(0.0, currMaxElapsed); + } + m_timeTableIter++; + } + botAxis->Modified(); + + vtkAxis* leftAxis = m_timeTableChart->GetAxis(vtkAxis::LEFT); + leftAxis->SetRange(xIndices->GetRange()); + leftAxis->SetCustomTickPositions(xIndices, labels); + + m_timeTable->Modified(); +} + +void +VTKRenderer::setTimeTableVisibility(const bool visible) +{ + m_timeTableChartActor->SetVisibility(visible); +} + +bool +VTKRenderer::getTimeTableVisibility() const +{ + return m_timeTableChartActor->GetVisibility(); +} + void VTKRenderer::updateSceneCamera(std::shared_ptr<Camera> imstkCam) { diff --git a/Source/Rendering/VTKRenderer/imstkVTKRenderer.h b/Source/Rendering/VTKRenderer/imstkVTKRenderer.h index 765e1be2f2f323a22839ba7f6533c523b62e8e36..516920cfa84f14eed6b631058620c198748b9c55 100644 --- a/Source/Rendering/VTKRenderer/imstkVTKRenderer.h +++ b/Source/Rendering/VTKRenderer/imstkVTKRenderer.h @@ -35,11 +35,17 @@ #include <vtkOpenVROverlayInternal.h> #endif +#include <unordered_map> + class vtkAxesActor; class vtkCamera; -class vtkProp; +class vtkChartXY; +class vtkContextActor; class vtkLight; +class vtkPlotBar; +class vtkProp; class vtkRenderer; +class vtkTable; namespace imstk { @@ -93,6 +99,21 @@ public: /// bool getAxesVisibility() const; + /// + /// \brief Sets the benchmarking table using unordered_map + /// + void setTimeTable(const std::unordered_map<std::string, double>& timeTable); + + /// + /// \brief Set the visibility of the benchmark graph + /// + void setTimeTableVisibility(const bool visible); + + /// + /// \brief Get the visibility of the benchmark graph + /// + bool getTimeTableVisibility() const; + /// /// \brief Updates the scene camera's position and orientation /// @@ -129,6 +150,7 @@ protected: /// void addActors(const std::vector<vtkSmartPointer<vtkProp>>& actorList); +protected: vtkSmartPointer<vtkRenderer> m_vtkRenderer; // cameras @@ -154,5 +176,12 @@ protected: #ifdef iMSTK_ENABLE_VR std::vector<vtkOpenVRCameraPose> m_camPos; #endif + + vtkSmartPointer<vtkChartXY> m_timeTableChart = nullptr; + vtkSmartPointer<vtkContextActor> m_timeTableChartActor = nullptr; + vtkSmartPointer<vtkTable> m_timeTable = nullptr; + + vtkPlotBar* m_timeTablePlot = nullptr; + int m_timeTableIter = 0; }; } diff --git a/Source/Scene/imstkCollisionGraph.cpp b/Source/Scene/imstkCollisionGraph.cpp index afd290de55049a70ae28f8f4afcf7de79bf321e7..6805e2faa8b2f64aa9d9dab2681bdf7f4978a515 100644 --- a/Source/Scene/imstkCollisionGraph.cpp +++ b/Source/Scene/imstkCollisionGraph.cpp @@ -20,168 +20,160 @@ =========================================================================*/ #include "imstkCollisionGraph.h" - -#include <g3log/g3log.hpp> +#include "imstkLogger.h" +#include "imstkObjectInteractionPair.h" +#include "imstkSceneObject.h" namespace imstk { -std::shared_ptr<InteractionPair> -CollisionGraph::addInteractionPair(CollidingObjectPtr A, - CollidingObjectPtr B, - CollisionDetection::Type CDType, - CollisionHandling::Type CHAType, - CollisionHandling::Type CHBType) -{ - // Check that interaction pair does not exist - if (this->getInteractionPair(A, B) != nullptr) - { - LOG(WARNING) << "CollisionGraph::addInteractionPair error: interaction already defined for " - << A->getName() << " & " << B->getName() << "."; - return nullptr; - } - - // Create interaction pair - auto intPair = std::make_shared<InteractionPair>(A, B, CDType, CHAType, CHBType); - - // Check validity - if (!intPair->isValid()) - { - LOG(WARNING) << "CollisionGraph::addInteractionPair error: could not create interaction for " - << A->getName() << " & " << B->getName() << " with those parameters."; - intPair.reset(); - return nullptr; - } - - // Populate book-keeping - m_interactionPairList.push_back(intPair); - m_interactionPairMap[A].push_back(intPair); - m_interactionPairMap[B].push_back(intPair); - - // Return interaction pair - return intPair; -} +//CollisionGraph::ObjectInteractionPtr +//CollisionGraph::addInteractionPair(SceneObjectPtr A, +// SceneObjectPtr B, +// CollisionDetection::Type CDType, +// CollisionHandling::Type CHAType, +// CollisionHandling::Type CHBType) +//{ +// // Check that interaction pair does not exist +// //if (this->getInteractionPair(A, B) != nullptr) +// //{ +// // LOG(WARNING) << "CollisionGraph::addInteractionPair error: interaction already defined for " +// // << A->getName() << " & " << B->getName() << "."; +// // return nullptr; +// //} +// +// //// Create interaction pair +// //auto intPair = std::make_shared<CollisionPair>(A, B, CDType, CHAType, CHBType); +// +// //// Check validity +// ///*if (!intPair->isValid()) +// //{ +// // LOG(WARNING) << "CollisionGraph::addInteractionPair error: could not create interaction for " +// // << A->getName() << " & " << B->getName() << " with those parameters."; +// // intPair.reset(); +// // return nullptr; +// //}*/ +// +// //// Populate book-keeping +// //m_interactionPairs.push_back(intPair); +// //m_interactionPairMap[A].push_back(intPair); +// //m_interactionPairMap[B].push_back(intPair); +// +// //// Return interaction pair +// //return intPair; +// return nullptr; +//} +// +//CollisionGraph::ObjectInteractionPtr +//CollisionGraph::addInteractionPair(SceneObjectPtr A, +// SceneObjectPtr B, +// CollisionDetectionPtr CD, +// CollisionHandlingPtr CHA, +// CollisionHandlingPtr CHB) +//{ +// //// Check that interaction pair does not exist +// //if (this->getInteractionPair(A, B) != nullptr) +// //{ +// // LOG(WARNING) << "CollisionGraph::addInteractionPair error: interaction already defined for " +// // << A->getName() << " & " << B->getName() << "."; +// // return nullptr; +// //} +// +// //// Create interaction pair +// //auto intPair = std::make_shared<CollisionPair>(A, B, CD, CHA, CHB); +// +// //// Check validity +// ///*if (!intPair->isValid()) +// //{ +// // LOG(WARNING) << "CollisionGraph::addInteractionPair error: could not create interaction for " +// // << A->getName() << " & " << B->getName() << " with those parameters."; +// // intPair.reset(); +// // return nullptr; +// //}*/ +// +// //// Populate book-keeping +// //m_interactionPairs.push_back(intPair); +// //m_interactionPairMap[A].push_back(intPair); +// //m_interactionPairMap[B].push_back(intPair); +// +// //// Return interaction pair +// //return intPair; +// return nullptr; +//} -//\todo Refactor -> PBD only void -CollisionGraph::addInteractionPair(std::shared_ptr<PbdInteractionPair> pair) +CollisionGraph::addInteraction(ObjectInteractionPtr pair) { - m_interactionPbdPairList.push_back(pair); -} - -std::shared_ptr<InteractionPair> -CollisionGraph::addInteractionPair(CollidingObjectPtr A, - CollidingObjectPtr B, - CollisionDetectionPtr CD, - CollisionHandlingPtr CHA, - CollisionHandlingPtr CHB) -{ - // Check that interaction pair does not exist - if (this->getInteractionPair(A, B) != nullptr) - { - LOG(WARNING) << "CollisionGraph::addInteractionPair error: interaction already defined for " - << A->getName() << " & " << B->getName() << "."; - return nullptr; - } - - // Create interaction pair - auto intPair = std::make_shared<InteractionPair>(A, B, CD, CHA, CHB); - - // Check validity - if (!intPair->isValid()) - { - LOG(WARNING) << "CollisionGraph::addInteractionPair error: could not create interaction for " - << A->getName() << " & " << B->getName() << " with those parameters."; - intPair.reset(); - return nullptr; - } - - // Populate book-keeping - m_interactionPairList.push_back(intPair); - m_interactionPairMap[A].push_back(intPair); - m_interactionPairMap[B].push_back(intPair); - - // Return interaction pair - return intPair; + m_interactionPairs.push_back(pair); + m_interactionPairMap[pair->getObjectsPair().first].push_back(pair); + m_interactionPairMap[pair->getObjectsPair().second].push_back(pair); } bool -CollisionGraph::removeInteractionPair(CollidingObjectPtr A, CollidingObjectPtr B) +CollisionGraph::removeInteractionPair(SceneObjectPtr A, SceneObjectPtr B) { - std::shared_ptr<InteractionPair> intPair = this->getInteractionPair(A, B); - - // Check that interaction pair exists - if (intPair == nullptr) - { - LOG(WARNING) << "CollisionGraph::removeInteractionPair error: no such pair for objects " - << A->getName() << " & " << B->getName() << "."; - return false; - } - - // Remove interaction pair from list - auto it = std::remove(m_interactionPairList.begin(), - m_interactionPairList.end(), - intPair); - m_interactionPairList.erase(it, m_interactionPairList.end()); - - // Remove interaction pair from maps - it = std::remove(m_interactionPairMap.at(A).begin(), - m_interactionPairMap.at(A).end(), - intPair); - m_interactionPairMap.at(A).erase(it, m_interactionPairMap.at(A).end()); - - it = std::remove(m_interactionPairMap.at(B).begin(), - m_interactionPairMap.at(B).end(), - intPair); - m_interactionPairMap.at(B).erase(it, m_interactionPairMap.at(B).end()); - - // If no more interactions for objects, remove objects from list - if (m_interactionPairMap.at(A).empty()) - { - m_interactionPairMap.erase(A); - } - if (m_interactionPairMap.at(B).empty()) - { - m_interactionPairMap.erase(B); - } + //std::shared_ptr<InteractionPair> intPair = this->getInteractionPair(A, B); + + //// Check that interaction pair exists + //if (intPair == nullptr) + //{ + // LOG(WARNING) << "CollisionGraph::removeInteractionPair error: no such pair for objects " + // << A->getName() << " & " << B->getName() << "."; + // return false; + //} + + //// Remove interaction pair from list + //auto it = std::remove(m_interactionPairs.begin(), + // m_interactionPairs.end(), + // intPair); + //m_interactionPairs.erase(it, m_interactionPairs.end()); + + //// Remove interaction pair from maps + //it = std::remove(m_interactionPairMap.at(A).begin(), + // m_interactionPairMap.at(A).end(), + // intPair); + //m_interactionPairMap.at(A).erase(it, m_interactionPairMap.at(A).end()); + + //it = std::remove(m_interactionPairMap.at(B).begin(), + // m_interactionPairMap.at(B).end(), + // intPair); + //m_interactionPairMap.at(B).erase(it, m_interactionPairMap.at(B).end()); + + //// If no more interactions for objects, remove objects from list + //if (m_interactionPairMap.at(A).empty()) + //{ + // m_interactionPairMap.erase(A); + //} + //if (m_interactionPairMap.at(B).empty()) + //{ + // m_interactionPairMap.erase(B); + //} return true; } bool -CollisionGraph::removeInteractionPair(InteractionPairPtr intPair) -{ - return this->removeInteractionPair(intPair->getObjectsPair().first, - intPair->getObjectsPair().second); -} - -const std::vector<std::shared_ptr<PbdInteractionPair>>& -CollisionGraph::getPbdPairList() const +CollisionGraph::removeInteractionPair(ObjectInteractionPtr intPair) { - return m_interactionPbdPairList; + /* std::shared_ptr<CollidingObject> obj1 = std::static_pointer_cast<CollidingObject>(intPair->getObjectsPair().first); + std::shared_ptr<CollidingObject> obj2 = std::static_pointer_cast<CollidingObject>(intPair->getObjectsPair().second); + return this->removeInteractionPair(obj1, obj2);*/ + return true; } -std::shared_ptr<InteractionPair> -CollisionGraph::getInteractionPair(CollidingObjectPtr A, CollidingObjectPtr B) +CollisionGraph::ObjectInteractionPtr +CollisionGraph::getInteractionPair(SceneObjectPtr A, SceneObjectPtr B) { - for (const auto& intPair : m_interactionPairList) + /*for (const auto& intPair : m_interactionPairs) { - if (intPair->getObjectsPair() == std::pair<CollidingObjectPtr, CollidingObjectPtr>(A, B) - /*|| intPair->getObjectsPair() == std::pair<CollidingObjectPtr, CollidingObjectPtr>(B, A)*/ - ) + if (intPair->getObjectsPair() == std::pair<std::shared_ptr<SceneObject>, std::shared_ptr<SceneObject>>(A, B)) { return intPair; } - } + }*/ return nullptr; } -const std::vector<std::shared_ptr<InteractionPair>>& -CollisionGraph::getInteractionPairList() const -{ - return m_interactionPairList; -} - -const std::unordered_map<std::shared_ptr<CollidingObject>, std::vector<std::shared_ptr<InteractionPair>>>& +const std::unordered_map<CollisionGraph::SceneObjectPtr, std::vector<CollisionGraph::ObjectInteractionPtr>>& CollisionGraph::getInteractionPairMap() const { return m_interactionPairMap; diff --git a/Source/Scene/imstkCollisionGraph.h b/Source/Scene/imstkCollisionGraph.h index 56668270a540f102407224b91f59fca06038a460..a547067b0cacd57181f4b5559789ed1560bc3354 100644 --- a/Source/Scene/imstkCollisionGraph.h +++ b/Source/Scene/imstkCollisionGraph.h @@ -21,31 +21,25 @@ #pragma once -#include <vector> +#include <memory> #include <unordered_map> - -// imstk -#include "imstkCollidingObject.h" -#include "imstkInteractionPair.h" -#include "imstkCollisionDetection.h" -#include "imstkCollisionHandling.h" - -#include "imstkPbdInteractionPair.h" +#include <vector> namespace imstk { +class SceneObject; +class ObjectInteractionPair; + /// /// \class CollisionGraph /// -/// \brief +/// \brief The CollisionGraph holds a set of interacting pairs of SceneObject's /// class CollisionGraph { public: - using CollidingObjectPtr = std::shared_ptr<CollidingObject>; - using CollisionHandlingPtr = std::shared_ptr<CollisionHandling>; - using CollisionDetectionPtr = std::shared_ptr<CollisionDetection>; - using InteractionPairPtr = std::shared_ptr<InteractionPair>; + using SceneObjectPtr = std::shared_ptr<SceneObject>; + using ObjectInteractionPtr = std::shared_ptr<ObjectInteractionPair>; /// /// \brief Default constructor @@ -57,51 +51,35 @@ public: /// ~CollisionGraph() = default; +public: /// - /// \brief Add interaction pair in collision graph + /// \brief Add an interaction pair to the graph /// - InteractionPairPtr addInteractionPair(CollidingObjectPtr A, - CollidingObjectPtr B, - CollisionDetection::Type CDType, - CollisionHandling::Type CHAType, - CollisionHandling::Type CHBType); - - //\todo Refactor -> PBD only - InteractionPairPtr addInteractionPair(CollidingObjectPtr A, - CollidingObjectPtr B, - CollisionDetectionPtr CD, - CollisionHandlingPtr CHA, - CollisionHandlingPtr CHB); - - void addInteractionPair(std::shared_ptr<PbdInteractionPair> pair); + void addInteraction(ObjectInteractionPtr pair); /// /// \brief Remove interaction pair in collision graph /// - bool removeInteractionPair(CollidingObjectPtr A, CollidingObjectPtr B); - bool removeInteractionPair(InteractionPairPtr intPair); + bool removeInteractionPair(SceneObjectPtr A, SceneObjectPtr B); + bool removeInteractionPair(ObjectInteractionPtr intPair); /// /// \brief Returns the interaction pair if it exists /// - InteractionPairPtr getInteractionPair(CollidingObjectPtr A, CollidingObjectPtr B); + ObjectInteractionPtr getInteractionPair(SceneObjectPtr A, SceneObjectPtr B); /// - /// \brief Returns a vector of all interaction pairs in the collision graph + /// \brief Returns all interaction pairs /// - const std::vector<InteractionPairPtr>& getInteractionPairList() const; - - const std::vector<std::shared_ptr<PbdInteractionPair>>& getPbdPairList() const; + const std::vector<ObjectInteractionPtr>& getInteractionPairs() const { return m_interactionPairs; } /// /// \brief Returns a map of all interaction pairs per object /// - const std::unordered_map<CollidingObjectPtr, std::vector<InteractionPairPtr>>& getInteractionPairMap() const; + const std::unordered_map<SceneObjectPtr, std::vector<ObjectInteractionPtr>>& getInteractionPairMap() const; protected: - std::vector<std::shared_ptr<PbdInteractionPair>> m_interactionPbdPairList; //\todo Refactor -> PBD only - - std::vector<InteractionPairPtr> m_interactionPairList; ///< All interaction pairs in the collision graph - std::unordered_map<CollidingObjectPtr, std::vector<InteractionPairPtr>> m_interactionPairMap; ///< Map of interaction pairs per colliding object + std::vector<ObjectInteractionPtr> m_interactionPairs; ///< All interaction pairs in the collision graph + std::unordered_map<SceneObjectPtr, std::vector<ObjectInteractionPtr>> m_interactionPairMap; ///< Map of interaction pairs per colliding object }; } diff --git a/Source/Scene/imstkCollisionPair.cpp b/Source/Scene/imstkCollisionPair.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae296795d07cf8366e02c4d56b8fea12adc381e9 --- /dev/null +++ b/Source/Scene/imstkCollisionPair.cpp @@ -0,0 +1,188 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkCollisionPair.h" +#include "imstkCollidingObject.h" +#include "imstkCollisionData.h" +#include "imstkDynamicObject.h" +#include "imstkInteractionPair.h" +#include "imstkLogger.h" +#include "imstkTaskGraph.h" + +namespace imstk +{ +CollisionPair::CollisionPair(std::shared_ptr<CollidingObject> objA, + std::shared_ptr<CollidingObject> objB) : ObjectInteractionPair(objA, objB) +{ +} + +CollisionPair::CollisionPair(std::shared_ptr<CollidingObject> objA, + std::shared_ptr<CollidingObject> objB, + std::shared_ptr<CollisionDetection> cd, + std::shared_ptr<CollisionHandling> chA, + std::shared_ptr<CollisionHandling> chB) : ObjectInteractionPair(objA, objB) +{ + setCollisionDetection(cd); + + if (chA != nullptr) + { + setCollisionHandlingA(chA); + } + if (chB != nullptr) + { + setCollisionHandlingB(chB); + } +} + +CollisionPair::CollisionPair(std::shared_ptr<CollidingObject> objA, std::shared_ptr<CollidingObject> objB, + std::shared_ptr<CollisionDetection> cd, + std::shared_ptr<CollisionHandling> chAB) : ObjectInteractionPair(objA, objB) +{ + setCollisionDetection(cd); + + if (chAB != nullptr) + { + setCollisionHandlingAB(chAB); + } +} + +void +CollisionPair::setCollisionDetection(std::shared_ptr<CollisionDetection> colDetect) +{ + m_colDetect = colDetect; + m_collisionDetectionNode = m_interactionFunction = m_colDetect->getTaskNode(); + m_collisionDetectionNode->m_name = getObjectsPair().first->getName() + "_" + getObjectsPair().second->getName() + "_CollisionDetection"; + m_colData = m_colDetect->getCollisionData(); +} + +void +CollisionPair::setCollisionHandlingA(std::shared_ptr<CollisionHandling> colHandlingA) +{ + m_colHandlingA = colHandlingA; + m_collisionHandleANode = m_colHandlingA->getTaskNode(); + m_collisionHandleANode->m_name = getObjectsPair().first->getName() + "_CollisionHandling"; +} + +void +CollisionPair::setCollisionHandlingB(std::shared_ptr<CollisionHandling> colHandlingB) +{ + m_colHandlingB = colHandlingB; + m_collisionHandleBNode = m_colHandlingB->getTaskNode(); + m_collisionHandleBNode->m_name = getObjectsPair().second->getName() + "_CollisionHandling"; +} + +void +CollisionPair::setCollisionHandlingAB(std::shared_ptr<CollisionHandling> colHandlingAB) +{ + m_colHandlingA = m_colHandlingB = colHandlingAB; + m_collisionHandleANode = m_collisionHandleBNode = colHandlingAB->getTaskNode(); + m_collisionHandleANode->m_name = getObjectsPair().first->getName() + '_' + getObjectsPair().second->getName() + "_CollisionHandling"; +} + +void +CollisionPair::apply() +{ + std::shared_ptr<TaskGraph> computeGraphA = m_objects.first->getTaskGraph(); + std::shared_ptr<TaskGraph> computeGraphB = m_objects.second->getTaskGraph(); + + // If nothing was added to the input/output list use default collision location + if ((m_taskNodeInputs.first.size() == 0) && (m_taskNodeInputs.second.size() == 0) + && (m_taskNodeOutputs.first.size() == 0) && (m_taskNodeOutputs.second.size() == 0)) + { + m_taskNodeInputs.first.clear(); + m_taskNodeInputs.second.clear(); + m_taskNodeOutputs.first.clear(); + m_taskNodeOutputs.second.clear(); + + // Default location is inbetween the source->updateNode + m_taskNodeInputs.first.push_back(computeGraphA->getSource()); + m_taskNodeInputs.second.push_back(computeGraphB->getSource()); + + m_taskNodeOutputs.first.push_back(m_objects.first->getUpdateNode()); + m_taskNodeOutputs.second.push_back(m_objects.second->getUpdateNode()); + } + + // Add all the nodes to the graph + computeGraphA->addNode(m_collisionDetectionNode); + computeGraphB->addNode(m_collisionDetectionNode); + if (m_collisionHandleANode != nullptr) + { + computeGraphA->addNode(m_collisionHandleANode); + } + if (m_collisionHandleBNode != nullptr) + { + computeGraphB->addNode(m_collisionHandleBNode); + } + + // Add the edges + { + // Connect inputA's->CD + for (size_t i = 0; i < m_taskNodeInputs.first.size(); i++) + { + computeGraphA->addEdge(m_taskNodeInputs.first[i], m_collisionDetectionNode); + } + + // Connect inputB's->CD + for (size_t i = 0; i < m_taskNodeInputs.second.size(); i++) + { + computeGraphA->addEdge(m_taskNodeInputs.second[i], m_collisionDetectionNode); + } + } + + // Now connect CD to CHA/CHB/CHAB + // This also works for the case CHA = CHB = CHAB + { + if (m_collisionHandleANode != nullptr) + { + computeGraphA->addEdge(m_collisionDetectionNode, m_collisionHandleANode); + } + if (m_collisionHandleBNode != nullptr) + { + computeGraphB->addEdge(m_collisionDetectionNode, m_collisionHandleBNode); + } + } + + // Connect either CD or CHA/CHAB to outputA's + for (size_t i = 0; i < m_taskNodeOutputs.first.size(); i++) + { + if (m_collisionHandleANode != nullptr) + { + computeGraphA->addEdge(m_collisionHandleANode, m_taskNodeOutputs.first[i]); + } + else + { + computeGraphA->addEdge(m_collisionDetectionNode, m_taskNodeOutputs.first[i]); + } + } + // Connect eitehr CD or CHB/CHAB to outputB's + for (size_t i = 0; i < m_taskNodeOutputs.second.size(); i++) + { + if (m_collisionHandleBNode != nullptr) + { + computeGraphB->addEdge(m_collisionHandleBNode, m_taskNodeOutputs.second[i]); + } + else + { + computeGraphB->addEdge(m_collisionDetectionNode, m_taskNodeOutputs.second[i]); + } + } +} +} diff --git a/Source/Scene/imstkCollisionPair.h b/Source/Scene/imstkCollisionPair.h new file mode 100644 index 0000000000000000000000000000000000000000..3ed5e23782c86503e25aa0104c9e32286f27fab2 --- /dev/null +++ b/Source/Scene/imstkCollisionPair.h @@ -0,0 +1,89 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkCollisionDetection.h" +#include "imstkCollisionHandling.h" +#include "imstkObjectInteractionPair.h" + +namespace imstk +{ +struct CollisionData; +class CollidingObject; + +/// +/// \class CollisionPair +/// +/// \brief CollisionPair is a specialization of InteractionPair that adds Handler functions. +/// The handler functions precede the interaction node/step as their own computational node/step. +/// The handler functions may be a singular handler node or two separate concurrent nodes. +/// +class CollisionPair : public ObjectInteractionPair +{ +public: + CollisionPair(std::shared_ptr<CollidingObject> objA, std::shared_ptr<CollidingObject> objB); + + /// + /// \brief Specifies a CollisionPair with two handles (one or both can be nullptr) + /// + CollisionPair(std::shared_ptr<CollidingObject> objA, std::shared_ptr<CollidingObject> objB, + std::shared_ptr<CollisionDetection> cd, + std::shared_ptr<CollisionHandling> chA, + std::shared_ptr<CollisionHandling> chB); + + /// + /// \brief Specifies CollisionPair with an AB handler + /// + CollisionPair(std::shared_ptr<CollidingObject> objA, std::shared_ptr<CollidingObject> objB, + std::shared_ptr<CollisionDetection> cd, + std::shared_ptr<CollisionHandling> chAB); + + ~CollisionPair() = default; + +public: + void setCollisionDetection(std::shared_ptr<CollisionDetection> colDetect); + void setCollisionHandlingA(std::shared_ptr<CollisionHandling> colHandlingA); + void setCollisionHandlingB(std::shared_ptr<CollisionHandling> colHandlingB); + void setCollisionHandlingAB(std::shared_ptr<CollisionHandling> colHandlingAB); + + std::shared_ptr<CollisionDetection> getCollisionDetection() const { return m_colDetect; } + std::shared_ptr<CollisionHandling> getCollisionHandlingA() const { return m_colHandlingA; } + std::shared_ptr<CollisionHandling> getCollisionHandlingB() const { return m_colHandlingB; } + + std::shared_ptr<TaskNode> getCollisionDetectionNode() const { return m_collisionDetectionNode; } + std::shared_ptr<TaskNode> getCollisionHandlingANode() const { return m_collisionHandleANode; } + std::shared_ptr<TaskNode> getCollisionHandlingBNode() const { return m_collisionHandleBNode; } + +public: + virtual void apply() override; + +protected: + std::shared_ptr<CollisionDetection> m_colDetect = nullptr; ///< Collision detection algorithm + std::shared_ptr<CollisionData> m_colData = nullptr; ///< Common Collision Data + std::shared_ptr<CollisionHandling> m_colHandlingA = nullptr; + std::shared_ptr<CollisionHandling> m_colHandlingB = nullptr; + + std::shared_ptr<TaskNode> m_collisionDetectionNode = nullptr; + std::shared_ptr<TaskNode> m_collisionHandleANode = nullptr; + std::shared_ptr<TaskNode> m_collisionHandleBNode = nullptr; +}; +} diff --git a/Source/Scene/imstkInteractionPair.cpp b/Source/Scene/imstkInteractionPair.cpp deleted file mode 100644 index b82844816017604575d2bcd9395569084e136e67..0000000000000000000000000000000000000000 --- a/Source/Scene/imstkInteractionPair.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkInteractionPair.h" -#include "imstkCollisionData.h" -#include "imstkCollidingObject.h" -#include "imstkCDObjectFactory.h" - -#include <g3log/g3log.hpp> - -namespace imstk -{ -InteractionPair::InteractionPair(std::shared_ptr<CollidingObject> A, - std::shared_ptr<CollidingObject> B, - CollisionDetection::Type CDType, - CollisionHandling::Type CHAType, - CollisionHandling::Type CHBType) : - m_colData(std::make_shared<CollisionData>()), - m_valid(false) -{ - // Check that objects exist - CHECK(A != nullptr && B != nullptr) << "InteractionPair error: invalid objects (nullptr)."; - - // Check if objects are different - /*if (A == B) - { - LOG(WARNING) << "InteractionPair error: object cannot interact with itself."; - return; - }*/ - - // Collision Detection - std::shared_ptr<CollisionDetection> CD = makeCollisionDetectionObject(CDType, A, B, m_colData); - - CHECK(CD != nullptr) << "InteractionPair error: can not instantiate collision detection algorithm."; - - // Collision Handling A - std::shared_ptr<CollisionHandling> CHA; - if (CHAType != CollisionHandling::Type::None) - { - CHA = CollisionHandling::make_collision_handling(CHAType, CollisionHandling::Side::A, m_colData, A, B); - - CHECK(CHA != nullptr) << "InteractionPair error: can not instantiate collision handling for '" - << A->getName() << "' object."; - } - - // Collision Handling B - std::shared_ptr<CollisionHandling> CHB; - if (CHBType != CollisionHandling::Type::None) - { - CHB = CollisionHandling::make_collision_handling(CHBType, CollisionHandling::Side::B, m_colData, B, A); - - CHECK(CHB != nullptr) << "InteractionPair error: can not instantiate collision handling for '" - << B->getName() << "' object."; - } - - // Init interactionPair - m_objects = ObjectsPair(A, B); - m_colDetect = CD; - m_colHandlingA = CHA; - m_colHandlingB = CHB; - m_valid = true; -} - -InteractionPair::InteractionPair(std::shared_ptr<CollidingObject> A, - std::shared_ptr<CollidingObject> B, - std::shared_ptr<CollisionDetection> CD, - std::shared_ptr<CollisionHandling> CHA, - std::shared_ptr<CollisionHandling> CHB) -{ - m_valid = false; - - // Check that objects exist - CHECK(A != nullptr && B != nullptr) << "InteractionPair error: invalid objects (nullptr)."; - - // Check if objects are different - /*if (A == B) - { - LOG(WARNING) << "InteractionPair error: object cannot interact with itself."; - return; - }*/ - - m_objects = ObjectsPair(A, B); - m_colDetect = CD; - m_colHandlingA = CHA; - m_colHandlingB = CHB; - m_colData = CD->getCollisionData(); - m_valid = true; -} - -void -InteractionPair::computeCollisionData() -{ - CHECK(m_valid) << "InteractionPair::computeCollisionData error: interaction not valid."; - m_colDetect->computeCollisionData(); -} - -void -InteractionPair::processCollisionData() -{ - CHECK(m_valid) << "InteractionPair::computeContactForces error: interaction not valid."; - - if (m_colHandlingA) - { - m_colHandlingA->processCollisionData(); - } - if (m_colHandlingB) - { - m_colHandlingB->processCollisionData(); - } -} - -const bool& -InteractionPair::isValid() -{ - return m_valid; -} - -const InteractionPair::ObjectsPair& -InteractionPair::getObjectsPair() const -{ - return m_objects; -} - -std::shared_ptr<CollisionDetection> -InteractionPair::getCollisionDetection() const -{ - return m_colDetect; -} - -std::shared_ptr<CollisionHandling> -InteractionPair::getCollisionHandlingA() const -{ - return m_colHandlingA; -} - -std::shared_ptr<CollisionHandling> -InteractionPair::getCollisionHandlingB() const -{ - return m_colHandlingB; -} -} diff --git a/Source/Scene/imstkInteractionPair.h b/Source/Scene/imstkInteractionPair.h index 85f1d67d1c239cedd98916cf5b9a1c7347a821f5..f2b5337f1c306174a5f03b30d65648b7d74afad5 100644 --- a/Source/Scene/imstkInteractionPair.h +++ b/Source/Scene/imstkInteractionPair.h @@ -21,89 +21,35 @@ #pragma once -// imstk -#include "imstkCollisionDetection.h" -#include "imstkCollisionHandling.h" +#include <memory> +#include <vector> namespace imstk { -struct CollisionData; -class CollidingObject; +class TaskNode; /// /// \class InteractionPair /// -/// \brief This class implements collision interaction between two given scene objects +/// \brief This class defines an interaction between nodes /// class InteractionPair { -using ObjectsPair = std::pair<std::shared_ptr<CollidingObject>, std::shared_ptr<CollidingObject>>; - public: + using Inputs = std::pair<std::vector<std::shared_ptr<TaskNode>>, std::vector<std::shared_ptr<TaskNode>>>; + using Outputs = std::pair<std::vector<std::shared_ptr<TaskNode>>, std::vector<std::shared_ptr<TaskNode>>>; - /// - /// \brief Constructor - /// - InteractionPair(std::shared_ptr<CollidingObject> A, - std::shared_ptr<CollidingObject> B, - CollisionDetection::Type CDType, - CollisionHandling::Type CHAType, - CollisionHandling::Type CHBType); - - InteractionPair(std::shared_ptr<CollidingObject> A, - std::shared_ptr<CollidingObject> B, - std::shared_ptr<CollisionDetection> CD, - std::shared_ptr<CollisionHandling> CHA, - std::shared_ptr<CollisionHandling> CHB); - - /// - /// \brief Destructor - /// - ~InteractionPair() = default; - - /// - /// \brief Call collision detection algorithm to compute collision data - /// - void computeCollisionData(); - - /// - /// \brief Call collision handling algorithm to compute contact forces for an object - /// - void processCollisionData(); - - /// - /// \brief Call collision handling algorithm to compute contact forces for an object - /// - const bool& isValid(); - - /// - /// \brief Returns objects pair - /// - const ObjectsPair& getObjectsPair() const; - - /// - /// \brief Returns collision detection algorithm for A-B - /// - std::shared_ptr<CollisionDetection> getCollisionDetection() const; - - /// - /// \brief Returns collision handling algorithm for object B - /// - std::shared_ptr<CollisionHandling> getCollisionHandlingA() const; +public: + InteractionPair() = default; + virtual ~InteractionPair() = default; - /// - /// \brief Returns collision handling algorithm for object A - /// - std::shared_ptr<CollisionHandling> getCollisionHandlingB() const; +public: + const Inputs& getTaskNodeInputs() const { return m_taskNodeInputs; } + const Outputs& getTaskNodeOutputs() const { return m_taskNodeOutputs; } protected: - - ObjectsPair m_objects; ///< Colliding objects - std::shared_ptr<CollisionDetection> m_colDetect; ///< Collision detection algorithm - std::shared_ptr<CollisionData> m_colData; ///< Common Collision Data - std::shared_ptr<CollisionHandling> m_colHandlingA; ///< Collision handling algorithm for A - std::shared_ptr<CollisionHandling> m_colHandlingB; ///< Collision handling algorithm for B - - bool m_valid; + Inputs m_taskNodeInputs; ///> The interacting nodes + Outputs m_taskNodeOutputs; ///> The interacting nodes + std::shared_ptr<TaskNode> m_interactionFunction; ///> Function to execute on interaction }; } diff --git a/Source/Scene/imstkObjectInteractionFactory.cpp b/Source/Scene/imstkObjectInteractionFactory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eaba788f7d8d03002a21f191d9e6d3d32c89133b --- /dev/null +++ b/Source/Scene/imstkObjectInteractionFactory.cpp @@ -0,0 +1,115 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkObjectInteractionFactory.h" +#include "imstkBoneDrillingCH.h" +#include "imstkCDObjectFactory.h" +#include "imstkCollidingObject.h" +#include "imstkCollisionData.h" +#include "imstkCollisionDetection.h" +#include "imstkDeformableObject.h" +#include "imstkLogger.h" +#include "imstkPbdObject.h" +#include "imstkPbdObjectCollisionPair.h" +#include "imstkPenaltyCH.h" +#include "imstkPickingCH.h" +#include "imstkSPHCollisionHandling.h" +#include "imstkSPHObject.h" + +namespace imstk +{ +// Cast type check +template<typename ObjectType> +static bool +isType(std::shared_ptr<SceneObject> obj) +{ + return std::dynamic_pointer_cast<ObjectType>(obj) != nullptr; +} + +std::shared_ptr<ObjectInteractionPair> +makeObjectInteractionPair(std::shared_ptr<CollidingObject> obj1, std::shared_ptr<CollidingObject> obj2, + InteractionType intType, CollisionDetection::Type cdType) +{ + std::shared_ptr<ObjectInteractionPair> results = nullptr; + if (intType == InteractionType::PbdObjToPbdObjCollision && isType<PbdObject>(obj1) && isType<PbdObject>(obj2)) + { + results = std::make_shared<PbdObjectCollisionPair>(std::dynamic_pointer_cast<PbdObject>(obj1), std::dynamic_pointer_cast<PbdObject>(obj2), cdType); + } + //else if (intType == InteractionType::PbdObjToCollidingObjCollision && isType<PbdObject>(obj1)) + //{ + // results = std::make_shared<PbdCollidingObjCollisionPair>( + // std::dynamic_pointer_cast<PbdObject>(obj1), + // std::dynamic_pointer_cast<PbdObject>(obj2), cdType); + //} + else if (intType == InteractionType::SphObjToCollidingObjCollision && isType<SPHObject>(obj1)) + { + // Setup CD, and collision data + std::shared_ptr<CollisionData> colData = std::make_shared<CollisionData>(); + std::shared_ptr<CollisionDetection> colDetect = makeCollisionDetectionObject(cdType, obj1->getCollidingGeometry(), obj2->getCollidingGeometry(), colData); + + // Setup the handler + std::shared_ptr<SPHCollisionHandling> colHandler = + std::make_shared<SPHCollisionHandling>(CollisionHandling::Side::A, colData, std::dynamic_pointer_cast<SPHObject>(obj1)); + + results = std::make_shared<CollisionPair>(obj1, obj2, colDetect, colHandler, nullptr); + } + else if (intType == InteractionType::FemObjToCollidingObjNodalPicking && isType<FeDeformableObject>(obj1)) + { + // Setup CD, and collision data + std::shared_ptr<CollisionData> colData = std::make_shared<CollisionData>(); + std::shared_ptr<CollisionDetection> colDetect = makeCollisionDetectionObject(cdType, obj1->getCollidingGeometry(), obj2->getCollidingGeometry(), colData); + + // Setup the handler + std::shared_ptr<PickingCH> colHandler = + std::make_shared<PickingCH>(CollisionHandling::Side::A, colData, std::dynamic_pointer_cast<FeDeformableObject>(obj1)); + + results = std::make_shared<CollisionPair>(obj1, obj2, colDetect, colHandler, nullptr); + } + else if (intType == InteractionType::FemObjToCollidingObjPenaltyForce && isType<FeDeformableObject>(obj1)) + { + // Setup CD, and collision data + std::shared_ptr<CollisionData> colData = std::make_shared<CollisionData>(); + std::shared_ptr<CollisionDetection> colDetect = makeCollisionDetectionObject(cdType, obj1->getCollidingGeometry(), obj2->getCollidingGeometry(), colData); + + // Setup the handler + std::shared_ptr<PenaltyCH> colHandler = + std::make_shared<PenaltyCH>(CollisionHandling::Side::A, colData, obj1); + } + else if (intType == InteractionType::FemObjToCollidingObjBoneDrilling && isType<FeDeformableObject>(obj1)) + { + // Setup CD, and collision data + std::shared_ptr<CollisionData> colData = std::make_shared<CollisionData>(); + std::shared_ptr<CollisionDetection> colDetect = makeCollisionDetectionObject(cdType, obj1->getCollidingGeometry(), obj2->getCollidingGeometry(), colData); + + // Setup the handler + std::shared_ptr<BoneDrillingCH> colHandler = + std::make_shared<BoneDrillingCH>(CollisionHandling::Side::A, colData, obj1, obj2); + + results = std::make_shared<CollisionPair>(obj1, obj2, colDetect, colHandler, nullptr); + } + + if (results == nullptr) + { + LOG(WARNING) << "Invalid SceneObjects for requested interaction."; + } + return results; +} +} \ No newline at end of file diff --git a/Source/Scene/imstkObjectInteractionFactory.h b/Source/Scene/imstkObjectInteractionFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..4f861d8090b638259a29e66623ffbd695e60d1e1 --- /dev/null +++ b/Source/Scene/imstkObjectInteractionFactory.h @@ -0,0 +1,53 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkCollisionDetection.h" +#include "imstkObjectInteractionPair.h" + +namespace imstk +{ +class CollidingObject; + +// Predefined standard/types of interaction from imstk +enum class InteractionType +{ + PbdObjToPbdObjCollision, + + PbdObjToCollidingObjCollision, + SphObjToCollidingObjCollision, + FemObjToCollidingObjCollision, + //RigidObjToCollidingObjCollision, + + //RigidObjToRigidObjCollision, + + FemObjToCollidingObjPenaltyForce, + FemObjToCollidingObjBoneDrilling, + FemObjToCollidingObjNodalPicking +}; + +/// +/// \brief Factory for InteractionPairs, returns nullptr and logs warning if failed +/// +extern std::shared_ptr<ObjectInteractionPair> makeObjectInteractionPair(std::shared_ptr<CollidingObject> obj1, std::shared_ptr<CollidingObject> obj2, + InteractionType intType, CollisionDetection::Type cdType); +} \ No newline at end of file diff --git a/Source/Scene/imstkObjectInteractionPair.cpp b/Source/Scene/imstkObjectInteractionPair.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df5c701441e539b0139f9efcb52c5324b5e9a04a --- /dev/null +++ b/Source/Scene/imstkObjectInteractionPair.cpp @@ -0,0 +1,35 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkObjectInteractionPair.h" +#include "imstkLogger.h" + +namespace imstk +{ +ObjectInteractionPair::ObjectInteractionPair(std::shared_ptr<SceneObject> objA, std::shared_ptr<SceneObject> objB) +{ + // Check that objects exist + CHECK(objA != nullptr && objB != nullptr) << "InteractionPair error: invalid objects (nullptr)."; + + this->m_objects.first = objA; + this->m_objects.second = objB; +} +} \ No newline at end of file diff --git a/Source/Scene/imstkObjectInteractionPair.h b/Source/Scene/imstkObjectInteractionPair.h new file mode 100644 index 0000000000000000000000000000000000000000..dc7e4d1a477ad70b396cf25839e9b29a0d9b748b --- /dev/null +++ b/Source/Scene/imstkObjectInteractionPair.h @@ -0,0 +1,60 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkInteractionPair.h" + +namespace imstk +{ +class SceneObject; + +/// +/// \class ObjectInteractionPair +/// +/// \brief This class defines an interaction between two SceneObjects +/// An interaction is a function occuring between two SceneObjects at some point +/// +class ObjectInteractionPair : public InteractionPair +{ +public: + using SceneObjectPair = std::pair<std::shared_ptr<SceneObject>, std::shared_ptr<SceneObject>>; + +// Cannot be constructed as synch points are externally undefinable +protected: + ObjectInteractionPair() = default; + ObjectInteractionPair(std::shared_ptr<SceneObject> objA, std::shared_ptr<SceneObject> objB); + + virtual ~ObjectInteractionPair() override = default; + +public: + const SceneObjectPair& getObjectsPair() const { return m_objects; } + +public: + /// + /// \brief Modifies the graph of the provide objects to apply the interaction + /// + virtual void apply() = 0; + +protected: + SceneObjectPair m_objects; ///> The two objects interacting +}; +} \ No newline at end of file diff --git a/Source/Scene/imstkPbdInteractionPair.cpp b/Source/Scene/imstkPbdInteractionPair.cpp deleted file mode 100644 index ddfa7243d7fb0dc25e1df9273c9ae4756d1981b5..0000000000000000000000000000000000000000 --- a/Source/Scene/imstkPbdInteractionPair.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#include "imstkPbdInteractionPair.h" -#include "imstkSurfaceMesh.h" -#include "imstkLineMesh.h" -#include "imstkGeometryMap.h" -#include "imstkCollisionUtils.h" -#include "imstkPbdModel.h" -#include "imstkPbdObject.h" -//#include "imstkColor.h" -#include "imstkPbdEdgeEdgeCollisionConstraint.h" -#include "imstkPbdPointTriCollisionConstraint.h" - -namespace imstk -{ -PbdInteractionPair::PbdInteractionPair(const std::shared_ptr<PbdObject>& A, - const std::shared_ptr<PbdObject>& B) : - m_firstObj(A), m_secondObj(B) -{ -} - -bool -PbdInteractionPair::doBroadPhaseCollision() -{ - Vec3d min1, max1; - m_firstObj->getCollidingGeometry()->computeBoundingBox(min1, max1); - - Vec3d min2, max2; - m_secondObj->getCollidingGeometry()->computeBoundingBox(min2, max2); - - auto prox1 = m_firstObjProximity; - auto prox2 = m_secondObjProximity; - - return CollisionUtils::testAABBToAABB(min1[0] - prox1, max1[0] + prox1, min1[1] - prox1, max1[1] + prox1, - min1[2] - prox1, max1[2] + prox1, min2[0] - prox2, max2[0] + prox2, - min2[1] - prox2, max2[1] + prox2, min2[2] - prox2, max2[2] + prox2); -} - -void -PbdInteractionPair::doNarrowPhaseCollision() -{ - auto g1 = m_firstObj->getCollidingGeometry(); - auto g2 = m_secondObj->getCollidingGeometry(); - - auto map1 = m_firstObj->getPhysicsToCollidingMap(); - auto map2 = m_secondObj->getPhysicsToCollidingMap(); - - auto dynaModel1 = std::static_pointer_cast<PbdModel>(m_firstObj->getDynamicalModel()); - auto dynaModel2 = std::static_pointer_cast<PbdModel>(m_secondObj->getDynamicalModel()); - - auto prox1 = dynaModel1->getParameters()->m_proximity; - auto prox2 = dynaModel2->getParameters()->m_proximity; - - auto mesh2 = std::static_pointer_cast<SurfaceMesh>(g2); - - if (g1->getType() == Geometry::Type::LineMesh) - { - auto mesh1 = std::static_pointer_cast<LineMesh>(g1); - - // brute force, use BVH or spatial grid would be much better - // point - for (size_t i = 0; i < mesh1->getNumVertices(); ++i) - { - Vec3d p = mesh1->getVertexPosition(i); - std::vector<SurfaceMesh::TriangleArray> elements = mesh2->getTrianglesVertices(); - - for (size_t j = 0; j < elements.size(); ++j) - { - SurfaceMesh::TriangleArray& e = elements[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - - if (CollisionUtils::testPointToTriAABB(p[0], p[1], p[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdPointTriangleConstraint>(); - c->initConstraint(dynaModel1, i, - dynaModel2, map2->getMapIdx(e[0]), map2->getMapIdx(e[1]), map2->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - } - } - } - - const auto nL1 = mesh1->getNumLines(); - const auto nV2 = mesh2->getNumVertices(); - std::vector<std::vector<bool>> E2(nV2, std::vector<bool>(nV2, 1)); - std::vector<SurfaceMesh::TriangleArray> elements2 = mesh2->getTrianglesVertices(); - - for (int k = 0; k < nL1; ++k) - { - auto nodes = mesh1->getLinesVertices()[k]; - size_t i1 = nodes[0]; - size_t i2 = nodes[1]; - - const Vec3d P = mesh1->getVertexPosition(i1); - const Vec3d Q = mesh1->getVertexPosition(i2); - - for (size_t j = 0; j < elements2.size(); ++j) - { - SurfaceMesh::TriangleArray& e = elements2[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - - if (E2[e[0]][e[1]] && E2[e[1]][e[0]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[0]), map1->getMapIdx(e[1])); - m_collisionConstraints.push_back(c); - } - E2[e[0]][e[1]] = 0; - } - if (E2[e[1]][e[2]] && E2[e[2]][e[1]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[1]), map1->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - } - E2[e[1]][e[2]] = 0; - } - if (E2[e[2]][e[0]] && E2[e[0]][e[2]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p2[0], p2[1], p2[2], - p0[0], p0[1], p0[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[2]), map1->getMapIdx(e[0])); - m_collisionConstraints.push_back(c); - } - E2[e[2]][e[0]] = 0; - } - } - } - } - else if (g1->getType() == Geometry::Type::PointSet) - { - auto mesh1 = std::static_pointer_cast<PointSet>(g1); - - // brute force, use BVH or spatial grid would be much better - // point - for (size_t i = 0; i < mesh1->getNumVertices(); ++i) - { - const auto p = mesh1->getVertexPosition(i); - auto elements = mesh2->getTrianglesVertices(); - - for (size_t j = 0; j < elements.size(); ++j) - { - auto& e = elements[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - - if (CollisionUtils::testPointToTriAABB(p[0], p[1], p[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdPointTriangleConstraint>(); - auto mappedIndex1 = (map1) ? map1->getMapIdx(i) : i; - - c->initConstraint(dynaModel1, mappedIndex1, dynaModel2, map2->getMapIdx(e[0]), map2->getMapIdx(e[1]), map2->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - } - } - } - } - else - { - auto mesh1 = std::static_pointer_cast<SurfaceMesh>(g1); - - // brute force, use BVH or spatial grid would be much better - // point - for (size_t i = 0; i < mesh1->getNumVertices(); ++i) - { - const Vec3d p = mesh1->getVertexPosition(i); - auto elements = mesh2->getTrianglesVertices(); - - for (size_t j = 0; j < elements.size(); ++j) - { - SurfaceMesh::TriangleArray& e = elements[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - - if (CollisionUtils::testPointToTriAABB(p[0], p[1], p[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdPointTriangleConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i), - dynaModel2, map2->getMapIdx(e[0]), map2->getMapIdx(e[1]), map2->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - } - } - } - - // edge - // since we don't have edge structure, the following is not good - const auto nV = mesh1->getNumVertices(); - std::vector<std::vector<bool>> E(nV, std::vector<bool>(nV, 1)); - - const auto nV2 = mesh2->getNumVertices(); - std::vector<std::vector<bool>> E2(nV2, std::vector<bool>(nV2, 1)); - - std::vector<SurfaceMesh::TriangleArray> elements = mesh1->getTrianglesVertices(); - for (size_t k = 0; k < elements.size(); ++k) - { - SurfaceMesh::TriangleArray& tri = elements[k]; - size_t i1 = tri[0]; - size_t i2 = tri[1]; - - if (E[i1][i2] && E[i2][i1]) - { - const Vec3d P = mesh1->getVertexPosition(i1); - const Vec3d Q = mesh1->getVertexPosition(i2); - std::vector<SurfaceMesh::TriangleArray> elements2 = mesh2->getTrianglesVertices(); - for (size_t j = 0; j < elements2.size(); ++j) - { - SurfaceMesh::TriangleArray& e = elements2[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - if (E2[e[0]][e[1]] && E2[e[1]][e[0]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[0]), map1->getMapIdx(e[1])); - m_collisionConstraints.push_back(c); - E2[e[0]][e[1]] = 0; - } - } - if (E2[e[1]][e[2]] && E2[e[2]][e[1]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[1]), map1->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - E2[e[1]][e[2]] = 0; - } - } - if (E2[e[2]][e[0]] && E2[e[0]][e[2]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p2[0], p2[1], p2[2], - p0[0], p0[1], p0[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[2]), map1->getMapIdx(e[0])); - m_collisionConstraints.push_back(c); - E2[e[2]][e[0]] = 0; - } - } - } - E[i1][i2] = 0; - } - - i1 = tri[1]; - i2 = tri[2]; - if (E[i1][i2] && E[i2][i1]) - { - const Vec3d P = mesh1->getVertexPosition(i1); - const Vec3d Q = mesh1->getVertexPosition(i2); - std::vector<SurfaceMesh::TriangleArray> elements2 = mesh2->getTrianglesVertices(); - - for (size_t j = 0; j < elements2.size(); ++j) - { - SurfaceMesh::TriangleArray& e = elements2[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - if (E2[e[0]][e[1]] && E2[e[1]][e[0]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[0]), map1->getMapIdx(e[1])); - m_collisionConstraints.push_back(c); - E2[e[0]][e[1]] = 0; - } - } - if (E2[e[1]][e[2]] && E2[e[2]][e[1]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[1]), map1->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - E2[e[1]][e[2]] = 0; - } - } - if (E2[e[2]][e[0]] && E2[e[0]][e[2]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p2[0], p2[1], p2[2], - p0[0], p0[1], p0[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[2]), map1->getMapIdx(e[0])); - m_collisionConstraints.push_back(c); - E2[e[2]][e[0]] = 0; - } - } - } - E[i1][i2] = 0; - } - - i1 = tri[2]; - i2 = tri[0]; - if (E[i1][i2] && E[i2][i1]) - { - const Vec3d P = mesh1->getVertexPosition(i1); - const Vec3d Q = mesh1->getVertexPosition(i2); - std::vector<SurfaceMesh::TriangleArray> elements2 = mesh2->getTrianglesVertices(); - for (size_t j = 0; j < elements2.size(); ++j) - { - SurfaceMesh::TriangleArray& e = elements2[j]; - const Vec3d p0 = mesh2->getVertexPosition(e[0]); - const Vec3d p1 = mesh2->getVertexPosition(e[1]); - const Vec3d p2 = mesh2->getVertexPosition(e[2]); - if (E2[e[0]][e[1]] && E2[e[1]][e[0]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p0[0], p0[1], p0[2], - p1[0], p1[1], p1[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[0]), map1->getMapIdx(e[1])); - m_collisionConstraints.push_back(c); - E2[e[0]][e[1]] = 0; - } - } - if (E2[e[1]][e[2]] && E2[e[2]][e[1]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p1[0], p1[1], p1[2], - p2[0], p2[1], p2[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[1]), map1->getMapIdx(e[2])); - m_collisionConstraints.push_back(c); - E2[e[1]][e[2]] = 0; - } - } - if (E2[e[2]][e[0]] && E2[e[0]][e[2]]) - { - if (CollisionUtils::testLineToLineAABB(P[0], P[1], P[2], - Q[0], Q[1], Q[2], - p2[0], p2[1], p2[2], - p0[0], p0[1], p0[2], prox1, prox2)) - { - auto c = std::make_shared<PbdEdgeEdgeConstraint>(); - c->initConstraint(dynaModel1, map1->getMapIdx(i1), map1->getMapIdx(i2), - dynaModel2, map2->getMapIdx(e[2]), map1->getMapIdx(e[0])); - m_collisionConstraints.push_back(c); - E2[e[2]][e[0]] = 0; - } - } - } - E[i1][i2] = 0; - } - } - } -} - -void -PbdInteractionPair::resolveCollision() -{ - if (!m_collisionConstraints.empty()) - { - unsigned int i = 0; - while (++i < maxIter) - { - for (size_t k = 0; k < m_collisionConstraints.size(); ++k) - { - m_collisionConstraints[k]->solvePositionConstraint(); - } - } - } -} -} // imstk diff --git a/Source/Scene/imstkPbdInteractionPair.h b/Source/Scene/imstkPbdInteractionPair.h deleted file mode 100644 index 1aaa93553c4ef0f69830a9d361f99322edfa4170..0000000000000000000000000000000000000000 --- a/Source/Scene/imstkPbdInteractionPair.h +++ /dev/null @@ -1,85 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include "imstkInteractionPair.h" - -namespace imstk -{ -class PbdObject; -class PbdCollisionConstraint; - -/// -/// \class PbdInteractionPair -/// -/// \brief -/// -class PbdInteractionPair -{ -public: - /// - /// \brief Constructor - /// - PbdInteractionPair(const std::shared_ptr<PbdObject>& A, const std::shared_ptr<PbdObject>& B); - - /// - /// \brief Clear the collisions from previous step - /// - inline void resetConstraints() - { - m_collisionConstraints.clear(); - } - - /// - /// \brief - /// - inline void setNumberOfInterations(const unsigned int& n) - { - maxIter = n; - } - - /// - /// \brief Broad phase collision detection using AABB - /// - bool doBroadPhaseCollision(); - - /// - /// \brief Narrow phase collision detection - /// - void doNarrowPhaseCollision(); - - /// - /// \brief Resolves the collision by solving pbd collision constraints - /// - void resolveCollision(); - -private: - std::vector<std::shared_ptr<PbdCollisionConstraint>> m_collisionConstraints; - - std::shared_ptr<PbdObject> m_firstObj; - std::shared_ptr<PbdObject> m_secondObj; - - double m_firstObjProximity = MAX_D; - double m_secondObjProximity = MAX_D; - unsigned int maxIter = 50; -}; -} diff --git a/Source/Scene/imstkPbdObjectCollisionPair.cpp b/Source/Scene/imstkPbdObjectCollisionPair.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80d51aa78416f917ccde3caa4f111e876c204fcf --- /dev/null +++ b/Source/Scene/imstkPbdObjectCollisionPair.cpp @@ -0,0 +1,96 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkPbdObjectCollisionPair.h" +#include "imstkCDObjectFactory.h" +#include "imstkCollisionData.h" +#include "imstkPBDCollisionHandling.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" +#include "imstkPbdSolver.h" +#include "imstkTaskGraph.h" + +namespace imstk +{ +// Pbd Collision will be tested before any step of pbd, then resolved after the solve steps of the two objects +PbdObjectCollisionPair::PbdObjectCollisionPair(std::shared_ptr<PbdObject> obj1, std::shared_ptr<PbdObject> obj2, + CollisionDetection::Type cdType) : CollisionPair(obj1, obj2) +{ + std::shared_ptr<PbdModel> pbdModel1 = obj1->getPbdModel(); + std::shared_ptr<PbdModel> pbdModel2 = obj2->getPbdModel(); + + // Define where collision interaction happens + m_taskNodeInputs.first.push_back(pbdModel1->getUpdateCollisionGeometryNode()); + m_taskNodeInputs.second.push_back(pbdModel2->getUpdateCollisionGeometryNode()); + + m_taskNodeOutputs.first.push_back(pbdModel1->getSolveNode()); + m_taskNodeOutputs.second.push_back(pbdModel2->getSolveNode()); + + // Define where solver interaction happens + m_solveNodeInputs.first.push_back(pbdModel1->getSolveNode()); + m_solveNodeInputs.second.push_back(pbdModel2->getSolveNode()); + + m_solveNodeOutputs.first.push_back(pbdModel1->getUpdateVelocityNode()); + m_solveNodeOutputs.second.push_back(pbdModel2->getUpdateVelocityNode()); + + // Setup the CD + m_colData = std::make_shared<CollisionData>(); + setCollisionDetection(makeCollisionDetectionObject(cdType, obj1->getCollidingGeometry(), obj2->getCollidingGeometry(), m_colData)); + + // Setup the handler + std::shared_ptr<PBDCollisionHandling> ch = std::make_shared<PBDCollisionHandling>(CollisionHandling::Side::AB, m_colData, obj1, obj2); + setCollisionHandlingAB(ch); + + // Setup compute node for collision solver (true/critical node) + m_collisionSolveNode = std::make_shared<TaskNode>([ch]() { ch->getCollisionSolver()->solve(); }, + obj1->getName() + "_vs_" + obj2->getName() + "_CollisionSolver", true); +} + +void +PbdObjectCollisionPair::apply() +{ + // Add the collision interaction + CollisionPair::apply(); + + // Add a secondary interaction + m_objects.first->getTaskGraph()->addNode(m_collisionSolveNode); + m_objects.second->getTaskGraph()->addNode(m_collisionSolveNode); + + // Add the solver interaction + for (size_t i = 0; i < m_solveNodeInputs.first.size(); i++) + { + m_objects.first->getTaskGraph()->addEdge(m_solveNodeInputs.first[i], m_collisionSolveNode); + } + for (size_t i = 0; i < m_solveNodeInputs.second.size(); i++) + { + m_objects.second->getTaskGraph()->addEdge(m_solveNodeInputs.second[i], m_collisionSolveNode); + } + + for (size_t i = 0; i < m_solveNodeOutputs.first.size(); i++) + { + m_objects.first->getTaskGraph()->addEdge(m_collisionSolveNode, m_solveNodeOutputs.first[i]); + } + for (size_t i = 0; i < m_solveNodeOutputs.second.size(); i++) + { + m_objects.second->getTaskGraph()->addEdge(m_collisionSolveNode, m_solveNodeOutputs.second[i]); + } +} +} \ No newline at end of file diff --git a/Source/Scene/imstkPbdObjectCollisionPair.h b/Source/Scene/imstkPbdObjectCollisionPair.h new file mode 100644 index 0000000000000000000000000000000000000000..fb476aeef6de4a26cfbd49af7a9b9378e649eff5 --- /dev/null +++ b/Source/Scene/imstkPbdObjectCollisionPair.h @@ -0,0 +1,50 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkCollisionPair.h" + +namespace imstk +{ +class PbdObject; +class PbdSolver; + +/// +/// \class PbdObjectCollisionPair +/// +/// \brief This class defines a collision interaction between two PbdObjects +/// +class PbdObjectCollisionPair : public CollisionPair +{ +public: + PbdObjectCollisionPair(std::shared_ptr<PbdObject> obj1, std::shared_ptr<PbdObject> obj2, + CollisionDetection::Type cdType = CollisionDetection::Type::MeshToMeshBruteForce); + + void apply() override; + +private: + // Pbd defines two interactions (one at CD and one at solver) + Inputs m_solveNodeInputs; + Outputs m_solveNodeOutputs; + std::shared_ptr<TaskNode> m_collisionSolveNode = nullptr; +}; +} \ No newline at end of file diff --git a/Source/Scene/imstkScene.cpp b/Source/Scene/imstkScene.cpp index 4409293f4133b3f07c2a55c074c8771ff5209b0e..dc6ee53090191c9257b1f81ad56cd1c14ecc3396 100644 --- a/Source/Scene/imstkScene.cpp +++ b/Source/Scene/imstkScene.cpp @@ -20,29 +20,40 @@ =========================================================================*/ #include "imstkScene.h" -#include "imstkSceneObject.h" -#include "imstkVisualModel.h" -#include "imstkCameraController.h" -#include "imstkSceneObjectControllerBase.h" +#include "imstkCamera.h" #include "imstkCameraController.h" +#include "imstkCollidingObject.h" +#include "imstkCollisionGraph.h" +#include "imstkCollisionPair.h" #include "imstkDebugRenderGeometry.h" #include "imstkDeformableObject.h" -#include "imstkTimer.h" -#include "imstkRigidBodyWorld.h" -#include "imstkPbdSolver.h" -#include "imstkPbdObject.h" -#include "imstkPBDCollisionHandling.h" -#include "imstkSolverBase.h" -#include "imstkCamera.h" -#include "imstkIBLProbe.h" -#include "imstkCollisionGraph.h" +#include "imstkDynamicObject.h" +#include "imstkFEMDeformableBodyModel.h" +#include "imstkInteractionPair.h" #include "imstkLight.h" -#include "imstkLogger.h" - -#include <iterator> +#include "imstkParallelUtils.h" +#include "imstkPbdObject.h" +#include "imstkRigidBodyWorld.h" +#include "imstkSceneObject.h" +#include "imstkSceneObjectControllerBase.h" +#include "imstkSequentialTaskGraphController.h" +#include "imstkTaskGraph.h" +#include "imstkTaskGraphVizWriter.h" +#include "imstkTbbTaskGraphController.h" +#include "imstkTimer.h" namespace imstk { +Scene::Scene(const std::string& name, std::shared_ptr<SceneConfig> config) : + m_name(name), + m_camera(std::make_shared<Camera>()), + m_collisionGraph(std::make_shared<CollisionGraph>()), + m_config(config), + m_taskGraph(std::make_shared<TaskGraph>("Scene_" + name + "_Source", "Scene_" + name + "_Sink")), + computeTimesLock(std::make_shared<ParallelUtils::SpinLock>()) +{ +} + Scene::~Scene() { // Join Camera Controllers @@ -56,54 +67,133 @@ Scene::~Scene() bool Scene::initialize() { - std::unordered_map<std::shared_ptr<PbdObject>, std::shared_ptr<PbdSolver>> pbdObjSolver; + // Initialize all the SceneObjects + for (auto const& it : m_sceneObjectsMap) + { + auto sceneObject = it.second; + CHECK(sceneObject->initialize()) << "Error initializing scene object: " << sceneObject->getName(); + } + // Build the compute graph + buildTaskGraph(); + + // Opportunity for external configuration + if (m_postTaskGraphConfigureCallback != nullptr) + { + m_postTaskGraphConfigureCallback(this); + } + + // Then init + initTaskGraph(); + + m_isInitialized = true; + LOG(INFO) << "Scene '" << this->getName() << "' initialized!"; + return true; +} + +void +Scene::buildTaskGraph() +{ + // Configures the edges/flow of the graph + + // Clear the compute graph of all nodes/edges except source+sink + m_taskGraph->clear(); + + // Setup all SceneObject compute graphs (and segment the rigid bodies) + std::list<std::shared_ptr<SceneObject>> rigidBodies; for (auto const& it : m_sceneObjectsMap) { - // Add pbd solver for all the pbd scene objects - if (it.second->getType() == SceneObject::Type::Pbd) + auto sceneObject = it.second; + if (sceneObject->getType() == SceneObject::Type::Rigid) { - auto pbdSolver = std::make_shared<PbdSolver>(); - auto pbdObj = std::dynamic_pointer_cast<PbdObject>(it.second); - pbdSolver->setPbdObject(pbdObj); - this->addNonlinearSolver(pbdSolver); - - pbdObjSolver[pbdObj] = pbdSolver; + rigidBodies.push_back(sceneObject); } - auto sceneObject = it.second; + sceneObject->initGraphEdges(); + } - CHECK(sceneObject->initialize()) << "Error initializing scene object: " << sceneObject->getName(); + // Apply all the interaction graph element operations to the SceneObject graphs + const std::vector<std::shared_ptr<ObjectInteractionPair>>& pairs = m_collisionGraph->getInteractionPairs(); + for (size_t i = 0; i < pairs.size(); i++) + { + pairs[i]->apply(); } - for (auto const& it : m_collisionGraph->getInteractionPairList()) + // Nest all the SceneObject graphs within this Scene's ComputeGraph + for (auto const& it : m_sceneObjectsMap) { - auto chA = it->getCollisionHandlingA(); - if (chA) + std::shared_ptr<TaskGraph> objComputeGraph = it.second->getTaskGraph(); + if (objComputeGraph != nullptr) { - if (chA->getType() == CollisionHandling::Type::PBD) - { - auto ch = std::dynamic_pointer_cast<PBDCollisionHandling>(chA); - ch->setSolver(pbdObjSolver[std::dynamic_pointer_cast<PbdObject>(it->getObjectsPair().first)]); - } + // Add edges between any nodes that are marked critical and running simulatenously + objComputeGraph = TaskGraph::resolveCriticalNodes(objComputeGraph); + // Sum and nest the graph + m_taskGraph->nestGraph(objComputeGraph, m_taskGraph->getSource(), m_taskGraph->getSink()); } - else - { - auto chB = it->getCollisionHandlingB(); - if (chB) + } + + // Edge Case: Rigid bodies all have a singular update point because of how PhysX works + // Think about generalizes these islands of interaction to Systems + if (rigidBodies.size() > 0) + { + // The node that updates the rigid body system + auto physXUpdate = m_taskGraph->addFunction("PhysXUpdate", [&]() { - if (chB->getType() == CollisionHandling::Type::PBD) - { - auto ch = std::dynamic_pointer_cast<PBDCollisionHandling>(chB); - ch->setSolver(pbdObjSolver[std::dynamic_pointer_cast<PbdObject>(it->getObjectsPair().second)]); - } - } + auto physxScene = RigidBodyWorld::getInstance()->m_Scene; + // TODO: update the time step, split into two steps, collide and advance + physxScene->simulate(RigidBodyWorld::getInstance()->getTimeStep()); + physxScene->fetchResults(true); + }); + + // Scene Source->physX Update->Rigid Body Update Geometry[i] + m_taskGraph->addEdge(m_taskGraph->getSource(), physXUpdate); + m_taskGraph->addEdge(physXUpdate, m_taskGraph->getSink()); + for (std::list<std::shared_ptr<SceneObject>>::iterator i = rigidBodies.begin(); i != rigidBodies.end(); i++) + { + m_taskGraph->addEdge(physXUpdate, (*i)->getUpdateGeometryNode()); } } +} - m_isInitialized = true; - LOG(INFO) << "Scene '" << this->getName() << "' initialized!"; - return true; +void +Scene::initTaskGraph() +{ + // Pick a controller for the graph execution + if (m_config->taskParallelizationEnabled) + { + m_taskGraphController = std::make_shared<TbbTaskGraphController>(); + } + else + { + m_taskGraphController = std::make_shared<SequentialTaskGraphController>(); + } + m_taskGraphController->setTaskGraph(m_taskGraph); + + // Reduce the graph, removing nonfunctional nodes, and redundant edges + if (m_config->graphReductionEnabled) + { + m_taskGraph = TaskGraph::reduce(m_taskGraph); + } + + // If user wants to benchmark, tell all the nodes to time themselves + for (std::shared_ptr<TaskNode> node : m_taskGraph->getNodes()) + { + node->m_enableTiming = m_config->taskTimingEnabled; + } + + // Generate unique names among the nodes + TaskGraph::getUniqueNodeNames(m_taskGraph, true); + m_nodeComputeTimes.clear(); + + if (m_config->writeTaskGraph) + { + TaskGraphVizWriter writer; + writer.setInput(m_taskGraph); + writer.setFileName("sceneTaskGraph.svg"); + writer.write(); + } + + m_taskGraphController->initialize(); } void @@ -304,18 +394,6 @@ Scene::getCollisionGraph() const return m_collisionGraph; } -const std::vector<std::shared_ptr<SolverBase>> -Scene::getSolvers() -{ - return m_solvers; -} - -void -Scene::addNonlinearSolver(std::shared_ptr<SolverBase> solver) -{ - m_solvers.push_back(solver); -} - void Scene::addObjectController(std::shared_ptr<SceneObjectControllerBase> controller) { @@ -340,20 +418,7 @@ Scene::resetSceneObjects() // Apply the geometry and apply maps to all the objects for (auto obj : this->getSceneObjects()) { - const auto objType = obj->getType(); - if (objType == SceneObject::Type::Rigid - || objType == SceneObject::Type::FEMDeformable - || objType == SceneObject::Type::Pbd - || objType == SceneObject::Type::SPH) - { - obj->reset(); - } - } - - // Apply the geometry and apply maps to all the objects - for (auto obj : this->getSceneObjects()) - { - obj->updateGeometries(); + obj->reset(); } //\todo reset the timestep to the fixed default value when paused->run or reset @@ -365,25 +430,20 @@ Scene::advance() StopWatch wwt; wwt.start(); - advance(elapsedTime); + advance(m_elapsedTime); - elapsedTime = wwt.getTimeElapsed(StopWatch::TimeUnitType::seconds); + m_elapsedTime = wwt.getTimeElapsed(StopWatch::TimeUnitType::seconds); } void Scene::advance(const double dt) { - // PhysX update; move this to solver - auto physxScene = RigidBodyWorld::getInstance()->m_Scene; - physxScene->simulate(RigidBodyWorld::getInstance()->getTimeStep()); // TODO: update the time step - physxScene->fetchResults(true); - // Reset Contact forces to 0 for (auto obj : this->getSceneObjects()) { if (auto defObj = std::dynamic_pointer_cast<FeDeformableObject>(obj)) { - defObj->getContactForce().setConstant(0.0); + defObj->getFEMModel()->getContactForce().setConstant(0.0); } else if (auto collidingObj = std::dynamic_pointer_cast<CollidingObject>(obj)) { @@ -397,26 +457,10 @@ Scene::advance(const double dt) controller->updateControlledObjects(); } - // Update the static octree and perform collision detection for some collision pairs CollisionDetection::updateInternalOctreeAndDetectCollision(); - // Compute collision data per interaction pair - for (auto intPair : this->getCollisionGraph()->getInteractionPairList()) - { - intPair->computeCollisionData(); - } - - // Process collision data per interaction pair - for (auto intPair : this->getCollisionGraph()->getInteractionPairList()) - { - intPair->processCollisionData(); - } - - // Run the solvers - for (auto solvers : this->getSolvers()) - { - solvers->solve(); - } + // Execute the computational graph + m_taskGraphController->execute(); // Apply updated forces on device for (auto controller : this->getSceneObjectControllers()) @@ -424,39 +468,19 @@ Scene::advance(const double dt) controller->applyForces(); } - // Apply the geometry and apply maps to all the objects - for (auto obj : this->getSceneObjects()) - { - obj->updateGeometries(); - } - // Set the trackers of the scene object controllers to out-of-date for (auto controller : this->getSceneObjectControllers()) { controller->setTrackerToOutOfDate(); } - // Update time step size of the dynamic objects for (auto obj : this->getSceneObjects()) { - if (obj->getType() == SceneObject::Type::Pbd) - { - if (auto dynaObj = std::dynamic_pointer_cast<PbdObject>(obj)) - { - if (dynaObj->getDynamicalModel()->getTimeStepSizeType() == TimeSteppingType::RealTime) - { - dynaObj->getDynamicalModel()->setTimeStep(dt); - } - } - } - else if (obj->getType() == SceneObject::Type::FEMDeformable) + if (auto dynaObj = std::dynamic_pointer_cast<DynamicObject>(obj)) { - if (auto dynaObj = std::dynamic_pointer_cast<FeDeformableObject>(obj)) + if (dynaObj->getDynamicalModel()->getTimeStepSizeType() == TimeSteppingType::RealTime) { - if (dynaObj->getDynamicalModel()->getTimeStepSizeType() == TimeSteppingType::RealTime) - { - dynaObj->getDynamicalModel()->setTimeStep(dt); - } + dynaObj->getDynamicalModel()->setTimeStep(dt); } } } @@ -469,5 +493,42 @@ Scene::advance(const double dt) } this->setFPS(1.0 / dt); + + // If benchmarking enabled, produce a time table for each step + if (m_config->taskTimingEnabled) + { + lockComputeTimes(); + for (std::shared_ptr<TaskNode> node : m_taskGraph->getNodes()) + { + m_nodeComputeTimes[node->m_name] = node->m_computeTime; + } + unlockComputeTimes(); + } +} + +double +Scene::getElapsedTime(const std::string& stepName) const +{ + if (m_nodeComputeTimes.count(stepName) == 0) + { + LOG(WARNING) << "Tried to get elapsed time of nonexistent step. Is benchmarking enabled?"; + return 0.0; + } + else + { + return m_nodeComputeTimes.at(stepName); + } +} + +void +Scene::lockComputeTimes() +{ + computeTimesLock->lock(); +} + +void +Scene::unlockComputeTimes() +{ + computeTimesLock->unlock(); } } // imstk diff --git a/Source/Scene/imstkScene.h b/Source/Scene/imstkScene.h index b367f41caf415d267c1a56447c4755a104060697..097cfb7db2c8f6b48bffe399a3b1b81b7fdf0558 100644 --- a/Source/Scene/imstkScene.h +++ b/Source/Scene/imstkScene.h @@ -21,23 +21,26 @@ #pragma once -#include <unordered_map> -#include <thread> #include <atomic> +#include <functional> +#include <thread> +#include <unordered_map> #include <vector> namespace imstk { -class SceneObjectControllerBase; -class VisualModel; -class CameraController; -class DebugRenderGeometry; -class SceneObject; -class SolverBase; class Camera; -class IBLProbe; +class CameraController; class CollisionGraph; +class IBLProbe; class Light; +class SceneObject; +class SceneObjectControllerBase; +class TaskGraph; +class TaskGraphController; +class VisualModel; + +namespace ParallelUtils { class SpinLock; } enum class TimeSteppingPolicy { @@ -56,6 +59,18 @@ struct SceneConfig // Keep track of the fps for the scene bool trackFPS = false; + + // If off, tasks will run sequentially + bool taskParallelizationEnabled = true; + + // If on, elapsed times for computational steps will be reported in map + bool taskTimingEnabled = false; + + // If on, the task graph will be written to a file + bool writeTaskGraph = false; + + // If on, non functional nodes and redundant edges will be removed from final graph + bool graphReductionEnabled = true; }; /// @@ -69,26 +84,32 @@ template<class T> using NamedMap = std::unordered_map<std::string, std::shared_ptr<T>>; public: - /// /// \brief Constructor /// - explicit Scene(const std::string& name, std::shared_ptr<SceneConfig> config = std::make_shared<SceneConfig>()) : - m_name(name), - m_camera(std::make_shared<Camera>()), - m_collisionGraph(std::make_shared<CollisionGraph>()), - m_config(config) {} + explicit Scene(const std::string& name, std::shared_ptr<SceneConfig> config = std::make_shared<SceneConfig>()); /// /// \brief Destructor /// ~Scene(); +public: /// /// \brief Initialize the scene /// bool initialize(); + /// + /// \brief Setup the task graph, this completely rebuilds the graph + /// + void buildTaskGraph(); + + /// + /// \brief Intializes the graph after its in a built state + /// + void initTaskGraph(); + /// /// \brief Launch camera controller and other scene specific modules that need to run independently /// @@ -180,6 +201,11 @@ public: /// const std::string& getName() const; + /// + /// \brief Get the computational graph of the scene + /// + std::shared_ptr<TaskGraph> getTaskGraph() const { return m_taskGraph; } + /// /// \brief Get the camera for the scene /// @@ -190,16 +216,6 @@ public: /// std::shared_ptr<CollisionGraph> getCollisionGraph() const; - /// - /// \brief Get the vector of non-linear solvers - /// - const std::vector<std::shared_ptr<SolverBase>> getSolvers(); - - /// - /// \brief Add nonlinear solver to the scene - /// - void addNonlinearSolver(std::shared_ptr<SolverBase> solver); - /// /// \brief Add objects controllers /// @@ -224,7 +240,32 @@ public: /// /// \brief Get the elapsed time /// - double getElapsedTime() { return elapsedTime; } + double getElapsedTime() { return m_elapsedTime; } + + /// + /// \brief Get the elapsed time of a particular step + /// + double getElapsedTime(const std::string& stepName) const; + + /// + /// \brief Get the map of elapsed times + /// + const std::unordered_map<std::string, double>& getTaskComputeTimes() const { return m_nodeComputeTimes; } + + /// + /// \brief Lock the compute times resource + /// + void lockComputeTimes(); + + /// + /// \brief Unlock the compute times resource + /// + void unlockComputeTimes(); + + /// + /// \brief Called after compute graph is built, but before initialized + /// + void setTaskGraphConfigureCallback(std::function<void(Scene*)> callback) { this->m_postTaskGraphConfigureCallback = callback; } /// /// \brief Get the configuration @@ -236,19 +277,27 @@ protected: std::shared_ptr<SceneConfig> m_config; std::string m_name; ///> Name of the scene - NamedMap<SceneObject> m_sceneObjectsMap; - NamedMap<VisualModel> m_DebugRenderModelMap; - NamedMap<Light> m_lightsMap; - std::shared_ptr<IBLProbe> m_globalIBLProbe = nullptr; - std::shared_ptr<Camera> m_camera = nullptr; + NamedMap<SceneObject> m_sceneObjectsMap; + NamedMap<VisualModel> m_DebugRenderModelMap; + NamedMap<Light> m_lightsMap; + std::shared_ptr<IBLProbe> m_globalIBLProbe = nullptr; + + std::shared_ptr<Camera> m_camera = std::make_shared<Camera>(); + std::shared_ptr<CollisionGraph> m_collisionGraph = nullptr; - std::vector<std::shared_ptr<SolverBase>> m_solvers; ///> List of non-linear solvers std::vector<std::shared_ptr<SceneObjectControllerBase>> m_objectControllers; ///> List of object controllers std::vector<std::shared_ptr<CameraController>> m_cameraControllers; ///> List of camera controllers std::unordered_map<std::string, std::thread> m_threadMap; ///> - double m_fps = 0.0; - double elapsedTime = 0.0; + std::shared_ptr<TaskGraph> m_taskGraph = nullptr; ///> Computational graph + std::shared_ptr<TaskGraphController> m_taskGraphController = nullptr; ///> Controller for the computational graph + std::function<void(Scene*)> m_postTaskGraphConfigureCallback = nullptr; + + std::shared_ptr<ParallelUtils::SpinLock> computeTimesLock = nullptr; + std::unordered_map<std::string, double> m_nodeComputeTimes; ///> Map of ComputeNode names to elapsed times for benchmarking + + double m_fps = 0.0; + double m_elapsedTime = 0.0; bool m_isInitialized = false; diff --git a/Source/SceneEntities/CMakeLists.txt b/Source/SceneEntities/CMakeLists.txt index e17ebff50371bb55c64312c1a7dc53ea298eb6ea..b53941e54921a2c759e7711b3b179f3fd91ceb93 100644 --- a/Source/SceneEntities/CMakeLists.txt +++ b/Source/SceneEntities/CMakeLists.txt @@ -1,8 +1,17 @@ #----------------------------------------------------------------------------- # Create target #----------------------------------------------------------------------------- + +if (NOT ${${PROJECT_NAME}_USE_MODEL_REDUCTION}) + list(APPEND ExclusionFiles + Objects/imstkReducedFeDeformableObject.h + Objects/imstkReducedFeDeformableObject.cpp) +endif() + include(imstkAddLibrary) -imstk_add_library( SceneEntities +imstk_add_library(SceneEntities + EXCLUDE_FILES + ${ExclusionFiles} DEPENDS Common Geometry @@ -11,8 +20,7 @@ imstk_add_library( SceneEntities MeshIO Constraints Devices - DynamicalModels - ) + DynamicalModels) #----------------------------------------------------------------------------- # Testing diff --git a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.h b/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.h deleted file mode 100644 index e9bfb4de1444facae73261e35b38e69f08dac32b..0000000000000000000000000000000000000000 --- a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.h +++ /dev/null @@ -1,67 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include "imstkPbdObject.h" - -namespace imstk -{ -class Geometry; -class GeometryMap; - -/// -/// \class PbdRigidObject -/// -/// \brief -/// -class PbdRigidObject : public PbdObject -{ -public: - /// - /// \brief Constructor - /// - explicit PbdRigidObject(const std::string& name) : PbdObject(name) - { m_type = SceneObject::Type::Pbd; } - - /// - /// \brief Destructor - /// - ~PbdRigidObject() = default; - - /// - /// \brief - /// - virtual void integratePosition() { return; } - - /// - /// \brief - /// - virtual void integrateVelocity() { return; } - - /// - /// \brief - /// - virtual void updatePbdStates(); - -protected: -}; -} // imstk diff --git a/Source/SceneEntities/Objects/imstkCollidingObject.cpp b/Source/SceneEntities/Objects/imstkCollidingObject.cpp index cb07dc45a23d0f5e97954361081d3569e1aefc7a..39560d82a9fa651b6f45a80af711a34de92c39bd 100644 --- a/Source/SceneEntities/Objects/imstkCollidingObject.cpp +++ b/Source/SceneEntities/Objects/imstkCollidingObject.cpp @@ -27,19 +27,17 @@ namespace imstk bool CollidingObject::initialize() { - if (SceneObject::initialize()) + if (!SceneObject::initialize()) { - if (m_collidingToVisualMap) - { - m_collidingToVisualMap->initialize(); - } - - return true; + return false; } - else + + if (m_collidingToVisualMap) { - return false; + m_collidingToVisualMap->initialize(); } + + return true; } const std::shared_ptr<Geometry>& diff --git a/Source/SceneEntities/Objects/imstkCollidingObject.h b/Source/SceneEntities/Objects/imstkCollidingObject.h index dfe6e5e14c8c4fe4d11706088d08c6a8401a3417..512f1ab55de7aa72f44107a1909a17ba42b332d4 100644 --- a/Source/SceneEntities/Objects/imstkCollidingObject.h +++ b/Source/SceneEntities/Objects/imstkCollidingObject.h @@ -23,6 +23,7 @@ #include "imstkSceneObject.h" #include "imstkMath.h" +#include <unordered_map> namespace imstk { @@ -81,7 +82,6 @@ public: virtual bool initialize() override; protected: - std::shared_ptr<Geometry> m_collidingGeometry; ///> Geometry for collisions std::shared_ptr<GeometryMap> m_collidingToVisualMap; ///> Maps transformations to visual geometry Vec3d m_force = Vec3d::Zero(); diff --git a/Source/SceneEntities/Objects/imstkDeformableObject.cpp b/Source/SceneEntities/Objects/imstkDeformableObject.cpp index cd760dd8a30aaf2e6aac4662701484e53a028a3b..e4d1fb73f7da0a3df888484e3fa1bc0ce7d4ed8b 100644 --- a/Source/SceneEntities/Objects/imstkDeformableObject.cpp +++ b/Source/SceneEntities/Objects/imstkDeformableObject.cpp @@ -25,62 +25,19 @@ namespace imstk { -Vectord& -FeDeformableObject::getContactForce() -{ - CHECK(m_defModel != nullptr) << "deformation model pointer not valid DeformableObject::getContactForce()"; - - return m_defModel->getContactForce(); -} - bool FeDeformableObject::initialize() { - m_defModel = std::dynamic_pointer_cast<FEMDeformableBodyModel>(m_dynamicalModel); - if (m_defModel) - { - return DynamicObject::initialize(); - } - else + m_femModel = std::dynamic_pointer_cast<FEMDeformableBodyModel>(m_dynamicalModel); + if (m_femModel == nullptr) { LOG(FATAL) << "Dynamics pointer cast failure in DeformableObject::initialize()"; return false; } -} - -const Vectord& -FeDeformableObject::getDisplacements() const -{ - return m_defModel->getCurrentState()->getQ(); -} - -const Vectord& -FeDeformableObject::getPrevDisplacements() const -{ - return m_defModel->getPreviousState()->getQ(); -} -const Vectord& -FeDeformableObject::getVelocities() const -{ - return m_defModel->getCurrentState()->getQDot(); -} + DynamicObject::initialize(); + m_femModel->initialize(); -const Vectord& -FeDeformableObject::getPrevVelocities() const -{ - return m_defModel->getPreviousState()->getQDot(); -} - -const Vectord& -FeDeformableObject::getAccelerations() const -{ - return m_defModel->getCurrentState()->getQDotDot(); -} - -const Vectord& -FeDeformableObject::getPrevAccelerations() const -{ - return m_defModel->getPreviousState()->getQDotDot(); + return true; } } // imstk diff --git a/Source/SceneEntities/Objects/imstkDeformableObject.h b/Source/SceneEntities/Objects/imstkDeformableObject.h index b2e9119e252280e4ea5a73dbe11bd08501fc29db..26c2fbc7475063dc9a7e12d04bb718d9732c2eea 100644 --- a/Source/SceneEntities/Objects/imstkDeformableObject.h +++ b/Source/SceneEntities/Objects/imstkDeformableObject.h @@ -30,8 +30,6 @@ namespace imstk { -class Geometry; -class GeometryMap; class FEMDeformableBodyModel; /// @@ -42,70 +40,20 @@ class FEMDeformableBodyModel; class FeDeformableObject : public DynamicObject { public: - - /// - /// \brief Constructor - /// explicit FeDeformableObject(const std::string& name) : DynamicObject(name) { m_type = Type::FEMDeformable; } FeDeformableObject() = delete; - /// - /// \brief Destructor - /// ~FeDeformableObject() = default; +public: /// /// \brief Initialize the deformable object /// bool initialize() override; - /// - /// \brief Initialize the kinematic state of the body - /// - void initializeState(); - void initializeState(const Vectord& p, const Vectord& v); - - /// - /// \brief Set/Get dynamical model - /// - //void setDynamicalModel(std::shared_ptr<DynamicalModel<VectorizedState>> dynaDefModel) override; - - /// - /// \brief Get the vector that holds the contact forces - /// - Vectord& getContactForce(); - - /// - /// \brief Get the vector of current displacements - /// - const Vectord& getDisplacements() const; - - /// - /// \brief Get the vector of displacements from previous time step - /// - const Vectord& getPrevDisplacements() const; - - /// - /// \brief Get the vector of current velocities - /// - const Vectord& getVelocities() const; - - /// - /// \brief Get the vector of velocities from previous time step - /// - const Vectord& getPrevVelocities() const; - - /// - /// \brief Get the vector of current accelerations - /// - const Vectord& getAccelerations() const; - - /// - /// \brief Get the vector of accelerations from previous time step - /// - const Vectord& getPrevAccelerations() const; + std::shared_ptr<FEMDeformableBodyModel> getFEMModel() const { return m_femModel; } protected: - std::shared_ptr<FEMDeformableBodyModel> m_defModel; + std::shared_ptr<FEMDeformableBodyModel> m_femModel = nullptr; }; } // imstk diff --git a/Source/SceneEntities/Objects/imstkDynamicObject.cpp b/Source/SceneEntities/Objects/imstkDynamicObject.cpp index 50d28d7b2ed4f744a3bcbd752cc506f5242af72a..c6bf8e64caa458d01b2e9b98fd575f00602f7335 100644 --- a/Source/SceneEntities/Objects/imstkDynamicObject.cpp +++ b/Source/SceneEntities/Objects/imstkDynamicObject.cpp @@ -20,9 +20,10 @@ =========================================================================*/ #include "imstkDynamicObject.h" -#include "imstkGeometryMap.h" #include "imstkAbstractDynamicalModel.h" +#include "imstkGeometryMap.h" #include "imstkLogger.h" +#include "imstkTaskGraph.h" namespace imstk { @@ -61,6 +62,17 @@ DynamicObject::updateGeometries() } } +void +DynamicObject::updatePhysicsGeometry() +{ + m_dynamicalModel->updatePhysicsGeometry(); + + if (m_physicsToCollidingGeomMap) + { + m_physicsToCollidingGeomMap->apply(); + } +} + bool DynamicObject::initialize() { @@ -84,6 +96,16 @@ DynamicObject::initialize() } } +void +DynamicObject::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) +{ + // Copy, sum, and connect the model graph to nest within this graph + m_taskGraph->addEdge(source, getUpdateNode()); + m_dynamicalModel->initGraphEdges(); + m_taskGraph->nestGraph(m_dynamicalModel->getTaskGraph(), getUpdateNode(), getUpdateGeometryNode()); + m_taskGraph->addEdge(getUpdateGeometryNode(), sink); +} + void DynamicObject::reset() { diff --git a/Source/SceneEntities/Objects/imstkDynamicObject.h b/Source/SceneEntities/Objects/imstkDynamicObject.h index 0535d059ab62f7389f3b3d3dda621778552d056b..58a6bb7930dc3030c91312c81c700b06106f8181 100644 --- a/Source/SceneEntities/Objects/imstkDynamicObject.h +++ b/Source/SceneEntities/Objects/imstkDynamicObject.h @@ -37,7 +37,6 @@ class AbstractDynamicalModel; class DynamicObject : public CollidingObject { public: - /// /// \brief Destructor /// @@ -82,16 +81,27 @@ public: /// void updateGeometries() final; + /// + /// \brief Update only the physics geometry and apply collision map + /// + void updatePhysicsGeometry(); + /// /// \brief Initialize the scene object /// virtual bool initialize() override; /// - /// \brief Reset the dynamic object to its initial state + /// \brief Reset the dynamic object by reseting the respective DynamicalModel and Geometry /// virtual void reset() override; +protected: + /// + /// \brief Setup connectivity of compute graph + /// + virtual void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) override; + protected: /// @@ -99,12 +109,12 @@ protected: /// explicit DynamicObject(const std::string& name) : CollidingObject(name) {} - std::shared_ptr<AbstractDynamicalModel> m_dynamicalModel; ///> Dynamical model - std::shared_ptr<Geometry> m_physicsGeometry; ///> Geometry used for Physics + std::shared_ptr<AbstractDynamicalModel> m_dynamicalModel = nullptr; ///> Dynamical model + std::shared_ptr<Geometry> m_physicsGeometry = nullptr; ///> Geometry used for Physics - //Maps - std::shared_ptr<GeometryMap> m_physicsToCollidingGeomMap; ///> Maps from Physics to collision geometry - std::shared_ptr<GeometryMap> m_physicsToVisualGeomMap; ///> Maps from Physics to visual geometry + // Maps + std::shared_ptr<GeometryMap> m_physicsToCollidingGeomMap = nullptr; ///> Maps from Physics to collision geometry + std::shared_ptr<GeometryMap> m_physicsToVisualGeomMap = nullptr; ///> Maps from Physics to visual geometry bool m_updateVisualFromPhysicsGeometry = true; ///> Defines if visual is updated from colliding mapping or physics mapping }; } // imstk diff --git a/Source/SceneEntities/Objects/imstkPbdObject.cpp b/Source/SceneEntities/Objects/imstkPbdObject.cpp index ae380e2f71d42a60afbbf080900b0f7d70ad8782..731aa243662478c8d175300cbc08b51910aef344 100644 --- a/Source/SceneEntities/Objects/imstkPbdObject.cpp +++ b/Source/SceneEntities/Objects/imstkPbdObject.cpp @@ -20,53 +20,31 @@ =========================================================================*/ #include "imstkPbdObject.h" -#include "imstkGeometryMap.h" -#include "imstkPbdModel.h" #include "imstkLogger.h" +#include "imstkPbdModel.h" namespace imstk { -bool -PbdObject::initialize() +std::shared_ptr<PbdModel> +PbdObject::getPbdModel() { m_pbdModel = std::dynamic_pointer_cast<PbdModel>(m_dynamicalModel); + return m_pbdModel; +}; - CHECK(m_pbdModel != nullptr) << "Dynamics pointer cast failure in PbdObject::initialize()"; - - return DynamicObject::initialize(); -} - -void -PbdObject::integratePosition() -{ - if (m_pbdModel && m_pbdModel->hasConstraints()) - { - m_pbdModel->integratePosition(); - } -} - -void -PbdObject::updateVelocity() +bool +PbdObject::initialize() { - if (m_pbdModel && m_pbdModel->hasConstraints()) + m_pbdModel = std::dynamic_pointer_cast<PbdModel>(m_dynamicalModel); + if (m_pbdModel == nullptr) { - m_pbdModel->updateVelocity(); + LOG(FATAL) << "Dynamics pointer cast failure in PbdObject::initialize()"; + return false; } -} -void -PbdObject::solveConstraints() -{ - if (m_pbdModel && m_pbdModel->hasConstraints()) - { - m_pbdModel->projectConstraints(); - } -} + DynamicObject::initialize(); + m_pbdModel->initialize(); -void -PbdObject::reset() -{ - DynamicObject::reset(); - this->updateVelocity(); + return true; } } //imstk diff --git a/Source/SceneEntities/Objects/imstkPbdObject.h b/Source/SceneEntities/Objects/imstkPbdObject.h index 17feda4e8ffffc9326316bba6045e9ca91b316bd..0ee18a27ce575a4c5a19b7b5e4d9139fa2964722 100644 --- a/Source/SceneEntities/Objects/imstkPbdObject.h +++ b/Source/SceneEntities/Objects/imstkPbdObject.h @@ -25,22 +25,17 @@ namespace imstk { -class Geometry; -class GeometryMap; class PbdModel; /// /// \class PbdObject /// /// \brief Base class for scene objects that move and/or deform under position -/// based dynamics formulation +/// based dynamics formulation, implements the PbdModel and PbdSolver /// class PbdObject : public DynamicObject { public: - /// - /// \brief Constructor - /// explicit PbdObject(const std::string& name) : DynamicObject(name) { m_type = SceneObject::Type::Pbd; @@ -48,37 +43,20 @@ public: PbdObject() = delete; - /// - /// \brief Destructor - /// ~PbdObject() override = default; +public: /// - /// \brief Initialize the pbd scene object - /// - bool initialize() override; - - /// - /// \brief Update the position based on Verlet time stepping rule - /// - virtual void integratePosition(); - - /// - /// \brief Update the velocity - /// - virtual void updateVelocity(); - - /// - /// \brief Solve the pbd constraints by projection + /// \biref Get the Pbd model of the object /// - virtual void solveConstraints(); + std::shared_ptr<PbdModel> getPbdModel(); /// - /// \brief Reset the PBD object to its initial state + /// \brief Initialize the pbd scene object /// - void reset() override; + bool initialize() override; protected: - std::shared_ptr<PbdModel> m_pbdModel; ///> PBD mathematical model + std::shared_ptr<PbdModel> m_pbdModel = nullptr; ///> PBD mathematical model }; } // imstk diff --git a/Source/SceneEntities/Objects/imstkReducedFeDeformableObject.cpp b/Source/SceneEntities/Objects/imstkReducedFeDeformableObject.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93830c7de4616392e5f9418cdf44f89189c2bd96 --- /dev/null +++ b/Source/SceneEntities/Objects/imstkReducedFeDeformableObject.cpp @@ -0,0 +1,87 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkReducedFeDeformableObject.h" +#include "imstkReducedStVKBodyModel.h" +#include "imstkLogger.h" + +namespace imstk +{ +Vectord& +ReducedFeDeformableObject::getContactForce() +{ + CHECK(m_defModel != nullptr) << "deformation model pointer not valid DeformableObject::getContactForce()"; + + return m_defModel->getContactForce(); +} + +bool +ReducedFeDeformableObject::initialize() +{ + m_defModel = std::dynamic_pointer_cast<ReducedStVK>(m_dynamicalModel); + + if (m_defModel) + { + return DynamicObject::initialize(); + } + else + { + LOG(FATAL) << "Dynamics pointer cast failure in DeformableObject::initialize()"; + return false; + } +} + +const Vectord& +ReducedFeDeformableObject::getDisplacements() const +{ + return m_defModel->getCurrentState()->getQ(); +} + +const Vectord& +ReducedFeDeformableObject::getPrevDisplacements() const +{ + return m_defModel->getPreviousState()->getQ(); +} + +const Vectord& +ReducedFeDeformableObject::getVelocities() const +{ + return m_defModel->getCurrentState()->getQDot(); +} + +const Vectord& +ReducedFeDeformableObject::getPrevVelocities() const +{ + return m_defModel->getPreviousState()->getQDot(); +} + +const Vectord& +ReducedFeDeformableObject::getAccelerations() const +{ + return m_defModel->getCurrentState()->getQDotDot(); +} + +const Vectord& +ReducedFeDeformableObject::getPrevAccelerations() const +{ + return m_defModel->getPreviousState()->getQDotDot(); +} +} // imstk diff --git a/Source/SceneEntities/Objects/imstkReducedFeDeformableObject.h b/Source/SceneEntities/Objects/imstkReducedFeDeformableObject.h new file mode 100644 index 0000000000000000000000000000000000000000..5f577e2e31e7c0ac806082921422e22baa44a3be --- /dev/null +++ b/Source/SceneEntities/Objects/imstkReducedFeDeformableObject.h @@ -0,0 +1,111 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <memory> +#include <string> + +// imstk +#include "imstkDynamicObject.h" +#include "imstkMath.h" + +namespace imstk +{ +class Geometry; +class GeometryMap; +class ReducedStVK; + +/// +/// \class DeformableObject +/// +/// \brief Scene objects that can deform +/// +class ReducedFeDeformableObject : public DynamicObject +{ +public: + + /// + /// \brief Constructor + /// + explicit ReducedFeDeformableObject(const std::string& name) : DynamicObject(name) { m_type = Type::ReducedFEMDeformable; } + ReducedFeDeformableObject() = delete; + + /// + /// \brief Destructor + /// + ~ReducedFeDeformableObject() = default; + + /// + /// \brief Initialize the deformable object + /// + bool initialize() override; + + /// + /// \brief Initialize the kinematic state of the body + /// + void initializeState(); + void initializeState(const Vectord& p, const Vectord& v); + + /// + /// \brief Set/Get dynamical model + /// + //void setDynamicalModel(std::shared_ptr<DynamicalModel<VectorizedState>> dynaDefModel) override; + + /// + /// \brief Get the vector that holds the contact forces + /// + Vectord& getContactForce(); + + /// + /// \brief Get the vector of current displacements + /// + const Vectord& getDisplacements() const; + + /// + /// \brief Get the vector of displacements from previous time step + /// + const Vectord& getPrevDisplacements() const; + + /// + /// \brief Get the vector of current velocities + /// + const Vectord& getVelocities() const; + + /// + /// \brief Get the vector of velocities from previous time step + /// + const Vectord& getPrevVelocities() const; + + /// + /// \brief Get the vector of current accelerations + /// + const Vectord& getAccelerations() const; + + /// + /// \brief Get the vector of accelerations from previous time step + /// + const Vectord& getPrevAccelerations() const; + +protected: + std::shared_ptr<ReducedStVK> m_defModel = nullptr; +}; +} // imstk diff --git a/Source/SceneEntities/Objects/imstkRigidObject.cpp b/Source/SceneEntities/Objects/imstkRigidObject.cpp index 407520854b7fb416a8c924a1d2b758b6a99bd8c6..fa2ebba781ae404919be4cf353ecb80b111c0cd0 100644 --- a/Source/SceneEntities/Objects/imstkRigidObject.cpp +++ b/Source/SceneEntities/Objects/imstkRigidObject.cpp @@ -18,28 +18,32 @@ limitations under the License. =========================================================================*/ + #include "imstkRigidObject.h" #include "imstkLogger.h" +#include "imstkRigidBodyModel.h" namespace imstk { bool RigidObject::initialize() { - auto m_rigidBodyModel = std::dynamic_pointer_cast<RigidBodyModel>(m_dynamicalModel); - if (m_rigidBodyModel) - { - /*if (!m_rigidBodyModel->getRigidBodyWorld()) - { - LOG(WARNING) << "RigidObject::initialize() - Rigid body world not specified!"; - return false; - }*/ - return DynamicObject::initialize(); - } - else + m_rigidBodyModel = std::dynamic_pointer_cast<RigidBodyModel>(m_dynamicalModel); + if (m_rigidBodyModel == nullptr) { - LOG(WARNING) << "RigidObject::initialize() - Dynamics pointer cast failure"; + LOG(FATAL) << "Dynamics pointer cast failure in RigidObject::initialize()"; return false; } + + DynamicObject::initialize(); + m_rigidBodyModel->initialize(); + + return true; +} + +void +RigidObject::addForce(const Vec3d& force, const Vec3d& pos, bool wakeup) +{ + m_rigidBodyModel->addForce(force, pos, wakeup); } } // imstk diff --git a/Source/SceneEntities/Objects/imstkRigidObject.h b/Source/SceneEntities/Objects/imstkRigidObject.h index 7e0ff3a2810f87506bc782d6108e36521b841abb..eb24b27f24ab84c3a3ec4b9f9703d5ffd29efca8 100644 --- a/Source/SceneEntities/Objects/imstkRigidObject.h +++ b/Source/SceneEntities/Objects/imstkRigidObject.h @@ -21,12 +21,12 @@ #pragma once -// imstk #include "imstkDynamicObject.h" -#include "imstkRigidBodyModel.h" namespace imstk { +class RigidBodyModel; + /// /// \class RigidObject /// @@ -35,20 +35,14 @@ namespace imstk class RigidObject : public DynamicObject { public: - - /// - /// \brief Constructor - /// explicit RigidObject(const std::string& name) : DynamicObject(name) { m_type = Type::Rigid; } - /// - /// \brief Destructor - /// ~RigidObject() = default; +public: /// /// \brief Initialize the rigid scene object /// @@ -57,24 +51,11 @@ public: /// /// \brief Add local force at a position relative to object /// - void addForce(const Vec3d& force, const Vec3d& pos, bool wakeup = true) - { - getRigidBodyModel()->addForce(force, pos, wakeup); - } + void addForce(const Vec3d& force, const Vec3d& pos, bool wakeup = true); - /// - /// \brief Get/Set rigid body model - /// - void setRigidBodyModel(std::shared_ptr<RigidBodyModel> rbModel) { m_dynamicalModel = rbModel; }; - std::shared_ptr<RigidBodyModel> getRigidBodyModel() const { return std::dynamic_pointer_cast<RigidBodyModel>(m_dynamicalModel); }; - - void reset() override - { - m_dynamicalModel->resetToInitialState(); - this->updateGeometries(); - } + std::shared_ptr<RigidBodyModel> getRigidBodyModel() const { return m_rigidBodyModel; } protected: - //std::shared_ptr<RigidBodyModel> m_rigidBodyModel; ///> PBD mathematical model + std::shared_ptr<RigidBodyModel> m_rigidBodyModel; }; } // imstk \ No newline at end of file diff --git a/Source/SceneEntities/Objects/imstkSPHObject.cpp b/Source/SceneEntities/Objects/imstkSPHObject.cpp index f5896db40469425e42833a82ca5ab554a6c1645a..9be7701c33be9051e53d9ece65162713da930191 100644 --- a/Source/SceneEntities/Objects/imstkSPHObject.cpp +++ b/Source/SceneEntities/Objects/imstkSPHObject.cpp @@ -26,15 +26,22 @@ namespace imstk { SPHObject::SPHObject(const std::string& name) : DynamicObject(name) { - this->m_type = SceneObject::Type::SPH; + this->m_type = Type::SPH; } bool SPHObject::initialize() { - m_SPHModel = std::dynamic_pointer_cast<SPHModel>(this->m_dynamicalModel); - LOG_IF(FATAL, (!m_SPHModel)) << "Invalid SPH model"; + m_SPHModel = std::dynamic_pointer_cast<SPHModel>(m_dynamicalModel); + if (m_SPHModel == nullptr) + { + LOG(FATAL) << "Dynamics pointer cast failure in SPHObject::initialize()"; + return false; + } - return m_SPHModel->initialize(); + DynamicObject::initialize(); + m_SPHModel->initialize(); + + return true; } } // end namespace imstk diff --git a/Source/SceneEntities/Objects/imstkSPHObject.h b/Source/SceneEntities/Objects/imstkSPHObject.h index c123cadb9c953c888d27de7b2e78cabf2ec9e593..d6f8936633e1e84e0043e18ae2c87b4cdc604987 100644 --- a/Source/SceneEntities/Objects/imstkSPHObject.h +++ b/Source/SceneEntities/Objects/imstkSPHObject.h @@ -30,22 +30,17 @@ class SPHModel; /// /// \class SPHObject /// -/// \brief Base class for scene objects that move and/or deform under position -/// based dynamics formulation +/// \brief Base class for scene objects that move and/or deform under +/// smooth particle hydrodynamics /// class SPHObject : public DynamicObject { public: - /// - /// \brief Constructor - /// explicit SPHObject(const std::string& name); - /// - /// \brief Destructor - /// virtual ~SPHObject() override = default; +public: /// /// \brief Initialize the SPH scene object /// @@ -54,9 +49,9 @@ public: /// /// \brief Get the SPH model of the object /// - const std::shared_ptr<SPHModel>& getSPHModel() const { assert(m_SPHModel); return m_SPHModel; } + std::shared_ptr<SPHModel> getSPHModel() const { assert(m_SPHModel); return m_SPHModel; } protected: - std::shared_ptr<SPHModel> m_SPHModel; + std::shared_ptr<SPHModel> m_SPHModel = nullptr; }; } // end namespace imstk diff --git a/Source/SceneEntities/Objects/imstkSceneObject.cpp b/Source/SceneEntities/Objects/imstkSceneObject.cpp index 65c82cc3cbef5701b0c6e0177f48a571c0d4f9ba..34aaad5cdab3eb2bb8f9127175c61946f598b847 100644 --- a/Source/SceneEntities/Objects/imstkSceneObject.cpp +++ b/Source/SceneEntities/Objects/imstkSceneObject.cpp @@ -20,12 +20,16 @@ =========================================================================*/ #include "imstkSceneObject.h" +#include "imstkTaskGraph.h" namespace imstk { -SceneObject::SceneObject(const std::string& name) : m_name(name), SceneEntity() +SceneObject::SceneObject(const std::string& name) : + m_type(Type::Visual), m_name(name), + m_taskGraph(std::make_shared<TaskGraph>("SceneObject_" + name + "_Source", "SceneObject_" + name + "_Sink")), SceneEntity() { - m_type = Type::Visual; + m_updateNode = m_taskGraph->addFunction("SceneObject_" + name + "_Update", std::bind(&SceneObject::update, this)); + m_updateGeometryNode = m_taskGraph->addFunction("SceneObject_" + name + "_UpdateGeometry", std::bind(&SceneObject::updateGeometries, this)); } std::shared_ptr<Geometry> @@ -51,57 +55,18 @@ SceneObject::setVisualGeometry(std::shared_ptr<Geometry> geometry) } } -std::shared_ptr<Geometry> -SceneObject::getMasterGeometry() const -{ - return this->getVisualGeometry(); -} - -std::shared_ptr<VisualModel> -SceneObject::getVisualModel(unsigned int index) -{ - return m_visualModels[index]; -} - void -SceneObject::addVisualModel(std::shared_ptr<VisualModel> visualModel) -{ - m_visualModels.push_back(visualModel); -} - -const std::vector<std::shared_ptr<VisualModel>>& -SceneObject::getVisualModels() -{ - return m_visualModels; -} - -size_t -SceneObject::getNumVisualModels() -{ - return m_visualModels.size(); -} - -const SceneObject::Type& -SceneObject::getType() const -{ - return m_type; -} - -void -SceneObject::setType(SceneObject::Type type) -{ - m_type = type; -} - -const std::string& -SceneObject::getName() const +SceneObject::initGraphEdges() { - return m_name; + m_taskGraph->clearEdges(); + initGraphEdges(m_taskGraph->getSource(), m_taskGraph->getSink()); } void -SceneObject::setName(const std::string& name) +SceneObject::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink) { - m_name = name; + m_taskGraph->addEdge(source, m_updateNode); + m_taskGraph->addEdge(m_updateNode, m_updateGeometryNode); + m_taskGraph->addEdge(m_updateGeometryNode, sink); } } // imstk diff --git a/Source/SceneEntities/Objects/imstkSceneObject.h b/Source/SceneEntities/Objects/imstkSceneObject.h index cb6ce62df8f4eda987eec780d64267b769673f61..d848c62e829de94f0fcd7f541694e28112e77fe9 100644 --- a/Source/SceneEntities/Objects/imstkSceneObject.h +++ b/Source/SceneEntities/Objects/imstkSceneObject.h @@ -29,8 +29,10 @@ namespace imstk { -class Geometry; class DeviceClient; +class Geometry; +class TaskGraph; +class TaskNode; /// /// \class SceneObject @@ -48,6 +50,7 @@ public: Colliding, Rigid, FEMDeformable, + ReducedFEMDeformable, Pbd, SPH }; @@ -62,16 +65,22 @@ public: /// virtual ~SceneObject() = default; +public: /// /// \brief Get the type of the object /// - const Type& getType() const; + const Type& getType() const { return m_type; } + + /// + /// \brief Get the computational graph + /// + std::shared_ptr<TaskGraph> getTaskGraph() const { return m_taskGraph; } /// /// \brief Get/Set the custom name of the scene object /// - const std::string& getName() const; - void setName(const std::string& name); + const std::string& getName() const { return m_name; } + void setName(const std::string& name) { m_name = name; } /// /// \brief DEPRECATED: Get/Set geometry used for viewing @@ -84,23 +93,38 @@ public: /// /// \brief Get/add visual model /// - std::shared_ptr<VisualModel> getVisualModel(unsigned int index=0); - void addVisualModel(std::shared_ptr<VisualModel> visualModel); + std::shared_ptr<VisualModel> getVisualModel(unsigned int index) { return m_visualModels[index]; } + void addVisualModel(std::shared_ptr<VisualModel> visualModel) { m_visualModels.push_back(visualModel); } /// /// \brief Get all visual models /// - const std::vector<std::shared_ptr<VisualModel>>& getVisualModels(); + const std::vector<std::shared_ptr<VisualModel>>& getVisualModels() { return m_visualModels; } /// /// \brief Get number of visual models /// - size_t getNumVisualModels(); + size_t getNumVisualModels() { return m_visualModels.size(); } /// /// \brief Get the master geometry /// - virtual std::shared_ptr<Geometry> getMasterGeometry() const; + virtual std::shared_ptr<Geometry> getMasterGeometry() const { return this->getVisualGeometry(); } + + /// + /// \brief Returns the computational node for updating + /// + std::shared_ptr<TaskNode> getUpdateNode() const { return m_updateNode; } + + /// + /// \brief Returns the computational node for updating geometry + /// + std::shared_ptr<TaskNode> getUpdateGeometryNode() const { return m_updateGeometryNode; } + + /// + /// \brief + /// + virtual void update() { } /// /// \brief @@ -112,20 +136,32 @@ public: /// virtual bool initialize() { return true; } + /// + /// \brief Initializes the edges of the SceneObject's computational graph + /// + void initGraphEdges(); + /// /// \brief /// - virtual void reset() {} + virtual void reset() { } protected: /// - /// \brief Assigns the type of the object + /// \brief Setup connectivity of the compute graph /// - void setType(Type type); + virtual void initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink); +protected: Type m_type; ///> Type of the scene object std::string m_name; ///> Custom name of the scene object std::vector<std::shared_ptr<VisualModel>> m_visualModels; ///> Visual objects for rendering + std::shared_ptr<TaskGraph> m_taskGraph = nullptr; ///> Computational Graph + +private: + // Dissallow reassignment of these in subclasses + std::shared_ptr<TaskNode> m_updateNode = nullptr; + std::shared_ptr<TaskNode> m_updateGeometryNode = nullptr; }; using VisualObject = SceneObject; diff --git a/Source/SimulationManager/VTKRenderer/imstkOpenVRCommand.cpp b/Source/SimulationManager/VTKRenderer/imstkOpenVRCommand.cpp index 04c24daf515ba8e005d3cbe73fc8167a9c0c15eb..2ba7f86c8b2cc2a950e5bf49e5b43eb856b14211 100644 --- a/Source/SimulationManager/VTKRenderer/imstkOpenVRCommand.cpp +++ b/Source/SimulationManager/VTKRenderer/imstkOpenVRCommand.cpp @@ -26,7 +26,6 @@ limitations under the License. #include "imstkOpenVRCommand.h" #include "imstkSimulationManager.h" #include "VTKRenderer/imstkVTKRenderer.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" namespace imstk diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyle.cpp b/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyle.cpp index 4dbbe40f9f620db31d77c151b640ebc0b2f895d9..78a57f8beb24b49ad386afc5ebea67b14c728be7 100644 --- a/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyle.cpp +++ b/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyle.cpp @@ -23,7 +23,6 @@ #include "imstkVTKInteractorStyle.h" #include "imstkSimulationManager.h" #include "imstkVTKTextStatusManager.h" -#include "imstkCollisionGraph.h" #include "imstkVTKRenderer.h" #include "imstkCamera.h" #include "imstkScene.h" diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKTextStatusManager.cpp b/Source/SimulationManager/VTKRenderer/imstkVTKTextStatusManager.cpp index 2f5ca2738730ab2a61be419e2eb3a8416de834f1..102cda91fc654ef868e3504f74ad45319ce0d594 100644 --- a/Source/SimulationManager/VTKRenderer/imstkVTKTextStatusManager.cpp +++ b/Source/SimulationManager/VTKRenderer/imstkVTKTextStatusManager.cpp @@ -22,7 +22,6 @@ #include "imstkSimulationManager.h" #include "imstkVTKTextStatusManager.h" #include "imstkVTKInteractorStyle.h" -#include "imstkCollisionGraph.h" #include "imstkLogger.h" #include "imstkCamera.h" diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp b/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp index 3f4052a82501e7040fc5d14cef6a1bcfcea1b76c..e53ca868bf6ea18f80a3479c8f6749eae1ae272e 100644 --- a/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp +++ b/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp @@ -22,7 +22,6 @@ #include "imstkVTKViewer.h" #include "imstkVTKRenderDelegate.h" #include "imstkVTKInteractorStyle.h" -#include "imstkCollisionGraph.h" #include "imstkVTKRenderer.h" #include "imstkCamera.h" #include "imstkLogger.h" @@ -31,12 +30,15 @@ #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> +#include <vtkCallbackCommand.h> + +#include "imstkParallelUtils.h" namespace imstk { -VTKViewer::VTKViewer(SimulationManager* manager /*= nullptr*/, bool enableVR /*= false*/) +VTKViewer::VTKViewer(SimulationManager* manager /*= nullptr*/, bool enableVR /*= false*/) : + m_enableVR(enableVR) { - m_enableVR = enableVR; // init render window / interactor / command based // depending on if we enable VR or not if (!m_enableVR) @@ -76,6 +78,11 @@ VTKViewer::VTKViewer(SimulationManager* manager /*= nullptr*/, bool enableVR /*= m_vtkRenderWindow->AddObserver(vtkCommand::StartEvent, m_openVRCommand, 1.0); } #endif + + // Setup callback for timer on the interactor + timerCallbackCommand = vtkSmartPointer<vtkCallbackCommand>::New(); + timerCallbackCommand->SetCallback(timerCallback); + timerCallbackCommand->SetClientData(this); } void @@ -181,6 +188,16 @@ VTKViewer::startRenderingLoop() { m_vtkRenderWindow->GetInteractor()->Initialize(); m_vtkRenderWindow->GetInteractor()->CreateOneShotTimer(0); + + // If the Scene wants benchmarking hookup timer to update the table + auto vtkRen = std::dynamic_pointer_cast<VTKRenderer>(getActiveRenderer()); + vtkRen->setTimeTableVisibility(m_activeScene->getConfig()->taskTimingEnabled); + if (m_activeScene->getConfig()->taskTimingEnabled) + { + m_vtkRenderWindow->GetInteractor()->AddObserver(vtkCommand::TimerEvent, timerCallbackCommand); + m_vtkRenderWindow->GetInteractor()->CreateRepeatingTimer(500); + } + m_vtkRenderWindow->SetWindowName(m_config->m_windowName.c_str()); m_vtkRenderWindow->GetInteractor()->Start(); m_vtkRenderWindow->GetInteractor()->DestroyTimer(); @@ -236,4 +253,18 @@ VTKViewer::getTextStatusManager() { return m_vtkInteractorStyle->getTextStatusManager(); } + +void +VTKViewer::timerCallback(vtkObject* caller, long unsigned int vtkNotUsed(eventId), void* clientData, void* vtkNotUsed(callData)) +{ + VTKViewer* self = static_cast<VTKViewer*>(clientData); + + if (self->getActiveScene()->getConfig()->taskTimingEnabled) + { + auto vtkRen = std::dynamic_pointer_cast<VTKRenderer>(self->getActiveRenderer()); + self->getActiveScene()->lockComputeTimes(); + vtkRen->setTimeTable(self->getActiveScene()->getTaskComputeTimes()); + self->getActiveScene()->unlockComputeTimes(); + } +} } // imstk diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKViewer.h b/Source/SimulationManager/VTKRenderer/imstkVTKViewer.h index 9ea6a5650cdb293ee61f84e1ff003b091204e291..812d7fd829ca9b73dd460a33a2753f95e784ef02 100644 --- a/Source/SimulationManager/VTKRenderer/imstkVTKViewer.h +++ b/Source/SimulationManager/VTKRenderer/imstkVTKViewer.h @@ -32,6 +32,7 @@ #include "vtkOpenVRRenderWindowInteractor.h" #endif +class vtkCallbackCommand; class vtkRenderWindow; namespace imstk @@ -109,10 +110,14 @@ public: const std::shared_ptr<VTKTextStatusManager>& getTextStatusManager(); protected: + static void timerCallback(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData); + vtkSmartPointer<vtkRenderWindow> m_vtkRenderWindow; std::shared_ptr<VTKInteractorStyle> m_vtkInteractorStyle; bool m_enableVR; + vtkSmartPointer<vtkCallbackCommand> timerCallbackCommand; + #ifdef iMSTK_ENABLE_VR vtkSmartPointer<OpenVRCommand> m_openVRCommand; #endif diff --git a/Source/SimulationManager/imstkSceneManager.cpp b/Source/SimulationManager/imstkSceneManager.cpp index 452fc8e52a21a7cd5ff31a1448f88b5117b9c501..d77823acb907d050d66349eebbec1c560cbd7531 100644 --- a/Source/SimulationManager/imstkSceneManager.cpp +++ b/Source/SimulationManager/imstkSceneManager.cpp @@ -23,7 +23,6 @@ #include "imstkScene.h" #include "imstkSceneManager.h" //#include "imstkCameraController.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" namespace imstk diff --git a/Source/SimulationManager/imstkSimulationManager.cpp b/Source/SimulationManager/imstkSimulationManager.cpp index a6b72f5cd23717e526fa4c3bcea612ce09516338..fa0caca444250f4b1ec44cb9ee0a90f32468427f 100644 --- a/Source/SimulationManager/imstkSimulationManager.cpp +++ b/Source/SimulationManager/imstkSimulationManager.cpp @@ -20,19 +20,17 @@ =========================================================================*/ #include "imstkSimulationManager.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" -#include "imstkThreadManager.h" -#include "imstkSceneManager.h" -#include "imstkViewer.h" -#include "imstkTimer.h" #include "imstkLogger.h" #include "imstkScene.h" +#include "imstkSceneManager.h" +#include "imstkThreadManager.h" +#include "imstkTimer.h" +#include "imstkViewer.h" +#include "imstkVTKRenderer.h" #include <string> -#include "imstkLogger.h" - namespace imstk { SimulationManager::SimulationManager(const std::shared_ptr<SimManagerConfig> config) : m_config(config) diff --git a/Source/SimulationManager/imstkViewer.cpp b/Source/SimulationManager/imstkViewer.cpp index d12ea424849953579acedf545871886a03b35b42..d19cf8d536069d16a86f983fde80cb1f6e1211e5 100644 --- a/Source/SimulationManager/imstkViewer.cpp +++ b/Source/SimulationManager/imstkViewer.cpp @@ -21,7 +21,6 @@ #include "imstkViewer.h" #include "imstkScene.h" -#include "imstkCollisionGraph.h" #include "imstkCamera.h" #include "imstkLogger.h" #include "imstkScreenCaptureUtility.h" diff --git a/Source/Solvers/CMakeLists.txt b/Source/Solvers/CMakeLists.txt index 14b17c8a3dd2ecff8eb109f6dc8d1c4efdcd8a50..9f45108fb8cb52421166d8cfc28e347a680b2927 100644 --- a/Source/Solvers/CMakeLists.txt +++ b/Source/Solvers/CMakeLists.txt @@ -5,7 +5,6 @@ include(imstkAddLibrary) imstk_add_library( Solvers DEPENDS Constraints - SceneEntities ) #----------------------------------------------------------------------------- diff --git a/Source/Solvers/imstkDirectLinearSolver.cpp b/Source/Solvers/imstkDirectLinearSolver.cpp index 77b4018ee1db170783bed698d08968edaaf57dce..1b82a4eb8a063a23f78be90caf956db0cad20986 100644 --- a/Source/Solvers/imstkDirectLinearSolver.cpp +++ b/Source/Solvers/imstkDirectLinearSolver.cpp @@ -59,14 +59,20 @@ setSystem(std::shared_ptr<LinearSystem<SparseMatrixd>> newSystem) void DirectLinearSolver<SparseMatrixd>::solve(const Vectord& rhs, Vectord& x) { - if(!m_linearSystem) LOG(FATAL) << "Linear system has not been set"; + if (!m_linearSystem) + { + LOG(FATAL) << "Linear system has not been set"; + } x = m_solver.solve(rhs); } void DirectLinearSolver<SparseMatrixd>::solve(Vectord& x) { - if(!m_linearSystem) LOG(FATAL) << "Linear system has not been set"; + if (!m_linearSystem) + { + LOG(FATAL) << "Linear system has not been set"; + } x.setZero(); auto b = m_linearSystem->getRHSVector(); @@ -76,14 +82,20 @@ DirectLinearSolver<SparseMatrixd>::solve(Vectord& x) void DirectLinearSolver<Matrixd>::solve(const Vectord& rhs, Vectord& x) { - if(!m_linearSystem) LOG(FATAL) << "Linear system has not been set"; + if (!m_linearSystem) + { + LOG(FATAL) << "Linear system has not been set"; + } x = m_solver.solve(rhs); } void DirectLinearSolver<Matrixd>::solve(Vectord& x) { - if(!m_linearSystem) LOG(FATAL) << "Linear system has not been set"; + if (!m_linearSystem) + { + LOG(FATAL) << "Linear system has not been set"; + } x.setZero(); auto b = m_linearSystem->getRHSVector(); diff --git a/Source/Solvers/imstkDirectLinearSolver.h b/Source/Solvers/imstkDirectLinearSolver.h index e586d811f03ec1e8fb0b21dd0cdc961d23616017..baaf133e5b79247b15e27777415aaf6e0dd0461e 100644 --- a/Source/Solvers/imstkDirectLinearSolver.h +++ b/Source/Solvers/imstkDirectLinearSolver.h @@ -47,7 +47,7 @@ public: /// \brief Default constructor/destructor. /// // DirectLinearSolver() = delete; - DirectLinearSolver() {} + DirectLinearSolver() {} ~DirectLinearSolver() {}; /// diff --git a/Source/Solvers/imstkNewtonSolver.cpp b/Source/Solvers/imstkNewtonSolver.cpp index 7632722119b1635cff0efc2ee94d6dd36e8faf57..28bf71a30b630c900b32351805baa8206d6022eb 100644 --- a/Source/Solvers/imstkNewtonSolver.cpp +++ b/Source/Solvers/imstkNewtonSolver.cpp @@ -30,7 +30,7 @@ namespace imstk { -template <> +template<> NewtonSolver<SparseMatrixd>::NewtonSolver() : m_linearSolver(std::make_shared<ConjugateGradient>()), m_forcingTerm(0.9), @@ -43,7 +43,7 @@ NewtonSolver<SparseMatrixd>::NewtonSolver() : { } -template <> +template<> NewtonSolver<Matrixd>::NewtonSolver() : m_linearSolver(std::make_shared<DirectLinearSolver<Matrixd>>()), m_forcingTerm(0.9), @@ -56,8 +56,7 @@ NewtonSolver<Matrixd>::NewtonSolver() : { } - -template <typename SystemMatrix> +template<typename SystemMatrix> void NewtonSolver<SystemMatrix>::solveGivenState(Vectord& x) { @@ -101,7 +100,7 @@ NewtonSolver<SystemMatrix>::solveGivenState(Vectord& x) } } -template <typename SystemMatrix> +template<typename SystemMatrix> void NewtonSolver<SystemMatrix>::solve() { @@ -112,14 +111,14 @@ NewtonSolver<SystemMatrix>::solve() } size_t iterNum; - const auto& u = this->m_nonLinearSystem->getUnknownVector(); - Vectord du = u; // make this a class member in future - double error0 = MAX_D; + const auto& u = this->m_nonLinearSystem->getUnknownVector(); + Vectord du = u; // make this a class member in future + double error0 = MAX_D; double epsilon = m_relativeTolerance * m_relativeTolerance; for (iterNum = 0; iterNum < m_maxIterations; ++iterNum) { - double error = updateJacobian(u); + double error = updateJacobian(u); if (iterNum == 0) { @@ -144,7 +143,7 @@ NewtonSolver<SystemMatrix>::solve() } } -template <typename SystemMatrix> +template<typename SystemMatrix> double NewtonSolver<SystemMatrix>::updateJacobian(const Vectord& x) { @@ -171,7 +170,7 @@ NewtonSolver<SystemMatrix>::updateJacobian(const Vectord& x) return std::sqrt(b.dot(b)); } -template <typename SystemMatrix> +template<typename SystemMatrix> void NewtonSolver<SystemMatrix>::updateForcingTerm(const double ratio, const double stopTolerance, const double fnorm) { @@ -188,36 +187,28 @@ NewtonSolver<SystemMatrix>::updateForcingTerm(const double ratio, const double s m_forcingTerm = std::max(std::min(eta, m_etaMax), 0.5 * stopTolerance / fnorm); } -template <typename SystemMatrix> -void -NewtonSolver<SystemMatrix>::setLinearSolver(std::shared_ptr<NewtonSolver::LinearSolverType> newLinearSolver) -{ - m_linearSolver = newLinearSolver; -} - -template <typename SystemMatrix> +template<typename SystemMatrix> // std::shared_ptr<NewtonSolver<SystemMatrix>::LinearSolverType> auto -NewtonSolver<SystemMatrix>::getLinearSolver() const -> std::shared_ptr<LinearSolverType> +NewtonSolver<SystemMatrix>::getLinearSolver() const->std::shared_ptr<LinearSolverType> { return m_linearSolver; } -template <typename SystemMatrix> +template<typename SystemMatrix> void NewtonSolver<SystemMatrix>::setAbsoluteTolerance(const double aTolerance) { m_absoluteTolerance = aTolerance; } -template <typename SystemMatrix> +template<typename SystemMatrix> double NewtonSolver<SystemMatrix>::getAbsoluteTolerance() const { return m_absoluteTolerance; } - template class NewtonSolver<SparseMatrixd>; template class NewtonSolver<Matrixd>; } // imstk diff --git a/Source/Solvers/imstkNewtonSolver.h b/Source/Solvers/imstkNewtonSolver.h index b06e0d977f2441602f3347abe80838107a1d1f30..2dded1606af66aec5fecab42a955738777bba738 100644 --- a/Source/Solvers/imstkNewtonSolver.h +++ b/Source/Solvers/imstkNewtonSolver.h @@ -33,7 +33,7 @@ namespace imstk /// root of F or fails. Global convergence is achieved using a line search sub-process /// and the Armijo rule /// -template <typename SystemMatrix> +template<typename SystemMatrix> class NewtonSolver : public NonLinearSolver<SystemMatrix> { public: @@ -74,7 +74,10 @@ public: /// /// \param newLinearSolver Linear solver pointer /// - void setLinearSolver(std::shared_ptr<LinearSolverType> newLinearSolver); + void setLinearSolver(std::shared_ptr<LinearSolverType> newLinearSolver) + { + m_linearSolver = newLinearSolver; + } /// /// \brief Get LinearSolver @@ -204,7 +207,7 @@ public: virtual void setToSemiImplicit() override { this->m_isSemiImplicit = true; - m_maxIterations = 1; + m_maxIterations = 1; } private: diff --git a/Source/Solvers/imstkNonLinearSolver.cpp b/Source/Solvers/imstkNonLinearSolver.cpp index c0f7f8b80454b93ae0af5609d1b68650c8b8b073..6eac4676c94c171ba1a6e480f8ee1160401c8ffa 100644 --- a/Source/Solvers/imstkNonLinearSolver.cpp +++ b/Source/Solvers/imstkNonLinearSolver.cpp @@ -24,7 +24,7 @@ namespace imstk { -template <typename SystemMatrix> +template<typename SystemMatrix> NonLinearSolver<SystemMatrix>::NonLinearSolver() : m_sigma(std::array<double, 2> { { 0.1, 0.5 } @@ -38,7 +38,7 @@ NonLinearSolver<SystemMatrix>::NonLinearSolver() : m_sigma(std::array<double, 2> }; } -template <typename SystemMatrix> +template<typename SystemMatrix> double NonLinearSolver<SystemMatrix>::armijo(const Vectord& dx, Vectord& x, const double previousFnorm) { @@ -99,7 +99,7 @@ NonLinearSolver<SystemMatrix>::armijo(const Vectord& dx, Vectord& x, const doubl return currentFnorm; } -template <typename SystemMatrix> +template<typename SystemMatrix> void NonLinearSolver<SystemMatrix>::parabolicModel(const std::array<double, 3>& fnorm, std::array<double, 3>& lambda) { @@ -134,69 +134,62 @@ NonLinearSolver<SystemMatrix>::parabolicModel(const std::array<double, 3>& fnorm lambda[0] = newLambda; } -template <typename SystemMatrix> +template<typename SystemMatrix> void NonLinearSolver<SystemMatrix>::setSigma(const std::array<double, 2>& newSigma) { m_sigma = newSigma; } -template <typename SystemMatrix> +template<typename SystemMatrix> const std::array<double, 2>& NonLinearSolver<SystemMatrix>::getSigma() const { return m_sigma; } -template <typename SystemMatrix> +template<typename SystemMatrix> void NonLinearSolver<SystemMatrix>::setAlpha(const double newAlpha) { m_alpha = newAlpha; } -template <typename SystemMatrix> +template<typename SystemMatrix> double NonLinearSolver<SystemMatrix>::getAlpha() const { return m_alpha; } -template <typename SystemMatrix> +template<typename SystemMatrix> void NonLinearSolver<SystemMatrix>::setArmijoMax(const size_t newArmijoMax) { m_armijoMax = newArmijoMax; } -template <typename SystemMatrix> +template<typename SystemMatrix> size_t NonLinearSolver<SystemMatrix>::getArmijoMax() const { return m_armijoMax; } -template <typename SystemMatrix> +template<typename SystemMatrix> void NonLinearSolver<SystemMatrix>::setSystem(std::shared_ptr<NonLinearSystem<SystemMatrix>> newSystem) { m_nonLinearSystem = newSystem; } -template <typename SystemMatrix> +template<typename SystemMatrix> std::shared_ptr<NonLinearSystem<SystemMatrix>> NonLinearSolver<SystemMatrix>::getSystem() const { return m_nonLinearSystem; } -template <typename SystemMatrix> -void -NonLinearSolver<SystemMatrix>::setUpdateIterate(const NonLinearSolver<SystemMatrix>::UpdateIterateType& newUpdateIterate) -{ - m_updateIterate = newUpdateIterate; -} - template class NonLinearSolver<SparseMatrixd>; template class NonLinearSolver<Matrixd>; } // imstk diff --git a/Source/Solvers/imstkNonLinearSolver.h b/Source/Solvers/imstkNonLinearSolver.h index 3902fb310a54ca81f1e11733ed556ce8efcc15de..f576ea458b2376f5c8574c01976adee1bf2af6ef 100644 --- a/Source/Solvers/imstkNonLinearSolver.h +++ b/Source/Solvers/imstkNonLinearSolver.h @@ -35,7 +35,7 @@ namespace imstk /// /// \brief Base class for non-linear solvers /// -template <typename SystemMatrix> +template<typename SystemMatrix> class NonLinearSolver : public SolverBase { public: @@ -114,7 +114,10 @@ public: /// /// \param newUpdateIterate Function used to update iterates. Default: x+=dx. /// - void setUpdateIterate(const UpdateIterateType& newUpdateIterate); + void setUpdateIterate(const UpdateIterateType& newUpdateIterate) + { + m_updateIterate = newUpdateIterate; + } /// /// \brief Set the Newton solver to be fully implicit @@ -133,12 +136,12 @@ public: } protected: - std::array<double, 2> m_sigma; ///< Safeguarding bounds for the line search - double m_alpha; ///< Parameter to measure decrease - size_t m_armijoMax; ///< Maximum number of step length reductions + std::array<double, 2> m_sigma; ///< Safeguarding bounds for the line search + double m_alpha; ///< Parameter to measure decrease + size_t m_armijoMax; ///< Maximum number of step length reductions std::shared_ptr<NonLinearSystem<SystemMatrix>> m_nonLinearSystem; ///< System of non-linear equations - UpdateIterateType m_updateIterate; ///< Update iteration function - bool m_isSemiImplicit = true; ///> Semi-Implicit solver + UpdateIterateType m_updateIterate; ///< Update iteration function + bool m_isSemiImplicit = true; ///> Semi-Implicit solver }; } // imstk diff --git a/Source/Solvers/imstkNonLinearSystem.cpp b/Source/Solvers/imstkNonLinearSystem.cpp index 92c688c54b8a1ac4b4e0409814c7fdfb58b6bc80..0ce7bc971b8f0d85199110ca511ba94aad6d975f 100644 --- a/Source/Solvers/imstkNonLinearSystem.cpp +++ b/Source/Solvers/imstkNonLinearSystem.cpp @@ -26,33 +26,33 @@ namespace imstk { -template <typename Matrix> +template<typename Matrix> NonLinearSystem<Matrix>::NonLinearSystem(const VectorFunctionType& F, const MatrixFunctionType& dF) : m_F(F), m_dF(dF) { } -template <typename Matrix> +template<typename Matrix> void NonLinearSystem<Matrix>::setFunction(const VectorFunctionType& function) { m_F = function; } -template <typename Matrix> +template<typename Matrix> void NonLinearSystem<Matrix>::setJacobian(const MatrixFunctionType& function) { m_dF = function; } -template <typename Matrix> +template<typename Matrix> const Vectord& NonLinearSystem<Matrix>::evaluateF(const Vectord& x, const bool isSemiImplicit) { return m_F(x, isSemiImplicit); } -template <typename Matrix> +template<typename Matrix> const Matrix& NonLinearSystem<Matrix>::evaluateJacobian(const Vectord& x) { @@ -61,5 +61,4 @@ NonLinearSystem<Matrix>::evaluateJacobian(const Vectord& x) template class NonLinearSystem<SparseMatrixd>; template class NonLinearSystem<Matrixd>; - } //imstk diff --git a/Source/Solvers/imstkNonLinearSystem.h b/Source/Solvers/imstkNonLinearSystem.h index d631063b9b287bb562a671059b09ea35e92ca1bf..2a49cc24362b31b3c083113b11f83c76d012bac7 100644 --- a/Source/Solvers/imstkNonLinearSystem.h +++ b/Source/Solvers/imstkNonLinearSystem.h @@ -31,7 +31,7 @@ namespace imstk /// /// \brief Base class for a multi-variable nonlinear system /// -template <typename Matrix> +template<typename Matrix> class NonLinearSystem { public: diff --git a/Source/Solvers/imstkPbdSolver.cpp b/Source/Solvers/imstkPbdSolver.cpp index 83cbc529fbc3b47bff087936aacd81d2a1bd4bd2..a03ff5351351d8448fa6371394dc1600eaaffe29 100644 --- a/Source/Solvers/imstkPbdSolver.cpp +++ b/Source/Solvers/imstkPbdSolver.cpp @@ -19,39 +19,111 @@ =========================================================================*/ -#include "imstkPbdObject.h" #include "imstkPbdSolver.h" -#include "imstkPbdCollisionConstraint.h" #include "imstkParallelUtils.h" namespace imstk { +PbdSolver::PbdSolver() : + m_partitionedConstraints(std::make_shared<std::vector<PBDConstraintVector>>()), + m_constraints(std::make_shared<PBDConstraintVector>()), + m_positions(std::make_shared<StdVectorOfVec3d>()), + m_invMasses(std::make_shared<StdVectorOfReal>()) +{ +} + void PbdSolver::solve() { - m_pbdObject->integratePosition(); - m_pbdObject->solveConstraints(); - resolveCollisionConstraints(); - m_pbdObject->updateVelocity(); + // Solve the constraints and partitioned constraints + StdVectorOfVec3d& currPositions = *m_positions; + const StdVectorOfReal& invMasses = *m_invMasses; + + const PBDConstraintVector& constraints = *m_constraints; + const std::vector<PBDConstraintVector>& partitionedConstraints = *m_partitionedConstraints; + + // zero out the Lagrange multiplier + for (size_t j = 0; j < constraints.size(); ++j) + { + constraints[j]->zeroOutLambda(); + } + + for (size_t j = 0; j < partitionedConstraints.size(); j++) + { + const PBDConstraintVector& constraintPartition = partitionedConstraints[j]; + ParallelUtils::parallelFor(constraintPartition.size(), + [&](const size_t idx) { constraintPartition[idx]->zeroOutLambda(); } + ); + } + + unsigned int i = 0; + while (i++ < m_iterations) + { + for (size_t j = 0; j < constraints.size(); ++j) + { + constraints[j]->projectConstraint(invMasses, m_dt, m_solverType, currPositions); + } + + for (size_t j = 0; j < partitionedConstraints.size(); j++) + { + const PBDConstraintVector& constraintPartition = partitionedConstraints[j]; + + ParallelUtils::parallelFor(constraintPartition.size(), + [&](const size_t idx) + { + constraintPartition[idx]->projectConstraint(invMasses, m_dt, m_solverType, currPositions); + }); + // Sequential + /*for (size_t k = 0; k < constraintPartition.size(); k++) + { + constraintPartition[k]->solvePositionConstraint(currPositions, invMasses); + }*/ + } + } +} + +PbdCollisionSolver::PbdCollisionSolver() : + m_collisionConstraints(std::make_shared<std::list<PBDCollisionConstraintVector*>>()), + m_collisionConstraintsData(std::make_shared<std::list<CollisionConstraintData>>()) +{ +} + +void +PbdCollisionSolver::addCollisionConstraints(PBDCollisionConstraintVector* constraints, + std::shared_ptr<StdVectorOfVec3d> posA, std::shared_ptr<StdVectorOfReal> invMassA, + std::shared_ptr<StdVectorOfVec3d> posB, std::shared_ptr<StdVectorOfReal> invMassB) +{ + m_collisionConstraints->push_back(constraints); + m_collisionConstraintsData->push_back({ posA, invMassA, posB, invMassB }); } void -PbdSolver::resolveCollisionConstraints() +PbdCollisionSolver::solve() { - if (m_PBDConstraints.size() > 0) + // Solve collision constraints + if (m_collisionConstraints->size() > 0) { - uint32_t maxIter = 3u; - uint32_t i = 0; - while (++i < maxIter) + unsigned int i = 0; + while (i++ < m_collisionIterations) { - for (const auto constraintList : m_PBDConstraints) + std::list<CollisionConstraintData>::iterator colDataIter = m_collisionConstraintsData->begin(); + for (auto constraintList : *m_collisionConstraints) { - for (size_t k = 0; k < constraintList->size(); ++k) + CollisionConstraintData colData = *colDataIter; + StdVectorOfVec3d& posA = *colData.m_posA; + const StdVectorOfReal& invMassA = *colData.m_invMassA; + StdVectorOfVec3d& posB = *colData.m_posB; + const StdVectorOfReal& invMassB = *colData.m_invMassB; + const PBDCollisionConstraintVector& constraints = *constraintList; + for (size_t j = 0; j < constraints.size(); j++) { - (*constraintList)[k]->solvePositionConstraint(); + constraints[j]->projectConstraint(invMassA, invMassB, posA, posB); } + colDataIter++; } } + m_collisionConstraints->clear(); + m_collisionConstraintsData->clear(); } } } // end namespace imstk diff --git a/Source/Solvers/imstkPbdSolver.h b/Source/Solvers/imstkPbdSolver.h index ef20dbdf5ce286c95b4bdf42810f46a5de65713a..f7012d6b5766bd9e6315db03f85812299793909b 100644 --- a/Source/Solvers/imstkPbdSolver.h +++ b/Source/Solvers/imstkPbdSolver.h @@ -21,67 +21,143 @@ #pragma once +#include "imstkPbdCollisionConstraint.h" +#include "imstkPbdConstraint.h" #include "imstkSolverBase.h" -#include "imstkGraph.h" - -#include <unordered_set> +#include "imstkLogger.h" namespace imstk { -class PbdObject; -class PbdCollisionConstraint; +struct CollisionConstraintData +{ + CollisionConstraintData(std::shared_ptr<StdVectorOfVec3d> posA, + std::shared_ptr<StdVectorOfReal> invMassA, + std::shared_ptr<StdVectorOfVec3d> posB, + std::shared_ptr<StdVectorOfReal> invMassB) : + m_posA(posA), m_invMassA(invMassA), m_posB(posB), m_invMassB(invMassB) + { + } + + std::shared_ptr<StdVectorOfVec3d> m_posA = nullptr; + std::shared_ptr<StdVectorOfReal> m_invMassA = nullptr; + std::shared_ptr<StdVectorOfVec3d> m_posB = nullptr; + std::shared_ptr<StdVectorOfReal> m_invMassB = nullptr; +}; + /// /// \class PbdSolver /// /// \brief Position Based Dynamics solver +/// This solver can operate on both partitioned constraints (unordered_set of vector'd constraints) in parallel +/// and sequentially on vector'd constraints. It requires a set of constraints, positions, and invMasses. /// class PbdSolver : public SolverBase { -using PBDConstraintVector = std::vector<PbdCollisionConstraint*>; - public: /// /// \brief Constructors/Destructor /// - PbdSolver() = default; + PbdSolver(); virtual ~PbdSolver() override = default; - PbdSolver(const PbdSolver& other) = delete; - PbdSolver& operator=(const PbdSolver& other) = delete; +public: + /// + /// \brief Set Iterations. The number of nonlinear iterations. + /// + void setIterations(const size_t iterations) { this->m_iterations = iterations; } + + /// + /// \brief Sets the partioned constraints the solver should solve for + /// These will be solved in parallel + /// + void setPartitionedConstraints(std::shared_ptr<std::vector<PBDConstraintVector>> partitionedConstraints) { this->m_partitionedConstraints = partitionedConstraints; } + + /// + /// \brief Sets the constraints the solver should solve for + /// These wil be solved sequentially + /// + void setConstraints(std::shared_ptr<PBDConstraintVector> constraints) { this->m_constraints = constraints; } /// - /// \brief Set MaxIterations. The maximum number of nonlinear iterations. + /// \brief Sets the positions the solver should solve with /// - void setMaxIterations(const size_t newMaxIterations) { this->m_maxIterations = newMaxIterations; } + void setPositions(std::shared_ptr<StdVectorOfVec3d> positions) { this->m_positions = positions; } /// - /// \brief Get MaxIterations. Returns current maximum nonlinear iterations. + /// \brief Sets the invMasses the solver should solve with /// - size_t getMaxIterations() const { return this->m_maxIterations; } + void setInvMasses(std::shared_ptr<StdVectorOfReal> invMasses) { this->m_invMasses = invMasses; } /// - /// \brief + /// \brief Set time step /// - void setPbdObject(const std::shared_ptr<PbdObject>& pbdObj) { m_pbdObject = pbdObj; } + void setTimeStep(const double dt) { m_dt = dt; } + + /// + /// \brief Get Iterations. Returns current nonlinear iterations. + /// + size_t getIterations() const { return this->m_iterations; } + + /// + /// \brief Set the PBD solver type + /// + void setSolverType(const PbdConstraint::SolverType& type) + { + if (type == PbdConstraint::SolverType::GCD) + { + LOG(WARNING) << "GCD is NOT implemented yet, use xPBD instead"; + m_solverType = PbdConstraint::SolverType::xPBD; + return; + } + + m_solverType = type; + } /// /// \brief Solve the non linear system of equations G(x)=0 using Newton's method. /// void solve() override; +private: + size_t m_iterations = 20; ///> Number of NL Gauss-Seidel iterations for regular constraints + double m_dt; ///> time step + + std::shared_ptr<std::vector<PBDConstraintVector>> m_partitionedConstraints = nullptr; ///> Set of vector'd/partitioned pbd constraints + std::shared_ptr<PBDConstraintVector> m_constraints = nullptr; ///> Vector of constraints + + std::shared_ptr<StdVectorOfVec3d> m_positions = nullptr; + std::shared_ptr<StdVectorOfReal> m_invMasses = nullptr; + PbdConstraint::SolverType m_solverType = PbdConstraint::SolverType::xPBD; +}; + +class PbdCollisionSolver : SolverBase +{ +public: + PbdCollisionSolver(); + virtual ~PbdCollisionSolver() override = default; + +public: + /// + /// \brief Get CollisionIterations + /// + size_t getCollisionIterations() const { return this->m_collisionIterations; } + /// /// \brief Add the global collision contraints to this solver /// - void addCollisionConstraints(PBDConstraintVector* constraints) { m_PBDConstraints.insert(constraints); } + void addCollisionConstraints(PBDCollisionConstraintVector* constraints, + std::shared_ptr<StdVectorOfVec3d> posA, std::shared_ptr<StdVectorOfReal> invMassA, + std::shared_ptr<StdVectorOfVec3d> posB, std::shared_ptr<StdVectorOfReal> invMassB); /// - /// \brief Solve the global collision contraints charged to this solver + /// \brief Solve the non linear system of equations G(x)=0 using Newton's method. /// - void resolveCollisionConstraints(); + void solve() override; private: - size_t m_maxIterations = 20; ///< Maximum number of NL Gauss-Seidel iterations - std::unordered_set<PBDConstraintVector*> m_PBDConstraints; ///< Collision contraints charged to this solver - std::shared_ptr<PbdObject> m_pbdObject; + size_t m_collisionIterations = 5; ///> Number of NL Gauss-Seidel iterations for collision constraints + + std::shared_ptr<std::list<PBDCollisionConstraintVector*>> m_collisionConstraints = nullptr; ///< Collision contraints charged to this solver + std::shared_ptr<std::list<CollisionConstraintData>> m_collisionConstraintsData = nullptr; }; } // imstk diff --git a/Source/Solvers/imstkSPHSolver.h b/Source/Solvers/imstkSPHSolver.h deleted file mode 100644 index 58f08edf73e468683864cbaebae71588208f4ae7..0000000000000000000000000000000000000000 --- a/Source/Solvers/imstkSPHSolver.h +++ /dev/null @@ -1,62 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include "imstkSolverBase.h" -#include "imstkSPHObject.h" -#include "imstkSPHModel.h" - -namespace imstk -{ -/// -/// \class SPHSolver -/// \brief SPH solver -/// -class SPHSolver : public SolverBase -{ -public: - SPHSolver() = default; - virtual ~SPHSolver() override = default; - - SPHSolver(const SPHSolver& other) = delete; - SPHSolver& operator=(const SPHSolver& other) = delete; - - /// - /// \brief Set the simulation object - /// - void setSPHObject(const std::shared_ptr<SPHObject>& obj) - { assert(obj); m_SPHObject = obj; } - - /// - /// \brief Advance one time step - /// - virtual void solve() override - { - const auto& SPHModel = m_SPHObject->getSPHModel(); - LOG_IF(FATAL, (!SPHModel)) << "SPH model has not been initialized"; - SPHModel->advanceTimeStep(); - } - -private: - std::shared_ptr<SPHObject> m_SPHObject; -}; -} // end namespace imstk diff --git a/Source/Solvers/imstkSolverBase.h b/Source/Solvers/imstkSolverBase.h index 4db160b9ad3e75938bad0a36a5e7c82219b21992..6249e0b0c412196e331a2b49fc52188f4d398343 100644 --- a/Source/Solvers/imstkSolverBase.h +++ b/Source/Solvers/imstkSolverBase.h @@ -28,11 +28,10 @@ namespace imstk /// class SolverBase { -public: - /// - /// \brief Default Constructor/Destructor - /// +protected: SolverBase() = default; + +public: virtual ~SolverBase() = default; virtual void solve() = 0; diff --git a/Source/apiUtilities/imstkAPIUtilities.cpp b/Source/apiUtilities/imstkAPIUtilities.cpp index 82bfcb51e895fd2093cfd7cf062fa32cf12ff387..3dcf882c6757e8e78c1157ca62b889715539bc17 100644 --- a/Source/apiUtilities/imstkAPIUtilities.cpp +++ b/Source/apiUtilities/imstkAPIUtilities.cpp @@ -210,6 +210,7 @@ printUPS(std::shared_ptr<SceneManager> sceneManager) return; } + sceneManager->enableFrameCount(); sceneManager->setPostUpdateCallback([&sceneManager](Module* module) { std::cout << "\r" << module->getName() << " running at " diff --git a/Source/apiUtilities/imstkAPIUtilities.h b/Source/apiUtilities/imstkAPIUtilities.h index f39515b79aa620b9c7a23580a178117a2fe608cc..8a929f125554ba9c91a75f12b5e0320696cfa25e 100644 --- a/Source/apiUtilities/imstkAPIUtilities.h +++ b/Source/apiUtilities/imstkAPIUtilities.h @@ -33,7 +33,7 @@ class PointSet; class SceneManager; class Scene; class FEMDeformableBodyModel; -template <typename Matrix> +template<typename Matrix> class NonLinearSystem; class CollidingObject; class Graph;