diff --git a/Examples/FastMarch/CMakeLists.txt b/Examples/FastMarch/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f84730ed20fddef5080b95d5f841b80d0b202966 --- /dev/null +++ b/Examples/FastMarch/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-FastMarch) + +#----------------------------------------------------------------------------- +# Create executable +#----------------------------------------------------------------------------- +imstk_add_executable(${PROJECT_NAME} FastMarchExample.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 Filtering) diff --git a/Examples/FastMarch/FastMarchExample.cpp b/Examples/FastMarch/FastMarchExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef005ed1a9fcce6a7148bd1ab3681783735b442a --- /dev/null +++ b/Examples/FastMarch/FastMarchExample.cpp @@ -0,0 +1,122 @@ +/*========================================================================= + + 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 "imstkCamera.h" +#include "imstkDataArray.h" +#include "imstkFastMarch.h" +#include "imstkKeyboardSceneControl.h" +#include "imstkLogger.h" +#include "imstkMeshIO.h" +#include "imstkMouseSceneControl.h" +#include "imstkNew.h" +#include "imstkScene.h" +#include "imstkSceneManager.h" +#include "imstkSceneObject.h" +#include "imstkSimulationManager.h" +#include "imstkVisualModel.h" +#include "imstkVolumeRenderMaterial.h" +#include "imstkVTKViewer.h" + +#include <vtkVolumeProperty.h> +#include <vtkColorTransferFunction.h> +#include <vtkPiecewiseFunction.h> + +using namespace imstk; + +/// +/// \brief This example demonstrates the volume renderer +/// +int +main() +{ + Logger::startLogger(); + + // SDK and Scene + imstkNew<Scene> scene("VolumeRendering"); + + // Create some blank image + imstkNew<ImageData> image; + image->allocate(IMSTK_DOUBLE, 1, Vec3i(50, 50, 50)); + std::shared_ptr<DataArray<double>> scalars = std::dynamic_pointer_cast<DataArray<double>>(image->getScalars()); + scalars->fill(0.0); + + // Seed voxels + imstkNew<FastMarch> fastMarch; + fastMarch->setDistThreshold(5.0); + fastMarch->setImage(image); + std::vector<Vec3i> seeds = { Vec3i(25, 25, 25) }; + fastMarch->setSeeds(seeds); + fastMarch->solve(); + + // Create a visual object in the scene for the volume + imstkNew<SceneObject> volumeObj("VisualVolume"); + volumeObj->setVisualGeometry(image); + scene->addSceneObject(volumeObj); + + imstkNew<VolumeRenderMaterial> volumeMaterial; + vtkNew<vtkColorTransferFunction> colorFunc; + colorFunc->AddRGBPoint(0.0, 1.0, 0.0, 0.0); + colorFunc->AddRGBPoint(8.0, 0.0, 0.0, 1.0); + volumeMaterial->getVolumeProperty()->SetColor(colorFunc); + vtkNew<vtkPiecewiseFunction> opacityFunc; + opacityFunc->AddPoint(0.0, 0.0); + opacityFunc->AddPoint(1.0, 1.0); + volumeMaterial->getVolumeProperty()->SetScalarOpacity(opacityFunc); + // Apply the preset to the visual object + volumeObj->getVisualModel(0)->setRenderMaterial(volumeMaterial); + + scene->getActiveCamera()->setPosition(Vec3d(0.0, -100.0, -100.0)); + scene->getActiveCamera()->setFocalPoint(Vec3d(25.0, 25.0, 25.0)); + scene->getActiveCamera()->setViewUp(Vec3d(0.0, 1.0, 0.0)); + + // Run the simulation + { + imstkNew<VTKViewer> viewer("Viewer"); + viewer->setActiveScene(scene); + + // Setup a scene manager to advance the scene in its own thread + imstkNew<SceneManager> sceneManager("Scene Manager"); + sceneManager->setActiveScene(scene); + sceneManager->setExecutionType(Module::ExecutionType::ADAPTIVE); + + imstkNew<SimulationManager> driver; + driver->addModule(viewer); + driver->addModule(sceneManager); + + // Add mouse and keyboard controls to the viewer + { + imstkNew<MouseSceneControl> mouseControl(viewer->getMouseDevice()); + mouseControl->setSceneManager(sceneManager); + viewer->addControl(mouseControl); + + imstkNew<KeyboardSceneControl> keyControl(viewer->getKeyboardDevice()); + keyControl->setSceneManager(sceneManager); + keyControl->setModuleDriver(driver); + viewer->addControl(keyControl); + } + + driver->start(); + } + + MeshIO::write(image, "results.nii"); + + return 0; +} diff --git a/Examples/FemurCut/FemurCutExample.cpp b/Examples/FemurCut/FemurCutExample.cpp index 199f61cf451381135995bb6cb7a66e82b0eddb20..b7926be9d31ed8ca0e94ecf526a18779b03cd549 100644 --- a/Examples/FemurCut/FemurCutExample.cpp +++ b/Examples/FemurCut/FemurCutExample.cpp @@ -25,6 +25,7 @@ #include "imstkHapticDeviceManager.h" #include "imstkImageData.h" #include "imstkIsometricMap.h" +#include "imstkKeyboardDeviceClient.h" #include "imstkKeyboardSceneControl.h" #include "imstkLevelSetCH.h" #include "imstkLevelSetDeformableObject.h" @@ -50,6 +51,8 @@ #include "imstkVolumeRenderMaterial.h" #include "imstkVTKViewer.h" +#include <vtkObject.h> + using namespace imstk; using namespace imstk::expiremental; @@ -61,27 +64,21 @@ makeLevelsetObj(const std::string& name, std::shared_ptr<LocalMarchingCubes> iso { imstkNew<LevelSetDeformableObject> levelsetObj(name); - std::shared_ptr<ImageData> initLvlsetImage = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/legs/femurBone_SDF.nii")->cast(IMSTK_DOUBLE); - const Vec3d& currSpacing = initLvlsetImage->getSpacing(); + std::shared_ptr<ImageData> initLvlSetImage = MeshIO::read<ImageData>("C:/Users/Andx_/Desktop/femurBoneSolid_SDF.nii")->cast(IMSTK_DOUBLE); + const Vec3d& currSpacing = initLvlSetImage->getSpacing(); + // Note: Anistropic scaling would invalidate the SDF - initLvlsetImage->setSpacing(currSpacing * 0.001); - initLvlsetImage->setOrigin(Vec3d(0.0, 0.8, 1.5)); + initLvlSetImage->setOrigin(Vec3d(0.0, 0.8, 1.5)); // Setup the Parameters imstkNew<LevelSetModelConfig> lvlSetConfig; lvlSetConfig->m_sparseUpdate = true; - lvlSetConfig->m_dt = 0.0001; - //lvlSetConfig->m_k = 0.8; - - // Setup the Model - imstkNew<LevelSetModel> model; - model->setModelGeometry(initLvlsetImage); - model->configure(lvlSetConfig); + lvlSetConfig->m_substeps = 30; // Too many chunks and you'll hit memory constraints quickly // Too little chunks and the updates for a chunk will take too long // The chunks must divide the image dimensions (image dim-1 must be divisible by # chunks) - isoExtract->setInputImage(initLvlsetImage); + isoExtract->setInputImage(initLvlSetImage); isoExtract->setIsoValue(0.0); isoExtract->setNumberOfChunks(Vec3i(32, 9, 9)); isoExtract->update(); @@ -103,6 +100,7 @@ makeLevelsetObj(const std::string& name, std::shared_ptr<LocalMarchingCubes> iso const double b = (rand() % 500) / 500.0; material->setColor(Color(r, g, b)); material->setEdgeColor(Color::Color::Orange); + //material->setOpacity(0.7); surfMeshModel->setRenderMaterial(material); levelsetObj->addVisualModel(surfMeshModel); chunksGenerated.insert(i); @@ -110,10 +108,14 @@ makeLevelsetObj(const std::string& name, std::shared_ptr<LocalMarchingCubes> iso } // Setup the Object - levelsetObj->setPhysicsGeometry(initLvlsetImage); - std::shared_ptr<SignedDistanceField> sdf = std::make_shared<SignedDistanceField>(initLvlsetImage); - // Since we scaled the image spacing we should also scale SDF - sdf->setScale(0.001); + imstkNew<SignedDistanceField> sdf(initLvlSetImage); + + // Setup the Model + imstkNew<LevelSetModel> model; + model->setModelGeometry(sdf); + model->configure(lvlSetConfig); + + levelsetObj->setPhysicsGeometry(sdf); levelsetObj->setCollidingGeometry(sdf); levelsetObj->setDynamicalModel(model); @@ -124,27 +126,28 @@ std::shared_ptr<RigidObject2> makeRigidObj(const std::string& name) { imstkNew<RigidBodyModel2> rbdModel; - rbdModel->getConfig()->m_dt = 0.001; - rbdModel->getConfig()->m_maxNumIterations = 5; + rbdModel->getConfig()->m_maxNumIterations = 8; rbdModel->getConfig()->m_velocityDamping = 1.0; rbdModel->getConfig()->m_angularVelocityDamping = 1.0; - rbdModel->getConfig()->m_maxNumConstraints = 25; + rbdModel->getConfig()->m_maxNumConstraints = 20; // Create the first rbd, plane floor imstkNew<RigidObject2> rigidObj("Cube"); { - auto toolMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Hull_Subdivided.stl"); + auto toolMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Hull_Subdivided3.stl"); toolMesh->rotate(Vec3d(0.0, 1.0, 0.0), 3.14, Geometry::TransformType::ApplyToData); toolMesh->rotate(Vec3d(1.0, 0.0, 0.0), -1.57, Geometry::TransformType::ApplyToData); toolMesh->scale(Vec3d(0.07, 0.07, 0.07), Geometry::TransformType::ApplyToData); - auto toolVisualMeshHandle = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Handle.dae"); + //auto toolVisualMeshHandle = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Handle.dae"); + auto toolVisualMeshHandle = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Hull_Subdivided3.stl"); toolVisualMeshHandle->rotate(Vec3d(0.0, 1.0, 0.0), 3.14, Geometry::TransformType::ApplyToData); toolVisualMeshHandle->rotate(Vec3d(1.0, 0.0, 0.0), -1.57, Geometry::TransformType::ApplyToData); toolVisualMeshHandle->scale(Vec3d(0.07, 0.07, 0.07), Geometry::TransformType::ApplyToData); - auto toolVisualMeshBlade = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Blade10.dae"); + //auto toolVisualMeshBlade = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Blade10.dae"); + auto toolVisualMeshBlade = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Scalpel/Scalpel_Hull_Subdivided3.stl"); toolVisualMeshBlade->rotate(Vec3d(0.0, 1.0, 0.0), 3.14, Geometry::TransformType::ApplyToData); toolVisualMeshBlade->rotate(Vec3d(1.0, 0.0, 0.0), -1.57, Geometry::TransformType::ApplyToData); toolVisualMeshBlade->scale(Vec3d(0.07, 0.07, 0.07), Geometry::TransformType::ApplyToData); @@ -154,8 +157,6 @@ makeRigidObj(const std::string& name) toolMaterial->setShadingModel(RenderMaterial::ShadingModel::PBR); toolMaterial->setMetalness(0.9f); toolMaterial->setRoughness(0.4f); - //toolMaterial->setDisplayMode(RenderMaterial::DisplayMode::Points); - //toolMaterial->setPointSize(15.0); toolMaterial->setDiffuseColor(Color(0.7, 0.7, 0.7)); imstkNew<VisualModel> visualModel1(toolVisualMeshHandle); @@ -173,9 +174,8 @@ makeRigidObj(const std::string& name) // Hack to add two maps rigidObj->setPhysicsToCollidingMap(std::make_shared<IsometricMap>(toolMesh, toolVisualMeshBlade)); rigidObj->setDynamicalModel(rbdModel); - rigidObj->getRigidBody()->m_mass = 1000.0; - //rigidObj->getRigidBody()->setInertiaFromPointSet(toolMesh, 0.01, false); - rigidObj->getRigidBody()->m_intertiaTensor = Mat3d::Identity() * 20.0; + rigidObj->getRigidBody()->m_mass = 1.0; + rigidObj->getRigidBody()->m_intertiaTensor = Mat3d::Identity() * 10000.0; rigidObj->getRigidBody()->m_initPos = Vec3d(0.0, 1.0, 2.0); } return rigidObj; @@ -189,6 +189,8 @@ makeRigidObj(const std::string& name) int main() { + vtkObject::GlobalWarningDisplayOff(); + // Setup logger (write to file and stdout) Logger::startLogger(); @@ -212,14 +214,19 @@ main() scene->addSceneObject(rbdGhostObj); imstkNew<RigidObjectLevelSetCollisionPair> interaction(rbdObj, lvlSetObj); - auto colHandlerA = std::dynamic_pointer_cast<RigidBodyCH>(interaction->getCollisionHandlingA()); - colHandlerA->setUseFriction(false); - colHandlerA->setStiffness(0.0); // inelastic collision - auto colHandlerB = std::dynamic_pointer_cast<LevelSetCH>(interaction->getCollisionHandlingB()); - colHandlerB->setLevelSetVelocityScaling(0.1); - colHandlerB->setKernel(3, 1.0); - //colHandlerB->setLevelSetVelocityScaling(0.0); // Can't push the levelset - colHandlerB->setUseProportionalVelocity(false); + { + auto colHandlerA = std::dynamic_pointer_cast<RigidBodyCH>(interaction->getCollisionHandlingA()); + colHandlerA->setUseFriction(false); + colHandlerA->setStiffness(0.05); // inelastic collision + + auto colHandlerB = std::dynamic_pointer_cast<LevelSetCH>(interaction->getCollisionHandlingB()); + colHandlerB->setLevelSetVelocityScaling(0.05); + colHandlerB->setKernel(3, 1.0); + //colHandlerB->setLevelSetVelocityScaling(0.0); // Can't push the levelset + colHandlerB->setUseProportionalVelocity(true); + } + std::shared_ptr<CollisionDetection> cd = interaction->getCollisionDetection(); + scene->getCollisionGraph()->addInteraction(interaction); // Light (white) @@ -229,9 +236,9 @@ main() scene->addLight(whiteLight); // Adjust camera - scene->getActiveCamera()->setFocalPoint(0.27, 0.74, 1.53); - scene->getActiveCamera()->setPosition(0.17, 1.09, 1.89); - scene->getActiveCamera()->setViewUp(0.17, 0.74, -0.65); + scene->getActiveCamera()->setFocalPoint(0.25, 0.83, 1.58); + scene->getActiveCamera()->setPosition(0.243, 1.06, 1.95); + scene->getActiveCamera()->setViewUp(0.05, 0.86, -0.51); { imstkNew<VTKViewer> viewer("Viewer"); @@ -243,29 +250,33 @@ main() sceneManager->setExecutionType(Module::ExecutionType::ADAPTIVE); imstkNew<HapticDeviceManager> hapticManager; - std::shared_ptr<HapticDeviceClient> hapticDeviceClient = hapticManager->makeDeviceClient("Default Device"); + std::shared_ptr<HapticDeviceClient> hapticDeviceClient = hapticManager->makeDeviceClient(); imstkNew<RigidObjectController> controller(rbdObj, hapticDeviceClient); - - controller->setLinearKd(100000.0); - controller->setAngularKd(550.0); - controller->setLinearKs(1000000.0); - controller->setAngularKs(10000.0); - controller->setForceScaling(0.0001); - - controller->setComputeVelocity(true); // The device we are using doesn't produce this quantity, with this flag its computed - controller->setComputeAngularVelocity(true); // The device we are using doesn't produce this quantity, with this flag its computed - - controller->setTranslationScaling(0.0015); - controller->setTranslationOffset(Vec3d(0.1, 0.9, 1.8)); - - scene->addController(controller); + { + controller->setLinearKd(1000.0 * 0.9); + controller->setLinearKs(100000.0 * 0.9); + controller->setAngularKs(300000000.0); + controller->setAngularKd(400000.0); + controller->setForceScaling(0.001); + + // The particular device we are using doesn't produce this quantity, with this flag its computed + // in code + controller->setComputeVelocity(true); + controller->setComputeAngularVelocity(true); + + controller->setTranslationScaling(0.0015); + controller->setTranslationOffset(Vec3d(0.1, 0.9, 1.6)); + controller->setSmoothingKernelSize(30); + + scene->addController(controller); + } connect<Event>(sceneManager->getActiveScene(), EventType::Configure, [&](Event*) { std::shared_ptr<TaskGraph> taskGraph = sceneManager->getActiveScene()->getTaskGraph(); - // Pipe the changes from the levelset into local marhcing cubes + // Pipe the changes from the levelset into local marching cubes // Compute this before the levelset is evolved taskGraph->insertBefore(lvlSetObj->getLevelSetModel()->getQuantityEvolveNode(0), std::make_shared<TaskNode>([&]() @@ -282,6 +293,7 @@ main() isoExtract->update(); // Create meshes for chunks if they now contain vertices (and weren't already generated) + // You could just create all the chunks, but this saves some memory for internal/empty ones const Vec3i& numChunks = isoExtract->getNumberOfChunks(); for (int i = 0; i < numChunks[0] * numChunks[1] * numChunks[2]; i++) { @@ -297,6 +309,7 @@ main() const double b = (rand() % 500) / 500.0; material->setColor(Color(r, g, b)); material->setEdgeColor(Color::Color::Orange); + //material->setOpacity(0.7); surfMeshModel->setRenderMaterial(material); lvlSetObj->addVisualModel(surfMeshModel); chunksGenerated.insert(i); @@ -305,7 +318,8 @@ main() }); connect<Event>(sceneManager, EventType::PostUpdate, [&](Event*) { - rbdObj->getRigidBodyModel2()->getConfig()->m_dt = sceneManager->getDt(); + rbdObj->getRigidBodyModel2()->getConfig()->m_dt = sceneManager->getDt(); + lvlSetObj->getLevelSetModel()->getConfig()->m_dt = sceneManager->getDt(); // Also apply controller transform to ghost geometry ghostMesh->setTranslation(controller->getPosition()); @@ -318,7 +332,7 @@ main() driver->addModule(viewer); driver->addModule(sceneManager); driver->addModule(hapticManager); - driver->setDesiredDt(0.0004); + driver->setDesiredDt(0.001); // Little over 1000ups { imstkNew<MouseSceneControl> mouseControl(viewer->getMouseDevice()); diff --git a/Examples/RigidBodyDynamics2/RigidBodyDynamicsExample2.cpp b/Examples/RigidBodyDynamics2/RigidBodyDynamicsExample2.cpp index 42865ce9222f7c440e45b446d738ec1179e05701..74fb6bbaf6621231632b512915aa5b854efee5b7 100644 --- a/Examples/RigidBodyDynamics2/RigidBodyDynamicsExample2.cpp +++ b/Examples/RigidBodyDynamics2/RigidBodyDynamicsExample2.cpp @@ -67,8 +67,8 @@ main() { // This model is shared among interacting rigid bodies imstkNew<RigidBodyModel2> rbdModel; - rbdModel->getConfig()->m_gravity = Vec3d(0.0, -900.0, 0.0); - rbdModel->getConfig()->m_maxNumIterations = 40; + rbdModel->getConfig()->m_gravity = Vec3d(0.0, -2500.0, 0.0); + rbdModel->getConfig()->m_maxNumIterations = 10; // Create the first rbd, plane floor imstkNew<CollidingObject> planeObj("Plane"); @@ -150,7 +150,7 @@ main() auto rbdInteraction = std::make_shared<RigidObjectCollidingCollisionPair>(cubeObj, planeObj, CollisionDetection::Type::PointSetToImplicit); std::shared_ptr<RigidBodyCH> ch = std::dynamic_pointer_cast<RigidBodyCH>(rbdInteraction->getCollisionHandlingA()); ch->setUseFriction(false); - ch->setStiffness(0.0); // Completely inelastic + ch->setStiffness(0.05); scene->getCollisionGraph()->addInteraction(rbdInteraction); scene->getActiveCamera()->setPosition(0.0, 40.0, 40.0); diff --git a/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h b/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h index b2c17c752859db5e56575a17a5e023141b5bfc5e..31788cf3bc48976e745a7bea86a479d58fd7e3dc 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h +++ b/Source/CollisionDetection/CollisionDetection/imstkCollisionDetection.h @@ -43,6 +43,7 @@ public: /// /// \brief Type of the collision detection + /// \todo: Remove and replace with factory names /// enum class Type { @@ -53,6 +54,7 @@ public: PointSetToSpherePicking, PointSetToSurfaceMesh, PointSetToImplicit, + PointSetToImplicitCCD, // Mesh to mesh (mesh to analytical object = mesh vertices to analytical object) SurfaceMeshToSurfaceMesh, diff --git a/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.cpp b/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.cpp index fd7b979984c8bc71adcc71f6c4347353673346c5..da765b9e1978e4eda426d84fc203a65c8d5bc787 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.cpp +++ b/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.cpp @@ -22,29 +22,59 @@ limitations under the License. #include "imstkImplicitGeometryToPointSetCCD.h" #include "imstkCollisionData.h" #include "imstkImageData.h" -#include "imstkImplicitGeometry.h" -#include "imstkMath.h" -#include "imstkPointSet.h" -#include "imstkSignedDistanceField.h" -#include "imstkVecDataArray.h" +#include "imstkSurfaceMesh.h" namespace imstk { ImplicitGeometryToPointSetCCD::ImplicitGeometryToPointSetCCD(std::shared_ptr<ImplicitGeometry> implicitGeomA, std::shared_ptr<PointSet> pointSetB, std::shared_ptr<CollisionData> colData) : - CollisionDetection(CollisionDetection::Type::PointSetToImplicit, colData), + CollisionDetection(CollisionDetection::Type::PointSetToImplicitCCD, colData), m_implicitGeomA(implicitGeomA), m_pointSetB(pointSetB) { centralGrad.setFunction(m_implicitGeomA); if (m_implicitGeomA->getType() == Geometry::Type::SignedDistanceField) { - centralGrad.setDx(std::dynamic_pointer_cast<SignedDistanceField>(m_implicitGeomA)->getImage()->getSpacing() * 0.9); + centralGrad.setDx(std::dynamic_pointer_cast<SignedDistanceField>(m_implicitGeomA)->getImage()->getSpacing()); } - displacements = std::make_shared<VecDataArray<double, 3>>(static_cast<int>(m_pointSetB->getNumVertices())); - m_pointSetB->setVertexAttribute("displacements", displacements); + displacementsPtr = std::make_shared<VecDataArray<double, 3>>(static_cast<int>(m_pointSetB->getNumVertices())); + m_pointSetB->setVertexAttribute("displacements", displacementsPtr); +} + +static bool +findFirstRoot(std::shared_ptr<ImplicitGeometry> implicitGeomA, const Vec3d& start, const Vec3d& end, Vec3d& root) +{ + const Vec3d displacement = end - start; + + Vec3d currPos = start; + Vec3d prevPos = start; + double currDist = implicitGeomA->getFunctionValue(start); + double prevDist = currDist; + + // Root find (could be multiple roots, we want the first, so start march from front) + // Gradient could be used for SDFs to converge faster but not for levelsets + const double stepRatio = 0.01; // 100/5=20, this will fail if object moves 20xwidth of the object + const double length = displacement.norm(); + const double stepLength = length * stepRatio; + const Vec3d dir = displacement * (1.0 / length); + for (double x = stepLength; x < length; x += stepLength) + { + prevPos = currPos; + currPos = start + dir * x; + + prevDist = currDist; + currDist = implicitGeomA->getFunctionValue(currPos); + + if (currDist <= 0.0) + { + // Pick midpoint + root = (prevPos + currPos) * 0.5; + return true; + } + } + return false; } void @@ -52,71 +82,114 @@ ImplicitGeometryToPointSetCCD::computeCollisionData() { m_colData->clearAll(); + // We are going to try to catch a contact before updating via marching along + // the displacements of every point in the mesh + + // First we project the mesh to the next timepoint (without collision) + if (!m_pointSetB->hasVertexAttribute("displacements")) { LOG(WARNING) << "ImplicitGeometry to PointSet CCD can't function without displacements on geometry"; return; } - const DataArray<double>& displacementsArr = *displacements; + const VecDataArray<double, 3>& displacements = *displacementsPtr; std::shared_ptr<VecDataArray<double, 3>> vertexData = m_pointSetB->getVertexPositions(); - const VecDataArray<double, 3>& vertices = *vertexData; - ParallelUtils::parallelFor(vertices.size(), - [&](const size_t idx) - { - const Vec3d& pt = vertices[idx]; - const Vec3d displacement = Vec3d(&displacementsArr[idx * 3]); - const double length = displacement.norm(); - - Vec3d prevPos = pt; - Vec3d currPos = pt; - double prevVal = m_implicitGeomA->getFunctionValue(currPos); - double currVal = prevVal; - - // Check if the starting point of the march is inside - // This could happen if the collision wasn't fully resolved by the solver (common) - if (currVal < 0.0) - { - PositionDirectionCollisionDataElement elem; - elem.dirAtoB = -centralGrad(currPos).normalized(); // Contact Normal - elem.nodeIdx = static_cast<uint32_t>(idx); - elem.penetrationDepth = std::abs(currVal); // Could also reverse march in gradient direction - elem.posB = currPos; - m_colData->PDColData.safeAppend(elem); - return; // No need to find an other root - } + const VecDataArray<double, 3>& vertices = *vertexData; // Vertices in tentative state - // If we didn't start inside the object, march along the displacement - // smapling to see if enter it - const double stepRatio = 0.05; - const double stepLength = length * stepRatio; - const Vec3d dir = displacement * (1.0 / length); - for (double x = 0.0; x < length; x += stepLength) - { - const Vec3d diff = dir * x; + for (int i = 0; i < vertices.size(); i++) + { + const Vec3d& pt = vertices[i]; + const Vec3d& displacement = displacements[i]; + const double limit = displacement.norm() * m_depthRatioLimit; + const Vec3d prevPt = pt - displacement; - prevPos = currPos; - currPos = pt + diff; + Vec3d prevPos = prevPt; + double prevDist = m_implicitGeomA->getFunctionValue(prevPt); + bool prevIsInside = std::signbit(prevDist); - prevVal = currVal; - currVal = m_implicitGeomA->getFunctionValue(currPos); + Vec3d currPos = pt; + double currDist = m_implicitGeomA->getFunctionValue(currPos); + bool currIsInside = std::signbit(currDist); - if (currVal < 0.0) - { - // Pick midpoint - const Vec3d contactPt = (prevPos + currPos) * 0.5; + // If both inside + if (prevIsInside && currIsInside) + { + if (prevOuterElementCounter[i] > 0) + { + // Static or persistant + prevOuterElementCounter[i]++; + const Vec3d start = prevOuterElement[i]; // The last outside point in its movement history + const Vec3d end = pt; + Vec3d contactPt = Vec3d::Zero(); + if (findFirstRoot(m_implicitGeomA, start, end, contactPt)) + { PositionDirectionCollisionDataElement elem; - elem.dirAtoB = -centralGrad(contactPt).normalized(); // Contact Normal - //elem.dirAtoB = dir; - elem.nodeIdx = static_cast<uint32_t>(idx); - elem.penetrationDepth = (contactPt - pt).norm(); + elem.dirAtoB = -centralGrad(contactPt).normalized(); // -centralGrad gives Outward facing contact normal + //elem.dirAtoB = (contactPt - start).normalized(); + /* { + Vec3d gradPos = forwardGrad(contactPt); + Vec3d gradNeg = backwardGrad(contactPt); + elem.dirAtoB[0] = std::min(gradNeg[0], 0.0) + std::max(gradPos[0], 0.0); + elem.dirAtoB[1] = std::min(gradNeg[1], 0.0) + std::max(gradPos[1], 0.0); + elem.dirAtoB[2] = std::min(gradNeg[2], 0.0) + std::max(gradPos[2], 0.0); + elem.dirAtoB.normalize(); + }*/ + elem.nodeIdx = static_cast<uint32_t>(i); + //elem.penetrationDepth = (contactPt - pt).norm(); + elem.penetrationDepth = std::max(0.0, (contactPt - end).dot(elem.dirAtoB)); + if (elem.penetrationDepth <= limit) + { + // Could be useful to find the closest point on the shape, as reprojected + // contact points could be outside + elem.posB = contactPt; + m_colData->PDColData.safeAppend(elem); + } + } + } + } + // If it just entered + else if (!prevIsInside && currIsInside) + { + const Vec3d start = prevPt; + const Vec3d end = pt; + Vec3d contactPt = Vec3d::Zero(); + if (findFirstRoot(m_implicitGeomA, start, end, contactPt)) + { + PositionDirectionCollisionDataElement elem; + elem.dirAtoB = -centralGrad(contactPt).normalized(); // -centralGrad gives Outward facing contact normal + //elem.dirAtoB = (contactPt - start).normalized(); + /* { + Vec3d gradPos = forwardGrad(contactPt); + Vec3d gradNeg = backwardGrad(contactPt); + elem.dirAtoB[0] = std::max(gradNeg[0], 0.0) + std::min(gradPos[0], 0.0); + elem.dirAtoB[1] = std::max(gradNeg[1], 0.0) + std::min(gradPos[1], 0.0); + elem.dirAtoB[2] = std::max(gradNeg[2], 0.0) + std::min(gradPos[2], 0.0); + elem.dirAtoB.normalize(); + }*/ + elem.nodeIdx = static_cast<uint32_t>(i); + //elem.penetrationDepth = (contactPt - pt).norm(); + elem.penetrationDepth = std::max(0.0, (contactPt - end).dot(elem.dirAtoB)); + if (elem.penetrationDepth <= limit) + { elem.posB = contactPt; m_colData->PDColData.safeAppend(elem); - - return; } + prevOuterElementCounter[i] = 1; + // Store the previous exterior point + prevOuterElement[i] = start; } - }); + else + { + prevOuterElementCounter[i] = 0; + //printf("Somethings wrong2\n"); + } + } + else + { + prevOuterElementCounter[i] = 0; + } + } } } \ No newline at end of file diff --git a/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.h b/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.h index 9cc52e1ef951e5578783012a96f59ac225e7677a..4093f295b0440c2c4f05d55094927c5a24e6d632 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.h +++ b/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCCD.h @@ -30,15 +30,16 @@ template<typename T, int N> class VecDataArray; class ImplicitGeometry; class PointSet; -struct CollisionData; /// /// \class ImplicitGeometryToPointSetCCD /// /// \brief ImplicitGeometry to PointSet continous collision detection. /// This CD method marches along the displacement of the points in the pointset -/// to converge on the zero crossing of the implicit geometry. Does not produce -/// time of impacts +/// to converge on the zero crossing of the implicit geometry. This particular +/// version is suited for levelsets not SDFs as it caches the history of the contact +/// to avoid sampling the implicit geometry anywhere but at the surface (it will also +/// work for SDFs, though better alterations/modifications of this exist for SDFs) /// class ImplicitGeometryToPointSetCCD : public CollisionDetection { @@ -52,7 +53,9 @@ public: ImplicitGeometryToPointSetCCD(std::shared_ptr<ImplicitGeometry> implicitGeomA, std::shared_ptr<PointSet> pointSetB, std::shared_ptr<CollisionData> colData); + virtual ~ImplicitGeometryToPointSetCCD() override = default; +public: /// /// \brief Detect collision and compute collision data /// @@ -63,6 +66,12 @@ private: std::shared_ptr<PointSet> m_pointSetB; ImplicitFunctionCentralGradient centralGrad; - std::shared_ptr<VecDataArray<double, 3>> displacements; + std::shared_ptr<VecDataArray<double, 3>> displacementsPtr; + + std::unordered_map<int, Vec3d> prevOuterElement; + std::unordered_map<int, int> prevOuterElementCounter; + + // Penetration depths are clamped to this ratio * displacement of the vertex + double m_depthRatioLimit = 0.3; }; } \ No newline at end of file diff --git a/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCD.cpp b/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCD.cpp index c9ff41d78defedca1f86159d1d71f0f6203f01f5..b3628e5a213c2db0215f12aae58bb7f7edb754dd 100644 --- a/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCD.cpp +++ b/Source/CollisionDetection/CollisionDetection/imstkImplicitGeometryToPointSetCD.cpp @@ -62,7 +62,7 @@ ImplicitGeometryToPointSetCD::computeCollisionData() elem.dirAtoB = -centralGrad(pt).normalized(); // Contact Normal elem.nodeIdx = static_cast<uint32_t>(idx); elem.penetrationDepth = std::abs(signedDistance); - elem.posB = pt; + elem.posB = pt;// +elem.dirAtoB * elem.penetrationDepth; m_colData->PDColData.safeAppend(elem); } }); diff --git a/Source/CollisionDetection/imstkCDObjectFactory.cpp b/Source/CollisionDetection/imstkCDObjectFactory.cpp index d73a5944c7a5327de986511ca9a249542bf59836..76fced2e2376aa22425eed531d3c61f41518f9c3 100644 --- a/Source/CollisionDetection/imstkCDObjectFactory.cpp +++ b/Source/CollisionDetection/imstkCDObjectFactory.cpp @@ -23,11 +23,12 @@ limitations under the License. #include "imstkCollisionData.h" // Points to objects +#include "imstkImplicitGeometryToPointSetCCD.h" +#include "imstkImplicitGeometryToPointSetCD.h" #include "imstkPointSetToCapsuleCD.h" #include "imstkPointSetToPlaneCD.h" #include "imstkPointSetToSphereCD.h" #include "imstkPointSetToSurfaceMeshCD.h" -#include "imstkImplicitGeometryToPointSetCD.h" // Mesh to mesh #include "imstkMeshToMeshBruteForceCD.h" @@ -137,6 +138,14 @@ makeCollisionDetectionObject(const CollisionDetection::Type type, auto implicitGeom = std::dynamic_pointer_cast<ImplicitGeometry>(collidingGeometryB); return std::make_shared<ImplicitGeometryToPointSetCD>(implicitGeom, pointSet, colData); } + case CollisionDetection::Type::PointSetToImplicitCCD: + { + IMSTK_CHECK_FOR_VALID_GEOMETRIES(collidingGeometryA, collidingGeometryB, PointSet, ImplicitGeometry); + + auto pointSet = std::dynamic_pointer_cast<PointSet>(collidingGeometryA); + auto implicitGeom = std::dynamic_pointer_cast<ImplicitGeometry>(collidingGeometryB); + return std::make_shared<ImplicitGeometryToPointSetCCD>(implicitGeom, pointSet, colData); + } // Mesh to mesh case CollisionDetection::Type::SurfaceMeshToSurfaceMesh: { diff --git a/Source/CollisionHandling/imstkLevelSetCH.cpp b/Source/CollisionHandling/imstkLevelSetCH.cpp index de3cd8b54d8383c60ba4f43f81c3abe3a5633bd5..17a276bdb01260ccc385fdb415ac84e2d7cc79f2 100644 --- a/Source/CollisionHandling/imstkLevelSetCH.cpp +++ b/Source/CollisionHandling/imstkLevelSetCH.cpp @@ -87,7 +87,7 @@ void LevelSetCH::processCollisionData() { std::shared_ptr<LevelSetModel> lvlSetModel = m_lvlSetObj->getLevelSetModel(); - std::shared_ptr<ImageData> grid = std::dynamic_pointer_cast<ImageData>(lvlSetModel->getModelGeometry()); + std::shared_ptr<ImageData> grid = std::dynamic_pointer_cast<SignedDistanceField>(lvlSetModel->getModelGeometry())->getImage(); if (grid == nullptr) { diff --git a/Source/Controllers/imstkKeyboardControl.h b/Source/Controllers/imstkKeyboardControl.h index 1c54898e9c918103faf0316ddfd64e7f78de3e66..bfdf682d585df520fe47a34a9eba2cca22a5a9f1 100644 --- a/Source/Controllers/imstkKeyboardControl.h +++ b/Source/Controllers/imstkKeyboardControl.h @@ -39,7 +39,7 @@ class KeyEvent; class KeyboardControl : public DeviceControl { public: - KeyboardControl() { } + KeyboardControl() = default; KeyboardControl(std::shared_ptr<KeyboardDeviceClient> keyDevice); virtual ~KeyboardControl() override = default; diff --git a/Source/Controllers/imstkRigidObjectController.cpp b/Source/Controllers/imstkRigidObjectController.cpp index 0b728cd1c55b50629a098438f3a97dde6c1abde6..cf399ed69467173e5a339dae1f61dd3abf4b67fa 100644 --- a/Source/Controllers/imstkRigidObjectController.cpp +++ b/Source/Controllers/imstkRigidObjectController.cpp @@ -20,9 +20,7 @@ =========================================================================*/ #include "imstkRigidObjectController.h" -#include "imstkCollidingObject.h" #include "imstkDeviceClient.h" -#include "imstkGeometry.h" #include "imstkLogger.h" #include "imstkRbdConstraint.h" #include "imstkRigidObject2.h" @@ -136,19 +134,13 @@ RigidObjectController::applyForces() if (m_forceSmoothening) { m_forces.push_back(force); + m_forceSum += force; if (m_forces.size() > m_smoothingKernelSize) { + m_forceSum -= m_forces.front(); m_forces.pop_front(); } - - Vec3d avgForce = Vec3d(0.0, 0.0, 0.0); - int count = 0; - for (auto i : m_forces) - { - avgForce += i; - count++; - } - avgForce /= count; + const Vec3d avgForce = m_forceSum / m_forces.size(); // Render only the spring force (not the other forces the body has) m_deviceClient->setForce(avgForce); diff --git a/Source/Controllers/imstkRigidObjectController.h b/Source/Controllers/imstkRigidObjectController.h index ca33943737d80aec07b62826841d946b7afc891c..ec47c7b86db2dda3cf3dfdf8eb70ad4db359a6d0 100644 --- a/Source/Controllers/imstkRigidObjectController.h +++ b/Source/Controllers/imstkRigidObjectController.h @@ -105,16 +105,6 @@ public: int getSmoothingKernelSize() const { return m_smoothingKernelSize; } void setSmoothingKernelSize(const int kernelSize) { m_smoothingKernelSize = kernelSize; } - /// - /// \brief Return the currently applied force - /// - const Vec3d& getForce() const { return fS; } - - /// - /// \brief Return the currently applied torque - /// - const Vec3d& getTorque() const { return tS; } - public: /// /// \brief Update controlled scene object using latest tracking information @@ -128,7 +118,6 @@ public: protected: std::shared_ptr<RigidObject2> m_rigidObject; - std::deque<Vec3d> m_forces; double m_linearKd = 10000.0; ///> Damping coefficient, linear double m_angularKd = 300.0; ///> Damping coefficient, rotational @@ -143,6 +132,8 @@ protected: bool m_forceSmoothening = true; int m_smoothingKernelSize = 25; + std::deque<Vec3d> m_forces; + Vec3d m_forceSum = Vec3d::Zero(); }; } } diff --git a/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.cpp b/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.cpp index b520cd84c35b49c6bfd280303108f4feea1d8466..dcec520d0ac64798388d40fcbc8136a5084a1196 100644 --- a/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.cpp @@ -20,17 +20,11 @@ =========================================================================*/ #include "imstkLevelSetModel.h" -#include "imstkTaskGraph.h" -#include "imstkGeometryUtilities.h" -#include "imstkLogger.h" -#include "imstkSignedDistanceField.h" -#include "imstkParallelFor.h" -#include "imstkImplicitGeometry.h" #include "imstkDataArray.h" #include "imstkImageData.h" - -#include <vtkImplicitPolyDataDistance.h> -#include <vtkPolyData.h> +#include "imstkLogger.h" +#include "imstkParallelFor.h" +#include "imstkTaskGraph.h" namespace imstk { @@ -75,28 +69,29 @@ LevelSetModel::initialize() m_backwardGrad.setFunction(m_mesh); m_curvature.setFunction(m_mesh); - // If dense update, we need a gradient image, which will store forward and backward gradient magnitudes - if (m_mesh->getType() == Geometry::Type::SignedDistanceField && !m_config->m_sparseUpdate) + if (m_mesh->getType() == Geometry::Type::SignedDistanceField) { - auto sdfImage = std::dynamic_pointer_cast<SignedDistanceField>(m_mesh)->getImage(); + auto sdf = std::dynamic_pointer_cast<SignedDistanceField>(m_mesh); + std::shared_ptr<ImageData> sdfImage = sdf->getImage(); + if (!m_config->m_sparseUpdate) + { + m_gradientMagnitudes = std::make_shared<ImageData>(); + m_gradientMagnitudes->allocate(IMSTK_DOUBLE, 2, sdfImage->getDimensions(), sdfImage->getSpacing(), sdfImage->getOrigin()); - m_gradientMagnitudes = std::make_shared<ImageData>(); - m_gradientMagnitudes->allocate(IMSTK_DOUBLE, 2, sdfImage->getDimensions(), sdfImage->getSpacing(), sdfImage->getOrigin()); + /* m_curvatures = std::make_shared<ImageData>(); + m_curvatures->allocate(IMSTK_DOUBLE, 1, sdfImage->getDimensions(), sdfImage->getSpacing(), sdfImage->getOrigin());*/ - m_curvatures = std::make_shared<ImageData>(); - m_curvatures->allocate(IMSTK_DOUBLE, 1, sdfImage->getDimensions(), sdfImage->getSpacing(), sdfImage->getOrigin()); + m_velocities = std::make_shared<ImageData>(); + m_velocities->allocate(IMSTK_DOUBLE, 1, sdfImage->getDimensions(), sdfImage->getSpacing(), sdfImage->getOrigin()); + //std::fill_n(static_cast<double*>(m_velocities->getScalars()->getVoidPointer()), m_velocities->get + } - m_velocities = std::make_shared<ImageData>(); - m_velocities->allocate(IMSTK_DOUBLE, 1, sdfImage->getDimensions(), sdfImage->getSpacing(), sdfImage->getOrigin()); - //std::fill_n(static_cast<double*>(m_velocities->getScalars()->getVoidPointer()), m_velocities->get + const Vec3d actualSpacing = sdf->getImage()->getSpacing();// *sdf->getScale(); + m_forwardGrad.setDx(Vec3i(1, 1, 1), actualSpacing); + m_backwardGrad.setDx(Vec3i(1, 1, 1), actualSpacing); + m_curvature.setDx(Vec3i(1, 1, 1), actualSpacing); } - m_forwardGrad.setDx(Vec3i(1, 1, 1), std::dynamic_pointer_cast<SignedDistanceField>(m_mesh)->getImage()->getSpacing()); - m_backwardGrad.setDx(Vec3i(1, 1, 1), std::dynamic_pointer_cast<SignedDistanceField>(m_mesh)->getImage()->getSpacing()); - /*forwardGrad.setDx(std::dynamic_pointer_cast<SignedDistanceField>(m_mesh)->getImage()->getSpacing()); - backwardGrad.setDx(std::dynamic_pointer_cast<SignedDistanceField>(m_mesh)->getImage()->getSpacing());*/ - m_curvature.setDx(Vec3i(1, 1, 1), std::dynamic_pointer_cast<SignedDistanceField>(m_mesh)->getImage()->getSpacing()); - return true; } @@ -115,7 +110,8 @@ LevelSetModel::evolve() auto imageData = std::dynamic_pointer_cast<ImageData>(sdf->getImage()); double* imgPtr = static_cast<double*>(imageData->getVoidPointer()); const Vec3i& dim = imageData->getDimensions(); - const double dt = m_config->m_dt; + const Vec3d& spacing = imageData->getSpacing(); + const double dt = m_config->m_dt / m_config->m_substeps; //const double k = m_config->m_k; if (m_config->m_sparseUpdate) @@ -131,57 +127,58 @@ LevelSetModel::evolve() nodeUpdates.reserve(m_nodesToUpdate.size()); // Compute gradients - for (std::unordered_map<size_t, std::tuple<Vec3i, double>>::iterator iter = m_nodesToUpdate.begin(); iter != m_nodesToUpdate.end(); iter++) + const double constantVel = m_config->m_constantVelocity; + for (int j = 0; j < m_config->m_substeps; j++) { - const size_t index = iter->first; - const Vec3i& coords = std::get<0>(iter->second); - const double f = std::get<1>(iter->second); - - // Gradients - const Vec3d gradPos = m_forwardGrad(Vec3d(coords[0], coords[1], coords[2])); - const Vec3d gradNeg = m_backwardGrad(Vec3d(coords[0], coords[1], coords[2])); + nodeUpdates.clear(); + for (std::unordered_map<size_t, std::tuple<Vec3i, double>>::iterator iter = m_nodesToUpdate.begin(); iter != m_nodesToUpdate.end(); iter++) + { + const size_t index = iter->first; + const Vec3i& coords = std::get<0>(iter->second); + const double f = std::get<1>(iter->second); - const double posMag = - std::pow(std::max(gradNeg[0], 0.0), 2) + std::pow(std::min(gradPos[0], 0.0), 2) + - std::pow(std::max(gradNeg[1], 0.0), 2) + std::pow(std::min(gradPos[1], 0.0), 2) + - std::pow(std::max(gradNeg[2], 0.0), 2) + std::pow(std::min(gradPos[2], 0.0), 2); + // Gradients + const Vec3d gradPos = m_forwardGrad(Vec3d(coords[0], coords[1], coords[2])); + const Vec3d gradNeg = m_backwardGrad(Vec3d(coords[0], coords[1], coords[2])); - const double negMag = - std::pow(std::min(gradNeg[0], 0.0), 2) + std::pow(std::max(gradPos[0], 0.0), 2) + - std::pow(std::min(gradNeg[1], 0.0), 2) + std::pow(std::max(gradPos[1], 0.0), 2) + - std::pow(std::min(gradNeg[2], 0.0), 2) + std::pow(std::max(gradPos[2], 0.0), 2); + const double posMag = + std::pow(std::max(gradNeg[0], 0.0), 2) + std::pow(std::min(gradPos[0], 0.0), 2) + + std::pow(std::max(gradNeg[1], 0.0), 2) + std::pow(std::min(gradPos[1], 0.0), 2) + + std::pow(std::max(gradNeg[2], 0.0), 2) + std::pow(std::min(gradPos[2], 0.0), 2); - // Curvature - //const double kappa = m_curvature(Vec3d(coords[0], coords[1], coords[2])); + const double negMag = + std::pow(std::min(gradNeg[0], 0.0), 2) + std::pow(std::max(gradPos[0], 0.0), 2) + + std::pow(std::min(gradNeg[1], 0.0), 2) + std::pow(std::max(gradPos[1], 0.0), 2) + + std::pow(std::min(gradNeg[2], 0.0), 2) + std::pow(std::max(gradPos[2], 0.0), 2); - nodeUpdates.push_back(std::tuple<size_t, Vec3i, double, Vec2d, double>(index, coords, f, Vec2d(negMag, posMag), 0.0)); - } + // Curvature + //const double kappa = m_curvature(Vec3d(coords[0], coords[1], coords[2])); - // Update levelset - const double constantVel = m_config->m_constantVelocity; - for (size_t i = 0; i < nodeUpdates.size(); i++) - { - const size_t index = std::get<0>(nodeUpdates[i]); - //const Vec3i& coords = std::get<1>(nodeUpdates[i]); - const double vel = std::get<2>(nodeUpdates[i]) + constantVel; - const Vec2d& g = std::get<3>(nodeUpdates[i]); - //const double kappa = std::get<4>(nodeUpdates[i]); - - // If speed function positive use forward difference (posMag) - if (vel > 0.0) - { - imgPtr[index] += dt * (vel * std::sqrt(g[0]) /*+ kappa*/); + nodeUpdates.push_back(std::tuple<size_t, Vec3i, double, Vec2d, double>(index, coords, f, Vec2d(negMag, posMag), 0.0)); } - // If speed function negative use backward difference (negMag) - else if (vel < 0.0) + + // Update levelset + for (size_t i = 0; i < nodeUpdates.size(); i++) { - imgPtr[index] += dt * (vel * std::sqrt(g[1]) /*+ kappa * k*/); + const size_t index = std::get<0>(nodeUpdates[i]); + const double vel = std::get<2>(nodeUpdates[i]) + constantVel; + const Vec2d& g = std::get<3>(nodeUpdates[i]); + //const double kappa = std::get<4>(nodeUpdates[i]); + + // If speed function positive use forward difference (posMag) + if (vel > 0.0) + { + imgPtr[index] += dt * (vel * std::sqrt(g[0]) /*+ kappa * k*/); + } + // If speed function negative use backward difference (negMag) + else if (vel < 0.0) + { + imgPtr[index] += dt * (vel * std::sqrt(g[1]) /*+ kappa * k*/); + } } } - if (m_nodesToUpdate.size() > 0) - { - m_nodesToUpdate.clear(); - } + + m_nodesToUpdate.clear(); } else { @@ -222,7 +219,6 @@ LevelSetModel::evolve() } }); - // Uniform advance const double constantVel = m_config->m_constantVelocity; ParallelUtils::parallelFor(dim[0] * dim[1] * dim[2], [&](const int& i) diff --git a/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.h b/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.h index 7bae77b492198c970e8d18c3076b07b2170882a2..7263a2ca5f9d3b3ee127011a9a4183aa271cf904 100644 --- a/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkLevelSetModel.h @@ -39,6 +39,7 @@ struct LevelSetModelConfig bool m_useCurvature = false; double m_k = 0.05; // Curvature term double m_constantVelocity = 0.0; // Constant velocity + int m_substeps = 10; // How many steps to do every iteration }; /// @@ -85,7 +86,14 @@ public: virtual void evolve(); + /// + /// \brief Add an impulse to the velocity field of the levelset + /// This actually takes the max of the current and provided + /// void addImpulse(const Vec3i& coord, double f); + /// + /// \brief Set the impulse in the velocity field, replaces + /// void setImpulse(const Vec3i& coord, double f); std::shared_ptr<TaskNode> getQuantityEvolveNode(size_t i) const { return m_evolveQuantitiesNodes[i]; } @@ -115,8 +123,6 @@ protected: // suspect floating point error StructuredForwardGradient m_forwardGrad; StructuredBackwardGradient m_backwardGrad; - /*ImplicitFunctionForwardGradient forwardGrad; - ImplicitFunctionBackwardGradient backwardGrad;*/ ImplicitStructuredCurvature m_curvature; }; diff --git a/Source/Filtering/imstkFastMarch.cpp b/Source/Filtering/imstkFastMarch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0958ca2e69508f41fc02e26b37b01b2672273b2e --- /dev/null +++ b/Source/Filtering/imstkFastMarch.cpp @@ -0,0 +1,216 @@ +/*========================================================================= + + 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 "imstkFastMarch.h" +#include "imstkLogger.h" +#include "imstkVecDataArray.h" + +namespace imstk +{ +void +FastMarch::solve() +{ + // Get the scalars (ensure they're single component doubles) + std::shared_ptr<AbstractDataArray> abstractScalars = imageData->getScalars(); + if (abstractScalars->getScalarType() != IMSTK_DOUBLE || abstractScalars->getNumberOfComponents() != 1) + { + LOG(WARNING) << "fastMarch only works with single component double images"; + return; + } + auto scalars = std::dynamic_pointer_cast<DataArray<double>>(abstractScalars); + double* imgPtr = scalars->getPointer(); + dim = imageData->getDimensions(); + spacing = imageData->getSpacing(); + indexShift = dim[0] * dim[1]; + + // We maintain a solution in maps, to keep things sparse + + // Sparse container for which nodes are marked visited + visited = std::unordered_set<int>(); + // Sparse container for nodal distances + distances = std::unordered_map<int, double>(); // Solved distances + + queue = std::priority_queue<Node, std::vector<Node>, NodeComparator>(); + + // Add the initial seeds to the queue + for (int i = 0; i < seedVoxels.size(); i++) + { + Vec3i coord = seedVoxels[i]; + if (coord[0] < 0 || coord[0] >= dim[0] + || coord[1] < 0 || coord[1] >= dim[1] + || coord[2] < 0 || coord[2] >= dim[2]) + { + continue; + } + const int index = static_cast<int>(imageData->getScalarIndex(coord)); + distances[index] = imgPtr[imageData->getScalarIndex(coord)]; + queue.push(Node(index, 0.0, coord)); + } + + // Process every node in order of minimum distance + while (!queue.empty()) + { + Node node = queue.top(); + queue.pop(); + + const Vec3i& coord = node.m_coord; + const int& nodeId = node.m_nodeId; + + // Termination conditions + if (isVisited(nodeId) + || getDistance(nodeId) >= distThreshold) + { + continue; + } + + // Mark node as visited (to avoid readdition) + visited.insert(nodeId); + + // Update all its neighbor cells (diagonals not considered neighbors) + // Right +x + int neighborId = nodeId + 1; + Vec3i neighborCoord = coord + Vec3i(1, 0, 0); + if (neighborCoord[0] < dim[0] && !isVisited(neighborId)) + { + solveNode(neighborCoord, neighborId); + } + // Left -x + neighborId = nodeId - 1; + neighborCoord = coord - Vec3i(1, 0, 0); + if (neighborCoord[0] >= 0 && !isVisited(neighborId)) + { + solveNode(neighborCoord, neighborId); + } + + // Up +y + neighborId = nodeId + dim[0]; + neighborCoord = coord + Vec3i(0, 1, 0); + if (neighborCoord[1] < dim[1] && !isVisited(neighborId)) + { + solveNode(neighborCoord, neighborId); + } + // Down -y + neighborId = nodeId - dim[0]; + neighborCoord = coord - Vec3i(0, 1, 0); + if (neighborCoord[1] >= 0 && !isVisited(neighborId)) + { + solveNode(neighborCoord, neighborId); + } + + // Forward +z + neighborId = nodeId + indexShift; + neighborCoord = coord + Vec3i(0, 0, 1); + if (neighborCoord[2] < dim[2] && !isVisited(neighborId)) + { + solveNode(neighborCoord, neighborId); + } + // Backward -z + neighborId = nodeId - indexShift; + neighborCoord = coord - Vec3i(0, 0, 1); + if (neighborCoord[2] >= 0 && !isVisited(neighborId)) + { + solveNode(neighborCoord, neighborId); + } + } + + // Write the sparse distances to the image + for (auto i : distances) + { + imgPtr[i.first] = i.second; + } +} + +void +FastMarch::solveNode(Vec3i coord, int index) +{ + // Compute the min distance in each axes + const double dists[6] = + { + coord[0] - 1 >= 0 ? getDistance(index - 1) : IMSTK_DOUBLE_MAX, + coord[0] + 1 < dim[0] ? getDistance(index + 1) : IMSTK_DOUBLE_MAX, + coord[1] - 1 >= 0 ? getDistance(index - dim[0]) : IMSTK_DOUBLE_MAX, + coord[1] + 1 < dim[1] ? getDistance(index + dim[0]) : IMSTK_DOUBLE_MAX, + coord[2] - 1 >= 0 ? getDistance(index - indexShift) : IMSTK_DOUBLE_MAX, + coord[2] + 1 < dim[2] ? getDistance(index + indexShift) : IMSTK_DOUBLE_MAX + }; + const double minDist[3] = + { + std::min(dists[0], dists[1]), + std::min(dists[2], dists[3]), + std::min(dists[4], dists[5]) + }; + + // Sort so that the min of minDist is first + int dimReorder[3] = { 0, 1, 2 }; + for (int i = 0; i < 3; i++) + { + for (int j = i + 1; j < 3; j++) + { + const int dim1 = dimReorder[i]; + const int dim2 = dimReorder[j]; + if (minDist[dim1] > minDist[dim2]) + { + std::swap(dimReorder[i], dimReorder[j]); + } + } + } + + double aa = 0.0; + double bb = 0.0; + double cc = -1.0; + + // For every dimension + double solution = IMSTK_DOUBLE_MAX; + double discrim = 0.0; + // todo: Sort dimensions by minDist, start with smallest (break faster) + for (unsigned int i = 0; i < 3; i++) + { + const double value = minDist[dimReorder[i]]; + if (solution >= value) + { + const double spaceFactor = std::sqrt(1.0 / spacing[dimReorder[i]]); + aa += spaceFactor; + bb += value * spaceFactor; + cc += std::pow(value, 2) * spaceFactor; + + discrim = std::pow(bb, 2) - aa * cc; + if (discrim < 0.0) + { + // Whoops + return; + } + + solution = (std::sqrt(discrim) + bb) / aa; + } + else + { + break; + } + } + + if (solution < IMSTK_DOUBLE_MAX) + { + // Accept it as the new distance + distances[index] = solution; + queue.push(Node(index, solution, coord)); + } +} +} \ No newline at end of file diff --git a/Source/Filtering/imstkFastMarch.h b/Source/Filtering/imstkFastMarch.h new file mode 100644 index 0000000000000000000000000000000000000000..d0965da950e21226edc4d7d6a2334c755c051799 --- /dev/null +++ b/Source/Filtering/imstkFastMarch.h @@ -0,0 +1,86 @@ +/*========================================================================= + + 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 "imstkImageData.h" + +#include <unordered_set> +#include <queue> + +namespace imstk +{ +class FastMarch +{ +protected: + // Setup Node struct for priority queue + struct Node + { + int m_nodeId; + double m_cost; + Vec3i m_coord; + + Node(int nodeId, double cost, Vec3i coord) : + m_nodeId(nodeId), m_cost(cost), m_coord(coord) + { + } + }; + struct NodeComparator + { + bool operator()(const Node& a, const Node& b) + { + return a.m_cost > b.m_cost; + } + }; + +public: + bool isVisited(int nodeId) { return visited.count(nodeId) == 0 ? false : true; } + double getDistance(int nodeId) { return distances.count(nodeId) == 0 ? IMSTK_DOUBLE_MAX : distances.at(nodeId); } + + void solve(); + + void solveNode(Vec3i coord, int index); + + void setSeeds(std::vector<Vec3i> seedVoxels) { this->seedVoxels = seedVoxels; } + void setVisited(std::unordered_set<int> visited) { this->visited = visited; } + void setDistnaces(std::unordered_map<int, double> distances) { this->distances = distances; } + void setImage(std::shared_ptr<ImageData> image) { imageData = image; } + void setDistThreshold(double distThreshold) { this->distThreshold = distThreshold; } + +protected: + // The image to operate on + std::shared_ptr<ImageData> imageData; + Vec3i dim; + Vec3d spacing; + int indexShift; + + std::unordered_set<int> visited; + std::unordered_map<int, double> distances; + + // The starting voxels + std::vector<Vec3i> seedVoxels; + + // Distance to go too + double distThreshold; + + std::priority_queue<Node, std::vector<Node>, NodeComparator> queue; +}; +} \ No newline at end of file diff --git a/Source/Geometry/Analytic/imstkCube.h b/Source/Geometry/Analytic/imstkCube.h index eb3d3470ea06963377afa574718ad9ccc61dc1fc..2ac49400ef44dd3414a62f196d98ccb0624df018 100644 --- a/Source/Geometry/Analytic/imstkCube.h +++ b/Source/Geometry/Analytic/imstkCube.h @@ -72,8 +72,14 @@ public: /// double getFunctionValue(const Vec3d& pos) const override { - const Vec3d dmin = pos - m_position - Vec3d(m_width, m_width, m_width) * 0.5; - const Vec3d dmax = m_position - pos - Vec3d(m_width, m_width, m_width) * 0.5; + // Unrotate the point and cube to axes align the cube + const Mat3d r = Quatd::FromTwoVectors(m_orientationAxisPostTransform, Vec3d(0.0, 1.0, 0.0)).toRotationMatrix(); + const Vec3d p = r * pos; + const Vec3d center = r * m_positionPostTransform; + + // Then test + const Vec3d dmin = p - center - Vec3d(m_widthPostTransform, m_widthPostTransform, m_widthPostTransform) * 0.5; + const Vec3d dmax = center - p - Vec3d(m_widthPostTransform, m_widthPostTransform, m_widthPostTransform) * 0.5; const Vec3d d = dmin.cwiseMax(dmax); return std::max(std::max(d[0], d[1]), d[2]); } diff --git a/Source/Geometry/imstkGeometryUtilities.cpp b/Source/Geometry/imstkGeometryUtilities.cpp index 3ffea8e3c63e392eb33522d1dc1d41514bac5c47..c48273963825de52bcf983c3d33d8a2758365aab 100644 --- a/Source/Geometry/imstkGeometryUtilities.cpp +++ b/Source/Geometry/imstkGeometryUtilities.cpp @@ -20,20 +20,18 @@ =========================================================================*/ #include "imstkGeometryUtilities.h" -#include "imstkVecDataArray.h" +#include "imstkCube.h" #include "imstkHexahedralMesh.h" #include "imstkImageData.h" #include "imstkLineMesh.h" #include "imstkLogger.h" +#include "imstkMacros.h" #include "imstkParallelUtils.h" +#include "imstkSphere.h" #include "imstkSurfaceMesh.h" #include "imstkTetrahedralMesh.h" -#include "imstkCube.h" -#include "imstkSphere.h" -#include "imstkPlane.h" - -#include "imstkMacros.h" #include "imstkTypes.h" +#include "imstkVecDataArray.h" #include <vtkAppendPolyData.h> #include <vtkCellData.h> @@ -45,7 +43,6 @@ #include <vtkFloatArray.h> #include <vtkImageData.h> #include <vtkMassProperties.h> -#include <vtkPlaneSource.h> #include <vtkPointData.h> #include <vtkShortArray.h> #include <vtkSphereSource.h> @@ -729,7 +726,10 @@ GeometryUtils::toCubeSurfaceMesh(std::shared_ptr<Cube> cube) vtkNew<vtkTriangleFilter> triangulate; triangulate->SetInputData(transformCube->GetOutput()); triangulate->Update(); - return copyToSurfaceMesh(triangulate->GetOutput()); + vtkNew<vtkCleanPolyData> cleanData; + cleanData->SetInputData(triangulate->GetOutput()); + cleanData->Update(); + return copyToSurfaceMesh(cleanData->GetOutput()); } std::shared_ptr<SurfaceMesh> @@ -743,33 +743,20 @@ GeometryUtils::toUVSphereSurfaceMesh(std::shared_ptr<Sphere> sphere, sphereSource->SetThetaResolution(thetaDivisions); sphereSource->Update(); - return copyToSurfaceMesh(sphereSource->GetOutput()); -} - -std::shared_ptr<SurfaceMesh> -GeometryUtils::toQuadSurfaceMesh(std::shared_ptr<Plane> plane) -{ - const Quatd r = Quatd::FromTwoVectors(Vec3d(0.0, 1.0, 0.0), plane->getOrientationAxis()); - const Vec3d i = r._transformVector(Vec3d(1.0, 0.0, 0.0)); - const Vec3d j = r._transformVector(Vec3d(0.0, 0.0, 1.0)); - - //Vec3d p1 = plane->getPosition() + plane->getWidth() * (i + j); - Vec3d p2 = plane->getPosition() + plane->getWidth() * (i - j); - Vec3d p3 = plane->getPosition() + plane->getWidth() * (-i + j); - Vec3d p4 = plane->getPosition() + plane->getWidth() * (-i - j); - - vtkNew<vtkPlaneSource> planeSource; - planeSource->SetOrigin(p4.data()); - planeSource->SetPoint1(p3.data()); - planeSource->SetPoint2(p2.data()); - planeSource->Update(); + vtkNew<vtkTransform> transform; + transform->SetMatrix(mat4dTranslate(sphere->getPosition()).data()); + vtkNew<vtkTransformFilter> transformFilter; + transformFilter->SetInputData(sphereSource->GetOutput()); + transformFilter->SetTransform(transform); + transformFilter->Update(); vtkNew<vtkTriangleFilter> triangulate; - triangulate->SetInputData(planeSource->GetOutput()); + triangulate->SetInputData(transformFilter->GetOutput()); triangulate->Update(); vtkNew<vtkCleanPolyData> cleanData; - cleanData->SetInputConnection(triangulate->GetOutputPort()); + cleanData->SetInputData(triangulate->GetOutput()); cleanData->Update(); + return copyToSurfaceMesh(cleanData->GetOutput()); } diff --git a/Source/Scene/imstkRigidObjectCollisionPair.cpp b/Source/Scene/imstkRigidObjectCollisionPair.cpp index 38dc4e788383a3e4d59576d27fa89359b292e9e4..9a049432df890fc3daebde5f9a38b4cc8a0101cf 100644 --- a/Source/Scene/imstkRigidObjectCollisionPair.cpp +++ b/Source/Scene/imstkRigidObjectCollisionPair.cpp @@ -22,11 +22,11 @@ limitations under the License. #include "imstkRigidObjectCollisionPair.h" #include "imstkCDObjectFactory.h" #include "imstkCollisionData.h" +#include "imstkPointSet.h" #include "imstkRigidBodyCH.h" +#include "imstkRigidBodyModel2.h" #include "imstkRigidObject2.h" #include "imstkTaskGraph.h" -#include "imstkRigidBodyModel2.h" -#include "imstkLogger.h" namespace imstk { @@ -74,5 +74,81 @@ RigidObjectCollisionPair::RigidObjectCollisionPair(std::shared_ptr<RigidObject2> auto ch = std::make_shared<RigidBodyCH>(side, m_colData, obj1, obj2); setCollisionHandlingAB(ch); } + +void +RigidObjectCollisionPair::apply() +{ + CollisionPair::apply(); + + auto obj1 = std::dynamic_pointer_cast<RigidObject2>(m_objects.first); + std::shared_ptr<RigidBodyModel2> rbdModel = obj1->getRigidBodyModel2(); + std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(obj1->getPhysicsGeometry()); + const bool measureDisplacements = (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")); + + // The tentative body is never actually computed, it should be good to catch the contact + // in the next frame + if (measureDisplacements) + { + // 1.) Copy the vertices at the start of the frame + obj1->getTaskGraph()->insertBefore(obj1->getRigidBodyModel2()->getComputeTentativeVelocitiesNode(), + std::make_shared<TaskNode>([ = ]() + { + copyVertsToPrevious(); + }, "CopyVertsToPrevious")); + + // If you were to update to tentative, you'd do it here, then compute displacements + + // 2.) Compute the displacements after updating geometry + obj1->getTaskGraph()->insertAfter(obj1->getUpdateGeometryNode(), + std::make_shared<TaskNode>([ = ]() + { + measureDisplacementFromPrevious(); + }, "ComputeDisplacements")); + } +} + +void +RigidObjectCollisionPair::copyVertsToPrevious() +{ + auto obj1 = std::dynamic_pointer_cast<RigidObject2>(m_objects.first); + std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(obj1->getPhysicsGeometry()); + + if (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")) + { + std::shared_ptr<VecDataArray<double, 3>> verticesPtr = pointSet->getVertexPositions(); + VecDataArray<double, 3>& vertices = *verticesPtr; + VecDataArray<double, 3>& prevVertices = *m_prevVertices; + + if (prevVertices.size() != vertices.size()) + { + prevVertices.resize(vertices.size()); + } + std::copy_n(vertices.getPointer(), vertices.size(), prevVertices.getPointer()); + } +} + +void +RigidObjectCollisionPair::measureDisplacementFromPrevious() +{ + auto obj1 = std::dynamic_pointer_cast<RigidObject2>(m_objects.first); + std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(obj1->getPhysicsGeometry()); + + if (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")) + { + std::shared_ptr<VecDataArray<double, 3>> displacements = + std::dynamic_pointer_cast<VecDataArray<double, 3>>(pointSet->getVertexAttribute("displacements")); + VecDataArray<double, 3>& displacementsArr = *displacements; + + std::shared_ptr<VecDataArray<double, 3>> verticesPtr = pointSet->getVertexPositions(); + VecDataArray<double, 3>& vertices = *verticesPtr; + VecDataArray<double, 3>& prevVertices = *m_prevVertices; + + ParallelUtils::parallelFor(displacements->size(), + [&](const int i) + { + displacementsArr[i] = vertices[i] - prevVertices[i]; + }); + } +} } } \ No newline at end of file diff --git a/Source/Scene/imstkRigidObjectCollisionPair.h b/Source/Scene/imstkRigidObjectCollisionPair.h index 3fccbcdd10c0fc7da6669a764b661238118d6035..3be8b0db3db3f8f75612499c6b74b37c83880a51 100644 --- a/Source/Scene/imstkRigidObjectCollisionPair.h +++ b/Source/Scene/imstkRigidObjectCollisionPair.h @@ -26,6 +26,8 @@ limitations under the License. namespace imstk { +template<typename T, int N> class VecDataArray; + namespace expiremental { class RigidObject2; @@ -44,6 +46,16 @@ public: CollisionDetection::Type cdType); virtual ~RigidObjectCollisionPair() override = default; + +public: + void apply() override; + + void copyVertsToPrevious(); + + void measureDisplacementFromPrevious(); + +public: + std::shared_ptr<VecDataArray<double, 3>> m_prevVertices; }; } } \ No newline at end of file diff --git a/Source/Scene/imstkRigidObjectLevelSetCollisionPair.cpp b/Source/Scene/imstkRigidObjectLevelSetCollisionPair.cpp index b328c76a6013a74584f18845c63237e61b8ccb3c..825ec75c0b689291f39c2d39f4a065e63d5dad8d 100644 --- a/Source/Scene/imstkRigidObjectLevelSetCollisionPair.cpp +++ b/Source/Scene/imstkRigidObjectLevelSetCollisionPair.cpp @@ -22,25 +22,24 @@ limitations under the License. #include "imstkRigidObjectLevelSetCollisionPair.h" #include "imstkCDObjectFactory.h" #include "imstkCollisionData.h" +#include "imstkImplicitGeometry.h" +#include "imstkImplicitGeometryToPointSetCCD.h" +#include "imstkImplicitGeometryToPointSetCD.h" #include "imstkLevelSetCH.h" #include "imstkLevelSetDeformableObject.h" #include "imstkLevelSetModel.h" #include "imstkRigidBodyCH.h" #include "imstkRigidBodyModel2.h" #include "imstkRigidObject2.h" +#include "imstkSurfaceMesh.h" #include "imstkTaskGraph.h" -#include "imstkPointSet.h" -#include "imstkImplicitGeometry.h" - -#include "imstkImplicitGeometryToPointSetCD.h" -#include "imstkImplicitGeometryToPointSetCCD.h" namespace imstk { namespace expiremental { RigidObjectLevelSetCollisionPair::RigidObjectLevelSetCollisionPair(std::shared_ptr<RigidObject2> obj1, std::shared_ptr<LevelSetDeformableObject> obj2) : - CollisionPair(obj1, obj2) + CollisionPair(obj1, obj2), m_prevVertices(std::make_shared<VecDataArray<double, 3>>()) { std::shared_ptr<RigidBodyModel2> rbdModel = obj1->getRigidBodyModel2(); std::shared_ptr<LevelSetModel> lvlSetModel = obj2->getLevelSetModel(); @@ -76,5 +75,80 @@ RigidObjectLevelSetCollisionPair::RigidObjectLevelSetCollisionPair(std::shared_p setCollisionHandlingA(std::make_shared<RigidBodyCH>(CollisionHandling::Side::A, m_colData, obj1, nullptr, 0.0, 0.0)); setCollisionHandlingB(std::make_shared<LevelSetCH>(CollisionHandling::Side::B, m_colData, obj2, obj1)); } + +void +RigidObjectLevelSetCollisionPair::apply() +{ + CollisionPair::apply(); + + auto obj1 = std::dynamic_pointer_cast<RigidObject2>(m_objects.first); + std::shared_ptr<RigidBodyModel2> rbdModel = obj1->getRigidBodyModel2(); + std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(obj1->getPhysicsGeometry()); + const bool measureDisplacements = (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")); + + // The tentative body is never actually computed, it should be good to catch the contact + // in the next frame + if (measureDisplacements) + { + // 1.) Copy the vertices at the start of the frame + obj1->getTaskGraph()->insertBefore(obj1->getRigidBodyModel2()->getComputeTentativeVelocitiesNode(), + std::make_shared<TaskNode>([ = ]() + { + copyVertsToPrevious(); + }, "CopyVertsToPrevious")); + + // If you were to update to tentative, you'd do it here, then compute displacements + + // 2.) Compute the displacements after updating geometry + std::shared_ptr<TaskNode> computeDisplacements = + std::make_shared<TaskNode>(std::bind(&RigidObjectLevelSetCollisionPair::measureDisplacementFromPrevious, this), "ComputeDisplacements"); + obj1->getTaskGraph()->insertAfter(obj1->getUpdateGeometryNode(), + computeDisplacements); + } +} + +void +RigidObjectLevelSetCollisionPair::copyVertsToPrevious() +{ + auto obj1 = std::dynamic_pointer_cast<RigidObject2>(m_objects.first); + std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(obj1->getPhysicsGeometry()); + + if (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")) + { + std::shared_ptr<VecDataArray<double, 3>> verticesPtr = pointSet->getVertexPositions(); + VecDataArray<double, 3>& vertices = *verticesPtr; + VecDataArray<double, 3>& prevVertices = *m_prevVertices; + + if (prevVertices.size() != vertices.size()) + { + prevVertices.resize(vertices.size()); + } + std::copy_n(vertices.getPointer(), vertices.size(), prevVertices.getPointer()); + } +} + +void +RigidObjectLevelSetCollisionPair::measureDisplacementFromPrevious() +{ + auto obj1 = std::dynamic_pointer_cast<RigidObject2>(m_objects.first); + std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(obj1->getPhysicsGeometry()); + + if (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")) + { + std::shared_ptr<VecDataArray<double, 3>> displacements = + std::dynamic_pointer_cast<VecDataArray<double, 3>>(pointSet->getVertexAttribute("displacements")); + VecDataArray<double, 3>& displacementsArr = *displacements; + + std::shared_ptr<VecDataArray<double, 3>> verticesPtr = pointSet->getVertexPositions(); + VecDataArray<double, 3>& vertices = *verticesPtr; + VecDataArray<double, 3>& prevVertices = *m_prevVertices; + + ParallelUtils::parallelFor(displacements->size(), + [&](const int i) + { + displacementsArr[i] = vertices[i] - prevVertices[i]; + }); + } +} } } \ No newline at end of file diff --git a/Source/Scene/imstkRigidObjectLevelSetCollisionPair.h b/Source/Scene/imstkRigidObjectLevelSetCollisionPair.h index 8bd6b806ea860a53d29ce2148c895174f73be104..a43acdd11a828479ddc5257e70a26b5621640f90 100644 --- a/Source/Scene/imstkRigidObjectLevelSetCollisionPair.h +++ b/Source/Scene/imstkRigidObjectLevelSetCollisionPair.h @@ -27,6 +27,7 @@ limitations under the License. namespace imstk { class LevelSetDeformableObject; +template<typename T, int N> class VecDataArray; namespace expiremental { @@ -43,6 +44,15 @@ class RigidObjectLevelSetCollisionPair : public CollisionPair public: RigidObjectLevelSetCollisionPair(std::shared_ptr<RigidObject2> obj1, std::shared_ptr<LevelSetDeformableObject> obj2); virtual ~RigidObjectLevelSetCollisionPair() override = default; + +public: + void apply() override; + + void copyVertsToPrevious(); + void measureDisplacementFromPrevious(); + +public: + std::shared_ptr<VecDataArray<double, 3>> m_prevVertices; }; } } \ No newline at end of file diff --git a/Source/SceneEntities/Objects/imstkRigidObject2.cpp b/Source/SceneEntities/Objects/imstkRigidObject2.cpp index 89c221991f6ae36732d88f3018c295b0702baa40..526e830dc098ebd6fc7be2babee1dcef9e125b67 100644 --- a/Source/SceneEntities/Objects/imstkRigidObject2.cpp +++ b/Source/SceneEntities/Objects/imstkRigidObject2.cpp @@ -23,9 +23,6 @@ #include "imstkLogger.h" #include "imstkRbdConstraint.h" #include "imstkRigidBodyModel2.h" -#include "imstkPointSet.h" -#include "imstkVecDataArray.h" -#include "imstkParallelFor.h" namespace imstk { @@ -68,28 +65,6 @@ RigidObject2::setDynamicalModel(std::shared_ptr<AbstractDynamicalModel> dynaMode void RigidObject2::updatePhysicsGeometry() { - // Record displacements (useful for CCD) - std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(m_physicsGeometry); - if (pointSet != nullptr && pointSet->hasVertexAttribute("displacements")) - { - const Vec3d& linearVelocity = m_rigidBody->getVelocity(); - const Vec3d& angularVelocity = m_rigidBody->getAngularVelocity(); - - std::shared_ptr<VecDataArray<double, 3>> displacements = - std::dynamic_pointer_cast<VecDataArray<double, 3>>(pointSet->getVertexAttribute("displacements")); - VecDataArray<double, 3>& displacementsArr = *displacements; - - ParallelUtils::parallelFor(displacements->size(), [&](const int i) - { - const Vec3d diff = pointSet->getVertexPosition(i) - m_rigidBody->getPosition(); - const Vec3d velocity = linearVelocity + angularVelocity.cross(diff); - const Vec3d displacement = velocity * m_rigidBodyModel2->getConfig()->m_dt; - displacementsArr[i][0] = displacement[0]; - displacementsArr[i][1] = displacement[1]; - displacementsArr[i][2] = displacement[2]; - }); - } - // Apply the transform back to the geometry m_physicsGeometry->setTranslation(m_rigidBody->getPosition()); m_physicsGeometry->setRotation(m_rigidBody->getOrientation()); diff --git a/Source/SceneEntities/Objects/imstkRigidObject2.h b/Source/SceneEntities/Objects/imstkRigidObject2.h index 9cd786ba784586d92672dfc976fad3ae0b8bb571..1a913e365d1746819e318fa4c5af4fc7ab298772 100644 --- a/Source/SceneEntities/Objects/imstkRigidObject2.h +++ b/Source/SceneEntities/Objects/imstkRigidObject2.h @@ -35,13 +35,15 @@ struct RigidBody; /// /// \class RigidObject2 /// -/// \brief Scene objects that are governed by rigid body dynamics +/// \brief Scene objects that are governed by rigid body dynamics under +/// the RigidBodyModel2 /// class RigidObject2 : public DynamicObject { public: RigidObject2(const std::string& name) : DynamicObject(name) { } - virtual ~RigidObject2() override = default; + + virtual ~RigidObject2() = default; public: virtual const std::string getTypeName() const override { return "RigidObject2"; } diff --git a/Source/SimulationManager/imstkKeyboardSceneControl.cpp b/Source/SimulationManager/imstkKeyboardSceneControl.cpp index c3810b7c836c0097b702ca9c2abe472d68684735..f9cf7617ba666cb27d3973a8bfe11ae2b9d3403f 100644 --- a/Source/SimulationManager/imstkKeyboardSceneControl.cpp +++ b/Source/SimulationManager/imstkKeyboardSceneControl.cpp @@ -30,10 +30,6 @@ namespace imstk { -KeyboardSceneControl::KeyboardSceneControl() -{ -} - KeyboardSceneControl::KeyboardSceneControl(std::shared_ptr<KeyboardDeviceClient> keyDevice) : KeyboardControl(keyDevice) { @@ -82,6 +78,9 @@ KeyboardSceneControl::OnKeyPress(const char key) module->setPaused(!paused); } } + + // In case the SceneManager is not apart of the driver + paused ? sceneManager->resume() : sceneManager->pause(); } else if (key == 'q' || key == 'Q' || key == 'e' || key == 'E') // end Simulation { @@ -94,8 +93,7 @@ KeyboardSceneControl::OnKeyPress(const char key) for (auto module : driver->getModules()) { - std::shared_ptr<SceneManager> subManager = std::dynamic_pointer_cast<SceneManager>(module); - if (subManager != nullptr) + if (auto subManager = std::dynamic_pointer_cast<SceneManager>(module)) { if (simModeOn) { @@ -106,8 +104,7 @@ KeyboardSceneControl::OnKeyPress(const char key) subManager->setMode(SceneManager::Mode::Simulation); } } - std::shared_ptr<VTKViewer> viewer = std::dynamic_pointer_cast<VTKViewer>(module); - if (viewer != nullptr) + if (auto viewer = std::dynamic_pointer_cast<VTKViewer>(module)) { if (simModeOn) { @@ -119,6 +116,8 @@ KeyboardSceneControl::OnKeyPress(const char key) } } } + + simModeOn ? sceneManager->setMode(SceneManager::Mode::Debug) : sceneManager->setMode(SceneManager::Mode::Simulation); } else if (key == 'p' || key == 'P') // switch framerate display { diff --git a/Source/SimulationManager/imstkKeyboardSceneControl.h b/Source/SimulationManager/imstkKeyboardSceneControl.h index 1396a8f89d7aebd55d47c7cee098b2919f6ab852..17f7dcbb913567b4eb1dc1178466efe20e36226b 100644 --- a/Source/SimulationManager/imstkKeyboardSceneControl.h +++ b/Source/SimulationManager/imstkKeyboardSceneControl.h @@ -40,7 +40,7 @@ class SceneManager; class KeyboardSceneControl : public KeyboardControl { public: - KeyboardSceneControl(); + KeyboardSceneControl() = default; KeyboardSceneControl(std::shared_ptr<KeyboardDeviceClient> keyDevice); virtual ~KeyboardSceneControl() override = default;