cesium-native 0.51.0
Loading...
Searching...
No Matches
TreeTraversalState.h
1#pragma once
2
3#include <CesiumUtility/Assert.h>
4
5#include <cstddef>
6#include <memory>
7#include <type_traits>
8#include <unordered_map>
9#include <vector>
10
11namespace CesiumUtility {
12
13template <typename T> class IntrusivePointer;
14
39template <typename TNodePointer, typename TState> class TreeTraversalState {
40public:
44 using TNodeInstance = std::remove_cvref_t<
45 typename std::pointer_traits<TNodePointer>::element_type>;
46
51
56
62 return this->_previousTraversal.size();
63 }
64
70 return this->_currentTraversal.size();
71 }
72
78 // If this assertion fails, it indicates a traversal is already in progress.
79 CESIUM_ASSERT(this->_parentIndices.empty());
80
81 this->_previousTraversal.swap(this->_currentTraversal);
82 this->_currentTraversal.clear();
83 this->_previousTraversalNextNodeIndex = 0;
84 }
85
95 void beginNode(const TNodePointer& pNode) {
96 int64_t currentTraversalIndex = int64_t(this->_currentTraversal.size());
97 int64_t previousTraversalIndex = this->_previousTraversalNextNodeIndex;
98
99 if (previousTraversalIndex >= 0 &&
100 size_t(previousTraversalIndex) < this->_previousTraversal.size()) {
101 const TraversalData& previousData =
102 this->_previousTraversal[size_t(previousTraversalIndex)];
103 if (previousData.pNode == pNode) {
104 ++this->_previousTraversalNextNodeIndex;
105 } else {
106 previousTraversalIndex = -1;
107 }
108 } else {
109 previousTraversalIndex = -1;
110 }
111
112 this->_parentIndices.emplace_back(TraversalIndices{
113 .previous = previousTraversalIndex,
114 .current = currentTraversalIndex});
115
116 this->_currentTraversal.emplace_back(TraversalData{pNode, -1, TState()});
117 }
118
128 TNodePointer getCurrentNode() const noexcept {
129 if (this->_parentIndices.empty())
130 return nullptr;
131
132 return this->currentData().pNode;
133 }
134
142 bool wasCurrentNodePreviouslyTraversed() const noexcept {
143 return this->previousState() != nullptr;
144 }
145
154 const TState* previousState() const {
155 const TraversalData* pData = this->previousData();
156 return pData ? &pData->state : nullptr;
157 }
158
167 TState& currentState() {
168 TraversalData& data = this->currentData();
169 return data.state;
170 }
171
179 const TState& currentState() const {
180 const TraversalData& data = this->currentData();
181 return data.state;
182 }
183
197 void finishNode([[maybe_unused]] const TNodePointer& pNode) {
198 // An assertion failure here indicates mismatched calls to beginNode /
199 // finishNode.
200 CESIUM_ASSERT(!this->_currentTraversal.empty());
201 CESIUM_ASSERT(!this->_parentIndices.empty());
202 CESIUM_ASSERT(this->currentData().pNode == pNode);
203
204 this->currentData().nextSiblingIndex =
205 int64_t(this->_currentTraversal.size());
206
207 // Now that this node is done, skip its subtree, if any, in the previous
208 // traversal. If this finished node doesn't exist in the previous traversal,
209 // look for the next node in the previous traversal at the current position.
210 const TraversalData* pPreviousData = this->previousData();
211 if (pPreviousData) {
212 CESIUM_ASSERT(pPreviousData->nextSiblingIndex >= 0);
213 this->_previousTraversalNextNodeIndex = pPreviousData->nextSiblingIndex;
214 }
215
216 this->_parentIndices.pop_back();
217 }
218
230 template <typename Func> void forEachPreviousChild(Func&& callback) const {
231 int64_t parentPreviousIndex = this->previousDataIndex();
232 if (parentPreviousIndex < 0) {
233 // Current node was not previously traversed.
234 return;
235 }
236
237 const TraversalData& parentPreviousData =
238 this->_previousTraversal[size_t(parentPreviousIndex)];
239
240 for (size_t i = size_t(parentPreviousIndex + 1);
241 i < size_t(parentPreviousData.nextSiblingIndex);) {
242 CESIUM_ASSERT(i < this->_previousTraversal.size());
243 const TraversalData& data = this->_previousTraversal[i];
244 callback(data.pNode, data.state);
245 CESIUM_ASSERT(
246 data.nextSiblingIndex >= 0 && size_t(data.nextSiblingIndex) > i);
247 i = size_t(data.nextSiblingIndex);
248 }
249 }
250
262 template <typename Func>
263 void forEachPreviousDescendant(Func&& callback) const {
264 int64_t parentPreviousIndex = this->previousDataIndex();
265 if (parentPreviousIndex < 0) {
266 // Current node was not previously traversed.
267 return;
268 }
269
270 const TraversalData& parentPreviousData =
271 this->_previousTraversal[size_t(parentPreviousIndex)];
272
273 for (size_t i = size_t(parentPreviousIndex + 1);
274 i < size_t(parentPreviousData.nextSiblingIndex);
275 ++i) {
276 CESIUM_ASSERT(i < this->_previousTraversal.size());
277 const TraversalData& data = this->_previousTraversal[i];
278 callback(data.pNode, data.state);
279 }
280 }
281
295 template <typename Func> void forEachCurrentDescendant(Func&& callback) {
296 int64_t parentCurrentIndex = this->currentDataIndex();
297 CESIUM_ASSERT(parentCurrentIndex >= 0);
298
299 const TraversalData& parentCurrentData =
300 this->_currentTraversal[size_t(parentCurrentIndex)];
301
302 size_t endIndex = parentCurrentData.nextSiblingIndex >= 0
303 ? size_t(parentCurrentData.nextSiblingIndex)
304 : this->_currentTraversal.size();
305
306 for (size_t i = size_t(parentCurrentIndex + 1); i < endIndex; ++i) {
307 TraversalData& data = this->_currentTraversal[i];
308 callback(data.pNode, data.state);
309 }
310 }
311
320 std::unordered_map<TRawConstNodePointer, TState>
322 return this->slowlyGetStates(this->_currentTraversal);
323 }
324
333 std::unordered_map<TRawConstNodePointer, TState>
335 return this->slowlyGetStates(this->_previousTraversal);
336 }
337
338private:
339 struct TraversalData {
340 TNodePointer pNode;
341 int64_t nextSiblingIndex;
342 TState state;
343 };
344
345 struct TraversalIndices {
346 int64_t previous;
347 int64_t current;
348 };
349
350 int64_t previousDataIndex() const {
351 CESIUM_ASSERT(!this->_parentIndices.empty());
352
353 int64_t result = this->_parentIndices.back().previous;
354
355 CESIUM_ASSERT(
356 result == -1 ||
357 (result >= 0 && size_t(result) < this->_previousTraversal.size()));
358
359 return result;
360 }
361
362 int64_t currentDataIndex() const {
363 CESIUM_ASSERT(!this->_parentIndices.empty());
364
365 // An assertion failure here may indicate beginTraversal wasn't called.
366 CESIUM_ASSERT(
367 this->_parentIndices.back().current >= 0 &&
368 this->_parentIndices.back().current <
369 static_cast<int64_t>(this->_currentTraversal.size()));
370
371 return this->_parentIndices.back().current;
372 }
373
374 const TraversalData* previousData() const {
375 int64_t previousIndex = this->previousDataIndex();
376 if (previousIndex < 0)
377 return nullptr;
378
379 const TraversalData& previousData =
380 this->_previousTraversal[size_t(previousIndex)];
381
382 CESIUM_ASSERT(previousData.pNode == this->currentData().pNode);
383
384 return &previousData;
385 }
386
387 TraversalData& currentData() {
388 int64_t currentIndex = this->currentDataIndex();
389 CESIUM_ASSERT(currentIndex >= 0);
390 return this->_currentTraversal[size_t(currentIndex)];
391 }
392
393 const TraversalData& currentData() const {
394 int64_t currentIndex = this->currentDataIndex();
395 CESIUM_ASSERT(currentIndex >= 0);
396 return this->_currentTraversal[size_t(currentIndex)];
397 }
398
399 std::unordered_map<TRawConstNodePointer, TState>
400 slowlyGetStates(const std::vector<TraversalData>& traversalData) const {
401 std::unordered_map<TRawConstNodePointer, TState> result;
402
403 for (const TraversalData& data : traversalData) {
404 result[data.pNode] = data.state;
405 }
406
407 return result;
408 }
409
410 std::vector<TraversalData> _previousTraversal;
411 std::vector<TraversalData> _currentTraversal;
412
413 // A stack of indices into the previous and current traversals. When
414 // `beginNode` is called, the index of that node within each traversal is
415 // pushed onto the end of this vector. This is always the index of the _last_
416 // node in the `_currentTraversal`, because `beginNode` always adds a new
417 // entry to the end of the `currentTraversal`. The previous traversal index
418 // may be -1 if the current node was not visited at all in the previous
419 // traversal. `finishNode` pops the last entry off the end of this array.
420 std::vector<TraversalIndices> _parentIndices;
421
422 // The index of the next node in the previous traversal. In `beginNode`, this
423 // new node is added to the end of `_currentTraversal`. In
424 // `_previousTraversal`, if it exists at all, it will be found at this index.
425 int64_t _previousTraversalNextNodeIndex = 0;
426};
427
428} // namespace CesiumUtility
A smart pointer that calls addReference and releaseReference on the controlled object.
Associates state (arbitrary data) with each node during partial, depth-first traversal of a tree....
size_t getNodeCountInCurrentTraversal() const
Gets the total number of nodes that have been visited so far in the current traversal.
void forEachCurrentDescendant(Func &&callback)
Invokes a callback for each descendant (children, grandchildren, etc.) of the current node that has b...
void beginNode(const TNodePointer &pNode)
Begins traversing a node in the tree. This node becomes the "current" node.
void finishNode(const TNodePointer &pNode)
Ends traversal of the given node.
const TState * previousState() const
Gets the state of the current node on the previous traversal. The current node is the one for which b...
bool wasCurrentNodePreviouslyTraversed() const noexcept
Determines if the current node was visited in the previous traversal.
TNodePointer getCurrentNode() const noexcept
Gets the current node in the traversal.
void beginTraversal()
Begins a new traversal of the tree. The "current" and "previous" traversals are swapped,...
std::unordered_map< TRawConstNodePointer, TState > slowlyGetPreviousStates() const
Gets a mapping of nodes to states for the previous traversal.
void forEachPreviousDescendant(Func &&callback) const
Invokes a callback for each descendant (children, grandchildren, etc.) of the current node that was t...
TState & currentState()
Gets the state of the current node during the current traversal. The current node is the one for whic...
void forEachPreviousChild(Func &&callback) const
Invokes a callback for each child of the current node that was traversed in the previous traversal.
std::remove_cvref_t< typename std::pointer_traits< Tile::Pointer >::element_type > TNodeInstance
const TState & currentState() const
Gets the state of the current node during the current traversal. The current node is the one for whic...
size_t getNodeCountInPreviousTraversal() const
Gets the total number of nodes that were visited in the previous traversal.
std::unordered_map< TRawConstNodePointer, TState > slowlyGetCurrentStates() const
Gets a mapping of nodes to states for the current traversal.
Utility classes for Cesium.