iMSTK
Interactive Medical Simulation Toolkit
PbdTissueStitchExample.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 "imstkCapsule.h"
9 #include "imstkDirectionalLight.h"
10 #include "imstkGeometryUtilities.h"
11 #include "imstkKeyboardDeviceClient.h"
12 #include "imstkKeyboardSceneControl.h"
13 #include "imstkLineMesh.h"
14 #include "imstkMouseDeviceClient.h"
15 #include "imstkMouseSceneControl.h"
16 #include "imstkPbdModel.h"
17 #include "imstkPbdModelConfig.h"
18 #include "imstkPbdObject.h"
19 #include "imstkPbdObjectCollision.h"
20 #include "imstkPbdObjectStitching.h"
21 #include "imstkPointwiseMap.h"
22 #include "imstkRbdConstraint.h"
23 #include "imstkRenderMaterial.h"
24 #include "imstkRigidBodyModel2.h"
25 #include "imstkRigidObject2.h"
26 #include "imstkRigidObjectController.h"
27 #include "imstkScene.h"
28 #include "imstkSceneManager.h"
29 #include "imstkSimulationManager.h"
30 #include "imstkSimulationUtils.h"
31 #include "imstkSurfaceMesh.h"
32 #include "imstkTetrahedralMesh.h"
33 #include "imstkVisualModel.h"
34 #include "imstkVTKViewer.h"
35 
36 using namespace imstk;
37 
38 // #define USE_THIN_TISSUE
39 
40 #ifdef iMSTK_USE_HAPTICS
41 #include "imstkDeviceManager.h"
42 #include "imstkDeviceManagerFactory.h"
43 #else
44 #include "imstkDummyClient.h"
45 #endif
46 
54 static std::shared_ptr<PbdObject>
55 makeTetTissueObj(const std::string& name,
56  const Vec3d& size, const Vec3i& dim, const Vec3d& center)
57 {
58  auto tissueObj = std::make_shared<PbdObject>(name);
59 
60  // Setup the Geometry
61  std::shared_ptr<TetrahedralMesh> tissueMesh = GeometryUtils::toTetGrid(center, size, dim);
62  std::shared_ptr<SurfaceMesh> surfMesh = tissueMesh->extractSurfaceMesh();
63 
64  // Setup the Parameters
65  auto pbdParams = std::make_shared<PbdModelConfig>();
66  // Use FEMTet constraints (42k - 85k for tissue, but we want
67  // something much more stretchy to wrap)
68  pbdParams->m_femParams->m_YoungModulus = 1000.0;
69  pbdParams->m_femParams->m_PoissonRatio = 0.4; // 0.48 for tissue
70  pbdParams->enableFemConstraint(PbdFemConstraint::MaterialType::StVK);
71  /* pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Volume, 0.01);
72  pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 0.4);*/
73  pbdParams->m_doPartitioning = false;
74  pbdParams->m_gravity = Vec3d(0.0, 0.0, 0.0);
75  pbdParams->m_dt = 0.001;
76  pbdParams->m_iterations = 5;
77  pbdParams->m_linearDampingCoeff = 0.05;
78 
79  // Setup the Model
80  auto pbdModel = std::make_shared<PbdModel>();
81  pbdModel->configure(pbdParams);
82 
83  // Setup the material
84  auto material = std::make_shared<RenderMaterial>();
85  material->setDisplayMode(RenderMaterial::DisplayMode::Wireframe);
86  material->setColor(Color(0.77, 0.53, 0.34));
87  material->setEdgeColor(Color(0.87, 0.63, 0.44));
88  material->setOpacity(0.5);
89 
90  // Setup the Object
91  tissueObj->setVisualGeometry(surfMesh);
92  tissueObj->getVisualModel(0)->setRenderMaterial(material);
93  tissueObj->setPhysicsGeometry(tissueMesh);
94  tissueObj->setCollidingGeometry(surfMesh);
95  tissueObj->setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(tissueMesh, surfMesh));
96  tissueObj->setDynamicalModel(pbdModel);
97  tissueObj->getPbdBody()->uniformMassValue = 0.00001;
98  // Fix the borders
99  for (int z = 0; z < dim[2]; z++)
100  {
101  for (int y = 0; y < dim[1]; y++)
102  {
103  for (int x = 0; x < dim[0]; x++)
104  {
105  if (x == 0)
106  {
107  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z));
108  }
109  }
110  }
111  }
112 
113  return tissueObj;
114 }
115 
119 static std::shared_ptr<PbdObject>
120 makeTriTissueObj(const std::string& name,
121  const Vec2d& size, const Vec2i& dim, const Vec3d& center)
122 {
123  auto tissueObj = std::make_shared<PbdObject>(name);
124 
125  // Setup the Geometry
126  std::shared_ptr<SurfaceMesh> triMesh =
127  GeometryUtils::toTriangleGrid(center, size, dim,
128  Quatd::Identity(), 1.0);
129 
130  // Setup the Parameters
131  auto pbdParams = std::make_shared<PbdModelConfig>();
132  pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 0.1);
133  pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Dihedral, 1e-6);
134  pbdParams->m_gravity = Vec3d(0.0, 0.0, 0.0);
135  pbdParams->m_dt = 0.001;
136  pbdParams->m_iterations = 5;
137  pbdParams->m_linearDampingCoeff = 0.025;
138 
139  // Setup the Model
140  auto pbdModel = std::make_shared<PbdModel>();
141  pbdModel->configure(pbdParams);
142 
143  // Setup the VisualModel
144  auto material = std::make_shared<RenderMaterial>();
145  material->setBackFaceCulling(false);
146  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
147  material->setColor(Color(0.77, 0.53, 0.34));
148  material->setEdgeColor(Color(0.87, 0.63, 0.44));
149 
150  // Setup the Object
151  tissueObj->setVisualGeometry(triMesh);
152  tissueObj->getVisualModel(0)->setRenderMaterial(material);
153  tissueObj->setPhysicsGeometry(triMesh);
154  tissueObj->setCollidingGeometry(triMesh);
155  tissueObj->setDynamicalModel(pbdModel);
156  tissueObj->getPbdBody()->uniformMassValue = 0.00001;
157  // Fix the borders
158  for (int y = 0; y < dim[1]; y++)
159  {
160  for (int x = 0; x < dim[0]; x++)
161  {
162  if (x == 0)
163  {
164  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * y);
165  }
166  }
167  }
168 
169  return tissueObj;
170 }
171 
172 static std::shared_ptr<RigidObject2>
173 makeToolObj()
174 {
175  auto toolGeom = std::make_shared<LineMesh>();
176  auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(2);
177  (*verticesPtr)[0] = Vec3d(0.0, -0.05, 0.0);
178  (*verticesPtr)[1] = Vec3d(0.0, 0.05, 0.0);
179  auto indicesPtr = std::make_shared<VecDataArray<int, 2>>(1);
180  (*indicesPtr)[0] = Vec2i(0, 1);
181  toolGeom->initialize(verticesPtr, indicesPtr);
182 
183  auto toolObj = std::make_shared<RigidObject2>("ToolObj");
184  toolObj->setVisualGeometry(toolGeom);
185  toolObj->setCollidingGeometry(toolGeom);
186  toolObj->setPhysicsGeometry(toolGeom);
187  toolObj->getVisualModel(0)->getRenderMaterial()->setColor(Color(0.9, 0.9, 0.9));
188  toolObj->getVisualModel(0)->getRenderMaterial()->setShadingModel(RenderMaterial::ShadingModel::PBR);
189  toolObj->getVisualModel(0)->getRenderMaterial()->setRoughness(0.5);
190  toolObj->getVisualModel(0)->getRenderMaterial()->setMetalness(1.0);
191  toolObj->getVisualModel(0)->getRenderMaterial()->setIsDynamicMesh(false);
192 
193  auto rbdModel = std::make_shared<RigidBodyModel2>();
194  rbdModel->getConfig()->m_gravity = Vec3d::Zero();
195  rbdModel->getConfig()->m_maxNumIterations = 5;
196  toolObj->setDynamicalModel(rbdModel);
197 
198  toolObj->getRigidBody()->m_mass = 0.3;
199  toolObj->getRigidBody()->m_intertiaTensor = Mat3d::Identity() * 10000.0;
200  toolObj->getRigidBody()->m_initPos = Vec3d(0.0, 0.0, 0.0);
201 
202  // Add a component for controller via another device
203  auto controller = toolObj->addComponent<RigidObjectController>();
204  controller->setControlledObject(toolObj);
205  controller->setLinearKs(1000.0);
206  controller->setAngularKs(10000000.0);
207  controller->setUseCritDamping(true);
208  controller->setForceScaling(0.0045);
209  controller->setSmoothingKernelSize(15);
210  controller->setUseForceSmoothening(true);
211 
212  return toolObj;
213 }
214 
218 int
219 main()
220 {
221  const double capsuleRadius = 0.02;
222  const double tissueLength = 0.15;
223 
224  // Setup logger (write to file and stdout)
226 
227  // Setup the scene
228  auto scene = std::make_shared<Scene>("PbdTissueStitch");
229  scene->getActiveCamera()->setPosition(0.0012, 0.0451, 0.1651);
230  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
231  scene->getActiveCamera()->setViewUp(0.0, 0.96, -0.28);
232 
233  // Setup a tet tissue
234 #ifdef USE_THIN_TISSUE
235  std::shared_ptr<PbdObject> tissueObj = makeTriTissueObj("Tissue",
236  Vec2d(tissueLength, 0.07), Vec2i(15, 5),
237  Vec3d(tissueLength * 0.5, -0.01 - capsuleRadius, 0.0));
238 #else
239  std::shared_ptr<PbdObject> tissueObj = makeTetTissueObj("Tissue",
240  Vec3d(tissueLength, 0.01, 0.07), Vec3i(15, 2, 5),
241  Vec3d(tissueLength * 0.5, -0.01 - capsuleRadius, 0.0));
242 #endif
243  scene->addSceneObject(tissueObj);
244 
245  // Setup a capsule to wrap around
246  auto cdObj = std::make_shared<CollidingObject>("collisionObject");
247  auto capsuleGeom = std::make_shared<Capsule>();
248  capsuleGeom->setPosition(0.0, 0.0, 0.0);
249  capsuleGeom->setRadius(capsuleRadius);
250  capsuleGeom->setLength(0.08);
251  capsuleGeom->setOrientation(Quatd(Rotd(PI_2, Vec3d(1.0, 0.0, 0.0))));
252  cdObj->setVisualGeometry(capsuleGeom);
253  cdObj->getVisualModel(0)->getRenderMaterial()->setColor(
254  Color(246.0 / 255.0, 127.0 / 255.0, 123.0 / 255.0));
255  cdObj->setCollidingGeometry(capsuleGeom);
256  scene->addSceneObject(cdObj);
257 
258  std::shared_ptr<RigidObject2> toolObj = makeToolObj();
259  scene->addSceneObject(toolObj);
260 
261  // Setup CD with a cylinder CD object
262  auto collisionInteraction = std::make_shared<PbdObjectCollision>(tissueObj, cdObj);
263  collisionInteraction->setFriction(0.0);
264  collisionInteraction->setDeformableStiffnessA(0.3);
265  scene->addInteraction(collisionInteraction);
266 
267  auto stitching = std::make_shared<PbdObjectStitching>(tissueObj);
268  stitching->setStitchDistance(0.015);
269  scene->addInteraction(stitching);
270 
271  // Lights
272  auto light1 = std::make_shared<DirectionalLight>();
273  light1->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
274  light1->setIntensity(0.5);
275  scene->addLight("light1", light1);
276  auto light2 = std::make_shared<DirectionalLight>();
277  light2->setFocalPoint(Vec3d(-5.0, -8.0, -5.0));
278  light2->setIntensity(0.5);
279  scene->addLight("light2", light2);
280 
281  // Run the simulation
282  {
283  // Setup a viewer to render
284  auto viewer = std::make_shared<VTKViewer>();
285  viewer->setActiveScene(scene);
286  viewer->setDebugAxesLength(0.001, 0.001, 0.001);
287 
288  // Setup a scene manager to advance the scene
289  auto sceneManager = std::make_shared<SceneManager>();
290  sceneManager->setActiveScene(scene);
291  sceneManager->pause(); // Start simulation paused
292 
293  auto driver = std::make_shared<SimulationManager>();
294  driver->addModule(viewer);
295  driver->addModule(sceneManager);
296  driver->setDesiredDt(0.001);
297 
298 #ifdef iMSTK_USE_HAPTICS
299  // Setup default haptics manager
300  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
301  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
302  driver->addModule(hapticManager);
303 #else
304  auto deviceClient = std::make_shared<DummyClient>();
305  connect<Event>(sceneManager, &SceneManager::postUpdate,
306  [&](Event*)
307  {
308  const Vec2d& pos = viewer->getMouseDevice()->getPos();
309 #ifdef USE_THIN_TISSUE
310  deviceClient->setPosition(Vec3d(40.0, 40.0, -(pos[1] * 100.0 - 50.0)));
311  deviceClient->setOrientation(Quatd(Rotd(-0.6, Vec3d(0.0, 0.0, 1.0))));
312 #else
313  deviceClient->setPosition(Vec3d(37.0, 0.0, -(pos[1] * 100.0 - 50.0)));
314  deviceClient->setOrientation(Quatd(Rotd(0.65, Vec3d(0.0, 0.0, 1.0))));
315 #endif
316  });
317 #endif
318 
319  auto controller = toolObj->getComponent<RigidObjectController>();
320  controller->setDevice(deviceClient);
321 
322 #ifdef iMSTK_USE_HAPTICS
323  connect<ButtonEvent>(deviceClient, &DeviceClient::buttonStateChanged,
324  [&](ButtonEvent* e)
325  {
326  if (e->m_button == 0 && e->m_buttonState == BUTTON_PRESSED)
327  {
328  auto toolGeom = std::dynamic_pointer_cast<LineMesh>(toolObj->getCollidingGeometry());
329  const Vec3d& v1 = toolGeom->getVertexPosition(0);
330  const Vec3d& v2 = toolGeom->getVertexPosition(1);
331  stitching->beginStitch(v1, (v2 - v1).normalized());
332  }
333  });
334 #endif
335 
336  double t = 0.0;
337  connect<KeyEvent>(viewer->getKeyboardDevice(), &KeyboardDeviceClient::keyPress,
338  [&](KeyEvent* e)
339  {
340  // Toggle gravity
341  if (e->m_key == 'g')
342  {
343  Vec3d& g = tissueObj->getPbdModel()->getConfig()->m_gravity;
344  g = Vec3d(0.0, -static_cast<double>(!(g.norm() > 0.0)), 0.0);
345  }
346  // Perform stitch
347  else if (e->m_key == 's')
348  {
349  auto toolGeom = std::dynamic_pointer_cast<LineMesh>(toolObj->getCollidingGeometry());
350  const Vec3d& v1 = toolGeom->getVertexPosition(0);
351  const Vec3d& v2 = toolGeom->getVertexPosition(1);
352  stitching->beginStitch(v1, (v2 - v1).normalized());
353  }
354  // Reset
355  else if (e->m_key == 'r')
356  {
357  t = 0.0;
358  }
359  });
360 
361  // Record the intial positions
362  std::vector<Vec3d> initPositions;
363 
364  auto pointMesh =
365  std::dynamic_pointer_cast<PointSet>(tissueObj->getPhysicsGeometry());
366  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = pointMesh->getVertexPositions();
367  VecDataArray<double, 3>& vertices = *verticesPtr;
368  const std::vector<int> fixedNodes = tissueObj->getPbdBody()->fixedNodeIds;
369  for (size_t i = 0; i < fixedNodes.size(); i++)
370  {
371  initPositions.push_back(vertices[fixedNodes[i]]);
372  }
373  bool stopped = false;
374 
375  // Script the movement of the tissues fixed points
376  connect<Event>(sceneManager, &SceneManager::postUpdate,
377  [&](Event*)
378  {
379  const double dt = sceneManager->getDt();
380  t += dt;
381  if (t < 10.5)
382  {
383  for (size_t i = 0; i < fixedNodes.size(); i++)
384  {
385  Vec3d initPos = initPositions[i];
386  Vec3d& pos = vertices[fixedNodes[i]];
387 
388  const double r = (capsuleGeom->getPosition().head<2>() - initPos.head<2>()).norm();
389  pos = Vec3d(-sin(t) * r, -cos(t) * r, initPos[2]);
390  }
391  }
392  else
393  {
394  if (!stopped)
395  {
396  // Clear and reinit all constraints (new resting lengths)
397  stopped = true;
398  tissueObj->getPbdBody()->fixedNodeIds.clear();
399  tissueObj->getPbdModel()->initialize();
400  }
401  }
402  });
403 
404  // Add default mouse and keyboard controls to the viewer
405  std::shared_ptr<Entity> mouseAndKeyControls =
406  SimulationUtils::createDefaultSceneControl(driver);
407  scene->addSceneObject(mouseAndKeyControls);
408 
409  driver->start();
410  }
411 
412  return 0;
413 }
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
std::shared_ptr< SurfaceMesh > toTriangleGrid(const Vec3d &center, const Vec2d &size, const Vec2i &dim, const Quatd orientation=Quatd::Identity(), const double uvScale=1.0)
Produces a triangle grid on a plane given the imstkPlane.
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
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.
void push_back(const ValueType &val)
Append the data array to hold the new value, resizes if neccesary.
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
const Vec3d & getVertexPosition(const size_t vertNum, DataType type=DataType::PostTransform) const
Returns the position of a vertex given its index.
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
Provides the information of a key event (press, release, & which key)
Base class for all volume mesh types.
Definition: imstkLineMesh.h:18
This class uses the provided device to control the provided rigid object via virtual coupling...
Color in RGB space.
Definition: imstkColor.h:24
Physically based rendering.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.