Commit e39a708f authored by Andrey Filippov's avatar Andrey Filippov

added top level boundin box generation to x3d models

parent aa4f4508
......@@ -145,6 +145,45 @@ def verticesToCheck(solid):
return lv
def getBoundBox(freecadObjects):
Calculate BoundBox for all shapes in the document
@param freecadObjects - list of FreeCAD objects or all solids/shells
@return FreeCAD.BoundBox object for the whole document
shells =[]
for o in freecadObjects:
if hasattr(o, "Shape"):
for shell in shape.Shells: # solids and open shells
elif hasattr(o, "BoundBox"):
for shell in shells:
thisBBox = shell.BoundBox
if not bBox:
bBox = FreeCAD.BoundBox(thisBBox.XMin,thisBBox.YMin,thisBBox.ZMin,
bBox = FreeCAD.BoundBox(min(thisBBox.XMin,bBox.XMin),min(thisBBox.YMin,bBox.YMin),min(thisBBox.ZMin,bBox.ZMin),
return bBox
def bBoxToX3d(bBox):
Convert FreeCAD BoundBox to X3D representation (center, size)
@param bBox - FreeCAD BoundBox object
@return dictionary of {'center':(xc,yc,zc), size:(xs,ys,zs)}
return {'center':((bBox.XMax + bBox.XMin)/2,(bBox.YMax + bBox.YMin)/2,(bBox.ZMax + bBox.ZMin)/2),
'size': ( bBox.XMax - bBox.XMin, bBox.YMax - bBox.YMin, bBox.ZMax - bBox.ZMin)}
def create_file_info_nogui(shape, fname=""):
A no-Gui version of the create_file_info, rather useless now as the color
......@@ -191,7 +230,7 @@ def create_file_info(freecadObjects, fname=""):
Collect information about each part/solid to be used for comparison between
assembly objects and parts
@param shape - FreeCAD Shape, containing one or more solids
@param freecadObjects - list of FreeCAD objects
@param fname - source file path
@return a pair of a list of info for each solid (as a dictionary) and a
list of solids in the shape
......@@ -395,7 +434,7 @@ def get_info_files(dir_list = None):
info_dict[name] = pickle.load(open(info_path, "rb"))
FreeCAD.Console.PrintMessage("get_info_files() - loaded");
# FreeCAD.Console.PrintMessage("get_info_files() - loaded"); #Rare FreeCAD crash?
#Put largest element of multi-solid parts as the [0] index.
for k in info_dict:
......@@ -409,7 +448,7 @@ def get_info_files(dir_list = None):
print ("Largest solid is number %d"%(mi))
if mi>0:
FreeCAD.Console.PrintMessage("get_info_files() - largest made first");
# FreeCAD.Console.PrintMessage("get_info_files() - largest made first"); # rare FreeCAD crash?
return info_dict
......@@ -849,7 +888,7 @@ def getShapeNode(vertices, faces, diffuseColor = None, main_color_index = 0, col
materialNode.set('diffuseColor', "%f %f %f" % tuple(diffuseColor[main_color_index * 3: main_color_index * 3 + 3]))
return shapeNode
def exportX3D(objects, filepath, id="part", colorPerVertex=False):
def exportX3D(objects, filepath, partName="", bbox = None, colorPerVertex=False):
Export given list of objects to a X3D file.
@param objects - a list of dictionaries in the following format:
......@@ -860,8 +899,11 @@ def exportX3D(objects, filepath, id="part", colorPerVertex=False):
@param filepath - os path of the file to save X3D data
@param id - id set for the X3D Group node wrapping all the objects in the file
@param bbox - optional bound box as a dictionary {'center':(xc,yc,zc), size:(xs,ys,zs)}
@param colorPerVertex - True: specify color per erach vertex, False - for each face (reduces file size)
progress_bar = Base.ProgressIndicator()
progress_bar.start("Saving objects to X3D file %s ..."%(filepath), len(objects))
......@@ -869,8 +911,21 @@ def exportX3D(objects, filepath, id="part", colorPerVertex=False):
x3dNode.set('profile', 'Interchange')
x3dNode.set('version', '3.3')
sceneNode = et.SubElement(x3dNode, 'Scene')
groupNode = et.SubElement(sceneNode, 'Group')
groupNode.set('id', id)
transformNode =et.SubElement(sceneNode, 'Transform') # Empty transform to adjust center for offset models (use bboxCenter)
transformNode.set('id', 'transformTop_'+partName)
transformNode.set('class', 'transformTop_'+partName)
# if bbox:
# transformNode.set('translation','%f %f %f'%(-bbox['center'][0],-bbox['center'][1],-bbox['center'][2]))
# else:
transformNode.set('translation','%f %f %f'%(0,0,0))
transformNode.set('rotation','%f %f %f %f'%(0,0,0,0))
groupNode = et.SubElement(transformNode, 'Group')
groupNode.set('id', 'groupTop_'+partName)
groupNode.set('class', 'groupTop_'+partName)
if bbox:
groupNode.set('bboxSize','%f %f %f'%bbox['size'])
groupNode.set('bboxCenter','%f %f %f'%bbox['center'])
for o in objects:
shapeNode = getShapeNode(o["points"], o["faces"], o["color"], o["main_color_index"], colorPerVertex)
......@@ -903,7 +958,8 @@ def prepareX3dExport(freecadObjects, fname=""):
if fname:
txt += " in "+fname
progress_bar.start("Generating objects%s to export to X3D ..."%(txt), len(freecadObjects))
for o in freecadObjects: # True) # have to do it here as 'for' uses 'continue', True - enable ESC to abort
if (not o.ViewObject) or (o.ViewObject.Visibility):
......@@ -983,8 +1039,10 @@ def generatePartsX3d(dir_list = [STEP_PARTS], colorPerVertex = COLOR_PER_VERTEX)
FreeCAD.loadFile(step_file) # os.path.join(ROOT_DIR,step_file))
doc = FreeCAD.activeDocument()
doc.Label = partName
x3d_objects = prepareX3dExport(doc.Objects, step_file) # step_file needed just for progress bar
exportX3D(x3d_objects, x3dFile, id="part_"+partName, colorPerVertex=colorPerVertex)
bboxX3d= bBoxToX3d(getBoundBox(doc.Objects))
exportX3D(x3d_objects, x3dFile, partName = partName, bbox = bboxX3d, colorPerVertex=colorPerVertex)
numExported += 1
......@@ -1075,7 +1133,7 @@ def generateAssemblyX3d(assembly_path,
info_dict = get_info_files(dir_list) # Will (re-) build info files if missing
# FreeCAD.Console.PrintMessage("generateAssemblyX3d()");
generatePartsX3d(dir_list = [STEP_PARTS], colorPerVertex = COLOR_PER_VERTEX) # Will only run if files are not there yet
FreeCAD.Console.PrintMessage("generatePartsX3d() Done");
if not components:
......@@ -1105,11 +1163,24 @@ def generateAssemblyX3d(assembly_path,
inlineNode.set('id', ass_with_suffix + '_config')
inlineNode.set('url',ass_with_suffix + '_config'+ X3D_EXT)
inlineNode.set('nameSpaceName', ass_with_suffix )
modelNode = et.SubElement(sceneNode, 'Transform')
modelNode.set('translation','%f %f %f'%(0,0,0))
modelNode.set('rotation','%f %f %f %f'%(0,0,0,0))
#empty Transform node to move/rotate the whole assembly now translation to -(bounding box center)
bboxX3d= bBoxToX3d(getBoundBox(components['solids']))
transformNode = et.SubElement(sceneNode, 'Transform')
# if bboxX3d:
# transformNode.set('translation','%f %f %f'%(-bboxX3d['center'][0],-bboxX3d['center'][1],-bboxX3d['center'][2]))
# else:
transformNode.set('translation','%f %f %f'%(0,0,0))
transformNode.set('rotation','%f %f %f %f'%(0,0,0,0))
#Group node to provide a bbox center/size of the whole assembly (and center the view)
modelNode = et.SubElement(transformNode, 'Group')
if bboxX3d:
modelNode.set('bboxSize','%f %f %f'%bboxX3d['size'])
modelNode.set('bboxCenter','%f %f %f'%bboxX3d['center'])
defined_parts = {} # for each defined part holds index (for ID generation)
#TODO: Reorder parts - smallest (by a product og gyration radii) first
......@@ -1132,9 +1203,7 @@ def generateAssemblyX3d(assembly_path,
part_name = part + ASSEMBLY_SUFFIX
transformation = transformations[part]
bboxCenter=((bbox.XMax + bbox.XMin)/2,(bbox.YMax + bbox.YMin)/2,(bbox.ZMax + bbox.ZMin)/2)
bboxSize= ( bbox.XMax - bbox.XMin, bbox.YMax - bbox.YMin, bbox.ZMax - bbox.ZMin)
bbox= bBoxToX3d(components['solids'][i].BoundBox)
transform = matrix4ToX3D(transformation)
......@@ -1155,8 +1224,9 @@ def generateAssemblyX3d(assembly_path,
groupNode = et.SubElement(transformNode, 'Group')
groupNode.set('bboxSize','%f %f %f'%bboxSize)
groupNode.set('bboxCenter','%f %f %f'%bboxCenter)
groupNode.set('bboxSize','%f %f %f'%bbox['size'])
groupNode.set('bboxCenter','%f %f %f'%bbox['center'])
if defined_parts[part]:
groupNode.set('USE', part_name)
......@@ -1742,7 +1812,7 @@ class X3dStepAssyDialog(QtGui.QWidget):
def executeMacro(self):
COMPONENTS = None # Start with new ones
FreeCAD.Console.PrintMessage("Starting conversion...")
# FreeCAD.Console.PrintMessage("Starting conversion...")
if self.log_file:
sys.stdout = open(self.log_file,"w")
......@@ -1839,6 +1909,7 @@ import x3d_step_assy
reload (x3d_step_assy)
form = x3d_step_assy.X3dStepAssyDialog()
x3d_step_assy.generateAssemblyX3d("") #or with path
or just run conversion from the command line in Python console
x3d_step_assy.generateAssemblyX3d("") #or with path
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment