#*************************************************************************** #* * #* Copyright (c) 2015 - Victor Titov (DeepSOIC) * #* * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * #* * #* This program 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 Library General Public License for more details. * #* * #* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * #*************************************************************************** import Part class CompoundExplorer: """ CompoundExplorer: Iterator class to traverse compound hierarchy. Usage: for (child, msg, it) in latticeCompoundExplorer.CompoundExplorer(App.ActiveDocument.Compound.Shape): #child is a shape. #msg is int equal to one of three constants: # CompoundExplorer.MSG_LEAF - child is a leaf (non-compound) # CompoundExplorer.MSG_DIVEDOWN - child is a compound that is about to be traversed # CompoundExplorer.MSG_BUBBLEUP - child is a compound that was just finished traversing #it is reference to iterator class (can be useful to extract current depth, or index stack) print ' ' * it.curDepth(), (child, msg) Output: (, 2) (, 1) (, 2) (, 1) (, 1) (, 3) (, 3) """ #constants MSG_LEAF = 1 #the iterator has output a leaf of the compound structure tree (a shape that is not a compound) MSG_DIVEDOWN = 2 #the iterator is entering a subcompound. Shape is the subcompound about to be iterated MSG_BUBBLEUP = 3 #the iterator has finished traversing a subcompound and leaves it. Shape is the compound that was just iterated through. def __init__(self, compound): self.compound = compound self.indexStack = [-1] #childStructure: list of lists of shapes. # It is for storing the stack of childShapes. The last in the # list is the list of children being traversed at the moment. # When just starting, the list contains the base shape - the compound itself. # After first iteration, childShapes of compound are added as a second item. # If among those childshapes, a subcompound is encountered, its childShapes are added as the next item. # When finishing the iteration of a subcompound, the last item is removed from this list. # The length of this list should always be equal to the length of indexStack self.childStructure = [[compound]] self.lastMsg = self.MSG_BUBBLEUP def __iter__(self): return self def nxtDepth(self): ''' nxtDepth(): Returns depth inside compound hierarchy that is being entered (applicable when depth is changing, i.e. msg != MSG_LEAF). For reference: depth of the base shape (compound that was supplied when initiating the loop) is zero. ''' assert(len(self.indexStack) == len(self.childStructure)) return len(self.indexStack) - 1 def prevDepth(self): ''' prevDepth(): Returns depth inside compound hierarchy that is being left (applicable when depth is changing, i.e. msg != MSG_LEAF). For reference: depth of the base shape (compound that was supplied when initiating the loop) is zero. ''' if self.lastMsg == self.MSG_BUBBLEUP: return self.nxtDepth() + 1 elif self.lastMsg == self.MSG_DIVEDOWN: return self.nxtDepth() - 1 else: return self.nxtDepth() def curDepth(self): ''' curDepth(): Returns depth inside compound hierarchy of the item that was just returned. For reference: depth of the base shape (compound that was supplied when initiating the loop) is zero. ''' if self.lastMsg == self.MSG_BUBBLEUP: return self.nxtDepth() elif self.lastMsg == self.MSG_DIVEDOWN: return self.nxtDepth() - 1 else: return self.nxtDepth() def next(self): """Returns a tuple: (child,message,self). """ d = self.nxtDepth() if d == -1: raise StopIteration() self.indexStack[d] += 1 i = self.indexStack[d] if i > len(self.childStructure[d])-1: #index gone out of range - finished traversing a subcompound msg = self.MSG_BUBBLEUP self.indexStack.pop() self.childStructure.pop() if len(self.indexStack) == 0: raise StopIteration() i = self.indexStack[d-1] sh = self.childStructure[d-1][i] else: sh = self.childStructure[d][i] if sh.ShapeType == 'Compound': msg = self.MSG_DIVEDOWN self.indexStack.append(-1) self.childStructure.append(sh.childShapes()) else: msg = self.MSG_LEAF assert(msg != 0) self.lastMsg = msg return (sh, msg, self) def CalculateNumberOfLeaves(compound): '''CalculateNumberOfLeaves(compound): calculates the number of non-compound shapes (leaves) in the compound tree. Slow; good candidate for transferring into C++.''' if compound.ShapeType != 'Compound': return 1 else: children = compound.childShapes(False,False) cnt = 0 for ch in children: cnt += CalculateNumberOfLeaves(ch) return cnt def AllLeaves(compound): 'AllLeaves(compound): Traverses the compound and collects all the leaves into a single list. Returns list of shapes.' output = [] for (child, msg, it) in CompoundExplorer(compound): if msg == it.MSG_LEAF: output.append(child) return output