def get_boundingbox(shape, tol=1e-6, use_mesh=True): """ return the bounding box of the TopoDS_Shape `shape` Parameters ---------- shape : TopoDS_Shape or a subclass such as TopoDS_Face the shape to compute the bounding box from tol: float tolerance of the computed boundingbox use_mesh : bool a flag that tells whether or not the shape has first to be meshed before the bbox computation. This produces more accurate results """ bbox = Bnd_Box() bbox.SetGap(tol) if use_mesh: mesh = BRepMesh_IncrementalMesh() mesh.SetParallel(True) mesh.SetShape(shape) mesh.Perform() if not mesh.IsDone(): raise AssertionError("Mesh not done.") brepbndlib_Add(shape, bbox, use_mesh) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() return xmin, ymin, zmin, xmax, ymax, zmax, xmax - xmin, ymax - ymin, zmax - zmin
def write_stl_file(a_shape, filename, mode="ascii", linear_deflection=0.9, angular_deflection=0.5): """ export the shape to a STL file Be careful, the shape first need to be explicitely meshed using BRepMesh_IncrementalMesh a_shape: the topods_shape to export filename: the filename mode: optional, "ascii" by default. Can either be "binary" linear_deflection: optional, default to 0.001. Lower, more occurate mesh angular_deflection: optional, default to 0.5. Lower, more accurate_mesh """ assert not a_shape.IsNull() assert mode in ["ascii", "binary"] if os.path.isfile(filename): print("Warning: %s file already exists and will be replaced" % filename) # first mesh the shape mesh = BRepMesh_IncrementalMesh(a_shape, linear_deflection, False, angular_deflection, True) #mesh.SetDeflection(0.05) mesh.Perform() assert mesh.IsDone() stl_exporter = StlAPI_Writer() if mode == "ascii": stl_exporter.SetASCIIMode(True) else: # binary, just set the ASCII flag to False stl_exporter.SetASCIIMode(False) stl_exporter.Write(a_shape, filename) assert os.path.isfile(filename)
def get_boundingbox(shape: TopoDS_Shape, tol=1e-6, use_mesh=True) -> Tuple[tuple, tuple]: """ :param shape: TopoDS_Shape or a subclass such as TopoDS_Face the shape to compute the bounding box from :param tol: tolerance of the computed boundingbox :param use_mesh: a flag that tells whether or not the shape has first to be meshed before the bbox computation. This produces more accurate results :return: return the bounding box of the TopoDS_Shape `shape` """ bbox = Bnd_Box() bbox.SetGap(tol) if use_mesh: mesh = BRepMesh_IncrementalMesh() mesh.SetParallelDefault(True) mesh.SetShape(shape) mesh.Perform() if not mesh.IsDone(): raise AssertionError("Mesh not done.") brepbndlib_Add(shape, bbox, use_mesh) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() return (xmin, ymin, zmin), (xmax, ymax, zmax)
def _to_stl(shp, path, delta): path = os.path.expanduser(path) mesh = BRepMesh_IncrementalMesh(shp.Shape(), delta) if mesh.IsDone() is False: return False stl_writer = StlAPI_Writer() stl_writer.Write(shp.Shape(), path) return True
def get_boundingbox(shape: TopoDS_Shape, tol=1e-6, use_mesh=True): bbox = Bnd_Box() bbox.SetGap(tol) if use_mesh: mesh = BRepMesh_IncrementalMesh() mesh.SetParallelDefault(True) mesh.SetShape(shape) mesh.Perform() if not mesh.IsDone(): raise AssertionError("Mesh not done.") brepbndlib_Add(shape, bbox, use_mesh) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() return xmin, ymin, zmin, xmax, ymax, zmax, xmax - xmin, ymax - ymin, zmax - zmin
def z_max_finder(stl_shp,tol=1e-6,use_mesh=True): """first change the model to mesh form in order to get an accurate MAX and Min bounfing box from topology""" bbox = Bnd_Box() bbox.SetGap(tol) if use_mesh: mesh = BRepMesh_IncrementalMesh() mesh.SetParallelDefault(True) mesh.SetShape(stl_shp) mesh.Perform() if not mesh.IsDone(): raise AssertionError("Mesh not done.") brepbndlib_Add(stl_shp, bbox, use_mesh) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() return zmax
def get_boundingbox(shape, tol=1e-6, use_mesh=True): bbox = Bnd_Box() bbox.SetGap(tol) if use_mesh: mesh = BRepMesh_IncrementalMesh() mesh.SetParallel(True) mesh.SetShape(shape) mesh.Perform() assert mesh.IsDone() brepbndlib_Add(shape, bbox, use_mesh) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() return [ xmin, ymin, zmin, xmax, ymax, zmax, xmax - xmin, ymax - ymin, zmax - zmin ]
def triangulation_from_shape(shape): linear_deflection = 0.01 angular_deflection = 0.5 mesh = BRepMesh_IncrementalMesh(shape, linear_deflection, False, angular_deflection, True) mesh.Perform() assert mesh.IsDone() pts = [] uvs = [] triangles = [] triangle_faces = [] faces = list_face(shape) offset = 0 for f in faces: aLoc = TopLoc_Location() aTriangulation = BRep_Tool().Triangulation(f, aLoc) aTrsf = aLoc.Transformation() aOrient = f.Orientation() aNodes = aTriangulation.Nodes() aUVNodes = aTriangulation.UVNodes() aTriangles = aTriangulation.Triangles() for i in range(1, aTriangulation.NbNodes() + 1): pt = aNodes.Value(i) pt.Transform(aTrsf) pts.append([pt.X(),pt.Y(),pt.Z()]) uv = aUVNodes.Value(i) uvs.append([uv.X(),uv.Y()]) for i in range(1, aTriangulation.NbTriangles() + 1): n1, n2, n3 = aTriangles.Value(i).Get() n1 -= 1 n2 -= 1 n3 -= 1 if aOrient == TopAbs_REVERSED: tmp = n1 n1 = n2 n2 = tmp n1 += offset n2 += offset n3 += offset triangles.append([n1, n2, n3]) triangle_faces.append(f) offset += aTriangulation.NbNodes() return pts, uvs, triangles, triangle_faces
def write_stl(shape, filename, definition=0.1): from OCC.Core.StlAPI import StlAPI_Writer import os directory = os.path.split(__name__)[0] stl_output_dir = os.path.abspath(directory) assert os.path.isdir(stl_output_dir) stl_file = os.path.join(stl_output_dir, filename) stl_writer = StlAPI_Writer() stl_writer.SetASCIIMode(False) from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh mesh = BRepMesh_IncrementalMesh(shape, definition) mesh.Perform() assert mesh.IsDone() stl_writer.Write(shape, stl_file) assert os.path.isfile(stl_file) return stl_file
def stp2stl(filename, fileIODir, linDeflection=0.1, angDeflection=0.1, solidOnly=True): # make sure the path exists otherwise OCE get confused assert os.path.isdir(fileIODir) nameBase = filename.split('.')[0] stpName = os.path.abspath(os.path.join(fileIODir, nameBase + '.stp')) modelShp = read_step_file(stpName) if solidOnly: solids = list(Topo(modelShp).solids()) modelShp = solids[0] mesh = BRepMesh_IncrementalMesh(modelShp, linDeflection) mesh.Perform() assert mesh.IsDone() # set the directory where to output the stlName = os.path.abspath(os.path.join(fileIODir, nameBase + '.stl')) stl_exporter = StlAPI_Writer() stl_exporter.SetASCIIMode(True) # change to False if you need binary export stl_exporter.Write(modelShp, stlName) # make sure the program was created assert os.path.isfile(stlName)
def write_stl_file(a_shape, filename, mode="ascii", linear_deflection=0.9, angular_deflection=0.5): """export the shape to a STL file Be careful, the shape first need to be explicitly meshed using BRepMesh_IncrementalMesh a_shape: the topods_shape to export filename: the filename mode: optional, "ascii" by default. Can either be "binary" linear_deflection: optional, default to 0.001. Lower, more occurate mesh angular_deflection: optional, default to 0.5. Lower, more accurate_mesh """ if a_shape.IsNull(): raise AssertionError("Shape is null.") if mode not in ["ascii", "binary"]: raise AssertionError("mode should be either ascii or binary") if os.path.isfile(filename): print(f"Warning: {filename} already exists and will be replaced") # first mesh the shape mesh = BRepMesh_IncrementalMesh(a_shape, linear_deflection, False, angular_deflection, True) # mesh.SetDeflection(0.05) mesh.Perform() if not mesh.IsDone(): raise AssertionError("Mesh is not done.") stl_exporter = StlAPI_Writer() if mode == "ascii": stl_exporter.SetASCIIMode(True) else: # binary, just set the ASCII flag to False stl_exporter.SetASCIIMode(False) stl_exporter.Write(a_shape, filename) if not os.path.isfile(filename): raise IOError("File not written to disk.")
def mesh_model(model, res_path, convert=True, all_edges=True): fil = model.split("/")[-1][:-5] folder = "/".join(model.split("/")[:-1]) with fileinput.FileInput(model, inplace=True) as fi: for line in fi: print(line.replace( "UNCERTAINTY_MEASURE_WITH_UNIT( LENGTH_MEASURE( 1.00000000000000E-06 )", "UNCERTAINTY_MEASURE_WITH_UNIT( LENGTH_MEASURE( 1.00000000000000E-17 )" ), end='') occ_steps = read_step_file(model) bt = BRep_Tool() for occ_cnt in range(len(occ_steps)): if convert: try: nurbs_converter = BRepBuilderAPI_NurbsConvert( occ_steps[occ_cnt]) nurbs_converter.Perform(occ_steps[occ_cnt]) nurbs = nurbs_converter.Shape() except: print("Conversion failed") continue else: nurbs = occ_steps[occ_cnt] mesh = BRepMesh_IncrementalMesh(occ_steps[occ_cnt], 0.9, False, 0.5, True) mesh.Perform() if not mesh.IsDone(): print("Mesh is not done.") continue occ_topo = TopologyExplorer(nurbs) occ_top = Topo(nurbs) occ_topo1 = TopologyExplorer(occ_steps[occ_cnt]) occ_top1 = Topo(occ_steps[occ_cnt]) d1_feats = [] d2_feats = [] t_curves = [] tr_curves = [] stats = {} stats["model"] = model total_edges = 0 total_surfs = 0 stats["curves"] = [] stats["surfs"] = [] c_cnt = 0 t_cnt = 0 # Iterate over edges for edge in occ_topo.edges(): curve = BRepAdaptor_Curve(edge) stats["curves"].append(edge_map[curve.GetType()]) d1_feat = convert_curve(curve) if edge_map[curve.GetType()] == "Other": continue for f in occ_top.faces_from_edge(edge): if f == None: print("Broken face") continue su = BRepAdaptor_Surface(f) c = BRepAdaptor_Curve2d(edge, f) t_curve = { "surface": f, "3dcurve": edge, "3dcurve_id": c_cnt, "2dcurve_id": t_cnt } t_curves.append(t_curve) tr_curves.append(convert_2dcurve(c)) t_cnt += 1 d1_feats.append(d1_feat) c_cnt += 1 total_edges += 1 patches = [] faces1 = list(occ_topo1.faces()) # Iterate over faces for fci, face in enumerate(occ_topo.faces()): surf = BRepAdaptor_Surface(face) stats["surfs"].append(surf_map[surf.GetType()]) d2_feat = convert_surface(surf) if surf_map[surf.GetType()] == "Other": continue for tc in t_curves: if tc["surface"] == face: patch = { "3dcurves": [], "2dcurves": [], "orientations": [], "surf_orientation": face.Orientation(), "wire_ids": [], "wire_orientations": [] } for wc, fw in enumerate(occ_top.wires_from_face(face)): patch["wire_orientations"].append(fw.Orientation()) if all_edges: edges = [ i for i in WireExplorer(fw).ordered_edges() ] else: edges = list(occ_top.edges_from_wire(fw)) for fe in edges: for ttc in t_curves: if ttc["3dcurve"].IsSame(fe) and tc[ "surface"] == ttc["surface"]: patch["3dcurves"].append(ttc["3dcurve_id"]) patch["2dcurves"].append(ttc["2dcurve_id"]) patch["wire_ids"].append(wc) orientation = fe.Orientation() patch["orientations"].append(orientation) patches.append(patch) break location = TopLoc_Location() facing = (bt.Triangulation(faces1[fci], location)) if facing != None: tab = facing.Nodes() tri = facing.Triangles() verts = [] for i in range(1, facing.NbNodes() + 1): verts.append(list(tab.Value(i).Coord())) faces = [] for i in range(1, facing.NbTriangles() + 1): index1, index2, index3 = tri.Value(i).Get() faces.append([index1 - 1, index2 - 1, index3 - 1]) os.makedirs(res_path, exist_ok=True) igl.write_triangle_mesh( "%s/%s_%03i_mesh_%04i.obj" % (res_path, fil, occ_cnt, fci), np.array(verts), np.array(faces)) d2_feat["faces"] = faces d2_feat["verts"] = verts else: print("Missing triangulation") continue d2_feats.append(d2_feat) total_surfs += 1 bbox = get_boundingbox(occ_steps[occ_cnt], use_mesh=False) xmin, ymin, zmin, xmax, ymax, zmax = bbox[:6] bbox1 = [ "%.2f" % xmin, "%.2f" % ymin, "%.2f" % zmin, "%.2f" % xmax, "%.2f" % ymax, "%.2f" % zmax, "%.2f" % (xmax - xmin), "%.2f" % (ymax - ymin), "%.2f" % (zmax - zmin) ] stats["#edges"] = total_edges stats["#surfs"] = total_surfs # Fix possible orientation problems if convert: for p in patches: # Check orientation of first curve if len(p["2dcurves"]) >= 2: cur = tr_curves[p["2dcurves"][0]] nxt = tr_curves[p["2dcurves"][1]] c_ori = p["orientations"][0] n_ori = p["orientations"][1] if c_ori == 0: pole0 = np.array(cur["poles"][0]) pole1 = np.array(cur["poles"][-1]) else: pole0 = np.array(cur["poles"][-1]) pole1 = np.array(cur["poles"][0]) if n_ori == 0: pole2 = np.array(nxt["poles"][0]) pole3 = np.array(nxt["poles"][-1]) else: pole2 = np.array(nxt["poles"][-1]) pole3 = np.array(nxt["poles"][0]) d02 = np.abs(pole0 - pole2) d12 = np.abs(pole1 - pole2) d03 = np.abs(pole0 - pole3) d13 = np.abs(pole1 - pole3) amin = np.argmin([d02, d12, d03, d13]) if amin == 0 or amin == 2: # Orientation of first curve incorrect, fix p["orientations"][0] = abs(c_ori - 1) # Fix all orientations for i in range(len(p["2dcurves"]) - 1): cur = tr_curves[p["2dcurves"][i]] nxt = tr_curves[p["2dcurves"][i + 1]] c_ori = p["orientations"][i] n_ori = p["orientations"][i + 1] if c_ori == 0: pole1 = np.array(cur["poles"][-1]) else: pole1 = np.array(cur["poles"][0]) if n_ori == 0: pole2 = np.array(nxt["poles"][0]) pole3 = np.array(nxt["poles"][-1]) else: pole2 = np.array(nxt["poles"][-1]) pole3 = np.array(nxt["poles"][0]) d12 = np.abs(pole1 - pole2) d13 = np.abs(pole1 - pole3) amin = np.min([d12, d13]) if amin == 1: # Incorrect orientation, flip p["orientations"][i + 1] = abs(n_ori - 1) features = { "curves": d1_feats, "surfaces": d2_feats, "trim": tr_curves, "topo": patches, "bbox": bbox1 } os.makedirs(res_path, exist_ok=True) fip = fil + "_features2" with open("%s/%s_%03i.yml" % (res_path, fip, occ_cnt), "w") as fili: yaml.dump(features, fili, indent=2) fip = fil + "_features" with open("%s/%s_%03i.yml" % (res_path, fip, occ_cnt), "w") as fili: features2 = copy.deepcopy(features) for sf in features2["surfaces"]: del sf["faces"] del sf["verts"] yaml.dump(features2, fili, indent=2) # res_path = folder.replace("/step/", "/stat/") # fip = fil + "_stats" # with open("%s/%s_%03i.yml"%(res_path, fip, occ_cnt), "w") as fili: # yaml.dump(stats, fili, indent=2) print("Writing results for %s with %i parts." % (model, len(occ_steps)))