tb_geometry.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // ================================================================================
  2. // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
  3. // == See tb_core.h for more information. ==
  4. // ================================================================================
  5. #include "tb_geometry.h"
  6. #include <assert.h>
  7. namespace tb {
  8. // == TBRect ============================================================================
  9. bool TBRect::Intersects(const TBRect &rect) const
  10. {
  11. if (IsEmpty() || rect.IsEmpty())
  12. return false;
  13. if (x + w > rect.x && x < rect.x + rect.w &&
  14. y + h > rect.y && y < rect.y + rect.h)
  15. return true;
  16. return false;
  17. }
  18. TBRect TBRect::MoveIn(const TBRect &bounding_rect) const
  19. {
  20. return TBRect(ClampClipMax(x, bounding_rect.x, bounding_rect.x + bounding_rect.w - w),
  21. ClampClipMax(y, bounding_rect.y, bounding_rect.y + bounding_rect.h - h),
  22. w, h);
  23. }
  24. TBRect TBRect::CenterIn(const TBRect &bounding_rect) const
  25. {
  26. return TBRect((bounding_rect.w - w) / 2, (bounding_rect.h - h) / 2, w, h);
  27. }
  28. TBRect TBRect::Union(const TBRect &rect) const
  29. {
  30. assert(!IsInsideOut());
  31. assert(!rect.IsInsideOut());
  32. if (IsEmpty())
  33. return rect;
  34. if (rect.IsEmpty())
  35. return *this;
  36. int minx = MIN(x, rect.x);
  37. int miny = MIN(y, rect.y);
  38. int maxx = x + w > rect.x + rect.w ?
  39. x + w : rect.x + rect.w;
  40. int maxy = y + h > rect.y + rect.h ?
  41. y + h : rect.y + rect.h;
  42. return TBRect(minx, miny, maxx - minx, maxy - miny);
  43. }
  44. TBRect TBRect::Clip(const TBRect &clip_rect) const
  45. {
  46. assert(!clip_rect.IsInsideOut());
  47. TBRect tmp;
  48. if (!Intersects(clip_rect))
  49. return tmp;
  50. tmp.x = MAX(x, clip_rect.x);
  51. tmp.y = MAX(y, clip_rect.y);
  52. tmp.w = MIN(x + w, clip_rect.x + clip_rect.w) - tmp.x;
  53. tmp.h = MIN(y + h, clip_rect.y + clip_rect.h) - tmp.y;
  54. return tmp;
  55. }
  56. // == TBRegion ==========================================================================
  57. TBRegion::TBRegion()
  58. : m_rects(nullptr)
  59. , m_num_rects(0)
  60. , m_capacity(0)
  61. {
  62. }
  63. TBRegion::~TBRegion()
  64. {
  65. RemoveAll(true);
  66. }
  67. void TBRegion::RemoveRect(int index)
  68. {
  69. assert(index >= 0 && index < m_num_rects);
  70. for (int i = index; i < m_num_rects - 1; i++)
  71. m_rects[i] = m_rects[i + 1];
  72. m_num_rects--;
  73. }
  74. void TBRegion::RemoveRectFast(int index)
  75. {
  76. assert(index >= 0 && index < m_num_rects);
  77. m_rects[index] = m_rects[--m_num_rects];
  78. }
  79. void TBRegion::RemoveAll(bool free_memory)
  80. {
  81. m_num_rects = 0;
  82. if (free_memory)
  83. {
  84. delete [] m_rects;
  85. m_rects = nullptr;
  86. m_capacity = 0;
  87. }
  88. }
  89. bool TBRegion::Set(const TBRect &rect)
  90. {
  91. RemoveAll();
  92. return AddRect(rect, false);
  93. }
  94. bool TBRegion::GrowIfNeeded()
  95. {
  96. if (m_num_rects == m_capacity)
  97. {
  98. int new_m_capacity = CLAMP(4, m_capacity * 2, 1024);
  99. TBRect *new_rects = new TBRect[new_m_capacity];
  100. if (!new_rects)
  101. return false;
  102. if (m_rects)
  103. memmove(new_rects, m_rects, sizeof(TBRect) * m_capacity);
  104. delete [] m_rects;
  105. m_rects = new_rects;
  106. m_capacity = new_m_capacity;
  107. }
  108. return true;
  109. }
  110. bool TBRegion::AddRect(const TBRect &rect, bool coalesce)
  111. {
  112. if (coalesce)
  113. {
  114. // If the rect can coalesce with any existing rect,
  115. // just replace it with the union of both, doing coalesce
  116. // check again recursively.
  117. // Searching backwards is most likely to give a hit quicker
  118. // in many usage scenarios.
  119. for (int i = m_num_rects - 1; i >= 0; i--)
  120. {
  121. if ( // Can coalesce vertically
  122. (rect.x == m_rects[i].x && rect.w == m_rects[i].w &&
  123. (rect.y == m_rects[i].y + m_rects[i].h || rect.y + rect.h == m_rects[i].y))
  124. || // Can coalesce horizontally
  125. (rect.y == m_rects[i].y && rect.h == m_rects[i].h &&
  126. (rect.x == m_rects[i].x + m_rects[i].w || rect.x + rect.w == m_rects[i].x))
  127. )
  128. {
  129. TBRect union_rect = m_rects[i].Union(rect);
  130. RemoveRectFast(i);
  131. return AddRect(union_rect, true);
  132. }
  133. }
  134. }
  135. if (!GrowIfNeeded())
  136. return false;
  137. m_rects[m_num_rects++] = rect;
  138. return true;
  139. }
  140. bool TBRegion::IncludeRect(const TBRect &include_rect)
  141. {
  142. for (int i = 0; i < m_num_rects; i++)
  143. {
  144. if (include_rect.Intersects(m_rects[i]))
  145. {
  146. // Make a region containing the non intersecting parts and then include
  147. // those recursively (they might still intersect some other part of the region).
  148. TBRegion inclusion_region;
  149. if (!inclusion_region.AddExcludingRects(include_rect, m_rects[i], false))
  150. return false;
  151. for (int j = 0; j < inclusion_region.m_num_rects; j++)
  152. {
  153. if (!IncludeRect(inclusion_region.m_rects[j]))
  154. return false;
  155. }
  156. return true;
  157. }
  158. }
  159. // Now we know that the rect can be added without overlap.
  160. // Add it with coalesce checking to keep the number of rects down.
  161. return AddRect(include_rect, true);
  162. }
  163. bool TBRegion::ExcludeRect(const TBRect &exclude_rect)
  164. {
  165. int num_rects_to_check = m_num_rects;
  166. for (int i = 0; i < num_rects_to_check; i++)
  167. {
  168. if (m_rects[i].Intersects(exclude_rect))
  169. {
  170. // Remove the existing rectangle we found we intersect
  171. // and add the pieces we don't intersect. New rects
  172. // will be added at the end of the list, so we can decrease
  173. // num_rects_to_check.
  174. TBRect rect = m_rects[i];
  175. RemoveRect(i);
  176. num_rects_to_check--;
  177. i--;
  178. if (!AddExcludingRects(rect, exclude_rect, true))
  179. return false;
  180. }
  181. }
  182. return true;
  183. }
  184. bool TBRegion::AddExcludingRects(const TBRect &rect, const TBRect &exclude_rect, bool coalesce)
  185. {
  186. assert(rect.Intersects(exclude_rect));
  187. TBRect remove = exclude_rect.Clip(rect);
  188. if (remove.y > rect.y)
  189. if (!AddRect(TBRect(rect.x, rect.y, rect.w, remove.y - rect.y), coalesce))
  190. return false;
  191. if (remove.x > rect.x)
  192. if (!AddRect(TBRect(rect.x, remove.y, remove.x - rect.x, remove.h), coalesce))
  193. return false;
  194. if (remove.x + remove.w < rect.x + rect.w)
  195. if (!AddRect(TBRect(remove.x + remove.w, remove.y, rect.x + rect.w - (remove.x + remove.w), remove.h), coalesce))
  196. return false;
  197. if (remove.y + remove.h < rect.y + rect.h)
  198. if (!AddRect(TBRect(rect.x, remove.y + remove.h, rect.w, rect.y + rect.h - (remove.y + remove.h)), coalesce))
  199. return false;
  200. return true;
  201. }
  202. const TBRect &TBRegion::GetRect(int index) const
  203. {
  204. assert(index >= 0 && index < m_num_rects);
  205. return m_rects[index];
  206. }
  207. }; // namespace tb