def change_label_name(self, uid, name): """Change the name of component with uid.""" entry, _ = uid.split('.') entry_parts = entry.split(':') if len(entry_parts) == 4: # first label at root j = 1 k = None elif len(entry_parts) == 5: # part is a component of label at root j = int(entry_parts[3]) # number of label at root k = int(entry_parts[4]) # component number shape_tool = XCAFDoc_DocumentTool_ShapeTool(self.doc.Main()) color_tool = XCAFDoc_DocumentTool_ColorTool(self.doc.Main()) labels = TDF_LabelSequence() # labels at root of self.doc shape_tool.GetShapes(labels) label = labels.Value(j) comps = TDF_LabelSequence() # Components of root_label subchilds = False is_assy = shape_tool.GetComponents(label, comps, subchilds) target_label = comps.Value(k) self.setLabelName(target_label, name) shape_tool.UpdateAssemblies() print(f"Name {name} set for part with uid = {uid}.") self.parse_doc()
class StepAnalyzer(): """A class that analyzes the structure of an OCAF document.""" def __init__(self, document=None, filename=None): """Supply one or the other: document or STEP filename.""" self.uid = 1 self.indent = 0 self.output = "" self.fname = filename if filename: self.doc = self.read_file(filename) elif document: self.doc = document self.shape_tool = XCAFDoc_DocumentTool_ShapeTool(self.doc.Main()) else: print("Supply one or the other: document or STEP filename.") def read_file(self, fname): """Read STEP file and return <TDocStd_Document>.""" # Create the application, empty document and shape_tool doc = TDocStd_Document(TCollection_ExtendedString("STEP")) app = XCAFApp_Application_GetApplication() app.NewDocument(TCollection_ExtendedString("MDTV-XCAF"), doc) self.shape_tool = XCAFDoc_DocumentTool_ShapeTool(doc.Main()) self.shape_tool.SetAutoNaming(True) # Read file and return populated doc step_reader = STEPCAFControl_Reader() step_reader.SetColorMode(True) step_reader.SetLayerMode(True) step_reader.SetNameMode(True) step_reader.SetMatMode(True) status = step_reader.ReadFile(fname) if status == IFSelect_RetDone: step_reader.Transfer(doc) return doc def dump(self): """Return assembly structure in indented outline form. Format of lines: Component Name [entry] => Referred Label Name [entry] Components are shown indented w/r/t line above.""" if self.fname: self.output += f"Assembly structure of file: {self.fname}\n\n" else: self.output += "Assembly structure of doc:\n\n" self.indent = 0 # Find root label of step doc labels = TDF_LabelSequence() self.shape_tool.GetShapes(labels) nbr = labels.Length() rootlabel = labels.Value(1) # First label at root # Get information from root label name = rootlabel.GetLabelName() entry = rootlabel.EntryDumpToString() is_assy = self.shape_tool.IsAssembly(rootlabel) if is_assy: # If 1st label at root holds an assembly, it is the Top Assy. # Through this label, the entire assembly is accessible. # There is no need to explicitly examine other labels at root. self.output += f"{self.uid}\t[{entry}] {name}\t" self.uid += 1 self.indent += 2 top_comps = TDF_LabelSequence() # Components of Top Assy subchilds = False is_assy = self.shape_tool.GetComponents(rootlabel, top_comps, subchilds) self.output += f"Number of labels at root = {nbr}\n" if top_comps.Length(): self.find_components(top_comps) return self.output def find_components(self, comps): """Discover components from comps (LabelSequence) of an assembly. Components of an assembly are, by definition, references which refer to either a shape or another assembly. Components are essentially 'instances' of the referred shape or assembly, and carry a location vector specifing the location of the referred shape or assembly. """ for j in range(comps.Length()): c_label = comps.Value(j + 1) # component label <class 'TDF_Label'> c_name = c_label.GetLabelName() c_entry = c_label.EntryDumpToString() ref_label = TDF_Label() # label of referred shape (or assembly) is_ref = self.shape_tool.GetReferredShape(c_label, ref_label) if is_ref: # just in case all components are not references ref_entry = ref_label.EntryDumpToString() ref_name = ref_label.GetLabelName() indent = "\t" * self.indent self.output += f"{self.uid}{indent}[{c_entry}] {c_name}" self.output += f" => [{ref_entry}] {ref_name}\n" self.uid += 1 if self.shape_tool.IsAssembly(ref_label): self.indent += 1 ref_comps = TDF_LabelSequence() # Components of Assy subchilds = False _ = self.shape_tool.GetComponents(ref_label, ref_comps, subchilds) if ref_comps.Length(): self.find_components(ref_comps) self.indent -= 1
def parse_doc(self): """Generate new part_dict & label_dict. part_dict (dict of dicts) is used primarily for 3D display part_dict = {uid: {'shape': , 'name': , 'color': , 'loc': }} label_dict (dict of dicts) is used primarily for tree view display label_dict = {uid: {'entry': , 'name': , 'parent_uid': , 'ref_entry': , 'is_assy': }} """ # Initialize dictionaries & list self._share_dict = {'0:1:1': 0} # {entry: ser_nbr} self.part_dict = {} self.label_dict = {} # Temporary use during unpacking self.parent_uid_stack = [] # uid of parent (topmost first) self.assy_entry_stack = ['0:1:1'] # [entries of containing assemblies] self.assy_loc_stack = [] # applicable <TopLoc_Location> locations shape_tool = XCAFDoc_DocumentTool_ShapeTool(self.doc.Main()) color_tool = XCAFDoc_DocumentTool_ColorTool(self.doc.Main()) # Find root label of self.doc labels = TDF_LabelSequence() shape_tool.GetShapes(labels) root_label = labels.Value(1) # First label at root nbr = labels.Length() # number of labels at root logger.debug('Number of labels at doc root : %i', nbr) # Get root label information # The first label at root holds an assembly, it is the Top Assy. # Through this label, the entire assembly is accessible. # There is no need to explicitly examine other labels at root. root_name = root_label.GetLabelName() root_entry = root_label.EntryDumpToString() root_uid = self.get_uid_from_entry(root_entry) loc = shape_tool.GetLocation(root_label) # <TopLoc_Location> self.assy_loc_stack.append(loc) self.assy_entry_stack.append(root_entry) self.label_dict = { root_uid: { 'entry': root_entry, 'name': root_name, 'parent_uid': None, 'ref_entry': None, 'is_assy': True } } self.parent_uid_stack.append(root_uid) top_comps = TDF_LabelSequence() # Components of Top Assy subchilds = False is_assy = shape_tool.GetComponents(root_label, top_comps, subchilds) if top_comps.Length(): # if is_assy: logger.debug("") logger.debug("Parsing components of label entry %s)", root_entry) self.parse_components(top_comps, shape_tool, color_tool) else: print("Something is wrong.")
def read_file(self): """Build self.tree (treelib.Tree()) containing CAD data read from a step file. Each node of self.tree contains the following: (Name, UID, ParentUID, {Data}) where the Data keys are: 'a' (isAssy?), 'l' (TopLoc_Location), 'c' (Quantity_Color), 's' (TopoDS_Shape) """ logger.info("Reading STEP file") doc = TDocStd_Document(TCollection_ExtendedString("STEP")) # Create the application app = XCAFApp_Application_GetApplication() app.NewDocument(TCollection_ExtendedString("MDTV-CAF"), doc) # Get root shapes shape_tool = XCAFDoc_DocumentTool_ShapeTool(doc.Main()) shape_tool.SetAutoNaming(True) self.color_tool = XCAFDoc_DocumentTool_ColorTool(doc.Main()) layer_tool = XCAFDoc_DocumentTool_LayerTool(doc.Main()) l_materials = XCAFDoc_DocumentTool_MaterialTool(doc.Main()) step_reader = STEPCAFControl_Reader() step_reader.SetColorMode(True) step_reader.SetLayerMode(True) step_reader.SetNameMode(True) step_reader.SetMatMode(True) status = step_reader.ReadFile(self.filename) if status == IFSelect_RetDone: logger.info("Transfer doc to STEPCAFControl_Reader") step_reader.Transfer(doc) # Test round trip by writing doc back to another file. logger.info("Doing a 'short-circuit' Round Trip test") doctype = type(doc) # <class 'OCC.Core.TDocStd.TDocStd_Document'> logger.info(f"Writing {doctype} back to another STEP file") self.testRTStep(doc) # Save doc to file (for educational purposes) (not working yet) logger.debug("Saving doc to file") savefilename = TCollection_ExtendedString('../doc.txt') app.SaveAs(doc, savefilename) labels = TDF_LabelSequence() color_labels = TDF_LabelSequence() shape_tool.GetShapes(labels) self.shape_tool = shape_tool logger.info('Number of labels at root : %i' % labels.Length()) try: label = labels.Value(1) # First label at root except RuntimeError: return name = self.getName(label) logger.info('Name of root label: %s' % name) isAssy = shape_tool.IsAssembly(label) logger.info("First label at root holds an assembly? %s" % isAssy) if isAssy: # If first label at root holds an assembly, it is the Top Assembly. # Through this label, the entire assembly is accessible. # No need to examine other labels at root explicitly. topLoc = TopLoc_Location() topLoc = shape_tool.GetLocation(label) self.assyLocStack.append(topLoc) entry = label.EntryDumpToString() logger.debug("Entry: %s" % entry) logger.debug("Top assy name: %s" % name) # Create root node for top assy newAssyUID = self.getNewUID() self.tree.create_node(name, newAssyUID, None, { 'a': True, 'l': None, 'c': None, 's': None }) self.assyUidStack.append(newAssyUID) topComps = TDF_LabelSequence() # Components of Top Assy subchilds = False isAssy = shape_tool.GetComponents(label, topComps, subchilds) logger.debug("Is Assembly? %s" % isAssy) logger.debug("Number of components: %s" % topComps.Length()) logger.debug("Is Reference? %s" % shape_tool.IsReference(label)) if topComps.Length(): self.findComponents(label, topComps) else: # Labels at root can hold solids or compounds (which are 'crude' assemblies) # Either way, we will need to create a root node in self.tree newAssyUID = self.getNewUID() self.tree.create_node(os.path.basename(self.filename), newAssyUID, None, { 'a': True, 'l': None, 'c': None, 's': None }) self.assyUidStack = [newAssyUID] for j in range(labels.Length()): label = labels.Value(j + 1) name = self.getName(label) isAssy = shape_tool.IsAssembly(label) logger.debug("Label %i is assembly?: %s" % (j + 1, isAssy)) shape = shape_tool.GetShape(label) color = self.getColor(shape) isSimpleShape = self.shape_tool.IsSimpleShape(label) logger.debug("Is Simple Shape? %s" % isSimpleShape) shapeType = shape.ShapeType() logger.debug("The shape type is: %i" % shapeType) if shapeType == 0: logger.debug( "The shape type is OCC.Core.TopAbs.TopAbs_COMPOUND") topo = TopologyExplorer(shape) #topo = aocutils.topology.Topo(shape) logger.debug("Nb of compounds : %i" % topo.number_of_compounds()) logger.debug("Nb of solids : %i" % topo.number_of_solids()) logger.debug("Nb of shells : %i" % topo.number_of_shells()) newAssyUID = self.getNewUID() for i, solid in enumerate(topo.solids()): name = "P%s" % str(i + 1) self.tree.create_node(name, self.getNewUID(), self.assyUidStack[-1], { 'a': False, 'l': None, 'c': color, 's': solid }) elif shapeType == 2: logger.debug( "The shape type is OCC.Core.TopAbs.TopAbs_SOLID") self.tree.create_node(name, self.getNewUID(), self.assyUidStack[-1], { 'a': False, 'l': None, 'c': color, 's': shape }) elif shapeType == 3: logger.debug( "The shape type is OCC.Core.TopAbs.TopAbs_SHELL") self.tree.create_node(name, self.getNewUID(), self.assyUidStack[-1], { 'a': False, 'l': None, 'c': color, 's': shape }) return True