💿🐜 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.

577 lines
16 KiB

  1. /*
  2. * Copyright (C) 2021 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_HYPEROCTREE_HPP
  20. #define ANTKEEPER_GEOM_HYPEROCTREE_HPP
  21. #include "math/compile.hpp"
  22. #include <algorithm>
  23. #include <array>
  24. #include <bit>
  25. #include <concepts>
  26. #include <cstddef>
  27. #include <set>
  28. #include <type_traits>
  29. #include <unordered_set>
  30. namespace geom {
  31. /// Orders in which hyperoctree nodes can be stored and traversed.
  32. enum class hyperoctree_order
  33. {
  34. /// Hyperoctree nodes are unordered, potentially resulting in faster insertions through the internal use of `std::unordered_set` rather than `std::set`.
  35. unordered,
  36. /// Hyperoctree nodes are stored and traversed in depth-first preorder.
  37. dfs_pre,
  38. /// Hyperoctree nodes are stored and traversed in breadth-first order.
  39. bfs
  40. };
  41. /// @private
  42. template <std::unsigned_integral T>
  43. using hyperoctree_dfs_pre_compare = std::less<T>;
  44. /// @private
  45. template <std::unsigned_integral T, std::size_t DepthBits>
  46. struct hyperoctree_bfs_compare
  47. {
  48. constexpr bool operator()(const T& lhs, const T& rhs) const noexcept
  49. {
  50. return std::rotr(lhs, DepthBits) < std::rotr(rhs, DepthBits);
  51. }
  52. };
  53. /// @private
  54. template <hyperoctree_order Order, std::unsigned_integral T, std::size_t DepthBits>
  55. struct hyperoctree_container {};
  56. /// @private
  57. template <std::unsigned_integral T, std::size_t DepthBits>
  58. struct hyperoctree_container<hyperoctree_order::unordered, T, DepthBits>
  59. {
  60. typedef std::unordered_set<T> type;
  61. };
  62. /// @private
  63. template <std::unsigned_integral T, std::size_t DepthBits>
  64. struct hyperoctree_container<hyperoctree_order::dfs_pre, T, DepthBits>
  65. {
  66. typedef std::set<T, hyperoctree_dfs_pre_compare<T>> type;
  67. };
  68. /// @private
  69. template <std::unsigned_integral T, std::size_t DepthBits>
  70. struct hyperoctree_container<hyperoctree_order::bfs, T, DepthBits>
  71. {
  72. typedef std::set<T, hyperoctree_bfs_compare<T, DepthBits>> type;
  73. };
  74. /**
  75. * Hashed linear hyperoctree.
  76. *
  77. * @tparam T Unsigned integral node identifier type.
  78. * @tparam N Number of dimensions.
  79. * @tparam Order Order in which nodes are stored and traversed.
  80. *
  81. * @see http://codervil.blogspot.com/2015/10/octree-node-identifiers.html
  82. * @see https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/
  83. */
  84. template <std::unsigned_integral T, std::size_t N, hyperoctree_order Order = hyperoctree_order::dfs_pre>
  85. class hyperoctree
  86. {
  87. private:
  88. /**
  89. * Finds the maximum hyperoctree depth level from the size of the node type `T` and number of dimensions `N`.
  90. *
  91. * @return Maximum hyperoctree depth level.
  92. *
  93. * @note There is likely a more elegant formula for this. Information about the 2D and 3D cases is given below:
  94. *
  95. * 2D:
  96. * 8 bit ( 1 byte) = max depth 1 ( 4 loc bits, 1 depth bits, 1 divider bit) = 6 bits
  97. * 16 bit ( 2 byte) = max depth 5 ( 12 loc bits, 3 depth bits, 1 divider bit) = 16 bits
  98. * 32 bit ( 4 byte) = max depth 12 ( 26 loc bits, 4 depth bits, 1 divider bit) = 31 bits
  99. * 64 bit ( 8 byte) = max depth 28 ( 58 loc bits, 5 depth bits, 1 divider bit) = 64 bits
  100. * 128 bit (16 byte) = max depth 59 (120 loc bits, 6 depth bits, 1 divider bit) = 127 bits
  101. * 256 bit (32 byte) = max depth 123 (248 loc bits, 7 depth bits, 1 divider bit) = 256 bits
  102. *
  103. * @see https://oeis.org/A173009
  104. *
  105. * 3D:
  106. * 8 bit ( 1 byte) = max depth 1 ( 6 loc bits, 1 depth bits, 1 divider bit) = 8 bits
  107. * 16 bit ( 2 byte) = max depth 3 ( 12 loc bits, 2 depth bits, 1 divider bit) = 15 bits
  108. * 32 bit ( 4 byte) = max depth 8 ( 27 loc bits, 4 depth bits, 1 divider bit) = 32 bits
  109. * 64 bit ( 8 byte) = max depth 18 ( 57 loc bits, 5 depth bits, 1 divider bit) = 63 bits
  110. * 128 bit (16 byte) = max depth 39 (120 loc bits, 6 depth bits, 1 divider bit) = 127 bits
  111. * 256 bit (32 byte) = max depth 81 (243 loc bits, 7 depth bits, 1 divider bit) = 251 bits
  112. *
  113. * @see https://oeis.org/A178420
  114. */
  115. static consteval std::size_t find_max_depth() noexcept
  116. {
  117. std::size_t max_depth = 0;
  118. for (std::size_t i = 1; i <= sizeof(T) * 8; ++i)
  119. {
  120. const std::size_t location_bits = sizeof(T) * 8 - i;
  121. max_depth = location_bits / N - 1;
  122. const std::size_t depth_bits = static_cast<std::size_t>(std::bit_width(max_depth));
  123. if (depth_bits + location_bits < sizeof(T) * 8)
  124. break;
  125. }
  126. return static_cast<T>(max_depth);
  127. }
  128. public:
  129. /// Node identifier type.
  130. typedef T node_type;
  131. /// Number of dimensions.
  132. static constexpr std::size_t dimensions = N;
  133. /// Node storage and traversal order.
  134. static constexpr hyperoctree_order order = Order;
  135. /// Maximum node depth level.
  136. static constexpr node_type max_depth = find_max_depth();
  137. /// Number of bits in the node type.
  138. static constexpr node_type node_bits = sizeof(node_type) * 8;
  139. /// Number of bits required to encode the depth of a node.
  140. static constexpr node_type depth_bits = std::bit_width(max_depth);
  141. /// Number of bits required to encode the Morton location code of a node.
  142. static constexpr node_type location_bits = (max_depth + 1) * N;
  143. /// Number of bits separating the depth and Morton location code in a node identifier.
  144. static constexpr node_type divider_bits = node_bits - (depth_bits + location_bits);
  145. /// Number of children per node.
  146. static constexpr node_type children_per_node = math::compile::exp2<node_type>(N);
  147. /// Number of siblings per node.
  148. static constexpr node_type siblings_per_node = children_per_node - 1;
  149. /// Resolution in each dimension.
  150. static constexpr node_type resolution = math::compile::exp2<node_type>(max_depth);
  151. /// Number of nodes in a full hyperoctree.
  152. static constexpr std::size_t max_node_count = (math::compile::pow<std::size_t>(resolution * 2, N) - 1) / siblings_per_node;
  153. /// Node identifier of the persistent root node.
  154. static constexpr node_type root = 0;
  155. /// Node container type.
  156. typedef typename hyperoctree_container<order, node_type, depth_bits>::type container_type;
  157. /// Iterator type.
  158. typedef typename container_type::iterator iterator;
  159. /// Constant iterator type.
  160. typedef typename container_type::const_iterator const_iterator;
  161. /// Reverse iterator type.
  162. typedef std::conditional<order != hyperoctree_order::unordered, std::reverse_iterator<iterator>, iterator>::type reverse_iterator;
  163. /// Constant reverse iterator type.
  164. typedef std::conditional<order != hyperoctree_order::unordered, std::reverse_iterator<const_iterator>, const_iterator>::type const_reverse_iterator;
  165. /// @name Nodes
  166. /// @{
  167. /**
  168. * Extracts the depth of a node from its identifier.
  169. *
  170. * @param node Node identifier.
  171. *
  172. * @return Depth of the node.
  173. */
  174. static inline constexpr node_type depth(node_type node) noexcept
  175. {
  176. constexpr node_type mask = math::compile::exp2<node_type>(depth_bits) - 1;
  177. return node & mask;
  178. }
  179. /**
  180. * Extracts the Morton location code of a node from its identifier.
  181. *
  182. * @param node Node identifier.
  183. *
  184. * @return Morton location code of the node.
  185. */
  186. static inline constexpr node_type location(node_type node) noexcept
  187. {
  188. return node >> ((node_bits - 1) - depth(node) * N);
  189. }
  190. /**
  191. * Extracts the depth and Morton location code of a node from its identifier.
  192. *
  193. * @param node Node identifier.
  194. *
  195. * @return Array containing the depth of the node, followed by the Morton location code of the node.
  196. */
  197. static constexpr std::array<node_type, 2> split(node_type node) noexcept
  198. {
  199. const node_type depth = hyperoctree::depth(node);
  200. const node_type location = node >> ((node_bits - 1) - depth * N);
  201. return {depth, location};
  202. }
  203. /**
  204. * Constructs an identifier for a node at the given depth and location.
  205. *
  206. * @param depth Depth level.
  207. * @param location Morton location code.
  208. *
  209. * @return Identifier of a node at the given depth and location.
  210. *
  211. * @warning If @p depth exceeds `max_depth`, the returned node identifier is not valid.
  212. */
  213. static inline constexpr node_type node(node_type depth, node_type location) noexcept
  214. {
  215. return (location << ((node_bits - 1) - depth * N)) | depth;
  216. }
  217. /**
  218. * Constructs an identifier for the ancestor of a node at a given depth.
  219. *
  220. * @param node Node identifier.
  221. * @param depth Absolute depth of an ancestor node.
  222. *
  223. * @return Identifier of the ancestor of the node at the given depth.
  224. *
  225. * @warning If @p depth exceeds the depth of @p node, the returned node identifier is not valid.
  226. */
  227. static inline constexpr node_type ancestor(node_type node, node_type depth) noexcept
  228. {
  229. const node_type mask = (~node_type(0)) << ((node_bits - 1) - depth * N);
  230. return (node & mask) | depth;
  231. }
  232. /**
  233. * Constructs an identifier for the parent of a node.
  234. *
  235. * @param node Node identifier.
  236. *
  237. * @return Identifier of the parent node.
  238. */
  239. static inline constexpr node_type parent(node_type node) noexcept
  240. {
  241. return ancestor(node, depth(node) - 1);
  242. }
  243. /**
  244. * Constructs an identifier for the nth sibling of a node.
  245. *
  246. * @param node Node identifier.
  247. * @param n Offset to a sibling, automatically wrapped to `[0, siblings_per_node]`.
  248. *
  249. * @return Identifier of the nth sibling node.
  250. */
  251. static constexpr node_type sibling(node_type node, node_type n) noexcept
  252. {
  253. constexpr node_type mask = (1 << N) - 1;
  254. const auto [depth, location] = split(node);
  255. const node_type sibling_location = (location & (~mask)) | ((location + n) & mask);
  256. return hyperoctree::node(depth, sibling_location);
  257. }
  258. /**
  259. * Constructs an identifier for the nth child of a node.
  260. *
  261. * @param node Node identifier.
  262. * @param n Offset to a sibling of the first child node, automatically wrapped to `[0, siblings_per_node]`.
  263. *
  264. * @return Identifier of the nth child node.
  265. */
  266. static inline constexpr node_type child(node_type node, T n) noexcept
  267. {
  268. return sibling(node + 1, n);
  269. }
  270. /**
  271. * Constructs an identifier for the first common ancestor of two nodes
  272. *
  273. * @param a Identifier of the first node.
  274. * @param b Identifier of the second node.
  275. *
  276. * @return Identifier of the first common ancestor of the two nodes.
  277. */
  278. static constexpr node_type common_ancestor(node_type a, node_type b) noexcept
  279. {
  280. const node_type bits = std::min<node_type>(depth(a), depth(b)) * N;
  281. const node_type marker = (node_type(1) << (node_bits - 1)) >> bits;
  282. const node_type depth = node_type(std::countl_zero((a ^ b) | marker) / N);
  283. return ancestor(a, depth);
  284. }
  285. /// @}
  286. /// Constructs a hyperoctree with a single root node.
  287. hyperoctree():
  288. nodes({root})
  289. {}
  290. /// @name Iterators
  291. /// @{
  292. /**
  293. * Returns an iterator to the first node, in the traversal order specified by hyperoctree::order.
  294. *
  295. * @note Node identifiers cannot be modified through iterators.
  296. */
  297. /// @{
  298. inline iterator begin() noexcept
  299. {
  300. return nodes.begin();
  301. }
  302. inline const_iterator begin() const noexcept
  303. {
  304. return nodes.begin();
  305. }
  306. inline const_iterator cbegin() const noexcept
  307. {
  308. return nodes.cbegin();
  309. }
  310. /// @}
  311. /**
  312. * Returns an iterator to the node following the last node, in the traversal order specified by hyperoctree::order.
  313. *
  314. * @note Node identifiers cannot be modified through iterators.
  315. */
  316. /// @{
  317. inline iterator end() noexcept
  318. {
  319. return nodes.end();
  320. }
  321. inline const_iterator end() const noexcept
  322. {
  323. return nodes.end();
  324. }
  325. inline const_iterator cend() const noexcept
  326. {
  327. return nodes.cend();
  328. }
  329. /// @}
  330. /**
  331. * Returns a reverse iterator to the first node of the revered hyperoctree, in the traversal order specified by hyperoctree::order.
  332. *
  333. * @note Node identifiers cannot be modified through iterators.
  334. * @note If the hyperoctree is unordered, reverse iteration and forward iteration will be identical.
  335. */
  336. /// @{
  337. inline reverse_iterator rbegin() noexcept
  338. {
  339. if constexpr (order != hyperoctree_order::unordered)
  340. return nodes.rbegin();
  341. else
  342. return nodes.begin();
  343. }
  344. inline const_reverse_iterator rbegin() const noexcept
  345. {
  346. if constexpr (order != hyperoctree_order::unordered)
  347. return nodes.rbegin();
  348. else
  349. return nodes.begin();
  350. }
  351. inline const_reverse_iterator crbegin() const noexcept
  352. {
  353. if constexpr (order != hyperoctree_order::unordered)
  354. return nodes.crbegin();
  355. else
  356. return nodes.cbegin();
  357. }
  358. /// @}
  359. /**
  360. * Returns a reverse iterator to the node following the last node of the reverse hyperoctree, in the traversal order specified by hyperoctree::order.
  361. *
  362. * @note Node identifiers cannot be modified through iterators.
  363. * @note If the hyperoctree is unordered, reverse iteration and forward iteration will be identical.
  364. */
  365. /// @{
  366. inline reverse_iterator rend() noexcept
  367. {
  368. if constexpr (order != hyperoctree_order::unordered)
  369. return nodes.rend();
  370. else
  371. return nodes.end();
  372. }
  373. inline const_reverse_iterator rend() const noexcept
  374. {
  375. if constexpr (order != hyperoctree_order::unordered)
  376. return nodes.rend();
  377. else
  378. return nodes.end();
  379. }
  380. inline const_reverse_iterator crend() const noexcept
  381. {
  382. if constexpr (order != hyperoctree_order::unordered)
  383. return nodes.crend();
  384. else
  385. return nodes.cend();
  386. }
  387. /// @}
  388. /// @}
  389. /// @name Capacity
  390. /// @{
  391. /**
  392. * Checks if the hyperoctree has no nodes.
  393. *
  394. * @return `true` if the hyperoctree is empty, `false` otherwise.
  395. *
  396. * @note This function should always return `false`, as the root node is persistent.
  397. */
  398. inline bool empty() const noexcept
  399. {
  400. return nodes.empty();
  401. }
  402. /**
  403. * Checks if the hyperoctree is full.
  404. *
  405. * @return `true` if the hyperoctree is full, `false` otherwise.
  406. */
  407. inline bool full() const noexcept
  408. {
  409. return size() == max_size();
  410. }
  411. /**
  412. * Returns the number of nodes in the hyperoctree.
  413. *
  414. * @return Number of nodes in the hyperoctree.
  415. *
  416. * @note Hyperoctree size will always be greater than or equal to one, as the root node is persistent.
  417. */
  418. inline std::size_t size() const noexcept
  419. {
  420. return nodes.size();
  421. }
  422. /// Returns the total number of nodes the hyperoctree is capable of containing.
  423. constexpr std::size_t max_size() const noexcept
  424. {
  425. return max_node_count;
  426. }
  427. /// @}
  428. /// @name Modifiers
  429. /// @{
  430. /**
  431. * Erases all nodes except the root node, which is persistent.
  432. */
  433. inline void clear()
  434. {
  435. nodes = {root};
  436. }
  437. /**
  438. * Inserts a node and its siblings into the hyperoctree, inserting ancestors as necessary.
  439. *
  440. * @param node Node to insert.
  441. *
  442. * @note The root node is persistent and does not need to be inserted.
  443. */
  444. void insert(node_type node)
  445. {
  446. if (contains(node))
  447. return;
  448. // Insert node
  449. nodes.emplace(node);
  450. // Insert node siblings
  451. for (node_type i = 1; i < children_per_node; ++i)
  452. nodes.emplace(sibling(node, i));
  453. // Insert node ancestors
  454. insert(parent(node));
  455. }
  456. /**
  457. * Erases a node, along with its descendants, siblings, and descendants of siblings.
  458. *
  459. * @param node Identifier of the node to erase.
  460. *
  461. * @note The root node is persistent and cannot be erased.
  462. */
  463. void erase(node_type node)
  464. {
  465. if (node == root || !contains(node))
  466. return;
  467. // Erase node and its descendants
  468. nodes.erase(node);
  469. erase(child(node, 0));
  470. // Erase node siblings
  471. for (node_type i = 0; i < siblings_per_node; ++i)
  472. {
  473. node = sibling(node, 1);
  474. // Erase sibling and its descendants
  475. nodes.erase(node);
  476. erase(child(node, 0));
  477. }
  478. }
  479. /// @}
  480. /// @name Lookup
  481. /// @{
  482. /**
  483. * Checks if a node is contained within the hyperoctree.
  484. *
  485. * @param node Identifier of the node to check for.
  486. *
  487. * @return `true` if the hyperoctree contains the node, `false` otherwise.
  488. */
  489. inline bool contains(node_type node) const
  490. {
  491. return nodes.contains(node);
  492. }
  493. /**
  494. * Checks if a node has no children.
  495. *
  496. * @param node Node identififer.
  497. *
  498. * @return `true` if the node has no children, and `false` otherwise.
  499. */
  500. inline bool is_leaf(node_type node) const
  501. {
  502. return !contains(child(node, 0));
  503. }
  504. /// @}
  505. private:
  506. container_type nodes;
  507. };
  508. /// Hyperoctree with unordered node storage and traversal.
  509. template <std::unsigned_integral T, std::size_t N>
  510. using unordered_hyperoctree = hyperoctree<T, N, hyperoctree_order::unordered>;
  511. } // namespace geom
  512. #endif // ANTKEEPER_GEOM_HYPEROCTREE_HPP