TriangulateCDT.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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.2020.01.10
  7. #pragma once
  8. #include <Mathematics/Logger.h>
  9. #include <Mathematics/ConstrainedDelaunay2.h>
  10. #include <Mathematics/ContPointInPolygon2.h>
  11. #include <memory>
  12. #include <queue>
  13. // The triangulation is based on constrained Delaunay triangulations (CDT),
  14. // which does not use divisions, so ComputeType may be chosen using BSNumber.
  15. // The input constraints are relaxed compared to TriangulateEC; specifically,
  16. // the inner polygons are allowed to share vertices with the outer polygons.
  17. // The CDT produces a triangulation of the convex hull of the input, which
  18. // includes triangles outside the top-level outer polygon and inside the
  19. // inner polygons. Only the triangles relevant to the input are returned
  20. // via the 'std::vector<int>& triangles', but the other triangles are
  21. // retained so that you can perform linear walks in search of points inside
  22. // the original polygon (nested polygon, tree of nested polygons). This is
  23. // useful, for example, when subsampling the polygon triangles for
  24. // interpolation of function data specified at the vertices. A linear walk
  25. // does not work for a mesh consisting only of the polygon triangles, but
  26. // with the additional triangles, the walk can navigate through holes in
  27. // the polygon to find the containing triangle of the specified point.
  28. namespace WwiseGTE
  29. {
  30. template <typename InputType, typename ComputeType>
  31. class TriangulateCDT
  32. {
  33. public:
  34. // The class is a functor to support triangulating multiple polygons
  35. // that share vertices in a collection of points. The interpretation
  36. // of 'numPoints' and 'points' is described before each operator()
  37. // function. Preconditions are numPoints >= 3 and points is a nonnull
  38. // pointer to an array of at least numPoints elements. If the
  39. // preconditions are satisfied, then operator() functions will return
  40. // 'true'; otherwise, they return 'false'.
  41. TriangulateCDT(int numPoints, Vector2<InputType> const* points)
  42. :
  43. mNumPoints(numPoints),
  44. mPoints(points)
  45. {
  46. LogAssert(mNumPoints >= 3 && mPoints != nullptr, "Invalid input.");
  47. }
  48. TriangulateCDT(std::vector<Vector2<InputType>> const& points)
  49. :
  50. mNumPoints(static_cast<int>(points.size())),
  51. mPoints(points.data())
  52. {
  53. LogAssert(mNumPoints >= 3 && mPoints != nullptr, "Invalid input.");
  54. }
  55. // The triangles of the polygon triangulation.
  56. inline std::vector<std::array<int, 3>> const& GetTriangles() const
  57. {
  58. return mTriangles;
  59. }
  60. // The triangles inside the convex hull of the points but outside the
  61. // triangulation.
  62. inline std::vector<std::array<int, 3>> const& GetOutsideTriangles() const
  63. {
  64. return mOutsideTriangles;
  65. }
  66. // The triangles of the convex hull of the inputs to the constructor.
  67. inline std::vector<std::array<int, 3>> const& GetAllTriangles() const
  68. {
  69. return mAllTriangles;
  70. }
  71. // The classification of whether a triangle is part of the
  72. // triangulation or outside the triangulation. These may be used in
  73. // conjunction with the array returned by GetAllTriangles().
  74. inline std::vector<bool> const& GetIsInside() const
  75. {
  76. return mIsInside;
  77. }
  78. inline bool IsInside(int triIndex) const
  79. {
  80. if (0 <= triIndex && triIndex < static_cast<int>(mIsInside.size()))
  81. {
  82. return mIsInside[triIndex];
  83. }
  84. else
  85. {
  86. return false;
  87. }
  88. }
  89. inline bool IsOutside(int triIndex) const
  90. {
  91. if (0 <= triIndex && triIndex < static_cast<int>(mIsInside.size()))
  92. {
  93. return !mIsInside[triIndex];
  94. }
  95. else
  96. {
  97. return false;
  98. }
  99. }
  100. // The outer polygons have counterclockwise ordered vertices. The
  101. // inner polygons have clockwise ordered vertices.
  102. typedef std::vector<int> Polygon;
  103. // The input 'points' represents an array of vertices for a simple
  104. // polygon. The vertices are points[0] through points[numPoints-1] and
  105. // are listed in counterclockwise order.
  106. bool operator()()
  107. {
  108. if (mPoints)
  109. {
  110. auto tree = std::make_shared<Tree>();
  111. tree->polygon.resize(mNumPoints);
  112. for (int i = 0; i < mNumPoints; ++i)
  113. {
  114. tree->polygon[i] = i;
  115. }
  116. return operator()(tree);
  117. }
  118. return false;
  119. }
  120. // The input 'points' represents an array of vertices that contains
  121. // the vertices of a simple polygon.
  122. bool operator()(Polygon const& polygon)
  123. {
  124. if (mPoints)
  125. {
  126. auto tree = std::make_shared<Tree>();
  127. tree->polygon = polygon;
  128. return operator()(tree);
  129. }
  130. return false;
  131. }
  132. // The input 'points' is a shared array of vertices that contains the
  133. // vertices for two simple polygons, an outer polygon and an inner
  134. // polygon. The inner polygon must be strictly inside the outer
  135. // polygon.
  136. bool operator()(Polygon const& outer, Polygon const& inner)
  137. {
  138. if (mPoints)
  139. {
  140. auto otree = std::make_shared<Tree>();
  141. otree->polygon = outer;
  142. otree->child.resize(1);
  143. auto itree = std::make_shared<Tree>();
  144. itree->polygon = inner;
  145. otree->child[0] = itree;
  146. return operator()(otree);
  147. }
  148. return false;
  149. }
  150. // The input 'points' is a shared array of vertices that contains the
  151. // vertices for multiple simple polygons, an outer polygon and one or
  152. // more inner polygons. The inner polygons must be nonoverlapping and
  153. // strictly inside the outer polygon.
  154. bool operator()(Polygon const& outer, std::vector<Polygon> const& inners)
  155. {
  156. if (mPoints)
  157. {
  158. auto otree = std::make_shared<Tree>();
  159. otree->polygon = outer;
  160. otree->child.resize(inners.size());
  161. std::vector<std::shared_ptr<Tree>> itree(inners.size());
  162. for (size_t i = 0; i < inners.size(); ++i)
  163. {
  164. itree[i] = std::make_shared<Tree>();
  165. itree[i]->polygon = inners[i];
  166. otree->child[i] = itree[i];
  167. }
  168. return operator()(otree);
  169. }
  170. return false;
  171. }
  172. // A tree of nested polygons. The root node corresponds to an outer
  173. // polygon. The children of the root correspond to inner polygons,
  174. // which are nonoverlapping polygons strictly contained in the outer
  175. // polygon. Each inner polygon may itself contain an outer polygon,
  176. // thus leading to a hierarchy of polygons. The outer polygons have
  177. // vertices listed in counterclockwise order. The inner polygons have
  178. // vertices listed in clockwise order.
  179. class Tree
  180. {
  181. public:
  182. Polygon polygon;
  183. std::vector<std::shared_ptr<Tree>> child;
  184. };
  185. // The input 'positions' is a shared array of vertices that contains
  186. // the vertices for multiple simple polygons in a tree of polygons.
  187. bool operator()(std::shared_ptr<Tree> const& tree)
  188. {
  189. if (mPoints)
  190. {
  191. std::map<std::shared_ptr<Tree>, int> offsets;
  192. int numPoints = GetNumPointsAndOffsets(tree, offsets);
  193. std::vector<Vector2<InputType>> points(numPoints);
  194. PackPoints(tree, points);
  195. if (TriangulatePacked(numPoints, &points[0], tree, offsets))
  196. {
  197. int numTriangles = static_cast<int>(mAllTriangles.size());
  198. for (int t = 0; t < numTriangles; ++t)
  199. {
  200. auto& tri = mAllTriangles[t];
  201. for (int j = 0; j < 3; ++j)
  202. {
  203. LookupIndex(tree, tri[j], offsets);
  204. }
  205. if (mIsInside[t])
  206. {
  207. mTriangles.push_back(tri);
  208. }
  209. else
  210. {
  211. mOutsideTriangles.push_back(tri);
  212. }
  213. }
  214. return true;
  215. }
  216. }
  217. return false;
  218. }
  219. private:
  220. // Triangulate the points referenced by an operator(...) query. The
  221. // mAllTriangles and mIsInside are populated by this function, but the
  222. // indices of mAllTriangles are relative to the packed 'points'.
  223. // After the call, the indices need to be mapped back to the original
  224. // set provided by the input arrays to operator(...). The mTriangles
  225. // and mOutsideTriangles are generated after the call by examining
  226. // mAllTriangles and mIsInside.
  227. bool TriangulatePacked(int numPoints, Vector2<InputType> const* points,
  228. std::shared_ptr<Tree> const& tree,
  229. std::map<std::shared_ptr<Tree>, int> const& offsets)
  230. {
  231. mTriangles.clear();
  232. mOutsideTriangles.clear();
  233. mAllTriangles.clear();
  234. mIsInside.clear();
  235. mConstrainedDelaunay(numPoints, points, static_cast<InputType>(0));
  236. InsertEdges(tree);
  237. ComputeType half = static_cast<ComputeType>(0.5);
  238. ComputeType fourth = static_cast<ComputeType>(0.25);
  239. auto const& query = mConstrainedDelaunay.GetQuery();
  240. auto const* ctPoints = query.GetVertices();
  241. int numTriangles = mConstrainedDelaunay.GetNumTriangles();
  242. int const* indices = &mConstrainedDelaunay.GetIndices()[0];
  243. mIsInside.resize(numTriangles);
  244. for (int t = 0; t < numTriangles; ++t)
  245. {
  246. int v0 = *indices++;
  247. int v1 = *indices++;
  248. int v2 = *indices++;
  249. auto ctInside = fourth * ctPoints[v0] + half * ctPoints[v1] + fourth * ctPoints[v2];
  250. mIsInside[t] = IsInside(tree, ctPoints, ctInside, offsets);
  251. mAllTriangles.push_back( { v0, v1, v2 } );
  252. }
  253. return true;
  254. }
  255. int GetNumPointsAndOffsets(std::shared_ptr<Tree> const& tree,
  256. std::map<std::shared_ptr<Tree>, int>& offsets) const
  257. {
  258. int numPoints = 0;
  259. std::queue<std::shared_ptr<Tree>> treeQueue;
  260. treeQueue.push(tree);
  261. while (treeQueue.size() > 0)
  262. {
  263. std::shared_ptr<Tree> outer = treeQueue.front();
  264. treeQueue.pop();
  265. offsets.insert(std::make_pair(outer, numPoints));
  266. numPoints += static_cast<int>(outer->polygon.size());
  267. int numChildren = static_cast<int>(outer->child.size());
  268. for (int c = 0; c < numChildren; ++c)
  269. {
  270. std::shared_ptr<Tree> inner = outer->child[c];
  271. offsets.insert(std::make_pair(inner, numPoints));
  272. numPoints += static_cast<int>(inner->polygon.size());
  273. int numGrandChildren = static_cast<int>(inner->child.size());
  274. for (int g = 0; g < numGrandChildren; ++g)
  275. {
  276. treeQueue.push(inner->child[g]);
  277. }
  278. }
  279. }
  280. return numPoints;
  281. }
  282. void PackPoints(std::shared_ptr<Tree> const& tree,
  283. std::vector<Vector2<InputType>>& points)
  284. {
  285. int numPoints = 0;
  286. std::queue<std::shared_ptr<Tree>> treeQueue;
  287. treeQueue.push(tree);
  288. while (treeQueue.size() > 0)
  289. {
  290. std::shared_ptr<Tree> outer = treeQueue.front();
  291. treeQueue.pop();
  292. int const numOuterIndices = static_cast<int>(outer->polygon.size());
  293. int const* outerIndices = outer->polygon.data();
  294. for (int i = 0; i < numOuterIndices; ++i)
  295. {
  296. points[numPoints++] = mPoints[outerIndices[i]];
  297. }
  298. int numChildren = static_cast<int>(outer->child.size());
  299. for (int c = 0; c < numChildren; ++c)
  300. {
  301. std::shared_ptr<Tree> inner = outer->child[c];
  302. int const numInnerIndices = static_cast<int>(inner->polygon.size());
  303. int const* innerIndices = inner->polygon.data();
  304. for (int i = 0; i < numInnerIndices; ++i)
  305. {
  306. points[numPoints++] = mPoints[innerIndices[i]];
  307. }
  308. int numGrandChildren = static_cast<int>(inner->child.size());
  309. for (int g = 0; g < numGrandChildren; ++g)
  310. {
  311. treeQueue.push(inner->child[g]);
  312. }
  313. }
  314. }
  315. }
  316. bool InsertEdges(std::shared_ptr<Tree> const& tree)
  317. {
  318. int numPoints = 0;
  319. std::array<int, 2> edge;
  320. std::vector<int> ignoreOutEdge;
  321. std::queue<std::shared_ptr<Tree>> treeQueue;
  322. treeQueue.push(tree);
  323. while (treeQueue.size() > 0)
  324. {
  325. auto outer = treeQueue.front();
  326. treeQueue.pop();
  327. int numOuter = static_cast<int>(outer->polygon.size());
  328. for (int i0 = numOuter - 1, i1 = 0; i1 < numOuter; i0 = i1++)
  329. {
  330. edge[0] = numPoints + i0;
  331. edge[1] = numPoints + i1;
  332. if (!mConstrainedDelaunay.Insert(edge, ignoreOutEdge))
  333. {
  334. return false;
  335. }
  336. }
  337. numPoints += numOuter;
  338. int numChildren = static_cast<int>(outer->child.size());
  339. for (int c = 0; c < numChildren; ++c)
  340. {
  341. auto inner = outer->child[c];
  342. int numInner = static_cast<int>(inner->polygon.size());
  343. for (int i0 = numInner - 1, i1 = 0; i1 < numInner; i0 = i1++)
  344. {
  345. edge[0] = numPoints + i0;
  346. edge[1] = numPoints + i1;
  347. if (!mConstrainedDelaunay.Insert(edge, ignoreOutEdge))
  348. {
  349. return false;
  350. }
  351. }
  352. numPoints += numInner;
  353. int numGrandChildren = static_cast<int>(inner->child.size());
  354. for (int g = 0; g < numGrandChildren; ++g)
  355. {
  356. treeQueue.push(inner->child[g]);
  357. }
  358. }
  359. }
  360. return true;
  361. }
  362. void LookupIndex(std::shared_ptr<Tree> const& tree, int& v,
  363. std::map<std::shared_ptr<Tree>, int> const& offsets) const
  364. {
  365. std::queue<std::shared_ptr<Tree>> treeQueue;
  366. treeQueue.push(tree);
  367. while (treeQueue.size() > 0)
  368. {
  369. auto outer = treeQueue.front();
  370. treeQueue.pop();
  371. int const numOuterIndices = static_cast<int>(outer->polygon.size());
  372. int const* outerIndices = outer->polygon.data();
  373. int offset = offsets.find(outer)->second;
  374. if (v < offset + numOuterIndices)
  375. {
  376. v = outerIndices[v - offset];
  377. return;
  378. }
  379. int numChildren = static_cast<int>(outer->child.size());
  380. for (int c = 0; c < numChildren; ++c)
  381. {
  382. auto inner = outer->child[c];
  383. int const numInnerIndices = static_cast<int>(inner->polygon.size());
  384. int const* innerIndices = inner->polygon.data();
  385. offset = offsets.find(inner)->second;
  386. if (v < offset + numInnerIndices)
  387. {
  388. v = innerIndices[v - offset];
  389. return;
  390. }
  391. int numGrandChildren = static_cast<int>(inner->child.size());
  392. for (int g = 0; g < numGrandChildren; ++g)
  393. {
  394. treeQueue.push(inner->child[g]);
  395. }
  396. }
  397. }
  398. }
  399. bool IsInside(std::shared_ptr<Tree> const& tree, Vector2<ComputeType> const* points,
  400. Vector2<ComputeType> const& test,
  401. std::map<std::shared_ptr<Tree>, int> const& offsets) const
  402. {
  403. std::queue<std::shared_ptr<Tree>> treeQueue;
  404. treeQueue.push(tree);
  405. while (treeQueue.size() > 0)
  406. {
  407. auto outer = treeQueue.front();
  408. treeQueue.pop();
  409. int const numOuterIndices = static_cast<int>(outer->polygon.size());
  410. int offset = offsets.find(outer)->second;
  411. PointInPolygon2<ComputeType> piOuter(numOuterIndices, points + offset);
  412. if (piOuter.Contains(test))
  413. {
  414. int numChildren = static_cast<int>(outer->child.size());
  415. int c;
  416. for (c = 0; c < numChildren; ++c)
  417. {
  418. auto inner = outer->child[c];
  419. int const numInnerIndices = static_cast<int>(inner->polygon.size());
  420. offset = offsets.find(inner)->second;
  421. PointInPolygon2<ComputeType> piInner(numInnerIndices, points + offset);
  422. if (piInner.Contains(test))
  423. {
  424. int numGrandChildren = static_cast<int>(inner->child.size());
  425. for (int g = 0; g < numGrandChildren; ++g)
  426. {
  427. treeQueue.push(inner->child[g]);
  428. }
  429. break;
  430. }
  431. }
  432. if (c == numChildren)
  433. {
  434. return true;
  435. }
  436. }
  437. }
  438. return false;
  439. }
  440. // The input polygon.
  441. int mNumPoints;
  442. Vector2<InputType> const* mPoints;
  443. // The output triangulation and those triangle inside the hull of the
  444. // points but outside the triangulation.
  445. std::vector<std::array<int, 3>> mTriangles;
  446. std::vector<std::array<int, 3>> mOutsideTriangles;
  447. std::vector<std::array<int, 3>> mAllTriangles;
  448. std::vector<bool> mIsInside;
  449. ConstrainedDelaunay2<InputType, ComputeType> mConstrainedDelaunay;
  450. };
  451. }