data_model_geometry.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2021 by
  4. * The Salk Institute for Biological Studies
  5. *
  6. * Use of this source code is governed by an MIT-style
  7. * license that can be found in the LICENSE file or at
  8. * https://opensource.org/licenses/MIT.
  9. *
  10. ******************************************************************************/
  11. #include "data_model_geometry.h"
  12. #include <stdexcept>
  13. #include <vtkSmartPointer.h>
  14. #include <vtkTriangle.h>
  15. #include <vtkCleanPolyData.h>
  16. #include <vtkTriangleFilter.h>
  17. #include <vtkTransform.h>
  18. #include <vtkPointData.h>
  19. #include <vtkFeatureEdges.h>
  20. #include <vtkMassProperties.h>
  21. #include <vtkPolyDataNormals.h>
  22. #include <vtkPolyDataMapper.h>
  23. #include "include/datamodel_defines.h"
  24. #include "generator_utils.h"
  25. using namespace Json;
  26. using Json::Value;
  27. namespace MCell {
  28. static double is_watertight(vtkSmartPointer<vtkPolyData> polydata) {
  29. // check if the object is watertight,
  30. // based on https://lorensen.github.io/VTKExamples/site/Cxx/Meshes/BoundaryEdges/
  31. vtkSmartPointer<vtkFeatureEdges> featureEdges =
  32. vtkSmartPointer<vtkFeatureEdges>::New();
  33. featureEdges->SetInputData(polydata.Get());
  34. featureEdges->BoundaryEdgesOn();
  35. featureEdges->FeatureEdgesOff();
  36. featureEdges->ManifoldEdgesOff();
  37. featureEdges->NonManifoldEdgesOff();
  38. featureEdges->Update();
  39. // if there are no edges, the object is watertight
  40. return featureEdges->GetOutput()->GetNumberOfCells() < 1;
  41. }
  42. static vtkSmartPointer<vtkPolyData> convert_dm_object_to_polydata(
  43. Json::Value& model_object) {
  44. string name = model_object[KEY_NAME].asString();
  45. // we need to convert each geometry object into VTK's polydata representation
  46. // example: https://vtk.org/Wiki/VTK/Examples/Cxx/PolyData/TriangleArea
  47. vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
  48. vtkSmartPointer<vtkCellArray> triangles = vtkSmartPointer<vtkCellArray>::New();
  49. Value& vertex_list = get_node(name, model_object, KEY_VERTEX_LIST);
  50. Value& element_connections = get_node(name, model_object, KEY_ELEMENT_CONNECTIONS);
  51. // assuming the the data model is correct
  52. for (Value::ArrayIndex i = 0; i < vertex_list.size(); i++) {
  53. Value& vertex = vertex_list[i];
  54. points->InsertNextPoint(vertex[0].asDouble(), vertex[1].asDouble(), vertex[2].asDouble());
  55. }
  56. // store triangles
  57. for (Value::ArrayIndex i = 0; i < element_connections.size(); i++) {
  58. Value& element = element_connections[i];
  59. vtkSmartPointer<vtkTriangle> triangle = vtkSmartPointer<vtkTriangle>::New();
  60. for (uint i = 0; i < VERTICES_IN_TRIANGLE; i++) {
  61. triangle->GetPointIds()->SetId(i, element[i].asInt());
  62. }
  63. triangles->InsertNextCell(triangle);
  64. }
  65. // create input polydata
  66. vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
  67. polydata->SetPoints(points);
  68. polydata->SetPolys(triangles);
  69. // clean them up
  70. vtkSmartPointer<vtkTriangleFilter> tri = vtkSmartPointer<vtkTriangleFilter>::New();
  71. tri->SetInputData(polydata);
  72. vtkSmartPointer<vtkCleanPolyData> clean = vtkSmartPointer<vtkCleanPolyData>::New();
  73. clean->SetInputConnection(tri->GetOutputPort());
  74. clean->Update();
  75. // also copy this information to our object
  76. return clean->GetOutput();
  77. }
  78. // practically the same as in vtk_utils
  79. // auxiliary function to compute volume, not related to counted volumes but uses VTK
  80. // returns FLT_INVALID if the object is not watertight
  81. static void get_polydata_volume_and_area(
  82. vtkSmartPointer<vtkPolyData> polydata, double& volume, double& area) {
  83. // volume computation is based on
  84. // https://lorensen.github.io/VTKExamples/site/Cxx/Utilities/MassProperties/
  85. vtkSmartPointer<vtkTriangleFilter> triangleFilter =
  86. vtkSmartPointer<vtkTriangleFilter>::New();
  87. triangleFilter->SetInputData(polydata);
  88. // Make the triangle windong order consistent
  89. vtkSmartPointer<vtkPolyDataNormals> normals =
  90. vtkSmartPointer<vtkPolyDataNormals>::New();
  91. normals->SetInputConnection(triangleFilter->GetOutputPort());
  92. normals->ConsistencyOn();
  93. normals->SplittingOff();
  94. vtkSmartPointer<vtkMassProperties> massProperties =
  95. vtkSmartPointer<vtkMassProperties>::New();
  96. massProperties->SetInputConnection(normals->GetOutputPort());
  97. massProperties->Update();
  98. volume = massProperties->GetVolume();
  99. area = massProperties->GetSurfaceArea();
  100. }
  101. void compute_volume_and_area(Json::Value& model_object, double& volume, double& area) {
  102. string name = model_object[KEY_NAME].asString();
  103. vtkSmartPointer<vtkPolyData> polydata = convert_dm_object_to_polydata(model_object);
  104. if (!is_watertight(polydata)) {
  105. throw ConversionError("Geometry object " + name + " is not watertight, could not compute volume.");
  106. }
  107. get_polydata_volume_and_area(polydata, volume, area);
  108. }
  109. } // namespace MCell