OGS
AddFaultToVoxelGrid.cpp
Go to the documentation of this file.
1 
10 #include <algorithm>
11 #include <memory>
12 #include <string>
13 #include <vector>
14 
15 // ThirdParty
16 #include <tclap/CmdLine.h>
17 
18 #include "GeoLib/AABB.h"
19 #include "InfoLib/GitInfo.h"
20 #include "MathLib/Point3d.h"
24 #include "MeshLib/Mesh.h"
25 #include "MeshLib/Node.h"
26 
27 static std::string mat_name = "MaterialIDs";
28 
29 // tests if a plane and an AABB are intersecting
30 // (based on Christer Ericson "Real Time Collision Detection" 5.2.3)
31 bool testAABBIntersectingPlane(Eigen::Vector3d const& aabb_centre,
32  Eigen::Vector3d const& aabb_extent,
33  Eigen::Vector3d const& plane_normal,
34  double const pd)
35 {
36  double const r = aabb_extent.dot(plane_normal.cwiseAbs());
37  double const s = plane_normal.dot(aabb_centre) - pd;
38  return std::abs(s) > r;
39 }
40 
41 // tests if a triangle and an AABB are intersecting
42 // (based on Christer Ericson "Real Time Collision Detection" 5.2.9)
44  MeshLib::Node const& n1,
45  MeshLib::Node const& n2,
46  Eigen::Vector3d const& c,
47  Eigen::Vector3d const& e)
48 {
49  // Translate triangle as conceptually moving AABB to origin
50  Eigen::Matrix3d v;
51  v << Eigen::Vector3d(n0.getCoords()) - c,
52  Eigen::Vector3d(n1.getCoords()) - c,
53  Eigen::Vector3d(n2.getCoords()) - c;
54 
55  // Test the three axes corresponding to the face normals of AABB b
56  if (((v.rowwise().minCoeff() - e).array() > 0).any() ||
57  ((v.rowwise().maxCoeff() + e).array() < 0).any())
58  {
59  return false;
60  }
61 
62  // separating axes
63  std::array<Eigen::Vector3d, 3> tri_edge{
64  {v.col(1) - v.col(0), v.col(2) - v.col(1), v.col(0) - v.col(2)}};
65  std::array<Eigen::Vector3d, 9> const axx{
66  {Eigen::Vector3d({0, -tri_edge[0].z(), tri_edge[0].y()}),
67  Eigen::Vector3d({0, -tri_edge[1].z(), tri_edge[1].y()}),
68  Eigen::Vector3d({0, -tri_edge[2].z(), tri_edge[2].y()}),
69  Eigen::Vector3d({tri_edge[0].z(), 0, -tri_edge[0].x()}),
70  Eigen::Vector3d({tri_edge[1].z(), 0, -tri_edge[1].x()}),
71  Eigen::Vector3d({tri_edge[2].z(), 0, -tri_edge[2].x()}),
72  Eigen::Vector3d({-tri_edge[0].y(), tri_edge[0].x(), 0}),
73  Eigen::Vector3d({-tri_edge[1].y(), tri_edge[1].x(), 0}),
74  Eigen::Vector3d({-tri_edge[2].y(), tri_edge[2].x(), 0})}};
75 
76  // Separating axis tests to check if there's a plane separating the
77  // projections of the AABB and the triangle according to the Separating Axis
78  // Theorem (see C. Ericson "Real Time Collision Detection" for details)
79  for (auto const& a : axx)
80  {
81  Eigen::Vector3d p = v.transpose() * a;
82  double const r = e.dot(a.cwiseAbs());
83  if (std::max(-p.maxCoeff(), p.minCoeff()) > r)
84  {
85  return false;
86  }
87  }
88 
89  // Test separating axis corresponding to triangle face normal
90  Eigen::Vector3d const plane_normal(tri_edge[0].cross(tri_edge[1]));
91  double const pd = plane_normal.dot(v.row(0));
92  return testAABBIntersectingPlane(c, e, plane_normal, pd);
93 }
94 
95 void markFaults(MeshLib::Mesh& mesh, MeshLib::Mesh const& fault,
96  int const fault_id, std::array<double, 3> half_cell_size)
97 {
98  auto const& elems = mesh.getElements();
99  std::size_t const n_elems = mesh.getNumberOfElements();
100  auto mat_ids = mesh.getProperties().getPropertyVector<int>(mat_name);
101  auto const& fnodes = fault.getNodes();
102  auto const& felems = fault.getElements();
103  GeoLib::AABB const fault_aabb(fnodes.cbegin(), fnodes.cend());
104  auto min_pnt = fault_aabb.getMinPoint();
105  auto max_pnt = fault_aabb.getMaxPoint();
106 
107  // get bounding box of fault + voxel extent
108  for (std::size_t i = 0; i < 3; ++i)
109  {
110  min_pnt[i] -= half_cell_size[i];
111  max_pnt[i] += half_cell_size[i];
112  }
113  std::array<Eigen::Vector3d, 2> const fault_extent{{min_pnt, max_pnt}};
114  GeoLib::AABB const fault_aabb_ext(fault_extent.cbegin(),
115  fault_extent.cend());
116 
117  // test each voxel grid element vs each fault triangle
118  Eigen::Vector3d const extent(
119  {half_cell_size[0], half_cell_size[1], half_cell_size[2]});
120  for (std::size_t j = 0; j < n_elems; ++j)
121  {
122  // test if bounding box of fault is intersecting voxel
123  auto const& centre_pnt = MeshLib::getCenterOfGravity(*elems[j]);
124  if (!fault_aabb_ext.containsPoint(centre_pnt, 0))
125  {
126  continue;
127  }
128 
129  // test if voxel is intersecting triangle
130  Eigen::Vector3d const c(centre_pnt.getCoords());
131  for (auto const* const fault_elem : felems)
132  {
133  if (fault_elem->getDimension() != 2)
134  {
135  continue;
136  }
137 
139  *fault_elem->getNode(0), *fault_elem->getNode(1),
140  *fault_elem->getNode(2), c, extent))
141  {
142  (*mat_ids)[j] = fault_id;
143  break;
144  }
145 
146  if (fault_elem->getGeomType() == MeshLib::MeshElemType::QUAD)
147  {
149  *fault_elem->getNode(0), *fault_elem->getNode(2),
150  *fault_elem->getNode(3), c, extent))
151  {
152  (*mat_ids)[j] = fault_id;
153  break;
154  }
155  }
156  }
157  }
158 }
159 
160 // test if input mesh is voxel grid
161 bool isVoxelGrid(MeshLib::Mesh const& mesh)
162 {
163  auto const& elements = mesh.getElements();
164  if (std::any_of(elements.cbegin(), elements.cend(),
165  [&](auto const& e) {
166  return (e->getGeomType() !=
167  MeshLib::MeshElemType::HEXAHEDRON);
168  }))
169  {
170  ERR("Input mesh needs to be voxel grid (i.e. equally sized axis "
171  "aligned hexahedra).");
172  return false;
173  }
174 
175  for (auto const& e : elements)
176  {
177  auto const n = e->getNodes();
178  if ((*n[0])[2] != (*n[1])[2] || (*n[1])[2] != (*n[2])[2] ||
179  (*n[4])[2] != (*n[5])[2] || (*n[5])[2] != (*n[6])[2] ||
180  (*n[1])[0] != (*n[2])[0] || (*n[2])[0] != (*n[5])[0] ||
181  (*n[0])[0] != (*n[3])[0] || (*n[3])[0] != (*n[7])[0])
182  {
183  ERR("Input mesh needs to be voxel grid (i.e. equally sized axis "
184  "aligned hexahedra).");
185  return false;
186  }
187  }
188  return true;
189 }
190 
191 int main(int argc, char* argv[])
192 {
193  constexpr int mat_not_set = std::numeric_limits<int>::max();
194 
195  TCLAP::CmdLine cmd(
196  "Marks all elements in a voxel grid (i.e. a structured hex grid, for "
197  "instance created with Layers2Grid or Vtu2Grid) that are intersected "
198  "by a triangulated 2D mesh representing a fault or some other "
199  "significant structure. The material group for those intersected "
200  "elements can be explicitly specified, otherwise the largest existing "
201  "MaterialID will be increased by one.\n\n"
202  "OpenGeoSys-6 software, version " +
204  ".\n"
205  "Copyright (c) 2012-2021, OpenGeoSys Community "
206  "(http://www.opengeosys.org)",
208  TCLAP::ValueArg<int> id_arg("m", "material",
209  "material id for cells intersected by fault",
210  false, mat_not_set, "non-negative integer");
211  cmd.add(id_arg);
212 
213  TCLAP::ValueArg<std::string> output_arg(
214  "o", "output", "name of output mesh (*.vtu)", true, "", "string");
215  cmd.add(output_arg);
216 
217  TCLAP::ValueArg<std::string> fault_arg(
218  "f", "fault", "name of mesh representing fault (*.vtu)", true, "",
219  "string");
220  cmd.add(fault_arg);
221 
222  TCLAP::ValueArg<std::string> input_arg(
223  "i", "input",
224  "name of the input file list containing the paths the all input layers "
225  "in correct order from top to bottom",
226  true, "", "string");
227  cmd.add(input_arg);
228  cmd.parse(argc, argv);
229 
230  std::string const input_name = input_arg.getValue();
231  std::string const fault_name = fault_arg.getValue();
232  std::string const output_name = output_arg.getValue();
233 
234  std::unique_ptr<MeshLib::Mesh> mesh(
235  MeshLib::IO::readMeshFromFile(input_name));
236  if (mesh == nullptr)
237  {
238  ERR("Input mesh not found...");
239  return EXIT_FAILURE;
240  }
241  if (!isVoxelGrid(*mesh))
242  {
243  return EXIT_FAILURE;
244  }
245  auto const& mat_ids = MeshLib::materialIDs(*mesh);
246  if (!mat_ids)
247  {
248  ERR("Input mesh has no material IDs");
249  return EXIT_FAILURE;
250  }
251 
252  std::unique_ptr<MeshLib::Mesh> fault(
253  MeshLib::IO::readMeshFromFile(fault_name));
254  if (mesh == nullptr)
255  {
256  ERR("Fault mesh not found...");
257  return EXIT_FAILURE;
258  }
259  if (fault->getDimension() != 2)
260  {
261  ERR("Fault needs to be a 2D mesh.");
262  return EXIT_FAILURE;
263  }
264 
265  int fault_id = id_arg.getValue();
266  if (!id_arg.isSet())
267  {
268  auto it = std::max_element(mat_ids->cbegin(), mat_ids->cend());
269  fault_id = *it + 1;
270  }
271 
272  std::array<double, 3> half_cell_size;
273  {
274  auto const n = *mesh->getElement(0)->getNode(0);
275  auto const c = MeshLib::getCenterOfGravity(*mesh->getElement(0));
276  half_cell_size[0] = std::abs(c[0] - n[0]);
277  half_cell_size[1] = std::abs(c[1] - n[1]);
278  half_cell_size[2] = std::abs(c[2] - n[2]);
279  };
280 
281  markFaults(*mesh, *fault, fault_id, half_cell_size);
282 
283  MeshLib::IO::VtuInterface vtu(mesh.get());
284  vtu.writeToFile(output_name);
285  return EXIT_SUCCESS;
286 }
Definition of the AABB class.
int main(int argc, char *argv[])
static std::string mat_name
bool isVoxelGrid(MeshLib::Mesh const &mesh)
bool testAABBIntersectingPlane(Eigen::Vector3d const &aabb_centre, Eigen::Vector3d const &aabb_extent, Eigen::Vector3d const &plane_normal, double const pd)
bool testTriangleIntersectingAABB(MeshLib::Node const &n0, MeshLib::Node const &n1, MeshLib::Node const &n2, Eigen::Vector3d const &c, Eigen::Vector3d const &e)
void markFaults(MeshLib::Mesh &mesh, MeshLib::Mesh const &fault, int const fault_id, std::array< double, 3 > half_cell_size)
Definition of the Element class.
Git information.
void ERR(char const *fmt, Args const &... args)
Definition: Logging.h:42
Definition of the Mesh class.
Definition of the Node class.
Definition of the Point3d class.
Implementation of the VtuInterface class.
Class AABB is an axis aligned bounding box around a given set of geometric points of (template) type ...
Definition: AABB.h:49
Eigen::Vector3d const & getMinPoint() const
Definition: AABB.h:174
bool containsPoint(T const &pnt, double eps) const
Definition: AABB.h:138
Eigen::Vector3d const & getMaxPoint() const
Definition: AABB.h:181
const T * getCoords() const
Definition: TemplatePoint.h:75
Reads and writes VtkXMLUnstructuredGrid-files (vtu) to and from OGS data structures....
Definition: VtuInterface.h:38
bool writeToFile(std::filesystem::path const &file_path)
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition: Mesh.h:95
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition: Mesh.h:98
Properties & getProperties()
Definition: Mesh.h:123
std::size_t getNumberOfElements() const
Get the number of elements.
Definition: Mesh.h:86
PropertyVector< T > const * getPropertyVector(std::string const &name) const
GITINFOLIB_EXPORT const std::string ogs_version
static const double p
static const double r
MeshLib::Mesh * readMeshFromFile(const std::string &file_name)
PropertyVector< int > const * materialIDs(Mesh const &mesh)
Definition: Mesh.cpp:258
MeshLib::Node getCenterOfGravity(Element const &element)
Calculates the center of gravity for the mesh element.
Definition: Element.cpp:126
Definition of readMeshFromFile function.