@@ -9,6 +9,11 @@ def roundvec(x):
99 return np .round (x * 100000000.0 ) / 100000000.0
1010
1111
12+ def read_stl (filename : str ):
13+ input_mesh = mesh .Mesh .from_file (filename )
14+ return np .array ([[roundvec (f ) for f in e ] for e in input_mesh .vectors ])
15+
16+
1217def merge_poly (face_idx_lst , face_idx2edge_idx_lst ):
1318 edge_idx_set = set ()
1419 for f in face_idx_lst :
@@ -55,12 +60,7 @@ def sort_edges(edges_merged):
5560 return sorted_edges
5661
5762
58- def read_stl (filename : str ):
59- input_mesh = mesh .Mesh .from_file (filename )
60- return np .array ([[roundvec (f ) for f in e ] for e in input_mesh .vectors ])
61-
62-
63- def construct_relations_pnts_faces (faces ):
63+ def match_points_of_faces (faces ):
6464 pnt2face_idx_lst = {(f [0 ], f [1 ], f [2 ]): [] for e in faces for f in e }
6565 for i , e in enumerate (faces ):
6666 for f in e :
@@ -76,23 +76,7 @@ def invert_relation(relation, number_of_bins):
7676 return inv_rel
7777
7878
79- if __name__ == "__main__" :
80-
81- filename = sys .argv [1 ]
82-
83- faces = read_stl (filename )
84-
85- number_of_faces = faces .shape [0 ]
86-
87- pnt2face_idx_lst = {(f [0 ], f [1 ], f [2 ]): [] for e in faces for f in e }
88- for i , e in enumerate (faces ):
89- for f in e :
90- pnt2face_idx_lst [(f [0 ], f [1 ], f [2 ])].append (i )
91-
92- pnts , pnt_idx2face_idx_lst = construct_relations_pnts_faces (faces )
93-
94- face_idx2pnt_idx_lst = invert_relation (pnt_idx2face_idx_lst , number_of_faces )
95-
79+ def find_edges_of_faces (face_idx2pnt_idx_lst ):
9680 edge2faces_idx = {}
9781 for j , f in enumerate (face_idx2pnt_idx_lst ):
9882 for i in range (len (f )):
@@ -109,68 +93,34 @@ def invert_relation(relation, number_of_bins):
10993 if edge not in edge2faces_idx :
11094 edge2faces_idx [edge ] = []
11195 edge2faces_idx [edge ].append (j )
96+ return list (edge2faces_idx .keys ()), list (edge2faces_idx .values ())
11297
113- edge_idx2edge = list (edge2faces_idx .keys ())
114- edge_idx2faces_idx = list (edge2faces_idx .values ())
115-
116- face_idx2edge_idx_list = invert_relation (edge_idx2faces_idx , number_of_faces )
117-
118- faces_arr = np .array (faces )
119-
120- faces_to_be_merged_list = cluster_planes (faces_arr )
121-
122- edges_idx_merged_list = [
123- [e for e in list (merge_poly (f , face_idx2edge_idx_list ))]
124- for f in faces_to_be_merged_list
125- ]
126- edges_merged_list = [
127- [edge_idx2edge [e ] for e in edges_idx_merged ]
128- for edges_idx_merged in edges_idx_merged_list
129- ]
130-
131- faces_merged = [f for m in faces_to_be_merged_list for f in m ]
132- remaining_faces = [
133- e for i , e in enumerate (face_idx2pnt_idx_lst ) if i not in faces_merged
134- ]
135-
136- sorted_edges_polygons = []
137- for edges_merged in edges_merged_list :
138- while len (edges_merged ) > 0 :
139- sorted_edges = sort_edges (edges_merged )
140- sorted_edges_polygons .append (sorted_edges )
141-
142- polygons_pts_idx = [
143- [
144- (set (sorted_edges [i - 1 ]).intersection (set (sorted_edges [i ]))).pop ()
145- for i in range (len (sorted_edges ))
146- ]
147- for sorted_edges in sorted_edges_polygons
148- ]
149- all_polygons = polygons_pts_idx + remaining_faces
15098
99+ def find_edges_of_polygons (polygons ):
151100 edge2polygon_idx = {}
152- for j , p in enumerate (all_polygons ):
101+ for j , p in enumerate (polygons ):
153102 for i in range (len (p )):
154103 edge = (p [i - 1 ], p [i ])
155104 if edge not in edge2polygon_idx :
156105 edge2polygon_idx [edge ] = []
157106 edge2 = (p [i ], p [i - 1 ])
158107 if edge2 not in edge2polygon_idx :
159108 edge2polygon_idx [edge2 ] = []
160-
161109 edge2polygon_idx [edge ].append (j )
110+ return edge2polygon_idx
162111
163- all_poly_idx_set = set (range (len (all_polygons )))
164112
113+ def correct_polygon_orientation (polygons ):
114+ edge2polygon_idx = find_edges_of_polygons (polygons )
115+ all_poly_idx_set = set (range (len (polygons )))
165116 nxt_polys = set ({0 })
166117 poly_res = []
167-
168118 while len (nxt_polys ) > 0 :
169119 current_poly_idx = nxt_polys .pop ()
170120 if current_poly_idx >= 0 :
171- current_poly = all_polygons [current_poly_idx ]
121+ current_poly = polygons [current_poly_idx ]
172122 else :
173- current_poly = all_polygons [- current_poly_idx ][::- 1 ]
123+ current_poly = polygons [- current_poly_idx ][::- 1 ]
174124 for i in range (len (current_poly )):
175125 edge = (current_poly [i ], current_poly [i - 1 ])
176126 edge_wrong = (current_poly [i - 1 ], current_poly [i ])
@@ -188,21 +138,91 @@ def invert_relation(relation, number_of_bins):
188138 nxt_polys .add (- e )
189139 all_poly_idx_set .remove (e )
190140 poly_res .append (- e )
141+ all_poly_correct = [polygons [e ] if e >= 0 else polygons [- e ][::- 1 ] for e in poly_res ]
142+ return all_poly_correct
191143
192- all_poly_correct = [
193- all_polygons [e ] if e >= 0 else all_polygons [- e ][::- 1 ] for e in poly_res
144+
145+ def merge_faces (faces_lst , face_idx2edge_idx_list , edge_idx2edge ):
146+ edges_idx_merged_list = [
147+ [e for e in list (merge_poly (f , face_idx2edge_idx_list ))] for f in faces_lst
148+ ]
149+ edges_merged_list = [
150+ [edge_idx2edge [e ] for e in edges_idx_merged ]
151+ for edges_idx_merged in edges_idx_merged_list
194152 ]
153+ sorted_edges_polygons = []
154+ for edges_merged in edges_merged_list :
155+ while len (edges_merged ) > 0 :
156+ sorted_edges = sort_edges (edges_merged )
157+ sorted_edges_polygons .append (sorted_edges )
158+ polygons_pts_idx = [
159+ [
160+ (set (sorted_edges [i - 1 ]).intersection (set (sorted_edges [i ]))).pop ()
161+ for i in range (len (sorted_edges ))
162+ ]
163+ for sorted_edges in sorted_edges_polygons
164+ ]
165+ return polygons_pts_idx
195166
196- all_poly_coord = [[pnts [f ] for f in e ] for e in all_poly_correct ]
197167
168+ def get_unclassified_faces (all_faces , classified_faces_lst ):
169+ classified_faces = [f for m in classified_faces_lst for f in m ]
170+ unclassified_faces = [
171+ e for i , e in enumerate (all_faces ) if i not in classified_faces
172+ ]
173+ return unclassified_faces
174+
175+
176+ def make_cq_poly_faces (poly_coords ):
198177 wire = []
199- for p in all_poly_coord :
178+ for p in poly_coords :
200179 wire .append (
201180 cq .Wire .makePolygon (
202181 [[c for c in p [i - 1 ]] for i in range (len (p ))], close = True
203182 )
204183 )
184+ return [cq .Face .makeFromWires (e , []) for e in wire ]
185+
186+
187+ if __name__ == "__main__" :
188+ print ("stl2step" )
189+
190+ # data input
191+ filename = sys .argv [1 ]
192+ print (f"Reading file: { filename } " )
193+ faces = read_stl (filename )
194+ print (f"Number of input triangles: { len (faces )} " )
195+
196+ # prepare input data
197+ number_of_faces = faces .shape [0 ]
198+ pnts , pnt_idx2face_idx_lst = match_points_of_faces (faces )
199+ face_idx2pnt_idx_lst = invert_relation (pnt_idx2face_idx_lst , number_of_faces )
200+ edge_idx2edge , edge_idx2faces_idx = find_edges_of_faces (face_idx2pnt_idx_lst )
201+ face_idx2edge_idx_list = invert_relation (edge_idx2faces_idx , number_of_faces )
202+
203+ # find planes
204+ faces_to_be_merged_list = cluster_planes (faces )
205+ print (f"Number of planes found: { len (faces_to_be_merged_list )} " )
206+
207+ # construct polygons of planes
208+ polygons_pts_idx = merge_faces (
209+ faces_to_be_merged_list , face_idx2edge_idx_list , edge_idx2edge
210+ )
211+
212+ # construct remaining unclassified triangles
213+ remaining_faces = get_unclassified_faces (
214+ face_idx2pnt_idx_lst , faces_to_be_merged_list
215+ )
216+ print (f"Number of remaining triangles: { len (remaining_faces )} " )
217+
218+ # build surface from planes and remaining triangles
219+ all_polygons = polygons_pts_idx + remaining_faces
220+ all_poly_correct = correct_polygon_orientation (all_polygons )
221+ all_poly_coord = [[pnts [f ] for f in e ] for e in all_poly_correct ]
222+ cq_plane_faces = make_cq_poly_faces (all_poly_coord )
205223
206- f = [cq .Face .makeFromWires (e , []) for e in wire ]
207- shell = cq .Shell .makeShell (f )
208- cq .exporters .export (shell , f"{ filename [:- 4 ]} .step" )
224+ shell = cq .Shell .makeShell (cq_plane_faces )
225+ output_filename = f"{ filename [:- 4 ]} .step"
226+ print (f"Writing file: { output_filename } " )
227+ cq .exporters .export (shell , output_filename )
228+ print ("Done." )
0 commit comments