Ver código fonte

improved mesh to step script

Nicole Portas 3 semanas atrás
pai
commit
813451b4c6
1 arquivos alterados com 57 adições e 37 exclusões
  1. 57 37
      mesh_to_step.py

+ 57 - 37
mesh_to_step.py

@@ -1,13 +1,13 @@
 #!/usr/bin/env python3
 """
 =============================================================================
-STL Mesh to Parametric STEP Solid Converter (OpenCASCADE Backend)
+STL Mesh to Parametric STEP Solid Converter with Decimation
 =============================================================================
-Requirements (Run inside your environment before executing):
-    pip install trimesh scipy cadquery
+Requirements:
+    pip install trimesh scipy cadquery fast-simplification numpy
 
 Usage:
-    python mesh_to_step.py <path_to_mesh.stl>
+    python mesh_to_step.py <path_to_mesh.stl> [target_face_count]
 =============================================================================
 """
 
@@ -17,45 +17,69 @@ 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
+    import fast_simplification
+    import numpy as np
+    from OCP.BRepBuilderAPI import (
+        BRepBuilderAPI_MakeFace,
+        BRepBuilderAPI_Sewing,
+        BRepBuilderAPI_MakeSolid,
+        BRepBuilderAPI_MakeEdge,
+        BRepBuilderAPI_MakeWire,
+    )
     from OCP.gp import gp_Pnt
+    from OCP.TopExp import TopExp_Explorer
+    from OCP.TopAbs import TopAbs_SHELL
+    from OCP.TopoDS import TopoDS
 except ImportError as e:
-    print(f"[fucking error] Missing dependencies. Run: pip install trimesh scipy cadquery")
+    print(f"[fucking error] Missing dependencies. Run: pip install trimesh scipy cadquery fast-simplification numpy")
     print(f"Details: {e}")
     sys.exit(1)
 
-def mesh_to_step(input_path):
+
+def mesh_to_step(input_path: str, target_faces: int = 25000):
     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())
+
+    initial_face_count = len(mesh.faces)
+    print(f"[*] Initial mesh complexity: {initial_face_count} faces")
+
+    # Geometry cleanup
+    mesh.process(validate=True)
     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
+
+    # Mesh decimation - Bypassing trimesh's broken wrapper
+    if len(mesh.faces) > target_faces:
+        print(f"[*] Decimating mesh down to ~{target_faces} faces without altering boundary geometry...")
+        vertices, faces = fast_simplification.simplify(
+            mesh.vertices.view(np.ndarray),
+            mesh.faces.view(np.ndarray),
+            target_count=target_faces
+        )
+        mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
+        print(f"[*] Post-simplification face count: {len(mesh.faces)}")
+
+    print(f"[*] Extracting {len(mesh.faces)} facets and building OCP faces...")
     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()
@@ -66,57 +90,53 @@ def mesh_to_step(input_path):
             except Exception:
                 continue
 
-    print(f"[*] Successfully added {success_faces}/{len(mesh.faces)} faces to the engine.")
+    print(f"[*] Successfully added {success_faces}/{len(mesh.faces)} faces to 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}")
+
+    print(f"[*] Exporting simplified 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>")
+        print("Usage: python mesh_to_step.py <path_to_mesh.stl> [target_face_count]")
         sys.exit(1)
-        
-    mesh_to_step(sys.argv[1])
+
+    faces = int(sys.argv[2]) if len(sys.argv) > 2 else 25000
+    mesh_to_step(sys.argv[1], target_faces=faces)