Loading x3d_step_assy_color_match.py +114 −30 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ from __future__ import print_function @contact: andrey@elphel.coml @contact: andrey@elphel.coml @deffield updated: Updated @deffield updated: Updated ''' ''' from email import Errors __author__ = "Andrey Filippov" __author__ = "Andrey Filippov" __copyright__ = "Copyright 2015, Elphel, Inc." __copyright__ = "Copyright 2015, Elphel, Inc." __license__ = "GPL" __license__ = "GPL" Loading @@ -37,7 +38,7 @@ __email__ = "andrey@elphel.com" __status__ = "Development" __status__ = "Development" import FreeCAD import FreeCAD import FreeCADGui # just to update cosole output (change to threads later?) import FreeCADGui # just to update console output (change to threads later?) - does not seem to work import Part import Part import os import os import time import time Loading @@ -57,6 +58,10 @@ X3D_DIR = "x3d" X3D_EXT = ".x3d" X3D_EXT = ".x3d" INFO_EXT = ".pickle" INFO_EXT = ".pickle" PRECISION = 0.0001 PRECISION = 0.0001 PRECISION_AREA = 0.001 PRECISION_VOLUME = 0.001 PRECISION_GYRATION = 0.001 PRECISION_INSIDE = 0.03 PRECISION_INSIDE = 0.03 COLOR_PER_VERTEX = True COLOR_PER_VERTEX = True if ROOT_DIR[0] == "~": if ROOT_DIR[0] == "~": Loading Loading @@ -333,6 +338,8 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci containing list of colors (tuples) for which there is an area match between assembly element and a part. containing list of colors (tuples) for which there is an area match between assembly element and a part. Build element part frame from colors first, then (if not enough) use inertial directions Build element part frame from colors first, then (if not enough) use inertial directions """ """ progress_bar = Base.ProgressIndicator() progress_bar.start("Finding transformations for library parts to match assembly elements ...", len(objects)) transformations=[] transformations=[] for i,s in enumerate(solids): for i,s in enumerate(solids): tolerance = insidePrecision * s.BoundBox.DiagonalLength # Or should it be fraction of the translation distance? tolerance = insidePrecision * s.BoundBox.DiagonalLength # Or should it be fraction of the translation distance? Loading Loading @@ -397,10 +404,12 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci trans.append(None) # so Transformations have same structure as candidates trans.append(None) # so Transformations have same structure as candidates print("*** Could not find match for part %s"%(cand_name)) print("*** Could not find match for part %s"%(cand_name)) transformations.append(trans) transformations.append(trans) progress_bar.next() # True) # True - enable ESC to abort progress_bar.stop() return transformations return transformations def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISION): def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISION_AREA): """ """ @return dictionary partName -> list of colors (as 3-tuples) @return dictionary partName -> list of colors (as 3-tuples) """ """ Loading Loading @@ -428,11 +437,27 @@ def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISIO if len(colors) == max_match: if len(colors) == max_match: colored_candidates[candidate]=colors colored_candidates[candidate]=colors return colored_candidates return colored_candidates """ PRECISION_AREA = 0.02 PRECISION_VOLUME = 0.03 PRECISION_INSIDE = 0.03 """ #components=scan_step_parts.findComponents("/home/andrey/parts/0393/export/nc393_07_flat_noassy.stp") #components=scan_step_parts.findComponents("/home/andrey/parts/0393/export/nc393_07_flat_noassy.stp") def findComponents(assembly, precision_inside = PRECISION_INSIDE, precision = PRECISION): def findComponents(assembly, precision_area = PRECISION_AREA, precision_volume = PRECISION_VOLUME, precision_gyration = PRECISION_GYRATION, precision_inside = PRECISION_INSIDE, precision = PRECISION, show_best = True): """ """ @param assembly - may be file path (different treatment for Gui/no-Gui, Shape or doc.Objects @param assembly - may be file path (different treatment for Gui/no-Gui, Shape or doc.Objects @param precision_area = PRECISION_AREA - relative precision in surface area calculations @param precision_volume = PRECISION_VOLUME - relative precision in volume calculations @param precision_gyration = PRECISION_GYRATION - relative precision in radius of gyration calculations @param precision_inside = PRECISION_INSIDE - relative precision in calculations of point inside/outside of a solid @param precision = PRECISION - precision in vector calculation @param show_best - calculate and show the best relative match for each parameter """ """ start_time=time.time() start_time=time.time() print("Getting parts database") print("Getting parts database") Loading @@ -458,39 +483,70 @@ def findComponents(assembly, precision_inside = PRECISION_INSIDE, precision = PR else: else: objects,solids = create_file_info(assembly, aname) objects,solids = create_file_info(assembly, aname) # print (objects) # print (objects) progress_bar = Base.ProgressIndicator() progress_bar.start("Looking for matching parts for each of the assembly element ...", len(objects)) candidates=[] candidates=[] for i,o in enumerate(objects): for i,o in enumerate(objects): # try: # try: # FreeCADGui.updateGui() # FreeCADGui.updateGui() # except: # except: # pass # pass # print (i,o) print (i,o) this_candidates = [] this_candidates = [] list_errors=[] rg=o['principal']['RadiusOfGyration'] rg=o['principal']['RadiusOfGyration'] rgp = precision*math.sqrt(rg[0]**2 + rg[1]**2 + rg[2]**2) rg_av = math.sqrt(rg[0]**2 + rg[1]**2 + rg[2]**2) vp = o['volume']*precision rgp = precision_gyration * rg_av ap = o['area']*precision vp = o['volume']*precision_volume ap = o['area']*precision_area for n in info_dict: for n in info_dict: co = info_dict[n][0] co = info_dict[n][0] if ((abs(o['volume'] - co['volume']) < vp) and errors = (abs(o['volume'] - co['volume']), (abs(o['area'] - co['area']) < ap) and abs(o['area'] - co['area']), (abs(rg[0] - co['principal']['RadiusOfGyration'][0]) < ap) and abs(rg[0] - co['principal']['RadiusOfGyration'][0]), (abs(rg[1] - co['principal']['RadiusOfGyration'][1]) < ap) and abs(rg[1] - co['principal']['RadiusOfGyration'][1]), (abs(rg[2] - co['principal']['RadiusOfGyration'][2]) < ap)): abs(rg[2] - co['principal']['RadiusOfGyration'][2]), ) if show_best: list_errors.append(errors) # if ((abs(o['volume'] - co['volume']) < vp) and # (abs(o['area'] - co['area']) < ap) and # (abs(rg[0] - co['principal']['RadiusOfGyration'][0]) < rgp) and # (abs(rg[1] - co['principal']['RadiusOfGyration'][1]) < rgp) and # (abs(rg[2] - co['principal']['RadiusOfGyration'][2]) < rgp)): if ((errors[0] < vp) and (errors[1] < ap) and (errors[2] < rgp) and (errors[3] < rgp) and (errors[4] < rgp)): this_candidates.append(n) this_candidates.append(n) if show_best: weighted_errors = [errors[0]/vp + errors[1]/ap + (errors[2] + errors[3] + errors[4])/rgp for errors in list_errors] best_index = weighted_errors.index(min(weighted_errors)) errors = list_errors[best_index] print ("Best match with %s, relative errors: dV=%f, dS=%f, dRG1=%f, dRG2=%f, dRG3=%f"%( info_dict.keys()[best_index], errors[0]/o['volume'], errors[1]/o['area'], errors[2]/rg_av, errors[3]/rg_av, errors[4]/rg_av)) # Filter candidates by number of color areas matched # Filter candidates by number of color areas matched colored_candidates=colorMatchCandidate(o, this_candidates, info_dict, precision) colored_candidates=colorMatchCandidate(o, this_candidates, info_dict, precision_area) try: try: num_ass_obj_colors = len(o["colorCenters"]) num_ass_obj_colors = len(o["colorCenters"]) except: except: num_ass_obj_colors = 0 num_ass_obj_colors = 0 # print ("%d :colors: %d candidates: %s, colored_candidates: %s"%(i,num_ass_obj_colors, str(this_candidates), str(colored_candidates))) print ("%d :colors: %d candidates: %s, colored_candidates: %s"%(i,num_ass_obj_colors, str(this_candidates), str(colored_candidates))) candidates.append(colored_candidates) candidates.append(colored_candidates) progress_bar.next() # True) # True - enable ESC to abort progress_bar.stop() transformations = findPartsTransformations(solids, objects, candidates, info_dict, precision_inside, precision) transformations = findPartsTransformations(solids, objects, candidates, info_dict, precision_inside, precision) #Each part can be in two orientations - check overlap after loading actual parts #Each part can be in two orientations - check overlap after loading actual parts return {"Solids":solids,"objects":objects,"candidates":candidates,"transformations":transformations} return {"solids":solids,"objects":objects,"candidates":candidates,"transformations":transformations} def ortho3(v0,v1): def ortho3(v0,v1): v0.normalize() v0.normalize() Loading Loading @@ -527,7 +583,7 @@ def ppToMatrix(pp, for color in colors: for color in colors: ## print ("colorCenters=", colorCenters[color]['center']," area=",colorCenters[color]['area']) ## print ("colorCenters=", colorCenters[color]['center']," area=",colorCenters[color]['area']) color_vectors.append(FreeCAD.Vector(colorCenters[color]['center']) - t) color_vectors.append(FreeCAD.Vector(colorCenters[color]['center']) - t) # print ("color_vectors=",color_vectors) print ("color_vectors=",color_vectors) ## print ("color_vectors=",color_vectors, "t=",t) ## print ("color_vectors=",color_vectors, "t=",t) if color_vectors: # find the longest one if color_vectors: # find the longest one lengths = [v.Length for v in color_vectors] lengths = [v.Length for v in color_vectors] Loading @@ -536,7 +592,7 @@ def ppToMatrix(pp, if l > eps: if l > eps: vectors.append(v.normalize()) vectors.append(v.normalize()) if color_vectors: # now find the vector having maximal orthogonal component to v[0] if vectors and color_vectors: # now find the vector having maximal orthogonal component to v[0] lengths = [v.cross(vectors[0]).Length for v in color_vectors] lengths = [v.cross(vectors[0]).Length for v in color_vectors] l = max(lengths) l = max(lengths) v = color_vectors.pop(lengths.index(l)) v = color_vectors.pop(lengths.index(l)) Loading Loading @@ -610,11 +666,16 @@ def list_parts_offsets(): for j,o in enumerate(info_files[name]): for j,o in enumerate(info_files[name]): d = math.sqrt(o["center"][0]**2 + o["center"][1]**2 + o["center"][2]**2) d = math.sqrt(o["center"][0]**2 + o["center"][1]**2 + o["center"][2]**2) if j == 0: if j == 0: print("%3i:"%(i), end="") print("%4d:"%(i), end="") else: else: print(" ", end="") print(" ", end="") print("%s offset = %6.1f"%(name, d)) print("%s offset = %6.1f"%(name, d)) def list_parts(): info_files = get_info_files() for i, name in enumerate(info_files): print ("%4d '%s': %d solids:%s"%(i,name,len(info_files[name]),str(info_files[name]))) # X3D Export # X3D Export def getShapeNode(vertices, faces, diffuseColor = None, main_color_index = 0, colorPerVertex = True): def getShapeNode(vertices, faces, diffuseColor = None, main_color_index = 0, colorPerVertex = True): Loading Loading @@ -647,7 +708,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])) materialNode.set('diffuseColor', "%f %f %f" % tuple(diffuseColor[main_color_index * 3: main_color_index * 3 + 3])) return shapeNode return shapeNode def exportX3D(objects, filepath, colorPerVertex): def exportX3D(objects, filepath, id="part", colorPerVertex=False): """Export given list of objects to a X3D file. """Export given list of objects to a X3D file. Each object is a dictionary in this form: Each object is a dictionary in this form: Loading @@ -657,17 +718,19 @@ def exportX3D(objects, filepath, colorPerVertex): color : [R, G, B,...] # number range is 0-1.0, exactly 3 elements for a single color, 3*N for per-vertex colors color : [R, G, B,...] # number range is 0-1.0, exactly 3 elements for a single color, 3*N for per-vertex colors } } """ """ progress_bar = Base.ProgressIndicator() progress_bar.start("Saving objects to X3D file %s ..."%(filepath), len(objects)) x3dNode = et.Element('x3d') x3dNode = et.Element('x3d') x3dNode.set('profile', 'Interchange') x3dNode.set('profile', 'Interchange') x3dNode.set('version', '3.3') x3dNode.set('version', '3.3') sceneNode = et.SubElement(x3dNode, 'Scene') sceneNode = et.SubElement(x3dNode, 'Scene') progress_bar = Base.ProgressIndicator() groupNode = et.SubElement(sceneNode, 'Group') progress_bar.start("Saving objects to X3D file %s ..."%(filepath), len(objects)) groupNode.set('id', id) for o in objects: for o in objects: shapeNode = getShapeNode(o["points"], o["faces"], o["color"], o["main_color_index"], colorPerVertex) shapeNode = getShapeNode(o["points"], o["faces"], o["color"], o["main_color_index"], colorPerVertex) sceneNode.append(shapeNode) groupNode.append(shapeNode) progress_bar.next() # True) # True - enable ESC to abort progress_bar.next() # True) # True - enable ESC to abort oneliner= et.tostring(x3dNode) oneliner= et.tostring(x3dNode) Loading Loading @@ -744,10 +807,12 @@ def prepareX3dExport(freecadObjects, fname=""): return objects return objects def generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): def generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): start_time=time.time() info_dict= get_info_files(dir_list) # Will (re-) build info files if missing info_dict= get_info_files(dir_list) # Will (re-) build info files if missing step_list = get_step_list(dir_list) #relative to ROOT_DIR step_list = get_step_list(dir_list) #relative to ROOT_DIR if not X3D_DIR in os.listdir(ROOT_DIR): if not X3D_DIR in os.listdir(ROOT_DIR): os.mkdir(os.path.join(ROOT_DIR,X3D_DIR)) os.mkdir(os.path.join(ROOT_DIR,X3D_DIR)) numExported=0 for step_file in step_list: for step_file in step_list: partName,_ = os.path.splitext(os.path.basename(step_file)) partName,_ = os.path.splitext(os.path.basename(step_file)) x3dFile = os.path.join(ROOT_DIR,X3D_DIR,partName + X3D_EXT) x3dFile = os.path.join(ROOT_DIR,X3D_DIR,partName + X3D_EXT) Loading @@ -757,9 +822,11 @@ def generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): doc = FreeCAD.activeDocument() doc = FreeCAD.activeDocument() doc.Label = partName doc.Label = partName x3d_objects = prepareX3dExport(doc.Objects, step_file) # step_file needed just for progress bar x3d_objects = prepareX3dExport(doc.Objects, step_file) # step_file needed just for progress bar exportX3D(x3d_objects, x3dFile, colorPerVertex) exportX3D(x3d_objects, x3dFile, id="part_"+partName, colorPerVertex=colorPerVertex) FreeCAD.closeDocument(doc.Name) FreeCAD.closeDocument(doc.Name) FreeCADGui.updateGui() FreeCADGui.updateGui() numExported += 1 print("Exported %d files as X3D in @%f seconds, "%(numExported, time.time()-start_time)) def matrix4ToX3D(m, eps=0.000001): #assuming 3x3 matrix is pure rotational def matrix4ToX3D(m, eps=0.000001): #assuming 3x3 matrix is pure rotational axis=FreeCAD.Vector(m.A32-m.A23, m.A13-m.A31, m.A21-m.A12) axis=FreeCAD.Vector(m.A32-m.A23, m.A13-m.A31, m.A21-m.A12) Loading Loading @@ -805,11 +872,27 @@ def matrix4ToX3D(m, eps=0.000001): #assuming 3x3 matrix is pure rotational def generateAssemblyX3d(assembly_path, components = None, dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): def generateAssemblyX3d(assembly_path, components = None, dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX, precision_area = PRECISION_AREA, precision_volume = PRECISION_VOLUME, precision_gyration = PRECISION_GYRATION, precision_inside = PRECISION_INSIDE, precision = PRECISION ): start_time=time.time() info_dict = get_info_files(dir_list) # Will (re-) build info files if missing info_dict = get_info_files(dir_list) # Will (re-) build info files if missing generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX) # Will only run if files are not there yet generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX) # Will only run if files are not there yet if not components: if not components: components = findComponents(assembly_path, precision_inside = PRECISION_INSIDE, precision = PRECISION) components = findComponents(assembly_path, precision_area = precision_area, precision_volume = precision_volume, precision_gyration = precision_gyration, precision_inside = precision_inside, precision = precision, show_best = False) assName,_ = os.path.splitext(os.path.basename(assembly_path)) assName,_ = os.path.splitext(os.path.basename(assembly_path)) x3dFile = os.path.join(ROOT_DIR,X3D_DIR,assName + X3D_EXT) # currently in the same directory as parts x3dFile = os.path.join(ROOT_DIR,X3D_DIR,assName + X3D_EXT) # currently in the same directory as parts x3dNode = et.Element('x3d') x3dNode = et.Element('x3d') Loading Loading @@ -880,6 +963,7 @@ def generateAssemblyX3d(assembly_path, components = None, dir_list = DIR_LIST, c print ("Writing assembly to %s"%(x3dFile)) print ("Writing assembly to %s"%(x3dFile)) with open(x3dFile, "wr") as f: with open(x3dFile, "wr") as f: f.write(reparsed.toprettyxml(indent=" ")) f.write(reparsed.toprettyxml(indent=" ")) print("Assembly %s exported as X3D file in @%f seconds, "%(x3dFile, time.time()-start_time)) Loading Loading
x3d_step_assy_color_match.py +114 −30 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ from __future__ import print_function @contact: andrey@elphel.coml @contact: andrey@elphel.coml @deffield updated: Updated @deffield updated: Updated ''' ''' from email import Errors __author__ = "Andrey Filippov" __author__ = "Andrey Filippov" __copyright__ = "Copyright 2015, Elphel, Inc." __copyright__ = "Copyright 2015, Elphel, Inc." __license__ = "GPL" __license__ = "GPL" Loading @@ -37,7 +38,7 @@ __email__ = "andrey@elphel.com" __status__ = "Development" __status__ = "Development" import FreeCAD import FreeCAD import FreeCADGui # just to update cosole output (change to threads later?) import FreeCADGui # just to update console output (change to threads later?) - does not seem to work import Part import Part import os import os import time import time Loading @@ -57,6 +58,10 @@ X3D_DIR = "x3d" X3D_EXT = ".x3d" X3D_EXT = ".x3d" INFO_EXT = ".pickle" INFO_EXT = ".pickle" PRECISION = 0.0001 PRECISION = 0.0001 PRECISION_AREA = 0.001 PRECISION_VOLUME = 0.001 PRECISION_GYRATION = 0.001 PRECISION_INSIDE = 0.03 PRECISION_INSIDE = 0.03 COLOR_PER_VERTEX = True COLOR_PER_VERTEX = True if ROOT_DIR[0] == "~": if ROOT_DIR[0] == "~": Loading Loading @@ -333,6 +338,8 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci containing list of colors (tuples) for which there is an area match between assembly element and a part. containing list of colors (tuples) for which there is an area match between assembly element and a part. Build element part frame from colors first, then (if not enough) use inertial directions Build element part frame from colors first, then (if not enough) use inertial directions """ """ progress_bar = Base.ProgressIndicator() progress_bar.start("Finding transformations for library parts to match assembly elements ...", len(objects)) transformations=[] transformations=[] for i,s in enumerate(solids): for i,s in enumerate(solids): tolerance = insidePrecision * s.BoundBox.DiagonalLength # Or should it be fraction of the translation distance? tolerance = insidePrecision * s.BoundBox.DiagonalLength # Or should it be fraction of the translation distance? Loading Loading @@ -397,10 +404,12 @@ def findPartsTransformations(solids, objects, candidates, info_dict, insidePreci trans.append(None) # so Transformations have same structure as candidates trans.append(None) # so Transformations have same structure as candidates print("*** Could not find match for part %s"%(cand_name)) print("*** Could not find match for part %s"%(cand_name)) transformations.append(trans) transformations.append(trans) progress_bar.next() # True) # True - enable ESC to abort progress_bar.stop() return transformations return transformations def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISION): def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISION_AREA): """ """ @return dictionary partName -> list of colors (as 3-tuples) @return dictionary partName -> list of colors (as 3-tuples) """ """ Loading Loading @@ -428,11 +437,27 @@ def colorMatchCandidate(assy_object, candidates, info_dict, precision = PRECISIO if len(colors) == max_match: if len(colors) == max_match: colored_candidates[candidate]=colors colored_candidates[candidate]=colors return colored_candidates return colored_candidates """ PRECISION_AREA = 0.02 PRECISION_VOLUME = 0.03 PRECISION_INSIDE = 0.03 """ #components=scan_step_parts.findComponents("/home/andrey/parts/0393/export/nc393_07_flat_noassy.stp") #components=scan_step_parts.findComponents("/home/andrey/parts/0393/export/nc393_07_flat_noassy.stp") def findComponents(assembly, precision_inside = PRECISION_INSIDE, precision = PRECISION): def findComponents(assembly, precision_area = PRECISION_AREA, precision_volume = PRECISION_VOLUME, precision_gyration = PRECISION_GYRATION, precision_inside = PRECISION_INSIDE, precision = PRECISION, show_best = True): """ """ @param assembly - may be file path (different treatment for Gui/no-Gui, Shape or doc.Objects @param assembly - may be file path (different treatment for Gui/no-Gui, Shape or doc.Objects @param precision_area = PRECISION_AREA - relative precision in surface area calculations @param precision_volume = PRECISION_VOLUME - relative precision in volume calculations @param precision_gyration = PRECISION_GYRATION - relative precision in radius of gyration calculations @param precision_inside = PRECISION_INSIDE - relative precision in calculations of point inside/outside of a solid @param precision = PRECISION - precision in vector calculation @param show_best - calculate and show the best relative match for each parameter """ """ start_time=time.time() start_time=time.time() print("Getting parts database") print("Getting parts database") Loading @@ -458,39 +483,70 @@ def findComponents(assembly, precision_inside = PRECISION_INSIDE, precision = PR else: else: objects,solids = create_file_info(assembly, aname) objects,solids = create_file_info(assembly, aname) # print (objects) # print (objects) progress_bar = Base.ProgressIndicator() progress_bar.start("Looking for matching parts for each of the assembly element ...", len(objects)) candidates=[] candidates=[] for i,o in enumerate(objects): for i,o in enumerate(objects): # try: # try: # FreeCADGui.updateGui() # FreeCADGui.updateGui() # except: # except: # pass # pass # print (i,o) print (i,o) this_candidates = [] this_candidates = [] list_errors=[] rg=o['principal']['RadiusOfGyration'] rg=o['principal']['RadiusOfGyration'] rgp = precision*math.sqrt(rg[0]**2 + rg[1]**2 + rg[2]**2) rg_av = math.sqrt(rg[0]**2 + rg[1]**2 + rg[2]**2) vp = o['volume']*precision rgp = precision_gyration * rg_av ap = o['area']*precision vp = o['volume']*precision_volume ap = o['area']*precision_area for n in info_dict: for n in info_dict: co = info_dict[n][0] co = info_dict[n][0] if ((abs(o['volume'] - co['volume']) < vp) and errors = (abs(o['volume'] - co['volume']), (abs(o['area'] - co['area']) < ap) and abs(o['area'] - co['area']), (abs(rg[0] - co['principal']['RadiusOfGyration'][0]) < ap) and abs(rg[0] - co['principal']['RadiusOfGyration'][0]), (abs(rg[1] - co['principal']['RadiusOfGyration'][1]) < ap) and abs(rg[1] - co['principal']['RadiusOfGyration'][1]), (abs(rg[2] - co['principal']['RadiusOfGyration'][2]) < ap)): abs(rg[2] - co['principal']['RadiusOfGyration'][2]), ) if show_best: list_errors.append(errors) # if ((abs(o['volume'] - co['volume']) < vp) and # (abs(o['area'] - co['area']) < ap) and # (abs(rg[0] - co['principal']['RadiusOfGyration'][0]) < rgp) and # (abs(rg[1] - co['principal']['RadiusOfGyration'][1]) < rgp) and # (abs(rg[2] - co['principal']['RadiusOfGyration'][2]) < rgp)): if ((errors[0] < vp) and (errors[1] < ap) and (errors[2] < rgp) and (errors[3] < rgp) and (errors[4] < rgp)): this_candidates.append(n) this_candidates.append(n) if show_best: weighted_errors = [errors[0]/vp + errors[1]/ap + (errors[2] + errors[3] + errors[4])/rgp for errors in list_errors] best_index = weighted_errors.index(min(weighted_errors)) errors = list_errors[best_index] print ("Best match with %s, relative errors: dV=%f, dS=%f, dRG1=%f, dRG2=%f, dRG3=%f"%( info_dict.keys()[best_index], errors[0]/o['volume'], errors[1]/o['area'], errors[2]/rg_av, errors[3]/rg_av, errors[4]/rg_av)) # Filter candidates by number of color areas matched # Filter candidates by number of color areas matched colored_candidates=colorMatchCandidate(o, this_candidates, info_dict, precision) colored_candidates=colorMatchCandidate(o, this_candidates, info_dict, precision_area) try: try: num_ass_obj_colors = len(o["colorCenters"]) num_ass_obj_colors = len(o["colorCenters"]) except: except: num_ass_obj_colors = 0 num_ass_obj_colors = 0 # print ("%d :colors: %d candidates: %s, colored_candidates: %s"%(i,num_ass_obj_colors, str(this_candidates), str(colored_candidates))) print ("%d :colors: %d candidates: %s, colored_candidates: %s"%(i,num_ass_obj_colors, str(this_candidates), str(colored_candidates))) candidates.append(colored_candidates) candidates.append(colored_candidates) progress_bar.next() # True) # True - enable ESC to abort progress_bar.stop() transformations = findPartsTransformations(solids, objects, candidates, info_dict, precision_inside, precision) transformations = findPartsTransformations(solids, objects, candidates, info_dict, precision_inside, precision) #Each part can be in two orientations - check overlap after loading actual parts #Each part can be in two orientations - check overlap after loading actual parts return {"Solids":solids,"objects":objects,"candidates":candidates,"transformations":transformations} return {"solids":solids,"objects":objects,"candidates":candidates,"transformations":transformations} def ortho3(v0,v1): def ortho3(v0,v1): v0.normalize() v0.normalize() Loading Loading @@ -527,7 +583,7 @@ def ppToMatrix(pp, for color in colors: for color in colors: ## print ("colorCenters=", colorCenters[color]['center']," area=",colorCenters[color]['area']) ## print ("colorCenters=", colorCenters[color]['center']," area=",colorCenters[color]['area']) color_vectors.append(FreeCAD.Vector(colorCenters[color]['center']) - t) color_vectors.append(FreeCAD.Vector(colorCenters[color]['center']) - t) # print ("color_vectors=",color_vectors) print ("color_vectors=",color_vectors) ## print ("color_vectors=",color_vectors, "t=",t) ## print ("color_vectors=",color_vectors, "t=",t) if color_vectors: # find the longest one if color_vectors: # find the longest one lengths = [v.Length for v in color_vectors] lengths = [v.Length for v in color_vectors] Loading @@ -536,7 +592,7 @@ def ppToMatrix(pp, if l > eps: if l > eps: vectors.append(v.normalize()) vectors.append(v.normalize()) if color_vectors: # now find the vector having maximal orthogonal component to v[0] if vectors and color_vectors: # now find the vector having maximal orthogonal component to v[0] lengths = [v.cross(vectors[0]).Length for v in color_vectors] lengths = [v.cross(vectors[0]).Length for v in color_vectors] l = max(lengths) l = max(lengths) v = color_vectors.pop(lengths.index(l)) v = color_vectors.pop(lengths.index(l)) Loading Loading @@ -610,11 +666,16 @@ def list_parts_offsets(): for j,o in enumerate(info_files[name]): for j,o in enumerate(info_files[name]): d = math.sqrt(o["center"][0]**2 + o["center"][1]**2 + o["center"][2]**2) d = math.sqrt(o["center"][0]**2 + o["center"][1]**2 + o["center"][2]**2) if j == 0: if j == 0: print("%3i:"%(i), end="") print("%4d:"%(i), end="") else: else: print(" ", end="") print(" ", end="") print("%s offset = %6.1f"%(name, d)) print("%s offset = %6.1f"%(name, d)) def list_parts(): info_files = get_info_files() for i, name in enumerate(info_files): print ("%4d '%s': %d solids:%s"%(i,name,len(info_files[name]),str(info_files[name]))) # X3D Export # X3D Export def getShapeNode(vertices, faces, diffuseColor = None, main_color_index = 0, colorPerVertex = True): def getShapeNode(vertices, faces, diffuseColor = None, main_color_index = 0, colorPerVertex = True): Loading Loading @@ -647,7 +708,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])) materialNode.set('diffuseColor', "%f %f %f" % tuple(diffuseColor[main_color_index * 3: main_color_index * 3 + 3])) return shapeNode return shapeNode def exportX3D(objects, filepath, colorPerVertex): def exportX3D(objects, filepath, id="part", colorPerVertex=False): """Export given list of objects to a X3D file. """Export given list of objects to a X3D file. Each object is a dictionary in this form: Each object is a dictionary in this form: Loading @@ -657,17 +718,19 @@ def exportX3D(objects, filepath, colorPerVertex): color : [R, G, B,...] # number range is 0-1.0, exactly 3 elements for a single color, 3*N for per-vertex colors color : [R, G, B,...] # number range is 0-1.0, exactly 3 elements for a single color, 3*N for per-vertex colors } } """ """ progress_bar = Base.ProgressIndicator() progress_bar.start("Saving objects to X3D file %s ..."%(filepath), len(objects)) x3dNode = et.Element('x3d') x3dNode = et.Element('x3d') x3dNode.set('profile', 'Interchange') x3dNode.set('profile', 'Interchange') x3dNode.set('version', '3.3') x3dNode.set('version', '3.3') sceneNode = et.SubElement(x3dNode, 'Scene') sceneNode = et.SubElement(x3dNode, 'Scene') progress_bar = Base.ProgressIndicator() groupNode = et.SubElement(sceneNode, 'Group') progress_bar.start("Saving objects to X3D file %s ..."%(filepath), len(objects)) groupNode.set('id', id) for o in objects: for o in objects: shapeNode = getShapeNode(o["points"], o["faces"], o["color"], o["main_color_index"], colorPerVertex) shapeNode = getShapeNode(o["points"], o["faces"], o["color"], o["main_color_index"], colorPerVertex) sceneNode.append(shapeNode) groupNode.append(shapeNode) progress_bar.next() # True) # True - enable ESC to abort progress_bar.next() # True) # True - enable ESC to abort oneliner= et.tostring(x3dNode) oneliner= et.tostring(x3dNode) Loading Loading @@ -744,10 +807,12 @@ def prepareX3dExport(freecadObjects, fname=""): return objects return objects def generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): def generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): start_time=time.time() info_dict= get_info_files(dir_list) # Will (re-) build info files if missing info_dict= get_info_files(dir_list) # Will (re-) build info files if missing step_list = get_step_list(dir_list) #relative to ROOT_DIR step_list = get_step_list(dir_list) #relative to ROOT_DIR if not X3D_DIR in os.listdir(ROOT_DIR): if not X3D_DIR in os.listdir(ROOT_DIR): os.mkdir(os.path.join(ROOT_DIR,X3D_DIR)) os.mkdir(os.path.join(ROOT_DIR,X3D_DIR)) numExported=0 for step_file in step_list: for step_file in step_list: partName,_ = os.path.splitext(os.path.basename(step_file)) partName,_ = os.path.splitext(os.path.basename(step_file)) x3dFile = os.path.join(ROOT_DIR,X3D_DIR,partName + X3D_EXT) x3dFile = os.path.join(ROOT_DIR,X3D_DIR,partName + X3D_EXT) Loading @@ -757,9 +822,11 @@ def generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): doc = FreeCAD.activeDocument() doc = FreeCAD.activeDocument() doc.Label = partName doc.Label = partName x3d_objects = prepareX3dExport(doc.Objects, step_file) # step_file needed just for progress bar x3d_objects = prepareX3dExport(doc.Objects, step_file) # step_file needed just for progress bar exportX3D(x3d_objects, x3dFile, colorPerVertex) exportX3D(x3d_objects, x3dFile, id="part_"+partName, colorPerVertex=colorPerVertex) FreeCAD.closeDocument(doc.Name) FreeCAD.closeDocument(doc.Name) FreeCADGui.updateGui() FreeCADGui.updateGui() numExported += 1 print("Exported %d files as X3D in @%f seconds, "%(numExported, time.time()-start_time)) def matrix4ToX3D(m, eps=0.000001): #assuming 3x3 matrix is pure rotational def matrix4ToX3D(m, eps=0.000001): #assuming 3x3 matrix is pure rotational axis=FreeCAD.Vector(m.A32-m.A23, m.A13-m.A31, m.A21-m.A12) axis=FreeCAD.Vector(m.A32-m.A23, m.A13-m.A31, m.A21-m.A12) Loading Loading @@ -805,11 +872,27 @@ def matrix4ToX3D(m, eps=0.000001): #assuming 3x3 matrix is pure rotational def generateAssemblyX3d(assembly_path, components = None, dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX): def generateAssemblyX3d(assembly_path, components = None, dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX, precision_area = PRECISION_AREA, precision_volume = PRECISION_VOLUME, precision_gyration = PRECISION_GYRATION, precision_inside = PRECISION_INSIDE, precision = PRECISION ): start_time=time.time() info_dict = get_info_files(dir_list) # Will (re-) build info files if missing info_dict = get_info_files(dir_list) # Will (re-) build info files if missing generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX) # Will only run if files are not there yet generatePartsX3d(dir_list = DIR_LIST, colorPerVertex = COLOR_PER_VERTEX) # Will only run if files are not there yet if not components: if not components: components = findComponents(assembly_path, precision_inside = PRECISION_INSIDE, precision = PRECISION) components = findComponents(assembly_path, precision_area = precision_area, precision_volume = precision_volume, precision_gyration = precision_gyration, precision_inside = precision_inside, precision = precision, show_best = False) assName,_ = os.path.splitext(os.path.basename(assembly_path)) assName,_ = os.path.splitext(os.path.basename(assembly_path)) x3dFile = os.path.join(ROOT_DIR,X3D_DIR,assName + X3D_EXT) # currently in the same directory as parts x3dFile = os.path.join(ROOT_DIR,X3D_DIR,assName + X3D_EXT) # currently in the same directory as parts x3dNode = et.Element('x3d') x3dNode = et.Element('x3d') Loading Loading @@ -880,6 +963,7 @@ def generateAssemblyX3d(assembly_path, components = None, dir_list = DIR_LIST, c print ("Writing assembly to %s"%(x3dFile)) print ("Writing assembly to %s"%(x3dFile)) with open(x3dFile, "wr") as f: with open(x3dFile, "wr") as f: f.write(reparsed.toprettyxml(indent=" ")) f.write(reparsed.toprettyxml(indent=" ")) print("Assembly %s exported as X3D file in @%f seconds, "%(x3dFile, time.time()-start_time)) Loading