iMSTK
Interactive Medical Simulation Toolkit
PBDTissueCutExample.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 "CutHelp.h"
8 #include "imstkCamera.h"
9 #include "imstkDeviceManager.h"
10 #include "imstkDeviceManagerFactory.h"
11 #include "imstkDirectionalLight.h"
12 #include "imstkGeometryUtilities.h"
13 #include "imstkKeyboardDeviceClient.h"
14 #include "imstkKeyboardSceneControl.h"
15 #include "imstkMouseDeviceClient.h"
16 #include "imstkMouseSceneControl.h"
17 #include "imstkPbdModel.h"
18 #include "imstkPbdModelConfig.h"
19 #include "imstkPbdObject.h"
20 #include "imstkPbdObjectCellRemoval.h"
21 #include "imstkPbdObjectCollision.h"
22 #include "imstkPbdObjectController.h"
23 #include "imstkPlane.h"
24 #include "imstkPointwiseMap.h"
25 #include "imstkRenderMaterial.h"
26 #include "imstkScene.h"
27 #include "imstkSceneManager.h"
28 #include "imstkSimulationManager.h"
29 #include "imstkSimulationUtils.h"
30 #include "imstkVisualModel.h"
31 #include "imstkVTKViewer.h"
32 #include "imstkVertexLabelVisualModel.h"
33 
34 using namespace imstk;
35 
43 static std::shared_ptr<PbdObject>
44 makeTissueObj(const std::string& name,
45  const Vec3d& size, const Vec3i& dim, const Vec3d& center,
46  std::shared_ptr<PbdModel> model)
47 {
48  // Setup the Geometry
49  std::shared_ptr<TetrahedralMesh> tissueMesh = GeometryUtils::toTetGrid(center, size, dim);
50  std::shared_ptr<SurfaceMesh> surfMesh = tissueMesh->extractSurfaceMesh();
51 
52  // Add a mask of ints to denote how many elements are referencing this vertex
53  auto referenceCountPtr = std::make_shared<DataArray<int>>(tissueMesh->getNumVertices());
54  referenceCountPtr->fill(0);
55  tissueMesh->setVertexAttribute("ReferenceCount", referenceCountPtr);
56 
57  // Use FEMTet constraints
58  model->getConfig()->m_femParams->m_YoungModulus = 50.0;
59  model->getConfig()->m_femParams->m_PoissonRatio = 0.4;
60  model->getConfig()->enableFemConstraint(PbdFemConstraint::MaterialType::StVK);
61 
62  // Setup the material
63  auto material = std::make_shared<RenderMaterial>();
64  material->setBackFaceCulling(false);
65  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
66  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
67 
68  // Setup the Object
69  auto tissueObj = std::make_shared<PbdObject>(name);
70  tissueObj->setPhysicsGeometry(tissueMesh);
71  tissueObj->setVisualGeometry(surfMesh);
72  tissueObj->setCollidingGeometry(surfMesh);
73  auto map = std::make_shared<PointwiseMap>(tissueMesh, surfMesh);
74  tissueObj->setPhysicsToCollidingMap(map);
75  tissueObj->getVisualModel(0)->setRenderMaterial(material);
76  tissueObj->setDynamicalModel(model);
77  tissueObj->getPbdBody()->uniformMassValue = 0.1;
78  // Fix the borders
79  for (int z = 0; z < dim[2]; z++)
80  {
81  for (int y = 0; y < dim[1]; y++)
82  {
83  for (int x = 0; x < dim[0]; x++)
84  {
85  if (x == 0 || /*z == 0 ||*/ x == dim[0] - 1 /*|| z == dim[2] - 1*/)
86  {
87  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z)); // +1 for dummy vertex
88  }
89  }
90  }
91  }
92 
93  return tissueObj;
94 }
95 
96 static std::shared_ptr<PbdObject>
97 makeToolObj(std::shared_ptr<PbdModel> model)
98 {
99  auto plane = std::make_shared<Plane>();
100  plane->setWidth(1.0);
101  std::shared_ptr<SurfaceMesh> toolGeom = GeometryUtils::toSurfaceMesh(plane);
102 
103  auto toolObj = std::make_shared<PbdObject>("Tool");
104  toolObj->setVisualGeometry(toolGeom);
105  toolObj->setCollidingGeometry(toolGeom);
106  toolObj->setPhysicsGeometry(toolGeom);
107  toolObj->setDynamicalModel(model);
108  toolObj->getVisualModel(0)->getRenderMaterial()->setColor(Color::Blue);
109  toolObj->getVisualModel(0)->getRenderMaterial()->setDisplayMode(RenderMaterial::DisplayMode::Surface);
110  toolObj->getVisualModel(0)->getRenderMaterial()->setBackFaceCulling(false);
111  toolObj->getVisualModel(0)->getRenderMaterial()->setLineWidth(1.0);
112 
113  toolObj->getPbdBody()->setRigid(
114  Vec3d(0.0, 0.8, 0.0), // Position
115  0.2, // Mass
116  Quatd::Identity(), // Orientation
117  Mat3d::Identity() * 10000.0); // Inertia
118 
119  auto controller = toolObj->addComponent<PbdObjectController>();
120  controller->setControlledObject(toolObj);
121  controller->setTranslationScaling(60.0);
122  controller->setLinearKs(1000.0);
123  controller->setLinearKd(50.0);
124  controller->setAngularKs(10000000.0);
125  controller->setAngularKd(500000.0);
126  controller->setForceScaling(0.0001);
127 
128  return toolObj;
129 }
130 
136 int
137 main()
138 {
139  // Setup logger (write to file and stdout)
141 
142  // Setup the scene
143  auto scene = std::make_shared<Scene>("PbdTissueCut");
144  scene->getActiveCamera()->setPosition(0.12, 4.51, 16.51);
145  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
146  scene->getActiveCamera()->setViewUp(0.0, 0.96, -0.28);
147 
148  // Setup the Model/System
149  auto pbdModel = std::make_shared<PbdModel>();
150  pbdModel->getConfig()->m_doPartitioning = false;
151  pbdModel->getConfig()->m_gravity = Vec3d(0.0, -0.2, 0.0);
152  pbdModel->getConfig()->m_dt = 0.05;
153  pbdModel->getConfig()->m_iterations = 5;
154 
155  // Setup a tissue
156  std::shared_ptr<PbdObject> tissueObj = makeTissueObj("Tissue",
157  Vec3d(10.0, 3.0, 10.0), Vec3i(8, 3, 8), Vec3d(0.0, -1.0, 0.0),
158  pbdModel);
159  scene->addSceneObject(tissueObj);
160 
161  auto cellRemoval = std::make_shared<PbdObjectCellRemoval>(tissueObj, PbdObjectCellRemoval::OtherMeshUpdateType::Collision);
162  scene->addInteraction(cellRemoval);
163 
164  std::shared_ptr<PbdObject> toolObj = makeToolObj(pbdModel);
165  scene->addSceneObject(toolObj);
166 
167  /*auto interaction = std::make_shared<PbdObjectCollision>(
168  toolObj, tissueObj, "ClosedSurfaceMeshToMeshCD");
169  scene->addInteraction(interaction);*/
170 
171  auto sceneObject = std::make_shared<SceneObject>();
172  auto visualModel = sceneObject->addComponent<VertexLabelVisualModel>();
173  visualModel->setGeometry(tissueObj->getCollidingGeometry());
174  //scene->addSceneObject(sceneObject);
175 
176  // Light
177  auto light = std::make_shared<DirectionalLight>();
178  light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
179  light->setIntensity(1.0);
180  scene->addLight("Light", light);
181 
182  // Run the simulation
183  {
184  // Setup a viewer to render
185  auto viewer = std::make_shared<VTKViewer>();
186  viewer->setActiveScene(scene);
187  viewer->setVtkLoggerMode(VTKViewer::VTKLoggerMode::MUTE);
188 
189  // Setup a scene manager to advance the scene
190  auto sceneManager = std::make_shared<SceneManager>();
191  sceneManager->setActiveScene(scene);
192  sceneManager->pause(); // Start simulation paused
193 
194  auto driver = std::make_shared<SimulationManager>();
195  driver->addModule(viewer);
196  driver->addModule(sceneManager);
197  driver->setDesiredDt(0.01);
198 
199  // Setup default haptics manager
200  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
201  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
202  driver->addModule(hapticManager);
203 
204  auto controller = toolObj->getComponent<PbdObjectController>();
205  controller->setDevice(deviceClient);
206 
207  connect<Event>(sceneManager, &SceneManager::preUpdate, [&](Event*)
208  {
209  // Keep the tool moving in real time
210  pbdModel->getConfig()->m_dt = sceneManager->getDt();
211  });
212 
213  connect<Event>(sceneManager, &SceneManager::postUpdate, [&](Event*)
214  {
215  if (deviceClient->getButton(0))
216  {
217  auto tissueMesh = std::dynamic_pointer_cast<TetrahedralMesh>(tissueObj->getPhysicsGeometry());
218  auto toolGeom = std::dynamic_pointer_cast<SurfaceMesh>(toolObj->getCollidingGeometry());
219 
220  // Default config of the tool is pointing downwards on y
221  const Mat3d rot = toolGeom->getRotation();
222  const Vec3d forward = (rot * Vec3d(0.0, 0.0, 1.0)).normalized();
223  const Vec3d left = (rot * Vec3d(1.0, 0.0, 0.0)).normalized();
224  const Vec3d n = (rot * Vec3d(0.0, 1.0, 0.0)).normalized();
225 
226  const Vec3d planePos = toolGeom->getTranslation();
227  const Vec3d planeNormal = n;
228  const double planeWidth = 1.1; // Slightly larger than collision geometry
229  const double planeHalfWidth = planeWidth * 0.5;
230 
231  std::shared_ptr<VecDataArray<double, 3>> tissueVerticesPtr = tissueMesh->getVertexPositions();
232  std::shared_ptr<VecDataArray<int, 4>> tissueIndicesPtr = tissueMesh->getCells();
233  VecDataArray<double, 3>& tissueVertices = *tissueVerticesPtr;
234  VecDataArray<int, 4>& tissueIndices = *tissueIndicesPtr;
235 
236  // Compute which tets should be removed
237  std::unordered_set<int> removedTets;
238  for (int i = 0; i < tissueIndices.size(); i++)
239  {
240  Vec4i& tet = tissueIndices[i];
241  std::array<Vec3d, 4> tetVerts;
242  tetVerts[0] = tissueVertices[tet[0]];
243  tetVerts[1] = tissueVertices[tet[1]];
244  tetVerts[2] = tissueVertices[tet[2]];
245  tetVerts[3] = tissueVertices[tet[3]];
246 
247  if (splitTest(tetVerts, planePos, left, planeHalfWidth, forward, planeHalfWidth, n))
248  {
249  cellRemoval->removeCellOnApply(i);
250  }
251  }
252  cellRemoval->apply();
253  }
254  });
255 
256  // Add default mouse and keyboard controls to the viewer
257  std::shared_ptr<Entity> mouseAndKeyControls =
258  SimulationUtils::createDefaultSceneControl(driver);
259  scene->addSceneObject(mouseAndKeyControls);
260 
261  driver->start();
262  }
263 
264  return 0;
265 }
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.
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.
Mat3d getRotation() const
Get/Set rotation.
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
Represents a set of tetrahedrons & vertices via an array of Vec3d double vertices & Vec4i integer ind...
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
Given a PointSet geometry it will render labels for each vertex with numberings.
std::shared_ptr< SurfaceMesh > toSurfaceMesh(std::shared_ptr< AnalyticalGeometry > geom)
Produces SurfaceMesh from an analytical geometry.
Physically based rendering.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.