RectangleManager.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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/IntrAlignedBox2AlignedBox2.h>
  9. #include <Mathematics/EdgeKey.h>
  10. #include <set>
  11. #include <vector>
  12. namespace WwiseGTE
  13. {
  14. template <typename Real>
  15. class RectangleManager
  16. {
  17. public:
  18. // Construction.
  19. RectangleManager(std::vector<AlignedBox2<Real>>& rectangles)
  20. :
  21. mRectangles(rectangles)
  22. {
  23. Initialize();
  24. }
  25. // No default construction, copy construction, or assignment are
  26. // allowed.
  27. RectangleManager() = delete;
  28. RectangleManager(RectangleManager const&) = delete;
  29. RectangleManager& operator=(RectangleManager const&) = delete;
  30. // This function is called by the constructor and does the
  31. // sort-and-sweep to initialize the update system. However, if you
  32. // add or remove items from the array of rectangles after the
  33. // constructor call, you will need to call this function once before
  34. // you start the multiple calls of the update function.
  35. void Initialize()
  36. {
  37. // Get the rectangle endpoints.
  38. int intrSize = static_cast<int>(mRectangles.size()), endpSize = 2 * intrSize;
  39. mXEndpoints.resize(endpSize);
  40. mYEndpoints.resize(endpSize);
  41. for (int i = 0, j = 0; i < intrSize; ++i)
  42. {
  43. mXEndpoints[j].type = 0;
  44. mXEndpoints[j].value = mRectangles[i].min[0];
  45. mXEndpoints[j].index = i;
  46. mYEndpoints[j].type = 0;
  47. mYEndpoints[j].value = mRectangles[i].min[1];
  48. mYEndpoints[j].index = i;
  49. ++j;
  50. mXEndpoints[j].type = 1;
  51. mXEndpoints[j].value = mRectangles[i].max[0];
  52. mXEndpoints[j].index = i;
  53. mYEndpoints[j].type = 1;
  54. mYEndpoints[j].value = mRectangles[i].max[1];
  55. mYEndpoints[j].index = i;
  56. ++j;
  57. }
  58. // Sort the rectangle endpoints.
  59. std::sort(mXEndpoints.begin(), mXEndpoints.end());
  60. std::sort(mYEndpoints.begin(), mYEndpoints.end());
  61. // Create the interval-to-endpoint lookup tables.
  62. mXLookup.resize(endpSize);
  63. mYLookup.resize(endpSize);
  64. for (int j = 0; j < endpSize; ++j)
  65. {
  66. mXLookup[2 * mXEndpoints[j].index + mXEndpoints[j].type] = j;
  67. mYLookup[2 * mYEndpoints[j].index + mYEndpoints[j].type] = j;
  68. }
  69. // Active set of rectangles (stored by index in array).
  70. std::set<int> active;
  71. // Set of overlapping rectangles (stored by pairs of indices in
  72. // array).
  73. mOverlap.clear();
  74. // Sweep through the endpoints to determine overlapping
  75. // x-intervals.
  76. for (int i = 0; i < endpSize; ++i)
  77. {
  78. Endpoint& endpoint = mXEndpoints[i];
  79. int index = endpoint.index;
  80. if (endpoint.type == 0) // an interval 'begin' value
  81. {
  82. // In the 1D problem, the current interval overlaps with
  83. // all the active intervals. In 2D we also need to check
  84. // for y-overlap.
  85. for (auto activeIndex : active)
  86. {
  87. // Rectangles activeIndex and index overlap in the
  88. // x-dimension. Test for overlap in the y-dimension.
  89. AlignedBox2<Real> const& r0 = mRectangles[activeIndex];
  90. AlignedBox2<Real> const& r1 = mRectangles[index];
  91. if (r0.max[1] >= r1.min[1] && r0.min[1] <= r1.max[1])
  92. {
  93. if (activeIndex < index)
  94. {
  95. mOverlap.insert(EdgeKey<false>(activeIndex, index));
  96. }
  97. else
  98. {
  99. mOverlap.insert(EdgeKey<false>(index, activeIndex));
  100. }
  101. }
  102. }
  103. active.insert(index);
  104. }
  105. else // an interval 'end' value
  106. {
  107. active.erase(index);
  108. }
  109. }
  110. }
  111. // After the system is initialized, you can move the rectangles using
  112. // this function. It is not enough to modify the input array of
  113. // rectangles because the endpoint values stored internally by this
  114. // class must also change. You can also retrieve the current
  115. // rectangles information.
  116. void SetRectangle(int i, AlignedBox2<Real> const& rectangle)
  117. {
  118. mRectangles[i] = rectangle;
  119. mXEndpoints[mXLookup[2 * i]].value = rectangle.min[0];
  120. mXEndpoints[mXLookup[2 * i + 1]].value = rectangle.max[0];
  121. mYEndpoints[mYLookup[2 * i]].value = rectangle.min[1];
  122. mYEndpoints[mYLookup[2 * i + 1]].value = rectangle.max[1];
  123. }
  124. inline void GetRectangle(int i, AlignedBox2<Real>& rectangle) const
  125. {
  126. rectangle = mRectangles[i];
  127. }
  128. // When you are finished moving rectangles, call this function to
  129. // determine the overlapping rectangles. An incremental update is
  130. // applied to determine the new set of overlapping rectangles.
  131. void Update()
  132. {
  133. InsertionSort(mXEndpoints, mXLookup);
  134. InsertionSort(mYEndpoints, mYLookup);
  135. }
  136. // If (i,j) is in the overlap set, then rectangle i and rectangle j
  137. // are overlapping. The indices are those for the the input array.
  138. // The set elements (i,j) are stored so that i < j.
  139. inline std::set<EdgeKey<false>> const& GetOverlap() const
  140. {
  141. return mOverlap;
  142. }
  143. private:
  144. class Endpoint
  145. {
  146. public:
  147. Real value; // endpoint value
  148. int type; // '0' if interval min, '1' if interval max.
  149. int index; // index of interval containing this endpoint
  150. // Support for sorting of endpoints.
  151. bool operator<(Endpoint const& endpoint) const
  152. {
  153. if (value < endpoint.value)
  154. {
  155. return true;
  156. }
  157. if (value > endpoint.value)
  158. {
  159. return false;
  160. }
  161. return type < endpoint.type;
  162. }
  163. };
  164. void InsertionSort(std::vector<Endpoint>& endpoint, std::vector<int>& lookup)
  165. {
  166. // Apply an insertion sort. Under the assumption that the
  167. // rectangles have not changed much since the last call, the
  168. // endpoints are nearly sorted. The insertion sort should be very
  169. // fast in this case.
  170. TIQuery<Real, AlignedBox2<Real>, AlignedBox2<Real>> query;
  171. int endpSize = static_cast<int>(endpoint.size());
  172. for (int j = 1; j < endpSize; ++j)
  173. {
  174. Endpoint key = endpoint[j];
  175. int i = j - 1;
  176. while (i >= 0 && key < endpoint[i])
  177. {
  178. Endpoint e0 = endpoint[i];
  179. Endpoint e1 = endpoint[i + 1];
  180. // Update the overlap status.
  181. if (e0.type == 0)
  182. {
  183. if (e1.type == 1)
  184. {
  185. // The 'b' of interval E0.index was smaller than
  186. // the 'e' of interval E1.index, and the intervals
  187. // *might have been* overlapping. Now 'b' and 'e'
  188. // are swapped, and the intervals cannot overlap.
  189. // Remove the pair from the overlap set. The
  190. // removal operation needs to find the pair and
  191. // erase it if it exists. Finding the pair is the
  192. // expensive part of the operation, so there is no
  193. // real time savings in testing for existence
  194. // first, then deleting if it does.
  195. mOverlap.erase(EdgeKey<false>(e0.index, e1.index));
  196. }
  197. }
  198. else
  199. {
  200. if (e1.type == 0)
  201. {
  202. // The 'b' of interval E1.index was larger than
  203. // the 'e' of interval E0.index, and the intervals
  204. // were not overlapping. Now 'b' and 'e' are
  205. // swapped, and the intervals *might be*
  206. // overlapping. Determine if they are overlapping
  207. // and then insert.
  208. if (query(mRectangles[e0.index], mRectangles[e1.index]).intersect)
  209. {
  210. mOverlap.insert(EdgeKey<false>(e0.index, e1.index));
  211. }
  212. }
  213. }
  214. // Reorder the items to maintain the sorted list.
  215. endpoint[i] = e1;
  216. endpoint[i + 1] = e0;
  217. lookup[2 * e1.index + e1.type] = i;
  218. lookup[2 * e0.index + e0.type] = i + 1;
  219. --i;
  220. }
  221. endpoint[i + 1] = key;
  222. lookup[2 * key.index + key.type] = i + 1;
  223. }
  224. }
  225. std::vector<AlignedBox2<Real>>& mRectangles;
  226. std::vector<Endpoint> mXEndpoints, mYEndpoints;
  227. std::set<EdgeKey<false>> mOverlap;
  228. // The intervals are indexed 0 <= i < n. The endpoint array has 2*n
  229. // entries. The original 2*n interval values are ordered as
  230. // b[0], e[0], b[1], e[1], ..., b[n-1], e[n-1]
  231. // When the endpoint array is sorted, the mapping between interval
  232. // values and endpoints is lost. In order to modify interval values
  233. // that are stored in the endpoint array, we need to maintain the
  234. // mapping. This is done by the following lookup table of 2*n
  235. // entries. The value mLookup[2*i] is the index of b[i] in the
  236. // endpoint array. The value mLookup[2*i+1] is the index of e[i]
  237. // in the endpoint array.
  238. std::vector<int> mXLookup, mYLookup;
  239. };
  240. }