/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TNT_UTILS_QUADTREE_H #define TNT_UTILS_QUADTREE_H #include #include #include #include #include #include #include namespace utils { namespace QuadTreeUtils { /** * 16-bits morton encoding * @param x 8-bits horizontal coordinate * @param y 8-bits vertical coordinate * @return morton encoding of (x,y) */ static inline constexpr uint16_t morton(uint8_t x, uint8_t y) noexcept { uint32_t r = x | (uint32_t(y) << 16); r = (r | (r << 4u)) & 0x0f0f0f0fu; r = (r | (r << 2u)) & 0x33333333u; r = (r | (r << 1u)) & 0x55555555u; return uint16_t(r | (r >> 15u)); } /** * size of a quad-tree of height `height` * @param height height of the Quad-tree * @return the number of elements in the tree */ static inline constexpr size_t size(size_t height) noexcept { return QuadTreeUtils::morton(uint8_t((1u << height) - 1u), 0u); } /** * Index in the QuadTreeArray of a Quad-tree node referenced by its height and code * @param l height of the node * @param code morton code of the node * @return index in the QuadTreeArray of this node */ static inline constexpr size_t index(size_t l, size_t code) noexcept { return size(l) + code; } /** * Index in the QuadTreeArray of the parent of the specified node * @param l height of the node * @param code morton code of the node * @return index in the QuadTreeArray of this node's parent */ static inline constexpr size_t parent(size_t l, size_t code) noexcept { assert_invariant(l > 0); return index(l - 1u, code >> 2u); } // integrated unit-tests! static_assert(size(0) == 0); static_assert(size(1) == 1); static_assert(size(2) == 5); static_assert(size(3) == 21); static_assert(size(4) == 85); } // namespace QuadTreeUtils /** * A Quad-tree encoded as an array. * @tparam T Type of the quad-tree nodes * @tparam HEIGHT Height of the quad-tree, muse be <= 4 */ template class QuadTreeArray : public std::array { static_assert(HEIGHT <= 4, "QuadTreeArray height must be <= 4"); // Simple fixed capacity stack template::value>::type> class stack { TYPE mElements[CAPACITY]; size_t mSize = 0; public: bool empty() const noexcept { return mSize == 0; } void push(TYPE const& v) noexcept { assert_invariant(mSize < CAPACITY); mElements[mSize++] = v; } void pop() noexcept { assert_invariant(mSize > 0); --mSize; } const TYPE& back() const noexcept { return mElements[mSize - 1]; } }; public: using code_t = uint8_t; struct NodeId { int8_t l; // height of the node or -1 if invalid code_t code; // morton code of the node }; enum class TraversalResult { EXIT, // end traversal RECURSE, // proceed with the children SKIP_CHILDREN // skip children }; static inline constexpr size_t height() noexcept { return HEIGHT; } /** * non-recursive depth-first traversal * * @tparam Process closure to process each visited node * @param l height of the node to start with * @param code code of the node to start with * @param h maximum height to visit, must be < height() * @param process closure to process each visited node */ template>::type> static void traverse(int8_t l, code_t code, size_t maxHeight, Process&& process) noexcept { assert_invariant(maxHeight < height()); const int8_t h = int8_t(maxHeight); stack stack; stack.push({ l, code }); while (!stack.empty()) { NodeId curr = stack.back(); stack.pop(); TraversalResult r = process(curr); if (r == TraversalResult::EXIT) { break; } if (r == TraversalResult::RECURSE && curr.l < h) { for (size_t c = 0; c < 4; c++) { stack.push({ int8_t(curr.l + 1u), code_t((curr.code << 2u) | (3u - c)) }); } } } } template>::type> static void traverse(int8_t l, code_t code, Node&& process) noexcept { traverse(l, code, height() - 1, std::forward(process)); } }; } // namespace utils #endif //TNT_UTILS_QUADTREE_H