iMSTK
Interactive Medical Simulation Toolkit
PBDTissueVolumeNeedleContactExample.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkCamera.h"
8 #include "imstkControllerForceText.h"
9 #include "imstkDebugGeometryModel.h"
10 #include "imstkDirectionalLight.h"
11 #include "imstkGeometryUtilities.h"
12 #include "imstkIsometricMap.h"
13 #include "imstkKeyboardDeviceClient.h"
14 #include "imstkKeyboardSceneControl.h"
15 #include "imstkMeshIO.h"
16 #include "imstkMouseDeviceClient.h"
17 #include "imstkMouseSceneControl.h"
18 #include "imstkObjectControllerGhost.h"
19 #include "imstkPbdCollisionHandling.h"
20 #include "imstkPbdContactConstraint.h"
21 #include "imstkPbdModel.h"
22 #include "imstkPbdModelConfig.h"
23 #include "imstkPbdObject.h"
24 #include "imstkPbdObjectController.h"
25 #include "imstkPointwiseMap.h"
26 #include "imstkPuncturable.h"
27 #include "imstkRenderMaterial.h"
28 #include "imstkScene.h"
29 #include "imstkSceneManager.h"
30 #include "imstkSimulationManager.h"
31 #include "imstkSimulationUtils.h"
32 #include "imstkStraightNeedle.h"
33 #include "imstkTextVisualModel.h"
34 #include "imstkVTKViewer.h"
35 #include "NeedleEmbedder.h"
36 #include "NeedleInteraction.h"
37 
38 #ifdef iMSTK_USE_HAPTICS
39 #include "imstkDeviceManager.h"
40 #include "imstkDeviceManagerFactory.h"
41 #else
42 #include "imstkDummyClient.h"
43 #endif
44 
45 using namespace imstk;
46 
51 static std::vector<int>
52 computeFixedPtsViaMap(std::shared_ptr<PointSet> parent,
53  std::shared_ptr<PointSet> child,
54  const double tolerance = 0.00001)
55 {
56  std::vector<int> fixedPts;
57 
58  auto map = std::make_shared<PointwiseMap>();
59  map->setParentGeometry(parent);
60  map->setChildGeometry(child);
61  map->setTolerance(tolerance);
62  map->compute();
63  fixedPts.reserve(child->getNumVertices());
64  for (int i = 0; i < child->getNumVertices(); i++)
65  {
66  fixedPts.push_back(map->getParentVertexId(i));
67  }
68  return fixedPts;
69 }
70 
76 static std::shared_ptr<PbdObject>
77 makeTissueObj(const std::string& name,
78  std::shared_ptr<PbdModel> model,
79  std::shared_ptr<TetrahedralMesh> tissueMesh)
80 {
81  // Setup the Geometry
82  std::shared_ptr<SurfaceMesh> surfMesh = tissueMesh->extractSurfaceMesh();
83 
84  // Setup the material
85  auto material = std::make_shared<RenderMaterial>();
86  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
87  material->setBackFaceCulling(false);
88  material->setOpacity(0.5);
89 
90  // Setup the Object
91  auto tissueObj = std::make_shared<PbdObject>(name);
92  tissueObj->setVisualGeometry(surfMesh);
93  tissueObj->getVisualModel(0)->setRenderMaterial(material);
94  tissueObj->setPhysicsGeometry(tissueMesh);
95  tissueObj->setCollidingGeometry(surfMesh);
96  tissueObj->setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(tissueMesh, surfMesh));
97  tissueObj->setDynamicalModel(model);
98  tissueObj->getPbdBody()->uniformMassValue = 0.04;
99 
100  // \todo: iMSTK doesn't support multiple different materials for FEM tet constraints without
101  // making the functor yourself
102  auto functor = std::make_shared<PbdFemTetConstraintFunctor>();
103  functor->setGeometry(tissueMesh);
104  functor->setBodyIndex(tissueObj->getPbdBody()->bodyHandle);
105  const double youngsModulus = 100000.0;
106  const double poissonRatio = 0.48;
107  auto constraintConfig = std::make_shared<PbdFemConstraintConfig>(
108  youngsModulus / 2.0 / (1.0 + poissonRatio),
109  youngsModulus * poissonRatio / ((1.0 + poissonRatio) * (1.0 - 2.0 * poissonRatio)),
110  youngsModulus,
111  poissonRatio);
112  functor->setFemConfig(constraintConfig);
113  functor->setMaterialType(PbdFemConstraint::MaterialType::StVK);
114  model->getConfig()->addPbdConstraintFunctor(functor);
115 
116  tissueObj->addComponent<Puncturable>();
117 
118  return tissueObj;
119 }
120 
121 static std::shared_ptr<PbdObject>
122 makeNeedleObj(const std::string& name,
123  std::shared_ptr<PbdModel> model)
124 {
125  auto toolObj = std::make_shared<PbdObject>(name);
126 
127  auto toolGeometry = std::make_shared<LineMesh>();
128  auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(2);
129  (*verticesPtr)[0] = Vec3d(0.0, 0.0, 0.0);
130  (*verticesPtr)[1] = Vec3d(0.0, 0.0, 0.25);
131  auto indicesPtr = std::make_shared<VecDataArray<int, 2>>(1);
132  (*indicesPtr)[0] = Vec2i(0, 1);
133  toolGeometry->initialize(verticesPtr, indicesPtr);
134 
135  auto trocarMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/LapTool/trocar.obj");
136 
137  toolObj->setVisualGeometry(trocarMesh);
138  toolObj->setCollidingGeometry(toolGeometry);
139  toolObj->setPhysicsGeometry(toolGeometry);
140  toolObj->setPhysicsToVisualMap(std::make_shared<IsometricMap>(toolGeometry, trocarMesh));
141  toolObj->getVisualModel(0)->getRenderMaterial()->setColor(Color(0.9, 0.9, 0.9));
142  toolObj->getVisualModel(0)->getRenderMaterial()->setShadingModel(RenderMaterial::ShadingModel::PBR);
143  toolObj->getVisualModel(0)->getRenderMaterial()->setRoughness(0.5);
144  toolObj->getVisualModel(0)->getRenderMaterial()->setMetalness(1.0);
145  toolObj->getVisualModel(0)->getRenderMaterial()->setIsDynamicMesh(false);
146 
147  toolObj->setDynamicalModel(model);
148  toolObj->getPbdBody()->setRigid(
149  Vec3d(0.0, 1.0, 0.0), // Position
150  1.0, // Mass
151  Quatd::Identity(), // Orientation
152  Mat3d::Identity() * 10000.0); // Inertia
153 
154  // Add a component for needle puncturing
155  auto needle = toolObj->addComponent<StraightNeedle>();
156  needle->setNeedleGeometry(toolGeometry);
157 
158  // Add a component for controlling via another device
159  auto controller = toolObj->addComponent<PbdObjectController>();
160  controller->setControlledObject(toolObj);
161  controller->setLinearKs(20000.0);
162  controller->setAngularKs(8000000.0);
163  controller->setUseCritDamping(true);
164  controller->setForceScaling(0.05);
165  controller->setSmoothingKernelSize(15);
166  controller->setUseForceSmoothening(true);
167 
168  // Add extra component to tool for the ghost
169  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
170  controllerGhost->setUseForceFade(true);
171  controllerGhost->setController(controller);
172 
173  return toolObj;
174 }
175 
176 static void
177 updateDebugGeom(std::shared_ptr<NeedleInteraction> interaction,
178  std::shared_ptr<DebugGeometryModel> debugGeomObj)
179 {
180  auto needleEmbedder = std::dynamic_pointer_cast<NeedleEmbedder>(interaction->getEmbedder());
181  const std::vector<Vec3d>& debugEmbeddingPts = needleEmbedder->m_debugEmbeddingPoints;
182  const std::vector<Vec3i>& debugEmbeddingTris = needleEmbedder->m_debugEmbeddedTriangles;
183  debugGeomObj->clear();
184  for (size_t i = 0; i < debugEmbeddingPts.size(); i++)
185  {
186  debugGeomObj->addPoint(debugEmbeddingPts[i]);
187  }
188  std::shared_ptr<PbdObject> tissueObj = interaction->getEmbedder()->getTissueObject();
189  auto verticesPtr = std::dynamic_pointer_cast<TetrahedralMesh>(tissueObj->getPhysicsGeometry())->getVertexPositions();
190  VecDataArray<double, 3>& vertices = *verticesPtr;
191  for (size_t i = 0; i < debugEmbeddingTris.size(); i++)
192  {
193  debugGeomObj->addTriangle(
194  vertices[debugEmbeddingTris[i][0]],
195  vertices[debugEmbeddingTris[i][1]],
196  vertices[debugEmbeddingTris[i][2]]);
197  }
198 }
199 
205 int
206 main()
207 {
208  // Setup logger (write to file and stdout)
210 
211  // Setup the scene
212  auto scene = std::make_shared<Scene>("PbdTissueVolumeNeedleContact");
213  scene->getActiveCamera()->setPosition(0.0, 0.412873, 0.102441);
214  scene->getActiveCamera()->setFocalPoint(0.0, -0.0, -0.0);
215  scene->getActiveCamera()->setViewUp(0.0, 0.242952, -0.969977);
216  scene->getConfig()->debugCamBoundingBox = false;
217  *scene->getCamera("debug") = *scene->getActiveCamera();
218 
219  // Setup the Model
220  auto pbdModel = std::make_shared<PbdModel>();
221  pbdModel->getConfig()->m_doPartitioning = false;
222  pbdModel->getConfig()->m_dt = 0.001; // realtime used in update calls later in main
223  pbdModel->getConfig()->m_iterations = 1; // Prefer small timestep over iterations
224  pbdModel->getConfig()->m_gravity = Vec3d(0.0, 0.0, 0.0);
225 
226  // Setup a tissue with surface collision geometry
227  const Vec3i dim = Vec3i(6, 3, 6);
228  auto tetGridMesh = GeometryUtils::toTetGrid(
229  Vec3d(0.0, 0.0, 0.0), // Center
230  Vec3d(0.2, 0.01, 0.2), // Size (meters)
231  dim); // Dimensions
232  std::shared_ptr<PbdObject> tissueObj = makeTissueObj("PbdTissue1", pbdModel, tetGridMesh);
233  // Fix the borders
234  for (int z = 0; z < dim[2]; z++)
235  {
236  for (int y = 0; y < dim[1]; y++)
237  {
238  for (int x = 0; x < dim[0]; x++)
239  {
240  if (x == 0 || /*z == 0 ||*/ x == dim[0] - 1 /*|| z == dim[2] - 1*/)
241  {
242  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z));
243  }
244  }
245  }
246  }
247  scene->addSceneObject(tissueObj);
248 
249  auto tetMesh = MeshIO::read<TetrahedralMesh>(iMSTK_DATA_ROOT "/Organs/Kidney/kidney_vol_low_rez.vtk");
250  tetMesh->translate(Vec3d(0.0, -0.07, -0.05), Geometry::TransformType::ApplyToData);
251  std::shared_ptr<PbdObject> tissueObj2 = makeTissueObj("PbdTissue2", pbdModel, tetMesh);
252  auto fixedPtMesh = MeshIO::read<PointSet>(iMSTK_DATA_ROOT "/Organs/Kidney/kidney_fixedpts_low_rez.obj");
253  fixedPtMesh->translate(Vec3d(0.0, -0.07, -0.05), Geometry::TransformType::ApplyToData);
254  tissueObj2->getPbdBody()->fixedNodeIds = computeFixedPtsViaMap(tetMesh, fixedPtMesh, 0.001);
255  tissueObj2->getVisualModel(0)->getRenderMaterial()->setColor(Color::Blood);
256  scene->addSceneObject(tissueObj2);
257 
258  // Setup a tool for the user to move
259  std::shared_ptr<PbdObject> toolObj = makeNeedleObj("PbdNeedle", pbdModel);
260  auto debugGeom = toolObj->addComponent<DebugGeometryModel>();
261  debugGeom->setLineWidth(0.1);
262  scene->addSceneObject(toolObj);
263 
264  // This adds both contact and puncture functionality
265  auto interaction = std::make_shared<NeedleInteraction>(tissueObj, toolObj);
266  interaction->setPunctureForceThreshold(3.0);
267  interaction->setNeedleCompliance(0.000001);
268  interaction->setFriction(0.1);
269  scene->addInteraction(interaction);
270  // This adds both contact and puncture functionality
271  auto interaction2 = std::make_shared<NeedleInteraction>(tissueObj2, toolObj);
272  interaction2->setPunctureForceThreshold(3.0);
273  interaction2->setNeedleCompliance(0.000001);
274  interaction2->setFriction(0.1);
275  scene->addInteraction(interaction2);
276 
277  // Light
278  auto light = std::make_shared<DirectionalLight>();
279  light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
280  light->setIntensity(1.0);
281  scene->addLight("Light", light);
282 
283  // Run the simulation
284  {
285  // Setup a viewer to render
286  auto viewer = std::make_shared<VTKViewer>();
287  viewer->setActiveScene(scene);
288  viewer->setVtkLoggerMode(VTKViewer::VTKLoggerMode::MUTE);
289  viewer->setDebugAxesLength(0.1, 0.1, 0.1);
290 
291  // Setup a scene manager to advance the scene
292  auto sceneManager = std::make_shared<SceneManager>();
293  sceneManager->setActiveScene(scene);
294  sceneManager->pause(); // Start simulation paused
295 
296  auto driver = std::make_shared<SimulationManager>();
297  driver->addModule(viewer);
298  driver->addModule(sceneManager);
299  driver->setDesiredDt(0.001); // 1ms, 1000hz
300 
301  auto controller = toolObj->getComponent<PbdObjectController>();
302 #ifdef iMSTK_USE_HAPTICS
303  // Setup default haptics manager
304  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
305  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
306  driver->addModule(hapticManager);
307 
308  if (hapticManager->getTypeName() == "HaplyDeviceManager")
309  {
310  controller->setTranslationOffset(Vec3d(0.125, -0.07, 0.0));
311  }
312 #else
313  auto deviceClient = std::make_shared<DummyClient>();
314 
315  connect<Event>(sceneManager, &SceneManager::postUpdate,
316  [&](Event*)
317  {
318  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
319  const Vec3d desiredPos = Vec3d(mousePos[0] - 0.5, mousePos[1] - 0.5, 0.0) * 0.1;
320  const Quatd desiredOrientation = Quatd(Rotd(0.0, Vec3d(1.0, 0.0, 0.0)));
321 
322  deviceClient->setPosition(desiredPos);
323  deviceClient->setOrientation(desiredOrientation);
324  });
325 #endif
326  controller->setDevice(deviceClient);
327 
328  int counter = 0;
329  connect<Event>(viewer, &VTKViewer::preUpdate,
330  [&](Event*)
331  {
332  // Copy constraint faces and points to debug geometry for display
333  updateDebugGeom(interaction, debugGeom);
334  });
335  connect<Event>(sceneManager, &SceneManager::preUpdate,
336  [&](Event*)
337  {
338  // Keep the tool moving in real time
339  pbdModel->getConfig()->m_dt = sceneManager->getDt();
340  });
341 
342  // Add default mouse and keyboard controls to the viewer
343  std::shared_ptr<Entity> mouseAndKeyControls =
344  SimulationUtils::createDefaultSceneControl(driver);
345  // Add something to display controller force
346  auto controllerForceTxt = mouseAndKeyControls->addComponent<ControllerForceText>();
347  controllerForceTxt->setController(controller);
348  controllerForceTxt->setCollision(interaction);
349  scene->addSceneObject(mouseAndKeyControls);
350 
351  driver->start();
352  }
353 
354  return 0;
355 }
Class for quickly rendering and showing various primivites such as line segments, triangles...
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
This class uses the provided device to control the provided rigid object via virtual coupling...
Place this on an object to make it puncturable by a needle. This allows puncturables to know they&#39;ve ...
void setUseForceFade(bool useForceFade)
Get/Set whether to use force fade or not. Force fade sets opacity of ghost geometry according to forc...
Compound Geometry.
std::shared_ptr< TetrahedralMesh > toTetGrid(const Vec3d &center, const Vec3d &size, const Vec3i &divisions, const Quatd orientation=Quatd::Identity())
Produces a tetrahedral grid given the OrientedBox with the given divisions.
Definition of straight, single segment needle.
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
Implements PBD embedded tissue handling for when the needle is embedded in the tissue.
Represents a set of tetrahedrons & vertices via an array of Vec3d double vertices & Vec4i integer ind...
Color in RGB space.
Definition: imstkColor.h:24
A behaviour that renders a second copy of the controlled object at a lower opacity in the physical po...
std::vector< Vec3d > m_debugEmbeddingPoints
Used for debug visualization.
Physically based rendering.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.
void setController(std::shared_ptr< PbdObjectController > controller)
Get/Set the controller to display the device force of.
Displays virtual coupling force text in the top right.