topologicpy 0.5.9__py3-none-any.whl → 6.0.0__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.
Files changed (94) hide show
  1. topologicpy/Aperture.py +72 -72
  2. topologicpy/Cell.py +2169 -2169
  3. topologicpy/CellComplex.py +1137 -1137
  4. topologicpy/Cluster.py +1288 -1280
  5. topologicpy/Color.py +423 -423
  6. topologicpy/Context.py +79 -79
  7. topologicpy/DGL.py +3213 -3240
  8. topologicpy/Dictionary.py +698 -698
  9. topologicpy/Edge.py +1187 -1187
  10. topologicpy/EnergyModel.py +1180 -1152
  11. topologicpy/Face.py +2141 -2141
  12. topologicpy/Graph.py +7768 -7768
  13. topologicpy/Grid.py +353 -353
  14. topologicpy/Helper.py +507 -507
  15. topologicpy/Honeybee.py +461 -461
  16. topologicpy/Matrix.py +271 -271
  17. topologicpy/Neo4j.py +521 -521
  18. topologicpy/Plotly.py +2 -2
  19. topologicpy/Polyskel.py +541 -541
  20. topologicpy/Shell.py +1768 -1768
  21. topologicpy/Speckle.py +508 -508
  22. topologicpy/Topology.py +7060 -7002
  23. topologicpy/Vector.py +905 -905
  24. topologicpy/Vertex.py +1585 -1585
  25. topologicpy/Wire.py +3050 -3050
  26. topologicpy/__init__.py +22 -38
  27. topologicpy/version.py +1 -0
  28. {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/LICENSE +661 -704
  29. topologicpy-6.0.0.dist-info/METADATA +751 -0
  30. topologicpy-6.0.0.dist-info/RECORD +32 -0
  31. topologicpy/bin/linux/topologic/__init__.py +0 -2
  32. topologicpy/bin/linux/topologic/libTKBO-6bdf205d.so.7.7.0 +0 -0
  33. topologicpy/bin/linux/topologic/libTKBRep-2960a069.so.7.7.0 +0 -0
  34. topologicpy/bin/linux/topologic/libTKBool-c44b74bd.so.7.7.0 +0 -0
  35. topologicpy/bin/linux/topologic/libTKFillet-9a670ba0.so.7.7.0 +0 -0
  36. topologicpy/bin/linux/topologic/libTKG2d-8f31849e.so.7.7.0 +0 -0
  37. topologicpy/bin/linux/topologic/libTKG3d-4c6bce57.so.7.7.0 +0 -0
  38. topologicpy/bin/linux/topologic/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
  39. topologicpy/bin/linux/topologic/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
  40. topologicpy/bin/linux/topologic/libTKMath-72572fa8.so.7.7.0 +0 -0
  41. topologicpy/bin/linux/topologic/libTKMesh-2a060427.so.7.7.0 +0 -0
  42. topologicpy/bin/linux/topologic/libTKOffset-6cab68ff.so.7.7.0 +0 -0
  43. topologicpy/bin/linux/topologic/libTKPrim-eb1262b3.so.7.7.0 +0 -0
  44. topologicpy/bin/linux/topologic/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
  45. topologicpy/bin/linux/topologic/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
  46. topologicpy/bin/linux/topologic/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
  47. topologicpy/bin/linux/topologic/libgcc_s-32c1665e.so.1 +0 -0
  48. topologicpy/bin/linux/topologic/libstdc++-672d7b41.so.6.0.30 +0 -0
  49. topologicpy/bin/linux/topologic/topologic.cpython-310-x86_64-linux-gnu.so +0 -0
  50. topologicpy/bin/linux/topologic/topologic.cpython-311-x86_64-linux-gnu.so +0 -0
  51. topologicpy/bin/linux/topologic/topologic.cpython-38-x86_64-linux-gnu.so +0 -0
  52. topologicpy/bin/linux/topologic/topologic.cpython-39-x86_64-linux-gnu.so +0 -0
  53. topologicpy/bin/linux/topologic.libs/libTKBO-6bdf205d.so.7.7.0 +0 -0
  54. topologicpy/bin/linux/topologic.libs/libTKBRep-2960a069.so.7.7.0 +0 -0
  55. topologicpy/bin/linux/topologic.libs/libTKBool-c44b74bd.so.7.7.0 +0 -0
  56. topologicpy/bin/linux/topologic.libs/libTKFillet-9a670ba0.so.7.7.0 +0 -0
  57. topologicpy/bin/linux/topologic.libs/libTKG2d-8f31849e.so.7.7.0 +0 -0
  58. topologicpy/bin/linux/topologic.libs/libTKG3d-4c6bce57.so.7.7.0 +0 -0
  59. topologicpy/bin/linux/topologic.libs/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
  60. topologicpy/bin/linux/topologic.libs/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
  61. topologicpy/bin/linux/topologic.libs/libTKMath-72572fa8.so.7.7.0 +0 -0
  62. topologicpy/bin/linux/topologic.libs/libTKMesh-2a060427.so.7.7.0 +0 -0
  63. topologicpy/bin/linux/topologic.libs/libTKOffset-6cab68ff.so.7.7.0 +0 -0
  64. topologicpy/bin/linux/topologic.libs/libTKPrim-eb1262b3.so.7.7.0 +0 -0
  65. topologicpy/bin/linux/topologic.libs/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
  66. topologicpy/bin/linux/topologic.libs/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
  67. topologicpy/bin/linux/topologic.libs/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
  68. topologicpy/bin/linux/topologic.libs/libgcc_s-32c1665e.so.1 +0 -0
  69. topologicpy/bin/linux/topologic.libs/libstdc++-672d7b41.so.6.0.30 +0 -0
  70. topologicpy/bin/macos/topologic/__init__.py +0 -2
  71. topologicpy/bin/windows/topologic/TKBO-f6b191de.dll +0 -0
  72. topologicpy/bin/windows/topologic/TKBRep-e56a600e.dll +0 -0
  73. topologicpy/bin/windows/topologic/TKBool-7b8d47ae.dll +0 -0
  74. topologicpy/bin/windows/topologic/TKFillet-0ddbf0a8.dll +0 -0
  75. topologicpy/bin/windows/topologic/TKG2d-2e2dee3d.dll +0 -0
  76. topologicpy/bin/windows/topologic/TKG3d-6674513d.dll +0 -0
  77. topologicpy/bin/windows/topologic/TKGeomAlgo-d240e370.dll +0 -0
  78. topologicpy/bin/windows/topologic/TKGeomBase-df87aba5.dll +0 -0
  79. topologicpy/bin/windows/topologic/TKMath-45bd625a.dll +0 -0
  80. topologicpy/bin/windows/topologic/TKMesh-d6e826b1.dll +0 -0
  81. topologicpy/bin/windows/topologic/TKOffset-79b9cc94.dll +0 -0
  82. topologicpy/bin/windows/topologic/TKPrim-aa430a86.dll +0 -0
  83. topologicpy/bin/windows/topologic/TKShHealing-bb48be89.dll +0 -0
  84. topologicpy/bin/windows/topologic/TKTopAlgo-7d0d1e22.dll +0 -0
  85. topologicpy/bin/windows/topologic/TKernel-08c8cfbb.dll +0 -0
  86. topologicpy/bin/windows/topologic/__init__.py +0 -2
  87. topologicpy/bin/windows/topologic/topologic.cp310-win_amd64.pyd +0 -0
  88. topologicpy/bin/windows/topologic/topologic.cp311-win_amd64.pyd +0 -0
  89. topologicpy/bin/windows/topologic/topologic.cp38-win_amd64.pyd +0 -0
  90. topologicpy/bin/windows/topologic/topologic.cp39-win_amd64.pyd +0 -0
  91. topologicpy-0.5.9.dist-info/METADATA +0 -86
  92. topologicpy-0.5.9.dist-info/RECORD +0 -91
  93. {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/WHEEL +0 -0
  94. {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/top_level.txt +0 -0
@@ -1,1137 +1,1137 @@
1
- # Copyright (C) 2024
2
- # Wassim Jabi <wassim.jabi@gmail.com>
3
- #
4
- # This program is free software: you can redistribute it and/or modify it under
5
- # the terms of the GNU Affero General Public License as published by the Free Software
6
- # Foundation, either version 3 of the License, or (at your option) any later
7
- # version.
8
- #
9
- # This program is distributed in the hope that it will be useful, but WITHOUT
10
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
- # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
12
- # details.
13
- #
14
- # You should have received a copy of the GNU Affero General Public License along with
15
- # this program. If not, see <https://www.gnu.org/licenses/>.
16
-
17
- import topologic
18
- import math
19
- import os
20
- from topologicpy.Topology import Topology
21
- import warnings
22
-
23
- try:
24
- import numpy as np
25
- except:
26
- print("CellComplex - Installing required numpy library.")
27
- try:
28
- os.system("pip install numpy")
29
- except:
30
- os.system("pip install numpy --user")
31
- try:
32
- import numpy as np
33
- print("CellComplex - numpy library installed correctly.")
34
- except:
35
- warnings.warn("CellComplex - Error: Could not import numpy.")
36
- try:
37
- from scipy.spatial import Delaunay
38
- from scipy.spatial import Voronoi
39
- except:
40
- print("CellComplex - Install required scipy library.")
41
- try:
42
- os.system("pip install scipy")
43
- except:
44
- os.system("pip install scipy --user")
45
- try:
46
- from scipy.spatial import Delaunay
47
- from scipy.spatial import Voronoi
48
- except:
49
- warnings.warn("CellComplex - Error: Could not import scipy.")
50
-
51
- class CellComplex(Topology):
52
- @staticmethod
53
- def Box(origin: topologic.Vertex = None,
54
- width: float = 1.0, length: float = 1.0, height: float = 1.0,
55
- uSides: int = 2, vSides: int = 2, wSides: int = 2,
56
- direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
57
- """
58
- Creates a box with internal cells.
59
-
60
- Parameters
61
- ----------
62
- origin : topologic.Vertex , optional
63
- The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
64
- width : float , optional
65
- The width of the box. The default is 1.
66
- length : float , optional
67
- The length of the box. The default is 1.
68
- height : float , optional
69
- The height of the box.
70
- uSides : int , optional
71
- The number of sides along the width. The default is 1.
72
- vSides : int, optional
73
- The number of sides along the length. The default is 1.
74
- wSides : int , optional
75
- The number of sides along the height. The default is 1.
76
- direction : list , optional
77
- The vector representing the up direction of the box. The default is [0, 0, 1].
78
- placement : str , optional
79
- The description of the placement of the origin of the box. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
80
- tolerance : float , optional
81
- The desired tolerance. The default is 0.0001.
82
-
83
- Returns
84
- -------
85
- topologic.CellComplex
86
- The created box.
87
-
88
- """
89
- return CellComplex.Prism(origin=origin,
90
- width=width, length=length, height=height,
91
- uSides=uSides, vSides=vSides, wSides=wSides,
92
- direction=direction, placement=placement, tolerance=tolerance)
93
-
94
- @staticmethod
95
- def ByCells(cells: list, tolerance: float = 0.0001, silent: bool = False) -> topologic.CellComplex:
96
- """
97
- Creates a cellcomplex by merging the input cells.
98
-
99
- Parameters
100
- ----------
101
- cells : list
102
- The list of input cells.
103
- tolerance : float , optional
104
- The desired tolerance. The default is 0.0001.
105
-
106
- Returns
107
- -------
108
- topologic.CellComplex
109
- The created cellcomplex.
110
-
111
- """
112
- from topologicpy.Cluster import Cluster
113
- from topologicpy.Topology import Topology
114
-
115
- if not isinstance(cells, list):
116
- if not silent:
117
- print("CellComplex.ByCells - Error: The input cells parameter is not a valid list. Returning None.")
118
- return None
119
- cells = [x for x in cells if isinstance(x, topologic.Cell)]
120
- if len(cells) < 1:
121
- if not silent:
122
- print("CellComplex.ByCells - Error: The input cells parameter does not contain any valid cells. Returning None.")
123
- return None
124
- cellComplex = None
125
- if len(cells) == 1:
126
- return topologic.CellComplex.ByCells(cells)
127
- else:
128
- try:
129
- cellComplex = topologic.CellComplex.ByCells(cells)
130
- except:
131
- topA = cells[0]
132
- topB = Cluster.ByTopologies(cells[1:])
133
- cellComplex = Topology.Merge(topA, topB, tranDict=False, tolerance=tolerance)
134
-
135
- if not isinstance(cellComplex, topologic.CellComplex):
136
- if not silent:
137
- print("CellComplex.ByCells - Warning: Could not create a CellComplex. Returning object of type topologic.Cluster instead of topologic.CellComplex.")
138
- return Cluster.ByTopologies(cells)
139
- else:
140
- temp_cells = CellComplex.Cells(cellComplex)
141
- if not isinstance(temp_cells, list):
142
- if not silent:
143
- print("CellComplex.ByCells - Error: The resulting object does not contain any cells. Returning None.")
144
- return None
145
- elif len(temp_cells) < 1:
146
- if silent:
147
- print("CellComplex.ByCells - Error: Could not create a CellComplex. Returning None.")
148
- return None
149
- elif len(temp_cells) == 1:
150
- if not silent:
151
- print("CellComplex.ByCells - Warning: Resulting object contains only one cell. Returning object of type topologic.Cell instead of topologic.CellComplex.")
152
- return(temp_cells[0])
153
- return cellComplex
154
-
155
- @staticmethod
156
- def ByCellsCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
157
- """
158
- Creates a cellcomplex by merging the cells within the input cluster.
159
-
160
- Parameters
161
- ----------
162
- cluster : topologic.Cluster
163
- The input cluster of cells.
164
- tolerance : float , optional
165
- The desired tolerance. The default is 0.0001.
166
-
167
- Returns
168
- -------
169
- topologic.CellComplex
170
- The created cellcomplex.
171
-
172
- """
173
-
174
- if not isinstance(cluster, topologic.Cluster):
175
- print("CellComplex.ByCellsCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
176
- return None
177
- cells = []
178
- _ = cluster.Cells(None, cells)
179
- return CellComplex.ByCells(cells, tolerance)
180
-
181
- @staticmethod
182
- def ByFaces(faces: list, tolerance: float = 0.0001) -> topologic.CellComplex:
183
- """
184
- Creates a cellcomplex by merging the input faces.
185
-
186
- Parameters
187
- ----------
188
- faces : topologic.Face
189
- The input faces.
190
- tolerance : float , optional
191
- The desired tolerance. The default is 0.0001.
192
-
193
- Returns
194
- -------
195
- topologic.CellComplex
196
- The created cellcomplex.
197
-
198
- """
199
-
200
- if not isinstance(faces, list):
201
- print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
202
- return None
203
- faces = [x for x in faces if isinstance(x, topologic.Face)]
204
- if len(faces) < 1:
205
- print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
206
- return None
207
- try:
208
- cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False)
209
- except:
210
- cellComplex = None
211
- if not cellComplex:
212
- print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
213
- cellComplex = faces[0]
214
- for i in range(1,len(faces)):
215
- newCellComplex = None
216
- try:
217
- newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
218
- except:
219
- print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
220
- if newCellComplex:
221
- cellComplex = newCellComplex
222
- if cellComplex.Type() != 64: #64 is the type of a CellComplex
223
- print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
224
- if cellComplex.Type() > 64:
225
- returnCellComplexes = []
226
- _ = cellComplex.CellComplexes(None, returnCellComplexes)
227
- if len(returnCellComplexes) > 0:
228
- return returnCellComplexes[0]
229
- else:
230
- print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
231
- return None
232
- else:
233
- print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
234
- return None
235
- else:
236
- return cellComplex
237
-
238
- @staticmethod
239
- def ByFacesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
240
- """
241
- Creates a cellcomplex by merging the faces within the input cluster.
242
-
243
- Parameters
244
- ----------
245
- cluster : topologic.Cluster
246
- The input cluster of faces.
247
- tolerance : float , optional
248
- The desired tolerance. The default is 0.0001.
249
-
250
- Returns
251
- -------
252
- topologic.CellComplex
253
- The created cellcomplex.
254
-
255
- """
256
-
257
- if not isinstance(cluster, topologic.Cluster):
258
- print("CellComplex.ByFacesCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
259
- return None
260
- faces = []
261
- _ = cluster.Faces(None, faces)
262
- return CellComplex.ByFaces(faces, tolerance)
263
-
264
- @staticmethod
265
- def ByWires(wires: list, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
266
- """
267
- Creates a cellcomplex by lofting through the input wires.
268
-
269
- Parameters
270
- ----------
271
- wires : list
272
- The input list of wires. The list should contain a minimum of two wires. All wires must have the same number of edges.
273
- triangulate : bool , optional
274
- If set to True, the faces will be triangulated. The default is True.
275
- tolerance : float , optional
276
- The desired tolerance. The default is 0.0001.
277
-
278
- Returns
279
- -------
280
- topologic.CellComplex
281
- The created cellcomplex.
282
-
283
- """
284
- from topologicpy.Edge import Edge
285
- from topologicpy.Wire import Wire
286
- from topologicpy.Face import Face
287
- from topologicpy.Topology import Topology
288
-
289
- if not isinstance(wires, list):
290
- print("CellComplex.ByFaces - Error: The input wires parameter is not a valid list. Returning None.")
291
- return None
292
- wires = [x for x in wires if isinstance(x, topologic.Wire)]
293
- if len(wires) < 2:
294
- print("CellComplex.ByWires - Error: The input wires parameter contains less than two valid wires. Returning None.")
295
- return None
296
- faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
297
- if triangulate == True:
298
- triangles = []
299
- for face in faces:
300
- if len(Topology.Vertices(face)) > 3:
301
- triangles += Face.Triangulate(face, tolerance=tolerance)
302
- else:
303
- triangles += [face]
304
- faces = triangles
305
- for i in range(len(wires)-1):
306
- wire1 = wires[i]
307
- wire2 = wires[i+1]
308
- f = Face.ByWire(wire2, tolerance=tolerance)
309
- if triangulate == True:
310
- if len(Topology.Vertices(face)) > 3:
311
- triangles = Face.Triangulate(face, tolerance=tolerance)
312
- else:
313
- triangles = [face]
314
- faces += triangles
315
- else:
316
- faces.append(f)
317
- w1_edges = []
318
- _ = wire1.Edges(None, w1_edges)
319
- w2_edges = []
320
- _ = wire2.Edges(None, w2_edges)
321
- if len(w1_edges) != len(w2_edges):
322
- print("CellComplex.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
323
- return None
324
- for j in range (len(w1_edges)):
325
- e1 = w1_edges[j]
326
- e2 = w2_edges[j]
327
- e3 = None
328
- e4 = None
329
- try:
330
- e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
331
- except:
332
- try:
333
- e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
334
- f = Face.ByExternalBoundary(Wire.ByEdges([e1, e2, e4], tolerance=tolerance))
335
- if triangulate == True:
336
- if len(Topology.Vertices(face)) > 3:
337
- triangles = Face.Triangulate(face, tolerance=tolerance)
338
- else:
339
- triangles = [face]
340
- faces += triangles
341
- else:
342
- faces.append(f)
343
- except:
344
- pass
345
- try:
346
- e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
347
- except:
348
- try:
349
- e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
350
- f = Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance)
351
- if triangulate == True:
352
- if len(Topology.Vertices(face)) > 3:
353
- triangles = Face.Triangulate(face, tolerance=tolerance)
354
- else:
355
- triangles = [face]
356
- faces += triangles
357
- else:
358
- faces.append(f)
359
- except:
360
- pass
361
- if e3 and e4:
362
- if triangulate == True:
363
- e5 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
364
- faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
365
- faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
366
- else:
367
- f = Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance) or Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance)
368
- if f:
369
- faces.append(f)
370
-
371
- elif e3:
372
- faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
373
- elif e4:
374
- faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
375
- return CellComplex.ByFaces(faces, tolerance=tolerance)
376
-
377
- @staticmethod
378
- def ByWiresCluster(cluster: topologic.Cluster, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
379
- """
380
- Creates a cellcomplex by lofting through the wires in the input cluster.
381
-
382
- Parameters
383
- ----------
384
- cluster : topologic.Cluster
385
- The input cluster of wires.
386
- triangulate : bool , optional
387
- If set to True, the faces will be triangulated. The default is True.
388
- tolerance : float , optional
389
- The desired tolerance. The default is 0.0001.
390
-
391
- Returns
392
- -------
393
- topologic.CellComplex
394
- The created cellcomplex.
395
-
396
- """
397
-
398
- if not isinstance(cluster, topologic.Cluster):
399
- print("CellComplex.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
400
- return None
401
- wires = []
402
- _ = cluster.Wires(None, wires)
403
- return CellComplex.ByWires(wires, triangulate=triangulate, tolerance=tolerance)
404
-
405
- @staticmethod
406
- def Cells(cellComplex: topologic.CellComplex) -> list:
407
- """
408
- Returns the cells of the input cellComplex.
409
-
410
- Parameters
411
- ----------
412
- cellComplex : topologic.CellComplex
413
- The input cellComplex.
414
-
415
- Returns
416
- -------
417
- list
418
- The list of cells.
419
-
420
- """
421
- if not isinstance(cellComplex, topologic.CellComplex):
422
- print("CellComplex.Cells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
423
- return None
424
- cells = []
425
- _ = cellComplex.Cells(None, cells)
426
- return cells
427
-
428
- @staticmethod
429
- def Decompose(cellComplex: topologic.CellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) -> dict:
430
- """
431
- Decomposes the input cellComplex into its logical components. This method assumes that the positive Z direction is UP.
432
-
433
- Parameters
434
- ----------
435
- cellComplex : topologic.CellComplex
436
- the input cellComplex.
437
- tiltAngle : float , optional
438
- The threshold tilt angle in degrees to determine if a face is vertical, horizontal, or tilted. The tilt angle is measured from the nearest cardinal direction. The default is 10.
439
- tolerance : float , optional
440
- The desired tolerance. The default is 0.0001.
441
-
442
- Returns
443
- -------
444
- dictionary
445
- A dictionary with the following keys and values:
446
- 1. "cells": list of cells
447
- 2. "externalVerticalFaces": list of external vertical faces
448
- 3. "internalVerticalFaces": list of internal vertical faces
449
- 4. "topHorizontalFaces": list of top horizontal faces
450
- 5. "bottomHorizontalFaces": list of bottom horizontal faces
451
- 6. "internalHorizontalFaces": list of internal horizontal faces
452
- 7. "externalInclinedFaces": list of external inclined faces
453
- 8. "internalInclinedFaces": list of internal inclined faces
454
- 9. "externalVerticalApertures": list of external vertical apertures
455
- 10. "internalVerticalApertures": list of internal vertical apertures
456
- 11. "topHorizontalApertures": list of top horizontal apertures
457
- 12. "bottomHorizontalApertures": list of bottom horizontal apertures
458
- 13. "internalHorizontalApertures": list of internal horizontal apertures
459
- 14. "externalInclinedApertures": list of external inclined apertures
460
- 15. "internalInclinedApertures": list of internal inclined apertures
461
-
462
- """
463
- from topologicpy.Face import Face
464
- from topologicpy.Vector import Vector
465
- from topologicpy.Aperture import Aperture
466
- from topologicpy.Topology import Topology
467
-
468
- def angleCode(f, up, tiltAngle):
469
- dirA = Face.NormalAtParameters(f)
470
- ang = round(Vector.Angle(dirA, up), 2)
471
- if abs(ang - 90) < tiltAngle:
472
- code = 0
473
- elif abs(ang) < tiltAngle:
474
- code = 1
475
- elif abs(ang - 180) < tiltAngle:
476
- code = 2
477
- else:
478
- code = 3
479
- return code
480
-
481
- def getApertures(topology):
482
- apertures = []
483
- apTopologies = []
484
- apertures = Topology.Apertures(topology)
485
- for aperture in apertures:
486
- apTopologies.append(Aperture.Topology(aperture))
487
- return apTopologies
488
-
489
- if not isinstance(cellComplex, topologic.CellComplex):
490
- print("CellComplex.Decompose - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
491
- return None
492
- externalVerticalFaces = []
493
- internalVerticalFaces = []
494
- topHorizontalFaces = []
495
- bottomHorizontalFaces = []
496
- internalHorizontalFaces = []
497
- externalInclinedFaces = []
498
- internalInclinedFaces = []
499
- externalVerticalApertures = []
500
- internalVerticalApertures = []
501
- topHorizontalApertures = []
502
- bottomHorizontalApertures = []
503
- internalHorizontalApertures = []
504
- externalInclinedApertures = []
505
- internalInclinedApertures = []
506
- tiltAngle = abs(tiltAngle)
507
- faces = CellComplex.Faces(cellComplex)
508
- zList = []
509
- for f in faces:
510
- zList.append(f.Centroid().Z())
511
- zMin = min(zList)
512
- zMax = max(zList)
513
- up = [0, 0, 1]
514
- for aFace in faces:
515
- aCode = angleCode(aFace, up, tiltAngle)
516
- cells = []
517
- aFace.Cells(cellComplex, cells)
518
- n = len(cells)
519
- if aCode == 0:
520
- if n == 1:
521
- externalVerticalFaces.append(aFace)
522
- externalVerticalApertures += getApertures(aFace)
523
- else:
524
- internalVerticalFaces.append(aFace)
525
- internalVerticalApertures += getApertures(aFace)
526
- elif aCode == 1:
527
- if n == 1:
528
- if abs(aFace.Centroid().Z() - zMin) < tolerance:
529
- bottomHorizontalFaces.append(aFace)
530
- bottomHorizontalApertures += getApertures(aFace)
531
- else:
532
- topHorizontalFaces.append(aFace)
533
- topHorizontalApertures += getApertures(aFace)
534
- else:
535
- internalHorizontalFaces.append(aFace)
536
- internalHorizontalApertures += getApertures(aFace)
537
- elif aCode == 2:
538
- if n == 1:
539
- if abs(aFace.Centroid().Z() - zMax) < tolerance:
540
- topHorizontalFaces.append(aFace)
541
- topHorizontalApertures += getApertures(aFace)
542
- else:
543
- bottomHorizontalFaces.append(aFace)
544
- bottomHorizontalApertures += getApertures(aFace)
545
- else:
546
- internalHorizontalFaces.append(aFace)
547
- internalHorizontalApertures += getApertures(aFace)
548
- elif aCode == 3:
549
- if n == 1:
550
- externalInclinedFaces.append(aFace)
551
- externalInclinedApertures += getApertures(aFace)
552
- else:
553
- internalInclinedFaces.append(aFace)
554
- internalInclinedApertures += getApertures(aFace)
555
-
556
- cells = Topology.Cells(cellComplex)
557
- d = {
558
- "cells" : cells,
559
- "externalVerticalFaces" : externalVerticalFaces,
560
- "internalVerticalFaces" : internalVerticalFaces,
561
- "topHorizontalFaces" : topHorizontalFaces,
562
- "bottomHorizontalFaces" : bottomHorizontalFaces,
563
- "internalHorizontalFaces" : internalHorizontalFaces,
564
- "externalInclinedFaces" : externalInclinedFaces,
565
- "internalInclinedFaces" : internalInclinedFaces,
566
- "externalVerticalApertures" : externalVerticalApertures,
567
- "internalVerticalApertures" : internalVerticalApertures,
568
- "topHorizontalApertures" : topHorizontalApertures,
569
- "bottomHorizontalApertures" : bottomHorizontalApertures,
570
- "internalHorizontalApertures" : internalHorizontalApertures,
571
- "externalInclinedApertures" : externalInclinedApertures,
572
- "internalInclinedApertures" : internalInclinedApertures
573
- }
574
- return d
575
-
576
- @staticmethod
577
- def Delaunay(vertices: list = None, tolerance: float = 0.0001) -> topologic.CellComplex:
578
- """
579
- Triangulates the input vertices based on the Delaunay method. See https://en.wikipedia.org/wiki/Delaunay_triangulation.
580
-
581
- Parameters
582
- ----------
583
- vertices: list , optional
584
- The input list of vertices to use for delaunay triangulation. If set to None, the algorithm uses the vertices of the input cell parameter.
585
- if both are set to none, a unit cube centered around the origin is used.
586
- tolerance : float , optional
587
- the desired tolerance. The default is 0.0001.
588
-
589
- Returns
590
- -------
591
- topologic.CellComplex
592
- The created delaunay cellComplex.
593
-
594
- """
595
- from topologicpy.Vertex import Vertex
596
- from topologicpy.Face import Face
597
- from topologicpy.Cell import Cell
598
- from topologicpy.Cluster import Cluster
599
- from topologicpy.Topology import Topology
600
- from scipy.spatial import Delaunay as SCIDelaunay
601
- import numpy as np
602
-
603
- if not isinstance(vertices, list):
604
- cell = Cell.Prism()
605
- vertices = Topology.Vertices(cell)
606
-
607
- vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
608
- if len(vertices) < 3:
609
- print("CellComplex/Delaunay - Error: The input vertices parameter does not contain enough valid vertices. Returning None.")
610
- return None
611
- # Get the vertices of the input cell
612
- points = np.array([Vertex.Coordinates(v) for v in vertices])
613
- # Compute Delaunay triangulation
614
- triangulation = SCIDelaunay(points, furthest_site=False)
615
-
616
- faces = []
617
- for simplex in triangulation.simplices:
618
- tetrahedron_vertices = points[simplex]
619
- verts = [Vertex.ByCoordinates(list(coord)) for coord in tetrahedron_vertices]
620
- tri1 = [verts[0], verts[1], verts[2], verts[0]]
621
- tri2 = [verts[0], verts[2], verts[3], verts[0]]
622
- tri3 = [verts[0], verts[1], verts[3], verts[0]]
623
- tri4 = [verts[1], verts[2], verts[3], verts[1]]
624
- f1 = Face.ByVertices(tri1)
625
- f2 = Face.ByVertices(tri2)
626
- f3 = Face.ByVertices(tri3)
627
- f4 = Face.ByVertices(tri4)
628
- faces.append(f1)
629
- faces.append(f2)
630
- faces.append(f3)
631
- faces.append(f4)
632
- cc = Topology.RemoveCoplanarFaces(CellComplex.ByFaces(faces, tolerance=tolerance))
633
- faces = [Topology.RemoveCollinearEdges(f) for f in Topology.Faces(cc)]
634
- cc = CellComplex.ByFaces(faces)
635
- return cc
636
-
637
- @staticmethod
638
- def Edges(cellComplex: topologic.CellComplex) -> list:
639
- """
640
- Returns the edges of the input cellComplex.
641
-
642
- Parameters
643
- ----------
644
- cellComplex : topologic.CellComplex
645
- The input cellComplex.
646
-
647
- Returns
648
- -------
649
- list
650
- The list of edges.
651
-
652
- """
653
- if not isinstance(cellComplex, topologic.CellComplex):
654
- print("CellComplex.Edges - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
655
- return None
656
- edges = []
657
- _ = cellComplex.Edges(None, edges)
658
- return edges
659
-
660
- @staticmethod
661
- def ExternalBoundary(cellComplex: topologic.CellComplex) -> topologic.Cell:
662
- """
663
- Returns the external boundary (cell) of the input cellComplex.
664
-
665
- Parameters
666
- ----------
667
- cellComplex : topologic.CellComplex
668
- The input cellComplex.
669
-
670
- Returns
671
- -------
672
- topologic.Cell
673
- The external boundary of the input cellComplex.
674
-
675
- """
676
- return cellComplex.ExternalBoundary()
677
-
678
- @staticmethod
679
- def ExternalFaces(cellComplex: topologic.CellComplex) -> list:
680
- """
681
- Returns the external faces of the input cellComplex.
682
-
683
- Parameters
684
- ----------
685
- cellComplex : topologic.CellComplex
686
- The input cellComplex.
687
-
688
- Returns
689
- -------
690
- list
691
- The list of external faces.
692
-
693
- """
694
- from topologicpy.Cell import Cell
695
- cell = cellComplex.ExternalBoundary()
696
- return Cell.Faces(cell)
697
-
698
- @staticmethod
699
- def Faces(cellComplex: topologic.CellComplex) -> list:
700
- """
701
- Returns the faces of the input cellComplex.
702
-
703
- Parameters
704
- ----------
705
- cellComplex : topologic.CellComplex
706
- The input cellComplex.
707
-
708
- Returns
709
- -------
710
- list
711
- The list of faces.
712
-
713
- """
714
- if not isinstance(cellComplex, topologic.CellComplex):
715
- print("CellComplex.Faces - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
716
- return None
717
- faces = []
718
- _ = cellComplex.Faces(None, faces)
719
- return faces
720
-
721
- @staticmethod
722
- def InternalFaces(cellComplex: topologic.CellComplex) -> list:
723
- """
724
- Returns the internal boundaries (faces) of the input cellComplex.
725
-
726
- Parameters
727
- ----------
728
- cellComplex : topologic.CellComplex
729
- The input cellComplex.
730
-
731
- Returns
732
- -------
733
- list
734
- The list of internal faces of the input cellComplex.
735
-
736
- """
737
- faces = []
738
- _ = cellComplex.InternalBoundaries(faces)
739
- return faces
740
-
741
- @staticmethod
742
- def NonManifoldFaces(cellComplex: topologic.CellComplex) -> list:
743
- """
744
- Returns the non-manifold faces of the input cellComplex.
745
-
746
- Parameters
747
- ----------
748
- cellComplex : topologic.CellComplex
749
- The input cellComplex.
750
-
751
- Returns
752
- -------
753
- list
754
- The list of non-manifold faces of the input cellComplex.
755
-
756
- """
757
- faces = []
758
- _ = cellComplex.NonManifoldFaces(faces)
759
- return faces
760
-
761
- @staticmethod
762
- def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
763
- direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.CellComplex:
764
- """
765
- Description
766
- ----------
767
- Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.
768
-
769
- Parameters
770
- ----------
771
- origin : topologic.Vertex , optional
772
- The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
773
- radius : float , optional
774
- The radius of the octahedron's circumscribed sphere. The default is 0.5.
775
- direction : list , optional
776
- The vector representing the up direction of the octahedron. The default is [0, 0, 1].
777
- placement : str , optional
778
- The description of the placement of the origin of the octahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
779
- tolerance : float , optional
780
- The desired tolerance. The default is 0.0001.
781
-
782
- Returns
783
- -------
784
- topologic.CellComplex
785
- The created octahedron.
786
-
787
- """
788
-
789
- from topologicpy.Vertex import Vertex
790
- from topologicpy.Face import Face
791
- from topologicpy.Topology import Topology
792
-
793
- if not origin:
794
- origin = Vertex.ByCoordinates(0, 0, 0)
795
- if not isinstance(origin, topologic.Vertex):
796
- print("CellComplex.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
797
- return None
798
-
799
- vb1 = Vertex.ByCoordinates(-0.5,0,0)
800
- vb2 = Vertex.ByCoordinates(0,-0.5,0)
801
- vb3 = Vertex.ByCoordinates(0.5,0,0)
802
- vb4 = Vertex.ByCoordinates(0,0.5,0)
803
- top = Vertex.ByCoordinates(0, 0, 0.5)
804
- bottom = Vertex.ByCoordinates(0, 0, -0.5)
805
- f1 = Face.ByVertices([top,vb1,vb2])
806
- f2 = Face.ByVertices([top,vb2,vb3])
807
- f3 = Face.ByVertices([top,vb3,vb4])
808
- f4 = Face.ByVertices([top,vb4,vb1])
809
- f5 = Face.ByVertices([bottom,vb1,vb2])
810
- f6 = Face.ByVertices([bottom,vb2,vb3])
811
- f7 = Face.ByVertices([bottom,vb3,vb4])
812
- f8 = Face.ByVertices([bottom,vb4,vb1])
813
- f9 = Face.ByVertices([vb1,vb2,vb3,vb4])
814
-
815
- octahedron = CellComplex.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9], tolerance=tolerance)
816
- octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
817
- if placement == "bottom":
818
- octahedron = Topology.Translate(octahedron, 0, 0, radius)
819
- elif placement == "lowerleft":
820
- octahedron = Topology.Translate(octahedron, radius, radius, radius)
821
- octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
822
- octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
823
- return octahedron
824
-
825
- @staticmethod
826
- def Prism(origin: topologic.Vertex = None,
827
- width: float = 1.0, length: float = 1.0, height: float = 1.0,
828
- uSides: int = 2, vSides: int = 2, wSides: int = 2,
829
- direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
830
- """
831
- Creates a prismatic cellComplex with internal cells.
832
-
833
- Parameters
834
- ----------
835
- origin : topologic.Vertex , optional
836
- The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
837
- width : float , optional
838
- The width of the prism. The default is 1.
839
- length : float , optional
840
- The length of the prism. The default is 1.
841
- height : float , optional
842
- The height of the prism.
843
- uSides : int , optional
844
- The number of sides along the width. The default is 1.
845
- vSides : int , optional
846
- The number of sides along the length. The default is 1.
847
- wSides : int , optional
848
- The number of sides along the height. The default is 1.
849
- direction : list , optional
850
- The vector representing the up direction of the prism. The default is [0, 0, 1].
851
- placement : str , optional
852
- The description of the placement of the origin of the prism. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
853
- tolerance : float , optional
854
- The desired tolerance. The default is 0.0001.
855
-
856
- Returns
857
- -------
858
- topologic.CellComplex
859
- The created prism.
860
-
861
- """
862
- from topologicpy.Vertex import Vertex
863
- from topologicpy.Face import Face
864
- from topologicpy.Cell import Cell
865
- from topologicpy.Cluster import Cluster
866
- from topologicpy.Topology import Topology
867
-
868
- def bb(topology):
869
- vertices = []
870
- _ = topology.Vertices(None, vertices)
871
- x = []
872
- y = []
873
- z = []
874
- for aVertex in vertices:
875
- x.append(aVertex.X())
876
- y.append(aVertex.Y())
877
- z.append(aVertex.Z())
878
- minX = min(x)
879
- minY = min(y)
880
- minZ = min(z)
881
- maxX = max(x)
882
- maxY = max(y)
883
- maxZ = max(z)
884
- return [minX, minY, minZ, maxX, maxY, maxZ]
885
-
886
- def slice(topology, uSides, vSides, wSides):
887
- minX, minY, minZ, maxX, maxY, maxZ = bb(topology)
888
- centroid = Vertex.ByCoordinates(minX+(maxX-minX)*0.5, minY+(maxY-minY)*0.5, minZ+(maxZ-minZ)*0.5)
889
- wOrigin = Vertex.ByCoordinates(Vertex.X(centroid), Vertex.Y(centroid), minZ)
890
- wFace = Face.Rectangle(origin=wOrigin, width=(maxX-minX)*1.1, length=(maxY-minY)*1.1)
891
- wFaces = []
892
- wOffset = (maxZ-minZ)/wSides
893
- for i in range(wSides-1):
894
- wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
895
- uOrigin = Vertex.ByCoordinates(minX, Vertex.Y(centroid), Vertex.Z(centroid))
896
- uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-minZ)*1.1, length=(maxY-minY)*1.1, direction=[1,0,0])
897
- uFaces = []
898
- uOffset = (maxX-minX)/uSides
899
- for i in range(uSides-1):
900
- uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
901
- vOrigin = Vertex.ByCoordinates(Vertex.X(centroid), minY, Vertex.Z(centroid))
902
- vFace = Face.Rectangle(origin=vOrigin, width=(maxX-minX)*1.1, length=(maxZ-minZ)*1.1, direction=[0,1,0])
903
- vFaces = []
904
- vOffset = (maxY-minY)/vSides
905
- for i in range(vSides-1):
906
- vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
907
- all_faces = uFaces+vFaces+wFaces
908
- if len(all_faces) > 0:
909
- f_clus = Cluster.ByTopologies(uFaces+vFaces+wFaces)
910
- return Topology.Slice(topology, f_clus, tolerance=tolerance)
911
- else:
912
- return topologic.CellComplex.ByCells([topology])
913
- if not isinstance(origin, topologic.Vertex):
914
- origin = Vertex.ByCoordinates(0, 0, 0)
915
-
916
- c = Cell.Prism(origin=origin, width=width, length=length, height=height, uSides=1, vSides=1, wSides=1, placement=placement, tolerance=tolerance)
917
- prism = slice(c, uSides=uSides, vSides=vSides, wSides=wSides)
918
- if prism:
919
- prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction)
920
- return prism
921
- else:
922
- print("CellComplex.Prism - Error: Could not create a prism. Returning None.")
923
- return None
924
-
925
- @staticmethod
926
- def RemoveCollinearEdges(cellComplex: topologic.CellComplex, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
927
- """
928
- Removes any collinear edges in the input cellComplex.
929
-
930
- Parameters
931
- ----------
932
- cellComplex : topologic.CellComplex
933
- The input cellComplex.
934
- angTolerance : float , optional
935
- The desired angular tolerance. The default is 0.1.
936
- tolerance : float , optional
937
- The desired tolerance. The default is 0.0001.
938
-
939
- Returns
940
- -------
941
- topologic.CellComplex
942
- The created cellComplex without any collinear edges.
943
-
944
- """
945
- from topologicpy.Cell import Cell
946
-
947
- if not isinstance(cellComplex, topologic.CellComplex):
948
- print("CellComplex.RemoveCollinearEdges - Error: The input cellComplex parameter is not a valid cellComplex. Returning None.")
949
- return None
950
- cells = CellComplex.Cells(cellComplex)
951
- clean_cells = []
952
- for cell in cells:
953
- clean_cells.append(Cell.RemoveCollinearEdges(cell, angTolerance=angTolerance, tolerance=tolerance))
954
- return CellComplex.ByCells(clean_cells, tolerance=tolerance)
955
-
956
- @staticmethod
957
- def Shells(cellComplex: topologic.CellComplex) -> list:
958
- """
959
- Returns the shells of the input cellComplex.
960
-
961
- Parameters
962
- ----------
963
- cellComplex : topologic.CellComplex
964
- The input cellComplex.
965
-
966
- Returns
967
- -------
968
- list
969
- The list of shells.
970
-
971
- """
972
- if not isinstance(cellComplex, topologic.CellComplex):
973
- print("CellComplex.Shells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
974
- return None
975
- shells = []
976
- _ = cellComplex.Shells(None, shells)
977
- return shells
978
-
979
- @staticmethod
980
- def Vertices(cellComplex: topologic.CellComplex) -> list:
981
- """
982
- Returns the vertices of the input cellComplex.
983
-
984
- Parameters
985
- ----------
986
- cellComplex : topologic.CellComplex
987
- The input cellComplex.
988
-
989
- Returns
990
- -------
991
- list
992
- The list of vertices.
993
-
994
- """
995
- if not isinstance(cellComplex, topologic.CellComplex):
996
- print("CellComplex.Vertices - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
997
- return None
998
- vertices = []
999
- _ = cellComplex.Vertices(None, vertices)
1000
- return vertices
1001
-
1002
- @staticmethod
1003
- def Volume(cellComplex: topologic.CellComplex, mantissa: int = 6) -> float:
1004
- """
1005
- Returns the volume of the input cellComplex.
1006
-
1007
- Parameters
1008
- ----------
1009
- cellComplex : topologic.CellComplex
1010
- The input cellComplex.
1011
- manitssa: int , optional
1012
- The desired length of the mantissa. The default is 6.
1013
-
1014
- Returns
1015
- -------
1016
- float
1017
- The volume of the input cellComplex.
1018
-
1019
- """
1020
- from topologicpy.Cell import Cell
1021
- if not isinstance(cellComplex, topologic.CellComplex):
1022
- print("CellComplex.Volume - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
1023
- return None
1024
- cells = CellComplex.Cells(cellComplex)
1025
- volume = 0
1026
- for cell in cells:
1027
- volume = Cell.Volume(cell)
1028
- if not volume == None:
1029
- volume += Cell.Volume(cell)
1030
- return round(volume, mantissa)
1031
-
1032
- @staticmethod
1033
- def Voronoi(vertices: list = None, cell: topologic.Cell = None, tolerance: float = 0.0001):
1034
- """
1035
- Partitions the input cell based on the Voronoi method. See https://en.wikipedia.org/wiki/Voronoi_diagram.
1036
-
1037
- Parameters
1038
- ----------
1039
- vertices: list , optional
1040
- The input list of vertices to use for voronoi partitioning. If set to None, the algorithm uses the vertices of the input cell parameter.
1041
- if both are set to none, a unit cube centered around the origin is used.
1042
- cell : topologic.Cell , optional
1043
- The input bounding cell. If set to None, an axes-aligned bounding cell is created from the list of vertices. The default is None.
1044
- tolerance : float , optional
1045
- the desired tolerance. The default is 0.0001.
1046
-
1047
-
1048
- Returns
1049
- -------
1050
- topologic.CellComplex
1051
- The created voronoi cellComplex.
1052
-
1053
- """
1054
- from topologicpy.Vertex import Vertex
1055
- from topologicpy.Face import Face
1056
- from topologicpy.Cell import Cell
1057
- from topologicpy.Cluster import Cluster
1058
- from topologicpy.Topology import Topology
1059
- from scipy.spatial import Voronoi as SCIVoronoi
1060
- import numpy as np
1061
-
1062
- def fracture_with_voronoi(points):
1063
- # Compute Voronoi tessellation
1064
- vor = SCIVoronoi(points)
1065
- verts = []
1066
- faces = []
1067
- for v in vor.vertices:
1068
- verts.append(Vertex.ByCoordinates(list(v)))
1069
- for region in vor.ridge_vertices:
1070
- temp_list = []
1071
- if -1 not in region and len(region) > 0:
1072
- for item in region:
1073
- temp_list.append(verts[item])
1074
- f = Face.ByVertices(temp_list)
1075
- if isinstance(f, topologic.Face):
1076
- faces.append(f)
1077
- if len(faces) < 1:
1078
- return None
1079
- return Cluster.ByTopologies(faces)
1080
-
1081
- if cell == None:
1082
- if not isinstance(vertices, list):
1083
- cell = Cell.Prism(uSides=2, vSides=2, wSides=2)
1084
- vertices = Topology.Vertices(cell)
1085
- vertices.append(Vertex.Origin())
1086
- else:
1087
- vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
1088
- if len(vertices) < 1:
1089
- print("CellComplex.Voronoi - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
1090
- return None
1091
- cell = Topology.BoundingBox(Cluster.ByTopologies(vertices))
1092
- if not isinstance(vertices, list):
1093
- if not isinstance(cell, topologic.Cell):
1094
- cell = Cell.Prism()
1095
- vertices = Topology.Vertices(cell)
1096
- else:
1097
- vertices = Topology.Vertices(cell)
1098
- else:
1099
- vertices += Topology.Vertices(cell)
1100
- vertices = [v for v in vertices if (Vertex.IsInternal(v, cell) or not Vertex.Index(v, Topology.Vertices(cell)) == None)]
1101
- if len(vertices) < 1:
1102
- print("CellComplex.Voronoi - Error: The input vertices parame ter does not contain any vertices that are inside the input cell parameter. Returning None.")
1103
- return None
1104
- voronoi_points = np.array([Vertex.Coordinates(v) for v in vertices])
1105
- cluster = fracture_with_voronoi(voronoi_points)
1106
- if cluster == None:
1107
- print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
1108
- return None
1109
- cellComplex = Topology.Slice(cell, cluster)
1110
- if not isinstance(cellComplex, topologic.CellComplex):
1111
- print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
1112
- return None
1113
- return cellComplex
1114
-
1115
- @staticmethod
1116
- def Wires(cellComplex: topologic.CellComplex) -> list:
1117
- """
1118
- Returns the wires of the input cellComplex.
1119
-
1120
- Parameters
1121
- ----------
1122
- cellComplex : topologic.CellComplex
1123
- The input cellComplex.
1124
-
1125
- Returns
1126
- -------
1127
- list
1128
- The list of wires.
1129
-
1130
- """
1131
- if not isinstance(cellComplex, topologic.CellComplex):
1132
- print("CellComplex.Wires - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
1133
- return None
1134
- wires = []
1135
- _ = cellComplex.Wires(None, wires)
1136
- return wires
1137
-
1
+ # Copyright (C) 2024
2
+ # Wassim Jabi <wassim.jabi@gmail.com>
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify it under
5
+ # the terms of the GNU Affero General Public License as published by the Free Software
6
+ # Foundation, either version 3 of the License, or (at your option) any later
7
+ # version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful, but WITHOUT
10
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
12
+ # details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License along with
15
+ # this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ import topologic_core as topologic
18
+ import math
19
+ import os
20
+ from topologicpy.Topology import Topology
21
+ import warnings
22
+
23
+ try:
24
+ import numpy as np
25
+ except:
26
+ print("CellComplex - Installing required numpy library.")
27
+ try:
28
+ os.system("pip install numpy")
29
+ except:
30
+ os.system("pip install numpy --user")
31
+ try:
32
+ import numpy as np
33
+ print("CellComplex - numpy library installed correctly.")
34
+ except:
35
+ warnings.warn("CellComplex - Error: Could not import numpy.")
36
+ try:
37
+ from scipy.spatial import Delaunay
38
+ from scipy.spatial import Voronoi
39
+ except:
40
+ print("CellComplex - Install required scipy library.")
41
+ try:
42
+ os.system("pip install scipy")
43
+ except:
44
+ os.system("pip install scipy --user")
45
+ try:
46
+ from scipy.spatial import Delaunay
47
+ from scipy.spatial import Voronoi
48
+ except:
49
+ warnings.warn("CellComplex - Error: Could not import scipy.")
50
+
51
+ class CellComplex(Topology):
52
+ @staticmethod
53
+ def Box(origin: topologic.Vertex = None,
54
+ width: float = 1.0, length: float = 1.0, height: float = 1.0,
55
+ uSides: int = 2, vSides: int = 2, wSides: int = 2,
56
+ direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
57
+ """
58
+ Creates a box with internal cells.
59
+
60
+ Parameters
61
+ ----------
62
+ origin : topologic.Vertex , optional
63
+ The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
64
+ width : float , optional
65
+ The width of the box. The default is 1.
66
+ length : float , optional
67
+ The length of the box. The default is 1.
68
+ height : float , optional
69
+ The height of the box.
70
+ uSides : int , optional
71
+ The number of sides along the width. The default is 1.
72
+ vSides : int, optional
73
+ The number of sides along the length. The default is 1.
74
+ wSides : int , optional
75
+ The number of sides along the height. The default is 1.
76
+ direction : list , optional
77
+ The vector representing the up direction of the box. The default is [0, 0, 1].
78
+ placement : str , optional
79
+ The description of the placement of the origin of the box. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
80
+ tolerance : float , optional
81
+ The desired tolerance. The default is 0.0001.
82
+
83
+ Returns
84
+ -------
85
+ topologic.CellComplex
86
+ The created box.
87
+
88
+ """
89
+ return CellComplex.Prism(origin=origin,
90
+ width=width, length=length, height=height,
91
+ uSides=uSides, vSides=vSides, wSides=wSides,
92
+ direction=direction, placement=placement, tolerance=tolerance)
93
+
94
+ @staticmethod
95
+ def ByCells(cells: list, tolerance: float = 0.0001, silent: bool = False) -> topologic.CellComplex:
96
+ """
97
+ Creates a cellcomplex by merging the input cells.
98
+
99
+ Parameters
100
+ ----------
101
+ cells : list
102
+ The list of input cells.
103
+ tolerance : float , optional
104
+ The desired tolerance. The default is 0.0001.
105
+
106
+ Returns
107
+ -------
108
+ topologic.CellComplex
109
+ The created cellcomplex.
110
+
111
+ """
112
+ from topologicpy.Cluster import Cluster
113
+ from topologicpy.Topology import Topology
114
+
115
+ if not isinstance(cells, list):
116
+ if not silent:
117
+ print("CellComplex.ByCells - Error: The input cells parameter is not a valid list. Returning None.")
118
+ return None
119
+ cells = [x for x in cells if isinstance(x, topologic.Cell)]
120
+ if len(cells) < 1:
121
+ if not silent:
122
+ print("CellComplex.ByCells - Error: The input cells parameter does not contain any valid cells. Returning None.")
123
+ return None
124
+ cellComplex = None
125
+ if len(cells) == 1:
126
+ return topologic.CellComplex.ByCells(cells)
127
+ else:
128
+ try:
129
+ cellComplex = topologic.CellComplex.ByCells(cells)
130
+ except:
131
+ topA = cells[0]
132
+ topB = Cluster.ByTopologies(cells[1:])
133
+ cellComplex = Topology.Merge(topA, topB, tranDict=False, tolerance=tolerance)
134
+
135
+ if not isinstance(cellComplex, topologic.CellComplex):
136
+ if not silent:
137
+ print("CellComplex.ByCells - Warning: Could not create a CellComplex. Returning object of type topologic.Cluster instead of topologic.CellComplex.")
138
+ return Cluster.ByTopologies(cells)
139
+ else:
140
+ temp_cells = CellComplex.Cells(cellComplex)
141
+ if not isinstance(temp_cells, list):
142
+ if not silent:
143
+ print("CellComplex.ByCells - Error: The resulting object does not contain any cells. Returning None.")
144
+ return None
145
+ elif len(temp_cells) < 1:
146
+ if silent:
147
+ print("CellComplex.ByCells - Error: Could not create a CellComplex. Returning None.")
148
+ return None
149
+ elif len(temp_cells) == 1:
150
+ if not silent:
151
+ print("CellComplex.ByCells - Warning: Resulting object contains only one cell. Returning object of type topologic.Cell instead of topologic.CellComplex.")
152
+ return(temp_cells[0])
153
+ return cellComplex
154
+
155
+ @staticmethod
156
+ def ByCellsCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
157
+ """
158
+ Creates a cellcomplex by merging the cells within the input cluster.
159
+
160
+ Parameters
161
+ ----------
162
+ cluster : topologic.Cluster
163
+ The input cluster of cells.
164
+ tolerance : float , optional
165
+ The desired tolerance. The default is 0.0001.
166
+
167
+ Returns
168
+ -------
169
+ topologic.CellComplex
170
+ The created cellcomplex.
171
+
172
+ """
173
+
174
+ if not isinstance(cluster, topologic.Cluster):
175
+ print("CellComplex.ByCellsCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
176
+ return None
177
+ cells = []
178
+ _ = cluster.Cells(None, cells)
179
+ return CellComplex.ByCells(cells, tolerance)
180
+
181
+ @staticmethod
182
+ def ByFaces(faces: list, tolerance: float = 0.0001) -> topologic.CellComplex:
183
+ """
184
+ Creates a cellcomplex by merging the input faces.
185
+
186
+ Parameters
187
+ ----------
188
+ faces : topologic.Face
189
+ The input faces.
190
+ tolerance : float , optional
191
+ The desired tolerance. The default is 0.0001.
192
+
193
+ Returns
194
+ -------
195
+ topologic.CellComplex
196
+ The created cellcomplex.
197
+
198
+ """
199
+
200
+ if not isinstance(faces, list):
201
+ print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
202
+ return None
203
+ faces = [x for x in faces if isinstance(x, topologic.Face)]
204
+ if len(faces) < 1:
205
+ print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
206
+ return None
207
+ try:
208
+ cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False)
209
+ except:
210
+ cellComplex = None
211
+ if not cellComplex:
212
+ print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
213
+ cellComplex = faces[0]
214
+ for i in range(1,len(faces)):
215
+ newCellComplex = None
216
+ try:
217
+ newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
218
+ except:
219
+ print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
220
+ if newCellComplex:
221
+ cellComplex = newCellComplex
222
+ if cellComplex.Type() != 64: #64 is the type of a CellComplex
223
+ print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
224
+ if cellComplex.Type() > 64:
225
+ returnCellComplexes = []
226
+ _ = cellComplex.CellComplexes(None, returnCellComplexes)
227
+ if len(returnCellComplexes) > 0:
228
+ return returnCellComplexes[0]
229
+ else:
230
+ print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
231
+ return None
232
+ else:
233
+ print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
234
+ return None
235
+ else:
236
+ return cellComplex
237
+
238
+ @staticmethod
239
+ def ByFacesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
240
+ """
241
+ Creates a cellcomplex by merging the faces within the input cluster.
242
+
243
+ Parameters
244
+ ----------
245
+ cluster : topologic.Cluster
246
+ The input cluster of faces.
247
+ tolerance : float , optional
248
+ The desired tolerance. The default is 0.0001.
249
+
250
+ Returns
251
+ -------
252
+ topologic.CellComplex
253
+ The created cellcomplex.
254
+
255
+ """
256
+
257
+ if not isinstance(cluster, topologic.Cluster):
258
+ print("CellComplex.ByFacesCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
259
+ return None
260
+ faces = []
261
+ _ = cluster.Faces(None, faces)
262
+ return CellComplex.ByFaces(faces, tolerance)
263
+
264
+ @staticmethod
265
+ def ByWires(wires: list, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
266
+ """
267
+ Creates a cellcomplex by lofting through the input wires.
268
+
269
+ Parameters
270
+ ----------
271
+ wires : list
272
+ The input list of wires. The list should contain a minimum of two wires. All wires must have the same number of edges.
273
+ triangulate : bool , optional
274
+ If set to True, the faces will be triangulated. The default is True.
275
+ tolerance : float , optional
276
+ The desired tolerance. The default is 0.0001.
277
+
278
+ Returns
279
+ -------
280
+ topologic.CellComplex
281
+ The created cellcomplex.
282
+
283
+ """
284
+ from topologicpy.Edge import Edge
285
+ from topologicpy.Wire import Wire
286
+ from topologicpy.Face import Face
287
+ from topologicpy.Topology import Topology
288
+
289
+ if not isinstance(wires, list):
290
+ print("CellComplex.ByFaces - Error: The input wires parameter is not a valid list. Returning None.")
291
+ return None
292
+ wires = [x for x in wires if isinstance(x, topologic.Wire)]
293
+ if len(wires) < 2:
294
+ print("CellComplex.ByWires - Error: The input wires parameter contains less than two valid wires. Returning None.")
295
+ return None
296
+ faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
297
+ if triangulate == True:
298
+ triangles = []
299
+ for face in faces:
300
+ if len(Topology.Vertices(face)) > 3:
301
+ triangles += Face.Triangulate(face, tolerance=tolerance)
302
+ else:
303
+ triangles += [face]
304
+ faces = triangles
305
+ for i in range(len(wires)-1):
306
+ wire1 = wires[i]
307
+ wire2 = wires[i+1]
308
+ f = Face.ByWire(wire2, tolerance=tolerance)
309
+ if triangulate == True:
310
+ if len(Topology.Vertices(face)) > 3:
311
+ triangles = Face.Triangulate(face, tolerance=tolerance)
312
+ else:
313
+ triangles = [face]
314
+ faces += triangles
315
+ else:
316
+ faces.append(f)
317
+ w1_edges = []
318
+ _ = wire1.Edges(None, w1_edges)
319
+ w2_edges = []
320
+ _ = wire2.Edges(None, w2_edges)
321
+ if len(w1_edges) != len(w2_edges):
322
+ print("CellComplex.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
323
+ return None
324
+ for j in range (len(w1_edges)):
325
+ e1 = w1_edges[j]
326
+ e2 = w2_edges[j]
327
+ e3 = None
328
+ e4 = None
329
+ try:
330
+ e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
331
+ except:
332
+ try:
333
+ e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
334
+ f = Face.ByExternalBoundary(Wire.ByEdges([e1, e2, e4], tolerance=tolerance))
335
+ if triangulate == True:
336
+ if len(Topology.Vertices(face)) > 3:
337
+ triangles = Face.Triangulate(face, tolerance=tolerance)
338
+ else:
339
+ triangles = [face]
340
+ faces += triangles
341
+ else:
342
+ faces.append(f)
343
+ except:
344
+ pass
345
+ try:
346
+ e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
347
+ except:
348
+ try:
349
+ e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
350
+ f = Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance)
351
+ if triangulate == True:
352
+ if len(Topology.Vertices(face)) > 3:
353
+ triangles = Face.Triangulate(face, tolerance=tolerance)
354
+ else:
355
+ triangles = [face]
356
+ faces += triangles
357
+ else:
358
+ faces.append(f)
359
+ except:
360
+ pass
361
+ if e3 and e4:
362
+ if triangulate == True:
363
+ e5 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
364
+ faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
365
+ faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
366
+ else:
367
+ f = Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance) or Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance)
368
+ if f:
369
+ faces.append(f)
370
+
371
+ elif e3:
372
+ faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
373
+ elif e4:
374
+ faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
375
+ return CellComplex.ByFaces(faces, tolerance=tolerance)
376
+
377
+ @staticmethod
378
+ def ByWiresCluster(cluster: topologic.Cluster, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
379
+ """
380
+ Creates a cellcomplex by lofting through the wires in the input cluster.
381
+
382
+ Parameters
383
+ ----------
384
+ cluster : topologic.Cluster
385
+ The input cluster of wires.
386
+ triangulate : bool , optional
387
+ If set to True, the faces will be triangulated. The default is True.
388
+ tolerance : float , optional
389
+ The desired tolerance. The default is 0.0001.
390
+
391
+ Returns
392
+ -------
393
+ topologic.CellComplex
394
+ The created cellcomplex.
395
+
396
+ """
397
+
398
+ if not isinstance(cluster, topologic.Cluster):
399
+ print("CellComplex.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
400
+ return None
401
+ wires = []
402
+ _ = cluster.Wires(None, wires)
403
+ return CellComplex.ByWires(wires, triangulate=triangulate, tolerance=tolerance)
404
+
405
+ @staticmethod
406
+ def Cells(cellComplex: topologic.CellComplex) -> list:
407
+ """
408
+ Returns the cells of the input cellComplex.
409
+
410
+ Parameters
411
+ ----------
412
+ cellComplex : topologic.CellComplex
413
+ The input cellComplex.
414
+
415
+ Returns
416
+ -------
417
+ list
418
+ The list of cells.
419
+
420
+ """
421
+ if not isinstance(cellComplex, topologic.CellComplex):
422
+ print("CellComplex.Cells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
423
+ return None
424
+ cells = []
425
+ _ = cellComplex.Cells(None, cells)
426
+ return cells
427
+
428
+ @staticmethod
429
+ def Decompose(cellComplex: topologic.CellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) -> dict:
430
+ """
431
+ Decomposes the input cellComplex into its logical components. This method assumes that the positive Z direction is UP.
432
+
433
+ Parameters
434
+ ----------
435
+ cellComplex : topologic.CellComplex
436
+ the input cellComplex.
437
+ tiltAngle : float , optional
438
+ The threshold tilt angle in degrees to determine if a face is vertical, horizontal, or tilted. The tilt angle is measured from the nearest cardinal direction. The default is 10.
439
+ tolerance : float , optional
440
+ The desired tolerance. The default is 0.0001.
441
+
442
+ Returns
443
+ -------
444
+ dictionary
445
+ A dictionary with the following keys and values:
446
+ 1. "cells": list of cells
447
+ 2. "externalVerticalFaces": list of external vertical faces
448
+ 3. "internalVerticalFaces": list of internal vertical faces
449
+ 4. "topHorizontalFaces": list of top horizontal faces
450
+ 5. "bottomHorizontalFaces": list of bottom horizontal faces
451
+ 6. "internalHorizontalFaces": list of internal horizontal faces
452
+ 7. "externalInclinedFaces": list of external inclined faces
453
+ 8. "internalInclinedFaces": list of internal inclined faces
454
+ 9. "externalVerticalApertures": list of external vertical apertures
455
+ 10. "internalVerticalApertures": list of internal vertical apertures
456
+ 11. "topHorizontalApertures": list of top horizontal apertures
457
+ 12. "bottomHorizontalApertures": list of bottom horizontal apertures
458
+ 13. "internalHorizontalApertures": list of internal horizontal apertures
459
+ 14. "externalInclinedApertures": list of external inclined apertures
460
+ 15. "internalInclinedApertures": list of internal inclined apertures
461
+
462
+ """
463
+ from topologicpy.Face import Face
464
+ from topologicpy.Vector import Vector
465
+ from topologicpy.Aperture import Aperture
466
+ from topologicpy.Topology import Topology
467
+
468
+ def angleCode(f, up, tiltAngle):
469
+ dirA = Face.NormalAtParameters(f)
470
+ ang = round(Vector.Angle(dirA, up), 2)
471
+ if abs(ang - 90) < tiltAngle:
472
+ code = 0
473
+ elif abs(ang) < tiltAngle:
474
+ code = 1
475
+ elif abs(ang - 180) < tiltAngle:
476
+ code = 2
477
+ else:
478
+ code = 3
479
+ return code
480
+
481
+ def getApertures(topology):
482
+ apertures = []
483
+ apTopologies = []
484
+ apertures = Topology.Apertures(topology)
485
+ for aperture in apertures:
486
+ apTopologies.append(Aperture.Topology(aperture))
487
+ return apTopologies
488
+
489
+ if not isinstance(cellComplex, topologic.CellComplex):
490
+ print("CellComplex.Decompose - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
491
+ return None
492
+ externalVerticalFaces = []
493
+ internalVerticalFaces = []
494
+ topHorizontalFaces = []
495
+ bottomHorizontalFaces = []
496
+ internalHorizontalFaces = []
497
+ externalInclinedFaces = []
498
+ internalInclinedFaces = []
499
+ externalVerticalApertures = []
500
+ internalVerticalApertures = []
501
+ topHorizontalApertures = []
502
+ bottomHorizontalApertures = []
503
+ internalHorizontalApertures = []
504
+ externalInclinedApertures = []
505
+ internalInclinedApertures = []
506
+ tiltAngle = abs(tiltAngle)
507
+ faces = CellComplex.Faces(cellComplex)
508
+ zList = []
509
+ for f in faces:
510
+ zList.append(f.Centroid().Z())
511
+ zMin = min(zList)
512
+ zMax = max(zList)
513
+ up = [0, 0, 1]
514
+ for aFace in faces:
515
+ aCode = angleCode(aFace, up, tiltAngle)
516
+ cells = []
517
+ aFace.Cells(cellComplex, cells)
518
+ n = len(cells)
519
+ if aCode == 0:
520
+ if n == 1:
521
+ externalVerticalFaces.append(aFace)
522
+ externalVerticalApertures += getApertures(aFace)
523
+ else:
524
+ internalVerticalFaces.append(aFace)
525
+ internalVerticalApertures += getApertures(aFace)
526
+ elif aCode == 1:
527
+ if n == 1:
528
+ if abs(aFace.Centroid().Z() - zMin) < tolerance:
529
+ bottomHorizontalFaces.append(aFace)
530
+ bottomHorizontalApertures += getApertures(aFace)
531
+ else:
532
+ topHorizontalFaces.append(aFace)
533
+ topHorizontalApertures += getApertures(aFace)
534
+ else:
535
+ internalHorizontalFaces.append(aFace)
536
+ internalHorizontalApertures += getApertures(aFace)
537
+ elif aCode == 2:
538
+ if n == 1:
539
+ if abs(aFace.Centroid().Z() - zMax) < tolerance:
540
+ topHorizontalFaces.append(aFace)
541
+ topHorizontalApertures += getApertures(aFace)
542
+ else:
543
+ bottomHorizontalFaces.append(aFace)
544
+ bottomHorizontalApertures += getApertures(aFace)
545
+ else:
546
+ internalHorizontalFaces.append(aFace)
547
+ internalHorizontalApertures += getApertures(aFace)
548
+ elif aCode == 3:
549
+ if n == 1:
550
+ externalInclinedFaces.append(aFace)
551
+ externalInclinedApertures += getApertures(aFace)
552
+ else:
553
+ internalInclinedFaces.append(aFace)
554
+ internalInclinedApertures += getApertures(aFace)
555
+
556
+ cells = Topology.Cells(cellComplex)
557
+ d = {
558
+ "cells" : cells,
559
+ "externalVerticalFaces" : externalVerticalFaces,
560
+ "internalVerticalFaces" : internalVerticalFaces,
561
+ "topHorizontalFaces" : topHorizontalFaces,
562
+ "bottomHorizontalFaces" : bottomHorizontalFaces,
563
+ "internalHorizontalFaces" : internalHorizontalFaces,
564
+ "externalInclinedFaces" : externalInclinedFaces,
565
+ "internalInclinedFaces" : internalInclinedFaces,
566
+ "externalVerticalApertures" : externalVerticalApertures,
567
+ "internalVerticalApertures" : internalVerticalApertures,
568
+ "topHorizontalApertures" : topHorizontalApertures,
569
+ "bottomHorizontalApertures" : bottomHorizontalApertures,
570
+ "internalHorizontalApertures" : internalHorizontalApertures,
571
+ "externalInclinedApertures" : externalInclinedApertures,
572
+ "internalInclinedApertures" : internalInclinedApertures
573
+ }
574
+ return d
575
+
576
+ @staticmethod
577
+ def Delaunay(vertices: list = None, tolerance: float = 0.0001) -> topologic.CellComplex:
578
+ """
579
+ Triangulates the input vertices based on the Delaunay method. See https://en.wikipedia.org/wiki/Delaunay_triangulation.
580
+
581
+ Parameters
582
+ ----------
583
+ vertices: list , optional
584
+ The input list of vertices to use for delaunay triangulation. If set to None, the algorithm uses the vertices of the input cell parameter.
585
+ if both are set to none, a unit cube centered around the origin is used.
586
+ tolerance : float , optional
587
+ the desired tolerance. The default is 0.0001.
588
+
589
+ Returns
590
+ -------
591
+ topologic.CellComplex
592
+ The created delaunay cellComplex.
593
+
594
+ """
595
+ from topologicpy.Vertex import Vertex
596
+ from topologicpy.Face import Face
597
+ from topologicpy.Cell import Cell
598
+ from topologicpy.Cluster import Cluster
599
+ from topologicpy.Topology import Topology
600
+ from scipy.spatial import Delaunay as SCIDelaunay
601
+ import numpy as np
602
+
603
+ if not isinstance(vertices, list):
604
+ cell = Cell.Prism()
605
+ vertices = Topology.Vertices(cell)
606
+
607
+ vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
608
+ if len(vertices) < 3:
609
+ print("CellComplex/Delaunay - Error: The input vertices parameter does not contain enough valid vertices. Returning None.")
610
+ return None
611
+ # Get the vertices of the input cell
612
+ points = np.array([Vertex.Coordinates(v) for v in vertices])
613
+ # Compute Delaunay triangulation
614
+ triangulation = SCIDelaunay(points, furthest_site=False)
615
+
616
+ faces = []
617
+ for simplex in triangulation.simplices:
618
+ tetrahedron_vertices = points[simplex]
619
+ verts = [Vertex.ByCoordinates(list(coord)) for coord in tetrahedron_vertices]
620
+ tri1 = [verts[0], verts[1], verts[2], verts[0]]
621
+ tri2 = [verts[0], verts[2], verts[3], verts[0]]
622
+ tri3 = [verts[0], verts[1], verts[3], verts[0]]
623
+ tri4 = [verts[1], verts[2], verts[3], verts[1]]
624
+ f1 = Face.ByVertices(tri1)
625
+ f2 = Face.ByVertices(tri2)
626
+ f3 = Face.ByVertices(tri3)
627
+ f4 = Face.ByVertices(tri4)
628
+ faces.append(f1)
629
+ faces.append(f2)
630
+ faces.append(f3)
631
+ faces.append(f4)
632
+ cc = Topology.RemoveCoplanarFaces(CellComplex.ByFaces(faces, tolerance=tolerance))
633
+ faces = [Topology.RemoveCollinearEdges(f) for f in Topology.Faces(cc)]
634
+ cc = CellComplex.ByFaces(faces)
635
+ return cc
636
+
637
+ @staticmethod
638
+ def Edges(cellComplex: topologic.CellComplex) -> list:
639
+ """
640
+ Returns the edges of the input cellComplex.
641
+
642
+ Parameters
643
+ ----------
644
+ cellComplex : topologic.CellComplex
645
+ The input cellComplex.
646
+
647
+ Returns
648
+ -------
649
+ list
650
+ The list of edges.
651
+
652
+ """
653
+ if not isinstance(cellComplex, topologic.CellComplex):
654
+ print("CellComplex.Edges - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
655
+ return None
656
+ edges = []
657
+ _ = cellComplex.Edges(None, edges)
658
+ return edges
659
+
660
+ @staticmethod
661
+ def ExternalBoundary(cellComplex: topologic.CellComplex) -> topologic.Cell:
662
+ """
663
+ Returns the external boundary (cell) of the input cellComplex.
664
+
665
+ Parameters
666
+ ----------
667
+ cellComplex : topologic.CellComplex
668
+ The input cellComplex.
669
+
670
+ Returns
671
+ -------
672
+ topologic.Cell
673
+ The external boundary of the input cellComplex.
674
+
675
+ """
676
+ return cellComplex.ExternalBoundary()
677
+
678
+ @staticmethod
679
+ def ExternalFaces(cellComplex: topologic.CellComplex) -> list:
680
+ """
681
+ Returns the external faces of the input cellComplex.
682
+
683
+ Parameters
684
+ ----------
685
+ cellComplex : topologic.CellComplex
686
+ The input cellComplex.
687
+
688
+ Returns
689
+ -------
690
+ list
691
+ The list of external faces.
692
+
693
+ """
694
+ from topologicpy.Cell import Cell
695
+ cell = cellComplex.ExternalBoundary()
696
+ return Cell.Faces(cell)
697
+
698
+ @staticmethod
699
+ def Faces(cellComplex: topologic.CellComplex) -> list:
700
+ """
701
+ Returns the faces of the input cellComplex.
702
+
703
+ Parameters
704
+ ----------
705
+ cellComplex : topologic.CellComplex
706
+ The input cellComplex.
707
+
708
+ Returns
709
+ -------
710
+ list
711
+ The list of faces.
712
+
713
+ """
714
+ if not isinstance(cellComplex, topologic.CellComplex):
715
+ print("CellComplex.Faces - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
716
+ return None
717
+ faces = []
718
+ _ = cellComplex.Faces(None, faces)
719
+ return faces
720
+
721
+ @staticmethod
722
+ def InternalFaces(cellComplex: topologic.CellComplex) -> list:
723
+ """
724
+ Returns the internal boundaries (faces) of the input cellComplex.
725
+
726
+ Parameters
727
+ ----------
728
+ cellComplex : topologic.CellComplex
729
+ The input cellComplex.
730
+
731
+ Returns
732
+ -------
733
+ list
734
+ The list of internal faces of the input cellComplex.
735
+
736
+ """
737
+ faces = []
738
+ _ = cellComplex.InternalBoundaries(faces)
739
+ return faces
740
+
741
+ @staticmethod
742
+ def NonManifoldFaces(cellComplex: topologic.CellComplex) -> list:
743
+ """
744
+ Returns the non-manifold faces of the input cellComplex.
745
+
746
+ Parameters
747
+ ----------
748
+ cellComplex : topologic.CellComplex
749
+ The input cellComplex.
750
+
751
+ Returns
752
+ -------
753
+ list
754
+ The list of non-manifold faces of the input cellComplex.
755
+
756
+ """
757
+ faces = []
758
+ _ = cellComplex.NonManifoldFaces(faces)
759
+ return faces
760
+
761
+ @staticmethod
762
+ def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
763
+ direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.CellComplex:
764
+ """
765
+ Description
766
+ ----------
767
+ Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.
768
+
769
+ Parameters
770
+ ----------
771
+ origin : topologic.Vertex , optional
772
+ The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
773
+ radius : float , optional
774
+ The radius of the octahedron's circumscribed sphere. The default is 0.5.
775
+ direction : list , optional
776
+ The vector representing the up direction of the octahedron. The default is [0, 0, 1].
777
+ placement : str , optional
778
+ The description of the placement of the origin of the octahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
779
+ tolerance : float , optional
780
+ The desired tolerance. The default is 0.0001.
781
+
782
+ Returns
783
+ -------
784
+ topologic.CellComplex
785
+ The created octahedron.
786
+
787
+ """
788
+
789
+ from topologicpy.Vertex import Vertex
790
+ from topologicpy.Face import Face
791
+ from topologicpy.Topology import Topology
792
+
793
+ if not origin:
794
+ origin = Vertex.ByCoordinates(0, 0, 0)
795
+ if not isinstance(origin, topologic.Vertex):
796
+ print("CellComplex.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
797
+ return None
798
+
799
+ vb1 = Vertex.ByCoordinates(-0.5,0,0)
800
+ vb2 = Vertex.ByCoordinates(0,-0.5,0)
801
+ vb3 = Vertex.ByCoordinates(0.5,0,0)
802
+ vb4 = Vertex.ByCoordinates(0,0.5,0)
803
+ top = Vertex.ByCoordinates(0, 0, 0.5)
804
+ bottom = Vertex.ByCoordinates(0, 0, -0.5)
805
+ f1 = Face.ByVertices([top,vb1,vb2])
806
+ f2 = Face.ByVertices([top,vb2,vb3])
807
+ f3 = Face.ByVertices([top,vb3,vb4])
808
+ f4 = Face.ByVertices([top,vb4,vb1])
809
+ f5 = Face.ByVertices([bottom,vb1,vb2])
810
+ f6 = Face.ByVertices([bottom,vb2,vb3])
811
+ f7 = Face.ByVertices([bottom,vb3,vb4])
812
+ f8 = Face.ByVertices([bottom,vb4,vb1])
813
+ f9 = Face.ByVertices([vb1,vb2,vb3,vb4])
814
+
815
+ octahedron = CellComplex.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9], tolerance=tolerance)
816
+ octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
817
+ if placement == "bottom":
818
+ octahedron = Topology.Translate(octahedron, 0, 0, radius)
819
+ elif placement == "lowerleft":
820
+ octahedron = Topology.Translate(octahedron, radius, radius, radius)
821
+ octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
822
+ octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
823
+ return octahedron
824
+
825
+ @staticmethod
826
+ def Prism(origin: topologic.Vertex = None,
827
+ width: float = 1.0, length: float = 1.0, height: float = 1.0,
828
+ uSides: int = 2, vSides: int = 2, wSides: int = 2,
829
+ direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
830
+ """
831
+ Creates a prismatic cellComplex with internal cells.
832
+
833
+ Parameters
834
+ ----------
835
+ origin : topologic.Vertex , optional
836
+ The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
837
+ width : float , optional
838
+ The width of the prism. The default is 1.
839
+ length : float , optional
840
+ The length of the prism. The default is 1.
841
+ height : float , optional
842
+ The height of the prism.
843
+ uSides : int , optional
844
+ The number of sides along the width. The default is 1.
845
+ vSides : int , optional
846
+ The number of sides along the length. The default is 1.
847
+ wSides : int , optional
848
+ The number of sides along the height. The default is 1.
849
+ direction : list , optional
850
+ The vector representing the up direction of the prism. The default is [0, 0, 1].
851
+ placement : str , optional
852
+ The description of the placement of the origin of the prism. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
853
+ tolerance : float , optional
854
+ The desired tolerance. The default is 0.0001.
855
+
856
+ Returns
857
+ -------
858
+ topologic.CellComplex
859
+ The created prism.
860
+
861
+ """
862
+ from topologicpy.Vertex import Vertex
863
+ from topologicpy.Face import Face
864
+ from topologicpy.Cell import Cell
865
+ from topologicpy.Cluster import Cluster
866
+ from topologicpy.Topology import Topology
867
+
868
+ def bb(topology):
869
+ vertices = []
870
+ _ = topology.Vertices(None, vertices)
871
+ x = []
872
+ y = []
873
+ z = []
874
+ for aVertex in vertices:
875
+ x.append(aVertex.X())
876
+ y.append(aVertex.Y())
877
+ z.append(aVertex.Z())
878
+ minX = min(x)
879
+ minY = min(y)
880
+ minZ = min(z)
881
+ maxX = max(x)
882
+ maxY = max(y)
883
+ maxZ = max(z)
884
+ return [minX, minY, minZ, maxX, maxY, maxZ]
885
+
886
+ def slice(topology, uSides, vSides, wSides):
887
+ minX, minY, minZ, maxX, maxY, maxZ = bb(topology)
888
+ centroid = Vertex.ByCoordinates(minX+(maxX-minX)*0.5, minY+(maxY-minY)*0.5, minZ+(maxZ-minZ)*0.5)
889
+ wOrigin = Vertex.ByCoordinates(Vertex.X(centroid), Vertex.Y(centroid), minZ)
890
+ wFace = Face.Rectangle(origin=wOrigin, width=(maxX-minX)*1.1, length=(maxY-minY)*1.1)
891
+ wFaces = []
892
+ wOffset = (maxZ-minZ)/wSides
893
+ for i in range(wSides-1):
894
+ wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
895
+ uOrigin = Vertex.ByCoordinates(minX, Vertex.Y(centroid), Vertex.Z(centroid))
896
+ uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-minZ)*1.1, length=(maxY-minY)*1.1, direction=[1,0,0])
897
+ uFaces = []
898
+ uOffset = (maxX-minX)/uSides
899
+ for i in range(uSides-1):
900
+ uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
901
+ vOrigin = Vertex.ByCoordinates(Vertex.X(centroid), minY, Vertex.Z(centroid))
902
+ vFace = Face.Rectangle(origin=vOrigin, width=(maxX-minX)*1.1, length=(maxZ-minZ)*1.1, direction=[0,1,0])
903
+ vFaces = []
904
+ vOffset = (maxY-minY)/vSides
905
+ for i in range(vSides-1):
906
+ vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
907
+ all_faces = uFaces+vFaces+wFaces
908
+ if len(all_faces) > 0:
909
+ f_clus = Cluster.ByTopologies(uFaces+vFaces+wFaces)
910
+ return Topology.Slice(topology, f_clus, tolerance=tolerance)
911
+ else:
912
+ return topologic.CellComplex.ByCells([topology])
913
+ if not isinstance(origin, topologic.Vertex):
914
+ origin = Vertex.ByCoordinates(0, 0, 0)
915
+
916
+ c = Cell.Prism(origin=origin, width=width, length=length, height=height, uSides=1, vSides=1, wSides=1, placement=placement, tolerance=tolerance)
917
+ prism = slice(c, uSides=uSides, vSides=vSides, wSides=wSides)
918
+ if prism:
919
+ prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction)
920
+ return prism
921
+ else:
922
+ print("CellComplex.Prism - Error: Could not create a prism. Returning None.")
923
+ return None
924
+
925
+ @staticmethod
926
+ def RemoveCollinearEdges(cellComplex: topologic.CellComplex, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
927
+ """
928
+ Removes any collinear edges in the input cellComplex.
929
+
930
+ Parameters
931
+ ----------
932
+ cellComplex : topologic.CellComplex
933
+ The input cellComplex.
934
+ angTolerance : float , optional
935
+ The desired angular tolerance. The default is 0.1.
936
+ tolerance : float , optional
937
+ The desired tolerance. The default is 0.0001.
938
+
939
+ Returns
940
+ -------
941
+ topologic.CellComplex
942
+ The created cellComplex without any collinear edges.
943
+
944
+ """
945
+ from topologicpy.Cell import Cell
946
+
947
+ if not isinstance(cellComplex, topologic.CellComplex):
948
+ print("CellComplex.RemoveCollinearEdges - Error: The input cellComplex parameter is not a valid cellComplex. Returning None.")
949
+ return None
950
+ cells = CellComplex.Cells(cellComplex)
951
+ clean_cells = []
952
+ for cell in cells:
953
+ clean_cells.append(Cell.RemoveCollinearEdges(cell, angTolerance=angTolerance, tolerance=tolerance))
954
+ return CellComplex.ByCells(clean_cells, tolerance=tolerance)
955
+
956
+ @staticmethod
957
+ def Shells(cellComplex: topologic.CellComplex) -> list:
958
+ """
959
+ Returns the shells of the input cellComplex.
960
+
961
+ Parameters
962
+ ----------
963
+ cellComplex : topologic.CellComplex
964
+ The input cellComplex.
965
+
966
+ Returns
967
+ -------
968
+ list
969
+ The list of shells.
970
+
971
+ """
972
+ if not isinstance(cellComplex, topologic.CellComplex):
973
+ print("CellComplex.Shells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
974
+ return None
975
+ shells = []
976
+ _ = cellComplex.Shells(None, shells)
977
+ return shells
978
+
979
+ @staticmethod
980
+ def Vertices(cellComplex: topologic.CellComplex) -> list:
981
+ """
982
+ Returns the vertices of the input cellComplex.
983
+
984
+ Parameters
985
+ ----------
986
+ cellComplex : topologic.CellComplex
987
+ The input cellComplex.
988
+
989
+ Returns
990
+ -------
991
+ list
992
+ The list of vertices.
993
+
994
+ """
995
+ if not isinstance(cellComplex, topologic.CellComplex):
996
+ print("CellComplex.Vertices - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
997
+ return None
998
+ vertices = []
999
+ _ = cellComplex.Vertices(None, vertices)
1000
+ return vertices
1001
+
1002
+ @staticmethod
1003
+ def Volume(cellComplex: topologic.CellComplex, mantissa: int = 6) -> float:
1004
+ """
1005
+ Returns the volume of the input cellComplex.
1006
+
1007
+ Parameters
1008
+ ----------
1009
+ cellComplex : topologic.CellComplex
1010
+ The input cellComplex.
1011
+ manitssa: int , optional
1012
+ The desired length of the mantissa. The default is 6.
1013
+
1014
+ Returns
1015
+ -------
1016
+ float
1017
+ The volume of the input cellComplex.
1018
+
1019
+ """
1020
+ from topologicpy.Cell import Cell
1021
+ if not isinstance(cellComplex, topologic.CellComplex):
1022
+ print("CellComplex.Volume - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
1023
+ return None
1024
+ cells = CellComplex.Cells(cellComplex)
1025
+ volume = 0
1026
+ for cell in cells:
1027
+ volume = Cell.Volume(cell)
1028
+ if not volume == None:
1029
+ volume += Cell.Volume(cell)
1030
+ return round(volume, mantissa)
1031
+
1032
+ @staticmethod
1033
+ def Voronoi(vertices: list = None, cell: topologic.Cell = None, tolerance: float = 0.0001):
1034
+ """
1035
+ Partitions the input cell based on the Voronoi method. See https://en.wikipedia.org/wiki/Voronoi_diagram.
1036
+
1037
+ Parameters
1038
+ ----------
1039
+ vertices: list , optional
1040
+ The input list of vertices to use for voronoi partitioning. If set to None, the algorithm uses the vertices of the input cell parameter.
1041
+ if both are set to none, a unit cube centered around the origin is used.
1042
+ cell : topologic.Cell , optional
1043
+ The input bounding cell. If set to None, an axes-aligned bounding cell is created from the list of vertices. The default is None.
1044
+ tolerance : float , optional
1045
+ the desired tolerance. The default is 0.0001.
1046
+
1047
+
1048
+ Returns
1049
+ -------
1050
+ topologic.CellComplex
1051
+ The created voronoi cellComplex.
1052
+
1053
+ """
1054
+ from topologicpy.Vertex import Vertex
1055
+ from topologicpy.Face import Face
1056
+ from topologicpy.Cell import Cell
1057
+ from topologicpy.Cluster import Cluster
1058
+ from topologicpy.Topology import Topology
1059
+ from scipy.spatial import Voronoi as SCIVoronoi
1060
+ import numpy as np
1061
+
1062
+ def fracture_with_voronoi(points):
1063
+ # Compute Voronoi tessellation
1064
+ vor = SCIVoronoi(points)
1065
+ verts = []
1066
+ faces = []
1067
+ for v in vor.vertices:
1068
+ verts.append(Vertex.ByCoordinates(list(v)))
1069
+ for region in vor.ridge_vertices:
1070
+ temp_list = []
1071
+ if -1 not in region and len(region) > 0:
1072
+ for item in region:
1073
+ temp_list.append(verts[item])
1074
+ f = Face.ByVertices(temp_list)
1075
+ if isinstance(f, topologic.Face):
1076
+ faces.append(f)
1077
+ if len(faces) < 1:
1078
+ return None
1079
+ return Cluster.ByTopologies(faces)
1080
+
1081
+ if cell == None:
1082
+ if not isinstance(vertices, list):
1083
+ cell = Cell.Prism(uSides=2, vSides=2, wSides=2)
1084
+ vertices = Topology.Vertices(cell)
1085
+ vertices.append(Vertex.Origin())
1086
+ else:
1087
+ vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
1088
+ if len(vertices) < 1:
1089
+ print("CellComplex.Voronoi - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
1090
+ return None
1091
+ cell = Topology.BoundingBox(Cluster.ByTopologies(vertices))
1092
+ if not isinstance(vertices, list):
1093
+ if not isinstance(cell, topologic.Cell):
1094
+ cell = Cell.Prism()
1095
+ vertices = Topology.Vertices(cell)
1096
+ else:
1097
+ vertices = Topology.Vertices(cell)
1098
+ else:
1099
+ vertices += Topology.Vertices(cell)
1100
+ vertices = [v for v in vertices if (Vertex.IsInternal(v, cell) or not Vertex.Index(v, Topology.Vertices(cell)) == None)]
1101
+ if len(vertices) < 1:
1102
+ print("CellComplex.Voronoi - Error: The input vertices parame ter does not contain any vertices that are inside the input cell parameter. Returning None.")
1103
+ return None
1104
+ voronoi_points = np.array([Vertex.Coordinates(v) for v in vertices])
1105
+ cluster = fracture_with_voronoi(voronoi_points)
1106
+ if cluster == None:
1107
+ print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
1108
+ return None
1109
+ cellComplex = Topology.Slice(cell, cluster)
1110
+ if not isinstance(cellComplex, topologic.CellComplex):
1111
+ print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
1112
+ return None
1113
+ return cellComplex
1114
+
1115
+ @staticmethod
1116
+ def Wires(cellComplex: topologic.CellComplex) -> list:
1117
+ """
1118
+ Returns the wires of the input cellComplex.
1119
+
1120
+ Parameters
1121
+ ----------
1122
+ cellComplex : topologic.CellComplex
1123
+ The input cellComplex.
1124
+
1125
+ Returns
1126
+ -------
1127
+ list
1128
+ The list of wires.
1129
+
1130
+ """
1131
+ if not isinstance(cellComplex, topologic.CellComplex):
1132
+ print("CellComplex.Wires - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
1133
+ return None
1134
+ wires = []
1135
+ _ = cellComplex.Wires(None, wires)
1136
+ return wires
1137
+