// The old version of this was recursing directly and deeply, leading to stack // overflows in stack restricted environments (e.g. multithreading). The // current version is a fairly direct iteratization of the old, directly // recursive version. void Skeletonize::traverse(const GraphVolume::Node& root, Skeleton& skeleton) { // DFS of nodes from root. Data-wise, Nodes are just integer values. std::stack<GraphVolume::Node> traversal; traversal.push(root); while (!traversal.empty()) { const GraphVolume::Node n = traversal.top(); const int nNeighbors = numNeighbors(n); // Special nodes that open new segments. const bool isOpeningNode = n == _root || nNeighbors != 2; // The second time we see a node, we are in back-traversal, popping from // traversal stack and potentially closing segments. if (_nodeLabels[n] == Visited) { if (isOpeningNode) skeleton.closeSegment(); traversal.pop(); continue; } // Otherwise, we're seeing the node for the first time, so opening / // extending segment. _nodeLabels[n] = Visited; const Position pos = _graphVolume.positions()[n]; const float boundDist = sqrt(boundaryDistance(pos)); if (isOpeningNode) { skeleton.openSegment(pos, 2*boundDist); } else { skeleton.extendSegment(pos, 2*boundDist); } // Iterate through neighbors and put unseen ones onto traversal stack. The // loop checks against nNeighbors to allow early termination. GraphVolume::IncEdgeIt e(_graphVolume.graph(), n); for (int i = 0; i < nNeighbors; ++e /* increment e, not i */) { assert(e != lemon::INVALID); // Should never occur. // Only increment i if we are using this edge. if (_distanceMap[e] != 0.0) continue; ++i; const GraphVolume::Node neighbor = (_graphVolume.graph().u(e) == n ? _graphVolume.graph().v(e) : _graphVolume.graph().u(e)); if (_nodeLabels[neighbor] != Visited) traversal.push(neighbor); } } }