trenchfoot 0.3.1__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of trenchfoot might be problematic. Click here for more details.
- trenchfoot/generate_scenarios.py +27 -27
- trenchfoot/render_colors.py +1 -0
- trenchfoot/scenarios/S01_straight_vwalls/sdf_metadata.json +23 -4
- trenchfoot/scenarios/S02_straight_slope_pipe/sdf_metadata.json +35 -4
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/sdf_metadata.json +52 -5
- trenchfoot/scenarios/S04_U_slope_multi_noise/sdf_metadata.json +66 -7
- trenchfoot/scenarios/S05_wide_slope_pair/sdf_metadata.json +52 -5
- trenchfoot/scenarios/S06_bumpy_wide_loop/sdf_metadata.json +63 -36
- trenchfoot/scenarios/S07_circular_well/sdf_metadata.json +136 -201
- trenchfoot/trench_scene_generator_v3.py +136 -24
- {trenchfoot-0.3.1.dist-info → trenchfoot-0.4.1.dist-info}/METADATA +1 -1
- {trenchfoot-0.3.1.dist-info → trenchfoot-0.4.1.dist-info}/RECORD +15 -15
- {trenchfoot-0.3.1.dist-info → trenchfoot-0.4.1.dist-info}/WHEEL +0 -0
- {trenchfoot-0.3.1.dist-info → trenchfoot-0.4.1.dist-info}/entry_points.txt +0 -0
- {trenchfoot-0.3.1.dist-info → trenchfoot-0.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,276 +2,211 @@
|
|
|
2
2
|
"sdf_metadata": {
|
|
3
3
|
"version": "2.0",
|
|
4
4
|
"normal_convention": "into_void",
|
|
5
|
-
"geometry_type": "
|
|
5
|
+
"geometry_type": "closed_well",
|
|
6
6
|
"trench_opening": {
|
|
7
7
|
"type": "polygon",
|
|
8
8
|
"vertices_xy": [
|
|
9
9
|
[
|
|
10
|
-
|
|
11
|
-
-
|
|
10
|
+
2.5048385723763116,
|
|
11
|
+
-2.7755575615628914e-15
|
|
12
12
|
],
|
|
13
13
|
[
|
|
14
|
-
|
|
15
|
-
0.
|
|
14
|
+
2.456708801572928,
|
|
15
|
+
0.4886697636833139
|
|
16
16
|
],
|
|
17
17
|
[
|
|
18
|
-
|
|
19
|
-
0.
|
|
18
|
+
2.3141690892632654,
|
|
19
|
+
0.9585602223974381
|
|
20
20
|
],
|
|
21
21
|
[
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
2.082697156654193,
|
|
23
|
+
1.3916137493315945
|
|
24
24
|
],
|
|
25
25
|
[
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
1.7711883403049211,
|
|
27
|
+
1.77118834030492
|
|
28
28
|
],
|
|
29
29
|
[
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
1.3916137493315943,
|
|
31
|
+
2.082697156654193
|
|
32
32
|
],
|
|
33
33
|
[
|
|
34
|
-
0.
|
|
35
|
-
|
|
34
|
+
0.9585602223974379,
|
|
35
|
+
2.3141690892632654
|
|
36
36
|
],
|
|
37
37
|
[
|
|
38
|
-
0.
|
|
39
|
-
|
|
38
|
+
0.488669763683314,
|
|
39
|
+
2.456708801572928
|
|
40
40
|
],
|
|
41
41
|
[
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
9.71445146547012e-17,
|
|
43
|
+
2.504838572376311
|
|
44
44
|
],
|
|
45
45
|
[
|
|
46
|
-
-0.
|
|
47
|
-
|
|
46
|
+
-0.48866976368331355,
|
|
47
|
+
2.456708801572928
|
|
48
48
|
],
|
|
49
49
|
[
|
|
50
|
-
-0.
|
|
51
|
-
|
|
50
|
+
-0.9585602223974382,
|
|
51
|
+
2.314169089263265
|
|
52
52
|
],
|
|
53
53
|
[
|
|
54
|
-
-
|
|
55
|
-
|
|
54
|
+
-1.3916137493315945,
|
|
55
|
+
2.082697156654193
|
|
56
56
|
],
|
|
57
57
|
[
|
|
58
|
-
-
|
|
59
|
-
|
|
58
|
+
-1.7711883403049202,
|
|
59
|
+
1.7711883403049207
|
|
60
60
|
],
|
|
61
61
|
[
|
|
62
|
-
-
|
|
63
|
-
|
|
62
|
+
-2.0826971566541927,
|
|
63
|
+
1.3916137493315948
|
|
64
64
|
],
|
|
65
65
|
[
|
|
66
|
-
-
|
|
67
|
-
0.
|
|
66
|
+
-2.314169089263265,
|
|
67
|
+
0.9585602223974394
|
|
68
68
|
],
|
|
69
69
|
[
|
|
70
|
-
-
|
|
71
|
-
0.
|
|
70
|
+
-2.456708801572928,
|
|
71
|
+
0.48866976368331483
|
|
72
72
|
],
|
|
73
73
|
[
|
|
74
|
-
-
|
|
75
|
-
|
|
74
|
+
-2.504838572376311,
|
|
75
|
+
1.8041124150158794e-16
|
|
76
76
|
],
|
|
77
77
|
[
|
|
78
|
-
-
|
|
79
|
-
-0.
|
|
78
|
+
-2.4567088015729275,
|
|
79
|
+
-0.48866976368331533
|
|
80
80
|
],
|
|
81
81
|
[
|
|
82
|
-
-
|
|
83
|
-
-0.
|
|
82
|
+
-2.3141690892632654,
|
|
83
|
+
-0.958560222397438
|
|
84
84
|
],
|
|
85
85
|
[
|
|
86
|
-
-
|
|
87
|
-
-
|
|
86
|
+
-2.0826971566541936,
|
|
87
|
+
-1.3916137493315939
|
|
88
88
|
],
|
|
89
89
|
[
|
|
90
|
-
-
|
|
91
|
-
-
|
|
90
|
+
-1.7711883403049204,
|
|
91
|
+
-1.7711883403049207
|
|
92
92
|
],
|
|
93
93
|
[
|
|
94
|
-
-
|
|
95
|
-
-
|
|
94
|
+
-1.3916137493315952,
|
|
95
|
+
-2.0826971566541927
|
|
96
96
|
],
|
|
97
97
|
[
|
|
98
|
-
-0.
|
|
99
|
-
-
|
|
98
|
+
-0.9585602223974392,
|
|
99
|
+
-2.314169089263265
|
|
100
100
|
],
|
|
101
101
|
[
|
|
102
|
-
-0.
|
|
103
|
-
-
|
|
102
|
+
-0.48866976368331394,
|
|
103
|
+
-2.456708801572928
|
|
104
104
|
],
|
|
105
105
|
[
|
|
106
|
-
-
|
|
107
|
-
-
|
|
106
|
+
-1.0408340855860843e-15,
|
|
107
|
+
-2.504838572376311
|
|
108
108
|
],
|
|
109
109
|
[
|
|
110
|
-
0.
|
|
111
|
-
-
|
|
110
|
+
0.4886697636833134,
|
|
111
|
+
-2.456708801572928
|
|
112
112
|
],
|
|
113
113
|
[
|
|
114
|
-
0.
|
|
115
|
-
-
|
|
114
|
+
0.9585602223974385,
|
|
115
|
+
-2.314169089263265
|
|
116
116
|
],
|
|
117
117
|
[
|
|
118
|
-
|
|
119
|
-
-
|
|
118
|
+
1.3916137493315957,
|
|
119
|
+
-2.0826971566541923
|
|
120
120
|
],
|
|
121
121
|
[
|
|
122
|
-
|
|
123
|
-
-
|
|
122
|
+
1.7711883403049202,
|
|
123
|
+
-1.7711883403049207
|
|
124
124
|
],
|
|
125
125
|
[
|
|
126
|
-
|
|
127
|
-
-
|
|
126
|
+
2.0826971566541927,
|
|
127
|
+
-1.391613749331595
|
|
128
128
|
],
|
|
129
129
|
[
|
|
130
|
-
|
|
131
|
-
-0.
|
|
130
|
+
2.314169089263265,
|
|
131
|
+
-0.9585602223974392
|
|
132
132
|
],
|
|
133
133
|
[
|
|
134
|
-
|
|
135
|
-
-0.
|
|
136
|
-
],
|
|
137
|
-
[
|
|
138
|
-
0.5048152733278031,
|
|
139
|
-
0.09801714032956123
|
|
140
|
-
],
|
|
141
|
-
[
|
|
142
|
-
2.495184726672197,
|
|
143
|
-
-0.09801714032956123
|
|
144
|
-
],
|
|
145
|
-
[
|
|
146
|
-
2.4663626472770424,
|
|
147
|
-
-0.3906526233537543
|
|
148
|
-
],
|
|
149
|
-
[
|
|
150
|
-
2.3427596344991386,
|
|
151
|
-
-0.864309825802098
|
|
152
|
-
],
|
|
153
|
-
[
|
|
154
|
-
2.1291256828021727,
|
|
155
|
-
-1.3047520863554014
|
|
156
|
-
],
|
|
157
|
-
[
|
|
158
|
-
1.8336706251425579,
|
|
159
|
-
-1.6950534559434671
|
|
160
|
-
],
|
|
161
|
-
[
|
|
162
|
-
1.4677486336930485,
|
|
163
|
-
-2.020214871816555
|
|
164
|
-
],
|
|
165
|
-
[
|
|
166
|
-
1.0454218853736323,
|
|
167
|
-
-2.267740563115285
|
|
168
|
-
],
|
|
169
|
-
[
|
|
170
|
-
0.5829201602786549,
|
|
171
|
-
-2.4281182563370542
|
|
172
|
-
],
|
|
173
|
-
[
|
|
174
|
-
0.09801714032956031,
|
|
175
|
-
-2.4951847266721967
|
|
176
|
-
],
|
|
177
|
-
[
|
|
178
|
-
-0.39065262335375434,
|
|
179
|
-
-2.4663626472770424
|
|
180
|
-
],
|
|
181
|
-
[
|
|
182
|
-
-0.864309825802098,
|
|
183
|
-
-2.3427596344991386
|
|
184
|
-
],
|
|
185
|
-
[
|
|
186
|
-
-1.3047520863554014,
|
|
187
|
-
-2.1291256828021727
|
|
188
|
-
],
|
|
189
|
-
[
|
|
190
|
-
-1.6950534559434667,
|
|
191
|
-
-1.8336706251425583
|
|
192
|
-
],
|
|
193
|
-
[
|
|
194
|
-
-2.020214871816555,
|
|
195
|
-
-1.4677486336930485
|
|
196
|
-
],
|
|
197
|
-
[
|
|
198
|
-
-2.2677405631152854,
|
|
199
|
-
-1.045421885373632
|
|
200
|
-
],
|
|
201
|
-
[
|
|
202
|
-
-2.4281182563370547,
|
|
203
|
-
-0.5829201602786548
|
|
204
|
-
],
|
|
205
|
-
[
|
|
206
|
-
-2.4951847266721967,
|
|
207
|
-
-0.09801714032956042
|
|
208
|
-
],
|
|
209
|
-
[
|
|
210
|
-
-2.4663626472770424,
|
|
211
|
-
0.3906526233537535
|
|
212
|
-
],
|
|
213
|
-
[
|
|
214
|
-
-2.3427596344991386,
|
|
215
|
-
0.8643098258020978
|
|
216
|
-
],
|
|
217
|
-
[
|
|
218
|
-
-2.129125682802173,
|
|
219
|
-
1.3047520863554005
|
|
220
|
-
],
|
|
221
|
-
[
|
|
222
|
-
-1.8336706251425579,
|
|
223
|
-
1.6950534559434671
|
|
224
|
-
],
|
|
225
|
-
[
|
|
226
|
-
-1.4677486336930485,
|
|
227
|
-
2.020214871816555
|
|
228
|
-
],
|
|
229
|
-
[
|
|
230
|
-
-1.0454218853736315,
|
|
231
|
-
2.2677405631152854
|
|
232
|
-
],
|
|
233
|
-
[
|
|
234
|
-
-0.5829201602786549,
|
|
235
|
-
2.4281182563370542
|
|
236
|
-
],
|
|
237
|
-
[
|
|
238
|
-
-0.0980171403295606,
|
|
239
|
-
2.4951847266721967
|
|
240
|
-
],
|
|
241
|
-
[
|
|
242
|
-
0.3906526233537532,
|
|
243
|
-
2.4663626472770424
|
|
244
|
-
],
|
|
245
|
-
[
|
|
246
|
-
0.8643098258020975,
|
|
247
|
-
2.3427596344991386
|
|
248
|
-
],
|
|
249
|
-
[
|
|
250
|
-
1.3047520863554007,
|
|
251
|
-
2.129125682802173
|
|
252
|
-
],
|
|
253
|
-
[
|
|
254
|
-
1.6950534559434671,
|
|
255
|
-
1.833670625142558
|
|
256
|
-
],
|
|
257
|
-
[
|
|
258
|
-
2.020214871816555,
|
|
259
|
-
1.4677486336930485
|
|
260
|
-
],
|
|
261
|
-
[
|
|
262
|
-
2.267740563115285,
|
|
263
|
-
1.0454218853736321
|
|
264
|
-
],
|
|
265
|
-
[
|
|
266
|
-
2.4281182563370542,
|
|
267
|
-
0.5829201602786551
|
|
268
|
-
],
|
|
269
|
-
[
|
|
270
|
-
2.4951847266721967,
|
|
271
|
-
0.0980171403295607
|
|
134
|
+
2.456708801572928,
|
|
135
|
+
-0.4886697636833141
|
|
272
136
|
]
|
|
273
137
|
],
|
|
274
138
|
"z_level": 0.0
|
|
139
|
+
},
|
|
140
|
+
"surface_groups": {
|
|
141
|
+
"trench_bottom": {
|
|
142
|
+
"normal_direction": "up",
|
|
143
|
+
"surface_type": "floor"
|
|
144
|
+
},
|
|
145
|
+
"trench_walls": {
|
|
146
|
+
"normal_direction": "inward",
|
|
147
|
+
"surface_type": "wall"
|
|
148
|
+
},
|
|
149
|
+
"ground_surface": {
|
|
150
|
+
"normal_direction": "up",
|
|
151
|
+
"surface_type": "ground"
|
|
152
|
+
},
|
|
153
|
+
"pipe0_pipe_side": {
|
|
154
|
+
"normal_direction": "outward",
|
|
155
|
+
"surface_type": "embedded_object"
|
|
156
|
+
},
|
|
157
|
+
"pipe0_pipe_cap_neg": {
|
|
158
|
+
"normal_direction": "outward",
|
|
159
|
+
"surface_type": "embedded_object"
|
|
160
|
+
},
|
|
161
|
+
"pipe0_pipe_cap_pos": {
|
|
162
|
+
"normal_direction": "outward",
|
|
163
|
+
"surface_type": "embedded_object"
|
|
164
|
+
},
|
|
165
|
+
"pipe1_pipe_side": {
|
|
166
|
+
"normal_direction": "outward",
|
|
167
|
+
"surface_type": "embedded_object"
|
|
168
|
+
},
|
|
169
|
+
"pipe1_pipe_cap_neg": {
|
|
170
|
+
"normal_direction": "outward",
|
|
171
|
+
"surface_type": "embedded_object"
|
|
172
|
+
},
|
|
173
|
+
"pipe1_pipe_cap_pos": {
|
|
174
|
+
"normal_direction": "outward",
|
|
175
|
+
"surface_type": "embedded_object"
|
|
176
|
+
},
|
|
177
|
+
"pipe2_pipe_side": {
|
|
178
|
+
"normal_direction": "outward",
|
|
179
|
+
"surface_type": "embedded_object"
|
|
180
|
+
},
|
|
181
|
+
"pipe2_pipe_cap_neg": {
|
|
182
|
+
"normal_direction": "outward",
|
|
183
|
+
"surface_type": "embedded_object"
|
|
184
|
+
},
|
|
185
|
+
"pipe2_pipe_cap_pos": {
|
|
186
|
+
"normal_direction": "outward",
|
|
187
|
+
"surface_type": "embedded_object"
|
|
188
|
+
},
|
|
189
|
+
"pipe3_pipe_side": {
|
|
190
|
+
"normal_direction": "outward",
|
|
191
|
+
"surface_type": "embedded_object"
|
|
192
|
+
},
|
|
193
|
+
"pipe3_pipe_cap_neg": {
|
|
194
|
+
"normal_direction": "outward",
|
|
195
|
+
"surface_type": "embedded_object"
|
|
196
|
+
},
|
|
197
|
+
"pipe3_pipe_cap_pos": {
|
|
198
|
+
"normal_direction": "outward",
|
|
199
|
+
"surface_type": "embedded_object"
|
|
200
|
+
},
|
|
201
|
+
"sphere0": {
|
|
202
|
+
"normal_direction": "outward",
|
|
203
|
+
"surface_type": "embedded_object"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"embedded_objects": {
|
|
207
|
+
"pipes": 4,
|
|
208
|
+
"boxes": 0,
|
|
209
|
+
"spheres": 1
|
|
275
210
|
}
|
|
276
211
|
}
|
|
277
212
|
}
|
|
@@ -34,7 +34,7 @@ except Exception:
|
|
|
34
34
|
Poly3DCollection = None
|
|
35
35
|
|
|
36
36
|
# Groups kept for internal metrics but excluded from OBJ export and previews
|
|
37
|
-
_INTERNAL_GROUPS = frozenset({"trench_cap_for_volume"})
|
|
37
|
+
_INTERNAL_GROUPS = frozenset({"trench_cap_for_volume", "inner_column_lid"})
|
|
38
38
|
|
|
39
39
|
# ---------------- Geometry helpers ----------------
|
|
40
40
|
|
|
@@ -123,6 +123,9 @@ def _offset_closed_polyline(path: List[Tuple[float,float]], offset: float) -> Li
|
|
|
123
123
|
this returns a single continuous closed ring for paths where first ≈ last point.
|
|
124
124
|
"""
|
|
125
125
|
P = np.array(path, float)
|
|
126
|
+
# Remove duplicate closing point if present (first ≈ last)
|
|
127
|
+
if len(P) > 1 and np.linalg.norm(P[0] - P[-1]) < 0.01:
|
|
128
|
+
P = P[:-1]
|
|
126
129
|
n = len(P)
|
|
127
130
|
if n < 3:
|
|
128
131
|
raise ValueError("Closed polyline needs at least 3 points")
|
|
@@ -195,6 +198,74 @@ def _ear_clipping_triangulation(poly_xy: np.ndarray) -> np.ndarray:
|
|
|
195
198
|
tris.append([V[0],V[1],V[2]])
|
|
196
199
|
return np.array(tris,int)
|
|
197
200
|
|
|
201
|
+
|
|
202
|
+
def _extract_boundary_polygon(V: np.ndarray, F: np.ndarray) -> Optional[np.ndarray]:
|
|
203
|
+
"""Extract ordered boundary polygon from a triangulated mesh.
|
|
204
|
+
|
|
205
|
+
Finds boundary edges (edges appearing in only one face) and chains
|
|
206
|
+
them together to form an ordered polygon. This correctly handles
|
|
207
|
+
non-convex shapes like L-shaped or U-shaped boundaries.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
V : np.ndarray
|
|
212
|
+
Vertices (n, 3) or (n, 2)
|
|
213
|
+
F : np.ndarray
|
|
214
|
+
Faces (m, 3) - triangle indices
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
np.ndarray or None
|
|
219
|
+
Ordered boundary vertices XY coordinates (k, 2), or None if
|
|
220
|
+
no boundary edges found (closed mesh).
|
|
221
|
+
"""
|
|
222
|
+
if F.size == 0:
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
# Count edge occurrences - boundary edges appear exactly once
|
|
226
|
+
edge_count: Dict[Tuple[int, int], int] = {}
|
|
227
|
+
for face in F:
|
|
228
|
+
for i in range(3):
|
|
229
|
+
v0, v1 = int(face[i]), int(face[(i + 1) % 3])
|
|
230
|
+
edge = (min(v0, v1), max(v0, v1))
|
|
231
|
+
edge_count[edge] = edge_count.get(edge, 0) + 1
|
|
232
|
+
|
|
233
|
+
# Boundary edges appear exactly once
|
|
234
|
+
boundary_edges = [edge for edge, count in edge_count.items() if count == 1]
|
|
235
|
+
|
|
236
|
+
if not boundary_edges:
|
|
237
|
+
return None # No boundary (closed mesh)
|
|
238
|
+
|
|
239
|
+
# Build adjacency from boundary edges
|
|
240
|
+
adj: Dict[int, List[int]] = {}
|
|
241
|
+
for v0, v1 in boundary_edges:
|
|
242
|
+
adj.setdefault(v0, []).append(v1)
|
|
243
|
+
adj.setdefault(v1, []).append(v0)
|
|
244
|
+
|
|
245
|
+
# Chain the boundary edges starting from any vertex
|
|
246
|
+
start = boundary_edges[0][0]
|
|
247
|
+
polygon = [start]
|
|
248
|
+
prev = None
|
|
249
|
+
curr = start
|
|
250
|
+
|
|
251
|
+
max_iter = len(boundary_edges) + 1
|
|
252
|
+
for _ in range(max_iter):
|
|
253
|
+
neighbors = adj.get(curr, [])
|
|
254
|
+
# Pick the neighbor that isn't the previous vertex
|
|
255
|
+
next_candidates = [n for n in neighbors if n != prev]
|
|
256
|
+
if not next_candidates:
|
|
257
|
+
break
|
|
258
|
+
next_v = next_candidates[0]
|
|
259
|
+
if next_v == start:
|
|
260
|
+
break # Completed the loop
|
|
261
|
+
polygon.append(next_v)
|
|
262
|
+
prev = curr
|
|
263
|
+
curr = next_v
|
|
264
|
+
|
|
265
|
+
# Extract XY coordinates
|
|
266
|
+
return V[polygon, :2]
|
|
267
|
+
|
|
268
|
+
|
|
198
269
|
# ---------------- Mesh IO & metrics ----------------
|
|
199
270
|
|
|
200
271
|
def write_obj_with_groups(path: str, groups: Dict[str, Tuple[np.ndarray, np.ndarray]]):
|
|
@@ -405,6 +476,7 @@ class GroundSpec:
|
|
|
405
476
|
z0: float = 0.0
|
|
406
477
|
slope: Tuple[float,float] = (0.0, 0.0) # (dz/dx, dz/dy)
|
|
407
478
|
size_margin: float = 3.0
|
|
479
|
+
fill_interior: bool = False # For closed paths: fill the interior with ground surface
|
|
408
480
|
|
|
409
481
|
@dataclass
|
|
410
482
|
class SceneSpec:
|
|
@@ -447,19 +519,15 @@ class SurfaceMeshResult:
|
|
|
447
519
|
if "trench_cap_for_volume" in self.groups:
|
|
448
520
|
V_cap, F_cap = self.groups["trench_cap_for_volume"]
|
|
449
521
|
if V_cap.size > 0:
|
|
450
|
-
#
|
|
451
|
-
#
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
trench_opening_vertices = xy_coords[boundary_indices].tolist()
|
|
460
|
-
except ImportError:
|
|
461
|
-
# Fallback: just use unique xy coords (unordered)
|
|
462
|
-
trench_opening_vertices = xy_coords.tolist()
|
|
522
|
+
# Extract boundary polygon by finding boundary edges (edges in only one face)
|
|
523
|
+
# and chaining them together. This correctly handles non-convex shapes
|
|
524
|
+
# like L-shaped or U-shaped trenches.
|
|
525
|
+
boundary_xy = _extract_boundary_polygon(V_cap, F_cap)
|
|
526
|
+
if boundary_xy is not None:
|
|
527
|
+
trench_opening_vertices = boundary_xy.tolist()
|
|
528
|
+
else:
|
|
529
|
+
# Fallback: just use all vertices (unordered)
|
|
530
|
+
trench_opening_vertices = V_cap[:, :2].tolist()
|
|
463
531
|
|
|
464
532
|
# Determine geometry type
|
|
465
533
|
is_closed = _is_path_closed(self.spec.path_xy)
|
|
@@ -485,7 +553,7 @@ class SurfaceMeshResult:
|
|
|
485
553
|
|
|
486
554
|
return {
|
|
487
555
|
"sdf_metadata": {
|
|
488
|
-
"version": "
|
|
556
|
+
"version": "2.0",
|
|
489
557
|
"normal_convention": "into_void",
|
|
490
558
|
"geometry_type": geometry_type,
|
|
491
559
|
"trench_opening": {
|
|
@@ -735,6 +803,16 @@ def make_trench_from_path_sloped(path_xy: List[Tuple[float,float]], width_top: f
|
|
|
735
803
|
V_walls = np.array(walls_V, float)
|
|
736
804
|
F_walls = np.array(walls_F, int)
|
|
737
805
|
|
|
806
|
+
# Inner column lid: cap the top of the inner column at ground level
|
|
807
|
+
# Reverse inner_top to get CCW winding for upward-facing normals
|
|
808
|
+
inner_top_ccw = inner_top[::-1].copy()
|
|
809
|
+
z_inner_top_ccw = z_inner_top[::-1].copy()
|
|
810
|
+
lid_xy, lid_faces = _triangulate_polygon_fan(inner_top_ccw)
|
|
811
|
+
# Assign z-values: polygon vertices use z_inner_top_ccw, centroid uses average
|
|
812
|
+
z_lid = np.concatenate([z_inner_top_ccw, [np.mean(z_inner_top_ccw)]])
|
|
813
|
+
V_lid = np.column_stack([lid_xy, z_lid])
|
|
814
|
+
F_lid = _ensure_upward_normals(V_lid, lid_faces)
|
|
815
|
+
|
|
738
816
|
# For closed path, poly_top is the outer ring (used for ground plane hole)
|
|
739
817
|
poly_top = outer_top
|
|
740
818
|
poly_bot = outer_bot
|
|
@@ -796,6 +874,10 @@ def make_trench_from_path_sloped(path_xy: List[Tuple[float,float]], width_top: f
|
|
|
796
874
|
"trench_walls": (V_walls, F_walls)
|
|
797
875
|
}
|
|
798
876
|
|
|
877
|
+
# Add inner column lid for closed paths
|
|
878
|
+
if is_closed:
|
|
879
|
+
groups["inner_column_lid"] = (V_lid, F_lid)
|
|
880
|
+
|
|
799
881
|
return groups, poly_top, poly_bot, extra
|
|
800
882
|
|
|
801
883
|
def _ensure_upward_normals(V: np.ndarray, F: np.ndarray) -> np.ndarray:
|
|
@@ -830,6 +912,28 @@ def _ensure_upward_normals(V: np.ndarray, F: np.ndarray) -> np.ndarray:
|
|
|
830
912
|
return F_out
|
|
831
913
|
|
|
832
914
|
|
|
915
|
+
def _triangulate_polygon_fan(polygon: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
916
|
+
"""Triangulate a simple polygon using fan triangulation from centroid.
|
|
917
|
+
|
|
918
|
+
Works well for convex or nearly-convex polygons. Returns (vertices, faces)
|
|
919
|
+
where vertices includes the original polygon points plus the centroid.
|
|
920
|
+
"""
|
|
921
|
+
n = len(polygon)
|
|
922
|
+
centroid = polygon.mean(axis=0)
|
|
923
|
+
|
|
924
|
+
# Vertices: polygon points first, then centroid at the end
|
|
925
|
+
verts = np.vstack([polygon, centroid.reshape(1, -1)])
|
|
926
|
+
centroid_idx = n
|
|
927
|
+
|
|
928
|
+
# Fan triangles from centroid to each edge
|
|
929
|
+
tris = []
|
|
930
|
+
for i in range(n):
|
|
931
|
+
j = (i + 1) % n
|
|
932
|
+
tris.append([centroid_idx, i, j])
|
|
933
|
+
|
|
934
|
+
return verts, np.array(tris, dtype=int)
|
|
935
|
+
|
|
936
|
+
|
|
833
937
|
def _triangulate_annulus(outer: np.ndarray, inner: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
834
938
|
"""Triangulate the annular region between outer and inner polygons.
|
|
835
939
|
|
|
@@ -919,9 +1023,8 @@ def make_ground_surface_plane(path_xy: List[Tuple[float,float]], width_top: floa
|
|
|
919
1023
|
is_closed = _is_path_closed(path_xy)
|
|
920
1024
|
|
|
921
1025
|
if is_closed:
|
|
922
|
-
# For closed paths
|
|
923
|
-
#
|
|
924
|
-
# The center (inside the trench) is left completely open.
|
|
1026
|
+
# For closed paths, ground is an annulus from outer boundary to trench edge.
|
|
1027
|
+
# Optionally, if fill_interior is set, also fill the interior island.
|
|
925
1028
|
|
|
926
1029
|
# Trench outer boundary (edge of trench opening)
|
|
927
1030
|
trench_outer = np.array(_offset_closed_polyline(path_xy, half_top), float)
|
|
@@ -933,14 +1036,23 @@ def make_ground_surface_plane(path_xy: List[Tuple[float,float]], width_top: floa
|
|
|
933
1036
|
trench_outer = _ensure_ccw(trench_outer)
|
|
934
1037
|
ground_outer = _ensure_ccw(ground_outer)
|
|
935
1038
|
|
|
936
|
-
#
|
|
937
|
-
|
|
938
|
-
|
|
1039
|
+
# Outer ground annulus: from ground_outer to trench_outer
|
|
1040
|
+
outer_xy, outer_tris = _triangulate_annulus(ground_outer, trench_outer)
|
|
1041
|
+
Vg_outer = np.array([[x, y, gfun(x, y)] for (x, y) in outer_xy], float)
|
|
1042
|
+
outer_tris = _ensure_upward_normals(Vg_outer, outer_tris)
|
|
939
1043
|
|
|
940
|
-
|
|
941
|
-
tris = _ensure_upward_normals(Vg, tris)
|
|
1044
|
+
result = {"ground_surface": (Vg_outer, outer_tris)}
|
|
942
1045
|
|
|
943
|
-
|
|
1046
|
+
# Optionally fill the interior island (for loop trenches, not wells/pits)
|
|
1047
|
+
if getattr(ground, 'fill_interior', False):
|
|
1048
|
+
trench_inner = np.array(_offset_closed_polyline(path_xy, -half_top), float)
|
|
1049
|
+
trench_inner = _ensure_ccw(trench_inner)
|
|
1050
|
+
inner_xy, inner_tris = _triangulate_polygon_fan(trench_inner)
|
|
1051
|
+
Vg_inner = np.array([[x, y, gfun(x, y)] for (x, y) in inner_xy], float)
|
|
1052
|
+
inner_tris = _ensure_upward_normals(Vg_inner, inner_tris)
|
|
1053
|
+
result["ground_island"] = (Vg_inner, inner_tris)
|
|
1054
|
+
|
|
1055
|
+
return result
|
|
944
1056
|
else:
|
|
945
1057
|
# Open paths: ground forms annulus with extensions past trench endpoints.
|
|
946
1058
|
# The outer ring (ground boundary) is extended, but the inner ring (trench
|