2 * PositionedOctree - define a spatial octree containing Positioned items
3 * arranged by their global cartesian position.
6 // Written by James Turner, started 2012.
8 // Copyright (C) 2012 James Turner
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include "PositionedOctree.hxx"
29 #include "positioned.hxx"
32 #include <algorithm> // for sort
33 #include <cstring> // for memset
36 #include <boost/foreach.hpp>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/structure/exception.hxx>
40 #include <simgear/timing/timestamp.hxx>
48 Node* global_spatialOctree = NULL;
51 void Node::addPolyLine(const PolyLineRef& aLine)
53 lines.push_back(aLine);
56 void Node::visitForLines(const SGVec3d& aPos, double aCutoff,
58 FindLinesDeque& aQ) const
63 aLines.insert(aLines.end(), lines.begin(), lines.end());
66 Node *Node::findNodeForBox(const SGBoxd&) const
68 return const_cast<Node*>(this);
71 Leaf::Leaf(const SGBoxd& aBox, int64_t aIdent) :
77 void Leaf::visit(const SGVec3d& aPos, double aCutoff,
78 FGPositioned::Filter* aFilter,
79 FindNearestResults& aResults, FindNearestPQueue&)
81 int previousResultsSize = aResults.size();
83 NavDataCache* cache = NavDataCache::instance();
87 ChildMap::const_iterator it = children.lower_bound(aFilter->minType());
88 ChildMap::const_iterator end = children.upper_bound(aFilter->maxType());
90 for (; it != end; ++it) {
91 FGPositioned* p = cache->loadById(it->second);
92 double d = dist(aPos, p->cart());
97 if (aFilter && !aFilter->pass(p)) {
102 aResults.push_back(OrderedPositioned(p, d));
105 if (addedCount == 0) {
109 // keep aResults sorted
110 // sort the new items, usually just one or two items
111 std::sort(aResults.begin() + previousResultsSize, aResults.end());
113 // merge the two sorted ranges together - in linear time
114 std::inplace_merge(aResults.begin(),
115 aResults.begin() + previousResultsSize, aResults.end());
118 void Leaf::insertChild(FGPositioned::Type ty, PositionedID id)
120 assert(childrenLoaded);
121 children.insert(children.end(), TypedPositioned(ty, id));
124 void Leaf::loadChildren()
126 if (childrenLoaded) {
130 NavDataCache* cache = NavDataCache::instance();
131 BOOST_FOREACH(TypedPositioned tp, cache->getOctreeLeafChildren(guid())) {
132 children.insert(children.end(), tp);
133 } // of leaf members iteration
135 childrenLoaded = true;
138 ///////////////////////////////////////////////////////////////////////////////
140 Branch::Branch(const SGBoxd& aBox, int64_t aIdent) :
142 childrenLoaded(false)
144 memset(children, 0, sizeof(Node*) * 8);
147 void Branch::visit(const SGVec3d& aPos, double aCutoff,
148 FGPositioned::Filter*,
149 FindNearestResults&, FindNearestPQueue& aQ)
152 for (unsigned int i=0; i<8; ++i) {
157 double d = children[i]->distToNearest(aPos);
159 continue; // exceeded cutoff
162 aQ.push(Ordered<Node*>(children[i], d));
163 } // of child iteration
166 void Branch::visitForLines(const SGVec3d& aPos, double aCutoff,
167 PolyLineList& aLines,
168 FindLinesDeque& aQ) const
170 // add our own lines, easy
171 Node::visitForLines(aPos, aCutoff, aLines, aQ);
173 for (unsigned int i=0; i<8; ++i) {
178 double d = children[i]->distToNearest(aPos);
180 continue; // exceeded cutoff
183 aQ.push_back(children[i]);
184 } // of child iteration
187 static bool boxContainsBox(const SGBoxd& a, const SGBoxd& b)
189 const SGVec3d aMin(a.getMin()),
193 for (int i=0; i<3; ++i) {
194 if ((bMin[i] < aMin[i]) || (bMax[i] > aMax[i])) return false;
200 Node *Branch::findNodeForBox(const SGBoxd &box) const
202 // do this so childAtIndex sees consistent state of
203 // children[] and loaded flag.
206 for (unsigned int i=0; i<8; ++i) {
207 const SGBoxd childBox(boxForChild(i));
208 if (boxContainsBox(childBox, box)) {
209 return childAtIndex(i)->findNodeForBox(box);
213 return Node::findNodeForBox(box);
216 Node* Branch::childForPos(const SGVec3d& aCart) const
218 assert(contains(aCart));
221 SGVec3d center(_box.getCenter());
222 // tests must match indices in SGbox::getCorner
223 if (aCart.x() < center.x()) {
227 if (aCart.y() < center.y()) {
231 if (aCart.z() < center.z()) {
235 return childAtIndex(childIndex);
238 Node* Branch::childAtIndex(int childIndex) const
240 Node* child = children[childIndex];
241 if (!child) { // lazy building of children
242 SGBoxd cb(boxForChild(childIndex));
243 double d2 = dot(cb.getSize(), cb.getSize());
245 assert(((_ident << 3) >> 3) == _ident);
247 // child index is 0..7, so 3-bits is sufficient, and hence we can
248 // pack 20 levels of octree into a int64, which is plenty
249 int64_t childIdent = (_ident << 3) | childIndex;
251 if (d2 < LEAF_SIZE_SQR) {
252 child = new Leaf(cb, childIdent);
254 child = new Branch(cb, childIdent);
257 children[childIndex] = child;
259 if (childrenLoaded) {
260 // childrenLoad is done, so we're defining a new node - add it to the
262 NavDataCache::instance()->defineOctreeNode(const_cast<Branch*>(this), child);
266 return children[childIndex];
269 void Branch::loadChildren() const
271 if (childrenLoaded) {
275 int childrenMask = NavDataCache::instance()->getOctreeBranchChildren(guid());
276 for (int i=0; i<8; ++i) {
277 if ((1 << i) & childrenMask) {
278 childAtIndex(i); // accessing will create!
280 } // of child index iteration
282 // set this after creating the child nodes, so the cache update logic
283 // in childAtIndex knows any future created children need to be added.
284 childrenLoaded = true;
287 int Branch::childMask() const
290 for (int i=0; i<8; ++i) {
299 bool findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter* aFilter, FGPositionedList& aResults, int aCutoffMsec)
302 FindNearestPQueue pq;
303 FindNearestResults results;
304 pq.push(Ordered<Node*>(global_spatialOctree, 0));
305 double cut = aCutoffM;
310 while (!pq.empty() && (tm.elapsedMSec() < aCutoffMsec)) {
311 if (!results.empty()) {
312 // terminate the search if we have sufficent results, and we are
313 // sure no node still on the queue contains a closer match
314 double furthestResultOrder = results.back().order();
315 if ((results.size() >= aN) && (furthestResultOrder < pq.top().order())) {
316 // clear the PQ to mark this has 'full results' instead of partial
317 pq = FindNearestPQueue();
322 Node* nd = pq.top().get();
325 nd->visit(aPos, cut, aFilter, results, pq);
326 } // of queue iteration
328 // depending on leaf population, we may have (slighty) more results
330 unsigned int numResults = std::min((unsigned int) results.size(), aN);
332 aResults.resize(numResults);
333 for (unsigned int r=0; r<numResults; ++r) {
334 aResults[r] = results[r].get();
340 bool findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filter* aFilter, FGPositionedList& aResults, int aCutoffMsec)
343 FindNearestPQueue pq;
344 FindNearestResults results;
345 pq.push(Ordered<Node*>(global_spatialOctree, 0));
346 double rng = aRangeM;
351 while (!pq.empty() && (tm.elapsedMSec() < aCutoffMsec)) {
352 Node* nd = pq.top().get();
355 nd->visit(aPos, rng, aFilter, results, pq);
356 } // of queue iteration
358 unsigned int numResults = results.size();
360 aResults.resize(numResults);
361 for (unsigned int r=0; r<numResults; ++r) {
362 aResults[r] = results[r].get();
368 } // of namespace Octree
370 } // of namespace flightgear