iMSTK
Interactive Medical Simulation Toolkit
vesselExample.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 "imstkDirectionalLight.h"
9 #include "imstkImageDistanceTransform.h"
10 #include "imstkKeyboardDeviceClient.h"
11 #include "imstkKeyboardSceneControl.h"
12 #include "imstkMeshIO.h"
13 #include "imstkMouseDeviceClient.h"
14 #include "imstkMouseSceneControl.h"
15 #include "imstkNew.h"
16 #include "imstkRenderMaterial.h"
17 #include "imstkScene.h"
18 #include "imstkSceneManager.h"
19 #include "imstkSignedDistanceField.h"
20 #include "imstkSimulationManager.h"
21 #include "imstkSimulationUtils.h"
22 #include "imstkSphModel.h"
23 #include "imstkSphObject.h"
24 #include "imstkSphObjectCollision.h"
25 #include "imstkSurfaceMesh.h"
26 #include "imstkSurfaceMeshDistanceTransform.h"
27 #include "imstkSurfaceMeshImageMask.h"
28 #include "imstkVisualModel.h"
29 #include "imstkVTKViewer.h"
30 
31 using namespace imstk;
32 
36 static std::shared_ptr<VecDataArray<double, 3>>
37 generateFluidVolume(const double particleRadius, std::shared_ptr<SurfaceMesh> spawnSurfaceVolume)
38 {
39  Vec3d minima, maxima;
40  spawnSurfaceVolume->computeBoundingBox(minima, maxima);
41 
42  double particleDiameter = particleRadius * 2.0;
43  const Vec3d size = (maxima - minima) + Vec3d(particleDiameter, particleDiameter, particleDiameter);
44  Vec3i dim = size.cwiseProduct(Vec3d(1.0 / particleDiameter, 1.0 / particleDiameter, 1.0 / particleDiameter)).cast<int>();
45 
46  // Compute the binary mask
47  imstkNew<SurfaceMeshImageMask> makeBinaryMask;
48  makeBinaryMask->setInputMesh(spawnSurfaceVolume);
49  makeBinaryMask->setDimensions(dim[0], dim[1], dim[2]);
50  makeBinaryMask->update();
51 
52  // Compute the DT (won't perfectly conform to surface as we used a binary mask)
53  imstkNew<ImageDistanceTransform> distTransformFromMask;
54  distTransformFromMask->setInputImage(makeBinaryMask->getOutputImage());
55  distTransformFromMask->update();
56 
57  std::shared_ptr<DataArray<float>> scalarsPtr = std::dynamic_pointer_cast<DataArray<float>>(distTransformFromMask->getOutputImage()->getScalars());
58  const DataArray<float>& scalars = *scalarsPtr;
59  const Vec3i& dim1 = makeBinaryMask->getOutputImage()->getDimensions();
60  const Vec3d& spacing = makeBinaryMask->getOutputImage()->getSpacing();
61  const Vec3d& shift = makeBinaryMask->getOutputImage()->getOrigin() + spacing * 0.5;
62  const double threshold = particleDiameter * 1.0; // How far from the boundary to accept particles
63 
65  particles->reserve(dim1[0] * dim1[1] * dim1[2]);
66 
67  int i = 0;
68  for (int z = 0; z < dim1[2]; z++)
69  {
70  for (int y = 0; y < dim1[1]; y++)
71  {
72  for (int x = 0; x < dim1[0]; x++, i++)
73  {
74  if (x > 1 && y > 1 && z > 1 && scalars[i] < -threshold)
75  {
76  particles->push_back(Vec3d(x, y, z).cwiseProduct(spacing) + shift);
77  }
78  }
79  }
80  }
81  particles->squeeze();
82  return particles;
83 }
84 
85 static std::shared_ptr<SphObject>
86 makeSPHObject(const std::string& name, const double particleRadius, const double particleSpacing)
87 {
88  // Create the sph object
89  imstkNew<SphObject> fluidObj(name);
90 
91  // Setup the Geometry
92  auto spawnMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/legs/femoralArteryCut.stl");
93  // By spacing them slightly closer we can induce larger compression at the start
94  std::shared_ptr<VecDataArray<double, 3>> particles = generateFluidVolume(particleSpacing, spawnMesh);
95  LOG(INFO) << "Number of particles: " << particles->size();
96  imstkNew<PointSet> fluidGeometry;
97  fluidGeometry->initialize(particles);
98 
99  // Setup the Parameters
100  imstkNew<SphModelConfig> sphParams(particleRadius);
101  sphParams->m_bNormalizeDensity = true;
102  sphParams->m_kernelOverParticleRadiusRatio = 6.0;
103  sphParams->m_surfaceTensionStiffness = 5.0;
104  sphParams->m_frictionBoundary = 0.1;
105 
106  // Setup the Model
107  imstkNew<SphModel> sphModel;
108  sphModel->setModelGeometry(fluidGeometry);
109  sphModel->configure(sphParams);
110  sphModel->setTimeStepSizeType(TimeSteppingType::RealTime);
111 
112  // Setup the VisualModel
113  imstkNew<VisualModel> fluidVisualModel;
114  fluidVisualModel->setGeometry(fluidGeometry);
115  imstkNew<RenderMaterial> fluidMaterial;
116  fluidMaterial->setDisplayMode(RenderMaterial::DisplayMode::Fluid);
117  fluidMaterial->setPointSize(particleRadius * 2.0f); // For fluids
118  //fluidMaterial->setDisplayMode(RenderMaterial::DisplayMode::Points);
119  //fluidMaterial->setPointSize(particleRadius * 1200.0f); // For points (todo: remove view dependence)
120  fluidVisualModel->setRenderMaterial(fluidMaterial);
121 
122  // Setup the Object
123  fluidObj->setDynamicalModel(sphModel);
124  fluidObj->addVisualModel(fluidVisualModel);
125  fluidObj->setCollidingGeometry(fluidGeometry);
126  fluidObj->setPhysicsGeometry(fluidGeometry);
127 
128  return fluidObj;
129 }
130 
131 static std::shared_ptr<CollidingObject>
132 makeLegs(const std::string& name)
133 {
134  // Create the pbd object
135  imstkNew<CollidingObject> legsObj(name);
136 
137  // Setup the Geometry (read dragon mesh)
138  auto legsMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/legs/legsCutaway.stl");
139  auto bonesMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/legs/legsBones.stl");
140  auto femoralMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/legs/femoralArtery.stl");
141  auto collisionMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/legs/femoralArteryCut.stl");
142 
143  // Setup the Legs VisualModel
144  imstkNew<VisualModel> legsMeshModel;
145  legsMeshModel->setGeometry(legsMesh);
146  imstkNew<RenderMaterial> legsMaterial;
147  legsMaterial->setDisplayMode(RenderMaterial::DisplayMode::Surface);
148  legsMaterial->setOpacity(0.85f);
149  legsMaterial->setDiffuseColor(Color(0.8, 0.688, 0.396));
150  legsMeshModel->setRenderMaterial(legsMaterial);
151 
152  // Setup the Bones VisualModel
153  imstkNew<VisualModel> bonesMeshModel;
154  bonesMeshModel->setGeometry(bonesMesh);
155  imstkNew<RenderMaterial> bonesMaterial;
156  bonesMaterial->setDisplayMode(RenderMaterial::DisplayMode::Surface);
157  bonesMaterial->setDiffuseColor(Color(0.538, 0.538, 0.538));
158  bonesMeshModel->setRenderMaterial(bonesMaterial);
159 
160  // Setup the Femoral VisualModel
161  imstkNew<VisualModel> femoralMeshModel;
162  femoralMeshModel->setGeometry(femoralMesh);
163  imstkNew<RenderMaterial> femoralMaterial;
164  femoralMaterial->setDisplayMode(RenderMaterial::DisplayMode::Surface);
165  femoralMaterial->setOpacity(0.2f);
166  femoralMaterial->setDiffuseColor(Color(0.8, 0.119, 0.180));
167  femoralMeshModel->setRenderMaterial(femoralMaterial);
168 
169  // Setup the Object
170  legsObj->addVisualModel(legsMeshModel);
171  legsObj->addVisualModel(bonesMeshModel);
172  legsObj->addVisualModel(femoralMeshModel);
173 
174  LOG(INFO) << "Computing SDF";
176  computeSdf->setInputMesh(collisionMesh);
177  computeSdf->setDimensions(100, 100, 100);
178  Vec3d min, max;
179  collisionMesh->computeBoundingBox(min, max);
180  const Vec3d size = max - min;
181  Vec6d bounds;
182  bounds[0] = min[0] - size[0] * 0.25;
183  bounds[1] = max[0] + size[0] * 0.25;
184  bounds[2] = min[1] - size[1] * 0.25;
185  bounds[3] = max[1] + size[1] * 0.25;
186  bounds[4] = min[2] - size[2] * 0.25;
187  bounds[5] = max[2] + size[2] * 0.25;
188  computeSdf->setBounds(bounds);
189  computeSdf->update();
190  LOG(INFO) << "SDF Complete";
191  legsObj->setCollidingGeometry(std::make_shared<SignedDistanceField>(computeSdf->getOutputImage()));
192 
193  return legsObj;
194 }
195 
200 int
201 main()
202 {
203  // Setup logger (write to file and stdout)
205 
206  imstkNew<Scene> scene("Vessel");
207 
208  // Setup the scene
209  {
210  // Static Dragon object
211  std::shared_ptr<CollidingObject> legsObj = makeLegs("Legs");
212  scene->addSceneObject(legsObj);
213 
214  // Position the camera
215  //const Vec6d& bounds = std::dynamic_pointer_cast<SignedDistanceField>(legsObj->getCollidingGeometry())->getBounds();
216  //const Vec3d center = (Vec3d(bounds[0], bounds[2], bounds[4]) + Vec3d(bounds[1], bounds[3], bounds[5])) * 0.5;
217  scene->getActiveCamera()->setPosition(3.25, 1.6, 3.38);
218  scene->getActiveCamera()->setFocalPoint(-2.05, 1.89, -1.32);
219  scene->getActiveCamera()->setViewUp(-0.66, 0.01, 0.75);
220 
221  // SPH fluid box overtop the dragon
222  std::shared_ptr<SphObject> sphObj = makeSPHObject("Fluid", 0.004, 0.0035);
223  scene->addSceneObject(sphObj);
224 
225  // Interaction
226  scene->addInteraction(
227  std::make_shared<SphObjectCollision>(sphObj, legsObj));
228 
229  // Light
231  light->setDirection(0.0, 1.0, -1.0);
232  light->setIntensity(1.0);
233  scene->addLight("light0", light);
234  }
235 
236  // Run the simulation
237  {
238  // Setup a viewer to render in its own thread
239  imstkNew<VTKViewer> viewer;
240  viewer->setActiveScene(scene);
241  viewer->setBackgroundColors(Color(0.3285, 0.3285, 0.6525), Color(0.13836, 0.13836, 0.2748), true);
242 
243  // Setup a scene manager to advance the scene in its own thread
244  imstkNew<SceneManager> sceneManager;
245  sceneManager->setActiveScene(scene);
246  sceneManager->pause();
247 
249  driver->addModule(viewer);
250  driver->addModule(sceneManager);
251  driver->setDesiredDt(0.005);
252 
253  // Add default mouse and keyboard controls to the viewer
254  std::shared_ptr<Entity> mouseAndKeyControls =
255  SimulationUtils::createDefaultSceneControl(driver);
256  scene->addSceneObject(mouseAndKeyControls);
257 
258  driver->start();
259  }
260 
261  return 0;
262 }
void initialize(std::shared_ptr< VecDataArray< double, 3 >> positions)
Initializes the data structure given vertex positions.
void setDesiredDt(const double dt)
Sets the target fixed timestep (may violate), seconds This ultimately effects the number of iteration...
void setActiveScene(std::shared_ptr< Scene > scene) override
Set scene to be rendered.
void configure(const std::shared_ptr< SphModelConfig > &params)
Set simulation parameters.
Definition: imstkSphModel.h:90
Renders a set of points using a screen-space fluid renderer.
virtual void setTimeStepSizeType(const TimeSteppingType type)
Get/Set the type of approach used to update the time step size after every frame. ...
Compound Geometry.
void addModule(std::shared_ptr< Module > module) override
Add a module to run.
virtual void setBackgroundColors(const Color color1, const Color color2=Color(0.0, 0.0, 0.0), const bool gradientBackground=false) override
Set the coloring of the screen background If &#39;gradientBackground&#39; is false or not supplied color1 wil...
void setIntensity(const double intensity)
Set the light intensity. This value is unbounded.
Definition: imstkLight.h:71
void setBounds(const Vec3d &min, const Vec3d &max)
Optionally one may specify bounds, if not specified bounds of the input SurfaceMesh is used...
std::shared_ptr<T> obj = std::make_shared<T>(); equivalent, convenience class for STL shared allocati...
Definition: imstkNew.h:29
void update()
Do the actual algorithm.
void setModelGeometry(std::shared_ptr< Geometry > geometry)
Sets the model geometry.
void setRenderMaterial(std::shared_ptr< RenderMaterial > renderMaterial)
Set/Get render material.
void setDirection(const Vec3d &dir)
Set the direction of the light.
Color in RGB space.
Definition: imstkColor.h:24
void setInputImage(std::shared_ptr< ImageData > refImage)
Required input, port 0.
void setActiveScene(std::string newSceneName)
Sets the currently updating scene.
Simple dynamic array implementation that also supports event posting and viewing/facade.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.
void setInputMesh(std::shared_ptr< SurfaceMesh > surfMesh)
Required input, port 0.