PlanarMesh.h 16 KB

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