CLODPolyline.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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/MinHeap.h>
  10. #include <Mathematics/DistPointSegment.h>
  11. #include <vector>
  12. // The continuous level of detail (CLOD) algorithm implemented here is
  13. // described in
  14. // https://www.geometrictools.com/Documentation/PolylineReduction.pdf
  15. // It is an algorithm to reduce incrementally the number of vertices in a
  16. // polyline (open or closed). The sequence of vertex collapses is based on
  17. // vertex weights associated with distance from vertices to polyline segments.
  18. namespace WwiseGTE
  19. {
  20. template <int N, typename Real>
  21. class CLODPolyline
  22. {
  23. public:
  24. // Constructor and destructor. The number of vertices must be 2 or
  25. // larger. The vertices are assumed to be ordered. The segments are
  26. // <V[i],V[i+1]> for 0 <= i <= numVertices-2 for an open polyline.
  27. // If the polyline is closed, an additional segment is
  28. // <V[numVertices-1],V[0]>.
  29. CLODPolyline(std::vector<Vector<N, Real>> const& vertices, bool closed)
  30. :
  31. mNumVertices(static_cast<int>(vertices.size())),
  32. mVertices(vertices),
  33. mClosed(closed),
  34. mNumEdges(0),
  35. mVMin(mClosed ? 3 : 2),
  36. mVMax(mNumVertices)
  37. {
  38. LogAssert(closed ? mNumVertices >= 3 : mNumVertices >= 2, "Invalid inputs.");
  39. // Compute the sequence of vertex collapses. The polyline starts
  40. // out at the full level of detail (mNumVertices equals mVMax).
  41. VertexCollapse collapser;
  42. collapser(mVertices, mClosed, mIndices, mNumEdges, mEdges);
  43. }
  44. ~CLODPolyline() = default;
  45. // Member access.
  46. inline int GetNumVertices() const
  47. {
  48. return mNumVertices;
  49. }
  50. inline std::vector<Vector<N, Real>> const& GetVertices() const
  51. {
  52. return mVertices;
  53. }
  54. inline bool GetClosed() const
  55. {
  56. return mClosed;
  57. }
  58. inline int GetNumEdges() const
  59. {
  60. return mNumEdges;
  61. }
  62. inline std::vector<int> const& GetEdges() const
  63. {
  64. return mEdges;
  65. }
  66. // Accessors to level of detail (MinLOD <= LOD <= MaxLOD is required).
  67. inline int GetMinLevelOfDetail() const
  68. {
  69. return mVMin;
  70. }
  71. inline int GetMaxLevelOfDetail() const
  72. {
  73. return mVMax;
  74. }
  75. inline int GetLevelOfDetail() const
  76. {
  77. return mNumVertices;
  78. }
  79. void SetLevelOfDetail(int numVertices)
  80. {
  81. if (numVertices < mVMin || numVertices > mVMax)
  82. {
  83. return;
  84. }
  85. // Decrease the level of detail.
  86. while (mNumVertices > numVertices)
  87. {
  88. --mNumVertices;
  89. mEdges[mIndices[mNumVertices]] = mEdges[2 * mNumEdges - 1];
  90. --mNumEdges;
  91. }
  92. // Increase the level of detail.
  93. while (mNumVertices < numVertices)
  94. {
  95. ++mNumEdges;
  96. mEdges[mIndices[mNumVertices]] = mNumVertices;
  97. ++mNumVertices;
  98. }
  99. }
  100. private:
  101. // Support for vertex collapses in a polyline.
  102. class VertexCollapse
  103. {
  104. public:
  105. void operator()(std::vector<Vector<N, Real>>& vertices,
  106. bool closed, std::vector<int>& indices, int& numEdges,
  107. std::vector<int>& edges)
  108. {
  109. int numVertices = static_cast<int>(vertices.size());
  110. indices.resize(vertices.size());
  111. if (closed)
  112. {
  113. numEdges = numVertices;
  114. edges.resize(2 * numEdges);
  115. if (numVertices == 3)
  116. {
  117. indices[0] = 0;
  118. indices[1] = 1;
  119. indices[2] = 3;
  120. edges[0] = 0; edges[1] = 1;
  121. edges[2] = 1; edges[3] = 2;
  122. edges[4] = 2; edges[5] = 0;
  123. return;
  124. }
  125. }
  126. else
  127. {
  128. numEdges = numVertices - 1;
  129. edges.resize(2 * numEdges);
  130. if (numVertices == 2)
  131. {
  132. indices[0] = 0;
  133. indices[1] = 1;
  134. edges[0] = 0; edges[1] = 1;
  135. return;
  136. }
  137. }
  138. // Create the heap of weights.
  139. MinHeap<int, Real> heap(numVertices);
  140. int qm1 = numVertices - 1;
  141. if (closed)
  142. {
  143. int qm2 = numVertices - 2;
  144. heap.Insert(0, GetWeight(qm1, 0, 1, vertices));
  145. heap.Insert(qm1, GetWeight(qm2, qm1, 0, vertices));
  146. }
  147. else
  148. {
  149. heap.Insert(0, std::numeric_limits<Real>::max());
  150. heap.Insert(qm1, std::numeric_limits<Real>::max());
  151. }
  152. for (int m = 0, z = 1, p = 2; z < qm1; ++m, ++z, ++p)
  153. {
  154. heap.Insert(z, GetWeight(m, z, p, vertices));
  155. }
  156. // Create the level of detail information for the polyline.
  157. std::vector<int> collapses(numVertices);
  158. CollapseVertices(heap, numVertices, collapses);
  159. ComputeEdges(numVertices, closed, collapses, indices, numEdges, edges);
  160. ReorderVertices(numVertices, vertices, collapses, numEdges, edges);
  161. }
  162. protected:
  163. // Weight calculation for vertex triple <V[m],V[z],V[p]>.
  164. Real GetWeight(int m, int z, int p, std::vector<Vector<N, Real>>& vertices)
  165. {
  166. Vector<N, Real> direction = vertices[p] - vertices[m];
  167. Real length = Normalize(direction);
  168. if (length > (Real)0)
  169. {
  170. Segment<N, Real> segment(vertices[m], vertices[p]);
  171. DCPQuery<Real, Vector<N, Real>, Segment<N, Real>> query;
  172. Real distance = query(vertices[z], segment).distance;
  173. return distance / length;
  174. }
  175. else
  176. {
  177. return std::numeric_limits<Real>::max();
  178. }
  179. }
  180. // Create data structures for the polyline.
  181. void CollapseVertices(MinHeap<int, Real>& heap,
  182. int numVertices, std::vector<int>& collapses)
  183. {
  184. for (int i = numVertices - 1; i >= 0; --i)
  185. {
  186. Real weight;
  187. heap.Remove(collapses[i], weight);
  188. }
  189. }
  190. void ComputeEdges(int numVertices, bool closed, std::vector<int>& collapses,
  191. std::vector<int>& indices, int numEdges, std::vector<int>& edges)
  192. {
  193. // Compute the edges (first to collapse is last in array). Do
  194. // not collapse the last line segment of an open polyline. Do
  195. // not collapse further when a closed polyline becomes a
  196. // triangle.
  197. int i, vIndex, eIndex = 2 * numEdges - 1;
  198. if (closed)
  199. {
  200. for (i = numVertices - 1; i >= 0; --i)
  201. {
  202. vIndex = collapses[i];
  203. edges[eIndex--] = (vIndex + 1) % numVertices;
  204. edges[eIndex--] = vIndex;
  205. }
  206. }
  207. else
  208. {
  209. for (i = numVertices - 1; i >= 2; --i)
  210. {
  211. vIndex = collapses[i];
  212. edges[eIndex--] = vIndex + 1;
  213. edges[eIndex--] = vIndex;
  214. }
  215. vIndex = collapses[0];
  216. edges[0] = vIndex;
  217. edges[1] = vIndex + 1;
  218. }
  219. // In the given edge order, find the index in the edge array
  220. // that corresponds to a collapse vertex and save the index
  221. // for the dynamic change in level of detail. This relies on
  222. // the assumption that a vertex is shared by at most two
  223. // edges.
  224. eIndex = 2 * numEdges - 1;
  225. for (i = numVertices - 1; i >= 0; --i)
  226. {
  227. vIndex = collapses[i];
  228. for (int e = 0; e < 2 * numEdges; ++e)
  229. {
  230. if (vIndex == edges[e])
  231. {
  232. indices[i] = e;
  233. edges[e] = edges[eIndex];
  234. break;
  235. }
  236. }
  237. eIndex -= 2;
  238. if (closed)
  239. {
  240. if (eIndex == 5)
  241. {
  242. break;
  243. }
  244. }
  245. else
  246. {
  247. if (eIndex == 1)
  248. {
  249. break;
  250. }
  251. }
  252. }
  253. // Restore the edge array to full level of detail.
  254. if (closed)
  255. {
  256. for (i = 3; i < numVertices; ++i)
  257. {
  258. edges[indices[i]] = collapses[i];
  259. }
  260. }
  261. else
  262. {
  263. for (i = 2; i < numVertices; ++i)
  264. {
  265. edges[indices[i]] = collapses[i];
  266. }
  267. }
  268. }
  269. void ReorderVertices(int numVertices, std::vector<Vector<N, Real>>& vertices,
  270. std::vector<int>& collapses, int numEdges, std::vector<int>& edges)
  271. {
  272. std::vector<int> permute(numVertices);
  273. std::vector<Vector<N, Real>> permutedVertex(numVertices);
  274. for (int i = 0; i < numVertices; ++i)
  275. {
  276. int vIndex = collapses[i];
  277. permute[vIndex] = i;
  278. permutedVertex[i] = vertices[vIndex];
  279. }
  280. for (int i = 0; i < 2 * numEdges; ++i)
  281. {
  282. edges[i] = permute[edges[i]];
  283. }
  284. vertices = permutedVertex;
  285. }
  286. };
  287. private:
  288. // The polyline vertices.
  289. int mNumVertices;
  290. std::vector<Vector<N, Real>> mVertices;
  291. bool mClosed;
  292. // The polyline edges.
  293. int mNumEdges;
  294. std::vector<int> mEdges;
  295. // The level of detail information.
  296. int mVMin, mVMax;
  297. std::vector<int> mIndices;
  298. };
  299. }