💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
5.1 KiB

  1. /*
  2. * Copyright (C) 2023 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #ifndef ANTKEEPER_GEOM_RECT_PACK_HPP
  20. #define ANTKEEPER_GEOM_RECT_PACK_HPP
  21. #include <engine/geom/primitives/rectangle.hpp>
  22. #include <memory>
  23. namespace geom {
  24. /**
  25. * Node used in 2D rectangle packing.
  26. *
  27. * @see geom::rect_pack
  28. */
  29. template <class T>
  30. struct rect_pack_node
  31. {
  32. /// Scalar type.
  33. using scalar_type = T;
  34. /// Rect type.
  35. using rect_type = rectangle<T>;
  36. /// Pointers to the two children of the node, if any.
  37. std::unique_ptr<rect_pack_node> children[2];
  38. /// Bounds of the node.
  39. rect_type bounds{{T{0}, T{0}}, {T{0}, T{0}}};
  40. /// `true` if the node is occupied, `false` otherwise.
  41. bool occupied{false};
  42. };
  43. /**
  44. * Packs 2D rectangles.
  45. *
  46. * @see geom::rect_pack_node
  47. *
  48. * @see http://www.blackpawn.com/texts/lightmaps/
  49. */
  50. template <class T>
  51. class rect_pack
  52. {
  53. public:
  54. /// Scalar type.
  55. using scalar_type = T;
  56. /// Node type.
  57. using node_type = rect_pack_node<T>;
  58. /**
  59. * Creates a rect pack and sets the bounds of the root node.
  60. *
  61. * @param w Width of the root node.
  62. * @param h Height of the root node.
  63. */
  64. rect_pack(scalar_type w, scalar_type h);
  65. /**
  66. * Creates an empty rect pack.
  67. */
  68. rect_pack();
  69. /**
  70. * Clears the pack and resizes the root node bounds.
  71. *
  72. * @param w New width of the root node.
  73. * @param h New height of the root node.
  74. *
  75. * @see rect_pack::clear()
  76. */
  77. void resize(scalar_type w, scalar_type h);
  78. /// Clear the pack, deallocating all nodes.
  79. void clear();
  80. /**
  81. * Packs a rect into the rect pack.
  82. *
  83. * @param w Width of the rect.
  84. * @param h Height of the rect.
  85. * @return Pointer to the node in which the rect was packed, or `nullptr` if the rect could not be packed.
  86. */
  87. const node_type* pack(scalar_type w, scalar_type h);
  88. /// Returns a reference to the root node.
  89. const node_type& get_root() const;
  90. private:
  91. static node_type* insert(node_type& parent, scalar_type w, scalar_type h);
  92. static void free();
  93. node_type root;
  94. };
  95. template <class T>
  96. rect_pack<T>::rect_pack(scalar_type w, scalar_type h)
  97. {
  98. root.bounds = {T{0}, T{0}, w, h};
  99. }
  100. template <class T>
  101. rect_pack<T>::rect_pack():
  102. rect_pack(0, 0)
  103. {}
  104. template <class T>
  105. void rect_pack<T>::resize(scalar_type w, scalar_type h)
  106. {
  107. clear();
  108. root.bounds = {{T{0}, T{0}}, {w, h}};
  109. }
  110. template <class T>
  111. void rect_pack<T>::clear()
  112. {
  113. root.children[0].reset();
  114. root.children[1].reset();
  115. root.occupied = false;
  116. }
  117. template <class T>
  118. const typename rect_pack<T>::node_type* rect_pack<T>::pack(scalar_type w, scalar_type h)
  119. {
  120. return insert(root, w, h);
  121. }
  122. template <class T>
  123. inline const typename rect_pack<T>::node_type& rect_pack<T>::get_root() const
  124. {
  125. return root;
  126. }
  127. template <class T>
  128. typename rect_pack<T>::node_type* rect_pack<T>::insert(node_type& node, scalar_type w, scalar_type h)
  129. {
  130. // If not a leaf node
  131. if (node.children[0] && node.children[1])
  132. {
  133. // Attempt to insert into first child
  134. node_type* result = insert(*node.children[0], w, h);
  135. if (result)
  136. {
  137. return result;
  138. }
  139. // Cannot fit in first child, attempt to insert into second child
  140. return insert(*node.children[1], w, h);
  141. }
  142. // Abort if node occupied
  143. if (node.occupied)
  144. {
  145. return nullptr;
  146. }
  147. // Determine node dimensions
  148. scalar_type node_w = node.bounds.max.x() - node.bounds.min.x();
  149. scalar_type node_h = node.bounds.max.y() - node.bounds.min.y();
  150. // Check if rect is larger than node
  151. if (w > node_w || h > node_h)
  152. {
  153. return nullptr;
  154. }
  155. // Check for a perfect fit
  156. if (w == node_w && h == node_h)
  157. {
  158. node.occupied = true;
  159. return &node;
  160. }
  161. // Split the node
  162. node.children[0] = std::make_unique<node_type>();
  163. node.children[1] = std::make_unique<node_type>();
  164. // Determine split direction
  165. scalar_type dw = node_w - w;
  166. scalar_type dh = node_h - h;
  167. if (dw > dh)
  168. {
  169. node.children[0]->bounds.min = node.bounds.min;
  170. node.children[0]->bounds.max = {node.bounds.min.x() + w, node.bounds.max.y()};
  171. node.children[1]->bounds.min = {node.bounds.min.x() + w, node.bounds.min.y()};
  172. node.children[1]->bounds.max = {node.bounds.max};
  173. }
  174. else
  175. {
  176. node.children[0]->bounds.min = node.bounds.min;
  177. node.children[0]->bounds.max = {node.bounds.max.x(), node.bounds.min.y() + h};
  178. node.children[1]->bounds.min = {node.bounds.min.x(), node.bounds.min.y() + h};
  179. node.children[1]->bounds.max = {node.bounds.max};
  180. }
  181. // Insert into first child
  182. return insert(*node.children[0], w, h);
  183. }
  184. } // namespace geom
  185. #endif // ANTKEEPER_GEOM_RECT_PACK_HPP