Delaunay3.h 34 KB

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