iMSTK
Interactive Medical Simulation Toolkit
PbdConnectiveTissueExample.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 "imstkBurnable.h"
8 #include "imstkBurner.h"
9 #include "imstkCamera.h"
10 #include "imstkCapsule.h"
11 #include "imstkCollisionUtils.h"
12 #include "imstkDirectionalLight.h"
13 #include "imstkGeometryUtilities.h"
14 #include "imstkKeyboardDeviceClient.h"
15 #include "imstkKeyboardSceneControl.h"
16 #include "imstkMeshIO.h"
17 #include "imstkMouseDeviceClient.h"
18 #include "imstkMouseSceneControl.h"
19 #include "imstkObjectControllerGhost.h"
20 #include "imstkPbdConnectiveTissueConstraintGenerator.h"
21 #include "imstkPbdModel.h"
22 #include "imstkPbdModelConfig.h"
23 #include "imstkPbdObject.h"
24 #include "imstkPbdObjectCollision.h"
25 #include "imstkPbdObjectController.h"
26 #include "imstkPbdObjectGrasping.h"
27 #include "imstkPointwiseMap.h"
28 #include "imstkRenderMaterial.h"
29 #include "imstkScene.h"
30 #include "imstkSceneManager.h"
31 #include "imstkSimulationManager.h"
32 #include "imstkSimulationUtils.h"
33 #include "imstkTearable.h"
34 #include "imstkVisualModel.h"
35 #include "imstkVTKViewer.h"
36 
37 #ifdef iMSTK_USE_HAPTICS
38 #include "imstkDeviceManager.h"
39 #include "imstkDeviceManagerFactory.h"
40 #else
41 #include "imstkDummyClient.h"
42 #endif
43 
44 #include <iostream>
45 #include "imstkCompoundGeometry.h"
46 
47 using namespace imstk;
48 
49 /*
50 This example simulates connective tissue by connecting a gallbladder to a kidney.
51 The gallbladder is deformable with strain energy constraints and the kidney is treated as
52 rigid by setting all of the nodes to be fixed.
53 
54 The units for this example are centimeters, kilograms, and seconds
55 */
56 
60 std::shared_ptr<PbdObject>
61 makeGallBladder(const std::string& name, std::shared_ptr<PbdModel> model)
62 {
63  // Setup the Geometry
64  auto tissueMesh = MeshIO::read<TetrahedralMesh>(iMSTK_DATA_ROOT "/Organs/Gallblader/gallblader.msh");
65  const Vec3d center = tissueMesh->getCenter();
66  tissueMesh->translate(-center, Geometry::TransformType::ApplyToData);
67  tissueMesh->scale(100.0, Geometry::TransformType::ApplyToData); // scale from meters (input mesh) to cm
68  tissueMesh->rotate(Vec3d(0.0, 0.0, 1.0), 30.0 / 180.0 * 3.14, Geometry::TransformType::ApplyToData);
69 
70  const Vec3d shift = { -4.0, 0.0, 0.0 };
71  tissueMesh->translate(shift, Geometry::TransformType::ApplyToData);
72 
73  auto surfMesh = tissueMesh->extractSurfaceMesh();
74 
75  // Setup the material
76  auto material = std::make_shared<RenderMaterial>();
77  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
78  material->setBackFaceCulling(false);
79  material->setOpacity(0.5);
80 
81  // Add a visual model to render the tet mesh
82  auto visualModel = std::make_shared<VisualModel>();
83  visualModel->setGeometry(surfMesh);
84  visualModel->setRenderMaterial(material);
85 
86  // Setup the Object
87  auto tissueObj = std::make_shared<PbdObject>(name);
88  tissueObj->addVisualModel(visualModel);
89  //tissueObj->addVisualModel(labelModel);
90  tissueObj->setPhysicsGeometry(tissueMesh);
91  tissueObj->setCollidingGeometry(surfMesh);
92  tissueObj->setDynamicalModel(model);
93 
94  // Create map for collisions
95  tissueObj->setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(tissueMesh, surfMesh));
96 
97  // Gallblader is about 60g
98  tissueObj->getPbdBody()->uniformMassValue = 0.06 / tissueMesh->getNumVertices();
99 
100  model->getConfig()->m_femParams->m_YoungModulus = 100.0; // in kg/(cm*s^2)
101  model->getConfig()->m_femParams->m_PoissonRatio = 0.4;
102  model->getConfig()->enableFemConstraint(PbdFemConstraint::MaterialType::StVK);
103  model->getConfig()->setBodyDamping(tissueObj->getPbdBody()->bodyHandle, 0.01);
104 
105  // Fix the top of the gallbladder
106  std::shared_ptr<VecDataArray<double, 3>> vertices = tissueMesh->getVertexPositions();
107  for (int i = 0; i < tissueMesh->getNumVertices(); i++)
108  {
109  const Vec3d& pos = (*vertices)[i];
110  if (pos[1] >= 1.7)
111  {
112  tissueObj->getPbdBody()->fixedNodeIds.push_back(i);
113  }
114  }
115 
116  LOG(INFO) << "Per particle mass: " << tissueObj->getPbdBody()->uniformMassValue;
117 
118  tissueObj->initialize();
119 
120  return tissueObj;
121 }
122 
126 static std::shared_ptr<PbdObject>
127 makeKidney(const std::string& name, std::shared_ptr<PbdModel> model)
128 {
129  // Setup the Geometry
130  auto tissueMesh = MeshIO::read<TetrahedralMesh>(iMSTK_DATA_ROOT "/Organs/Kidney/kidney_vol_low_rez.vtk");
131  const Vec3d center = tissueMesh->getCenter();
132 
133  tissueMesh->translate(-center, Geometry::TransformType::ApplyToData);
134  tissueMesh->scale(100.0, Geometry::TransformType::ApplyToData); // scale from meters (input mesh) to cm
135  tissueMesh->rotate(Vec3d(0.0, 0.0, 1.0), 30.0 / 180.0 * 3.14, Geometry::TransformType::ApplyToData);
136  tissueMesh->rotate(Vec3d(0.0, 1.0, 0.0), 90.0 / 180.0 * 3.14, Geometry::TransformType::ApplyToData);
137 
138  const Vec3d shift = { 4.0, 0.0, 0.0 };
139  tissueMesh->translate(shift, Geometry::TransformType::ApplyToData);
140 
141  auto surfMesh = tissueMesh->extractSurfaceMesh();
142 
143  // Setup the material
144  auto material = std::make_shared<RenderMaterial>();
145  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
146  material->setBackFaceCulling(false);
147  material->setOpacity(0.5);
148 
149  // Add a visual model to render the tet mesh
150  auto visualModel = std::make_shared<VisualModel>();
151  visualModel->setGeometry(surfMesh);
152  visualModel->setRenderMaterial(material);
153 
154  // Setup the Object
155  auto tissueObj = std::make_shared<PbdObject>(name);
156 
157  tissueObj->addVisualModel(visualModel);
158  //tissueObj->addVisualModel(labelModel);
159  tissueObj->setPhysicsGeometry(surfMesh);
160  tissueObj->setDynamicalModel(model);
161  tissueObj->setCollidingGeometry(surfMesh);
162 
163  // Gallblader is about 60g
164  tissueObj->getPbdBody()->uniformMassValue = 0.06 / surfMesh->getNumVertices();
165 
166  // Fix the kidney in position
167  std::shared_ptr<VecDataArray<double, 3>> vertices = surfMesh->getVertexPositions();
168  for (int i = 0; i < surfMesh->getNumVertices(); i++)
169  {
170  tissueObj->getPbdBody()->fixedNodeIds.push_back(i);
171  }
172 
173  LOG(INFO) << "Per particle mass: " << tissueObj->getPbdBody()->uniformMassValue;
174 
175  return tissueObj;
176 }
177 
178 static std::shared_ptr<PbdObject>
179 makeHookToolObject(std::shared_ptr<PbdModel> model)
180 {
181  auto body = std::make_shared<Capsule>();
182  body->setRadius(0.4);
183  body->setLength(4.0);
184  body->setPosition(Vec3d(0.0, 0.0, 0.0));
185  body->setOrientation(Quatd(0.707, 0.707, 0.0, 0.0));
186 
187  auto geometry = std::make_shared<CompoundGeometry>();
188  geometry->add(body);
189 
190  auto hook = std::make_shared<Capsule>();
191  hook->setRadius(0.15);
192  hook->setLength(1);
193  hook->setPosition(Vec3d(0.0, -0.5, -2));
194  //hook->setOrientation(Quatd::FromTwoVectors(Vec3d(0.0, 1.0, 0.0), Vec3d(0.0, 0.0, 1.0)));
195  geometry->add(hook);
196 
197  auto toolObj = std::make_shared<PbdObject>("Tool");
198 
199  // Create the object
200  // toolObj->setVisualGeometry(body);
201  toolObj->setPhysicsGeometry(geometry);
202  toolObj->setCollidingGeometry(geometry);
203  toolObj->setDynamicalModel(model);
204  toolObj->getPbdBody()->setRigid(
205  Vec3d(0.0, 2.0, 2.0),
206  0.01,
207  Quatd::Identity(),
208  Mat3d::Identity() * 100000.0);
209  {
210  auto visuals = std::make_shared<VisualModel>();
211  visuals->setGeometry(body);
212  toolObj->addVisualModel(visuals);
213  toolObj->getVisualModel(0)->getRenderMaterial()->setOpacity(0.9);
214  }
215  {
216  auto visuals = std::make_shared<VisualModel>();
217  visuals->setGeometry(hook);
218  toolObj->addVisualModel(visuals);
219  toolObj->getVisualModel(1)->getRenderMaterial()->setOpacity(0.9);
220  }
221  // Add a component for controlling via another device
222  auto controller = toolObj->addComponent<PbdObjectController>();
223  controller->setControlledObject(toolObj);
224  controller->setTranslationScaling(100.0); // this convertes from meters to cm
225  controller->setLinearKs(1000.0); // in N/cm
226  controller->setAngularKs(1000000000.0);
227  controller->setUseCritDamping(true);
228  controller->setForceScaling(0.01); // 1 N = 1 kg/(m*s^2) = 0.01 kg/(cm*s^2)
229  controller->setSmoothingKernelSize(15);
230  controller->setUseForceSmoothening(true);
231 
232  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
233  controllerGhost->setController(controller);
234 
235  return toolObj;
236 }
237 
238 Mat4d
239 getJawPosition(double angle)
240 {
241  const double toolLength = 2.0;
242  const double capsuleLength = 1.0;
243  Eigen::Affine3d t(Eigen::Translation3d(0.0, 0.0, -toolLength));
244  t.rotate(Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitX()));
245  t.translate(Eigen::Vector3d(0.0, capsuleLength / 2.0, 0));
246  return t.matrix();
247 }
248 
249 static std::shared_ptr<PbdObject>
250 makeGraspingToolObject(std::shared_ptr<PbdModel> model)
251 {
252  auto body = std::make_shared<Capsule>();
253  body->setRadius(0.4);
254  body->setLength(4.0);
255  body->setPosition(Vec3d(0.0, 0.0, 0.0));
256  body->setOrientation(Quatd(0.707, 0.707, 0.0, 0.0));
257 
258  auto geometry = std::make_shared<CompoundGeometry>();
259  geometry->add(body);
260 
261  auto hook1 = std::make_shared<Capsule>();
262  hook1->setRadius(0.15);
263  hook1->setLength(1);
264 
265  //hook1->setPosition(Vec3d(0.0, -0.5, -2));
266  //hook->setOrientation(Quatd::FromTwoVectors(Vec3d(0.0, 1.0, 0.0), Vec3d(0.0, 0.0, 1.0)));
267  geometry->add(hook1);
268 
269  geometry->setLocalTransform(1, getJawPosition(1.5));
270 
271  auto hook2 = std::make_shared<Capsule>();
272  hook2->setRadius(0.15);
273  hook2->setLength(1);
274  //hook2->setPosition(Vec3d(0.0, 0.5, -2));
275  //hook->setOrientation(Quatd::FromTwoVectors(Vec3d(0.0, 1.0, 0.0), Vec3d(0.0, 0.0, 1.0)));
276  geometry->add(hook2);
277 
278  geometry->setLocalTransform(2, getJawPosition(-1.5));
279 
280  auto toolObj = std::make_shared<PbdObject>("Tool");
281 
282  // Create the object
283  // toolObj->setVisualGeometry(body);
284  toolObj->setPhysicsGeometry(geometry);
285  toolObj->setCollidingGeometry(geometry);
286  toolObj->setDynamicalModel(model);
287  toolObj->getPbdBody()->setRigid(
288  Vec3d(0.0, 2.0, 2.0),
289  0.01,
290  Quatd::Identity(),
291  Mat3d::Identity() * 100000.0);
292  {
293  auto visuals = std::make_shared<VisualModel>();
294  visuals->setGeometry(body);
295  toolObj->addVisualModel(visuals);
296  toolObj->getVisualModel(0)->getRenderMaterial()->setOpacity(0.9);
297  }
298  {
299  auto visuals = std::make_shared<VisualModel>();
300  visuals->setGeometry(hook1);
301  toolObj->addVisualModel(visuals);
302  toolObj->getVisualModel(1)->getRenderMaterial()->setOpacity(0.9);
303  }
304  {
305  auto visuals = std::make_shared<VisualModel>();
306  visuals->setGeometry(hook2);
307  toolObj->addVisualModel(visuals);
308  toolObj->getVisualModel(2)->getRenderMaterial()->setOpacity(0.9);
309  }
310  // Add a component for controlling via another device
311  auto controller = toolObj->addComponent<PbdObjectController>();
312  controller->setControlledObject(toolObj);
313  controller->setTranslationScaling(100.0); // this convertes from meters to cm
314  controller->setLinearKs(1000.0); // in N/cm
315  controller->setAngularKs(1000000000.0);
316  controller->setUseCritDamping(true);
317  controller->setForceScaling(0.01); // 1 N = 1 kg/(m*s^2) = 0.01 kg/(cm*s^2)
318  controller->setSmoothingKernelSize(15);
319  controller->setUseForceSmoothening(true);
320 
321  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
322  controllerGhost->setController(controller);
323 
324  return toolObj;
325 }
326 
330 static std::shared_ptr<PbdObject>
331 makeCapsuleToolObj(std::shared_ptr<PbdModel> model)
332 {
333  auto toolGeometry = std::make_shared<Capsule>();
334  toolGeometry->setRadius(0.4);
335  toolGeometry->setLength(4.0);
336  toolGeometry->setPosition(Vec3d(0.0, 0.0, 0.0));
337  toolGeometry->setOrientation(Quatd(0.707, 0.707, 0.0, 0.0));
338 
339  auto toolObj = std::make_shared<PbdObject>("Tool");
340 
341  // Create the object
342  toolObj->setVisualGeometry(toolGeometry);
343  toolObj->setPhysicsGeometry(toolGeometry);
344  toolObj->setCollidingGeometry(toolGeometry);
345  toolObj->setDynamicalModel(model);
346  toolObj->getPbdBody()->setRigid(
347  Vec3d(0.0, 2.0, 2.0),
348  0.01,
349  Quatd::Identity(),
350  Mat3d::Identity() * 100000.0);
351 
352  toolObj->getVisualModel(0)->getRenderMaterial()->setOpacity(1.0);
353 
354  // Add a component for controlling via another device
355  auto controller = toolObj->addComponent<PbdObjectController>();
356  controller->setControlledObject(toolObj);
357  controller->setTranslationScaling(100.0); // this convertes from meters to cm
358  controller->setLinearKs(1000.0); // in N/cm
359  controller->setAngularKs(1000000000.0);
360  controller->setUseCritDamping(true);
361  controller->setForceScaling(0.01); // 1 N = 1 kg/(m*s^2) = 0.01 kg/(cm*s^2)
362  controller->setSmoothingKernelSize(15);
363  controller->setUseForceSmoothening(true);
364 
365  // Add extra component to tool for the ghost
366  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
367  controllerGhost->setController(controller);
368 
369  return toolObj;
370 }
371 
373 {
374  std::shared_ptr<PbdObject> tool;
375  std::shared_ptr<CompoundGeometry> compoundGeometry;
376  std::vector<std::shared_ptr<PbdObjectGrasping>> graspers;
377  std::vector<std::shared_ptr<AnalyticalGeometry>> geometry;
378 };
379 
380 void
381 startGraspingToolGrasp(GraspingData& data)
382 {
383  data.geometry.clear();
384  for (int i = 0; i < data.graspers.size(); ++i)
385  {
386  auto capsule = std::dynamic_pointer_cast<Capsule>(data.compoundGeometry->get(i + 1));
387  CHECK(capsule != nullptr);
388  auto dilatedCapsule = std::make_shared<Capsule>(*capsule);
389  dilatedCapsule->setRadius(capsule->getRadius() * 1.1);
390  data.geometry.push_back(dilatedCapsule);
391  data.graspers[i]->beginCellGrasp(dilatedCapsule);
392  }
393 }
394 
395 void
396 regraspGraspingTool(GraspingData& data)
397 {
398  for (int i = 0; i < data.graspers.size(); ++i)
399  {
400  auto t = data.compoundGeometry->get(i + 1)->getTransform();
401  data.geometry[i]->setTransform(t);
402  data.graspers[i]->regrasp();
403  }
404 }
405 
406 int
407 runHookToolScene()
408 {
409  // Setup logger (write to file and stdout)
411 
412  // Setup the scene
413  auto scene = std::make_shared<Scene>("PbdConnectiveTissue");
414  scene->getActiveCamera()->setPosition(0.944275, 8.47551, 21.4164);
415  scene->getActiveCamera()->setFocalPoint(-0.450427, 0.519797, 0.817356);
416  scene->getActiveCamera()->setViewUp(-0.0370536, 0.933044, -0.357851);
417 
418  // Setup the PBD Model
419  auto pbdModel = std::make_shared<PbdModel>();
420  pbdModel->getConfig()->m_doPartitioning = false;
421  pbdModel->getConfig()->m_dt = 0.001;
422  pbdModel->getConfig()->m_iterations = 6;
423  pbdModel->getConfig()->m_gravity = Vec3d(0.0, -981.0, 0.0); //in cm/s^2
424  pbdModel->getConfig()->m_linearDampingCoeff = 0.005; // Removed from velocity
425  pbdModel->getConfig()->m_angularDampingCoeff = 0.005;
426 
427  // Setup gallbladder object
428  std::shared_ptr<PbdObject> gallbladerObj = makeGallBladder("Gallbladder", pbdModel);
429  scene->addSceneObject(gallbladerObj);
430  gallbladerObj->addComponent<Burnable>();
431 
432  // Setup kidney
433  std::shared_ptr<PbdObject> kidneyObj = makeKidney("Kidney", pbdModel);
434  scene->addSceneObject(kidneyObj);
435 
436  // Create PBD object of connective strands with associated constraints
437  double maxDist = 3.5;
438  std::shared_ptr<PbdObject> connectiveStrands = makeConnectiveTissue(gallbladerObj, kidneyObj, pbdModel, maxDist, 2.5, 10);
439  pbdModel->getConfig()->setBodyDamping(connectiveStrands->getPbdBody()->bodyHandle, 0.015, 0.0);
440 
441  // Add Tearing
442  // connectiveStrands->addComponent<Tearable>();
443 
444  // Add burnable
445  connectiveStrands->addComponent<Burnable>();
446 
447  // Add strands to scene
448  scene->addSceneObject(connectiveStrands);
449 
450  // Setup a tool to grasp with
451  std::shared_ptr<PbdObject> toolObj = makeHookToolObject(pbdModel);
452  scene->addSceneObject(toolObj);
453 
454  // add collisions
455  auto strandCollision = std::make_shared<PbdObjectCollision>(connectiveStrands, toolObj);
456  scene->addInteraction(strandCollision);
457 
458  auto gallCollision = std::make_shared<PbdObjectCollision>(gallbladerObj, toolObj);
459  scene->addInteraction(gallCollision);
460 
461  // Create new picking with constraints
462  auto grasper = std::make_shared<PbdObjectGrasping>(connectiveStrands, toolObj);
463  grasper->setStiffness(0.5);
464  scene->addInteraction(grasper);
465 
466  auto grasper_gall = std::make_shared<PbdObjectGrasping>(gallbladerObj, toolObj);
467  grasper_gall->setStiffness(0.5);
468  scene->addInteraction(grasper_gall);
469 
470  // Add burner component to tool
471  auto burning = std::make_shared<Burner>();
472  burning->addObject(connectiveStrands);
473  burning->addObject(gallbladerObj);
474  burning->setOnTime(1.0);
475  burning->setWattage(200);
476  {
477  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
478  auto geom = std::dynamic_pointer_cast<AnalyticalGeometry>(compound->get(0));
479  burning->setBurnerGeometry(geom);
480  }
481 
482  toolObj->addComponent(burning);
483 
484  // Light
485  auto light = std::make_shared<DirectionalLight>();
486  light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
487  light->setIntensity(1.0);
488  scene->addLight("Light", light);
489 
490  // Run the simulation
491  {
492  // Setup a viewer to render
493  auto viewer = std::make_shared<VTKViewer>();
494  viewer->setVtkLoggerMode(VTKViewer::VTKLoggerMode::MUTE);
495  viewer->setActiveScene(scene);
496  viewer->setDebugAxesLength(1.0, 1.0, 1.0);
497 
498  // Setup a scene manager to advance the scene
499  auto sceneManager = std::make_shared<SceneManager>();
500  sceneManager->setActiveScene(scene);
501  sceneManager->pause(); // Start simulation pause
502 
503  auto driver = std::make_shared<SimulationManager>();
504  driver->setDesiredDt(0.005);
505  driver->addModule(viewer);
506  driver->addModule(sceneManager);
507 
508  auto controller = toolObj->getComponent<PbdObjectController>();
509 #ifdef iMSTK_USE_HAPTICS
510  // Setup default haptics manager
511  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
512  if (hapticManager->getTypeName() == "HaplyDeviceManager")
513  {
514  controller->setTranslationOffset(Vec3d(2.0, 0.0, -2.0));
515  }
516  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
517  driver->addModule(hapticManager);
518 
519  connect<ButtonEvent>(deviceClient, &DeviceClient::buttonStateChanged,
520  [&](ButtonEvent* e)
521  {
522  if (e->m_buttonState == BUTTON_PRESSED)
523  {
524  if (e->m_button == 1)
525  {
526  std::shared_ptr<Capsule> capsule;
527  capsule = std::dynamic_pointer_cast<Capsule>(toolObj->getCollidingGeometry());
528  // If using the hook the capsule the above will return nullptr
529  if (capsule == nullptr)
530  {
531  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
532  capsule = std::dynamic_pointer_cast<Capsule>(compound->get(1));
533  }
534  // Use a slightly larger capsule since collision prevents intersection
535  auto dilatedCapsule = std::make_shared<Capsule>(*capsule);
536  dilatedCapsule->setRadius(capsule->getRadius() * 1.1);
537  grasper->beginCellGrasp(dilatedCapsule);
538  grasper_gall->beginCellGrasp(dilatedCapsule);
539 
540  gallCollision->setEnabled(false);
541 
542  std::cout << "Grasping! \n";
543  }
544  }
545  else if (e->m_buttonState == BUTTON_RELEASED)
546  {
547  if (e->m_button == 1)
548  {
549  grasper->endGrasp();
550  grasper_gall->endGrasp();
551  gallCollision->setEnabled(true);
552 
553  std::cout << "Released! \n";
554  }
555  }
556  });
557 #else
558  auto deviceClient = std::make_shared<DummyClient>();
559  connect<Event>(sceneManager, &SceneManager::postUpdate,
560  [&](Event*)
561  {
562  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
563  const Vec3d worldPos = Vec3d(mousePos[0] - 0.5, mousePos[1] - 0.5, 0.0) * 0.1;
564 
565  deviceClient->setPosition(worldPos);
566  });
567 
568  // Add click event and side effects
569  connect<Event>(viewer->getMouseDevice(), &MouseDeviceClient::mouseButtonPress,
570  [&](Event*)
571  {
572  grasper->beginVertexGrasp(std::dynamic_pointer_cast<Capsule>(toolObj->getCollidingGeometry()));
573  //pbdToolCollision->setEnabled(false);
574  });
575  connect<Event>(viewer->getMouseDevice(), &MouseDeviceClient::mouseButtonRelease,
576  [&](Event*)
577  {
578  grasper->endGrasp();
579  //pbdToolCollision->setEnabled(true);
580  });
581 #endif
582 
583  // auto controller = toolObj->getComponent<PbdObjectController>();
584  controller->setDevice(deviceClient);
585 
586  // Add default mouse and keyboard controls to the viewer
587  std::shared_ptr<Entity> mouseAndKeyControls =
588  SimulationUtils::createDefaultSceneControl(driver);
589  scene->addSceneObject(mouseAndKeyControls);
590  double angle = 0.6;
591  double origin = PI + PI / 2;
592  // Add keyboard controlls for burning and grasping (Note: only for haptic devices without buttons)
593  std::shared_ptr<KeyboardDeviceClient> keyDevice = viewer->getKeyboardDevice();
594  connect<Event>(sceneManager, &SceneManager::postUpdate, [&](Event*)
595  {
596  // If b pressed, burn
597  if (keyDevice->getButton('b') == KEY_PRESS)
598  {
599  burning->start();
600  }
601  if (keyDevice->getButton('b') == KEY_RELEASE)
602  {
603  burning->stop();
604  }
605  // If g pressed, grasp
606  if (keyDevice->getButton('g') == KEY_PRESS && grasper->getGraspState() == false && grasper_gall->getGraspState() == false)
607  {
608  // Use a slightly larger capsule since collision prevents intersection
609  std::shared_ptr<Capsule> capsule;
610  capsule = std::dynamic_pointer_cast<Capsule>(toolObj->getCollidingGeometry());
611  // If using the hook the capsule the above will return nullptr
612  if (capsule == nullptr)
613  {
614  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
615  capsule = std::dynamic_pointer_cast<Capsule>(compound->get(1));
616  }
617  auto dilatedCapsule = std::make_shared<Capsule>(*capsule);
618  dilatedCapsule->setRadius(capsule->getRadius() * 1.1);
619  grasper->beginCellGrasp(dilatedCapsule);
620  grasper_gall->beginCellGrasp(dilatedCapsule);
621 
622  std::cout << "Grasping! \n";
623  }
624  if (keyDevice->getButton('g') == KEY_RELEASE && (grasper_gall->getGraspState() || grasper->getGraspState()))
625  {
626  grasper->endGrasp();
627  grasper_gall->endGrasp();
628 
629  std::cout << "Released! \n";
630  }
631  if (keyDevice->getButton('o') == KEY_PRESS)
632  {
633  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
634  angle += (angle < 0.6) ? 0.025 : 0.0;
635  compound->setLocalTransform(1, getJawPosition(origin + angle));
636  compound->setLocalTransform(2, getJawPosition(origin - angle));
637  }
638  if (keyDevice->getButton('i') == KEY_PRESS)
639  {
640  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
641  angle -= (angle > 0) ? 0.025 : 0.0;
642  compound->setLocalTransform(1, getJawPosition(origin + angle));
643  compound->setLocalTransform(2, getJawPosition(origin - angle));
644  }
645  });
646 
647  driver->start();
648  }
649 
650  return 0;
651 }
652 
653 int
654 runGraspingToolScene()
655 {
656  // Setup logger (write to file and stdout)
658 
659  // Setup the scene
660  auto scene = std::make_shared<Scene>("PbdConnectiveTissue");
661  scene->getActiveCamera()->setPosition(0.944275, 8.47551, 21.4164);
662  scene->getActiveCamera()->setFocalPoint(-0.450427, 0.519797, 0.817356);
663  scene->getActiveCamera()->setViewUp(-0.0370536, 0.933044, -0.357851);
664 
665  // Setup the PBD Model
666  auto pbdModel = std::make_shared<PbdModel>();
667  pbdModel->getConfig()->m_doPartitioning = false;
668  pbdModel->getConfig()->m_dt = 0.001;
669  pbdModel->getConfig()->m_iterations = 6;
670  pbdModel->getConfig()->m_gravity = Vec3d(0.0, -981.0, 0.0); //in cm/s^2
671  pbdModel->getConfig()->m_linearDampingCoeff = 0.005; // Removed from velocity
672  pbdModel->getConfig()->m_angularDampingCoeff = 0.005;
673 
674  // Setup gallbladder object
675  std::shared_ptr<PbdObject> gallbladerObj = makeGallBladder("Gallbladder", pbdModel);
676  scene->addSceneObject(gallbladerObj);
677 
678  // Setup kidney
679  std::shared_ptr<PbdObject> kidneyObj = makeKidney("Kidney", pbdModel);
680  scene->addSceneObject(kidneyObj);
681 
682  // Create PBD object of connective strands with associated constraints
683  double maxDist = 3.5;
684  std::shared_ptr<PbdObject> connectiveStrands = makeConnectiveTissue(gallbladerObj, kidneyObj, pbdModel, maxDist, 2.5, 10);
685  pbdModel->getConfig()->setBodyDamping(connectiveStrands->getPbdBody()->bodyHandle, 0.015, 0.0);
686 
687  // Add Tearing
688  connectiveStrands->addComponent<Tearable>();
689 
690  // Add burnable
691  auto burnable = std::make_shared<Burnable>();
692  connectiveStrands->addComponent(burnable);
693 
694  // Add strands to scene
695  scene->addSceneObject(connectiveStrands);
696 
697  // Setup a tool to grasp with
698  std::shared_ptr<PbdObject> toolObj = makeGraspingToolObject(pbdModel);
699  scene->addSceneObject(toolObj);
700 
701  // add collisions
702  auto strandCollision = std::make_shared<PbdObjectCollision>(connectiveStrands, toolObj);
703  scene->addInteraction(strandCollision);
704 
705  auto gallCollision = std::make_shared<PbdObjectCollision>(gallbladerObj, toolObj);
706  scene->addInteraction(gallCollision);
707 
708  GraspingData graspingData;
709  graspingData.tool = toolObj;
710  graspingData.compoundGeometry = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
711 
712  auto compoundGeomtry = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
713  CHECK(compoundGeomtry != nullptr);
714  // Need two graspers for each jaw
715  {
716  auto grasper = std::make_shared<PbdObjectGrasping>(connectiveStrands, toolObj);
717 
718  grasper->setStiffness(0.5);
719  scene->addInteraction(grasper);
720  graspingData.graspers.push_back(grasper);
721  }
722  {
723  auto grasper = std::make_shared<PbdObjectGrasping>(connectiveStrands, toolObj);
724  grasper->setStiffness(0.5);
725  scene->addInteraction(grasper);
726  graspingData.graspers.push_back(grasper);
727  }
728 
729  // Create new picking with constraints
730 
731  auto grasper_gall = std::make_shared<PbdObjectGrasping>(gallbladerObj, toolObj);
732  grasper_gall->setStiffness(0.5);
733  scene->addInteraction(grasper_gall);
734 
735  // Light
736  auto light = std::make_shared<DirectionalLight>();
737  light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
738  light->setIntensity(1.0);
739  scene->addLight("Light", light);
740 
741  // Run the simulation
742  {
743  // Setup a viewer to render
744  auto viewer = std::make_shared<VTKViewer>();
745  viewer->setVtkLoggerMode(VTKViewer::VTKLoggerMode::MUTE);
746  viewer->setActiveScene(scene);
747  viewer->setDebugAxesLength(1.0, 1.0, 1.0);
748 
749  // Setup a scene manager to advance the scene
750  auto sceneManager = std::make_shared<SceneManager>();
751  sceneManager->setActiveScene(scene);
752  sceneManager->pause(); // Start simulation pause
753 
754  auto driver = std::make_shared<SimulationManager>();
755  driver->setDesiredDt(0.005);
756  driver->addModule(viewer);
757  driver->addModule(sceneManager);
758 
759  auto controller = toolObj->getComponent<PbdObjectController>();
760 #ifdef iMSTK_USE_HAPTICS
761  // Setup default haptics manager
762  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
763  if (hapticManager->getTypeName() == "HaplyDeviceManager")
764  {
765  controller->setTranslationOffset(Vec3d(2.0, 0.0, -2.0));
766  }
767  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
768  driver->addModule(hapticManager);
769 #else
770  auto deviceClient = std::make_shared<DummyClient>();
771  connect<Event>(sceneManager, &SceneManager::postUpdate,
772  [&](Event*)
773  {
774  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
775  const Vec3d worldPos = Vec3d(mousePos[0] - 0.5, mousePos[1] - 0.5, 0.0) * 0.1;
776 
777  deviceClient->setPosition(worldPos);
778  });
779 
780 #endif
781 
782  // auto controller = toolObj->getComponent<PbdObjectController>();
783  controller->setDevice(deviceClient);
784 
785  // Add default mouse and keyboard controls to the viewer
786  std::shared_ptr<Entity> mouseAndKeyControls =
787  SimulationUtils::createDefaultSceneControl(driver);
788  scene->addSceneObject(mouseAndKeyControls);
789  double angle = 0.6;
790  double origin = PI + PI / 2;
791  // Add keyboard controlls for burning and grasping (Note: only for haptic devices without buttons)
792  std::shared_ptr<KeyboardDeviceClient> keyDevice = viewer->getKeyboardDevice();
793 
794  bool m_closing = false;
795  bool m_opening = true;
796 
797  auto openJaws = [&]() {
798  if (!m_opening)
799  {
800  // Transitioning from closing to opening stop grasping
801  for (auto& grasper : graspingData.graspers)
802  {
803  grasper->endGrasp();
804  }
805  m_closing = false;
806  m_opening = true;
807  }
808  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
809  angle += (angle < 0.6) ? 0.025 : 0.0;
810  compound->setLocalTransform(1, getJawPosition(origin + angle));
811  compound->setLocalTransform(2, getJawPosition(origin - angle));
812  };
813 
814  auto closeJaws = [&]() {
815  if (!m_closing)
816  {
817  // Transitioning from opening to closing, start grasping
818  startGraspingToolGrasp(graspingData);
819  m_closing = true;
820  m_opening = false;
821  }
822  else
823  {
824  regraspGraspingTool(graspingData);
825  }
826 
827  auto compound = std::dynamic_pointer_cast<CompoundGeometry>(toolObj->getCollidingGeometry());
828  angle -= (angle > 0) ? 0.025 : 0.0;
829  compound->setLocalTransform(1, getJawPosition(origin + angle));
830  compound->setLocalTransform(2, getJawPosition(origin - angle));
831  };
832 
833  connect<Event>(sceneManager, &SceneManager::postUpdate, [&](Event*)
834  {
835  if (keyDevice->getButton('o') == KEY_PRESS || deviceClient->getButton(0) != 0)
836  {
837  openJaws();
838  }
839  if (keyDevice->getButton('i') == KEY_PRESS || deviceClient->getButton(1) != 0)
840  {
841  closeJaws();
842  }
843  });
844 
845  driver->start();
846  }
847 
848  return 0;
849 }
850 
851 int
852 main()
853 {
854  runHookToolScene();
855  //runGraspingToolScene();
856 }
Base class for any analytical geometrical representation.
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...
Compound Geometry.
Defines the behaviour to allow a Pbd Object to be burned. This object creates the state data on the m...
Definition: imstkBurnable.h:29
Defines the behaviour to allow a mesh to seperate based on strain in a given cell. This class approximates strain using the constraint value in from the PBD solver. This is well defined for line meshes, but the behavior may not be what is expected for surface or tet meshes.
Definition: imstkTearable.h:28
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
void setLocalTransform(size_t index, const Mat4d &transform)
A behaviour that renders a second copy of the controlled object at a lower opacity in the physical po...
Capsule geometry, default configuration is centered at origin with length running up and down the y a...
Definition: imstkCapsule.h:21
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.