#!/usr/bin/env python # coding: utf-8 # In[1]: import os from OCC.Core.IFSelect import IFSelect_RetDone from OCC.Core.STEPCAFControl import STEPCAFControl_Reader from OCC.Core.TCollection import TCollection_ExtendedString from OCC.Core.TDocStd import TDocStd_Document from OCC.Core.VrmlAPI import VrmlAPI_Writer from OCC.Core.VrmlData import VrmlData_WorldInfo from OCC.Core.XCAFDoc import XCAFDoc_ShapeTool, XCAFDoc_DocumentTool, XCAFDoc_DocumentTool_ShapeTool from OCC.Core.XCAFApp import XCAFApp_Application_GetApplication from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool_ShapeTool, xcafdoc from OCC.Core.BRep import BRep_Tool from OCC.Core.TDF import TDF_LabelSequence, TDF_Label, TDF_ChildIterator, TDF_AttributeIterator, TDF_Tool, TDF_AttributeMap, TDF_Attribute, TDF_ChildIDIterator from OCC.Core.TCollection import TCollection_AsciiString from OCC.Core.TDataStd import TDataStd_TreeNode from OCC.Core.Standard import Standard_GUID #, Standard_Handle #from OCC.Core.Standard import Standard_Real from OCC.Core.TNaming import TNaming_UsedShapes from OCC.Extend.TopologyUtils import TopologyExplorer from OCC.Extend.TopologyUtils import is_edge, is_wire, discretize_edge, discretize_wire from OCC.Extend.DataExchange import read_step_file_with_names_colors, read_step_file from OCC.Display.WebGl.x3dom_renderer import X3DExporter, X3DomRenderer from OCC.Core.Tesselator import ShapeTesselator from OCC.Core.TopAbs import topabs from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.gp import gp_XYZ, gp_Vec import xml.etree.ElementTree as ET from IPython.display import HTML import x3d.x3d as XX3D import re # In[62]: #stp_filename = 'VentilatorAP203' #'as1_pe_203' #stp_filename = 'as1-oc-214' #tp_filename = 'as1_pe_203' #stp_filename = 'screw' stp_filename = 'RC_Buggy_2_front_suspension' stp_path = os.path.join('assets', stp_filename + '.stp') # load the STEP file doc = TDocStd_Document(TCollection_ExtendedString("pythonocc-doc")) step_reader = STEPCAFControl_Reader() step_reader.SetColorMode(True) step_reader.SetLayerMode(True) step_reader.SetNameMode(True) step_reader.SetMatMode(True) step_reader.SetGDTMode(True) status = step_reader.ReadFile(stp_path) if status == IFSelect_RetDone: step_reader.Transfer(doc) else: raise IOError("STEP file could not be read.") # In[63]: doc # In[64]: ##Copyright 2018 Thomas Paviot (tpaviot@gmail.com) ##(C) 2020 Andreas Plesch ## ##This file is part of pythonOCC. ## ##pythonOCC is free software: you can redistribute it and/or modify ##it under the terms of the GNU Lesser General Public License as published by ##the Free Software Foundation, either version 3 of the License, or ##(at your option) any later version. ## ##pythonOCC 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 Lesser General Public License for more details. ## ##You should have received a copy of the GNU Lesser General Public License ##along with pythonOCC. If not, see . import os from OCC.Core.TopoDS import TopoDS_Shape from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.StlAPI import stlapi_Read, StlAPI_Writer from OCC.Core.BRep import BRep_Builder from OCC.Core.gp import gp_Pnt, gp_Dir, gp_Pnt2d from OCC.Core.Bnd import Bnd_Box2d from OCC.Core.TopoDS import TopoDS_Compound from OCC.Core.IGESControl import IGESControl_Reader, IGESControl_Writer from OCC.Core.STEPControl import STEPControl_Reader, STEPControl_Writer, STEPControl_AsIs from OCC.Core.Interface import Interface_Static_SetCVal from OCC.Core.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity from OCC.Core.TDocStd import TDocStd_Document from OCC.Core.XCAFDoc import (XCAFDoc_DocumentTool_ShapeTool, XCAFDoc_DocumentTool_ColorTool) from OCC.Core.STEPCAFControl import STEPCAFControl_Reader from OCC.Core.TDF import TDF_LabelSequence, TDF_Label from OCC.Core.TCollection import TCollection_ExtendedString from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB from OCC.Core.TopLoc import TopLoc_Location from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Transform from OCC.Extend.TopologyUtils import (discretize_edge, get_sorted_hlr_edges, list_of_shapes_to_compound) try: import svgwrite HAVE_SVGWRITE = True except ImportError: HAVE_SVGWRITE = False def scenegraph_from_document_with_names_colors(doc): """ Returns a scene as list of node dictionaries Uses OCAF. """ output_shapes = {} scene = [] visited = {} DEFset = set() shape_tool = XCAFDoc_DocumentTool_ShapeTool(doc.Main()) color_tool = XCAFDoc_DocumentTool_ColorTool(doc.Main()) locs = [] def _get_sub_shapes(lab, loc, parent): name = lab.GetLabelName() labelString = lab.EntryDumpToString() if labelString in visited: return visited[labelString] = lab if shape_tool.IsAssembly(lab): node = { 'node' : 'Group', 'DEF' : labelString, 'name' : name, 'children' : [] } l_c = TDF_LabelSequence() shape_tool.GetComponents(lab, l_c) for i in range(l_c.Length()): label = l_c.Value(i + 1) #print("Group Name DEF :", name, labelString) if shape_tool.IsReference(label): print("######## component label :", label.GetLabelName() ) loc = shape_tool.GetLocation(label) #print(" Transform loc DEF :", loc.HashCode(100)) label_reference = TDF_Label() shape_tool.GetReferredShape(label, label_reference) print("######## Transform USE to DEF ==> referenced label :", label_reference.GetLabelName() ) trafo = { 'node' : 'Transform', 'DEF' : label.EntryDumpToString(), 'name' : label_reference.GetLabelName()+'-trafo', 'transform' : loc, 'children': [] } reference_entry = label_reference.EntryDumpToString() if reference_entry in DEFset: #already defined, use USE reference = { 'node' : 'Transform', 'USE' : reference_entry, 'name' : label_reference.GetLabelName()+'-ref' } trafo['children'].append(reference) else: DEFset.add(reference_entry) _get_sub_shapes(label_reference, loc, trafo['children']) node['children'].append(trafo) elif shape_tool.IsSimpleShape(lab): #print("Transform DEF Shape Name :", name, labelString ) shape = shape_tool.GetShape(lab) c = _set_color(lab, shape) clabel = color_tool.FindColor(c) clabelString = clabel.EntryDumpToString() n = c.Name(c.Red(), c.Green(), c.Blue()) #print(' instance color Name & RGB: ', n, c.Red(), c.Green(), c.Blue()) labloc = shape_tool.GetLocation(lab) #print(" Shape Transform: ", labloc.HashCode(100)) ##subshapes l_subss = TDF_LabelSequence() shape_tool.GetSubShapes(lab, l_subss) #node = {} if (l_subss.Length() == 0 and labloc.IsIdentity()): # does not need transform # but still needs transform wrapper for proper USE reference node = { 'node' : 'Transform', 'DEF' : labelString, 'name' : name + '-wrapper', 'children' : [] } shapenode = { 'node' : 'Shape', 'label' : lab, 'shape' : shape, 'name' : name + '-shape', 'colorString' : f"{c.Red()} {c.Green()} {c.Blue()}", 'color' : (c.Red(), c.Green(), c.Blue()), 'colorDEF' : clabelString } node['children'].append(shapenode) else: # needs grouping or has transform node = { 'node' : 'Transform', 'DEF' : labelString, 'transform' : labloc, 'transformhash' : labloc.HashCode(100), 'name' : name, 'children' : [] } shapenode = { 'node' : 'Shape', 'label' : lab, 'shape' : shape, 'name' : name + '-shape', 'colorString' : f"{c.Red()} {c.Green()} {c.Blue()}", 'color' : (c.Red(), c.Green(), c.Blue()), 'colorDEF' : clabelString } node['children'].append(shapenode) for i in range(l_subss.Length()): lab_subs = l_subss.Value(i+1) print("######## simpleshape subshape label :", lab.GetLabelName()) shape_sub = shape_tool.GetShape(lab_subs) c = _set_color(lab_subs, shape_sub) clabel = color_tool.FindColor(c) clabelString = clabel.EntryDumpToString() n = c.Name(c.Red(), c.Green(), c.Blue()) #print(' shape color Name & RGB: ', n, c.Red(), c.Green(), c.Blue()) subloc = shape_tool.GetLocation(lab_subs) #print(" subshape Transform: ", subloc.HashCode(100)) shapenode = { 'node' : 'SubShape', 'label' : lab_subs, 'shape' : shape_sub, 'DEF' : lab_subs.EntryDumpToString(), 'name' : lab_subs.GetLabelName()+'-subshape', 'colorString' : f"{c.Red()} {c.Green()} {c.Blue()}", 'color' : (c.Red(), c.Green(), c.Blue()), 'colorDEF' : clabelString, 'trafo' : subloc } node['children'].append(shapenode) parent.append(node) def _set_color(lab, shape): c = Quantity_Color(0.5, 0.5, 0.5, Quantity_TOC_RGB) # default color colorSet = False if (color_tool.GetInstanceColor(shape, 0, c) or color_tool.GetInstanceColor(shape, 1, c) or color_tool.GetInstanceColor(shape, 2, c)): colorSet = True if not colorSet: if (color_tool.GetColor(lab, 0, c) or color_tool.GetColor(lab, 1, c) or color_tool.GetColor(lab, 2, c)): colorSet = True if colorSet: color_tool.SetInstanceColor(shape, 0, c) color_tool.SetInstanceColor(shape, 1, c) color_tool.SetInstanceColor(shape, 2, c) return c def _get_shapes(): labels = TDF_LabelSequence() shape_tool.GetFreeShapes(labels) print("Number of shapes at root :", labels.Length()) for i in range(labels.Length()): root_item = labels.Value(i+1) _get_sub_shapes(root_item, None, scene) _get_shapes() print('DONE') return scene def x3d_from_scenegraph(scene=[], doc=None): #shape_tool = XCAFDoc_DocumentTool_ShapeTool(doc.Main()) appDEFset = {} def _x3dapplyLocation(x3dtransformnode, location): # get translation and rotation from location transformation = location.Transformation() rot_axis = gp_XYZ() #rot_angle = 0.0 success, rot_angle = transformation.GetRotation(rot_axis)#.GetVectorAndAngle(rot_axis, rot_angle) translation = transformation.TranslationPart() scale_factor = transformation.ScaleFactor() x3dtransformnode.rotation = (rot_axis.X(), rot_axis.Y(), rot_axis.Z(), rot_angle) x3dtransformnode.translation = (translation.X(), translation.Y(), translation.Z()) x3dtransformnode.scale = (scale_factor, scale_factor, scale_factor) return def _x3dgeofromTShape(shape): geo = XX3D.Box() #if label.IsNull(): # return geo #shape = shape_tool.GetShape(label) tesselator = ShapeTesselator(shape) tesselator.Compute( compute_edges=False, mesh_quality=1, parallel=True) x3dstring = tesselator.ExportShapeToX3DIndexedFaceSet()#x3dexp._triangle_sets[0] # there should be just one element = ET.XML(x3dstring) geo = XX3D.Box() if (element.tag == 'TriangleSet'): coordele = list(element.iter('Coordinate'))[0] normalele = list(element.iter('Normal'))[0] coord = XX3D.Coordinate(point = _MFVec3ffromString(coordele.attrib['point'])) normal = XX3D.Normal(vector = _MFVec3ffromString(normalele.attrib['vector'])) geo = XX3D.TriangleSet(coord = coord, normal=normal, solid=False) # get tesselated triangleset or lineset return geo def _MFVec3ffromString(sepString): mflist = sepString.split() mf = [] for i in range(len(mflist)): if (i % 3 == 2): mf.append((float(mflist[i-2]), float(mflist[i-1]), float(mflist[i]))) return mf def _x3dappfromColor(c, DEF): if DEF in appDEFset: return XX3D.Appearance(USE = DEF) else: x3dmat = XX3D.Material(diffuseColor = c) return XX3D.Appearance(DEF = DEF, material = x3dmat) def _getx3dnode(node): def _sanitizeDEF(name): # IdFirstChar ::= # Any ISO-10646 character encoded using UTF-8 except: 0x30-0x3a, 0x0-0x20, 0x22, 0x23, 0x27, 0x2b, 0x2c, 0x2d, 0x2e, 0x5b, 0x5c, 0x5d, 0x7b, 0x7d, 0x7f ; # first no [0-9],space,",#,',+,comma,-,.,[,\,],{,} # IdRestChars ::= # Any number of ISO-10646 characters except: 0x0-0x20, 0x22, 0x23, 0x27, 0x2c, 0x2e, 0x3a, 0x5b, 0x5c, 0x5d, 0x7b, 0x7d, 0x7f ; # rest no space,",#,',comma,.,:,[,\,],{,} return 'L-' + ( name .replace(" ","_") .replace('"','^') .replace('#','N') .replace("'","^") .replace(",",";") .replace(".",";") .replace(":","-") .replace("[","(") .replace("]",")") .replace("{","(") .replace("}",")") .replace("\\","/") ) def _applyDEFUSE(x3dnode): if 'USE' in node: x3dnode.USE = _sanitizeDEF(node['USE']) if 'DEF' in node: x3dnode.DEF = _sanitizeDEF(node['DEF']) return def _applychildren(x3dnode): if 'children' in node: for n in node['children']: success, child = _getx3dnode(n) if success: x3dnode.children.append(child) return if not 'node' in node: print('no node type, skipping') return False, None ntype = node['node'] # vrml does not allow : in DEF names but x3d does, TODO sanitize if (ntype == 'Group'): x3dnode = XX3D.Group(class_ = node['name'], children = []) _applyDEFUSE(x3dnode) _applychildren(x3dnode) elif (ntype == 'Transform'): x3dnode = XX3D.Transform(class_ = node['name'], children = []) if 'transform' in node: _x3dapplyLocation(x3dnode, node['transform']) _applyDEFUSE(x3dnode) _applychildren(x3dnode) elif (ntype == 'Shape' or ntype == 'SubShape'): x3dnode = XX3D.Shape (class_ = node['name']) _applyDEFUSE(x3dnode) if 'shape' in node: x3dnode.geometry = _x3dgeofromTShape(node['shape']) if 'color' in node: x3dnode.appearance = _x3dappfromColor(node['color'], node['colorDEF']) else: print ('unknown node: --' + ntype + "--") return True, x3dnode x3dscene = XX3D.Scene(children=[]) x3ddoc = XX3D.X3D(Scene = x3dscene) for node in scene: success, x3dnode = _getx3dnode(node) if success: x3dscene.children.append(x3dnode) else: print('no x3d for root node, skipped') return x3ddoc # In[ ]: ascene=scenegraph_from_document_with_names_colors(doc) x3ddoc=x3d_from_scenegraph(ascene, doc) # In[66]: ascene # In[71]: #print(x3ddoc.XML()) # In[68]: x3domHEAD = ''' ''' # In[69]: x3dele = list(ET.XML(x3ddoc.XML()).iter('X3D'))[0] x3dHTML = ET.tostring(x3dele, encoding="unicode", short_empty_elements=False) # In[70]: HTML(x3domHEAD + x3dHTML) # In[ ]: