Skip to content

Commit 9e35672

Browse files
committed
refactored; simple status output
1 parent 592a4ee commit 9e35672

File tree

1 file changed

+93
-73
lines changed

1 file changed

+93
-73
lines changed

stl2step/__main__.py

Lines changed: 93 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
1217
def 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

Comments
 (0)