compartment_utils.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2020 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 <vtkSmartPointer.h>
  12. #include <vtkPolyData.h>
  13. #include <vtkTriangle.h>
  14. #include <vtkCleanPolyData.h>
  15. #include <vtkTriangleFilter.h>
  16. #include <vtkSelectEnclosedPoints.h>
  17. #include "compartment_utils.h"
  18. #include "api/api_common.h"
  19. #include "vtk_utils.h"
  20. #include "geometry_object.h"
  21. using namespace std;
  22. namespace MCell {
  23. using namespace VtkUtils;
  24. namespace API {
  25. // note: similar code is in convert_and_set_geometry_object_polydata but uses different input data
  26. static void convert_compartment_objects_to_geom_object_infos(
  27. const std::vector<std::shared_ptr<API::GeometryObject>>& compartment_objects,
  28. GeomObjectInfoVector& compartment_infos) {
  29. bool res = true;
  30. for (const auto& obj_ptr: compartment_objects) {
  31. const API::GeometryObject& obj = *obj_ptr;
  32. // we need to convert each geometry object into VTK's polydata representation
  33. // example: https://vtk.org/Wiki/VTK/Examples/Cxx/PolyData/TriangleArea
  34. vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
  35. vtkSmartPointer<vtkCellArray> triangles = vtkSmartPointer<vtkCellArray>::New();
  36. GeomObjectInfo obj_info(obj.name);
  37. obj_info.polydata = vtkSmartPointer<vtkPolyData>::New();
  38. // first collect vertices
  39. uint_set<vertex_index_t> vertex_indices;
  40. for (const std::vector<double>& v: obj.vertex_list) {
  41. assert(v.size() == 3);
  42. points->InsertNextPoint(v[0], v[1], v[2]);
  43. }
  44. // store triangles
  45. for (const std::vector<int>& wi: obj.wall_list) {
  46. assert(wi.size() == VERTICES_IN_TRIANGLE);
  47. vtkSmartPointer<vtkTriangle> triangle = vtkSmartPointer<vtkTriangle>::New();
  48. for (uint i = 0; i < VERTICES_IN_TRIANGLE; i++) {
  49. triangle->GetPointIds()->SetId(i, wi[i]);
  50. }
  51. triangles->InsertNextCell(triangle);
  52. }
  53. // create input polydata
  54. vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
  55. polydata->SetPoints(points);
  56. polydata->SetPolys(triangles);
  57. // clean them up
  58. vtkSmartPointer<vtkTriangleFilter> tri = vtkSmartPointer<vtkTriangleFilter>::New();
  59. tri->SetInputData(polydata);
  60. vtkSmartPointer<vtkCleanPolyData> clean = vtkSmartPointer<vtkCleanPolyData>::New();
  61. clean->SetInputConnection(tri->GetOutputPort());
  62. clean->Update();
  63. int closed = vtkSelectEnclosedPoints::IsSurfaceClosed(clean->GetOutput());
  64. if (closed != 1) {
  65. throw RuntimeError("Compartment object must be closed, error for " + obj.name + ".");
  66. }
  67. // and finally store the points and faces
  68. obj_info.polydata = clean->GetOutput();
  69. compartment_infos.push_back(obj_info);
  70. }
  71. }
  72. static std::shared_ptr<API::GeometryObject> get_obj_by_name(
  73. std::vector<std::shared_ptr<API::GeometryObject>>& compartment_objects,
  74. const string& name) {
  75. for (auto& obj: compartment_objects) {
  76. if (obj->name == name) {
  77. return obj;
  78. }
  79. }
  80. release_assert(false);
  81. return std::shared_ptr<API::GeometryObject>(nullptr);
  82. }
  83. void set_parent_and_children_compartments(
  84. std::vector<std::shared_ptr<API::GeometryObject>>& compartment_objects) {
  85. #if 0
  86. auto obj_cp = std::shared_ptr<API::GeometryObject>(nullptr);
  87. for (auto obj: compartment_objects) {
  88. release_assert(obj->name == "CP" || obj->name == "EC"); // limited for now
  89. if (obj->name == "CP") {
  90. obj_cp = obj;
  91. }
  92. }
  93. auto obj_ec = std::shared_ptr<API::GeometryObject>(nullptr);
  94. for (auto obj: compartment_objects) {
  95. if (obj->name == "EC") {
  96. obj->child_compartments.insert(obj_cp);
  97. obj_ec = obj;
  98. }
  99. }
  100. if (is_set(obj_cp) && is_set(obj_ec)) {
  101. obj_cp->parent_compartment = obj_ec;
  102. }
  103. return true;
  104. #endif
  105. GeomObjectInfoVector compartment_infos;
  106. convert_compartment_objects_to_geom_object_infos(compartment_objects, compartment_infos);
  107. // get information on what objects are contained within each other
  108. // e.g. CP -> {EC} - CP is contained in EC, mapping contains all compartments, not just the direct parent
  109. ContainmentMap contained_in_mapping;
  110. IntersectingSet intersecting_object_infos;
  111. bool ok = compute_containement_mapping(
  112. nullptr, compartment_infos, contained_in_mapping, intersecting_object_infos);
  113. if (!ok) {
  114. throw RuntimeError("Unexpected error while computing compartment hierarchy.");
  115. }
  116. if (!intersecting_object_infos.empty()) {
  117. string names;
  118. for (const GeomObjectInfo& info: intersecting_object_infos) {
  119. names += info.name + ", ";
  120. }
  121. names = names.substr(0, names.size() - 2); // remove last comma
  122. throw RuntimeError("Error while computing compartment hierarchy, following compartments intersect " +
  123. names + ".");
  124. }
  125. // set children and parents
  126. for (const auto& pair_name_parents: contained_in_mapping) {
  127. auto child = get_obj_by_name(compartment_objects, pair_name_parents.first.name);
  128. // select the direct parent
  129. const GeomObjectInfo* direct_parent_info =
  130. get_direct_parent_info(pair_name_parents.first, contained_in_mapping);
  131. if (direct_parent_info == nullptr) {
  132. // no parent, children set themselves as children of this compartment
  133. }
  134. else {
  135. auto parent = get_obj_by_name(compartment_objects, direct_parent_info->name);
  136. parent->child_compartments.insert(child);
  137. if (is_set(child->parent_compartment)) {
  138. // this should not really occur but lets better report it
  139. throw RuntimeError("Compartment object " + child->name + " cannot have multiple parent compartments " +
  140. child->parent_compartment->name + " and " + parent->name + ".");
  141. }
  142. child->parent_compartment = parent;
  143. }
  144. }
  145. }
  146. } // namespace API
  147. } // namespace MCell