iMSTK
Interactive Medical Simulation Toolkit
pbdStaticSutureExample.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 "imstkArcNeedle.h"
8 #include "imstkCamera.h"
9 #include "imstkGeometryUtilities.h"
10 #include "imstkIsometricMap.h"
11 #include "imstkKeyboardDeviceClient.h"
12 #include "imstkKeyboardSceneControl.h"
13 #include "imstkMeshIO.h"
14 #include "imstkMouseDeviceClient.h"
15 #include "imstkMouseSceneControl.h"
16 #include "imstkOrientedBox.h"
17 #include "imstkPbdModel.h"
18 #include "imstkPbdModelConfig.h"
19 #include "imstkPbdObject.h"
20 #include "imstkPbdObjectCollision.h"
21 #include "imstkPuncturable.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 "imstkVisualModel.h"
32 #include "imstkVTKViewer.h"
33 #include "NeedleInteraction.h"
34 
35 #undef iMSTK_USE_HAPTICS
36 #ifdef iMSTK_USE_HAPTICS
37 #include "imstkDeviceManager.h"
38 #include "imstkDeviceManagerFactory.h"
39 #else
40 #include "imstkDummyClient.h"
41 #endif
42 
43 using namespace imstk;
44 
48 static std::shared_ptr<PbdObject>
49 makePbdString(
50  const std::string& name,
51  const Vec3d& pos, const Vec3d& dir, const int numVerts,
52  const double stringLength)
53 {
54  auto stringObj = std::make_shared<PbdObject>(name);
55 
56  // Setup the Geometry
57  std::shared_ptr<LineMesh> stringMesh =
58  GeometryUtils::toLineGrid(pos, dir, stringLength, numVerts);
59 
60  // Setup the Parameters
61  auto pbdParams = std::make_shared<PbdModelConfig>();
62  pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 100.0);
63  pbdParams->enableBendConstraint(100000.0, 1);
64  pbdParams->enableBendConstraint(100000.0, 2);
65  pbdParams->m_gravity = Vec3d(0.0, -9.8, 0.0);
66  pbdParams->m_dt = 0.0005; // Overwritten for real time
67 
68  // Requires large amounts of iterations the longer, a different
69  // solver would help
70  pbdParams->m_iterations = 100;
71  pbdParams->m_linearDampingCoeff = 0.01;
72 
73  // Setup the Model
74  auto pbdModel = std::make_shared<PbdModel>();
75  pbdModel->configure(pbdParams);
76 
77  // Setup the VisualModel
78  auto material = std::make_shared<RenderMaterial>();
79  material->setBackFaceCulling(false);
80  material->setColor(Color::Red);
81  material->setLineWidth(2.0);
82  material->setPointSize(6.0);
83  material->setDisplayMode(RenderMaterial::DisplayMode::Wireframe);
84 
85  // Setup the Object
86  stringObj->setVisualGeometry(stringMesh);
87  stringObj->getVisualModel(0)->setRenderMaterial(material);
88  stringObj->setPhysicsGeometry(stringMesh);
89  stringObj->setCollidingGeometry(stringMesh);
90  stringObj->setDynamicalModel(pbdModel);
91  stringObj->getPbdBody()->fixedNodeIds = { 0, 1, 19, 20 };
92  stringObj->getPbdBody()->uniformMassValue = 0.002 / numVerts; // grams
93 
94  return stringObj;
95 }
96 
100 static std::shared_ptr<CollidingObject>
101 makeTissueObj()
102 {
103  auto tissueObj = std::make_shared<CollidingObject>("tissue");
104 
105  auto box1 = std::make_shared<OrientedBox>(Vec3d(0.0, -0.1, -0.1), Vec3d(0.1, 0.025, 0.1));
106  auto box1Model = std::make_shared<VisualModel>();
107  box1Model->setGeometry(box1);
108  box1Model->getRenderMaterial()->setShadingModel(RenderMaterial::ShadingModel::Gouraud);
109  box1Model->getRenderMaterial()->setColor(Color::LightSkin);
110  tissueObj->addVisualModel(box1Model);
111 
112  tissueObj->setCollidingGeometry(box1);
113 
114  auto box2 = std::make_shared<OrientedBox>(Vec3d(0.0, -0.105, -0.1), Vec3d(0.1001, 0.025, 0.1001));
115  auto box2Model = std::make_shared<VisualModel>();
116  box2Model->setGeometry(box2);
117  box2Model->getRenderMaterial()->setShadingModel(RenderMaterial::ShadingModel::Gouraud);
118  box2Model->getRenderMaterial()->setColor(Color::darken(Color::Yellow, 0.2));
119  tissueObj->addVisualModel(box2Model);
120 
121  tissueObj->addComponent<Puncturable>();
122 
123  return tissueObj;
124 }
125 
126 static std::shared_ptr<SceneObject>
127 makeToolObj(std::string name)
128 {
129  auto surfMesh =
130  MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Clamps/Gregory Suture Clamp/gregory_suture_clamp.obj");
131 
132  auto toolObj = std::make_shared<SceneObject>(name);
133  toolObj->setVisualGeometry(surfMesh);
134  auto renderMaterial = std::make_shared<RenderMaterial>();
135  renderMaterial->setColor(Color::LightGray);
136  renderMaterial->setShadingModel(RenderMaterial::ShadingModel::PBR);
137  renderMaterial->setRoughness(0.5);
138  renderMaterial->setMetalness(1.0);
139  toolObj->getVisualModel(0)->setRenderMaterial(renderMaterial);
140 
141  return toolObj;
142 }
143 
144 static std::shared_ptr<RigidObject2>
145 makeNeedleObj()
146 {
147  auto needleObj = std::make_shared<RigidObject2>();
148 
149  auto sutureMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Needles/c6_suture.stl");
150  auto sutureLineMesh = MeshIO::read<LineMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Needles/c6_suture_hull.vtk");
151 
152  const Mat4d rot = mat4dRotation(Rotd(-PI_2, Vec3d(0.0, 1.0, 0.0))) *
153  mat4dRotation(Rotd(-0.6, Vec3d(1.0, 0.0, 0.0)));
154  sutureMesh->transform(rot, Geometry::TransformType::ApplyToData);
155  sutureLineMesh->transform(rot, Geometry::TransformType::ApplyToData);
156 
157  needleObj->setVisualGeometry(sutureMesh);
158  needleObj->setCollidingGeometry(sutureLineMesh);
159  needleObj->setPhysicsGeometry(sutureLineMesh);
160  needleObj->setPhysicsToVisualMap(std::make_shared<IsometricMap>(sutureLineMesh, sutureMesh));
161  needleObj->getVisualModel(0)->getRenderMaterial()->setColor(Color(0.9, 0.9, 0.9));
162  needleObj->getVisualModel(0)->getRenderMaterial()->setShadingModel(RenderMaterial::ShadingModel::PBR);
163  needleObj->getVisualModel(0)->getRenderMaterial()->setRoughness(0.5);
164  needleObj->getVisualModel(0)->getRenderMaterial()->setMetalness(1.0);
165 
166  std::shared_ptr<RigidBodyModel2> rbdModel = std::make_shared<RigidBodyModel2>();
167  rbdModel->getConfig()->m_gravity = Vec3d::Zero();
168  rbdModel->getConfig()->m_maxNumIterations = 5;
169  needleObj->setDynamicalModel(rbdModel);
170 
171  needleObj->getRigidBody()->m_mass = 1.0;
172  needleObj->getRigidBody()->m_intertiaTensor = Mat3d::Identity() * 10000.0;
173  needleObj->getRigidBody()->m_initPos = Vec3d(0.0, 0.0, 0.0);
174 
175  // Manually setup an arc aligned with the geometry, some sort of needle+arc generator
176  // could be a nice addition to imstk
177  Mat3d arcBasis = Mat3d::Identity();
178  arcBasis.col(0) = Vec3d(0.0, 0.0, -1.0);
179  arcBasis.col(1) = Vec3d(1.0, 0.0, 0.0);
180  arcBasis.col(2) = Vec3d(0.0, 1.0, 0.0);
181  arcBasis = rot.block<3, 3>(0, 0) * arcBasis;
182  const Vec3d arcCenter = (rot * Vec4d(0.0, -0.005455, 0.008839, 1.0)).head<3>();
183  const double arcRadius = 0.010705;
184 
185  // Add a component for needles
186  auto needle = needleObj->addComponent<ArcNeedle>();
187  needle->setArc(arcCenter, arcBasis, arcRadius, 0.558, 2.583);
188 
189  // Add a component to control the tool
190  auto controller = needleObj->addComponent<RigidObjectController>();
191  controller->setControlledObject(needleObj);
192  controller->setLinearKs(1000.0);
193  controller->setAngularKs(10000000.0);
194  controller->setUseCritDamping(true);
195  controller->setForceScaling(0.2);
196  controller->setSmoothingKernelSize(5);
197  controller->setUseForceSmoothening(true);
198 
199  return needleObj;
200 }
201 
211 int
212 main()
213 {
214  // Setup logger (write to file and stdout)
216 
217  auto scene = std::make_shared<Scene>("PbdStaticSuture");
218 
219  // Create the arc needle
220  std::shared_ptr<RigidObject2> needleObj = makeNeedleObj();
221  scene->addSceneObject(needleObj);
222 
223  // Create the suture pbd-based string
224  const double stringLength = 0.2;
225  const int stringVertexCount = 30;
226  std::shared_ptr<PbdObject> sutureThreadObj =
227  makePbdString("SutureThread", Vec3d(0.0, 0.0, 0.018), Vec3d(0.0, 0.0, 1.0),
228  stringVertexCount, stringLength);
229  scene->addSceneObject(sutureThreadObj);
230 
231  // Create a static box for tissue
232  std::shared_ptr<CollidingObject> tissueObj = makeTissueObj();
233  scene->addSceneObject(tissueObj);
234 
235  // Create clamps that follow the needle around
236  std::shared_ptr<SceneObject> clampsObj = makeToolObj("Clamps");
237  scene->addSceneObject(clampsObj);
238 
239  // Create ghost clamps to show real position of hand under virtual coupling
240  std::shared_ptr<SceneObject> ghostClampsObj = makeToolObj("GhostClamps");
241  ghostClampsObj->getVisualModel(0)->getRenderMaterial()->setColor(Color::Orange);
242  scene->addSceneObject(ghostClampsObj);
243 
244  // Add point based collision between the tissue & suture thread
245  auto interaction = std::make_shared<PbdObjectCollision>(sutureThreadObj, tissueObj);
246  interaction->setFriction(0.0);
247  scene->addInteraction(interaction);
248 
249  // Add needle constraining behaviour between the tissue & arc needle
250  auto needleInteraction = std::make_shared<NeedleInteraction>(tissueObj, needleObj);
251  scene->addInteraction(needleInteraction);
252 
253  // Adjust the camera
254  scene->getActiveCamera()->setFocalPoint(0.00138345, -0.0601133, -0.0261938);
255  scene->getActiveCamera()->setPosition(0.00137719, 0.0492882, 0.201508);
256  scene->getActiveCamera()->setViewUp(-0.000780726, 0.901361, -0.433067);
257 
258  // Run the simulation
259  {
260  // Setup a viewer to render
261  auto viewer = std::make_shared<VTKViewer>();
262  viewer->setActiveScene(scene);
263  viewer->setDebugAxesLength(0.01, 0.01, 0.01);
264 
265  // Setup a scene manager to advance the scene
266  auto sceneManager = std::make_shared<SceneManager>();
267  sceneManager->setActiveScene(scene);
268  sceneManager->pause(); // Start simulation paused
269 
270  // Setup a simulation manager to manage renders & scene updates
271  auto driver = std::make_shared<SimulationManager>();
272  driver->addModule(viewer);
273  driver->addModule(sceneManager);
274  driver->setDesiredDt(0.001); // 1ms, 1000hz
275 
276  auto controller = needleObj->getComponent<RigidObjectController>();
277 #ifdef iMSTK_USE_HAPTICS
278  // Setup default haptics manager
279  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
280  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
281  driver->addModule(hapticManager);
282 
283  controller->setTranslationOffset(Vec3d(0.05, -0.05, 0.0));
284 #else
285  auto deviceClient = std::make_shared<DummyClient>();
286  deviceClient->setOrientation(Quatd(Rotd(1.57, Vec3d(0.0, 1.0, 0.0))));
287  controller->setTranslationScaling(0.13);
288  controller->setTranslationOffset(Vec3d(-0.05, -0.1, -0.005));
289 
290  auto needleMouseMove = needleObj->addComponent<LambdaBehaviour>("NeedleMouseMove");
291  needleMouseMove->setUpdate([&](const double&)
292  {
293  const Vec2d& pos2d = viewer->getMouseDevice()->getPos();
294  deviceClient->setPosition(Vec3d(pos2d[0], pos2d[1], 0.0));
295  });
296  connect<MouseEvent>(viewer->getMouseDevice(), &MouseDeviceClient::mouseScroll,
297  [&](MouseEvent* e)
298  {
299  const Quatd delta = Quatd(Rotd(e->m_scrollDx * 0.1, Vec3d(0.0, 0.0, 1.0)));
300  deviceClient->setOrientation(deviceClient->getOrientation() * delta);
301  });
302 #endif
303  controller->setDevice(deviceClient);
304 
305  // Update the timesteps for real time
306  connect<Event>(sceneManager, &SceneManager::preUpdate,
307  [&](Event*)
308  {
309  needleObj->getRigidBodyModel2()->getConfig()->m_dt = sceneManager->getDt();
310  // sutureThreadObj->getPbdModel()->getConfig()->m_dt = sceneManager->getDt();
311  });
312  // Constrain the first two vertices of the string to the needle
313  connect<Event>(sceneManager, &SceneManager::postUpdate,
314  [&](Event*)
315  {
316  auto needleLineMesh = std::dynamic_pointer_cast<LineMesh>(needleObj->getPhysicsGeometry());
317  auto sutureLineMesh = std::dynamic_pointer_cast<LineMesh>(sutureThreadObj->getPhysicsGeometry());
318  (*sutureLineMesh->getVertexPositions())[1] = (*needleLineMesh->getVertexPositions())[0];
319  (*sutureLineMesh->getVertexPositions())[0] = (*needleLineMesh->getVertexPositions())[1];
320  });
321  // Transform the clamps relative to the needle
322  const Vec3d clampOffset = Vec3d(-0.009, 0.01, 0.001);
323  connect<Event>(sceneManager, &SceneManager::postUpdate,
324  [&](Event*)
325  {
326  clampsObj->getVisualGeometry()->setTransform(
327  needleObj->getVisualGeometry()->getTransform() *
328  mat4dTranslate(clampOffset) *
329  mat4dRotation(Rotd(PI, Vec3d(0.0, 1.0, 0.0))));
330  clampsObj->getVisualGeometry()->postModified();
331  });
332  // Transform the ghost tool clamps to show the real tool location
333  connect<Event>(sceneManager, &SceneManager::postUpdate,
334  [&](Event*)
335  {
336  ghostClampsObj->getVisualGeometry()->setTransform(
337  mat4dTranslate(controller->getPosition()) * mat4dRotation(controller->getOrientation()) *
338  mat4dTranslate(clampOffset) *
339  mat4dRotation(Rotd(PI, Vec3d(0.0, 1.0, 0.0))));
340  ghostClampsObj->getVisualGeometry()->updatePostTransformData();
341  ghostClampsObj->getVisualGeometry()->postModified();
342  ghostClampsObj->getVisualModel(0)->getRenderMaterial()->setOpacity(std::min(1.0, controller->getDeviceForce().norm() / 5.0));
343  });
344 
345  // Add default mouse and keyboard controls to the viewer
346  std::shared_ptr<Entity> mouseAndKeyControls =
347  SimulationUtils::createDefaultSceneControl(driver);
348  scene->addSceneObject(mouseAndKeyControls);
349 
350  driver->start();
351  }
352 
353  return 0;
354 }
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
Place this on an object to make it puncturable by a needle. This allows puncturables to know they&#39;ve ...
Compound Geometry.
Gouraud shading model (default)
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
std::shared_ptr< LineMesh > toLineGrid(const Vec3d &start, const Vec3d &dir, const double length, const int dim)
Creates a set of connected lines.
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
Provides the information of a mouse event, this includes button presses/releases and scrolling...
A SceneBehaviour that can update via a lambda function.
Physically based rendering.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.