/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 1991-2008 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Description \*---------------------------------------------------------------------------*/ #include "treeNode.H" #include "octree.H" #include "treeLeaf.H" #include "treeBoundBox.H" #include "long.H" #include "linePointRef.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // template const label treeNode::leafOffset = 100; template const labelList treeNode::dummy(1); // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template void treeNode::setAsNode(const label octant) { subNodeTypes_ |= (0x1 << octant); } template void treeNode::setAsLeaf(const label octant) { subNodeTypes_ &= ~(0x1 << octant); } // Set pointer to sub node template void treeNode::setNodePtr(const label octant, treeElem* treeNodePtr) { setAsNode(octant); subNodes_[octant] = treeNodePtr; } // Set pointer to sub leaf template void treeNode::setLeafPtr(const label octant, treeElem* treeLeafPtr) { setAsLeaf(octant); subNodes_[octant] = treeLeafPtr; } template void treeNode::setVolType(const label octant, const label type) { if ((type < 0) || (type > 3)) { FatalErrorIn("treeNode::setVolType(const label, const label)") << "Type " << type << " not within range 0..3" << endl; } // Clear out two bits at position 2*octant volType_ &= ~(0x3 << 2*octant); // Add the two bits of type volType_ |= (type << 2*octant); } template void treeNode::space(Ostream& os, const label n) { for(label i=0; i template const treeLeaf* treeNode::findLeafLineOctant ( const int level, const Type& shapes, const label octant, const vector& direction, point& start, const point& end ) const { static const char* functionName = "treeNode::findLeafLineOctant" "(const int, const Type&, const label, const vector&," " point&, const point&)"; if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLineOctant : bb:" << this->bb() << " start:" << start << " end:" << end << " mid:" << mid() << " Searching octant:" << octant << endl; } if (subNodes()[octant]) { if (isNode(octant)) { // Node: recurse into subnodes const treeNode* subNodePtr = getNodePtr(octant); if (subNodePtr->bb().contains(direction, start)) { // Search on lower level const treeLeaf* subLeafPtr = subNodePtr->findLeafLine ( level + 1, shapes, start, end ); if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLineOctant : bb:" << this->bb() << " returning from sub treeNode" << " with start:" << start << " subLeaf:" << long(subLeafPtr) << endl; } return subLeafPtr; } else { FatalErrorIn(functionName) << "Sub node " << subNodePtr->bb() << " at octant " << octant << " does not contain start " << start << abort(FatalError); } } else { // Leaf const treeLeaf* subLeafPtr = getLeafPtr(octant); if (subLeafPtr->bb().contains(direction, start)) { // Step to end of subleaf bb point tmp; if ( !subLeafPtr->bb().intersects ( end, start, tmp ) ) { FatalErrorIn(functionName) << "Sub leaf contains start " << start << " but line does not intersect its bb " << subLeafPtr->bb() << abort(FatalError); } start = tmp; if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLineOctant : returning from intersecting" << " treeLeaf " << subLeafPtr->bb() << " with start:" << start << " subLeaf:" << long(subLeafPtr) << endl; } return subLeafPtr; } else { FatalErrorIn(functionName) << "Sub leaf " << subLeafPtr->bb() << " at octant " << octant << " does not contain start " << start << abort(FatalError); } } } else { // Empty subNode. Transfer across. const treeBoundBox emptyBb = this->bb().subBbox(mid(), octant); if (emptyBb.contains(direction, start)) { if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLineOctant : Empty node. Octant:" << octant << " start:" << start << " bb:" << this->bb() << " emptyBb:" << emptyBb << endl; } // Update start by clipping to emptyBb point tmp; if ( !emptyBb.intersects ( end, start, tmp ) ) { FatalErrorIn(functionName) << "Empty node contains start " << start << " but line does not intersect its (calculated)" << " bb " << emptyBb << endl << "This might be due to truncation error" << abort(FatalError); } start = tmp; if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLineOctant : returning from intersecting with" << " empty " << emptyBb << " with start:" << start << " subLeaf:" << 0 << endl; } return NULL; } else { FatalErrorIn(functionName) << "Empty node " << emptyBb << " at octant " << octant << " does not contain start " << start << abort(FatalError); } } FatalErrorIn(functionName) << "Octant " << octant << " of cube " << this->bb() << " does not contain start " << start << abort(FatalError); return NULL; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // Construct from components template treeNode::treeNode(const treeBoundBox& bb) : treeElem(bb), treeNodeName(), mid_(bb.mid()), subNodeTypes_(0), volType_(0) { for(label octant=0; octant<8; octant++) { subNodes_[octant] = NULL; setVolType(octant, octree::UNKNOWN); } } // Construct from Istream template treeNode::treeNode ( Istream& is ) { is >> *this; } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // template treeNode::~treeNode() { for(int octant=0; octant<8; octant++) { if (subNodes()[octant]) { if (isNode(octant)) { delete getNodePtr(octant); } else { delete getLeafPtr(octant); } } } } // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // // Distributes cells to subLeaves template void treeNode::distribute ( const label level, octree& top, const Type& shapes, const labelList& indices ) { if (debug & 1) { space(Pout, level); Pout<< "treeNode::distributing " << indices.size() << endl; } // Create subLeaves if nessecary for(label octant=0; octant<8; octant++) { if (subNodes()[octant]) { printNode(Pout, level); FatalErrorIn ( "treeNode::distribute(const label, octree&, " "const Type&, const labelList&)" ) << "subNode already available at octant:" << octant << abort(FatalError); } else { treeLeaf* subLeafPtr = new treeLeaf ( this->bb().subBbox(mid(), octant), indices.size() ); top.setLeaves(top.nLeaves() + 1); setLeafPtr(octant, subLeafPtr); } } // add cells to correct sub leaf forAll(indices, i) { const label shapei = indices[i]; for(label octant=0; octant<8; octant++) { treeLeaf* leafPtr = getLeafPtr(octant); if (shapes.overlaps(shapei, leafPtr->bb())) { if (debug == 1) { space(Pout, level); Pout<< "inserting " << shapei; shapes.write(Pout, shapei); Pout<< " into " << leafPtr->bb() << endl; } leafPtr->insert(shapei); top.setEntries(top.nEntries() + 1); } } } // Trim size of subLeaves for(label octant=0; octant<8; octant++) { treeLeaf* subLeafPtr = getLeafPtr(octant); if (subLeafPtr->size() == 0) { // Contains no data. Delete. setLeafPtr(octant, NULL); delete subLeafPtr; top.setLeaves(top.nLeaves() - 1); } else { // Trim to actual size. subLeafPtr->trim(); } } if (debug & 1) { space(Pout, level); Pout<< "end of treeNode::distribute" << endl; } } // Descends to refineLevel and checks the subLeaves for redistribution template void treeNode::redistribute ( const label level, octree& top, const Type& shapes, const label refineLevel ) { if (debug & 1) { space(Pout, level); Pout<< "treeNode::redistribute with level:" << level << " refineLevel:" << refineLevel << endl; } // Descend to correct level if (level < refineLevel) { for(label octant=0; octant<8; octant++) { if (subNodes()[octant]) { if (isNode(octant)) { getNodePtr(octant)->redistribute ( level+1, top, shapes, refineLevel ); } } } } else { // Reached correct (should also be deepest) level of treeNode if (debug & 1) { space(Pout, level); Pout<< "treeNode::redistribute : now at correct level" << endl; } // handle redistribution of sub leaves for(label octant=0; octant<8; octant++) { if (subNodes()[octant]) { if (isNode(octant)) { FatalErrorIn ( "treeNode::redistribute(const int, octree& top," "const int, const treeBoundBox&)" ) << "found treeNode instead of treeLeaf" << endl << abort(FatalError); } else { treeLeaf* leafPtr = getLeafPtr(octant); treeLeaf* newSubPtr = leafPtr->redistribute ( level, top, shapes ); if (newSubPtr && (newSubPtr != leafPtr)) { // redistribute has created nodePtr // so delete no longer used subPtr and update info if (debug & 1) { Pout<< "deleting " << top.nEntries() - leafPtr->size() << " entries" << endl; } top.setEntries(top.nEntries() - leafPtr->size()); delete leafPtr; top.setLeaves(top.nLeaves() - 1); setNodePtr(octant, newSubPtr); } } } } if (debug & 1) { space(Pout, level); Pout<< "end of treeNode::redistribute for correct level" << endl; } } if (debug & 1) { space(Pout, level); Pout<< "return from treeNode::redistribute with bb:" << this->bb() << endl; } } // Set type of node. template label treeNode::setSubNodeType ( const label level, octree& top, const Type& shapes ) { if (debug & 4) { space(Pout, level); Pout<< "treeNode::setSubNodeType with level:" << level << " bb:" << this->bb() << endl; } label myType = -1; for(label octant=0; octant<8; octant++) { label subType = -1; if (subNodes()[octant]) { if (isNode(octant)) { subType = getNodePtr(octant)->setSubNodeType ( level+1, top, shapes ); } else { subType = getLeafPtr(octant)->setSubNodeType ( level+1, top, shapes ); } } else { // No data in this one. Set type for octant acc. to its bounding // box. const treeBoundBox subBb = this->bb().subBbox(mid(), octant); subType = shapes.getSampleType(top, subBb.mid()); } if (debug & 4) { space(Pout, level); Pout<< "treeNode::setSubNodeType : setting octant with bb:" << this->bb().subBbox(mid(), octant) << " to type:" << octree::volType(subType) << endl; } setVolType(octant, subType); // Combine sub node types into type for treeNode. Result is 'mixed' if // types differ among subnodes. if (myType == -1) { myType = subType; } else if (subType != myType) { myType = octree::MIXED; } } if (debug & 4) { space(Pout, level); Pout<< "return from treeNode::setSubNodeType with type:" << octree::volType(myType) << " bb:" << this->bb() << endl; } return myType; } // Get type of node. template label treeNode::getSampleType ( const label level, const octree& top, const Type& shapes, const point& sample ) const { if (debug & 4) { space(Pout, level); Pout<< "treeNode::getSampleType with level:" << level << " bb:" << this->bb() << " sample:" << sample << endl; } // Determine octant of bb. If on edge just use whichever octant. bool onEdge = false; label octant = this->bb().subOctant(mid(), sample, onEdge); label type = getVolType(octant); if (type == octree::MIXED) { // At this level multiple sub types. Recurse to resolve. if (subNodes()[octant]) { if (isNode(octant)) { // Node: recurse into subnodes type = getNodePtr(octant)->getSampleType ( level + 1, top, shapes, sample ); } else { // Leaf type = getLeafPtr(octant)->getSampleType ( level + 1, top, shapes, sample ); } } else { // Problem: empty subnode should have a type FatalErrorIn ( "treeNode::getSampleType" "(const label, octree&, const Type&, const point&)" ) << "Empty node bb:" << this->bb().subBbox(mid(), octant) << " has non-mixed type:" << octree::volType(type) << abort(FatalError); } } if (type == octree::MIXED) { FatalErrorIn ( "treeNode::getSampleType" "(const label, octree&, const Type&, const point&)" ) << "Type is MIXED when searching for " << sample << " at level " << this->bb() << endl << "This probably is because the octree has not been constructed" << " with search facility." << exit(FatalError); } if (debug & 4) { space(Pout, level); Pout<< "return from treeNode::getSampleType with type:" << octree::volType(type) << " bb:" << this->bb() << " sample:" << sample << endl; } return type; } template label treeNode::find ( const Type& shapes, const point& sample ) const { // Find octant of sample. Don't care if on edge (since any item on edge // will have been inserted in both subcubes) bool onEdge = false; label octant = this->bb().subOctant(mid(), sample, onEdge); if (subNodes()[octant]) { if (isNode(octant)) { // Node: recurse into subnodes return getNodePtr(octant)->find ( shapes, sample ); } else { // Leaf: let leaf::find handle this return getLeafPtr(octant)->find(shapes, sample); } } return -1; } template bool treeNode::findTightest ( const Type& shapes, const point& sample, treeBoundBox& tightest ) const { bool changed = false; // Get best guess for starting octant bool onEdge = false; label sampleOctant = this->bb().subOctant(mid(), sample, onEdge); // Go into all suboctants (one containing sample first) and update tightest. // Order of visiting is if e.g. sampleOctant = 5: // 5 1 2 3 4 0 6 7 for(label octanti=0; octanti<8; octanti++) { label octant; if (octanti == 0) { // Use sampleOctant first octant = sampleOctant; } else if (octanti == sampleOctant) { octant = 0; } else { octant = octanti; } if (subNodes()[octant]) { if (isNode(octant)) { // Node: recurse into subnodes const treeNode* subNodePtr = getNodePtr(octant); if (subNodePtr->bb().overlaps(tightest)) { // there might be a better fit inside this subNode changed |= subNodePtr->findTightest ( shapes, sample, tightest ); } } else { // Leaf: let leaf::find handle this const treeLeaf* subLeafPtr = getLeafPtr(octant); if (subLeafPtr->bb().overlaps(tightest)) { // there might be a better fit inside this subLeaf changed |= subLeafPtr->findTightest ( shapes, sample, tightest ); } } } } return changed; } template bool treeNode::findNearest ( const Type& shapes, const point& sample, treeBoundBox& tightest, label& tightesti, scalar& tightestDist ) const { bool changed = false; if (debug & 8) { Pout<< "In findNearest with sample:" << sample << " cube:" << this->bb() << " tightest:" << tightest << endl; } bool onEdge = false; label sampleOctant = this->bb().subOctant(mid(), sample, onEdge); // Go into all suboctants (one containing sample first) and update tightest. // Order of visiting is if e.g. sampleOctant = 5: // 5 1 2 3 4 0 6 7 for(label octanti=0; octanti<8; octanti++) { label octant; if (octanti == 0) { // Use sampleOctant first octant = sampleOctant; } else if (octanti == sampleOctant) { octant = 0; } else { octant = octanti; } if (subNodes()[octant]) { if (isNode(octant)) { // Node const treeNode* subNodePtr = getNodePtr(octant); if (subNodePtr->bb().overlaps(tightest)) { // there might be a better fit inside this subNode changed |= subNodePtr->findNearest ( shapes, sample, tightest, tightesti, tightestDist ); } } else { // Leaf: let leaf::find handle this const treeLeaf* subLeafPtr = getLeafPtr(octant); if (subLeafPtr->bb().overlaps(tightest)) { // there might be a better fit inside this subNode changed |= subLeafPtr->findNearest ( shapes, sample, tightest, tightesti, tightestDist ); } } } } if (debug & 8) { Pout<< "Exiting findNearest for sample:" << sample << " cube:" << this->bb() << " tightesti:" << tightesti << endl; } return changed; } template bool treeNode::findNearest ( const Type& shapes, const linePointRef& ln, treeBoundBox& tightest, label& tightesti, point& linePoint, // nearest point on line point& shapePoint // nearest point on shape ) const { bool changed = false; bool onEdge = false; // Estimate for where best to start searching label sampleOctant = this->bb().subOctant(mid(), ln.centre(), onEdge); // Go into all suboctants (one containing sample first) and update tightest. // Order of visiting is if e.g. sampleOctant = 5: // 5 1 2 3 4 0 6 7 for(label octanti=0; octanti<8; octanti++) { label octant; if (octanti == 0) { // Use sampleOctant first octant = sampleOctant; } else if (octanti == sampleOctant) { octant = 0; } else { octant = octanti; } if (subNodes()[octant]) { if (isNode(octant)) { // Node const treeNode* subNodePtr = getNodePtr(octant); if (subNodePtr->bb().overlaps(tightest)) { // there might be a better fit inside this subNode changed |= subNodePtr->findNearest ( shapes, ln, tightest, tightesti, linePoint, shapePoint ); } } else { // Leaf: let leaf::find handle this const treeLeaf* subLeafPtr = getLeafPtr(octant); if (subLeafPtr->bb().overlaps(tightest)) { // there might be a better fit inside this subNode changed |= subLeafPtr->findNearest ( shapes, ln, tightest, tightesti, linePoint, shapePoint ); } } } } return changed; } template bool treeNode::findBox ( const Type& shapes, const boundBox& box, labelHashSet& elements ) const { bool changed = false; bool onEdge = false; // Estimate for where best to start searching point boxMid(0.5*(box.min() + box.max())); label sampleOctant = this->bb().subOctant(mid(), boxMid, onEdge); // Go into all suboctants (one containing sample first) and update tightest. // Order of visiting is if e.g. sampleOctant = 5: // 5 1 2 3 4 0 6 7 for(label octanti=0; octanti<8; octanti++) { label octant; if (octanti == 0) { // Use sampleOctant first octant = sampleOctant; } else if (octanti == sampleOctant) { octant = 0; } else { octant = octanti; } if (subNodes()[octant]) { if (isNode(octant)) { // Node const treeNode* subNodePtr = getNodePtr(octant); if (subNodePtr->bb().overlaps(box)) { // Visit sub node. changed |= subNodePtr->findBox(shapes, box, elements); } } else { // Leaf: let leaf::find handle this const treeLeaf* subLeafPtr = getLeafPtr(octant); if (subLeafPtr->bb().overlaps(box)) { // Visit sub leaf. changed |= subLeafPtr->findBox(shapes, box, elements); } } } } return changed; } // look from in current cube (given by this->bb()). template const treeLeaf* treeNode::findLeafLine ( const int level, const Type& shapes, point& start, const point& end ) const { if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLine : bb:" << this->bb() << " mid:" << mid() << " start:" << start << endl; } scalar typDim = this->bb().avgDim(); const vector direction = end - start; // Loop on current level until start has been updated to be outside // of this->bb(). Note that max only four subcubes can be crossed so this is // check on whether there are any truncation error problems. label iter = 0; while(true) { if (!this->bb().contains(direction, start)) { if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLine : Start not inside bb " << this->bb() << ". Returning with start:" << start << " subLeaf:" << 0 << endl; } return NULL; } // Check if start and equal if ((mag(start - end)/typDim) < SMALL) { if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLine : start equals end" << ". Returning with start:" << start << " subLeaf:" << 0 << endl; } return NULL; } if (iter >= 4) { // Too many iterations. Is hanging. Handle outside of loop. break; } bool onEdge = false; label octant = this->bb().subOctant(mid(), direction, start, onEdge); // Try finding non-empty treeleaf in octant const treeLeaf* leafPtr = findLeafLineOctant ( level, shapes, octant, direction, start, end ); if (leafPtr) { // Found treeLeaf -> return if (debug & 2) { space(Pout, 2*level); Pout<< "findLeafLine : Found treeLeaf" << ". Returning with start:" << start << " subLeaf:" << long(leafPtr) << endl; } return leafPtr; } iter++; } // Check if is hanging. Max 4 octants can be crossed by a straight line FatalErrorIn ( "treeNode::findLeafLine" "(const label, octree&, point&," " const point&)" ) << "Did not leave bb " << this->bb() << " after " << iter << " iterations of updating starting point." << "start:" << start << " end:" << end << abort(FatalError); return NULL; } template void treeNode::findLeaves ( List*>& leafArray, label& leafIndex ) const { // Go into all sub boxes for(label octant=0; octant<8; octant++) { if (subNodes()[octant]) { if (isNode(octant)) { // Node: recurse into subnodes const treeNode* subNodePtr = getNodePtr(octant); subNodePtr->findLeaves(leafArray, leafIndex); } else { // Leaf: store treeLeaf* subLeafPtr = getLeafPtr(octant); leafArray[leafIndex++] = subLeafPtr; } } } } template void treeNode::findLeaves ( List*>& leafArray, label& leafIndex ) const { // Go into all sub boxes for(label octant=0; octant<8; octant++) { if (subNodes()[octant]) { if (isNode(octant)) { // Node: recurse into subnodes const treeNode* subNodePtr = getNodePtr(octant); subNodePtr->findLeaves(leafArray, leafIndex); } else { // Leaf: store treeLeaf* subLeafPtr = getLeafPtr(octant); leafArray[leafIndex++] = subLeafPtr; } } } } template void treeNode::printNode ( Ostream& os, const label level ) const { space(os, 2*level); os << "node:" << this->bb() << endl; for(label octant=0; octant<8; octant++) { label type = getVolType(octant); string typeString = octree::volType(type); if (!subNodes_[octant]) { space(os, level); os << octant << ":" << typeString << " : null" << endl; } else if (isNode(octant)) { space(os, level); os << octant << ":" << typeString << " : node" << endl; getNodePtr(octant)->printNode(os, level+1); } else { space(os, level); os << octant << ":" << typeString << " : leaf" << endl; treeLeaf* leafPtr = getLeafPtr(octant); leafPtr->printLeaf(os, level+1); } } } template void treeNode::writeOBJ ( Ostream& os, const label level, label& vertNo ) const { point midPoint(this->bb().mid()); label midVertNo = vertNo; os << "v " << midPoint.x() << " " << midPoint.y() << " " << midPoint.z() << endl; vertNo++; for(label octant=0; octant<8; octant++) { if (subNodes_[octant]) { if (isNode(octant)) { treeNode* nodePtr = getNodePtr(octant); point subMidPoint(nodePtr->bb().mid()); os << "v " << subMidPoint.x() << " " << subMidPoint.y() << " " << subMidPoint.z() << endl; os << "l " << midVertNo + 1<< " " << vertNo + 1 << endl; vertNo++; nodePtr->writeOBJ(os, level+1, vertNo); } else { treeLeaf* leafPtr = getLeafPtr(octant); point subMidPoint(leafPtr->bb().mid()); os << "v " << subMidPoint.x() << " " << subMidPoint.y() << " " << subMidPoint.z() << endl; os << "l " << midVertNo + 1<< " " << vertNo + 1 << endl; vertNo++; //leafPtr->writeOBJ(os, level+1, vertNo); } } } } // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * // template Istream& operator>>(Istream& is, treeNode& oc) { for(label octant = 0; octant < 8; octant++) { oc.subNodes_[octant] = NULL; } is >> oc.bb(); label nPtrs; // Read number of entries folllowing is >> nPtrs; is.readBegin("treeNode"); for (label octant = 0; octant < nPtrs; octant++) { label index; is >> index; if (index >= treeNode::leafOffset) { // Leaf recognized by out of range index treeLeaf* leafPtr = new treeLeaf(is); oc.setLeafPtr(index - treeNode::leafOffset, leafPtr); } else { oc.setNodePtr(index, new treeNode(is)); } } // Read end of treeNode list is.readEnd("treeNode"); // Check state of Istream is.check("Istream& operator>>(Istream&, treeNode&)"); return is; } template Ostream& operator<<(Ostream& os, const treeNode& tn) { // Count valid subnodes: // - treeNode // - treeLeafs with non-zero cell list. label nPtrs = 0; for (label octant = 0; octant < 8; octant++) { if (tn.subNodes_[octant]) { if ( tn.isNode(octant) || (tn.getLeafPtr(octant)->indices().size() != 0) ) { nPtrs++; } } } // output subnodes as list of length nPtrs os << token::SPACE << tn.bb() << token::SPACE << nPtrs << token::SPACE << token::BEGIN_LIST; for (label octant = 0; octant < 8; octant++) { if (tn.subNodes_[octant]) { if (tn.isNode(octant)) { const treeNode* subNodePtr = tn.getNodePtr(octant); // Node: output index, value os << token::SPACE << octant << token::SPACE << *subNodePtr << token::NL; } else if (tn.getLeafPtr(octant)->indices().size() != 0) { // treeLeaf: mark by putting index invalid const treeLeaf* subLeafPtr = tn.getLeafPtr(octant); os << token::SPACE << octant + treeNode::leafOffset << token::SPACE << *subLeafPtr << token::NL; } } } os << token::SPACE << token::END_LIST; return os; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam // ************************************************************************* //