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 extension. 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("2D objects are not currently supported.") return obj3d_from_vertices_and_faces(mesh.vertices, mesh.faces)Load a 3d object from a file.
The format read from
filenameis determined by the file's extension.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 obj3d_from_vertices_and_faces(vertices: list[tuple[float, float, float]],
faces: list[tuple[int, int, int]],
check: str = 'none') ‑> Obj3d-
Expand source code
def obj3d_from_vertices_and_faces( vertices: list[tuple[float, float, float]], faces: list[tuple[int, int, int]], check: str = "none", ) -> Obj3d: """ Create an Obj3d instance from vertices and faces with optional mesh checking and repair. Used internally within the Piecad framework, but might be useful to advanced users. Parameters: - faces: List of face tuples (int, int, int) - vertices: List of vertex tuples (float, float, float) - check: One of "interactive", "batch", "repair", "none" Returns: - Obj3d instance Raises: - ValidationError if the mesh is flawed in batch mode or if manifold creation fails. """ if check == "interactive": if not _check_mesh(vertices, faces): return Obj3d() elif check == "batch": msg = _quick_check_mesh(vertices, faces) if msg != "": raise ValidationError(f"Polyhedron is flawed: {msg}") elif check == "repair": mesh_output = trimesh.Trimesh( vertices=vertices, faces=faces, process=True, validate=True ) vertices = mesh_output.vertices faces = mesh_output.faces vertices = np.array(vertices, np.float64) faces = np.array(faces, np.uint64) mesh = _m.Mesh64(vertices, faces) mo = _m.Manifold(mesh) if mo.is_empty(): raise ValidationError(f"Error creating Manifold: {mo.status()}.") return Obj3d(mo)Create an Obj3d instance from vertices and faces with optional mesh checking and repair. Used internally within the Piecad framework, but might be useful to advanced users.
Parameters: - faces: List of face tuples (int, int, int) - vertices: List of vertex tuples (float, float, float) - check: One of "interactive", "batch", "repair", "none"
Returns: - Obj3d instance
Raises: - ValidationError if the mesh is flawed in batch mode or if manifold creation fails.
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 extension. 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(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_mesh64() if mesh.vert_properties.shape[1] > 3: vertices = mesh.vert_properties[:, :3] else: vertices = mesh.vert_properties face_colors = _face_colors(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_geometry() 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 extension.
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. If you have trouble viewing and are using a graphical debugger, see the `view_all_now()` function below. ``` 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))) ``` """ global _view_meshes, _view_meshes_titles _chkGO("obj", obj) if title == "": title = _info_str("view") if type(obj) == Obj2d: color = obj._color if obj._color is not None else Config.get_default_color() obj = Obj3d(_m.Manifold.extrude(obj.mo, 0.1)).color(color) 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 mesh_output = trimesh.Trimesh( vertices=vertices, faces=faces, face_colors=_face_colors(mesh), process=True, validate=False, ) if len(_view_meshes) == 0: atexit.register(_wait_for_view_handler_exit) _view_meshes.append(mesh_output) _view_meshes_titles.append(title) return objUse
Piecad-Viewerto display the geometry object.Returns obj unchanged… so that it works well in return statements.
If you have trouble viewing and are using a graphical debugger, see the
view_all_now()function below.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))) def view_all_now() ‑> None-
Expand source code
def view_all_now() -> None: """ The `view()` function merely records a list of objects to be displayed. By default, a function to view the meshes is called from `atexit`. Unfortunately a number of graphical debuggers (e.g. Visual Studio Code and PyCharm) have a short timeout for `atexit` functions. To avoid this issue, use `view_all_now()` at the end of your script to display all objects recorded by `view()` with no timeout. Alternatively, run the script without debugging. """ from ._viewer import show_meshes v = show_meshes(_view_meshes, _view_meshes_titles) _viewer_closed_event.wait() v.clear() _view_meshes.clear() _view_meshes_titles.clear() _viewer_closed_event.clear()The
view()function merely records a list of objects to be displayed. By default, a function to view the meshes is called fromatexit. Unfortunately a number of graphical debuggers (e.g. Visual Studio Code and PyCharm) have a short timeout foratexitfunctions. To avoid this issue, useview_all_now()at the end of your script to display all objects recorded byview()with no timeout. Alternatively, run the script without debugging. 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".