| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- #!/usr/bin/env python3
- """
- =============================================================================
- STL Mesh to Parametric STEP Solid Converter (OpenCASCADE Backend)
- =============================================================================
- Requirements (Run inside your environment before executing):
- pip install trimesh scipy cadquery
- Usage:
- python mesh_to_step.py <path_to_mesh.stl>
- =============================================================================
- """
- import sys
- from pathlib import Path
- try:
- import trimesh
- import cadquery as cq
- from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Sewing, BRepBuilderAPI_MakeSolid, BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire
- from OCP.gp import gp_Pnt
- except ImportError as e:
- print(f"[fucking error] Missing dependencies. Run: pip install trimesh scipy cadquery")
- print(f"Details: {e}")
- sys.exit(1)
- def mesh_to_step(input_path):
- input_file = Path(input_path)
- if not input_file.exists():
- print(f"[fucking error] File not found: {input_file}")
- sys.exit(1)
-
- output_step = input_file.with_suffix('.step')
-
- print(f"[*] Loading mesh: {input_file}")
- mesh = trimesh.load(input_file, force='mesh')
-
- print("[*] Running geometry healing filters...")
- mesh.update_faces(mesh.unique_faces())
- mesh.fill_holes()
-
- print(f"[*] Extracting {len(mesh.faces)} facets and building faces...")
- # 0.1mm tolerance to sew loose vertices and fix misaligned triangle normals
- sewing = BRepBuilderAPI_Sewing(0.1)
-
- success_faces = 0
- for face in mesh.faces:
- p1 = gp_Pnt(float(mesh.vertices[face[0]][0]), float(mesh.vertices[face[0]][1]), float(mesh.vertices[face[0]][2]))
- p2 = gp_Pnt(float(mesh.vertices[face[1]][0]), float(mesh.vertices[face[1]][1]), float(mesh.vertices[face[1]][2]))
- p3 = gp_Pnt(float(mesh.vertices[face[2]][0]), float(mesh.vertices[face[2]][1]), float(mesh.vertices[face[2]][2]))
-
- try:
- ocp_face = BRepBuilderAPI_MakeFace(p1, p2, p3).Face()
- sewing.Add(ocp_face)
- success_faces += 1
- except Exception:
- try:
- # Fallback path if raw surface generation hits a localized coordinate breakdown
- e1 = BRepBuilderAPI_MakeEdge(p1, p2).Edge()
- e2 = BRepBuilderAPI_MakeEdge(p2, p3).Edge()
- e3 = BRepBuilderAPI_MakeEdge(p3, p1).Edge()
- wire = BRepBuilderAPI_MakeWire(e1, e2, e3).Wire()
- ocp_face = BRepBuilderAPI_MakeFace(wire).Face()
- sewing.Add(ocp_face)
- success_faces += 1
- except Exception:
- continue
- print(f"[*] Successfully added {success_faces}/{len(mesh.faces)} faces to the engine.")
- if success_faces == 0:
- print("[fucking error] All faces rejected by OpenCASCADE geometry constraints.")
- sys.exit(1)
- print("[*] Sewing facets together via OpenCASCADE engine...")
- sewing.Perform()
-
- print("\n=== OpenCASCADE Sewing Diagnostics ===")
- print(f"Free edges (gaps): {sewing.NbFreeEdges()}")
- print(f"Multiple edges: {sewing.NbMultipleEdges()}")
- print("======================================\n")
- sewed_shape = sewing.SewedShape()
-
- print("[*] Computing solid volume...")
- solid_builder = BRepBuilderAPI_MakeSolid()
-
- from OCP.TopExp import TopExp_Explorer
- from OCP.TopAbs import TopAbs_SHELL
- from OCP.TopoDS import TopoDS
-
- explorer = TopExp_Explorer(sewed_shape, TopAbs_SHELL)
- shell_count = 0
-
- while explorer.More():
- shell = TopoDS.Shell(explorer.Current())
- solid_builder.Add(shell)
- shell_count += 1
- explorer.Next()
-
- if shell_count == 0:
- print("[fucking error] Sewing failed to produce a valid shell structure.")
- sys.exit(1)
-
- if not solid_builder.IsDone():
- print("[fucking error] OpenCASCADE sewed the shell but cannot compute a closed solid volume.")
- sys.exit(1)
-
- ocp_solid = solid_builder.Solid()
-
- # Cast directly into CadQuery's core Shape object to handle pristine STEP output
- cq_shape = cq.Shape.cast(ocp_solid)
-
- print(f"[*] Exporting pristine STEP file: {output_step}")
- cq.exporters.export(cq_shape, str(output_step), cq.exporters.ExportTypes.STEP)
- print("[+] Done!")
- if __name__ == "__main__":
- if len(sys.argv) < 2:
- print("Usage: python mesh_to_step.py <path_to_mesh.stl>")
- sys.exit(1)
-
- mesh_to_step(sys.argv[1])
|