##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 <http://www.gnu.org/licenses/>.
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 _unescapeStep(name):
def _toUnicode(match):
return chr(int(match.group(1), 16))
reg1 = re.compile(r'\\X\\(..)')
reg2 = re.compile(r'\\X2\\(....)\\X0\\')
reg3 = re.compile(r'\\X4\\(........)\\X0\\')
return reg3.sub(_toUnicode,
reg2.sub(_toUnicode,
reg1.sub(_toUnicode, name)))
def _get_sub_shapes(lab, loc, parent):
name = _unescapeStep(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 :", _unescapeStep(label.GetLabelName()) )
loc = shape_tool.GetLocation(label)
#print(" Transform loc DEF :", loc.HashCode(100))
label_reference = TDF_Label()
shape_tool.GetReferredShape(label, label_reference)
reference_name = _unescapeStep(label_reference.GetLabelName())
print("######## Transform USE to DEF ==> referenced label :", reference_name )
trafo = {
'node' : 'Transform',
'DEF' : label.EntryDumpToString(),
'name' : reference_name + '-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' : reference_name + '-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 :", name)
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' : _unescapeStep(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, specularColor = (0.5, 0.5, 0.5))
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