GteDelaunay3.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>
13 #include <Mathematics/GteLine.h>
15 #include <vector>
16 
17 // Delaunay tetrahedralization of points (intrinsic dimensionality 3).
18 // VQ = number of vertices
19 // V = array of vertices
20 // TQ = number of tetrahedra
21 // I = Array of 4-tuples of indices into V that represent the tetrahedra
22 // (4*TQ total elements). Access via GetIndices(*).
23 // A = Array of 4-tuples of indices into I that represent the adjacent
24 // tetrahedra (4*TQ total elements). Access via GetAdjacencies(*).
25 // The i-th tetrahedron has vertices
26 // vertex[0] = V[I[4*i+0]]
27 // vertex[1] = V[I[4*i+1]]
28 // vertex[2] = V[I[4*i+2]]
29 // vertex[3] = V[I[4*i+3]]
30 // and face index triples listed below. The face vertex ordering when
31 // viewed from outside the tetrahedron is counterclockwise.
32 // face[0] = <I[4*i+1],I[4*i+2],I[4*i+3]>
33 // face[1] = <I[4*i+0],I[4*i+3],I[4*i+2]>
34 // face[2] = <I[4*i+0],I[4*i+1],I[4*i+3]>
35 // face[3] = <I[4*i+0],I[4*i+2],I[4*i+1]>
36 // The tetrahedra adjacent to these faces have indices
37 // adjacent[0] = A[4*i+0] is the tetrahedron opposite vertex[0], so it
38 // is the tetrahedron sharing face[0].
39 // adjacent[1] = A[4*i+1] is the tetrahedron opposite vertex[1], so it
40 // is the tetrahedron sharing face[1].
41 // adjacent[2] = A[4*i+2] is the tetrahedron opposite vertex[2], so it
42 // is the tetrahedron sharing face[2].
43 // adjacent[3] = A[4*i+3] is the tetrahedron opposite vertex[3], so it
44 // is the tetrahedron sharing face[3].
45 // If there is no adjacent tetrahedron, the A[*] value is set to -1. The
46 // tetrahedron adjacent to face[j] has vertices
47 // adjvertex[0] = V[I[4*adjacent[j]+0]]
48 // adjvertex[1] = V[I[4*adjacent[j]+1]]
49 // adjvertex[2] = V[I[4*adjacent[j]+2]]
50 // adjvertex[3] = V[I[4*adjacent[j]+3]]
51 // The only way to ensure a correct result for the input vertices (assumed to
52 // be exact) is to choose ComputeType for exact rational arithmetic. You may
53 // use BSNumber. No divisions are performed in this computation, so you do
54 // not have to use BSRational.
55 //
56 // The worst-case choices of N for Real of type BSNumber or BSRational with
57 // integer storage UIntegerFP32<N> are listed in the next table. The numerical
58 // computations are encapsulated in PrimalQuery3<Real>::ToPlane and
59 // PrimalQuery3<Real>::ToCircumsphere, the latter query the dominant one in
60 // determining N. We recommend using only BSNumber, because no divisions are
61 // performed in the convex-hull computations.
62 //
63 // input type | compute type | N
64 // -----------+--------------+--------
65 // float | BSNumber | 44
66 // float | BSRational | 329
67 // double | BSNumber | 298037
68 // double | BSRational | 2254442
69 
70 namespace gte
71 {
72 
73 template <typename InputType, typename ComputeType>
74 class Delaunay3
75 {
76 public:
77  // The class is a functor to support computing the Delaunay
78  // tetrahedralization of multiple data sets using the same class object.
79  Delaunay3();
80 
81  // The input is the array of vertices whose Delaunay tetrahedralization
82  // is required. The epsilon value is used to determine the intrinsic
83  // dimensionality of the vertices (d = 0, 1, 2, or 3). When epsilon is
84  // positive, the determination is fuzzy--vertices approximately the same
85  // point, approximately on a line, approximately planar, or volumetric.
86  bool operator()(int numVertices, Vector3<InputType> const* vertices, InputType epsilon);
87 
88  // Dimensional information. If GetDimension() returns 1, the points lie
89  // on a line P+t*D (fuzzy comparison when epsilon > 0). You can sort
90  // these if you need a polyline output by projecting onto the line each
91  // vertex X = P+t*D, where t = Dot(D,X-P). If GetDimension() returns 2,
92  // the points line on a plane P+s*U+t*V (fuzzy comparison when
93  // epsilon > 0). You can project each vertex X = P+s*U+t*V, where
94  // s = Dot(U,X-P) and t = Dot(V,X-P), then apply Delaunay2 to the (s,t)
95  // tuples.
96  inline InputType GetEpsilon() const;
97  inline int GetDimension() const;
98  inline Line3<InputType> const& GetLine() const;
99  inline Plane3<InputType> const& GetPlane() const;
100 
101  // Member access.
102  inline int GetNumVertices() const;
103  inline int GetNumUniqueVertices() const;
104  inline int GetNumTetrahedra() const;
105  inline Vector3<InputType> const* GetVertices() const;
106  inline PrimalQuery3<ComputeType> const& GetQuery() const;
107  inline TSManifoldMesh const& GetGraph() const;
108  inline std::vector<int> const& GetIndices() const;
109  inline std::vector<int> const& GetAdjacencies() const;
110 
111  // Locate those tetrahedra faces that do not share other tetrahedra. The
112  // returned array has hull.size() = 3*numFaces indices, each triple
113  // representing a triangle. The triangles are counterclockwise ordered
114  // when viewed from outside the hull. The return value is 'true' iff the
115  // dimension is 3.
116  bool GetHull(std::vector<int>& hull) const;
117 
118  // Get the vertex indices for tetrahedron i. The function returns 'true'
119  // when the dimension is 3 and i is a valid tetrahedron index, in which
120  // case the vertices are valid; otherwise, the function returns 'false'
121  // and the vertices are invalid.
122  bool GetIndices(int i, std::array<int, 4>& indices) const;
123 
124  // Get the indices for tetrahedra adjacent to tetrahedron i. The function
125  // returns 'true' when the dimension is 3 and i is a valid tetrahedron
126  // index, in which case the adjacencies are valid; otherwise, the function
127  // returns 'false' and the adjacencies are invalid.
128  bool GetAdjacencies(int i, std::array<int, 4>& adjacencies) const;
129 
130  // Support for searching the tetrahedralization for a tetrahedron that
131  // contains a point. If there is a containing tetrahedron, the returned
132  // value is a tetrahedron index i with 0 <= i < GetNumTetrahedra(). If
133  // there is not a containing tetrahedron, -1 is returned. The
134  // computations are performed using exact rational arithmetic.
135  //
136  // The SearchInfo input stores information about the tetrahedron search
137  // when looking for the tetrahedron (if any) that contains p. The first
138  // tetrahedron searched is 'initialTetrahedron'. On return 'path' stores
139  // those (ordered) tetrahedron indices visited during the search. The
140  // last visited tetrahedron has index 'finalTetrahedron and vertex indices
141  // 'finalV[0,1,2,3]', stored in volumetric counterclockwise order. The
142  // last face of the search is <finalV[0],finalV[1],finalV[2]>. For
143  // spatially coherent inputs p for numerous calls to this function, you
144  // will want to specify 'finalTetrahedron' from the previous call as
145  // 'initialTetrahedron' for the next call, which should reduce search
146  // times.
147  struct SearchInfo
148  {
150  int numPath;
151  std::vector<int> path;
153  std::array<int, 4> finalV;
154  };
155  int GetContainingTetrahedron(Vector3<InputType> const& p, SearchInfo& info) const;
156 
157 private:
158  // Support for incremental Delaunay tetrahedralization.
160  bool GetContainingTetrahedron(int i, std::shared_ptr<Tetrahedron>& tetra) const;
161  bool GetAndRemoveInsertionPolyhedron(int i, std::set<std::shared_ptr<Tetrahedron>>& candidates,
162  std::set<TriangleKey<true>>& boundary);
163  bool Update(int i);
164 
165  // The epsilon value is used for fuzzy determination of intrinsic
166  // dimensionality. If the dimension is 0, 1, or 2, the constructor
167  // returns early. The caller is responsible for retrieving the dimension
168  // and taking an alternate path should the dimension be smaller than 3.
169  // If the dimension is 0, the caller may as well treat all vertices[]
170  // as a single point, say, vertices[0]. If the dimension is 1, the
171  // caller can query for the approximating line and project vertices[]
172  // onto it for further processing. If the dimension is 2, the caller can
173  // query for the approximating plane and project vertices[] onto it for
174  // further processing.
175  InputType mEpsilon;
179 
180  // The array of vertices used for geometric queries. If you want to be
181  // certain of a correct result, choose ComputeType to be BSNumber.
182  std::vector<Vector3<ComputeType>> mComputeVertices;
184 
185  // The graph information.
191  std::vector<int> mIndices;
192  std::vector<int> mAdjacencies;
193 };
194 
195 
196 template <typename InputType, typename ComputeType>
198  :
199  mEpsilon((InputType)0),
200  mDimension(0),
201  mLine(Vector3<InputType>::Zero(), Vector3<InputType>::Zero()),
202  mPlane(Vector3<InputType>::Zero(), (InputType)0),
203  mNumVertices(0),
205  mNumTetrahedra(0),
206  mVertices(nullptr)
207 {
208 }
209 
210 template <typename InputType, typename ComputeType>
212  Vector3<InputType> const* vertices, InputType epsilon)
213 {
214  mEpsilon = std::max(epsilon, (InputType)0);
215  mDimension = 0;
216  mLine.origin = Vector3<InputType>::Zero();
217  mLine.direction = Vector3<InputType>::Zero();
218  mPlane.normal = Vector3<InputType>::Zero();
219  mPlane.constant = (InputType)0;
220  mNumVertices = numVertices;
221  mNumUniqueVertices = 0;
222  mNumTetrahedra = 0;
223  mVertices = vertices;
225  mIndices.clear();
226  mAdjacencies.clear();
227 
228  int i, j;
229  if (mNumVertices < 4)
230  {
231  // Delaunay3 should be called with at least four points.
232  return false;
233  }
234 
236  if (info.dimension == 0)
237  {
238  // mDimension is 0; mGraph, mIndices, and mAdjacencies are empty
239  return false;
240  }
241 
242  if (info.dimension == 1)
243  {
244  // The set is (nearly) collinear.
245  mDimension = 1;
246  mLine = Line3<InputType>(info.origin, info.direction[0]);
247  return false;
248  }
249 
250  if (info.dimension == 2)
251  {
252  // The set is (nearly) coplanar.
253  mDimension = 2;
255  info.direction[1]), info.origin);
256  return false;
257  }
258 
259  mDimension = 3;
260 
261  // Compute the vertices for the queries.
264  for (i = 0; i < mNumVertices; ++i)
265  {
266  for (j = 0; j < 3; ++j)
267  {
268  mComputeVertices[i][j] = vertices[i][j];
269  }
270  }
271 
272  // Insert the (nondegenerate) tetrahedron constructed by the call to
273  // GetInformation. This is necessary for the circumsphere-visibility
274  // algorithm to work correctly.
275  if (!info.extremeCCW)
276  {
277  std::swap(info.extreme[2], info.extreme[3]);
278  }
279  if (!mGraph.Insert(info.extreme[0], info.extreme[1], info.extreme[2], info.extreme[3]))
280  {
281  return false;
282  }
283 
284  // Incrementally update the tetrahedralization. The set of processed
285  // points is maintained to eliminate duplicates, either in the original
286  // input points or in the points obtained by snap rounding.
287  std::set<Vector3<InputType>> processed;
288  for (i = 0; i < 4; ++i)
289  {
290  processed.insert(vertices[info.extreme[i]]);
291  }
292  for (i = 0; i < mNumVertices; ++i)
293  {
294  if (processed.find(vertices[i]) == processed.end())
295  {
296  if (!Update(i))
297  {
298  // A failure can occur if ComputeType is not an exact
299  // arithmetic type.
300  return false;
301  }
302  processed.insert(vertices[i]);
303  }
304  }
305  mNumUniqueVertices = static_cast<int>(processed.size());
306 
307  // Assign integer values to the tetrahedra for use by the caller.
308  std::map<std::shared_ptr<Tetrahedron>, int> permute;
309  i = -1;
310  permute[nullptr] = i++;
311  for (auto const& element : mGraph.GetTetrahedra())
312  {
313  permute[element.second] = i++;
314  }
315 
316  // Put Delaunay tetrahedra into an array (vertices and adjacency info).
317  mNumTetrahedra = static_cast<int>(mGraph.GetTetrahedra().size());
318  int numIndices = 4 * mNumTetrahedra;
319  if (mNumTetrahedra > 0)
320  {
321  mIndices.resize(numIndices);
322  mAdjacencies.resize(numIndices);
323  i = 0;
324  for (auto const& element : mGraph.GetTetrahedra())
325  {
326  std::shared_ptr<Tetrahedron> tetra = element.second;
327  for (j = 0; j < 4; ++j, ++i)
328  {
329  mIndices[i] = tetra->V[j];
330  mAdjacencies[i] = permute[tetra->S[j].lock()];
331  }
332  }
333  }
334 
335  return true;
336 }
337 
338 template <typename InputType, typename ComputeType> inline
340 {
341  return mEpsilon;
342 }
343 
344 template <typename InputType, typename ComputeType> inline
346 {
347  return mDimension;
348 }
349 
350 template <typename InputType, typename ComputeType> inline
352 {
353  return mLine;
354 }
355 
356 template <typename InputType, typename ComputeType> inline
358 {
359  return mPlane;
360 }
361 
362 template <typename InputType, typename ComputeType> inline
364 {
365  return mNumVertices;
366 }
367 
368 template <typename InputType, typename ComputeType> inline
370 {
371  return mNumUniqueVertices;
372 }
373 
374 template <typename InputType, typename ComputeType> inline
376 {
377  return mNumTetrahedra;
378 }
379 
380 template <typename InputType, typename ComputeType> inline
382 {
383  return mVertices;
384 }
385 
386 template <typename InputType, typename ComputeType> inline
388 {
389  return mQuery;
390 }
391 
392 template <typename InputType, typename ComputeType> inline
394 {
395  return mGraph;
396 }
397 
398 template <typename InputType, typename ComputeType> inline
399 std::vector<int> const& Delaunay3<InputType, ComputeType>::GetIndices() const
400 {
401  return mIndices;
402 }
403 
404 template <typename InputType, typename ComputeType> inline
406 {
407  return mAdjacencies;
408 }
409 
410 template <typename InputType, typename ComputeType>
411 bool Delaunay3<InputType, ComputeType>::GetHull(std::vector<int>& hull) const
412 {
413  if (mDimension == 3)
414  {
415  // Count the number of triangles that are not shared by two
416  // tetrahedra.
417  int numTriangles = 0;
418  for (auto adj : mAdjacencies)
419  {
420  if (adj == -1)
421  {
422  ++numTriangles;
423  }
424  }
425 
426  if (numTriangles > 0)
427  {
428  // Enumerate the triangles. The prototypical case is the single
429  // tetrahedron V[0] = (0,0,0), V[1] = (1,0,0), V[2] = (0,1,0), and
430  // V[3] = (0,0,1) with no adjacent tetrahedra. The mIndices[]
431  // array is <0,1,2,3>.
432  // i = 0, face = 0:
433  // skip index 0, <x,1,2,3>, no swap, triangle = <1,2,3>
434  // i = 1, face = 1:
435  // skip index 1, <0,x,2,3>, swap, triangle = <0,3,2>
436  // i = 2, face = 2:
437  // skip index 2, <0,1,x,3>, no swap, triangle = <0,1,3>
438  // i = 3, face = 3:
439  // skip index 3, <0,1,2,x>, swap, triangle = <0,2,1>
440  // To guarantee counterclockwise order of triangles when viewed
441  // outside the tetrahedron, the swap of the last two indices
442  // occurs when face is an odd number; (face % 2) != 0.
443  hull.resize(3 * numTriangles);
444  int current = 0, i = 0;
445  for (auto adj : mAdjacencies)
446  {
447  if (adj == -1)
448  {
449  int tetra = i / 4, face = i % 4;
450  for (int j = 0; j < 4; ++j)
451  {
452  if (j != face)
453  {
454  hull[current++] = mIndices[4 * tetra + j];
455  }
456  }
457  if ((face % 2) != 0)
458  {
459  std::swap(hull[current - 1], hull[current - 2]);
460  }
461  }
462  ++i;
463  }
464  return true;
465  }
466  else
467  {
468  LogError("Unexpected. There must be at least one tetrahedron.");
469  }
470  }
471  else
472  {
473  LogError("The dimension must be 3.");
474  }
475  return false;
476 }
477 
478 template <typename InputType, typename ComputeType>
479 bool Delaunay3<InputType, ComputeType>::GetIndices(int i, std::array<int, 4>& indices) const
480 {
481  if (mDimension == 3)
482  {
483  int numTetrahedra = static_cast<int>(mIndices.size() / 4);
484  if (0 <= i && i < numTetrahedra)
485  {
486  indices[0] = mIndices[4 * i];
487  indices[1] = mIndices[4 * i + 1];
488  indices[2] = mIndices[4 * i + 2];
489  indices[3] = mIndices[4 * i + 3];
490  return true;
491  }
492  }
493  else
494  {
495  LogError("The dimension must be 3.");
496  }
497  return false;
498 }
499 
500 template <typename InputType, typename ComputeType>
501 bool Delaunay3<InputType, ComputeType>::GetAdjacencies(int i, std::array<int, 4>& adjacencies) const
502 {
503  if (mDimension == 3)
504  {
505  int numTetrahedra = static_cast<int>(mIndices.size() / 4);
506  if (0 <= i && i < numTetrahedra)
507  {
508  adjacencies[0] = mAdjacencies[4 * i];
509  adjacencies[1] = mAdjacencies[4 * i + 1];
510  adjacencies[2] = mAdjacencies[4 * i + 2];
511  adjacencies[3] = mAdjacencies[4 * i + 3];
512  return true;
513  }
514  }
515  else
516  {
517  LogError("The dimension must be 3.");
518  }
519  return false;
520 }
521 
522 template <typename InputType, typename ComputeType>
524 {
525  if (mDimension == 3)
526  {
527  Vector3<ComputeType> test{ p[0], p[1], p[2] };
528 
529  int numTetrahedra = static_cast<int>(mIndices.size() / 4);
530  info.path.resize(numTetrahedra);
531  info.numPath = 0;
532  int tetrahedron;
533  if (0 <= info.initialTetrahedron
534  && info.initialTetrahedron < numTetrahedra)
535  {
536  tetrahedron = info.initialTetrahedron;
537  }
538  else
539  {
540  info.initialTetrahedron = 0;
541  tetrahedron = 0;
542  }
543 
544  // Use tetrahedron faces as binary separating planes.
545  for (int i = 0; i < numTetrahedra; ++i)
546  {
547  int ibase = 4 * tetrahedron;
548  int const* v = &mIndices[ibase];
549 
550  info.path[info.numPath++] = tetrahedron;
551  info.finalTetrahedron = tetrahedron;
552  info.finalV[0] = v[0];
553  info.finalV[1] = v[1];
554  info.finalV[2] = v[2];
555  info.finalV[3] = v[3];
556 
557  // <V1,V2,V3> counterclockwise when viewed outside tetrahedron.
558  if (mQuery.ToPlane(test, v[1], v[2], v[3]) > 0)
559  {
560  tetrahedron = mAdjacencies[ibase];
561  if (tetrahedron == -1)
562  {
563  info.finalV[0] = v[1];
564  info.finalV[1] = v[2];
565  info.finalV[2] = v[3];
566  info.finalV[3] = v[0];
567  return -1;
568  }
569  continue;
570  }
571 
572  // <V0,V3,V2> counterclockwise when viewed outside tetrahedron.
573  if (mQuery.ToPlane(test, v[0], v[2], v[3]) < 0)
574  {
575  tetrahedron = mAdjacencies[ibase + 1];
576  if (tetrahedron == -1)
577  {
578  info.finalV[0] = v[0];
579  info.finalV[1] = v[2];
580  info.finalV[2] = v[3];
581  info.finalV[3] = v[1];
582  return -1;
583  }
584  continue;
585  }
586 
587  // <V0,V1,V3> counterclockwise when viewed outside tetrahedron.
588  if (mQuery.ToPlane(test, v[0], v[1], v[3]) > 0)
589  {
590  tetrahedron = mAdjacencies[ibase + 2];
591  if (tetrahedron == -1)
592  {
593  info.finalV[0] = v[0];
594  info.finalV[1] = v[1];
595  info.finalV[2] = v[3];
596  info.finalV[3] = v[2];
597  return -1;
598  }
599  continue;
600  }
601 
602  // <V0,V2,V1> counterclockwise when viewed outside tetrahedron.
603  if (mQuery.ToPlane(test, v[0], v[1], v[2]) < 0)
604  {
605  tetrahedron = mAdjacencies[ibase + 3];
606  if (tetrahedron == -1)
607  {
608  info.finalV[0] = v[0];
609  info.finalV[1] = v[1];
610  info.finalV[2] = v[2];
611  info.finalV[3] = v[3];
612  return -1;
613  }
614  continue;
615  }
616 
617  return tetrahedron;
618  }
619  }
620  else
621  {
622  LogError("The dimension must be 3.");
623  }
624  return -1;
625 }
626 
627 template <typename InputType, typename ComputeType>
628 bool Delaunay3<InputType, ComputeType>::GetContainingTetrahedron(int i, std::shared_ptr<Tetrahedron>& tetra) const
629 {
630  int numTetrahedra = static_cast<int>(mGraph.GetTetrahedra().size());
631  for (int t = 0; t < numTetrahedra; ++t)
632  {
633  int j;
634  for (j = 0; j < 4; ++j)
635  {
636  auto const& opposite = TetrahedronKey<true>::oppositeFace;
637  int v0 = tetra->V[opposite[j][0]];
638  int v1 = tetra->V[opposite[j][1]];
639  int v2 = tetra->V[opposite[j][2]];
640  if (mQuery.ToPlane(i, v0, v1, v2) > 0)
641  {
642  // Point i sees face <v0,v1,v2> from outside the tetrahedron.
643  auto adjTetra = tetra->S[j].lock();
644  if (adjTetra)
645  {
646  // Traverse to the tetrahedron sharing the face.
647  tetra = adjTetra;
648  break;
649  }
650  else
651  {
652  // We reached a hull face, so the point is outside the
653  // hull. TODO: Once a hull data structure is in place,
654  // return tetra->S[j] as the candidate for starting a
655  // search for visible hull faces.
656  return false;
657  }
658  }
659 
660  }
661 
662  if (j == 4)
663  {
664  // The point is inside all four faces, so the point is inside
665  // a tetrahedron.
666  return true;
667  }
668  }
669 
670  LogError("Unexpected termination of GetContainingTetrahedron.");
671  return false;
672 }
673 
674 template <typename InputType, typename ComputeType>
676  std::set<std::shared_ptr<Tetrahedron>>& candidates, std::set<TriangleKey<true>>& boundary)
677 {
678  // Locate the tetrahedra that make up the insertion polyhedron.
679  TSManifoldMesh polyhedron;
680  while (candidates.size() > 0)
681  {
682  std::shared_ptr<Tetrahedron> tetra = *candidates.begin();
683  candidates.erase(candidates.begin());
684 
685  for (int j = 0; j < 4; ++j)
686  {
687  auto adj = tetra->S[j].lock();
688  if (adj && candidates.find(adj) == candidates.end())
689  {
690  int a0 = adj->V[0];
691  int a1 = adj->V[1];
692  int a2 = adj->V[2];
693  int a3 = adj->V[3];
694  if (mQuery.ToCircumsphere(i, a0, a1, a2, a3) <= 0)
695  {
696  // Point i is in the circumsphere.
697  candidates.insert(adj);
698  }
699  }
700  }
701 
702  int v0 = tetra->V[0];
703  int v1 = tetra->V[1];
704  int v2 = tetra->V[2];
705  int v3 = tetra->V[3];
706  if (!polyhedron.Insert(v0, v1, v2, v3))
707  {
708  return false;
709  }
710  if (!mGraph.Remove(v0, v1, v2, v3))
711  {
712  return false;
713  }
714  }
715 
716  // Get the boundary triangles of the insertion polyhedron.
717  for (auto const& element : polyhedron.GetTetrahedra())
718  {
719  std::shared_ptr<Tetrahedron> tetra = element.second;
720  for (int j = 0; j < 4; ++j)
721  {
722  if (!tetra->S[j].lock())
723  {
724  auto const& opposite = TetrahedronKey<true>::oppositeFace;
725  int v0 = tetra->V[opposite[j][0]];
726  int v1 = tetra->V[opposite[j][1]];
727  int v2 = tetra->V[opposite[j][2]];
728  boundary.insert(TriangleKey<true>(v0, v1, v2));
729  }
730  }
731  }
732  return true;
733 }
734 
735 template <typename InputType, typename ComputeType>
737 {
738  auto const& smap = mGraph.GetTetrahedra();
739  std::shared_ptr<Tetrahedron> tetra = smap.begin()->second;
740  if (GetContainingTetrahedron(i, tetra))
741  {
742  // The point is inside the convex hull. The insertion polyhedron
743  // contains only tetrahedra in the current tetrahedralization; the
744  // hull does not change.
745 
746  // Use a depth-first search for those tetrahedra whose circumspheres
747  // contain point i.
748  std::set<std::shared_ptr<Tetrahedron>> candidates;
749  candidates.insert(tetra);
750 
751  // Get the boundary of the insertion polyhedron C that contains the
752  // tetrahedra whose circumspheres contain point i. C contains the
753  // point i.
754  std::set<TriangleKey<true>> boundary;
755  if (!GetAndRemoveInsertionPolyhedron(i, candidates, boundary))
756  {
757  return false;
758  }
759 
760  // The insertion polyhedron consists of the tetrahedra formed by
761  // point i and the faces of C.
762  for (auto const& key : boundary)
763  {
764  int v0 = key.V[0];
765  int v1 = key.V[1];
766  int v2 = key.V[2];
767  if (mQuery.ToPlane(i, v0, v1, v2) < 0)
768  {
769  if (!mGraph.Insert(i, v0, v1, v2))
770  {
771  return false;
772  }
773  }
774  // else: Point i is on an edge or face of 'tetra', so the
775  // subdivision has degenerate tetrahedra. Ignore these.
776  }
777  }
778  else
779  {
780  // The point is outside the convex hull. The insertion polyhedron
781  // is formed by point i and any tetrahedra in the current
782  // tetrahedralization whose circumspheres contain point i.
783 
784  // Locate the convex hull of the tetrahedra. TODO: Maintain a hull
785  // data structure that is updated incrementally.
786  std::set<TriangleKey<true>> hull;
787  for (auto const& element : smap)
788  {
789  std::shared_ptr<Tetrahedron> t = element.second;
790  for (int j = 0; j < 4; ++j)
791  {
792  if (!t->S[j].lock())
793  {
794  auto const& opposite = TetrahedronKey<true>::oppositeFace;
795  int v0 = t->V[opposite[j][0]];
796  int v1 = t->V[opposite[j][1]];
797  int v2 = t->V[opposite[j][2]];
798  hull.insert(TriangleKey<true>(v0, v1, v2));
799  }
800  }
801  }
802 
803  // TODO: Until the hull change, for now just iterate over all the
804  // hull faces and use the ones visible to point i to locate the
805  // insertion polyhedron.
806  auto const& tmap = mGraph.GetTriangles();
807  std::set<std::shared_ptr<Tetrahedron>> candidates;
808  std::set<TriangleKey<true>> visible;
809  for (auto const& key : hull)
810  {
811  int v0 = key.V[0];
812  int v1 = key.V[1];
813  int v2 = key.V[2];
814  if (mQuery.ToPlane(i, v0, v1, v2) > 0)
815  {
816  auto iter = tmap.find(TriangleKey<false>(v0, v1, v2));
817  if (iter != tmap.end() && iter->second->T[1].lock() == nullptr)
818  {
819  auto adj = iter->second->T[0].lock();
820  if (adj && candidates.find(adj) == candidates.end())
821  {
822  int a0 = adj->V[0];
823  int a1 = adj->V[1];
824  int a2 = adj->V[2];
825  int a3 = adj->V[3];
826  if (mQuery.ToCircumsphere(i, a0, a1, a2, a3) <= 0)
827  {
828  // Point i is in the circumsphere.
829  candidates.insert(adj);
830  }
831  else
832  {
833  // Point i is not in the circumsphere but the hull
834  // face is visible.
835  visible.insert(key);
836  }
837  }
838  }
839  else
840  {
841  LogError("Unexpected condition (ComputeType not exact?)");
842  return false;
843  }
844  }
845  }
846 
847  // Get the boundary of the insertion subpolyhedron C that contains the
848  // tetrahedra whose circumspheres contain point i.
849  std::set<TriangleKey<true>> boundary;
850  if (!GetAndRemoveInsertionPolyhedron(i, candidates, boundary))
851  {
852  return false;
853  }
854 
855  // The insertion polyhedron P consists of the tetrahedra formed by
856  // point i and the back faces of C *and* the visible faces of
857  // mGraph-C.
858  for (auto const& key : boundary)
859  {
860  int v0 = key.V[0];
861  int v1 = key.V[1];
862  int v2 = key.V[2];
863  if (mQuery.ToPlane(i, v0, v1, v2) < 0)
864  {
865  // This is a back face of the boundary.
866  if (!mGraph.Insert(i, v0, v1, v2))
867  {
868  return false;
869  }
870  }
871  }
872  for (auto const& key : visible)
873  {
874  if (!mGraph.Insert(i, key.V[0], key.V[2], key.V[1]))
875  {
876  return false;
877  }
878  }
879  }
880 
881  return true;
882 }
883 
884 }
TSManifoldMesh const & GetGraph() const
Definition: GteDelaunay3.h:393
TSManifoldMesh::Tetrahedron Tetrahedron
Definition: GteDelaunay3.h:159
int ToPlane(int i, int v0, int v1, int v2) const
bool Remove(int v0, int v1, int v2, int v3)
int GetNumTetrahedra() const
Definition: GteDelaunay3.h:375
GLfloat GLfloat v1
Definition: glcorearb.h:812
Plane3< InputType > const & GetPlane() const
Definition: GteDelaunay3.h:357
bool operator()(int numVertices, Vector3< InputType > const *vertices, InputType epsilon)
Definition: GteDelaunay3.h:211
InputType mEpsilon
Definition: GteDelaunay3.h:175
Vector< N, Real > UnitCross(Vector< N, Real > const &v0, Vector< N, Real > const &v1, bool robust=false)
Definition: GteVector3.h:130
int ToCircumsphere(int i, int v0, int v1, int v2, int v3) const
int GetDimension() const
Definition: GteDelaunay3.h:345
bool GetAndRemoveInsertionPolyhedron(int i, std::set< std::shared_ptr< Tetrahedron >> &candidates, std::set< TriangleKey< true >> &boundary)
Definition: GteDelaunay3.h:675
int GetNumUniqueVertices() const
Definition: GteDelaunay3.h:369
std::vector< int > const & GetIndices() const
Definition: GteDelaunay3.h:399
PrimalQuery3< ComputeType > mQuery
Definition: GteDelaunay3.h:183
Vector3< InputType > const * mVertices
Definition: GteDelaunay3.h:189
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
bool GetHull(std::vector< int > &hull) const
Definition: GteDelaunay3.h:411
#define LogError(message)
Definition: GteLogger.h:92
static Vector Zero()
Definition: GteVector.h:295
TSManifoldMesh mGraph
Definition: GteDelaunay3.h:190
std::vector< Vector3< ComputeType > > mComputeVertices
Definition: GteDelaunay3.h:182
GLfloat GLfloat GLfloat GLfloat v3
Definition: glcorearb.h:814
int GetContainingTetrahedron(Vector3< InputType > const &p, SearchInfo &info) const
Definition: GteDelaunay3.h:523
InputType GetEpsilon() const
Definition: GteDelaunay3.h:339
Vector3< Real > direction[3]
Definition: GteVector3.h:96
int GetNumVertices() const
Definition: GteDelaunay3.h:363
Line3< InputType > const & GetLine() const
Definition: GteDelaunay3.h:351
GLfloat v0
Definition: glcorearb.h:811
GLdouble GLdouble t
Definition: glext.h:239
std::array< int, 4 > finalV
Definition: GteDelaunay3.h:153
PrimalQuery3< ComputeType > const & GetQuery() const
Definition: GteDelaunay3.h:387
bool Update(int i)
Definition: GteDelaunay3.h:736
std::vector< int > mAdjacencies
Definition: GteDelaunay3.h:192
TMap const & GetTriangles() const
void Set(int numVertices, Vector3< Real > const *vertices)
Vector3< InputType > const * GetVertices() const
Definition: GteDelaunay3.h:381
std::vector< int > mIndices
Definition: GteDelaunay3.h:191
const GLdouble * v
Definition: glcorearb.h:832
Vector3< Real > origin
Definition: GteVector3.h:95
GLfloat GLfloat GLfloat v2
Definition: glcorearb.h:813
Line3< InputType > mLine
Definition: GteDelaunay3.h:177
std::vector< int > const & GetAdjacencies() const
Definition: GteDelaunay3.h:405
std::shared_ptr< Tetrahedron > Insert(int v0, int v1, int v2, int v3)
Plane3< InputType > mPlane
Definition: GteDelaunay3.h:178
GLfloat GLfloat p
Definition: glext.h:11668
GLenum GLuint GLint GLenum face
Definition: glext.h:3311
std::vector< int > path
Definition: GteDelaunay3.h:151
SMap const & GetTetrahedra() const


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:59