2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * @file DigitalSetByOctree.ih
19 * @author Bastien DOIGNIES
24 * Header file for module DigitalSetByOctree.cpp
26 * This file is part of the DGtal library.
31 void DigitalSetByOctree<Space>::OctreeIterator::findNextLeaf()
33 while (myMemory.size() != 0)
35 TraversalMemory& m = myMemory.back();
36 // Find next unexplored child. Invalid index means no child was explored yet
37 CellIndex k = (m.currentChildIdx == INVALID_IDX) ? 0 : (m.currentChildIdx + 1);
38 for (; k < CELL_COUNT; ++k)
40 if (myContainer->myNodes[m.lvl][m.idx].children[k] != INVALID_IDX)
42 // Update explored child
43 m.currentChildIdx = k;
50 // Child found, go down
51 if (myMemory.back().lvl == myContainer->myNodes.size() - 1) break;
53 TraversalMemory newMemory;
54 newMemory.lvl = m.lvl + 1;
55 newMemory.domain = splitDomain(m.domain, SIDES_FROM_INDEX[k].data());
56 newMemory.idx = myContainer->myNodes[m.lvl][m.idx].children[k];
57 newMemory.currentChildIdx = INVALID_IDX;
58 myMemory.push_back(std::move(newMemory));
61 { // No more child unexplored, go back to parent
65 if (myMemory.size() != 0)
67 // For leaves, find first non 0 index
68 auto& mem = myMemory.back();
69 if (mem.currentChildIdx == INVALID_IDX)
71 for (CellIndex k = 0; k < CELL_COUNT; ++k)
73 if (myContainer->myNodes[mem.lvl][mem.idx].children[k] != 0)
75 mem.currentChildIdx = k;
84 DigitalSetByOctree<Space>::DigitalSetByOctree(const Domain& dom)
86 const auto lb = dom.lowerBound();
87 const auto ub = dom.upperBound();
90 // Enforce cubical domain with side that are powers of 2
91 CellIndex newSize = 1;
92 for (DimIndex d = 0; d < D; ++d)
94 if (!(size[d] & (size[d] - 1)))
96 newSize = (newSize > size[d]) ? newSize : size[d];
100 CellIndex s = 1 << (static_cast<CellIndex>(std::ceil(std::log2(size[d]))));
101 newSize = (newSize > s) ? newSize : s;
105 for (DimIndex d = 0; d < D; ++d)
108 size_t lvl = static_cast<size_t>(std::ceil(std::log2(newSize)));
110 myNodes[0].push_back(Node());
111 myDomain = CowPtr(new Domain(lb, lb + size));
112 myAdmissibleDomain = CowPtr(new Domain(lb, lb + size - 1));
116 template<class Space>
117 HyperRectDomain<Space> DigitalSetByOctree<Space>::splitDomain(const Domain& domain, const DimIndex* sides)
119 auto lb = domain.lowerBound();
120 const auto ub = domain.upperBound();
121 const auto size = (ub - lb) / 2;
122 for (DimIndex d = 0; d < D; ++d)
123 lb[d] += sides[d] * size[d];
125 return Domain(lb, lb + size);
128 template<class Space>
129 void DigitalSetByOctree<Space>::insert(const Point& p)
131 if (!myAdmissibleDomain->isInside(p)) return;
133 if (myState == State::OCTREE)
135 Domain domain = *myDomain;
136 CellIndex nodeIdx = 0;
138 for (size_t i = 0; i < myNodes.size() - 1; ++i)
140 const auto lb = domain.lowerBound();
141 const auto ub = domain.upperBound();
142 const Point middle = domain.lowerBound() + (ub - lb) / 2;
143 CellIndex childIdx = 0;
145 for (DimIndex d = 0; d < D; ++d)
147 sides[d] = (p[d] >= middle[d]);
148 childIdx += sides[d] * (1 << d);
150 domain = splitDomain(domain, sides);
151 if (myNodes[i][nodeIdx].children[childIdx] == INVALID_IDX)
153 myNodes[i + 1].push_back(Node());
154 myNodes[i][nodeIdx].children[childIdx] = myNodes[i + 1].size() - 1;
156 nodeIdx = myNodes[i][nodeIdx].children[childIdx];
158 // This octree does not create nodes for leaves.
159 // We treat them separately and use children field as
160 // a binary array to indicate whether a voxel is stored or not
161 const auto lb = domain.lowerBound();
162 const auto ub = domain.upperBound();
163 const Point middle = domain.lowerBound() + (ub - lb) / 2;
164 CellIndex childIdx = 0;
165 for (DimIndex d = 0; d < D; ++d) {
166 childIdx += (p[d] >= middle[d]) * (1 << (D - d - 1));
168 if (myNodes[myNodes.size() - 1][nodeIdx].children[childIdx] != 1) {
169 myNodes[myNodes.size() - 1][nodeIdx].children[childIdx] = 1;
175 template<class Space>
176 typename DigitalSetByOctree<Space>::OctreeIterator
177 DigitalSetByOctree<Space>::find(const Point& p) const
179 if (!myAdmissibleDomain->isInside(p)) return end();
181 Domain domain = *myDomain;
182 std::vector<TraversalMemory> traversal;
183 CellIndex nodeIdx = 0;
185 TraversalMemory root;
188 root.domain = domain;
189 traversal.push_back(root);
191 for (size_t i = 0; i < myNodes.size() - 1; ++i)
193 const auto lb = domain.lowerBound();
194 const auto ub = domain.upperBound();
195 const Point middle = domain.lowerBound() + (ub - lb) / 2;
196 CellIndex childIdx = 0;
198 for (DimIndex d = 0; d < D; ++d)
200 sides[d] = (p[d] >= middle[d]);
201 childIdx += sides[d] * (1 << d);
204 if (myNodes[i][nodeIdx].children[childIdx] == INVALID_IDX)
207 domain = splitDomain(domain, sides);
208 nodeIdx = myNodes[i][nodeIdx].children[childIdx];
209 TraversalMemory memory;
210 memory.domain = domain;
212 memory.idx = nodeIdx;
213 memory.currentChildIdx = INVALID_IDX;
215 traversal.back().currentChildIdx = childIdx;
216 traversal.push_back(memory);
219 const auto lb = domain.lowerBound();
220 const auto ub = domain.upperBound();
221 const Point middle = domain.lowerBound() + (ub - lb) / 2;
222 CellIndex childIdx = 0;
223 for (DimIndex d = 0; d < D; ++d)
225 childIdx += (p[d] >= middle[d]) * (1 << (D - d - 1));
227 traversal.back().currentChildIdx = childIdx;
228 if (myNodes[traversal.back().lvl][traversal.back().idx].children[childIdx] != INVALID_IDX)
229 return Iterator(this, traversal);
233 template<class Space>
234 size_t DigitalSetByOctree<Space>::erase(const Iterator& it)
236 if (it.myMemory.size() == 0) return 0;
238 if (myState == State::OCTREE)
240 std::vector<std::pair<CellIndex, CellIndex>> reindex;
241 bool childRemoved = true;
242 auto mem = it.myMemory;
245 const auto& tmem = mem.back();
246 if (childRemoved) // Can disconnect parent from the child
248 CellIndex child = myNodes[tmem.lvl][tmem.idx].children[tmem.currentChildIdx];
249 myNodes[tmem.lvl][tmem.idx].children[tmem.currentChildIdx] = INVALID_IDX;
251 // Reindex children because one was removed in previous layer
252 for (CellIndex i = 0; i < myNodes[tmem.lvl].size(); ++i)
254 auto& node = myNodes[tmem.lvl][i];
255 for (CellIndex j = 0; j < CELL_COUNT; ++j)
257 if (node.children[j] != INVALID_IDX && node.children[j] >= child)
264 for (CellIndex i = 0; i < CELL_COUNT; ++i)
265 count += (myNodes[tmem.lvl][tmem.idx].children[i] != INVALID_IDX);
269 myNodes[tmem.lvl].erase(myNodes[tmem.lvl].begin() + tmem.idx);
274 childRemoved = false;
284 template<class Space>
285 void DigitalSetByOctree<Space>::convertToDAG()
287 std::vector<size_t> correspondances;
288 for (CellIndex i = myNodes.size() - 1; i > 0; --i)
290 std::vector<Node> newNodes;
291 std::map<std::array<CellIndex, CELL_COUNT>, size_t> signatures;
292 std::vector<size_t> newCorrespondances(myNodes[i].size(), 0);
294 for (CellIndex j = 0; j < myNodes[i].size(); ++j) {
295 std::array<CellIndex, CELL_COUNT> signature = {};
297 if (correspondances.size() != 0) // Mostly leaves
299 for (CellIndex k = 0; k < CELL_COUNT; ++k) {
300 if (myNodes[i][j].children[k] != INVALID_IDX)
301 myNodes[i][j].children[k] = correspondances[myNodes[i][j].children[k]];
303 signature[k] = myNodes[i][j].children[k];
308 for (CellIndex k = 0; k < CELL_COUNT; ++k)
309 signature[k] = myNodes[i][j].children[k];
312 auto it = signatures.find(signature);
313 if (it != signatures.end())
315 newCorrespondances[j] = it->second;
319 newNodes.push_back(myNodes[i][j]);
320 signatures[signature] = newNodes.size() - 1;
321 newCorrespondances[j] = newNodes.size() - 1;
324 myNodes[i] = newNodes;
325 correspondances = newCorrespondances;
327 // New pointers for root lvl:
328 if (correspondances.size() != 0)
330 for (CellIndex k = 0; k < CELL_COUNT; ++k)
332 if (myNodes[0][0].children[k] != INVALID_IDX)
333 myNodes[0][0].children[k] = correspondances[myNodes[0][0].children[k]];
338 myState = State::DAG;
341 template<class Space>
342 void DigitalSetByOctree<Space>::dumpOctree() const
344 for (size_t i = 0; i < myNodes.size(); ++i)
346 trace.info() << "===" << i << "===\n";
347 for (size_t j = 0; j < myNodes[i].size(); j++)
349 trace.info() << " -> " << j << "\n";
350 std::bitset<CELL_COUNT> bits;
351 for (size_t k = 0; k < CELL_COUNT; k++)
353 trace.info() << "\t" << k << ": ";
354 if (myNodes[i][j].children[k] == INVALID_IDX)
356 trace.info() << "N.A." << "\n";
360 trace.info() << myNodes[i][j].children[k] << "\n";
364 trace.info() << "\tSig: " << bits.to_ulong() << std::endl;
367 trace.info() << std::flush;
370 template<class Space>
372 auto DigitalSetByOctree<Space>::computeFunction(
373 DigitalSetByOctree<Space>::OctreeIterator start,
374 DigitalSetByOctree<Space>::OctreeIterator end,
379 using ResultType = decltype(f({}, {}));
380 std::map<ComputationCacheKey, ResultType> cache;
381 std::vector<ResultType> rslt;
383 for (DimIndex d = 0; d < D; ++d) neighborSize[d] = range;
384 for (; start != end; ++start)
386 // Shift so the position is at the minimum
387 const auto pos = *start;
388 const auto shiftedPos = pos - myDomain->lowerBound();
389 // Find parent depth that contains the whole neighborhood
390 // This is the maximum over dimension, of how many prefix
391 // bits of position, position - range, position + range
394 for (DimIndex d = 0; d < D; ++d) {
395 const CellIndex coord = static_cast<CellIndex>(shiftedPos[d]);
396 const CellIndex coord_min = coord - range;
397 const CellIndex coord_max = coord + range;
398 // Parent in common, for this dimension, this can be higher than
399 // the depth of the tree for points on the border.
400 const CellIndex l = std::max(boost::core::bit_width(coord ^ coord_min),
401 boost::core::bit_width(coord ^ coord_max));
402 lvl = std::max(lvl, std::min(l, static_cast<CellIndex>(myNodes.size())));
404 const auto& parent = start.myMemory[start.myMemory.size() - lvl];
405 ComputationCacheKey key;
406 key.parentLvl = parent.lvl;
407 key.parentIdx = parent.idx;
408 key.code.resize(D * lvl);
409 for (CellIndex i = 0; i < lvl; ++i)
411 const size_t idx = start.myMemory.size() - i - 1;
412 for (DimIndex d = 0; d < D; ++d)
414 key.code[d + i * D] = SIDES_FROM_INDEX[start.myMemory[idx].currentChildIdx][d];
417 const auto it = cache.find(key);
418 if (it != cache.end())
420 // Cache hit, get value
421 rslt.push_back(it->second);
425 std::vector<Point> neighborhood;
427 // Gather neighborhood
428 const Domain neighborhoodDomain(pos - neighborSize, pos + neighborSize);
429 for (auto dIt = neighborhoodDomain.begin(); dIt != neighborhoodDomain.end(); ++dIt)
431 const auto ptIt = *dIt;
432 if (operator()(ptIt))
433 neighborhood.push_back(ptIt);
435 const auto feval = f(pos, neighborhood);
437 rslt.push_back(feval);