capytaine 2.2__cp312-cp312-macosx_11_0_arm64.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.
Files changed (76) hide show
  1. capytaine/.dylibs/libgcc_s.1.1.dylib +0 -0
  2. capytaine/.dylibs/libgfortran.5.dylib +0 -0
  3. capytaine/.dylibs/libquadmath.0.dylib +0 -0
  4. capytaine/__about__.py +16 -0
  5. capytaine/__init__.py +35 -0
  6. capytaine/bem/__init__.py +0 -0
  7. capytaine/bem/airy_waves.py +106 -0
  8. capytaine/bem/engines.py +441 -0
  9. capytaine/bem/problems_and_results.py +545 -0
  10. capytaine/bem/solver.py +497 -0
  11. capytaine/bodies/__init__.py +4 -0
  12. capytaine/bodies/bodies.py +1185 -0
  13. capytaine/bodies/dofs.py +19 -0
  14. capytaine/bodies/predefined/__init__.py +6 -0
  15. capytaine/bodies/predefined/cylinders.py +151 -0
  16. capytaine/bodies/predefined/rectangles.py +109 -0
  17. capytaine/bodies/predefined/spheres.py +70 -0
  18. capytaine/green_functions/__init__.py +2 -0
  19. capytaine/green_functions/abstract_green_function.py +12 -0
  20. capytaine/green_functions/delhommeau.py +432 -0
  21. capytaine/green_functions/libs/Delhommeau_float32.cpython-312-darwin.so +0 -0
  22. capytaine/green_functions/libs/Delhommeau_float64.cpython-312-darwin.so +0 -0
  23. capytaine/green_functions/libs/__init__.py +0 -0
  24. capytaine/io/__init__.py +0 -0
  25. capytaine/io/bemio.py +141 -0
  26. capytaine/io/legacy.py +328 -0
  27. capytaine/io/mesh_loaders.py +1085 -0
  28. capytaine/io/mesh_writers.py +692 -0
  29. capytaine/io/meshio.py +38 -0
  30. capytaine/io/xarray.py +516 -0
  31. capytaine/matrices/__init__.py +16 -0
  32. capytaine/matrices/block.py +590 -0
  33. capytaine/matrices/block_toeplitz.py +325 -0
  34. capytaine/matrices/builders.py +89 -0
  35. capytaine/matrices/linear_solvers.py +232 -0
  36. capytaine/matrices/low_rank.py +393 -0
  37. capytaine/meshes/__init__.py +6 -0
  38. capytaine/meshes/clipper.py +464 -0
  39. capytaine/meshes/collections.py +324 -0
  40. capytaine/meshes/geometry.py +409 -0
  41. capytaine/meshes/meshes.py +868 -0
  42. capytaine/meshes/predefined/__init__.py +6 -0
  43. capytaine/meshes/predefined/cylinders.py +314 -0
  44. capytaine/meshes/predefined/rectangles.py +261 -0
  45. capytaine/meshes/predefined/spheres.py +62 -0
  46. capytaine/meshes/properties.py +242 -0
  47. capytaine/meshes/quadratures.py +80 -0
  48. capytaine/meshes/quality.py +448 -0
  49. capytaine/meshes/surface_integrals.py +63 -0
  50. capytaine/meshes/symmetric.py +383 -0
  51. capytaine/post_pro/__init__.py +6 -0
  52. capytaine/post_pro/free_surfaces.py +88 -0
  53. capytaine/post_pro/impedance.py +92 -0
  54. capytaine/post_pro/kochin.py +54 -0
  55. capytaine/post_pro/rao.py +60 -0
  56. capytaine/tools/__init__.py +0 -0
  57. capytaine/tools/cache_on_disk.py +26 -0
  58. capytaine/tools/deprecation_handling.py +18 -0
  59. capytaine/tools/lists_of_points.py +52 -0
  60. capytaine/tools/lru_cache.py +49 -0
  61. capytaine/tools/optional_imports.py +27 -0
  62. capytaine/tools/prony_decomposition.py +94 -0
  63. capytaine/tools/symbolic_multiplication.py +107 -0
  64. capytaine/ui/__init__.py +0 -0
  65. capytaine/ui/cli.py +28 -0
  66. capytaine/ui/rich.py +5 -0
  67. capytaine/ui/vtk/__init__.py +3 -0
  68. capytaine/ui/vtk/animation.py +329 -0
  69. capytaine/ui/vtk/body_viewer.py +28 -0
  70. capytaine/ui/vtk/helpers.py +82 -0
  71. capytaine/ui/vtk/mesh_viewer.py +461 -0
  72. capytaine-2.2.dist-info/LICENSE +674 -0
  73. capytaine-2.2.dist-info/METADATA +751 -0
  74. capytaine-2.2.dist-info/RECORD +76 -0
  75. capytaine-2.2.dist-info/WHEEL +4 -0
  76. capytaine-2.2.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,464 @@
