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.
- topologicpy/Aperture.py +72 -72
- topologicpy/Cell.py +2169 -2169
- topologicpy/CellComplex.py +1137 -1137
- topologicpy/Cluster.py +1288 -1280
- topologicpy/Color.py +423 -423
- topologicpy/Context.py +79 -79
- topologicpy/DGL.py +3213 -3240
- topologicpy/Dictionary.py +698 -698
- topologicpy/Edge.py +1187 -1187
- topologicpy/EnergyModel.py +1180 -1152
- topologicpy/Face.py +2141 -2141
- topologicpy/Graph.py +7768 -7768
- topologicpy/Grid.py +353 -353
- topologicpy/Helper.py +507 -507
- topologicpy/Honeybee.py +461 -461
- topologicpy/Matrix.py +271 -271
- topologicpy/Neo4j.py +521 -521
- topologicpy/Plotly.py +2 -2
- topologicpy/Polyskel.py +541 -541
- topologicpy/Shell.py +1768 -1768
- topologicpy/Speckle.py +508 -508
- topologicpy/Topology.py +7060 -7002
- topologicpy/Vector.py +905 -905
- topologicpy/Vertex.py +1585 -1585
- topologicpy/Wire.py +3050 -3050
- topologicpy/__init__.py +22 -38
- topologicpy/version.py +1 -0
- {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/LICENSE +661 -704
- topologicpy-6.0.0.dist-info/METADATA +751 -0
- topologicpy-6.0.0.dist-info/RECORD +32 -0
- topologicpy/bin/linux/topologic/__init__.py +0 -2
- topologicpy/bin/linux/topologic/libTKBO-6bdf205d.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKBRep-2960a069.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKBool-c44b74bd.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKFillet-9a670ba0.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKG2d-8f31849e.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKG3d-4c6bce57.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKMath-72572fa8.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKMesh-2a060427.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKOffset-6cab68ff.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKPrim-eb1262b3.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic/libgcc_s-32c1665e.so.1 +0 -0
- topologicpy/bin/linux/topologic/libstdc++-672d7b41.so.6.0.30 +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-310-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-311-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-38-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic/topologic.cpython-39-x86_64-linux-gnu.so +0 -0
- topologicpy/bin/linux/topologic.libs/libTKBO-6bdf205d.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKBRep-2960a069.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKBool-c44b74bd.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKFillet-9a670ba0.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKG2d-8f31849e.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKG3d-4c6bce57.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKMath-72572fa8.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKMesh-2a060427.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKOffset-6cab68ff.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKPrim-eb1262b3.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
- topologicpy/bin/linux/topologic.libs/libgcc_s-32c1665e.so.1 +0 -0
- topologicpy/bin/linux/topologic.libs/libstdc++-672d7b41.so.6.0.30 +0 -0
- topologicpy/bin/macos/topologic/__init__.py +0 -2
- topologicpy/bin/windows/topologic/TKBO-f6b191de.dll +0 -0
- topologicpy/bin/windows/topologic/TKBRep-e56a600e.dll +0 -0
- topologicpy/bin/windows/topologic/TKBool-7b8d47ae.dll +0 -0
- topologicpy/bin/windows/topologic/TKFillet-0ddbf0a8.dll +0 -0
- topologicpy/bin/windows/topologic/TKG2d-2e2dee3d.dll +0 -0
- topologicpy/bin/windows/topologic/TKG3d-6674513d.dll +0 -0
- topologicpy/bin/windows/topologic/TKGeomAlgo-d240e370.dll +0 -0
- topologicpy/bin/windows/topologic/TKGeomBase-df87aba5.dll +0 -0
- topologicpy/bin/windows/topologic/TKMath-45bd625a.dll +0 -0
- topologicpy/bin/windows/topologic/TKMesh-d6e826b1.dll +0 -0
- topologicpy/bin/windows/topologic/TKOffset-79b9cc94.dll +0 -0
- topologicpy/bin/windows/topologic/TKPrim-aa430a86.dll +0 -0
- topologicpy/bin/windows/topologic/TKShHealing-bb48be89.dll +0 -0
- topologicpy/bin/windows/topologic/TKTopAlgo-7d0d1e22.dll +0 -0
- topologicpy/bin/windows/topologic/TKernel-08c8cfbb.dll +0 -0
- topologicpy/bin/windows/topologic/__init__.py +0 -2
- topologicpy/bin/windows/topologic/topologic.cp310-win_amd64.pyd +0 -0
- topologicpy/bin/windows/topologic/topologic.cp311-win_amd64.pyd +0 -0
- topologicpy/bin/windows/topologic/topologic.cp38-win_amd64.pyd +0 -0
- topologicpy/bin/windows/topologic/topologic.cp39-win_amd64.pyd +0 -0
- topologicpy-0.5.9.dist-info/METADATA +0 -86
- topologicpy-0.5.9.dist-info/RECORD +0 -91
- {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/WHEEL +0 -0
- {topologicpy-0.5.9.dist-info → topologicpy-6.0.0.dist-info}/top_level.txt +0 -0
topologicpy/CellComplex.py
CHANGED
@@ -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
|
+
|