Module piecad.utilities
Miscellaneous (but important) functions
Functions
def check_mesh(vertices: list[tuple[float, float, float]], faces: list[tuple[int, int, int]]) ‑> bool-
Expand source code
def check_mesh( vertices: list[tuple[float, float, float]], faces: list[tuple[int, int, int]] ) -> bool: """ Check manifold and winding of the mesh defined in vertices and faces. Returns true if all is well. Otherwise diagnostics will be offered and false is returned. """ return _check_mesh(vertices, faces)Check manifold and winding of the mesh defined in vertices and faces.
Returns true if all is well. Otherwise diagnostics will be offered and false is returned.
def load(filename: str) ‑> Obj2d | Obj3d-
Expand source code
def load(filename: str) -> Obj3d | Obj2d: """ Load a 3d object from a file. The format read from `filename` is determined by the file's extention. The available formats for 3D are: | Type | Extension | |:------------|:------------:| | 3MF | .3mf | | GLB | .glb | | GLTF | .gltf | | OBJ | .obj | | PLY | .ply | | STL | .stl_ascii | | STL binary | .stl | \\(See [https://github/mikedh/trimesh] for more formats.\\) Currently 2d objects are not supported. """ dot_idx = filename.rindex(".") ext = filename[dot_idx + 1 :] mesh = trimesh.exchange.load.load( filename, ext, force="mesh", process=True, validate=False ) if type(mesh) == trimesh.path.Path2D: raise ValidationError("Currently 2d objects are no supported.") else: vertices = np.array(mesh.vertices, np.float64) faces = np.array(mesh.faces, np.uint64) o = Obj3d(_m.Manifold(_m.Mesh64(vertices, faces))) return oLoad a 3d object from a file.
The format read from
filenameis determined by the file's extention.The available formats for 3D are:
Type Extension 3MF .3mf GLB .glb GLTF .gltf OBJ .obj PLY .ply STL .stl_ascii STL binary .stl (See [https://github/mikedh/trimesh] for more formats.)
Currently 2d objects are not supported.
def quick_check_mesh(vertices: list[tuple[float, float, float]], faces: list[tuple[int, int, int]]) ‑> str-
Expand source code
def quick_check_mesh( vertices: list[tuple[float, float, float]], faces: list[tuple[int, int, int]] ) -> str: """ Same checking as check_mesh, but no advice is offered Returns an empty string `""` if all is well, otherwise a short string describing the problem is returned. """ return _quick_check_mesh(vertices, faces)Same checking as check_mesh, but no advice is offered
Returns an empty string
""if all is well, otherwise a short string describing the problem is returned. def save(filename: str,
*objs: Obj2d | Obj3d) ‑> None-
Expand source code
def save(filename: str, *objs: Obj3d | Obj2d) -> None: """ Save a 3d or 2d object in a file suitable for printing, etc. If [p filename] does not contain a path separator character (`'/'` or `'\\'`) the `Downloads` directory for you platform is prepended. Thus if you set [p filename] to `output.obj`, the place where the file is saved is `c:\\Users\\username\\Downloads\\output.obj` on Windows or `/home/username/Downloads/output.obj` on Linux and MacOS. If you want a file saved in the current directory, prepend `'./'` or `'.\\'`. You can override the `Downloads` directory by setting the global environment variable `PIECAD_SAVE_DIR`. The model format placed in [p:filename] is determined by the file's extention. The available formats for 3D are: | Type | Extension | |:------------|:------------:| | 3MF | .3mf | | GLB | .glb | | GLTF | .gltf | | OBJ | .obj | | PLY | .ply | | STL | .stl_ascii | | STL binary | .stl | \\(See [https://github/mikedh/trimesh] for more formats.\\) For 2D, only the SVG (.svg) format is available. """ if filename.find("/") == -1 and filename.find("\\") == -1: filename = str(_Path(_get_save_dir()) / filename) print("Saving: " + filename) _chkGE("len(objs)", len(objs), 1) dot_idx = filename.rindex(".") ext = filename[dot_idx + 1 :] if type(objs[0]) == Obj3d: if len(objs) == 1: obj = objs[0] if filename.endswith(".3mf"): _export_3mf( filename, obj.mo, Obj3d.color_map, Config.get_default_units(), Config.get_default_color(), ) return mesh = obj.mo.to_mesh64() if mesh.vert_properties.shape[1] > 3: vertices = mesh.vert_properties[:, :3] else: vertices = mesh.vert_properties face_colors = _face_colors(obj, mesh) mesh_output = trimesh.Trimesh( vertices=vertices, faces=mesh.tri_verts, face_colors=face_colors, process=True, validate=False, ) # Manifold3d has a different definition than Trimesh if not mesh_output.is_watertight: print("WARNING: output mesh is not watertight") trimesh.exchange.export.export_mesh(mesh_output, filename, ext) else: scene = trimesh.Scene() for obj in objs: mesh = obj.mo.to_mesh() if mesh.vert_properties.shape[1] > 3: vertices = mesh.vert_properties[:, :3] else: vertices = mesh.vert_properties face_colors = _face_colors(obj, mesh) mesh_output = trimesh.Trimesh( vertices=vertices, faces=mesh.tri_verts, face_colors=face_colors, process=True, validate=False, ) # Manifold3d has a different definition than Trimesh if not mesh_output.is_watertight: print("WARNING: output mesh is not watertight") scene.add_geometry(mesh_output) if filename.endswith(".3mf"): s_mesh = scene.to_mesh64() s_vertices = np.array(s_mesh.vertices, np.float64) s_faces = np.array(s_mesh.faces, np.uint64) mo = _m.Manifold(_m.Mesh64(s_vertices, s_faces)) _export_3mf( filename, mo, Obj3d.color_map, Config.get_default_units(), Config.get_default_color(), ) return trimesh.exchange.export.export_scene(scene, filename, ext) # trimesh obj file export does not end with newline # currently this upsets prusa_slicer if ext == "obj": with open(filename, "a") as f: f.write("\n") else: # Obj2d if ext != "svg": raise (ValidationError("Only the SVG format is supported for Obj2d.")) _save_svg(filename, *objs)Save a 3d or 2d object in a file suitable for printing, etc.
If [p filename] does not contain a path separator character (
'/'or'\') theDownloadsdirectory for you platform is prepended. Thus if you set [p filename] tooutput.obj, the place where the file is saved isc:\Users\username\Downloads\output.objon Windows or/home/username/Downloads/output.objon Linux and MacOS. If you want a file saved in the current directory, prepend'./'or'.\'.You can override the
Downloadsdirectory by setting the global environment variablePIECAD_SAVE_DIR.The model format placed in [p:filename] is determined by the file's extention.
The available formats for 3D are:
Type Extension 3MF .3mf GLB .glb GLTF .gltf OBJ .obj PLY .ply STL .stl_ascii STL binary .stl (See [https://github/mikedh/trimesh] for more formats.)
For 2D, only the SVG (.svg) format is available.
def view(obj: Obj2d | Obj3d,
title: str = '') ‑> None-
Expand source code
def view(obj: Obj3d | Obj2d, title: str = "") -> None: """ Use `Piecad-Viewer` to display the geometry object. Returns obj unchanged... so that it works well in return statements. ``` return union(o1, o2, o3) # can be displayed in 3 parts and the whole object, like this: return view(union(view(o1), view(o2), view(o3))) ``` If `Piecad-Viewer` is not already started, it will be auto-started. It is rarely necessary, but one can control `Piecad-Viewer` host and port, by setting your operating systems `PIECAD_VIEWER` environment variable. By default this is set to: "127.0.0.1:8037". This environment variable is also used by the `piecad-viewer` program. """ global _view_thread if _viewer_available == False: return hptmp = os.environ.get("PIECAD_VIEWER", None) if hptmp != None: _piecad_viewer = hptmp _chkGO("obj", obj) if title == "": title = _info_str("view") if type(obj) == Obj2d: color = obj._color if color == None: color = Config.get_default_color() obj = Obj3d(_m.Manifold.extrude(obj.mo, 0.1)).color(color) if _view_thread == None: _view_thread = threading.Thread(target=_view_handler, daemon=True) _view_thread.start() atexit.register(_tell_view_handler_to_exit) mesh = obj.mo.to_mesh64() if mesh.vert_properties.shape[1] > 3: vertices = mesh.vert_properties[:, :3] else: vertices = mesh.vert_properties faces = mesh.tri_verts face_colors = _face_colors(obj, mesh) view_data = {} view_data["title"] = title fc = face_colors.tolist() view_data["color"] = fc view_data["vertices"] = vertices.tolist() fl = faces.tolist() view_data["faces"] = fl _view_queue.put(view_data) return objUse
Piecad-Viewerto display the geometry object.Returns obj unchanged… so that it works well in return statements.
return union(o1, o2, o3) # can be displayed in 3 parts and the whole object, like this: return view(union(view(o1), view(o2), view(o3)))If
Piecad-Vieweris not already started, it will be auto-started.It is rarely necessary, but one can control
Piecad-Viewerhost and port, by setting your operating systemsPIECAD_VIEWERenvironment variable. By default this is set to: "127.0.0.1:8037". This environment variable is also used by thepiecad-viewerprogram. def winding(lt: list[tuple[float, float]]) ‑> str-
Expand source code
def winding(lt: list[tuple[float, float]]) -> str: """ String description of winding of a 2D polygon. Returns one of `"cw"`, `"ccw"`, `"zero"` or `"too small"`. """ def wstr(winding): if winding > 0: return "cw" if winding < 0: return "ccw" return "zero" length = len(lt) if length < 3: return "too small" winding = 0.0 for i in range(0, length): winding += (lt[(i + 1) % length][0] - lt[i][0]) * ( lt[(i + 1) % length][1] + lt[i][1] ) return wstr(winding)String description of winding of a 2D polygon.
Returns one of
"cw","ccw","zero"or"too small".