1
+ """This module implements a tools to clip meshes against a plane.
2
+ Based on meshmagick <https://github.com/LHEEA/meshmagick> by François Rongère.
3
+ """
4
+ # Copyright (C) 2017-2019 Matthieu Ancellin, based on the work of François Rongère
5
+ # See LICENSE file at <https://github.com/mancellin/capytaine>
6
+
7
+ import logging
8
+
9
+ import numpy as np
10
+
11
+ from capytaine.meshes.geometry import Plane
12
+ from capytaine.meshes.meshes import Mesh
13
+
14
+ LOG = logging.getLogger(__name__)
15
+
16
+
17
+ def clip(source_mesh: Mesh, plane: Plane, vicinity_tol=1e-12, name=None):
18
+ """Return a new mesh containing the source mesh clipped by the plane.
19
+
20
+ Parameters
21
+ ----------
22
+ source_mesh : Mesh
23
+ The mesh to be clipped.
24
+ plane : Plane, optional
25
+ The clipping plane.
26
+ vicinity_tol : float, optional
27
+ The absolute tolerance to consider en vertex is on the plane. Default is 1e-12.
28
+ name: string, optional
29
+ A name for the new clipped mesh.
30
+ """
31
+ vertices_data = _vertices_positions_wrt_plane(source_mesh, plane, vicinity_tol)
32
+
33
+ nb_vertices_above_or_on_plane = np.count_nonzero(
34
+ vertices_data['vertices_above_mask'] | vertices_data['vertices_on_mask']
35
+ )
36
+ nb_vertices_below_or_on_plane = np.count_nonzero(
37
+ vertices_data['vertices_below_mask'] | vertices_data['vertices_on_mask']
38
+ )
39
+
40
+ if nb_vertices_above_or_on_plane == source_mesh.nb_vertices:
41
+ LOG.warning(f"Clipping {source_mesh.name} by {plane}: all vertices are removed.")
42
+ clipped_mesh = Mesh(None, None)
43
+ clipped_mesh._clipping_data = dict(faces_ids=[])
44
+
45
+ elif nb_vertices_below_or_on_plane == source_mesh.nb_vertices:
46
+ LOG.info(f"Clipping {source_mesh.name} by {plane}: no action.")
47
+ clipped_mesh = source_mesh.copy()
48
+ clipped_mesh._clipping_data = dict(faces_ids=list(range(source_mesh.nb_faces)))
49
+
50
+ else:
51
+ upper_mesh, crown_mesh, lower_mesh = _partition_mesh(vertices_data, source_mesh)
52
+
53
+ if crown_mesh.nb_faces > 0:
54
+ clipped_crown_mesh = _clip_crown(crown_mesh, plane)
55
+ clipped_mesh = lower_mesh + clipped_crown_mesh
56
+ clipped_mesh._clipping_data = {
57
+ 'faces_ids': np.concatenate((lower_mesh._clipping_data['faces_ids'],
58
+ clipped_crown_mesh._clipping_data['faces_ids']))
59
+ }
60
+ else:
61
+ clipped_mesh = lower_mesh
62
+
63
+ if name is None:
64
+ clipped_mesh.name = f'{source_mesh.name}_clipped'
65
+ clipped_mesh.remove_unused_vertices()
66
+
67
+ return clipped_mesh
68
+
69
+
70
+ def _vertices_positions_wrt_plane(source_mesh, plane, vicinity_tol):
71
+ """Classifies vertices with respect to the clipping plane."""
72
+ vertices_distances = plane.distance_to_point(source_mesh.vertices)
73
+ vertices_data = {'vertices_distances': vertices_distances,
74
+ 'vertices_above_mask': vertices_distances > vicinity_tol,
75
+ 'vertices_on_mask': np.abs(vertices_distances) < vicinity_tol,
76
+ 'vertices_below_mask': vertices_distances < -vicinity_tol
77
+ }
78
+ return vertices_data
79
+
80
+
81
+ def _partition_mesh(vertices_data, source_mesh):
82
+ """Partitions the mesh in 3 with respect to the plane:
83
+ * upper_mesh: part entirely above the clipping plane
84
+ * crown_mesh: part intersecting the clipping plane
85
+ * lower_mesh: part entirely under the clipping plane
86
+ """
87
+ nb_vertices_above_per_face = vertices_data['vertices_above_mask'][source_mesh.faces].sum(axis=1)
88
+ nb_vertices_below_per_face = vertices_data['vertices_below_mask'][source_mesh.faces].sum(axis=1)
89
+
90
+ # Simple criteria ensuring that _faces are totally above or below the plane (4 _vertices at the same side)
91
+ # Works for both triangles and quadrangles
92
+ above_faces_mask = nb_vertices_above_per_face == 4
93
+ below_faces_mask = nb_vertices_below_per_face == 4
94
+ crown_faces_mask = np.logical_not(np.logical_or(above_faces_mask, below_faces_mask))
95
+
96
+ faces_ids = {
97
+ 'upper_mesh': np.where(above_faces_mask)[0],
98
+ 'crown_mesh': np.where(crown_faces_mask)[0],
99
+ 'lower_mesh': np.where(below_faces_mask)[0]
100
+ }
101
+
102
+ partition = []
103
+ for name in ['upper_mesh', 'crown_mesh', 'lower_mesh']:
104
+ new_mesh, vertices_ids = source_mesh.extract_faces(
105
+ id_faces_to_extract=faces_ids[name], return_index=True, name=name)
106
+ new_mesh._clipping_data = {
107
+ 'faces_ids': faces_ids[name],
108
+ 'vertices_ids': vertices_ids,
109
+ 'vertices_distances': vertices_data['vertices_distances'][vertices_ids],
110
+ 'above_vertices_mask': vertices_data['vertices_above_mask'][vertices_ids],
111
+ 'on_vertices_mask': vertices_data['vertices_on_mask'][vertices_ids],
112
+ 'below_vertices_mask': vertices_data['vertices_below_mask'][vertices_ids],
113
+ }
114
+ partition.append(new_mesh)
115
+
116
+ return partition
117
+
118
+
119
+ def _clip_crown(crown_mesh, plane):
120
+ """Performs the clipping operation of the crown_mesh and determines the obtained boundaries.
121
+ This is the heart method of the class.
122
+ """
123
+ vertices = crown_mesh.vertices
124
+
125
+ above_vertices_mask = crown_mesh._clipping_data['above_vertices_mask']
126
+ below_vertices_mask = crown_mesh._clipping_data['below_vertices_mask']
127
+
128
+ on_vertices_mask = crown_mesh._clipping_data['on_vertices_mask']
129
+ # TODO: Vertices pre-projection to be done here
130
+
131
+ vertices_distances = crown_mesh._clipping_data['vertices_distances']
132
+
133
+ # Init
134
+ clipped_crown_mesh_faces = list()
135
+ clipped_crown_mesh_relative_faces_ids = list()
136
+
137
+ direct_boundary_edges = dict()
138
+ inv_boundary_edges = dict()
139
+ intersections_vertices = list()
140
+
141
+ index_new_vertices = crown_mesh.nb_vertices
142
+
143
+ for face_id in range(crown_mesh.nb_faces):
144
+
145
+ face = crown_mesh.get_face(face_id)
146
+
147
+ # # Determining the type of face clipping
148
+ v_above_face = np.where(above_vertices_mask[face])[0]
149
+ v_on_face = np.where(on_vertices_mask[face])[0]
150
+ v_below_face = np.where(below_vertices_mask[face])[0]
151
+
152
+ nb_above = len(v_above_face)
153
+ nb_on = len(v_on_face)
154
+ nb_below = len(v_below_face)
155
+
156
+ face_type = str(nb_above) + str(nb_on) + str(nb_below)
157
+
158
+ if face_type == '202':
159
+ # 0*-----*3
160
+ # | |
161
+ # ----o-----o----
162
+ # | |
163
+ # 1*-----*2
164
+ if v_above_face[1] == v_above_face[0] + 1:
165
+ face = np.roll(face, -v_above_face[1])
166
+ p0, p1, p2, p3 = vertices[face]
167
+ ileft = plane.get_edge_intersection(p0, p1)
168
+ iright = plane.get_edge_intersection(p2, p3)
169
+ intersections_vertices += [ileft, iright]
170
+ boundary_edge = [index_new_vertices, index_new_vertices + 1]
171
+ clipped_crown_mesh_faces.append([index_new_vertices, face[1], face[2], index_new_vertices + 1])
172
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
173
+ index_new_vertices += 2
174
+
175
+ elif face_type == '301':
176
+ # *2
177
+ # / \
178
+ # / \
179
+ # / \
180
+ # 3* *1
181
+ # \ /
182
+ # ---o---o---
183
+ # \ /
184
+ # *0
185
+ face = np.roll(face, -v_below_face[0])
186
+ p0, p1, p3 = vertices[face[[0, 1, 3]]]
187
+ ileft = plane.get_edge_intersection(p0, p3)
188
+ iright = plane.get_edge_intersection(p0, p1)
189
+ intersections_vertices += [ileft, iright]
190
+ boundary_edge = [index_new_vertices, index_new_vertices + 1]
191
+ clipped_crown_mesh_faces.append([index_new_vertices, face[0], index_new_vertices + 1, index_new_vertices])
192
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
193
+ index_new_vertices += 2
194
+
195
+ elif face_type == '103':
196
+ # *0
197
+ # / \
198
+ # ---o---o---
199
+ # / \
200
+ # 1* - - - *3
201
+ # \ /
202
+ # \ /
203
+ # \ /
204
+ # *2
205
+ face = np.roll(face, -v_above_face[0])
206
+ p0, p1, p3 = vertices[face[[0, 1, 3]]]
207
+ ileft = plane.get_edge_intersection(p0, p1)
208
+ iright = plane.get_edge_intersection(p0, p3)
209
+ intersections_vertices += [ileft, iright]
210
+ boundary_edge = [index_new_vertices, index_new_vertices + 1]
211
+ clipped_crown_mesh_faces.append([index_new_vertices, face[1], face[3], index_new_vertices + 1])
212
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
213
+ clipped_crown_mesh_faces.append([face[1], face[2], face[3], face[1]])
214
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
215
+ index_new_vertices += 2
216
+
217
+ elif face_type == '102':
218
+ # *O
219
+ # / \
220
+ # ---o---o---
221
+ # / \
222
+ # 1*-------*2
223
+ face = np.roll(face, -v_above_face[0])
224
+ p0, p1, p2 = vertices[face]
225
+ ileft = plane.get_edge_intersection(p0, p1)
226
+ iright = plane.get_edge_intersection(p0, p2)
227
+ intersections_vertices += [ileft, iright]
228
+ boundary_edge = [index_new_vertices, index_new_vertices + 1]
229
+ clipped_crown_mesh_faces.append([index_new_vertices, face[1], face[2], index_new_vertices + 1])
230
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
231
+ index_new_vertices += 2
232
+
233
+ elif face_type == '201': # done
234
+ # 2*-------*1
235
+ # \ /
236
+ # ---o---o---
237
+ # \ /
238
+ # *0
239
+ face = np.roll(face, -v_below_face[0])
240
+ p0, p1, p2 = vertices[face]
241
+ ileft = plane.get_edge_intersection(p0, p2)
242
+ iright = plane.get_edge_intersection(p0, p1)
243
+ intersections_vertices += [ileft, iright]
244
+ boundary_edge = [index_new_vertices, index_new_vertices + 1]
245
+ clipped_crown_mesh_faces.append([index_new_vertices, face[0], index_new_vertices + 1, index_new_vertices])
246
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
247
+ index_new_vertices += 2
248
+
249
+ elif face_type == '211':
250
+ # *3 *1
251
+ # / \ / \
252
+ # / *2 or 2* \
253
+ # 0/ / \ \0
254
+ # ---*---o--- ---o---*---
255
+ # \ / \ /
256
+ # *1 *3
257
+ #
258
+
259
+ face = np.roll(face, -v_on_face[0])
260
+ if vertices_distances[face[1]] < 0.:
261
+ p1, p2 = vertices[face[[1, 2]]]
262
+ iright = plane.get_edge_intersection(p1, p2)
263
+ intersections_vertices.append(iright)
264
+ boundary_edge = [face[0], index_new_vertices]
265
+ clipped_crown_mesh_faces.append([face[0], face[1], index_new_vertices, face[0]])
266
+ else:
267
+ p2, p3 = vertices[face[[2, 3]]]
268
+ ileft = plane.get_edge_intersection(p2, p3)
269
+ intersections_vertices.append(ileft)
270
+ boundary_edge = [index_new_vertices, face[0]]
271
+ clipped_crown_mesh_faces.append([index_new_vertices, face[3], face[0], index_new_vertices])
272
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
273
+ index_new_vertices += 1
274
+
275
+ elif face_type == '112':
276
+ # *3 *1
277
+ # / \ / \
278
+ # ---*---o--- or ---o---*---
279
+ # 0\ \ / /0
280
+ # \ *2 2* /
281
+ # \ / \ /
282
+ # *1 *3
283
+ face = np.roll(face, -v_on_face[0])
284
+ if vertices_distances[face[1]] < 0.:
285
+ p2, p3 = vertices[face[[2, 3]]]
286
+ iright = plane.get_edge_intersection(p2, p3)
287
+ intersections_vertices.append(iright)
288
+ boundary_edge = [face[0], index_new_vertices]
289
+ clipped_crown_mesh_faces.append([face[0], face[1], face[2], index_new_vertices])
290
+ else:
291
+ p1, p2 = vertices[face[[1, 2]]]
292
+ ileft = plane.get_edge_intersection(p1, p2)
293
+ intersections_vertices.append(ileft)
294
+ boundary_edge = [index_new_vertices, face[0]]
295
+ clipped_crown_mesh_faces.append([index_new_vertices, face[2], face[3], face[0]])
296
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
297
+ index_new_vertices += 1
298
+
299
+ elif face_type == '013':
300
+ # -----*-----
301
+ # / \
302
+ # / \
303
+ # * *
304
+ # \ /
305
+ # \ /
306
+ # *
307
+ boundary_edge = None
308
+ clipped_crown_mesh_faces.append(list(face))
309
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
310
+
311
+ elif face_type == '210' or face_type == '310':
312
+ # *-------* *
313
+ # \ 210 / / \ 310
314
+ # \ / * *
315
+ # \ / \ /
316
+ # ----*---- ----*----
317
+ boundary_edge = None
318
+
319
+ elif face_type == '111':
320
+ # *2 *1
321
+ # /| |\
322
+ # / | | \
323
+ # ---*--o--- or ---o--*---
324
+ # 0\ | | /0
325
+ # \| |/
326
+ # *1 *2
327
+ face = np.roll(face, -v_on_face[0])
328
+ p1, p2 = vertices[face[[1, 2]]]
329
+ if vertices_distances[face[1]] < 0.:
330
+ iright = plane.get_edge_intersection(p1, p2)
331
+ intersections_vertices.append(iright)
332
+ boundary_edge = [face[0], index_new_vertices]
333
+ clipped_crown_mesh_faces.append([face[0], face[1], index_new_vertices, face[0]])
334
+ else:
335
+ ileft = plane.get_edge_intersection(p1, p2)
336
+ intersections_vertices.append(ileft)
337
+ boundary_edge = [index_new_vertices, face[0]]
338
+ clipped_crown_mesh_faces.append([index_new_vertices, face[2], face[0], index_new_vertices])
339
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
340
+ index_new_vertices += 1
341
+
342
+ elif face_type == '120':
343
+ # *O
344
+ # / \
345
+ # / \
346
+ # 1/ \2
347
+ # ----*-------*----
348
+ # face = np.roll(face, -v_above_face[0])
349
+ # boundary_edge = [face[1], face[2]]
350
+ # FIXME: quick fix here : robust ?
351
+ boundary_edge = None
352
+
353
+ elif face_type == '021':
354
+ # ----*-------*----
355
+ # 2\ /1
356
+ # \ /
357
+ # \ /
358
+ # *0
359
+ face = np.roll(face, -v_below_face[0])
360
+ boundary_edge = [face[2], face[1]]
361
+ face = list(face)
362
+ face.append(face[0])
363
+ clipped_crown_mesh_faces.append(face)
364
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
365
+
366
+ elif face_type == '022':
367
+ # ----*-----*----
368
+ # 0| |3
369
+ # | |
370
+ # 1*-----*2
371
+ if v_on_face[1] == v_on_face[0] + 1:
372
+ face = np.roll(face, -v_on_face[1])
373
+ boundary_edge = [face[0], face[3]]
374
+ clipped_crown_mesh_faces.append(list(face))
375
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
376
+
377
+ elif face_type == '012':
378
+ # ------*------
379
+ # / \
380
+ # / \
381
+ # / \
382
+ # *-------*
383
+ boundary_edge = None
384
+ face = list(face)
385
+ face.append(face[0])
386
+ clipped_crown_mesh_faces.append(face)
387
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
388
+
389
+ elif face_type == '220':
390
+ # 0*-----*3
391
+ # | |
392
+ # 1| |2
393
+ # ----*-----*----
394
+
395
+ # if v_above_face[1] == v_above_face[0] + 1:
396
+ # face = np.roll(face, -v_above_face[1])
397
+ # boundary_edge = [face[1], face[2]]
398
+ # FIXME: quick fix here : robust ?
399
+ boundary_edge = None
400
+
401
+ elif face_type == '121':
402
+ # *0
403
+ # / \
404
+ # / \
405
+ # ---*-----*---
406
+ # 1\ /3
407
+ # \ /
408
+ # *2
409
+ face = np.roll(face, -v_above_face[0])
410
+ boundary_edge = [face[1], face[3]]
411
+ clipped_crown_mesh_faces.append([face[1], face[2], face[3], face[1]])
412
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
413
+
414
+ elif face_type == '300' or face_type == '400':
415
+ # * *-----*
416
+ # / \ | |
417
+ # /300\ or | 400 |
418
+ # *-----* *-----*
419
+ # ____________ ______________
420
+ boundary_edge = None
421
+
422
+ elif face_type == '003':
423
+ # -----------
424
+ # *
425
+ # / \
426
+ # / \
427
+ # *-----*
428
+ boundary_edge = None
429
+ face = list(face)
430
+ face.append(face[0])
431
+ clipped_crown_mesh_faces.append(face)
432
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
433
+
434
+ elif face_type == '004':
435
+ # ---------------
436
+ # *-----*
437
+ # | |
438
+ # | |
439
+ # *-----*
440
+ boundary_edge = None
441
+ clipped_crown_mesh_faces.append(list(face))
442
+ clipped_crown_mesh_relative_faces_ids.append(face_id)
443
+
444
+ elif face_type == '030' or face_type == '040':
445
+ # Face is totally on the plane --> rare case...
446
+ boundary_edge = None
447
+
448
+ else:
449
+ raise Exception("Face %u clipping case %s not known." % (face_id, face_type))
450
+
451
+ # Building boundary connectivity
452
+ if boundary_edge is not None:
453
+ direct_boundary_edges[boundary_edge[0]] = boundary_edge[1]
454
+ inv_boundary_edges[boundary_edge[1]] = boundary_edge[0]
455
+
456
+ if len(intersections_vertices) > 0:
457
+ vertices = np.concatenate((vertices, intersections_vertices))
458
+
459
+ clipped_crown_mesh = Mesh(vertices, clipped_crown_mesh_faces)
460
+ clipped_crown_mesh._clipping_data = {
461
+ 'faces_ids': crown_mesh._clipping_data['faces_ids'][clipped_crown_mesh_relative_faces_ids]
462
+ }
463
+
464
+ return clipped_crown_mesh