DisjointRectSet.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/DisjointRectSet.h>
  7. namespace Gfx {
  8. bool DisjointRectSet::add_no_shatter(const IntRect& new_rect)
  9. {
  10. if (new_rect.is_empty())
  11. return false;
  12. for (auto& rect : m_rects) {
  13. if (rect.contains(new_rect))
  14. return false;
  15. }
  16. m_rects.append(new_rect);
  17. return true;
  18. }
  19. void DisjointRectSet::shatter()
  20. {
  21. Vector<IntRect, 32> output;
  22. output.ensure_capacity(m_rects.size());
  23. bool pass_had_intersections = false;
  24. do {
  25. pass_had_intersections = false;
  26. output.clear_with_capacity();
  27. for (size_t i = 0; i < m_rects.size(); ++i) {
  28. auto& r1 = m_rects[i];
  29. for (size_t j = 0; j < m_rects.size(); ++j) {
  30. if (i == j)
  31. continue;
  32. auto& r2 = m_rects[j];
  33. if (!r1.intersects(r2))
  34. continue;
  35. pass_had_intersections = true;
  36. auto pieces = r1.shatter(r2);
  37. for (auto& piece : pieces)
  38. output.append(piece);
  39. m_rects.remove(i);
  40. for (; i < m_rects.size(); ++i)
  41. output.append(m_rects[i]);
  42. goto next_pass;
  43. }
  44. output.append(r1);
  45. }
  46. next_pass:
  47. swap(output, m_rects);
  48. } while (pass_had_intersections);
  49. }
  50. void DisjointRectSet::move_by(int dx, int dy)
  51. {
  52. for (auto& r : m_rects)
  53. r.translate_by(dx, dy);
  54. }
  55. bool DisjointRectSet::contains(const IntRect& rect) const
  56. {
  57. if (is_empty() || rect.is_empty())
  58. return false;
  59. // TODO: This could use some optimization
  60. DisjointRectSet remainder(rect);
  61. for (auto& r : m_rects) {
  62. auto shards = remainder.shatter(r);
  63. if (shards.is_empty())
  64. return true;
  65. remainder = move(shards);
  66. }
  67. return false;
  68. }
  69. bool DisjointRectSet::intersects(const IntRect& rect) const
  70. {
  71. for (auto& r : m_rects) {
  72. if (r.intersects(rect))
  73. return true;
  74. }
  75. return false;
  76. }
  77. bool DisjointRectSet::intersects(const DisjointRectSet& rects) const
  78. {
  79. if (this == &rects)
  80. return true;
  81. for (auto& r : m_rects) {
  82. for (auto& r2 : rects.m_rects) {
  83. if (r.intersects(r2))
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. DisjointRectSet DisjointRectSet::intersected(const IntRect& rect) const
  90. {
  91. DisjointRectSet intersected_rects;
  92. intersected_rects.m_rects.ensure_capacity(m_rects.capacity());
  93. for (auto& r : m_rects) {
  94. auto intersected_rect = r.intersected(rect);
  95. if (!intersected_rect.is_empty())
  96. intersected_rects.m_rects.append(intersected_rect);
  97. }
  98. // Since there should be no overlaps, we don't need to call shatter()
  99. return intersected_rects;
  100. }
  101. DisjointRectSet DisjointRectSet::intersected(const DisjointRectSet& rects) const
  102. {
  103. if (&rects == this)
  104. return clone();
  105. if (is_empty() || rects.is_empty())
  106. return {};
  107. DisjointRectSet intersected_rects;
  108. intersected_rects.m_rects.ensure_capacity(m_rects.capacity());
  109. for (auto& r : m_rects) {
  110. for (auto& r2 : rects.m_rects) {
  111. auto intersected_rect = r.intersected(r2);
  112. if (!intersected_rect.is_empty())
  113. intersected_rects.m_rects.append(intersected_rect);
  114. }
  115. }
  116. // Since there should be no overlaps, we don't need to call shatter()
  117. return intersected_rects;
  118. }
  119. DisjointRectSet DisjointRectSet::shatter(const IntRect& hammer) const
  120. {
  121. if (hammer.is_empty())
  122. return clone();
  123. DisjointRectSet shards;
  124. for (auto& rect : m_rects) {
  125. for (auto& shard : rect.shatter(hammer))
  126. shards.add_no_shatter(shard);
  127. }
  128. // Since there should be no overlaps, we don't need to call shatter()
  129. return shards;
  130. }
  131. DisjointRectSet DisjointRectSet::shatter(const DisjointRectSet& hammer) const
  132. {
  133. if (this == &hammer)
  134. return {};
  135. if (hammer.is_empty() || !intersects(hammer))
  136. return clone();
  137. // TODO: This could use some optimization
  138. DisjointRectSet shards = shatter(hammer.m_rects[0]);
  139. auto rects_count = hammer.m_rects.size();
  140. for (size_t i = 1; i < rects_count && !shards.is_empty(); i++) {
  141. if (hammer.m_rects[i].intersects(shards.m_rects)) {
  142. auto shattered = shards.shatter(hammer.m_rects[i]);
  143. shards = move(shattered);
  144. }
  145. }
  146. // Since there should be no overlaps, we don't need to call shatter()
  147. return shards;
  148. }
  149. }