iMSTK
Interactive Medical Simulation Toolkit
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
imstkGeometryUtilities.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 "imstkGeometryUtilities.h"
8 #include "imstkCapsule.h"
9 #include "imstkCylinder.h"
10 #include "imstkHexahedralMesh.h"
11 #include "imstkImageData.h"
12 #include "imstkLineMesh.h"
13 #include "imstkLogger.h"
14 #include "imstkOrientedBox.h"
15 #include "imstkParallelUtils.h"
16 #include "imstkPlane.h"
17 #include "imstkSphere.h"
18 #include "imstkSurfaceMesh.h"
19 #include "imstkTetrahedralMesh.h"
20 
21 #include <vtkCapsuleSource.h>
22 #include <vtkCellData.h>
23 #include <vtkCharArray.h>
24 #include <vtkCleanPolyData.h>
25 #include <vtkCubeSource.h>
26 #include <vtkCylinderSource.h>
27 #include <vtkDoubleArray.h>
28 #include <vtkFeatureEdges.h>
29 #include <vtkFloatArray.h>
30 #include <vtkImageData.h>
31 #include <vtkMassProperties.h>
32 #include <vtkPlaneSource.h>
33 #include <vtkPointData.h>
34 #include <vtkShortArray.h>
35 #include <vtkSphereSource.h>
36 #include <vtkTransform.h>
37 #include <vtkTransformFilter.h>
38 #include <vtkTriangleFilter.h>
39 #include <vtkUnsignedCharArray.h>
40 #include <vtkUnsignedIntArray.h>
41 #include <vtkUnsignedLongArray.h>
42 #include <vtkUnsignedLongLongArray.h>
43 #include <vtkUnsignedShortArray.h>
44 #include <vtkUnstructuredGrid.h>
45 
46 static vtkSmartPointer<vtkDataArray>
47 makeVtkDataArray(unsigned char type)
48 {
49  vtkSmartPointer<vtkDataArray> arr = nullptr;
50  switch (type)
51  {
52  case VTK_CHAR:
53  arr = vtkSmartPointer<vtkCharArray>::New();
54  break;
55  case VTK_UNSIGNED_CHAR:
56  arr = vtkSmartPointer<vtkUnsignedCharArray>::New();
57  break;
58  case VTK_SHORT:
59  arr = vtkSmartPointer<vtkShortArray>::New();
60  break;
61  case VTK_UNSIGNED_SHORT:
62  arr = vtkSmartPointer<vtkUnsignedShortArray>::New();
63  break;
64  case VTK_INT:
65  arr = vtkSmartPointer<vtkIntArray>::New();
66  break;
67  case VTK_UNSIGNED_INT:
68  arr = vtkSmartPointer<vtkUnsignedIntArray>::New();
69  break;
70  case VTK_FLOAT:
71  arr = vtkSmartPointer<vtkFloatArray>::New();
72  break;
73  case VTK_DOUBLE:
74  arr = vtkSmartPointer<vtkDoubleArray>::New();
75  break;
76  case VTK_LONG_LONG:
77  arr = vtkSmartPointer<vtkLongLongArray>::New();
78  break;
79  case VTK_UNSIGNED_LONG:
80  arr = vtkSmartPointer<vtkUnsignedLongArray>::New();
81  break;
82  case VTK_UNSIGNED_LONG_LONG:
83  arr = vtkSmartPointer<vtkUnsignedLongLongArray>::New();
84  default:
85  LOG(WARNING) << "Unknown scalar type";
86  break;
87  }
88  ;
89  return arr;
90 }
91 
92 namespace imstk
93 {
94 vtkSmartPointer<vtkDataArray>
95 GeometryUtils::coupleVtkDataArray(std::shared_ptr<AbstractDataArray> imstkArray)
96 {
97  CHECK(imstkArray != nullptr) << "AbstractDataArray provided is not valid!";
98  CHECK(imstkArray->getVoidPointer() != nullptr) << "AbstractDataArray data provided is not valid!";
99 
100  // Create corresponding data array
101  vtkSmartPointer<vtkDataArray> arr = makeVtkDataArray(imstkToVtkScalarType[imstkArray->getScalarType()]);
102  arr->SetNumberOfComponents(imstkArray->getNumberOfComponents());
103  arr->SetVoidArray(imstkArray->getVoidPointer(), imstkArray->size(), 1);
104 
105  return arr;
106 }
107 
108 vtkSmartPointer<vtkImageData>
109 GeometryUtils::coupleVtkImageData(std::shared_ptr<ImageData> imageData)
110 {
111  CHECK(imageData != nullptr) << "ImageData provided is not valid!";
112 
113  // VTK puts center of min voxel at origin of world space, we put min of bot voxel at origin
114  std::shared_ptr<AbstractDataArray> arr = imageData->getScalars();
115  vtkSmartPointer<vtkDataArray> vtkArr = coupleVtkDataArray(arr);
116 
117  vtkSmartPointer<vtkImageData> imageDataVtk = vtkSmartPointer<vtkImageData>::New();
118  imageDataVtk->SetDimensions(imageData->getDimensions().data());
119  imageDataVtk->SetSpacing(imageData->getSpacing().data());
120  const Vec3d vtkOrigin = imageData->getOrigin() + imageData->getSpacing() * 0.5;
121  imageDataVtk->SetOrigin(vtkOrigin.data());
122  imageDataVtk->SetNumberOfScalarComponents(imageData->getNumComponents(), imageDataVtk->GetInformation());
123  imageDataVtk->SetScalarType(imstkToVtkScalarType[arr->getScalarType()], imageDataVtk->GetInformation());
124  imageDataVtk->GetPointData()->SetScalars(vtkArr);
125  return imageDataVtk;
126 }
127 
128 vtkSmartPointer<vtkDataArray>
129 GeometryUtils::copyToVtkDataArray(std::shared_ptr<AbstractDataArray> imstkArray)
130 {
131  CHECK(imstkArray != nullptr) << "AbstractDataArray provided is not valid!";
132 
133  // Create resulting data array
134  vtkSmartPointer<vtkDataArray> arr = makeVtkDataArray(imstkToVtkScalarType[imstkArray->getScalarType()]);
135  arr->SetNumberOfComponents(imstkArray->getNumberOfComponents());
136  arr->SetNumberOfTuples(imstkArray->size() / imstkArray->getNumberOfComponents());
137  switch (imstkArray->getScalarType())
138  {
139  TemplateMacro(std::copy_n(static_cast<IMSTK_TT*>(imstkArray->getVoidPointer()), imstkArray->size(), static_cast<IMSTK_TT*>(arr->GetVoidPointer(0))); );
140  default:
141  LOG(WARNING) << "Unknown scalar type";
142  break;
143  }
144 
145  return arr;
146 }
147 
148 std::shared_ptr<AbstractDataArray>
149 GeometryUtils::copyToDataArray(vtkSmartPointer<vtkDataArray> vtkArray)
150 {
151  CHECK(vtkArray != nullptr) << "vtkDataArray provided is not valid!";
152 
153  std::shared_ptr<AbstractDataArray> arr = nullptr;
154 
155  const int numComps = vtkArray->GetNumberOfComponents();
156 
157  // Create and copy the array
158  switch (vtkToImstkScalarType[vtkArray->GetDataType()])
159  {
160  TemplateMacro(
161  // We enumerate a number of different common # of components
162  // There's long reasoning but ultimately this is because we use eigen for DataArrays. limiting
163  if (numComps == 1)
164  {
165  arr = std::make_shared<DataArray<IMSTK_TT>>(vtkArray->GetNumberOfTuples() * vtkArray->GetNumberOfComponents());
166  std::copy_n(static_cast<IMSTK_TT*>(vtkArray->GetVoidPointer(0)), vtkArray->GetNumberOfValues(), static_cast<IMSTK_TT*>(arr->getVoidPointer()));
167  }
168  else if (numComps == 2)
169  {
170  arr = (std::make_shared<VecDataArray<IMSTK_TT, 2>>(vtkArray->GetNumberOfTuples()));
171  std::copy_n(static_cast<IMSTK_TT*>(vtkArray->GetVoidPointer(0)), vtkArray->GetNumberOfValues(), static_cast<IMSTK_TT*>(arr->getVoidPointer()));
172  }
173  else if (numComps == 3)
174  {
175  arr = (std::make_shared<VecDataArray<IMSTK_TT, 3>>(vtkArray->GetNumberOfTuples()));
176  std::copy_n(static_cast<IMSTK_TT*>(vtkArray->GetVoidPointer(0)), vtkArray->GetNumberOfValues(), static_cast<IMSTK_TT*>(arr->getVoidPointer()));
177  }
178  else if (numComps == 4)
179  {
180  arr = (std::make_shared<VecDataArray<IMSTK_TT, 4>>(vtkArray->GetNumberOfTuples()));
181  std::copy_n(static_cast<IMSTK_TT*>(vtkArray->GetVoidPointer(0)), vtkArray->GetNumberOfValues(), static_cast<IMSTK_TT*>(arr->getVoidPointer()));
182  }
183  );
184  default:
185  LOG(WARNING) << "Unknown scalar type";
186  break;
187  }
188  return arr;
189 }
190 
191 std::shared_ptr<ImageData>
192 GeometryUtils::copyToImageData(vtkSmartPointer<vtkImageData> vtkImage)
193 {
194  CHECK(vtkImage != nullptr) << "vtkImageData provided is not valid!";
195 
196  double* spacingPtr = vtkImage->GetSpacing();
197  const Vec3d spacing = Vec3d(spacingPtr[0], spacingPtr[1], spacingPtr[2]);
198  // vtk origin is not bounds and vtk origin is not actual origin
199  double* bounds = vtkImage->GetBounds();
200  // image data origin starts at center of first voxel
201  const Vec3d origin = Vec3d(bounds[0], bounds[2], bounds[4]) - spacing * 0.5;
202 
203  std::unique_ptr<ImageData> imageData = std::make_unique<ImageData>();
204  imageData->setScalars(copyToDataArray(vtkImage->GetPointData()->GetScalars()),
205  vtkImage->GetNumberOfScalarComponents(), vtkImage->GetDimensions());
206  imageData->setSpacing(spacing);
207  imageData->setOrigin(origin);
208  return imageData;
209 }
210 
211 vtkSmartPointer<vtkImageData>
212 GeometryUtils::copyToVtkImageData(std::shared_ptr<ImageData> imageData)
213 {
214  CHECK(imageData != nullptr) << "ImageData provided is not valid!";
215 
216  // Our image data does not use vtk extents
217  const Vec3i& dim = imageData->getDimensions();
218 
219  vtkSmartPointer<vtkImageData> imageDataVtk = vtkSmartPointer<vtkImageData>::New();
220  imageDataVtk->SetSpacing(imageData->getSpacing().data());
221  const Vec3d vtkOrigin = imageData->getOrigin() + imageData->getSpacing() * 0.5;
222  imageDataVtk->SetOrigin(vtkOrigin.data());
223  imageDataVtk->SetExtent(0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1);
224  imageDataVtk->AllocateScalars(imstkToVtkScalarType[imageData->getScalarType()], imageData->getNumComponents());
225  imageDataVtk->GetPointData()->SetScalars(copyToVtkDataArray(imageData->getScalars()));
226  return imageDataVtk;
227 }
228 
229 std::shared_ptr<PointSet>
230 GeometryUtils::copyToPointSet(vtkSmartPointer<vtkPointSet> vtkMesh)
231 {
232  CHECK(vtkMesh != nullptr) << "vtkPolyData provided is not valid!";
233 
234  std::shared_ptr<VecDataArray<double, 3>> vertices = copyToVecDataArray(vtkMesh->GetPoints());
235 
236  auto mesh = std::make_unique<PointSet>();
237  mesh->initialize(vertices);
238 
239  // Point Data
240  std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> dataMap;
241  copyToDataMap(vtkMesh->GetPointData(), dataMap);
242  if (!dataMap.empty())
243  {
244  mesh->setVertexAttributes(dataMap);
245  vtkPointData* pointData = vtkMesh->GetPointData();
246  vtkDataArray* normals = pointData->GetNormals();
247  vtkDataArray* tCoords = pointData->GetTCoords();
248  vtkDataArray* scalars = pointData->GetScalars();
249  vtkDataArray* tangents = pointData->GetTangents();
250  if (normals != nullptr)
251  {
252  mesh->setVertexNormals(std::string(normals->GetName()));
253  }
254  if (tCoords != nullptr)
255  {
256  mesh->setVertexTCoords(std::string(tCoords->GetName()));
257  }
258  if (scalars != nullptr)
259  {
260  mesh->setVertexScalars(std::string(scalars->GetName()));
261  }
262  if (tangents != nullptr)
263  {
264  mesh->setVertexTangents(std::string(tangents->GetName()));
265  }
266  }
267 
268  return mesh;
269 }
270 
271 std::shared_ptr<SurfaceMesh>
272 GeometryUtils::copyToSurfaceMesh(vtkSmartPointer<vtkPolyData> vtkMesh)
273 {
274  CHECK(vtkMesh != nullptr) << "vtkPolyData provided is not valid!";
275 
276  std::shared_ptr<VecDataArray<double, 3>> vertices = copyToVecDataArray(vtkMesh->GetPoints());
277  std::shared_ptr<VecDataArray<int, 3>> cells = copyToVecDataArray<3>(vtkMesh->GetPolys());
278 
279  auto mesh = std::make_unique<SurfaceMesh>();
280  mesh->initialize(vertices, cells);
281 
282  // Point Data
283  std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> vertexDataMap;
284  copyToDataMap(vtkMesh->GetPointData(), vertexDataMap);
285  if (!vertexDataMap.empty())
286  {
287  mesh->setVertexAttributes(vertexDataMap);
288  vtkPointData* pointData = vtkMesh->GetPointData();
289  vtkDataArray* normals = pointData->GetNormals();
290  vtkDataArray* tCoords = pointData->GetTCoords();
291  vtkDataArray* scalars = pointData->GetScalars();
292  vtkDataArray* tangents = pointData->GetTangents();
293  if (normals != nullptr)
294  {
295  mesh->setVertexNormals(std::string(normals->GetName()));
296  }
297  if (tCoords != nullptr)
298  {
299  mesh->setVertexTCoords(std::string(tCoords->GetName()));
300  }
301  if (scalars != nullptr)
302  {
303  mesh->setVertexScalars(std::string(scalars->GetName()));
304  }
305  if (tangents != nullptr)
306  {
307  mesh->setVertexTangents(std::string(tangents->GetName()));
308  }
309  }
310 
311  // Cell Data
312  std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> cellDataMap;
313  copyToDataMap(vtkMesh->GetCellData(), cellDataMap);
314  if (!cellDataMap.empty())
315  {
316  mesh->setCellAttributes(cellDataMap);
317  vtkCellData* cellData = vtkMesh->GetCellData();
318  vtkDataArray* normals = cellData->GetNormals();
319  vtkDataArray* scalars = cellData->GetScalars();
320  vtkDataArray* tangents = cellData->GetTangents();
321  if (normals != nullptr)
322  {
323  mesh->setCellNormals(std::string(normals->GetName()));
324  }
325  if (scalars != nullptr)
326  {
327  mesh->setCellScalars(std::string(scalars->GetName()));
328  }
329  if (tangents != nullptr)
330  {
331  mesh->setCellTangents(std::string(tangents->GetName()));
332  }
333  }
334 
335  // Active Texture
336  if (auto pointData = vtkMesh->GetPointData())
337  {
338  if (auto tcoords = pointData->GetTCoords())
339  {
340  mesh->setVertexTCoords(tcoords->GetName());
341  }
342  }
343 
344  return mesh;
345 }
346 
347 std::shared_ptr<TetrahedralMesh>
349  const Vec3d& center, const Vec3d& size, const Vec3i& dim,
350  const Quatd orientation)
351 {
352  auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(dim[0] * dim[1] * dim[2]);
353  VecDataArray<double, 3>& vertices = *verticesPtr;
354  const Vec3d dx = size.cwiseQuotient((dim - Vec3i(1, 1, 1)).cast<double>());
355  int iter = 0;
356  for (int z = 0; z < dim[2]; z++)
357  {
358  for (int y = 0; y < dim[1]; y++)
359  {
360  for (int x = 0; x < dim[0]; x++, iter++)
361  {
362  vertices[iter] = Vec3i(x, y, z).cast<double>().cwiseProduct(dx) - size * 0.5 + center;
363  }
364  }
365  }
366 
367  // Add connectivity data
368  auto indicesPtr = std::make_shared<VecDataArray<int, 4>>();
369  VecDataArray<int, 4>& indices = *indicesPtr;
370  for (int z = 0; z < dim[2] - 1; z++)
371  {
372  for (int y = 0; y < dim[1] - 1; y++)
373  {
374  for (int x = 0; x < dim[0] - 1; x++)
375  {
376  int cubeIndices[8] =
377  {
378  x + dim[0] * (y + dim[1] * z),
379  (x + 1) + dim[0] * (y + dim[1] * z),
380  (x + 1) + dim[0] * (y + dim[1] * (z + 1)),
381  x + dim[0] * (y + dim[1] * (z + 1)),
382  x + dim[0] * ((y + 1) + dim[1] * z),
383  (x + 1) + dim[0] * ((y + 1) + dim[1] * z),
384  (x + 1) + dim[0] * ((y + 1) + dim[1] * (z + 1)),
385  x + dim[0] * ((y + 1) + dim[1] * (z + 1))
386  };
387 
388  // Alternate the pattern so the edges line up on the sides of each voxel
389  if ((z % 2 ^ x % 2) ^ y % 2)
390  {
391  indices.push_back(Vec4i(cubeIndices[0], cubeIndices[7], cubeIndices[5], cubeIndices[4]));
392  indices.push_back(Vec4i(cubeIndices[3], cubeIndices[7], cubeIndices[2], cubeIndices[0]));
393  indices.push_back(Vec4i(cubeIndices[2], cubeIndices[7], cubeIndices[5], cubeIndices[0]));
394  indices.push_back(Vec4i(cubeIndices[1], cubeIndices[2], cubeIndices[0], cubeIndices[5]));
395  indices.push_back(Vec4i(cubeIndices[2], cubeIndices[6], cubeIndices[7], cubeIndices[5]));
396  }
397  else
398  {
399  indices.push_back(Vec4i(cubeIndices[3], cubeIndices[7], cubeIndices[6], cubeIndices[4]));
400  indices.push_back(Vec4i(cubeIndices[1], cubeIndices[3], cubeIndices[6], cubeIndices[4]));
401  indices.push_back(Vec4i(cubeIndices[3], cubeIndices[6], cubeIndices[2], cubeIndices[1]));
402  indices.push_back(Vec4i(cubeIndices[1], cubeIndices[6], cubeIndices[5], cubeIndices[4]));
403  indices.push_back(Vec4i(cubeIndices[0], cubeIndices[3], cubeIndices[1], cubeIndices[4]));
404  }
405  }
406  }
407  }
408 
409  // Ensure correct windings
410  for (int i = 0; i < indices.size(); i++)
411  {
412  if (tetVolume(vertices[indices[i][0]], vertices[indices[i][1]], vertices[indices[i][2]], vertices[indices[i][3]]) < 0.0)
413  {
414  std::swap(indices[i][0], indices[i][2]);
415  }
416  }
417 
418  auto tetMesh = std::make_shared<TetrahedralMesh>();
419  tetMesh->initialize(verticesPtr, indicesPtr);
420  tetMesh->rotate(orientation, Geometry::TransformType::ApplyToData);
421  return tetMesh;
422 }
423 
424 std::shared_ptr<SurfaceMesh>
426  const Vec3d& center, const Vec2d& size, const Vec2i& dim,
427  const Quatd orientation,
428  const double uvScale)
429 {
430  auto verticesPtr =
431  std::make_shared<VecDataArray<double, 3>>(dim[0] * dim[1]);
432  VecDataArray<double, 3>& vertices = *verticesPtr;
433  const Vec3d size3 = Vec3d(size[0], 0.0, size[1]);
434  const Vec3i dim3 = Vec3i(dim[0], 0, dim[1]);
435  Vec3d dx = size3.cwiseQuotient((dim3 - Vec3i(1, 0, 1)).cast<double>());
436  dx[1] = 0.0;
437  int iter = 0;
438  for (int y = 0; y < dim[1]; y++)
439  {
440  for (int x = 0; x < dim[0]; x++, iter++)
441  {
442  vertices[iter] = Vec3i(x, 0, y).cast<double>().cwiseProduct(dx) + center - size3 * 0.5;
443  }
444  }
445 
446  // Add connectivity data
447  auto indicesPtr = std::make_shared<VecDataArray<int, 3>>();
448  VecDataArray<int, 3>& indices = *indicesPtr;
449  for (int y = 0; y < dim[1] - 1; y++)
450  {
451  for (int x = 0; x < dim[0] - 1; x++)
452  {
453  const int index1 = y * dim[0] + x;
454  const int index2 = index1 + dim[0];
455  const int index3 = index1 + 1;
456  const int index4 = index2 + 1;
457 
458  // Interleave [/][\]
459  if (x % 2 ^ y % 2)
460  {
461  indices.push_back(Vec3i(index1, index2, index3));
462  indices.push_back(Vec3i(index4, index3, index2));
463  }
464  else
465  {
466  indices.push_back(Vec3i(index2, index4, index1));
467  indices.push_back(Vec3i(index4, index3, index1));
468  }
469  }
470  }
471 
472  auto uvCoordsPtr = std::make_shared<VecDataArray<float, 2>>(dim[0] * dim[1]);
473  VecDataArray<float, 2>& uvCoords = *uvCoordsPtr.get();
474  iter = 0;
475  for (int i = 0; i < dim[1]; i++)
476  {
477  for (int j = 0; j < dim[0]; j++, iter++)
478  {
479  uvCoords[iter] = Vec2f(
480  static_cast<float>(i) / dim[1],
481  static_cast<float>(j) / dim[0]) * uvScale;
482  }
483  }
484 
485  auto triMesh = std::make_shared<SurfaceMesh>();
486  triMesh->initialize(verticesPtr, indicesPtr);
487  triMesh->setVertexTCoords("uvs", uvCoordsPtr);
488  triMesh->rotate(orientation, Geometry::TransformType::ApplyToData);
489  return triMesh;
490 }
491 
492 std::shared_ptr<LineMesh>
494  const Vec3d& start, const Vec3d& dir,
495  const double length, const int dim)
496 {
497  auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(dim);
498  const Vec3d dirN = dir.normalized();
499  VecDataArray<double, 3>& vertices = *verticesPtr;
500  for (int i = 0; i < dim; i++)
501  {
502  double t = static_cast<double>(i) / (dim - 1);
503  vertices[i] = start + dirN * t * length;
504  }
505 
506  // Add connectivity data
507  auto indicesPtr = std::make_shared<VecDataArray<int, 2>>();
508  VecDataArray<int, 2>& indices = *indicesPtr;
509  for (int i = 0; i < dim - 1; i++)
510  {
511  indices.push_back(Vec2i(i, i + 1));
512  }
513 
514  // Create the geometry
515  auto lineMesh = std::make_shared<LineMesh>();
516  lineMesh->initialize(verticesPtr, indicesPtr);
517  return lineMesh;
518 }
519 
520 std::shared_ptr<LineMesh>
521 GeometryUtils::copyToLineMesh(vtkSmartPointer<vtkPolyData> vtkMesh)
522 {
523  CHECK(vtkMesh != nullptr) << "vtkPolyData provided is not valid!";
524 
525  std::shared_ptr<VecDataArray<double, 3>> vertices = copyToVecDataArray(vtkMesh->GetPoints());
526  std::shared_ptr<VecDataArray<int, 2>> cells = copyToVecDataArray<2>(vtkMesh->GetPolys());
527 
528  // If polys is empty use lines instead
529  if (cells->size() == 0)
530  {
531  cells = copyToVecDataArray<2>(vtkMesh->GetLines());
532  }
533 
534  auto mesh = std::make_unique<LineMesh>();
535  mesh->initialize(vertices, cells);
536 
537  // Point Data
538  std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> dataMap;
539  copyToDataMap(vtkMesh->GetPointData(), dataMap);
540  if (!dataMap.empty())
541  {
542  mesh->setVertexAttributes(dataMap);
543  vtkPointData* pointData = vtkMesh->GetPointData();
544  vtkDataArray* normals = pointData->GetNormals();
545  vtkDataArray* tCoords = pointData->GetTCoords();
546  vtkDataArray* scalars = pointData->GetScalars();
547  vtkDataArray* tangents = pointData->GetTangents();
548  if (normals != nullptr)
549  {
550  mesh->setVertexNormals(std::string(normals->GetName()));
551  }
552  if (tCoords != nullptr)
553  {
554  mesh->setVertexTCoords(std::string(tCoords->GetName()));
555  }
556  if (scalars != nullptr)
557  {
558  mesh->setVertexScalars(std::string(scalars->GetName()));
559  }
560  if (tangents != nullptr)
561  {
562  mesh->setVertexTangents(std::string(tangents->GetName()));
563  }
564  }
565 
566  // Cell Data
567  std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> cellDataMap;
568  copyToDataMap(vtkMesh->GetCellData(), cellDataMap);
569  if (!cellDataMap.empty())
570  {
571  mesh->setCellAttributes(cellDataMap);
572  vtkCellData* cellData = vtkMesh->GetCellData();
573  vtkDataArray* scalars = cellData->GetScalars();
574  if (scalars != nullptr)
575  {
576  mesh->setCellScalars(std::string(scalars->GetName()));
577  }
578  }
579 
580  return mesh;
581 }
582 
583 std::shared_ptr<AbstractCellMesh>
584 GeometryUtils::copyToCellMesh(vtkSmartPointer<vtkUnstructuredGrid> vtkMesh)
585 {
586  CHECK(vtkMesh != nullptr) << "vtkUnstructuredGrid provided is not valid!";
587 
588  std::shared_ptr<VecDataArray<double, 3>> vertices = copyToVecDataArray(vtkMesh->GetPoints());
589 
590  const int cellType = vtkMesh->GetCellType(vtkMesh->GetNumberOfCells() - 1);
591  std::shared_ptr<AbstractCellMesh> vMesh = nullptr;
592  if (cellType == VTK_TETRA)
593  {
594  std::shared_ptr<VecDataArray<int, 4>> cells = copyToVecDataArray<4>(vtkMesh->GetCells());
595 
596  std::shared_ptr<TetrahedralMesh> mesh = std::make_unique<TetrahedralMesh>();
597  vMesh = mesh;
598  mesh->initialize(vertices, cells);
599  }
600  else if (cellType == VTK_HEXAHEDRON)
601  {
602  std::shared_ptr<VecDataArray<int, 8>> cells = copyToVecDataArray<8>(vtkMesh->GetCells());
603 
604  std::shared_ptr<HexahedralMesh> mesh = std::make_unique<HexahedralMesh>();
605  vMesh = mesh;
606  mesh->initialize(vertices, cells);
607  }
608  else
609  {
610  LOG(FATAL) << "No support for vtkCellType = "
611  << cellType << ".";
612  }
613 
614  // Point Data
615  std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> vertexDataMap;
616  copyToDataMap(vtkMesh->GetPointData(), vertexDataMap);
617  if (!vertexDataMap.empty())
618  {
619  vMesh->setVertexAttributes(vertexDataMap);
620  vtkPointData* pointData = vtkMesh->GetPointData();
621  vtkDataArray* normals = pointData->GetNormals();
622  vtkDataArray* tCoords = pointData->GetTCoords();
623  vtkDataArray* scalars = pointData->GetScalars();
624  vtkDataArray* tangents = pointData->GetTangents();
625  if (normals != nullptr)
626  {
627  vMesh->setVertexNormals(std::string(normals->GetName()));
628  }
629  if (tCoords != nullptr)
630  {
631  vMesh->setVertexTCoords(std::string(tCoords->GetName()));
632  }
633  if (scalars != nullptr)
634  {
635  vMesh->setVertexScalars(std::string(scalars->GetName()));
636  }
637  if (tangents != nullptr)
638  {
639  vMesh->setVertexTangents(std::string(tangents->GetName()));
640  }
641  }
642 
643  // Cell Data
644  /*std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>> cellDataMap;
645  copyToDataMap(vtkMesh->GetCellData(), cellDataMap);
646  if (!cellDataMap.empty())
647  {
648  vMesh->setCellAttributes(cellDataMap);
649  vMesh->setCellNormals(vtkMesh->GetCellData()->GetNormals() == nullptr ? "" : std::string(vtkMesh->GetCellData()->GetNormals()->GetName()));
650  vMesh->setCellScalars(vtkMesh->GetCellData()->GetScalars() == nullptr ? "" : std::string(vtkMesh->GetCellData()->GetScalars()->GetName()));
651  vMesh->setCellTangents(vtkMesh->GetCellData()->GetTangents() == nullptr ? "" : std::string(vtkMesh->GetCellData()->GetTangents()->GetName()));
652  }*/
653 
654  return vMesh;
655 }
656 
657 vtkSmartPointer<vtkPointSet>
658 GeometryUtils::copyToVtkPointSet(std::shared_ptr<PointSet> imstkMesh)
659 {
660  vtkSmartPointer<vtkPoints> points = copyToVtkPoints(imstkMesh->getVertexPositions());
661 
662  auto polydata = vtkSmartPointer<vtkPolyData>::New();
663  polydata->SetPoints(points);
664 
665  copyToVtkDataAttributes(polydata->GetPointData(), imstkMesh->getVertexAttributes());
666  if (imstkMesh->getActiveVertexNormals() != "")
667  {
668  polydata->GetPointData()->SetActiveNormals(imstkMesh->getActiveVertexNormals().c_str());
669  }
670  if (imstkMesh->getActiveVertexScalars() != "")
671  {
672  polydata->GetPointData()->SetActiveScalars(imstkMesh->getActiveVertexScalars().c_str());
673  }
674  if (imstkMesh->getActiveVertexTangents() != "")
675  {
676  polydata->GetPointData()->SetActiveTangents(imstkMesh->getActiveVertexTangents().c_str());
677  }
678  if (imstkMesh->getActiveVertexTCoords() != "")
679  {
680  polydata->GetPointData()->SetActiveTCoords(imstkMesh->getActiveVertexTCoords().c_str());
681  }
682 
683  return polydata;
684 }
685 
686 vtkSmartPointer<vtkPolyData>
687 GeometryUtils::copyToVtkPolyData(std::shared_ptr<LineMesh> imstkMesh)
688 {
689  vtkSmartPointer<vtkPoints> points = copyToVtkPoints(imstkMesh->getVertexPositions());
690 
691  vtkSmartPointer<vtkCellArray> polys = copyToVtkCellArray<2>(imstkMesh->getCells());
692 
693  auto polydata = vtkSmartPointer<vtkPolyData>::New();
694  polydata->SetPoints(points);
695  polydata->SetLines(polys);
696 
697  // Copy vertex attributes
698  copyToVtkDataAttributes(polydata->GetPointData(), imstkMesh->getVertexAttributes());
699  if (imstkMesh->getActiveVertexNormals() != "")
700  {
701  polydata->GetPointData()->SetActiveNormals(imstkMesh->getActiveVertexNormals().c_str());
702  }
703  if (imstkMesh->getActiveVertexScalars() != "")
704  {
705  polydata->GetPointData()->SetActiveScalars(imstkMesh->getActiveVertexScalars().c_str());
706  }
707  if (imstkMesh->getActiveVertexTangents() != "")
708  {
709  polydata->GetPointData()->SetActiveTangents(imstkMesh->getActiveVertexTangents().c_str());
710  }
711  if (imstkMesh->getActiveVertexTCoords() != "")
712  {
713  polydata->GetPointData()->SetActiveTCoords(imstkMesh->getActiveVertexTCoords().c_str());
714  }
715 
716  // Copy cell attributes
717  copyToVtkDataAttributes(polydata->GetCellData(), imstkMesh->getCellAttributes());
718  if (imstkMesh->getActiveCellScalars() != "")
719  {
720  polydata->GetCellData()->SetActiveScalars(imstkMesh->getActiveCellScalars().c_str());
721  }
722 
723  return polydata;
724 }
725 
726 vtkSmartPointer<vtkPolyData>
727 GeometryUtils::copyToVtkPolyData(std::shared_ptr<SurfaceMesh> imstkMesh)
728 {
729  vtkSmartPointer<vtkPoints> points = copyToVtkPoints(imstkMesh->getVertexPositions());
730  vtkSmartPointer<vtkCellArray> polys = copyToVtkCellArray<3>(imstkMesh->getCells());
731 
732  auto polydata = vtkSmartPointer<vtkPolyData>::New();;
733  polydata->SetPoints(points);
734  polydata->SetPolys(polys);
735 
736  // Copy vertex attributes
737  copyToVtkDataAttributes(polydata->GetPointData(), imstkMesh->getVertexAttributes());
738  if (imstkMesh->getActiveVertexNormals() != "")
739  {
740  polydata->GetPointData()->SetActiveNormals(imstkMesh->getActiveVertexNormals().c_str());
741  }
742  if (imstkMesh->getActiveVertexScalars() != "")
743  {
744  polydata->GetPointData()->SetActiveScalars(imstkMesh->getActiveVertexScalars().c_str());
745  }
746  if (imstkMesh->getActiveVertexTangents() != "")
747  {
748  polydata->GetPointData()->SetActiveTangents(imstkMesh->getActiveVertexTangents().c_str());
749  }
750  if (imstkMesh->getActiveVertexTCoords() != "")
751  {
752  polydata->GetPointData()->SetActiveTCoords(imstkMesh->getActiveVertexTCoords().c_str());
753  }
754 
755  // Copy cell attributes
756  copyToVtkDataAttributes(polydata->GetCellData(), imstkMesh->getCellAttributes());
757  if (imstkMesh->getActiveCellNormals() != "")
758  {
759  polydata->GetCellData()->SetActiveNormals(imstkMesh->getActiveCellNormals().c_str());
760  }
761  if (imstkMesh->getActiveCellScalars() != "")
762  {
763  polydata->GetCellData()->SetActiveScalars(imstkMesh->getActiveCellScalars().c_str());
764  }
765  if (imstkMesh->getActiveCellTangents() != "")
766  {
767  polydata->GetCellData()->SetActiveTangents(imstkMesh->getActiveCellTangents().c_str());
768  }
769 
770  return polydata;
771 }
772 
773 vtkSmartPointer<vtkUnstructuredGrid>
774 GeometryUtils::copyToVtkUnstructuredGrid(std::shared_ptr<TetrahedralMesh> imstkMesh)
775 {
776  vtkSmartPointer<vtkPoints> points = copyToVtkPoints(imstkMesh->getVertexPositions());
777  vtkSmartPointer<vtkCellArray> tetras = copyToVtkCellArray<4>(imstkMesh->getCells());
778 
779  auto ug = vtkSmartPointer<vtkUnstructuredGrid>::New();
780  ug->SetPoints(points);
781  ug->SetCells(VTK_TETRA, tetras);
782 
783  copyToVtkDataAttributes(ug->GetPointData(), imstkMesh->getVertexAttributes());
784  if (imstkMesh->getActiveVertexNormals() != "")
785  {
786  ug->GetPointData()->SetActiveNormals(imstkMesh->getActiveVertexNormals().c_str());
787  }
788  if (imstkMesh->getActiveVertexScalars() != "")
789  {
790  ug->GetPointData()->SetActiveScalars(imstkMesh->getActiveVertexScalars().c_str());
791  }
792  if (imstkMesh->getActiveVertexTangents() != "")
793  {
794  ug->GetPointData()->SetActiveTangents(imstkMesh->getActiveVertexTangents().c_str());
795  }
796  if (imstkMesh->getActiveVertexTCoords() != "")
797  {
798  ug->GetPointData()->SetActiveTCoords(imstkMesh->getActiveVertexTCoords().c_str());
799  }
800 
801  // \todo: TetrahedralMeshes don't have cell attributes yet
802 
803  return ug;
804 }
805 
806 vtkSmartPointer<vtkUnstructuredGrid>
807 GeometryUtils::copyToVtkUnstructuredGrid(std::shared_ptr<HexahedralMesh> imstkMesh)
808 {
809  vtkSmartPointer<vtkPoints> points = copyToVtkPoints(imstkMesh->getVertexPositions());
810  vtkSmartPointer<vtkCellArray> bricks = copyToVtkCellArray<8>(imstkMesh->getCells());
811 
812  auto ug = vtkSmartPointer<vtkUnstructuredGrid>::New();
813  ug->SetPoints(points);
814  ug->SetCells(VTK_HEXAHEDRON, bricks);
815 
816  copyToVtkDataAttributes(ug->GetPointData(), imstkMesh->getVertexAttributes());
817  if (imstkMesh->getActiveVertexNormals() != "")
818  {
819  ug->GetPointData()->SetActiveNormals(imstkMesh->getActiveVertexNormals().c_str());
820  }
821  if (imstkMesh->getActiveVertexScalars() != "")
822  {
823  ug->GetPointData()->SetActiveScalars(imstkMesh->getActiveVertexScalars().c_str());
824  }
825  if (imstkMesh->getActiveVertexTangents() != "")
826  {
827  ug->GetPointData()->SetActiveTangents(imstkMesh->getActiveVertexTangents().c_str());
828  }
829  if (imstkMesh->getActiveVertexTCoords() != "")
830  {
831  ug->GetPointData()->SetActiveTCoords(imstkMesh->getActiveVertexTCoords().c_str());
832  }
833 
834  // \todo: HexahedralMesh's don't have cell attributes yet
835 
836  return ug;
837 }
838 
839 std::shared_ptr<VecDataArray<double, 3>>
840 GeometryUtils::copyToVecDataArray(vtkSmartPointer<vtkPoints> points)
841 {
842  CHECK(points != nullptr) << "No valid point data provided!";
843 
844  std::shared_ptr<VecDataArray<double, 3>> vertices = std::make_shared<VecDataArray<double, 3>>(points->GetNumberOfPoints());
845  VecDataArray<double, 3>& vertexData = *vertices;
846  for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i)
847  {
848  double pos[3];
849  points->GetPoint(i, pos);
850  vertexData[i] = Vec3d(pos[0], pos[1], pos[2]);
851  }
852  return vertices;
853 }
854 
855 vtkSmartPointer<vtkPoints>
857 {
858  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
859  const VecDataArray<double, 3>& vertexData = *vertices;
860  points->SetNumberOfPoints(vertexData.size());
861  for (int i = 0; i < vertexData.size(); i++)
862  {
863  points->SetPoint(i, vertexData[i][0], vertexData[i][1], vertexData[i][2]);
864  }
865  return points;
866 }
867 
868 template<size_t dim>
869 vtkSmartPointer<vtkCellArray>
871 {
872  vtkSmartPointer<vtkCellArray> vtkCells = vtkSmartPointer<vtkCellArray>::New();
873 
874  VecDataArray<int, dim>& cells = *cellsPtr;
875  for (int i = 0; i < cells.size(); i++)
876  {
877  vtkCells->InsertNextCell(dim);
878  for (size_t k = 0; k < dim; k++)
879  {
880  vtkCells->InsertCellPoint(cells[i][k]);
881  }
882  }
883  return vtkCells;
884 }
885 
886 template<size_t dim>
887 std::shared_ptr<VecDataArray<int, dim>>
888 GeometryUtils::copyToVecDataArray(vtkCellArray* vtkCells)
889 {
890  using ValueType = typename VecDataArray<int, dim>::ValueType;
891 
892  CHECK(vtkCells != nullptr) << "No cells found!";
893 
894  std::shared_ptr<VecDataArray<int, dim>> indices = std::make_shared<VecDataArray<int, dim>>();
895  indices->reserve(vtkCells->GetNumberOfCells());
896 
897  vtkNew<vtkIdList> vtkCell;
898  vtkCells->InitTraversal();
899  while (vtkCells->GetNextCell(vtkCell))
900  {
901  if (vtkCell->GetNumberOfIds() != dim)
902  {
903  continue;
904  }
905  ValueType cell;
906  for (size_t i = 0; i < dim; i++)
907  {
908  cell[i] = vtkCell->GetId(i);
909  }
910  indices->push_back(cell);
911  }
912  indices->squeeze();
913  return indices;
914 }
915 
916 void
917 GeometryUtils::copyToDataMap(vtkDataSetAttributes* dataAttributes, std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>>& dataMap)
918 {
919  CHECK(dataAttributes != nullptr) << "No point data provided!";
920 
921  for (int i = 0; i < dataAttributes->GetNumberOfArrays(); ++i)
922  {
923  vtkDataArray* array = dataAttributes->GetArray(i);
924  std::string name = "unnamed";
925  if (array->GetName() == NULL)
926  {
927  int iter = 0;
928  // If name already exists, iterate key
929  while (dataMap.count(name + std::to_string(iter)) != 0)
930  {
931  iter++;
932  }
933  name = name + std::to_string(iter);
934  array->SetName(name.c_str());
935  }
936  else
937  {
938  name = std::string(array->GetName());
939  }
940  dataMap[name] = copyToDataArray(array);
941  }
942 }
943 
944 void
945 GeometryUtils::copyToVtkDataAttributes(vtkDataSetAttributes* pointData, const std::unordered_map<std::string, std::shared_ptr<AbstractDataArray>>& dataMap)
946 {
947  CHECK(pointData != nullptr) << "No point data provided!";
948 
949  for (auto i : dataMap)
950  {
951  vtkSmartPointer<vtkDataArray> arr = copyToVtkDataArray(i.second);
952  if (i.first != "")
953  {
954  arr->SetName(i.first.c_str());
955  }
956  pointData->AddArray(arr);
957  }
958 }
959 
960 std::shared_ptr<SurfaceMesh>
961 GeometryUtils::toUVSphereSurfaceMesh(std::shared_ptr<Sphere> sphere,
962  const unsigned int phiDivisions, const unsigned int thetaDivisions)
963 {
964  vtkNew<vtkSphereSource> sphereSource;
965  sphereSource->SetCenter(sphere->getPosition(Geometry::DataType::PreTransform).data());
966  sphereSource->SetRadius(sphere->getRadius());
967  sphereSource->SetPhiResolution(phiDivisions);
968  sphereSource->SetThetaResolution(thetaDivisions);
969  sphereSource->Update();
970 
971  vtkNew<vtkTransform> transform;
972  transform->SetMatrix(mat4dTranslate(sphere->getPosition()).data());
973 
974  vtkNew<vtkTransformFilter> transformFilter;
975  transformFilter->SetInputData(sphereSource->GetOutput());
976  transformFilter->SetTransform(transform);
977  transformFilter->Update();
978  vtkNew<vtkTriangleFilter> triangulate;
979  triangulate->SetInputData(transformFilter->GetOutput());
980  triangulate->Update();
981  vtkNew<vtkCleanPolyData> cleanData;
982  cleanData->SetInputData(triangulate->GetOutput());
983  cleanData->Update();
984 
985  return copyToSurfaceMesh(cleanData->GetOutput());
986 }
987 
988 std::shared_ptr<SurfaceMesh>
989 GeometryUtils::toSurfaceMesh(std::shared_ptr<AnalyticalGeometry> geom)
990 {
991  vtkSmartPointer<vtkPointSet> results = nullptr;
992  if (auto plane = std::dynamic_pointer_cast<Plane>(geom))
993  {
994  const Quatd r = Quatd(plane->getRotation());
995  const Vec3d i = r._transformVector(Vec3d(1.0, 0.0, 0.0));
996  const Vec3d j = r._transformVector(Vec3d(0.0, 0.0, 1.0));
997 
998  //Vec3d p1 = plane->getPosition() + plane->getWidth() * (i + j);
999  Vec3d p2 = plane->getPosition() + plane->getWidth() * (i - j);
1000  Vec3d p3 = plane->getPosition() + plane->getWidth() * (-i + j);
1001  Vec3d p4 = plane->getPosition() + plane->getWidth() * (-i - j);
1002 
1003  vtkNew<vtkPlaneSource> source;
1004  source->SetOrigin(p4.data());
1005  source->SetPoint1(p3.data());
1006  source->SetPoint2(p2.data());
1007  source->Update();
1008  results = source->GetOutput();
1009  }
1010  else if (auto orientedBox = std::dynamic_pointer_cast<OrientedBox>(geom))
1011  {
1012  vtkNew<vtkCubeSource> source;
1013  Vec3d extents = orientedBox->getExtents(Geometry::DataType::PreTransform);
1014  source->SetCenter(0.0, 0.0, 0.0);
1015  source->SetXLength(extents[0] * 2.0);
1016  source->SetYLength(extents[1] * 2.0);
1017  source->SetZLength(extents[2] * 2.0);
1018  source->Update();
1019 
1020  AffineTransform3d T = AffineTransform3d::Identity();
1021  T.translate(orientedBox->getPosition(Geometry::DataType::PostTransform));
1022  T.rotate(orientedBox->getOrientation(Geometry::DataType::PostTransform));
1023  T.scale(orientedBox->getScaling());
1024  T.matrix().transposeInPlace();
1025 
1026  vtkNew<vtkTransform> transformVtk;
1027  transformVtk->SetMatrix(T.data());
1028 
1029  vtkNew<vtkTransformFilter> transformFilter;
1030  transformFilter->SetInputData(source->GetOutput());
1031  transformFilter->SetTransform(transformVtk);
1032  transformFilter->Update();
1033  results = transformFilter->GetOutput();
1034  }
1035  else if (auto cylinder = std::dynamic_pointer_cast<Cylinder>(geom))
1036  {
1037  vtkNew<vtkCylinderSource> source;
1038  source->SetCenter(0.0, 0.0, 0.0);
1039  source->SetRadius(cylinder->getRadius());
1040  source->SetHeight(cylinder->getLength());
1041  source->SetResolution(20);
1042  source->Update();
1043 
1044  AffineTransform3d T = AffineTransform3d::Identity();
1045  T.translate(cylinder->getPosition(Geometry::DataType::PostTransform));
1046  T.rotate(cylinder->getOrientation(Geometry::DataType::PostTransform));
1047  T.scale(1.0);
1048  T.matrix().transposeInPlace();
1049 
1050  vtkNew<vtkTransform> transformVtk;
1051  transformVtk->SetMatrix(T.data());
1052 
1053  vtkNew<vtkTransformFilter> transformFilter;
1054  transformFilter->SetInputData(source->GetOutput());
1055  transformFilter->SetTransform(transformVtk);
1056  transformFilter->Update();
1057  results = transformFilter->GetOutput();
1058  }
1059  else if (auto capsule = std::dynamic_pointer_cast<Capsule>(geom))
1060  {
1061  vtkNew<vtkCapsuleSource> source;
1062  source->SetCenter(0.0, 0.0, 0.0);
1063  source->SetRadius(capsule->getRadius());
1064  source->SetCylinderLength(capsule->getLength());
1065  source->SetLatLongTessellation(20);
1066  source->SetPhiResolution(20);
1067  source->SetThetaResolution(20);
1068  source->Update();
1069 
1070  AffineTransform3d T = AffineTransform3d::Identity();
1071  T.translate(capsule->getPosition(Geometry::DataType::PostTransform));
1072  T.rotate(capsule->getOrientation(Geometry::DataType::PostTransform));
1073  T.scale(1.0);
1074  T.matrix().transposeInPlace();
1075 
1076  vtkNew<vtkTransform> transformVtk;
1077  transformVtk->SetMatrix(T.data());
1078 
1079  vtkNew<vtkTransformFilter> transformFilter;
1080  transformFilter->SetInputData(source->GetOutput());
1081  transformFilter->SetTransform(transformVtk);
1082  transformFilter->Update();
1083  results = transformFilter->GetOutput();
1084  }
1085  else
1086  {
1087  LOG(WARNING) << "Failed to produce SurfaceMesh from provided AnalyticalGeometry";
1088  return nullptr;
1089  }
1090 
1091  // Triangulate, mesh could have quads or other primitives
1092  vtkNew<vtkTriangleFilter> triangulate;
1093  triangulate->SetInputData(results);
1094  triangulate->Update();
1095  vtkNew<vtkCleanPolyData> cleanData;
1096  cleanData->SetInputConnection(triangulate->GetOutputPort());
1097  cleanData->Update();
1098  return copyToSurfaceMesh(cleanData->GetOutput());
1099 }
1100 
1101 int
1102 GeometryUtils::getOpenEdgeCount(std::shared_ptr<SurfaceMesh> surfMesh)
1103 {
1104  vtkNew<vtkFeatureEdges> checkClosed;
1105  checkClosed->SetInputData(GeometryUtils::copyToVtkPolyData(surfMesh));
1106  checkClosed->FeatureEdgesOff();
1107  checkClosed->BoundaryEdgesOn();
1108  checkClosed->NonManifoldEdgesOn();
1109  checkClosed->Update();
1110  return checkClosed->GetOutput()->GetNumberOfCells();
1111 }
1112 
1113 double
1114 GeometryUtils::getVolume(std::shared_ptr<SurfaceMesh> surfMesh)
1115 {
1116  vtkNew<vtkMassProperties> massProps;
1117  massProps->SetInputData(copyToVtkPolyData(surfMesh));
1118  massProps->Update();
1119  return massProps->GetVolume();
1120 }
1121 
1122 namespace // anonymous namespace
1123 {
1131 template<typename ElemConn>
1132 static void
1133 buildVertexToVertexConnectivity(const std::vector<ElemConn>& conn,
1134  const size_t numVerts,
1135  std::vector<std::unordered_set<size_t>>& vertToVert)
1136 {
1137  // constexpr size_t numVertPerElem = ElemConn::size();
1138  std::vector<size_t> vertToElemPtr(numVerts + 1, 0);
1139  std::vector<size_t> vertToElem;
1140 
1141  // find the number of adjacent elements for each vertex
1142  for (const auto& vertices : conn)
1143  {
1144  for (auto vid : vertices)
1145  {
1146  vertToElemPtr[vid + 1] += 1;
1147  }
1148  }
1149 
1150  // accumulate pointer
1151  for (size_t i = 0; i < numVerts; ++i)
1152  {
1153  vertToElemPtr[i + 1] += vertToElemPtr[i];
1154  }
1155 
1156  // track the front position for each vertex in \p vertToElem
1157  auto pos = vertToElemPtr;
1158  // the total number of element neighbors of all vertices, ie, size of \p vertToElem
1159  const size_t totNum = vertToElemPtr.back();
1160 
1161  vertToElem.resize(totNum);
1162 
1163  for (size_t eid = 0; eid < conn.size(); ++eid)
1164  {
1165  for (auto vid : conn[eid])
1166  {
1167  vertToElem[pos[vid]] = eid;
1168  ++pos[vid];
1169  }
1170  }
1171 
1172  // connectivity of vertex-to-vertex
1173  vertToVert.resize(numVerts);
1174  auto getVertexNbrs = [&vertToElem, &vertToElemPtr, &conn, &vertToVert](const size_t i)
1175  {
1176  const auto ptr0 = vertToElemPtr[i];
1177  const auto ptr1 = vertToElemPtr[i + 1];
1178 
1179  for (auto ptr = ptr0; ptr < ptr1; ++ptr)
1180  {
1181  for (auto vid : conn[vertToElem[ptr]])
1182  {
1183  // vertex-i itself is also included.
1184  vertToVert[i].insert(vid);
1185  }
1186  }
1187  };
1188 
1189  // for (size_t i = 0; i < numVerts; ++i)
1190  // {
1191  // getVertexNbrs(i);
1192  // }
1193  ParallelUtils::parallelFor(numVerts, getVertexNbrs);
1194 }
1195 
1204 template<typename NeighborContainer>
1205 static std::vector<size_t>
1206 RCM(const std::vector<NeighborContainer>& neighbors)
1207 {
1208  const size_t invalid = std::numeric_limits<size_t>::max();
1209 
1210  // is greater in terms of degrees
1211  auto isGreater = [&neighbors](const size_t i, const size_t j) {
1212  return neighbors[i].size() > neighbors[j].size() ? true : false;
1213  };
1214 
1215  const size_t numVerts = neighbors.size();
1216 
1217  std::vector<size_t> P(numVerts);
1218  for (size_t i = 0; i < numVerts; ++i)
1219  {
1220  P[i] = i;
1221  }
1222  std::sort(P.begin(), P.end(), isGreater);
1223 
1224  // \todo an alternative is to use std::set for P
1225  // std::set<size_t, isGreater> P;
1226  // for (size_t i=0; i<numVerts; ++i)
1227  // {
1228  // P.insert(i);
1229  // }
1230 
1231  std::vector<size_t> R(numVerts, invalid); // permutation
1232  std::queue<size_t> Q; // queue
1233  std::vector<bool> isInP(numVerts, true); // if a vertex is still in P
1234  size_t pos = 0; // track how many vertices are put into R
1235 
1240  auto moveVertexIntoRAndItsNeighborsIntoQ = [&neighbors, &isInP, &pos, &R, &Q](const size_t vid)
1241  {
1242  R[pos] = vid;
1243  ++pos;
1244  isInP[vid] = false;
1245 
1246  // Put the unordered neighbors into Q, in ascending order.
1247  // first find unordered neighbors
1248  std::set<size_t> unorderedNbrs;
1249  for (auto nbr : neighbors[vid])
1250  {
1251  if (isInP[nbr])
1252  {
1253  unorderedNbrs.insert(nbr);
1254  }
1255  }
1256 
1257  for (auto nbr : unorderedNbrs)
1258  {
1259  Q.push(nbr);
1260  isInP[nbr] = false;
1261  }
1262  };
1263 
1264  size_t pCur = 0;
1265 
1266  // main loop
1267  while (true)
1268  {
1269  size_t parent = invalid;
1270  for (size_t vid = pCur; vid < isInP.size(); ++vid)
1271  {
1272  if (isInP[vid])
1273  {
1274  isInP[vid] = false;
1275  pCur = vid;
1276  parent = vid;
1277  break;
1278  }
1279  }
1280  if (parent == invalid)
1281  {
1282  break;
1283  }
1284 
1285  moveVertexIntoRAndItsNeighborsIntoQ(parent);
1286 
1287  while (!Q.empty())
1288  {
1289  parent = Q.front();
1290  Q.pop();
1291  moveVertexIntoRAndItsNeighborsIntoQ(parent);
1292  }
1293 
1294  // here we have empty Q
1295  }
1296 
1297  CHECK(pos == numVerts) << "RCM ordering: we should never get here";
1298 
1299  std::reverse(R.begin(), R.end());
1300  return R;
1301 }
1302 
1305 //
1311 template<typename ElemConn>
1312 static std::vector<size_t>
1313 RCM(const std::vector<ElemConn>& conn, const size_t numVerts)
1314 {
1315  // connectivity of vertex-to-vertex
1316  std::vector<std::unordered_set<size_t>> vertToVert;
1317  buildVertexToVertexConnectivity(conn, numVerts, vertToVert);
1318  return RCM(vertToVert);
1319 }
1320 
1328 void
1329 markPointsInsideAndOut(std::vector<bool>& isInside,
1330  SurfaceMesh& surfaceMesh,
1331  const StdVectorOfVec3d& coords)
1332 {
1333  isInside.clear();
1334  isInside.resize(coords.size(), false);
1335 
1336  Vec3d aabbMin, aabbMax;
1337  surfaceMesh.computeBoundingBox(aabbMin, aabbMax, 1.0);
1338  /*
1339  auto genRandomDirection = [](Vec3d& direction)
1340  {
1341  for (int i = 0; i < 3; ++i)
1342  {
1343  direction[i] = rand();
1344  }
1345  double mag = direction.norm();
1346 
1347  for (int i = 0; i < 3; ++i)
1348  {
1349  direction[i] /= mag;
1350  }
1351  return;
1352  };
1353  */
1354 
1355  auto triangleRayIntersection = [](const Vec3d& xyz,
1356  const Vec3d& triVert0,
1357  const Vec3d& triVert1,
1358  const Vec3d& triVert2,
1359  const Vec3d& direction)
1360  {
1361  // const double eps = 1e-15;
1362  constexpr const double eps = std::numeric_limits<double>::epsilon();
1363  Vec3d edge0 = triVert1 - triVert0;
1364  Vec3d edge1 = triVert2 - triVert0;
1365  Vec3d pvec = direction.cross(edge1);
1366  double det = edge0.dot(pvec);
1367 
1368  if (det > -eps && det < eps)
1369  {
1370  return false;
1371  }
1372  double inv_det = 1.0 / det;
1373  Vec3d tvec = xyz - triVert0;
1374  double u = tvec.dot(pvec) * inv_det;
1375  if (u < 0.0 || u > 1.0)
1376  {
1377  return false;
1378  }
1379  Vec3d qvec = tvec.cross(edge0);
1380  double v = direction.dot(qvec) * inv_det;
1381  if (v < 0.0 || u + v > 1.0)
1382  {
1383  return false;
1384  }
1385 
1386  double t = edge1.dot(qvec) * inv_det;
1387  if (t > 0.0)
1388  {
1389  return true;
1390  }
1391  else
1392  {
1393  return false;
1394  }
1395  };
1396 
1397  std::vector<Vec3d> bBoxMin;
1398  std::vector<Vec3d> bBoxMax;
1399 
1400  bBoxMin.resize(surfaceMesh.getNumCells());
1401  bBoxMax.resize(surfaceMesh.getNumCells());
1402 
1403  const VecDataArray<int, 3>& indices = *surfaceMesh.getCells();
1404  for (int idx = 0; idx < surfaceMesh.getNumCells(); ++idx)
1405  {
1406  const auto& verts = indices[idx];
1407  const auto& xyz0 = surfaceMesh.getVertexPosition(verts[0]);
1408  const auto& xyz1 = surfaceMesh.getVertexPosition(verts[1]);
1409  const auto& xyz2 = surfaceMesh.getVertexPosition(verts[2]);
1410  // const auto& xyz0 = m_vertexPositions[verts[0]];
1411  // const auto& xyz1 = m_vertexPositions[verts[1]];
1412  // const auto& xyz2 = m_vertexPositions[verts[2]];
1413 
1414  bBoxMin[idx][0] = xyz0[0];
1415  bBoxMin[idx][1] = xyz0[1];
1416  bBoxMin[idx][2] = xyz0[2];
1417  bBoxMax[idx][0] = xyz0[0];
1418  bBoxMax[idx][1] = xyz0[1];
1419  bBoxMax[idx][2] = xyz0[2];
1420 
1421  bBoxMin[idx][0] = std::min(bBoxMin[idx][0], xyz1[0]);
1422  bBoxMin[idx][1] = std::min(bBoxMin[idx][1], xyz1[1]);
1423  bBoxMin[idx][2] = std::min(bBoxMin[idx][2], xyz1[2]);
1424  bBoxMin[idx][0] = std::min(bBoxMin[idx][0], xyz2[0]);
1425  bBoxMin[idx][1] = std::min(bBoxMin[idx][1], xyz2[1]);
1426  bBoxMin[idx][2] = std::min(bBoxMin[idx][2], xyz2[2]);
1427 
1428  bBoxMax[idx][0] = std::max(bBoxMax[idx][0], xyz1[0]);
1429  bBoxMax[idx][1] = std::max(bBoxMax[idx][1], xyz1[1]);
1430  bBoxMax[idx][2] = std::max(bBoxMax[idx][2], xyz1[2]);
1431  bBoxMax[idx][0] = std::max(bBoxMax[idx][0], xyz2[0]);
1432  bBoxMax[idx][1] = std::max(bBoxMax[idx][1], xyz2[1]);
1433  bBoxMax[idx][2] = std::max(bBoxMax[idx][2], xyz2[2]);
1434  }
1435 
1436  auto rayTracingFunc = [&](const size_t i)
1437  {
1438  bool outBox = coords[i][0] < aabbMin[0] || coords[i][0] > aabbMax[0]
1439  || coords[i][1] < aabbMin[1] || coords[i][1] > aabbMax[1]
1440  || coords[i][2] < aabbMin[2] || coords[i][2] > aabbMax[2];
1441  if (outBox)
1442  {
1443  return;
1444  }
1445 
1447  const Vec3d direction = { 0.0, 0.0, 1.0 };
1448  // Vec3d direction;
1449  // genRandomDirection(direction);
1450  // std::cout << direction << std::endl;
1451  int numIntersections = 0;
1452  const auto& xyz = *surfaceMesh.getVertexPositions();
1453 
1454  for (int j = 0; j < surfaceMesh.getNumCells(); ++j)
1455  {
1456  const Vec3i& verts = indices[j];
1457 
1458  // consider directed ray
1459  if (coords[i][2] > bBoxMax[j][2])
1460  {
1461  continue;
1462  }
1463 
1464  if (coords[i][0] > bBoxMax[j][0])
1465  {
1466  continue;
1467  }
1468  if (coords[i][0] < bBoxMin[j][0])
1469  {
1470  continue;
1471  }
1472  if (coords[i][1] > bBoxMax[j][1])
1473  {
1474  continue;
1475  }
1476  if (coords[i][1] < bBoxMin[j][1])
1477  {
1478  continue;
1479  }
1480 
1481  auto intersected = triangleRayIntersection(coords[i],
1482  xyz[verts[0]],
1483  xyz[verts[1]],
1484  xyz[verts[2]],
1485  direction);
1486  if (intersected)
1487  {
1488  ++numIntersections;
1489  }
1490  }
1491 
1492  if (numIntersections % 2 == 1)
1493  {
1494  isInside[i] = true;
1495  }
1496 
1497  return;
1498  };
1499 
1500  // for (size_t i = 0; i < coords.size(); ++i)
1501  // {
1502  // rayTracingFunc(i);
1503  // }
1504 
1505  ParallelUtils::parallelFor(coords.size(), rayTracingFunc);
1506 }
1507 
1518 void
1519 markPointsInsideAndOut(std::vector<bool>& isInside,
1520  SurfaceMesh& surfaceMesh,
1521  const VecDataArray<double, 3>& coords,
1522  const size_t nx,
1523  const size_t ny,
1524  const size_t nz)
1525 {
1526  isInside.clear();
1527  isInside.resize(coords.size(), false);
1528 
1529  Vec3d aabbMin, aabbMax;
1530  surfaceMesh.computeBoundingBox(aabbMin, aabbMax, 1.0);
1531  // space between two adjacent points
1532  const Vec3d h = { coords[1][0] - coords[0][0], coords[nx][1] - coords[0][1], coords[nx * ny][2] - coords[0][2] };
1533 
1539  auto triangleRayIntersection = [](const Vec3d& xyz, const Vec3d& triVert0, const Vec3d& triVert1, const Vec3d& triVert2, const Vec3d& direction, double& distance)
1540  {
1541  const double eps = std::numeric_limits<double>::epsilon();
1542  const Vec3d edge0 = triVert1 - triVert0;
1543  const Vec3d edge1 = triVert2 - triVert0;
1544  const Vec3d pvec = direction.cross(edge1);
1545  const double det = edge0.dot(pvec);
1546 
1547  if (det > -eps && det < eps)
1548  {
1549  return false;
1550  }
1551  const double inv_det = 1.0 / det;
1552  const Vec3d tvec = xyz - triVert0;
1553  const double u = tvec.dot(pvec) * inv_det;
1554  if (u < 0.0 || u > 1.0)
1555  {
1556  return false;
1557  }
1558  const Vec3d qvec = tvec.cross(edge0);
1559  const double v = direction.dot(qvec) * inv_det;
1560  if (v < 0.0 || u + v > 1.0)
1561  {
1562  return false;
1563  }
1564 
1565  distance = edge1.dot(qvec) * inv_det;
1566  if (distance > 0.0)
1567  {
1568  return true;
1569  }
1570  else
1571  {
1572  return false;
1573  }
1574  };
1575 
1576  std::vector<Vec3d> bBoxMin;
1577  std::vector<Vec3d> bBoxMax;
1578 
1579  bBoxMin.resize(surfaceMesh.getNumCells());
1580  bBoxMax.resize(surfaceMesh.getNumCells());
1581 
1583  const VecDataArray<int, 3>& indices = *surfaceMesh.getCells();
1584  auto findBoundingBox = [&](const size_t idx)
1585  {
1586  const auto& verts = indices[idx];
1587  const auto& vertXyz = *surfaceMesh.getVertexPositions();
1588  const auto& xyz0 = vertXyz[verts[0]];
1589  const auto& xyz1 = vertXyz[verts[1]];
1590  const auto& xyz2 = vertXyz[verts[2]];
1591 
1592  bBoxMin[idx][0] = xyz0[0];
1593  bBoxMin[idx][1] = xyz0[1];
1594  bBoxMin[idx][2] = xyz0[2];
1595  bBoxMax[idx][0] = xyz0[0];
1596  bBoxMax[idx][1] = xyz0[1];
1597  bBoxMax[idx][2] = xyz0[2];
1598 
1599  bBoxMin[idx][0] = std::min(bBoxMin[idx][0], xyz1[0]);
1600  bBoxMin[idx][1] = std::min(bBoxMin[idx][1], xyz1[1]);
1601  bBoxMin[idx][2] = std::min(bBoxMin[idx][2], xyz1[2]);
1602  bBoxMin[idx][0] = std::min(bBoxMin[idx][0], xyz2[0]);
1603  bBoxMin[idx][1] = std::min(bBoxMin[idx][1], xyz2[1]);
1604  bBoxMin[idx][2] = std::min(bBoxMin[idx][2], xyz2[2]);
1605 
1606  bBoxMax[idx][0] = std::max(bBoxMax[idx][0], xyz1[0]);
1607  bBoxMax[idx][1] = std::max(bBoxMax[idx][1], xyz1[1]);
1608  bBoxMax[idx][2] = std::max(bBoxMax[idx][2], xyz1[2]);
1609  bBoxMax[idx][0] = std::max(bBoxMax[idx][0], xyz2[0]);
1610  bBoxMax[idx][1] = std::max(bBoxMax[idx][1], xyz2[1]);
1611  bBoxMax[idx][2] = std::max(bBoxMax[idx][2], xyz2[2]);
1612  };
1613 
1614  ParallelUtils::parallelFor(surfaceMesh.getNumCells(), findBoundingBox);
1615 
1616  // ray tracing for all points in the x-axis. These points are those start with indices (0,j,k)
1617  // and jk = j + k*ny
1618  auto rayTracingLine = [&](const size_t jk)
1619  {
1620  size_t idx0 = jk * nx;
1621  bool outBox = coords[idx0][0] < aabbMin[0] || coords[idx0][0] > aabbMax[0]
1622  || coords[idx0][1] < aabbMin[1] || coords[idx0][1] > aabbMax[1]
1623  || coords[idx0][2] < aabbMin[2] || coords[idx0][2] > aabbMax[2];
1624  if (outBox)
1625  {
1626  return;
1627  }
1628 
1629  const Vec3d direction = { 1.0, 0.0, 0.0 };
1630  const auto& xyz = *surfaceMesh.getVertexPositions();
1631 
1632  size_t i = 0;
1633  while (i < nx)
1634  {
1635  size_t idx = idx0 + i;
1636  int numIntersections = 0;
1637  double dist = 0.0;
1638  double distMin = h[0] * (nz + 1);
1639 
1640  for (int j = 0; j < surfaceMesh.getNumCells(); ++j)
1641  {
1642  const Vec3i& verts = indices[j];
1643 
1644  // consider directed ray
1645  if (coords[idx][0] > bBoxMax[j][0])
1646  {
1647  continue;
1648  }
1649 
1650  if (coords[idx][1] > bBoxMax[j][1])
1651  {
1652  continue;
1653  }
1654  if (coords[idx][1] < bBoxMin[j][1])
1655  {
1656  continue;
1657  }
1658  if (coords[idx][2] > bBoxMax[j][2])
1659  {
1660  continue;
1661  }
1662  if (coords[idx][2] < bBoxMin[j][2])
1663  {
1664  continue;
1665  }
1666 
1667  auto intersected = triangleRayIntersection(coords[idx],
1668  xyz[verts[0]],
1669  xyz[verts[1]],
1670  xyz[verts[2]],
1671  direction,
1672  dist);
1673  if (intersected)
1674  {
1675  ++numIntersections;
1676  distMin = std::min(dist, distMin);
1677  }
1678  }
1679 
1680  // core of the algorithm: points between the current one and iEnd share the same label so we can skip them.
1681  size_t iEnd = i + static_cast<size_t>(distMin / h[0]) + 1;
1682  iEnd = std::min(iEnd, nx);
1683 
1684  if (numIntersections % 2 == 1)
1685  {
1686  for (size_t ii = idx; ii < idx0 + iEnd; ++ii)
1687  {
1688  isInside[ii] = true;
1689  }
1690  }
1691 
1692  i = iEnd;
1693  }
1694  };
1695 
1696  ParallelUtils::parallelFor(ny * nz, rayTracingLine);
1697 }
1698 } // anonymous namespace
1699 
1700 std::shared_ptr<TetrahedralMesh>
1702  const Vec3d& aabbMax,
1703  const int nx,
1704  const int ny,
1705  const int nz)
1706 {
1707  const Vec3d h = { (aabbMax[0] - aabbMin[0]) / nx,
1708  (aabbMax[1] - aabbMin[1]) / ny,
1709  (aabbMax[2] - aabbMin[2]) / nz };
1710  LOG_IF(FATAL, (h[0] <= 0.0 || h[1] <= 0.0 || h[2] <= 0.0)) << "Invalid bounding box";
1711 
1712  const size_t numVertices = (nx + 1) * (ny + 1) * (nz + 1);
1713 
1714  // std::vector<Vec3d> coords;
1715  std::shared_ptr<VecDataArray<double, 3>> coords = std::make_shared<VecDataArray<double, 3>>();
1716  VecDataArray<double, 3>& vertices = *coords;
1717  vertices.resize(static_cast<int>(numVertices));
1718  int cnt = 0;
1719 
1720  for (int k = 0; k < nz + 1; ++k)
1721  {
1722  double z = aabbMin[2] + k * h[2];
1723  for (int j = 0; j < ny + 1; ++j)
1724  {
1725  double y = aabbMin[1] + j * h[1];
1726  for (int i = 0; i < nx + 1; ++i)
1727  {
1728  double x = aabbMin[0] + i * h[0];
1729  vertices[cnt] = { x, y, z };
1730  ++cnt;
1731  }
1732  }
1733  }
1734 
1735  const int numDiv = 6;
1736  const int numTets = numDiv * nx * ny * nz;
1737 
1738  std::shared_ptr<VecDataArray<int, 4>> indicesPtr = std::make_shared<VecDataArray<int, 4>>();
1739  VecDataArray<int, 4>& indices = *indicesPtr;
1740  indices.resize(static_cast<int>(numTets));
1741  cnt = 0;
1742  std::vector<int> indx(8);
1743 
1744  for (int k = 0; k < nz; ++k)
1745  {
1746  for (int j = 0; j < ny; ++j)
1747  {
1748  for (int i = 0; i < nx; ++i)
1749  {
1750  indx[3] = i + j * (nx + 1) + k * (nx + 1) * (ny + 1);
1751  indx[2] = indx[3] + 1;
1752  indx[0] = indx[3] + nx + 1;
1753  indx[1] = indx[0] + 1;
1754  indx[4] = indx[0] + (nx + 1) * (ny + 1);
1755  indx[5] = indx[1] + (nx + 1) * (ny + 1);
1756  indx[6] = indx[2] + (nx + 1) * (ny + 1);
1757  indx[7] = indx[3] + (nx + 1) * (ny + 1);
1758 
1759  indices[cnt + 0] = Vec4i(indx[0], indx[2], indx[3], indx[6]);
1760  indices[cnt + 1] = Vec4i(indx[0], indx[3], indx[7], indx[6]);
1761  indices[cnt + 2] = Vec4i(indx[0], indx[7], indx[4], indx[6]);
1762  indices[cnt + 3] = Vec4i(indx[0], indx[5], indx[6], indx[4]);
1763  indices[cnt + 4] = Vec4i(indx[1], indx[5], indx[6], indx[0]);
1764  indices[cnt + 5] = Vec4i(indx[1], indx[6], indx[2], indx[0]);
1765  cnt += numDiv;
1766  }
1767  }
1768  }
1769 
1770  auto mesh = std::make_shared<TetrahedralMesh>();
1771  mesh->initialize(coords, indicesPtr);
1772  return mesh;
1773 }
1774 
1775 std::shared_ptr<TetrahedralMesh>
1776 GeometryUtils::createTetrahedralMeshCover(std::shared_ptr<SurfaceMesh> surfMesh,
1777  const int nx,
1778  const int ny,
1779  const int nz)
1780 {
1781  Vec3d aabbMin, aabbMax;
1782 
1783  // create a background mesh
1784  surfMesh->computeBoundingBox(aabbMin, aabbMax, 1. /*percentage padding*/);
1785  auto uniformMesh = createUniformMesh(aabbMin, aabbMax, nx, ny, nz);
1786 
1787  // ray-tracing
1788  const VecDataArray<double, 3>& coords = *uniformMesh->getVertexPositions();
1789  std::vector<bool> insideSurfMesh;
1790  markPointsInsideAndOut(insideSurfMesh, *surfMesh.get(), coords, nx + 1, ny + 1, nz + 1);
1791 
1792  // label elements
1793  std::vector<bool> validTet(uniformMesh->getNumCells(), false);
1794  std::vector<bool> validVtx(uniformMesh->getNumVertices(), false);
1795 
1796  // TetrahedralMesh::WeightsArray weights = {0.0, 0.0, 0.0, 0.0};
1797  const Vec3d h = { (aabbMax[0] - aabbMin[0]) / nx,
1798  (aabbMax[1] - aabbMin[1]) / ny,
1799  (aabbMax[2] - aabbMin[2]) / nz };
1800 
1801  // a customized approach to find the enclosing tet for each surface points
1803  auto labelEnclosingTet = [&aabbMin, &h, nx, ny, nz, &uniformMesh, &validTet](const Vec3d& xyz)
1804  {
1805  const int idX = (xyz[0] - aabbMin[0]) / h[0];
1806  const int idY = (xyz[1] - aabbMin[1]) / h[1];
1807  const int idZ = (xyz[2] - aabbMin[2]) / h[2];
1808  const int hexId = idX + idY * nx + idZ * nx * ny;
1809 
1810  // the index range of tets inside the enclosing hex
1811  const int numDiv = 6;
1812  const int tetId0 = numDiv * hexId;
1813  const int tetId1 = tetId0 + numDiv;
1814 
1815  static Vec4d weights = Vec4d::Zero();
1816 
1817  // loop over the tets to find the enclosing tets
1818  for (int id = tetId0; id < tetId1; ++id)
1819  {
1820  if (validTet[id])
1821  {
1822  continue;
1823  }
1824  weights = uniformMesh->computeBarycentricWeights(id, xyz);
1825 
1826  if ((weights[0] >= 0) && (weights[1] >= 0) && (weights[2] >= 0) && (weights[3] >= 0))
1827  {
1828  validTet[id] = true;
1829  break;
1830  }
1831  }
1832  };
1833 
1834  auto labelEnclosingTetOfVertices = [&surfMesh, &uniformMesh, &aabbMin, &h, nx, ny, nz, &labelEnclosingTet, &validTet](const int i)
1835  {
1836  const auto& xyz = surfMesh->getVertexPosition(i);
1837  labelEnclosingTet(xyz);
1838  };
1839 
1840  for (size_t i = 0; i < validTet.size(); ++i)
1841  {
1842  const Vec4i& verts = (*uniformMesh->getCells())[i];
1843  if (insideSurfMesh[verts[0]]
1844  || insideSurfMesh[verts[1]]
1845  || insideSurfMesh[verts[2]]
1846  || insideSurfMesh[verts[3]])
1847  {
1848  validTet[i] = true;
1849  }
1850  }
1851 
1852  // find the enclosing tets of a group of points on a surface triangle
1853  const VecDataArray<int, 3>& indices = *surfMesh->getCells();
1854  auto labelEnclosingTetOfInteriorPnt = [&](const int fid)
1855  {
1856  auto verts = indices[fid];
1857  const auto& vtx0 = surfMesh->getVertexPosition(verts[0]);
1858  const auto& vtx1 = surfMesh->getVertexPosition(verts[1]);
1859  const auto& vtx2 = surfMesh->getVertexPosition(verts[2]);
1860  std::vector<Vec3d> pnts(12);
1861 
1862  pnts[0] = 0.75 * vtx0 + 0.25 * vtx1;
1863  pnts[1] = 0.50 * vtx0 + 0.50 * vtx1;
1864  pnts[2] = 0.25 * vtx0 + 0.75 * vtx1;
1865  pnts[3] = 0.75 * vtx1 + 0.25 * vtx2;
1866  pnts[4] = 0.50 * vtx1 + 0.50 * vtx2;
1867  pnts[5] = 0.25 * vtx1 + 0.75 * vtx2;
1868  pnts[6] = 0.75 * vtx2 + 0.25 * vtx0;
1869  pnts[7] = 0.50 * vtx2 + 0.50 * vtx0;
1870  pnts[8] = 0.25 * vtx2 + 0.75 * vtx0;
1871  pnts[9] = 2.0 / 3.0 * pnts[0] + 1.0 / 3.0 * pnts[5];
1872  pnts[10] = 0.5 * (pnts[1] + pnts[4]);
1873  pnts[11] = 0.5 * (pnts[4] + pnts[7]);
1874 
1875  for (size_t i = 0; i < pnts.size(); ++i)
1876  {
1877  labelEnclosingTet(pnts[i]);
1878  }
1879  };
1880 
1881  // enclose all vertices
1882  for (int i = 0; i < surfMesh->getNumVertices(); ++i)
1883  {
1884  labelEnclosingTetOfVertices(i);
1885  }
1886 
1887  // enclose some interior points on triangles
1888  for (int i = 0; i < surfMesh->getNumCells(); ++i)
1889  {
1890  labelEnclosingTetOfInteriorPnt(i);
1891  }
1892 
1893  int numElems = 0;
1894  for (size_t i = 0; i < validTet.size(); ++i)
1895  {
1896  const Vec4i& verts = (*uniformMesh->getCells())[i];
1897  if (validTet[i])
1898  {
1899  validVtx[verts[0]] = true;
1900  validVtx[verts[1]] = true;
1901  validVtx[verts[2]] = true;
1902  validVtx[verts[3]] = true;
1903  ++numElems;
1904  }
1905  }
1906 
1907  // discard useless vertices, and build old-to-new index map
1908  size_t numVerts = 0;
1909  for (auto b : validVtx)
1910  {
1911  if (b)
1912  {
1913  ++numVerts;
1914  }
1915  }
1916 
1917  std::shared_ptr<VecDataArray<double, 3>> newCoords = std::make_shared<VecDataArray<double, 3>>(static_cast<int>(numVerts));
1918  VecDataArray<double, 3>& vertices = *newCoords;
1919  std::vector<int> oldToNew(coords.size(), std::numeric_limits<int>::max());
1920 
1921  int cnt = 0;
1922  for (size_t i = 0; i < validVtx.size(); ++i)
1923  {
1924  if (validVtx[i])
1925  {
1926  vertices[cnt] = coords[i];
1927  oldToNew[i] = cnt;
1928  ++cnt;
1929  }
1930  }
1931 
1932  // update tet-to-vert connectivity
1933  std::shared_ptr<VecDataArray<int, 4>> newIndicesPtr = std::make_shared<VecDataArray<int, 4>>(numElems);
1934  VecDataArray<int, 4>& newIndices = *newIndicesPtr;
1935  cnt = 0;
1936  for (int i = 0; i < uniformMesh->getNumCells(); ++i)
1937  {
1938  if (validTet[i])
1939  {
1940  const Vec4i& oldIndices = (*uniformMesh->getCells())[i];
1941 
1942  newIndices[cnt][0] = oldToNew[oldIndices[0]];
1943  newIndices[cnt][1] = oldToNew[oldIndices[1]];
1944  newIndices[cnt][2] = oldToNew[oldIndices[2]];
1945  newIndices[cnt][3] = oldToNew[oldIndices[3]];
1946 
1947  ++cnt;
1948  }
1949  }
1950 
1951  // ready to create the final mesh
1952  auto mesh = std::make_shared<TetrahedralMesh>();
1953  mesh->initialize(newCoords, newIndicesPtr);
1954 
1955  return mesh;
1956 }
1957 
1958 template<typename NeighborContainer>
1959 std::vector<size_t>
1960 GeometryUtils::reorderConnectivity(const std::vector<NeighborContainer>& neighbors, const MeshNodeRenumberingStrategy& method)
1961 {
1962  switch (method)
1963  {
1964  case (MeshNodeRenumberingStrategy::ReverseCuthillMckee):
1965  return RCM(neighbors);
1966  default:
1967  LOG(WARNING) << "Unrecognized reorder method; using RCM instead";
1968  return RCM(neighbors);
1969  }
1970 }
1971 
1972 template<typename ElemConn>
1973 std::vector<size_t>
1974 GeometryUtils::reorderConnectivity(const std::vector<ElemConn>& conn, const size_t numVerts, const MeshNodeRenumberingStrategy& method)
1975 {
1976  switch (method)
1977  {
1978  case (MeshNodeRenumberingStrategy::ReverseCuthillMckee):
1979  return RCM(conn, numVerts);
1980  default:
1981  LOG(WARNING) << "Unrecognized reorder method; using Reverse Cuthill-Mckee strategy instead";
1982  return RCM(conn, numVerts);
1983  }
1984 }
1985 } // namespace imstk
1986 
1987 template std::vector<size_t> imstk::GeometryUtils::reorderConnectivity<std::set<size_t>>(const std::vector<std::set<size_t>>&, const GeometryUtils::MeshNodeRenumberingStrategy&);
1988 
1989 template std::vector<size_t> imstk::GeometryUtils::reorderConnectivity<std::array<size_t, 4>>(const std::vector<std::array<size_t, 4>>&, const size_t, const MeshNodeRenumberingStrategy&);
vtkSmartPointer< vtkUnstructuredGrid > copyToVtkUnstructuredGrid(std::shared_ptr< TetrahedralMesh > imstkMesh)
Converts imstk tetrahedral mesh into a vtk unstructured grid.
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.
vtkSmartPointer< vtkDataArray > coupleVtkDataArray(std::shared_ptr< AbstractDataArray > imstkArray)
Coupling functions, these create vtk data objects that point to our data objects thus no copying is d...
std::shared_ptr< LineMesh > copyToLineMesh(vtkSmartPointer< vtkPolyData > vtkMesh)
Converts vtk polydata into a imstk surface mesh.
vtkSmartPointer< vtkPolyData > copyToVtkPolyData(std::shared_ptr< LineMesh > imstkMesh)
Converts imstk line mesh into a vtk polydata.
int getOpenEdgeCount(std::shared_ptr< SurfaceMesh > surfMesh)
Returns the number of open edges, use to tell if manifold (==0)
vtkSmartPointer< vtkPoints > copyToVtkPoints(std::shared_ptr< VecDataArray< double, 3 >> vertices)
Copies vertices from imstk structure to VTK one.
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 resize(const int size) override
Resize data array to hold exactly size number of values.
void push_back(const ValueType &val)
Append the data array to hold the new value, resizes if neccesary.
std::shared_ptr< AbstractCellMesh > copyToCellMesh(vtkSmartPointer< vtkUnstructuredGrid > vtkMesh)
Get imstk cell mesh given vtkUnstructuredGrid as input iMSTK only supports homogenous cells...
virtual void computeBoundingBox(Vec3d &lowerCorner, Vec3d &upperCorner, const double paddingPercent=0.0) override
Compute the bounding box for the entire mesh.
int getNumCells() const override
Returns the number of cells.
std::shared_ptr< LineMesh > toLineGrid(const Vec3d &start, const Vec3d &dir, const double length, const int dim)
Creates a set of connected lines.
const Vec3d & getVertexPosition(const size_t vertNum, DataType type=DataType::PostTransform) const
Returns the position of a vertex given its index.
vtkSmartPointer< vtkPointSet > copyToVtkPointSet(std::shared_ptr< PointSet > imstkMesh)
Converts imstk point set into a vtk polydata.
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
void copyToDataMap(vtkDataSetAttributes *pointData, std::unordered_map< std::string, std::shared_ptr< AbstractDataArray >> &dataMap)
Copy vtkPointData to a data map.
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
std::shared_ptr< TetrahedralMesh > createUniformMesh(const Vec3d &aabbMin, const Vec3d &aabbMax, const int nx, const int ny, const int nz)
Create a tetrahedral mesh based on a uniform Cartesian mesh.
VecDataArray< U, N > cast()
Templated copy the current array with a new internal data type, does not change the number of compone...
std::shared_ptr< SurfaceMesh > toUVSphereSurfaceMesh(std::shared_ptr< Sphere > sphere, const unsigned int phiDivisions, const unsigned int thetaDivisions)
UV sphere from imstkSphere.
std::shared_ptr< SurfaceMesh > copyToSurfaceMesh(vtkSmartPointer< vtkPolyData > vtkMesh)
Converts vtk polydata into a imstk surface mesh.
std::shared_ptr< TetrahedralMesh > createTetrahedralMeshCover(std::shared_ptr< SurfaceMesh > surfMesh, const int nx, const int ny, int nz)
Create a tetrahedral mesh cover.
double getVolume(std::shared_ptr< SurfaceMesh > surfMesh)
Returns volume estimate of closed SurfaceMesh.
vtkSmartPointer< vtkCellArray > copyToVtkCellArray(std::shared_ptr< VecDataArray< int, dim >> cells)
Copies cells of the given dimension from imstk structure to VTK one.
void copyToVtkDataAttributes(vtkDataSetAttributes *pointData, const std::unordered_map< std::string, std::shared_ptr< AbstractDataArray >> &dataMap)
Copy from a data map to vtkDataAttributes (used for vtkCellData and vtkPointData) warning: Components...
std::shared_ptr< PointSet > copyToPointSet(vtkSmartPointer< vtkPointSet > vtkMesh)
Converts vtk polydata into a imstk point set.
vtkSmartPointer< vtkDataArray > copyToVtkDataArray(std::shared_ptr< AbstractDataArray > imstkArray)
Copy functions, these copy to/from vtk data objects.
std::shared_ptr< SurfaceMesh > toSurfaceMesh(std::shared_ptr< AnalyticalGeometry > geom)
Produces SurfaceMesh from an analytical geometry.
MeshNodeRenumberingStrategy
Enumeration for reordering method.
std::shared_ptr< VecDataArray< double, 3 > > copyToVecDataArray(vtkSmartPointer< vtkPoints > points)
Copy from vtk points to a imstk vertices array.
std::vector< size_t > reorderConnectivity(const std::vector< NeighborContainer > &neighbors, const MeshNodeRenumberingStrategy &method=MeshNodeRenumberingStrategy::ReverseCuthillMckee)
Reorder indices in a connectivity to reduce bandwidth.