diff --git a/Examples/PBD/PBDTissueStitch/CMakeLists.txt b/Examples/PBD/PBDTissueStitch/CMakeLists.txt index c9fc339846eec338910b9048ea2fd6a01cb2b420..69368542efe362bac34d0c41f40b0f4d19d09891 100644 --- a/Examples/PBD/PBDTissueStitch/CMakeLists.txt +++ b/Examples/PBD/PBDTissueStitch/CMakeLists.txt @@ -33,4 +33,6 @@ SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples/PBD) #----------------------------------------------------------------------------- target_link_libraries(${PROJECT_NAME} SimulationManager - ViewerVTK) \ No newline at end of file + ViewerVTK) + +#target_compile_options(${PROJECT_NAME} PUBLIC -DUSE_THIN_TISSUE) \ No newline at end of file diff --git a/Examples/PBD/PBDTissueStitch/PBDTissueStitchExample.cpp b/Examples/PBD/PBDTissueStitch/PBDTissueStitchExample.cpp index a34decc2fc1cff511a0bc87ed54da3a9d3ccb60b..4c6b068f8630a8bdeef75dba6144bbe3dd04b113 100644 --- a/Examples/PBD/PBDTissueStitch/PBDTissueStitchExample.cpp +++ b/Examples/PBD/PBDTissueStitch/PBDTissueStitchExample.cpp @@ -46,10 +46,9 @@ using namespace imstk; -#define USE_FEM -//#define USE_HAPTICS +// #define USE_THIN_TISSUE -#ifdef USE_HAPTICS +#ifdef iMSTK_USE_OPENHAPTICS #include "imstkHapticDeviceClient.h" #include "imstkHapticDeviceManager.h" #else @@ -155,6 +154,8 @@ makeTetGrid(const Vec3d& size, const Vec3i& dim, const Vec3d& center) /// \brief Creates triangle grid geometry /// \param cloth width (x), height (z) /// \param cloth dimensions/divisions +/// \param center of tissue/translation control +/// \param tex coord scale /// static std::shared_ptr<SurfaceMesh> makeTriangleGrid(const Vec2d size, @@ -221,7 +222,7 @@ makeTriangleGrid(const Vec2d size, } /// -/// \brief Creates tissue object +/// \brief Creates tetrahedral tissue object /// \param name /// \param physical dimension of tissue /// \param dimensions of tetrahedral grid used for tissue @@ -239,16 +240,13 @@ makeTetTissueObj(const std::string& name, // Setup the Parameters auto pbdParams = std::make_shared<PbdModelConfig>(); -#ifdef USE_FEM // Use FEMTet constraints (42k - 85k for tissue, but we want // something much more stretchy to wrap) pbdParams->m_femParams->m_YoungModulus = 1000.0; pbdParams->m_femParams->m_PoissonRatio = 0.4; // 0.48 for tissue pbdParams->enableFemConstraint(PbdFemConstraint::MaterialType::StVK); -#else - pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Volume, 0.01); - pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 0.4); -#endif + /* pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Volume, 0.01); + pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 0.4);*/ pbdParams->m_doPartitioning = false; pbdParams->m_uniformMassValue = 0.00001; pbdParams->m_gravity = Vec3d(0.0, 0.0, 0.0); @@ -294,6 +292,9 @@ makeTetTissueObj(const std::string& name, return tissueObj; } +/// +/// \brief Creates thin tissue object +/// static std::shared_ptr<PbdObject> makeTriTissueObj(const std::string& name, const Vec2d& size, const Vec2i& dim, const Vec3d& center) @@ -333,10 +334,9 @@ makeTriTissueObj(const std::string& name, // Setup the VisualModel auto material = std::make_shared<RenderMaterial>(); material->setBackFaceCulling(false); - material->setDisplayMode(RenderMaterial::DisplayMode::Wireframe); + material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); material->setColor(Color(0.77, 0.53, 0.34)); material->setEdgeColor(Color(0.87, 0.63, 0.44)); - material->setOpacity(0.5); // Setup the Object tissueObj->setVisualGeometry(triMesh); @@ -387,13 +387,12 @@ makeToolObj() int main() { - // Setup logger (write to file and stdout) - Logger::startLogger(); - const double capsuleRadius = 0.02; - const bool useThinTissue = false; const double tissueLength = 0.15; + // Setup logger (write to file and stdout) + Logger::startLogger(); + // Setup the scene auto scene = std::make_shared<Scene>("PbdTissueStitch"); scene->getActiveCamera()->setPosition(0.0012, 0.0451, 0.1651); @@ -401,19 +400,15 @@ main() scene->getActiveCamera()->setViewUp(0.0, 0.96, -0.28); // Setup a tet tissue - std::shared_ptr<PbdObject> tissueObj = nullptr; - if (useThinTissue) - { - tissueObj = makeTriTissueObj("Tissue", - Vec2d(tissueLength, 0.07), Vec2i(15, 5), - Vec3d(tissueLength * 0.5, -0.01 - capsuleRadius, 0.0)); - } - else - { - tissueObj = makeTetTissueObj("Tissue", - Vec3d(tissueLength, 0.01, 0.07), Vec3i(15, 2, 5), - Vec3d(tissueLength * 0.5, -0.01 - capsuleRadius, 0.0)); - } +#ifdef USE_THIN_TISSUE + std::shared_ptr<PbdObject> tissueObj = makeTriTissueObj("Tissue", + Vec2d(tissueLength, 0.07), Vec2i(15, 5), + Vec3d(tissueLength * 0.5, -0.01 - capsuleRadius, 0.0)); +#else + std::shared_ptr<PbdObject> tissueObj = makeTetTissueObj("Tissue", + Vec3d(tissueLength, 0.01, 0.07), Vec3i(15, 2, 5), + Vec3d(tissueLength * 0.5, -0.01 - capsuleRadius, 0.0)); +#endif scene->addSceneObject(tissueObj); // Setup a capsule to wrap around @@ -433,11 +428,12 @@ main() scene->addSceneObject(toolObj); // Setup CD with a cylinder CD object - auto col1 = std::make_shared<PbdObjectCollision>(tissueObj, cdObj, "SurfaceMeshToCapsuleCD"); - col1->setFriction(0.0); - scene->addInteraction(col1); + auto collisionInteraction = std::make_shared<PbdObjectCollision>(tissueObj, cdObj, "SurfaceMeshToCapsuleCD"); + collisionInteraction->setFriction(0.0); + scene->addInteraction(collisionInteraction); auto stitching = std::make_shared<PbdObjectStitching>(tissueObj); + stitching->setStitchDistance(0.015); scene->addInteraction(stitching); // Lights @@ -467,7 +463,7 @@ main() driver->addModule(sceneManager); driver->setDesiredDt(0.001); -#ifdef USE_HAPTICS +#ifdef iMSTK_USE_OPENHAPTICS auto hapticManager = std::make_shared<HapticDeviceManager>(); hapticManager->setSleepDelay(0.1); // Delay for 1ms (haptics thread is limited to max 1000hz) std::shared_ptr<HapticDeviceClient> deviceClient = hapticManager->makeDeviceClient(); @@ -478,16 +474,13 @@ main() [&](Event*) { const Vec2d& pos = viewer->getMouseDevice()->getPos(); - if (useThinTissue) - { - deviceClient->setPosition(Vec3d(40.0, 40.0, -(pos[1] * 100.0 - 50.0))); - deviceClient->setOrientation(Quatd(Rotd(-0.6, Vec3d(0.0, 0.0, 1.0)))); - } - else - { - deviceClient->setPosition(Vec3d(37.0, 0.0, -(pos[1] * 100.0 - 50.0))); - deviceClient->setOrientation(Quatd(Rotd(0.65, Vec3d(0.0, 0.0, 1.0)))); - } +#ifdef USE_THIN_TISSUE + deviceClient->setPosition(Vec3d(40.0, 40.0, -(pos[1] * 100.0 - 50.0))); + deviceClient->setOrientation(Quatd(Rotd(-0.6, Vec3d(0.0, 0.0, 1.0)))); +#else + deviceClient->setPosition(Vec3d(37.0, 0.0, -(pos[1] * 100.0 - 50.0))); + deviceClient->setOrientation(Quatd(Rotd(0.65, Vec3d(0.0, 0.0, 1.0)))); +#endif }); #endif @@ -501,7 +494,7 @@ main() controller->setUseForceSmoothening(true); scene->addController(controller); -#ifdef USE_HAPTICS +#ifdef iMSTK_USE_OPENHAPTICS connect<ButtonEvent>(deviceClient, &HapticDeviceClient::buttonStateChanged, [&](ButtonEvent* e) { @@ -510,7 +503,7 @@ main() auto toolGeom = std::dynamic_pointer_cast<LineMesh>(toolObj->getCollidingGeometry()); const Vec3d& v1 = toolGeom->getVertexPosition(0); const Vec3d& v2 = toolGeom->getVertexPosition(1); - stitching->beginRayPointStitch(v1, (v2 - v1).normalized()); + stitching->beginStitch(v1, (v2 - v1).normalized()); } }); #endif @@ -531,7 +524,7 @@ main() auto toolGeom = std::dynamic_pointer_cast<LineMesh>(toolObj->getCollidingGeometry()); const Vec3d& v1 = toolGeom->getVertexPosition(0); const Vec3d& v2 = toolGeom->getVertexPosition(1); - stitching->beginRayPointStitch(v1, (v2 - v1).normalized()); + stitching->beginStitch(v1, (v2 - v1).normalized()); } // Reset else if (e->m_key == 'r') @@ -575,10 +568,9 @@ main() { if (!stopped) { + // Clear and reinit all constraints (new resting lengths) stopped = true; tissueObj->getPbdModel()->getConfig()->m_fixedNodeIds.clear(); - - // Clear and reinit all constraints tissueObj->initialize(); } } diff --git a/Source/Scene/imstkPbdObjectStitching.cpp b/Source/Scene/imstkPbdObjectStitching.cpp index c0a14e345191ac5cbf46caeb6251cec0b9fe2cc3..ae9bfeda5355c5005e324fa186558950fd7ca6c1 100644 --- a/Source/Scene/imstkPbdObjectStitching.cpp +++ b/Source/Scene/imstkPbdObjectStitching.cpp @@ -20,21 +20,16 @@ limitations under the License. =========================================================================*/ #include "imstkPbdObjectStitching.h" -#include "imstkCDObjectFactory.h" #include "imstkCellPicker.h" #include "imstkLineMesh.h" -#include "imstkOneToOneMap.h" #include "imstkPbdBaryPointToPointConstraint.h" #include "imstkPbdModel.h" #include "imstkPbdObject.h" #include "imstkPointPicker.h" #include "imstkSurfaceMesh.h" +#include "imstkSurfaceToTetraMap.h" #include "imstkTaskGraph.h" #include "imstkTetrahedralMesh.h" -#include "imstkVertexPicker.h" -#include "imstkSurfaceToTetraMap.h" -#include "imstkVisualModel.h" -#include "imstkRenderMaterial.h" namespace imstk { @@ -144,14 +139,13 @@ PbdObjectStitching::PbdObjectStitching(std::shared_ptr<PbdObject> obj) : } void -PbdObjectStitching::beginRayPointStitch(const Vec3d& rayStart, const Vec3d& rayDir, const double maxDist) +PbdObjectStitching::beginStitch(const Vec3d& rayStart, const Vec3d& rayDir, const double maxDist) { auto pointPicker = std::make_shared<PointPicker>(); pointPicker->setPickingRay(rayStart, rayDir, maxDist); m_pickMethod = pointPicker; - m_mode = StitchMode::RayPoint; - m_isStitching = true; + m_performStitch = true; LOG(INFO) << "Begin stitch"; } @@ -178,18 +172,15 @@ PbdObjectStitching::addStitchConstraints() } std::shared_ptr<VecDataArray<double, 3>> verticesPtr = pbdPhysicsGeom->getVertexPositions(); - //VecDataArray<double, 3>& vertices = *verticesPtr; // Get the attributes from the physics geometry auto velocitiesPtr = std::dynamic_pointer_cast<VecDataArray<double, 3>>(pbdPhysicsGeom->getVertexAttribute("Velocities")); CHECK(velocitiesPtr != nullptr) << "Trying to vertex pick with geometry that has no Velocities"; - //VecDataArray<double, 3>& velocities = *velocitiesPtr; auto invMassesPtr = std::dynamic_pointer_cast<DataArray<double>>(pbdPhysicsGeom->getVertexAttribute("InvMass")); CHECK(invMassesPtr != nullptr) << "Trying to vertex pick with geometry that has no InvMass"; - //const DataArray<double>& invMasses = *invMassesPtr; std::shared_ptr<AbstractDataArray> indicesPtr = nullptr; if (auto lineMesh = std::dynamic_pointer_cast<LineMesh>(pointSetToPick)) @@ -221,30 +212,31 @@ PbdObjectStitching::addStitchConstraints() indicesPtr.get(), map); - auto getCellVerts = [&](const PickData& data) - { - const CellTypeId pickedCellType = data.cellType; - - std::vector<std::pair<int, VertexMassPair>> cellIdVerts; - if (pickedCellType == IMSTK_TETRAHEDRON) - { - cellIdVerts = getElement<4>(data, meshStruct); - } - else if (pickedCellType == IMSTK_TRIANGLE) - { - cellIdVerts = getElement<3>(data, meshStruct); - } - else if (pickedCellType == IMSTK_EDGE) - { - cellIdVerts = getElement<2>(data, meshStruct); - } - std::vector<VertexMassPair> cellVerts(cellIdVerts.size()); - for (size_t j = 0; j < cellIdVerts.size(); j++) - { - cellVerts[j] = cellIdVerts[j].second; - } - return cellVerts; - }; + auto getCellVerts = + [&](const PickData& data) + { + const CellTypeId pickedCellType = data.cellType; + + std::vector<std::pair<int, VertexMassPair>> cellIdVerts; + if (pickedCellType == IMSTK_TETRAHEDRON) + { + cellIdVerts = getElement<4>(data, meshStruct); + } + else if (pickedCellType == IMSTK_TRIANGLE) + { + cellIdVerts = getElement<3>(data, meshStruct); + } + else if (pickedCellType == IMSTK_EDGE) + { + cellIdVerts = getElement<2>(data, meshStruct); + } + std::vector<VertexMassPair> cellVerts(cellIdVerts.size()); + for (size_t j = 0; j < cellIdVerts.size(); j++) + { + cellVerts[j] = cellIdVerts[j].second; + } + return cellVerts; + }; auto pointPicker = std::dynamic_pointer_cast<PointPicker>(m_pickMethod); pointPicker->setUseFirstHit(false); @@ -330,23 +322,14 @@ PbdObjectStitching::addStitchConstraints() } } - // Digest the pick data based on grasp mode - if (m_mode == StitchMode::RayCell) + // Constrain only the pick points between the two elements + for (size_t i = 0; i < constraintPair.size(); i++) { - // Constrain all vertices of the elements - - // If a tetrahedron, use the mapped face + const PickData& pickData1 = constraintPair[i].first; + const PickData& pickData2 = constraintPair[i].second; - // If a surfacemesh just use the face - } - else if (m_mode == StitchMode::RayPoint) - { - // Constrain only the pick points between the two elements - for (size_t i = 0; i < constraintPair.size(); i++) + if (m_maxStitchDist == -1.0 || (pickData2.pickPoint - pickData1.pickPoint).norm() < m_maxStitchDist) { - const PickData& pickData1 = constraintPair[i].first; - const PickData& pickData2 = constraintPair[i].second; - std::vector<VertexMassPair> cellVerts1 = getCellVerts(pickData1); std::vector<double> weights1 = getWeights(cellVerts1, pickData1.pickPoint); std::vector<VertexMassPair> cellVerts2 = getCellVerts(pickData2); @@ -383,13 +366,11 @@ PbdObjectStitching::updateStitching() m_objectToStitch->updateGeometries(); // If started - if (!m_isPrevStitching && m_isStitching) + if (m_performStitch) { addStitchConstraints(); - m_isStitching = false; + m_performStitch = false; } - // Push back the state - m_isPrevStitching = m_isStitching; updateConstraints(); } diff --git a/Source/Scene/imstkPbdObjectStitching.h b/Source/Scene/imstkPbdObjectStitching.h index bdba85ed7511e6dc9506eaec10c2f56cd10c862f..8be3fd206a7c8fad1b4bca675bbfb2e7f782516b 100644 --- a/Source/Scene/imstkPbdObjectStitching.h +++ b/Source/Scene/imstkPbdObjectStitching.h @@ -43,15 +43,6 @@ class PickingAlgorithm; /// class PbdObjectStitching : public SceneObject { -protected: - enum class StitchMode - { - Vertex, // Stitch two vertices together - Cell, // Stitch two cells together - RayPoint, // Stitch two points on two cells together found via rays - RayCell // Stitch two cells found via rays - }; - public: PbdObjectStitching(std::shared_ptr<PbdObject> obj1); ~PbdObjectStitching() override = default; @@ -66,34 +57,19 @@ public: double getStiffness() const { return m_stiffness; } ///@} - /* /// - /// \brief Begin a vertex grasp (picking will begin on the next update) - /// \param Geometry attached/grasped too - /// - void beginVertexGrasp(std::shared_ptr<AnalyticalGeometry> geometry); - - /// - /// \brief Begin a cell grasp (picking will begin on the next update) - /// \param Geometry attached/grasped too - /// \param The intersection type/class name - /// - void beginCellGrasp(std::shared_ptr<AnalyticalGeometry> geometry, std::string cdType);*/ + /// + /// \brief Set/Get the maximum distance for which a stitch may be placed + ///@{ + void setStitchDistance(const double distance) { m_maxStitchDist = distance; } + double getStitchDistance() const { return m_maxStitchDist; } + ///@} /// - /// \brief Begin a ray point grasp (picking will begin on the next update) + /// \brief Begin a ray point stitch. Stitches two points for separate elements. /// \param Global space ray start /// \param Global space ray direction /// - void beginRayPointStitch(const Vec3d& rayStart, const Vec3d& rayDir, const double maxDist = -1.0); - - /* /// - /// \brief Begin a ray point grasp (picking will begin on the next update) - /// \param Geometry attached/grasped too - /// \param Global space ray start - /// \param Global space ray direction - /// - void beginRayCellStitch(std::shared_ptr<AnalyticalGeometry> geometry, - const Vec3d& rayStart, const Vec3d& rayDir, const double maxDist = -1.0);*/ + void beginStitch(const Vec3d& rayStart, const Vec3d& rayDir, const double maxDist = -1.0); /// /// \brief Compute/generate the constraints for stitching @@ -158,16 +134,13 @@ protected: std::shared_ptr<PbdObject> m_objectToStitch = nullptr; std::shared_ptr<PickingAlgorithm> m_pickMethod = nullptr; - StitchMode m_mode = StitchMode::Cell; - bool m_isStitching = false; - bool m_isPrevStitching = false; + bool m_performStitch = false; /// Stiffness of stitches, when 1 the position is completely moved too the grasp point /// when stiffness < 1 it will slowly converge on the grasp point - double m_stiffness = 0.1; - - //bool m_retractingStitch = false; ///< Place a stitch that slowly retracts to 0 after placement + double m_stiffness = 0.1; + double m_maxStitchDist = -1.0; // Set a maximum distance for which a stitch can be placed std::vector<std::shared_ptr<PbdBaryPointToPointConstraint>> m_constraints; ///< List of PBD constraints };