/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2005 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * . * \**************************************************************************/ #include #include #include #include #include /*! \class SoCompactPathList Inventor/misc/SoCompactPathList.h \brief The SoCompactPathList class is used to optimize SoPathList traversal. This class creates a lookup table which is used while doing SoPathList traversal. For each node in the paths, it stores the following information: 1. the number of distinct children at that level 2. a child index to each distinct child to be used for IN_PATH travesal 3. an index to each child's lookup table position 1. and 2. are used for optimized IN_PATH action traversal. 3. is used internally to traverse this lookup table. */ #define FULL_PATH(list, i) ((SoFullPath *) list[i]) /*! Constructor. */ SoCompactPathList::SoCompactPathList(const SoPathList & list) : stack(256) { assert(list.getLength()); SoNode * head = FULL_PATH(list, 0)->getHead(); int numnodes = 0; for (int i = 0; i < list.getLength(); i++) { assert(FULL_PATH(list, i)->getHead() == head); numnodes += FULL_PATH(list, i)->getLength() - 1; } // 3 entries for each node + one extra for the root. This is a // worst-case size, but memory usage isn't an issue for this class // (memory usage will be small compared to memory used by the paths) this->lookupsize = 3 * numnodes + 1; this->lookuptable = new int[this->lookupsize]; this->createLookupTable(0, 1, list, 0, list.getLength()); this->reset(); } /*! Destructor. */ SoCompactPathList::~SoCompactPathList() { delete[] this->lookuptable; } /*! Reset path list. */ void SoCompactPathList::reset() { this->stack.truncate(0); this->lookupidx = 0; } /*! Returns the children to be used for IN_PATH traversal. */ void SoCompactPathList::getChildren(int & numindices, const int *& indices) { assert(this->lookupidx >= 0); numindices = this->getNumIndices(); indices = this->lookuptable + this->getStartIndex(); } /*! Push \a childindex to the current path. */ SbBool SoCompactPathList::push(int childindex) { assert(this->lookupidx >= 0); // this function should be used only for IN_PATH traversal // push current node to be able to restore it in pop() this->stack.push(this->lookupidx); int i; const int n = this->getNumIndices(); const int idx = this->getStartIndex(); // check if childindex is IN_PATH or not for (i = 0; i < n; i++) { if (this->lookuptable[idx + i] == childindex) break; } if (i < n) { // IN_PATH // get ready to traverse the child this->lookupidx = this->getChildIndex(i); assert(this->lookupidx < this->lookupsize); } else { // OFF_PATH this->lookupidx = -1; } return (this->lookupidx >= 0); } /*! Pop the current node off the path. */ void SoCompactPathList::pop() { this->lookupidx = this->stack.pop(); } /*! Precalculate tables for each node in the paths. */ int SoCompactPathList::createLookupTable(int curidx, int depth, const SoPathList & list, int firstpath, int numpaths) { // When we get to the tail, store a 0 for no children. The traversal // will switch to BELOW_PATH when this happens. if (depth >= FULL_PATH(list, firstpath)->getLength()) { this->lookuptable[curidx] = 0; return curidx + 1; } int i; // count the number of IN_PATH indices that we need to create and // fill in first part of the lookup table. We do this by finding the // number of distinct children in all the paths we're traversing. int numchildren = 0; int prevchildidx = -1; for (i = 0; i < numpaths; i++) { int childidx = FULL_PATH(list, firstpath + i)->getIndex(depth); if (childidx != prevchildidx) { // fill in the IN_PATH table indices this->lookuptable[curidx + 1 + numchildren] = childidx; numchildren++; prevchildidx = childidx; } } // store the size of this node's tables this->lookuptable[curidx] = numchildren; // Find next free position in lookup table. We need to store 1 + // 2*numchildren integers for this node (size + IN_PATH table and // lookup table position for each child) int nextidx = curidx + 1 + 2 * numchildren; // Recurse and fill in info for each child in the lookup table int curchild = 0; i = 0; while (i < numpaths) { int startpath = i + firstpath; int childidx = FULL_PATH(list, firstpath + i)->getIndex(depth); int pathcounter = 1; i++; // find all paths that go through childidx while ((i < numpaths) && (FULL_PATH(list, firstpath + i)->getIndex(depth) == childidx)) { i++; pathcounter++; } this->lookuptable[curidx + 1 + numchildren + curchild] = nextidx; nextidx = this->createLookupTable(nextidx, depth + 1, list, startpath, pathcounter); curchild++; } assert(curchild == numchildren); return nextidx; } #undef FULL_PATH /*! Returns the number of IN_PATH children for the current node. */ int SoCompactPathList::getNumIndices(void) { assert(this->lookupidx >= 0); return this->lookuptable[this->lookupidx]; } /*! Returns the position of the IN_PATH table. */ int SoCompactPathList::getStartIndex(void) { assert(this->lookupidx >= 0); return this->lookupidx + 1; } /*! Returns the lookup table position for \a child. */ int SoCompactPathList::getChildIndex(const int child) { const int idx = this->getStartIndex() + this->getNumIndices() + child; assert(idx < this->lookupsize); return this->lookuptable[idx]; } /*! Returns the depth of the current node. */ int SoCompactPathList::getDepth(void) const { return 1 + this->stack.getLength(); }