GtePlanarMesh.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
10 #include <LowLevel/GteLogger.h>
14 #include <algorithm>
15 #include <vector>
16 
17 // The planar mesh class is convenient for many applications involving
18 // searches for triangles containing a specified point. A couple of
19 // issues can show up in practice when the input data to the constructors
20 // is very large (number of triangles on the order of 10^5 or larger).
21 //
22 // The first constructor builds an ETManifoldMesh mesh that contains
23 // std::map objects. When such maps are large, the amount of time it
24 // takes to delete them is enormous. Although you can control the level
25 // of debug support in MSVS 2013 (see _ITERATOR_DEBUG_LEVEL), turning off
26 // checking might very well affect other classes for which you want
27 // iterator checking to be on. An alternative to reduce debugging time
28 // is to dynamically allocate the PlanarMesh object in the main thread but
29 // then launch another thread to delete the object and avoid stalling
30 // the main thread. For example,
31 //
32 // PlanarMesh<IT,CT,RT>* pmesh =
33 // new PlanarMesh<IT,CT,RT>(numV, vertices, numT, indices);
34 // <make various calls to pmesh>;
35 // std::thread deleter = [pmesh](){ delete pmesh; };
36 // deleter.detach(); // Do not wait for the thread to finish.
37 //
38 // The second constructor has the mesh passed in, but mTriIndexMap is used
39 // in both constructors and can take time to delete.
40 //
41 // The input mesh should be consistently oriented, say, the triangles are
42 // counterclockwise ordered. The vertices should be consistent with this
43 // ordering. However, floating-point rounding errors in generating the
44 // vertices can cause apparent fold-over of the mesh; that is, theoretically
45 // the vertex geometry supports counterclockwise geometry but numerical
46 // errors cause an inconsistency. This can manifest in the mQuery.ToLine
47 // tests whereby cycles of triangles occur in the linear walk. When cycles
48 // occur, GetContainingTriangle(P,startTriangle) will iterate numTriangle
49 // times before reporting that the triangle cannot be found, which is a
50 // very slow process (in debug or release builds). The function
51 // GetContainingTriangle(P,startTriangle,visited) is provided to avoid the
52 // performance loss, trapping a cycle the first time and exiting, but
53 // again reporting that the triangle cannot be found. If you know that the
54 // query should be (theoretically) successful, use the second version of
55 // GetContainingTriangle. If it fails by returning -1, then perform an
56 // exhaustive search over the triangles. For example,
57 //
58 // int triangle = pmesh->GetContainingTriangle(P,startTriangle,visited);
59 // if (triangle >= 0)
60 // {
61 // <take action; for example, compute barycenteric coordinates>;
62 // }
63 // else
64 // {
65 // int numTriangles = pmesh->GetNumTriangles();
66 // for (triangle = 0; triangle < numTriangles; ++triangle)
67 // {
68 // if (pmesh->Contains(triangle, P))
69 // {
70 // <take action>;
71 // break;
72 // }
73 // }
74 // if (triangle == numTriangles)
75 // {
76 // <Triangle still not found, take appropriate action>;
77 // }
78 // }
79 //
80 // The PlanarMesh<*>::Contains function does not require the triangles to
81 // be ordered.
82 
83 namespace gte
84 {
85 
86 template <typename InputType, typename ComputeType, typename RationalType>
88 {
89 public:
90  // Construction. The inputs must represent a manifold mesh of triangles
91  // in the plane. The index array must have 3*numTriangles elements, each
92  // triple of indices representing a triangle in the mesh. Each index is
93  // into the 'vertices' array.
94  PlanarMesh(int numVertices, Vector2<InputType> const* vertices, int numTriangles, int const* indices);
95  PlanarMesh(int numVertices, Vector2<InputType> const* vertices, ETManifoldMesh const& mesh);
96 
97  // Mesh information.
98  inline int GetNumVertices() const;
99  inline int GetNumTriangles() const;
100  inline Vector2<InputType> const* GetVertices() const;
101  inline int const* GetIndices() const;
102  inline int const* GetAdjacencies() const;
103 
104  // Containment queries. The function GetContainingTriangle works
105  // correctly when the planar mesh is a convex set. If the mesh is not
106  // convex, it is possible that the linear-walk search algorithm exits the
107  // mesh before finding a containing triangle. For example, a C-shaped
108  // mesh can contain a point in the top branch of the "C". A starting
109  // point in the bottom branch of the "C" will lead to the search exiting
110  // the bottom branch and having no path to walk to the top branch. If
111  // your mesh is not convex and you want a correct containment query, you
112  // will have to append "outside" triangles to your mesh to form a convex
113  // set.
114  int GetContainingTriangle(Vector2<InputType> const& P, int startTriangle = 0) const;
115  int GetContainingTriangle(Vector2<InputType> const& P, int startTriangle, std::set<int>& visited) const;
116  bool GetVertices(int t, std::array<Vector2<InputType>, 3>& vertices) const;
117  bool GetIndices(int t, std::array<int, 3>& indices) const;
118  bool GetAdjacencies(int t, std::array<int, 3>& adjacencies) const;
119  bool GetBarycentrics(int t, Vector2<InputType> const& P, std::array<InputType, 3>& bary) const;
120  bool Contains(int triangle, Vector2<InputType> const& P) const;
121 
122 public:
123  void CreateVertices(int numVertices, Vector2<InputType> const* vertices);
124 
128  std::vector<int> mIndices;
130  std::map<TriangleKey<true>, int> mTriIndexMap;
131  std::vector<int> mAdjacencies;
132  std::vector<Vector2<ComputeType>> mComputeVertices;
134 };
135 
136 
137 template <typename InputType, typename ComputeType, typename RationalType>
139  Vector2<InputType> const* vertices, int numTriangles, int const* indices)
140  :
141  mNumVertices(0),
142  mVertices(nullptr),
143  mNumTriangles(0)
144 {
145  if (numVertices < 3 || !vertices || numTriangles < 1 || !indices)
146  {
147  LogError("Invalid input.");
148  return;
149  }
150 
151  // Create a mesh in order to get adjacency information.
152  int const* current = indices;
153  for (int t = 0; t < numTriangles; ++t)
154  {
155  int v0 = *current++;
156  int v1 = *current++;
157  int v2 = *current++;
158  if (!mMesh.Insert(v0, v1, v2))
159  {
160  // The 'mesh' object will assert on nonmanifold inputs.
161  return;
162  }
163  }
164 
165  // We have a valid mesh.
166  CreateVertices(numVertices, vertices);
167 
168  // Build the adjacency graph using the triangle ordering implied by the
169  // indices, not the mesh triangle map, to preserve the triangle ordering
170  // of the input indices.
171  mNumTriangles = numTriangles;
172  int const numIndices = 3 * numTriangles;
173  mIndices.resize(numIndices);
174 
175  std::copy(indices, indices + numIndices, mIndices.begin());
176  for (int t = 0, vIndex = 0; t < numTriangles; ++t)
177  {
178  int v0 = indices[vIndex++];
179  int v1 = indices[vIndex++];
180  int v2 = indices[vIndex++];
181  TriangleKey<true> key(v0, v1, v2);
182  mTriIndexMap.insert(std::make_pair(key, t));
183  }
184 
185  mAdjacencies.resize(numIndices);
186  auto const& tmap = mMesh.GetTriangles();
187  for (int t = 0, base = 0; t < numTriangles; ++t, base += 3)
188  {
189  int v0 = indices[base];
190  int v1 = indices[base + 1];
191  int v2 = indices[base + 2];
192  TriangleKey<true> key(v0, v1, v2);
193  auto element = tmap.find(key);
194  for (int i = 0; i < 3; ++i)
195  {
196  auto adj = element->second->T[i].lock();
197  if (adj)
198  {
199  key = TriangleKey<true>(adj->V[0], adj->V[1], adj->V[2]);
200  mAdjacencies[base + i] = mTriIndexMap.find(key)->second;
201  }
202  else
203  {
204  mAdjacencies[base + i] = -1;
205  }
206  }
207  }
208 }
209 
210 template <typename InputType, typename ComputeType, typename RationalType>
212  Vector2<InputType> const* vertices, ETManifoldMesh const& mesh)
213  :
214  mNumVertices(0),
215  mVertices(nullptr),
216  mNumTriangles(0)
217 {
218  if (numVertices < 3 || !vertices || mesh.GetTriangles().size() < 1)
219  {
220  LogError("Invalid input.");
221  return;
222  }
223 
224  // We have a valid mesh.
225  CreateVertices(numVertices, vertices);
226 
227  // Build the adjacency graph using the triangle ordering implied by the
228  // mesh triangle map.
229  auto const& tmap = mesh.GetTriangles();
230  mNumTriangles = static_cast<int>(tmap.size());
231  mIndices.resize(3 * mNumTriangles);
232 
233  int tIndex = 0, vIndex = 0;
234  for (auto const& element : tmap)
235  {
236  mTriIndexMap.insert(std::make_pair(element.first, tIndex++));
237  for (int i = 0; i < 3; ++i, ++vIndex)
238  {
239  mIndices[vIndex] = element.second->V[i];
240  }
241  }
242 
243  mAdjacencies.resize(3 * mNumTriangles);
244  vIndex = 0;
245  for (auto const& element : tmap)
246  {
247  for (int i = 0; i < 3; ++i, ++vIndex)
248  {
249  auto adj = element.second->T[i].lock();
250  if (adj)
251  {
252  TriangleKey<true> key(adj->V[0], adj->V[1], adj->V[2]);
253  mAdjacencies[vIndex] = mTriIndexMap.find(key)->second;
254  }
255  else
256  {
257  mAdjacencies[vIndex] = -1;
258  }
259  }
260  }
261 }
262 
263 template <typename InputType, typename ComputeType, typename RationalType>
266 {
267  return mNumVertices;
268 }
269 
270 template <typename InputType, typename ComputeType, typename RationalType>
272 {
273  return mNumTriangles;
274 }
275 
276 template <typename InputType, typename ComputeType, typename RationalType>
278 {
279  return mVertices;
280 }
281 
282 template <typename InputType, typename ComputeType, typename RationalType>
284 {
285  return &mIndices[0];
286 }
287 
288 template <typename InputType, typename ComputeType, typename RationalType>
290 {
291  return &mAdjacencies[0];
292 }
293 
294 template <typename InputType, typename ComputeType, typename RationalType>
296  Vector2<InputType> const& P, int startTriangle) const
297 {
298  Vector2<ComputeType> test{ P[0], P[1] };
299 
300  // Use triangle edges as binary separating lines.
301  int triangle = startTriangle;
302  for (int i = 0; i < mNumTriangles; ++i)
303  {
304  int ibase = 3 * triangle;
305  int const* v = &mIndices[ibase];
306 
307  if (mQuery.ToLine(test, v[0], v[1]) > 0)
308  {
309  triangle = mAdjacencies[ibase];
310  if (triangle == -1)
311  {
312  return -1;
313  }
314  continue;
315  }
316 
317  if (mQuery.ToLine(test, v[1], v[2]) > 0)
318  {
319  triangle = mAdjacencies[ibase + 1];
320  if (triangle == -1)
321  {
322  return -1;
323  }
324  continue;
325  }
326 
327  if (mQuery.ToLine(test, v[2], v[0]) > 0)
328  {
329  triangle = mAdjacencies[ibase + 2];
330  if (triangle == -1)
331  {
332  return -1;
333  }
334  continue;
335  }
336 
337  return triangle;
338  }
339 
340  return -1;
341 }
342 
343 template <typename InputType, typename ComputeType, typename RationalType>
345  Vector2<InputType> const& P, int startTriangle, std::set<int>& visited) const
346 {
347  Vector2<ComputeType> test{ P[0], P[1] };
348  visited.clear();
349 
350  // Use triangle edges as binary separating lines.
351  int triangle = startTriangle;
352  for (int i = 0; i < mNumTriangles; ++i)
353  {
354  visited.insert(triangle);
355  int ibase = 3 * triangle;
356  int const* v = &mIndices[ibase];
357 
358  if (mQuery.ToLine(test, v[0], v[1]) > 0)
359  {
360  triangle = mAdjacencies[ibase];
361  if (triangle == -1 || visited.find(triangle) != visited.end())
362  {
363  return -1;
364  }
365  continue;
366  }
367 
368  if (mQuery.ToLine(test, v[1], v[2]) > 0)
369  {
370  triangle = mAdjacencies[ibase + 1];
371  if (triangle == -1 || visited.find(triangle) != visited.end())
372  {
373  return -1;
374  }
375  continue;
376  }
377 
378  if (mQuery.ToLine(test, v[2], v[0]) > 0)
379  {
380  triangle = mAdjacencies[ibase + 2];
381  if (triangle == -1 || visited.find(triangle) != visited.end())
382  {
383  return -1;
384  }
385  continue;
386  }
387 
388  return triangle;
389  }
390 
391  return -1;
392 }
393 
394 template <typename InputType, typename ComputeType, typename RationalType>
396  int t, std::array<Vector2<InputType>, 3>& vertices) const
397 {
398  if (0 <= t && t < mNumTriangles)
399  {
400  for (int i = 0, vIndex = 3 * t; i < 3; ++i, ++vIndex)
401  {
402  vertices[i] = mVertices[mIndices[vIndex]];
403  }
404  return true;
405  }
406  return false;
407 }
408 
409 template <typename InputType, typename ComputeType, typename RationalType>
411  int t, std::array<int, 3>& indices) const
412 {
413  if (0 <= t && t < mNumTriangles)
414  {
415  for (int i = 0, vIndex = 3 * t; i < 3; ++i, ++vIndex)
416  {
417  indices[i] = mIndices[vIndex];
418  }
419  return true;
420  }
421  return false;
422 }
423 
424 template <typename InputType, typename ComputeType, typename RationalType>
426  int t, std::array<int, 3>& adjacencies) const
427 {
428  if (0 <= t && t < mNumTriangles)
429  {
430  for (int i = 0, vIndex = 3 * t; i < 3; ++i, ++vIndex)
431  {
432  adjacencies[i] = mAdjacencies[vIndex];
433  }
434  return true;
435  }
436  return false;
437 }
438 
439 template <typename InputType, typename ComputeType, typename RationalType>
441  int t, Vector2<InputType> const& P, std::array<InputType, 3>& bary) const
442 {
443  std::array<int, 3> indices;
444  if (GetIndices(t, indices))
445  {
446  Vector2<RationalType> rtP{ P[0], P[1] };
447  std::array<Vector2<RationalType>, 3> rtV;
448  for (int i = 0; i < 3; ++i)
449  {
450  Vector2<ComputeType> const& V = mComputeVertices[indices[i]];
451  for (int j = 0; j < 2; ++j)
452  {
453  rtV[i][j] = (RationalType)V[j];
454  }
455  };
456 
457  RationalType rtBary[3];
458  if (ComputeBarycentrics(rtP, rtV[0], rtV[1], rtV[2], rtBary))
459  {
460  for (int i = 0; i < 3; ++i)
461  {
462  bary[i] = (InputType)rtBary[i];
463  }
464  return true;
465  }
466  }
467  return false;
468 }
469 
470 template <typename InputType, typename ComputeType, typename RationalType>
472  int triangle, Vector2<InputType> const& P) const
473 {
474  Vector2<ComputeType> test{ P[0], P[1] };
476  v[0] = mComputeVertices[mIndices[3 * triangle + 0]];
477  v[1] = mComputeVertices[mIndices[3 * triangle + 1]];
478  v[2] = mComputeVertices[mIndices[3 * triangle + 2]];
480  return pip.Contains(test);
481 }
482 
483 template <typename InputType, typename ComputeType, typename RationalType>
485  int numVertices, Vector2<InputType> const* vertices)
486 {
487  mNumVertices = numVertices;
488  mVertices = vertices;
490  for (int i = 0; i < mNumVertices; ++i)
491  {
492  for (int j = 0; j < 2; ++j)
493  {
494  mComputeVertices[i][j] = (ComputeType)mVertices[i][j];
495  }
496  }
497  mQuery.Set(mNumVertices, &mComputeVertices[0]);
498 }
499 
500 }
std::vector< Vector2< ComputeType > > mComputeVertices
int GetNumTriangles() const
void Set(int numVertices, Vector2< Real > const *vertices)
int GetNumVertices() const
bool Contains(int triangle, Vector2< InputType > const &P) const
Vector2< InputType > const * GetVertices() const
GLfloat GLfloat v1
Definition: glcorearb.h:812
PrimalQuery2< ComputeType > mQuery
std::map< TriangleKey< true >, int > mTriIndexMap
int GetContainingTriangle(Vector2< InputType > const &P, int startTriangle=0) const
bool Contains(Vector2< Real > const &p) const
int ToLine(int i, int v0, int v1) const
ETManifoldMesh mMesh
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
#define LogError(message)
Definition: GteLogger.h:92
void CreateVertices(int numVertices, Vector2< InputType > const *vertices)
int const * GetIndices() const
bool GetBarycentrics(int t, Vector2< InputType > const &P, std::array< InputType, 3 > &bary) const
virtual std::shared_ptr< Triangle > Insert(int v0, int v1, int v2)
GLfloat v0
Definition: glcorearb.h:811
GLdouble GLdouble t
Definition: glext.h:239
bool ComputeBarycentrics(Vector2< Real > const &p, Vector2< Real > const &v0, Vector2< Real > const &v1, Vector2< Real > const &v2, Real bary[3], Real epsilon=(Real) 0)
Definition: GteVector2.h:135
TMap const & GetTriangles() const
GLenum array
Definition: glext.h:6669
const GLdouble * v
Definition: glcorearb.h:832
GLfloat GLfloat GLfloat v2
Definition: glcorearb.h:813
int const * GetAdjacencies() const
std::vector< int > mAdjacencies
Vector2< InputType > const * mVertices
PlanarMesh(int numVertices, Vector2< InputType > const *vertices, int numTriangles, int const *indices)
std::vector< int > mIndices


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:01