topologicpy 0.5.8__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 -393
- topologicpy/Context.py +79 -79
- topologicpy/DGL.py +3213 -3136
- topologicpy/Dictionary.py +698 -695
- topologicpy/Edge.py +1187 -1187
- topologicpy/EnergyModel.py +1180 -1171
- topologicpy/Face.py +2141 -2141
- topologicpy/Graph.py +7768 -7700
- 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 -6988
- 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.8.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.8.dist-info/METADATA +0 -96
- topologicpy-0.5.8.dist-info/RECORD +0 -91
- {topologicpy-0.5.8.dist-info → topologicpy-6.0.0.dist-info}/WHEEL +0 -0
- {topologicpy-0.5.8.dist-info → topologicpy-6.0.0.dist-info}/top_level.txt +0 -0
topologicpy/Cell.py
CHANGED
@@ -1,2169 +1,2169 @@
|
|
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
|
-
from topologicpy.Wire import Wire
|
19
|
-
from topologicpy.Topology import Topology
|
20
|
-
import math
|
21
|
-
|
22
|
-
class Cell(Topology):
|
23
|
-
@staticmethod
|
24
|
-
def Area(cell: topologic.Cell, mantissa: int = 6) -> float:
|
25
|
-
"""
|
26
|
-
Returns the surface area of the input cell.
|
27
|
-
|
28
|
-
Parameters
|
29
|
-
----------
|
30
|
-
cell : topologic.Cell
|
31
|
-
The cell.
|
32
|
-
mantissa : int , optional
|
33
|
-
The desired length of the mantissa. The default is 6.
|
34
|
-
|
35
|
-
Returns
|
36
|
-
-------
|
37
|
-
float
|
38
|
-
The surface area of the input cell.
|
39
|
-
|
40
|
-
"""
|
41
|
-
from topologicpy.Face import Face
|
42
|
-
|
43
|
-
faces = []
|
44
|
-
_ = cell.Faces(None, faces)
|
45
|
-
area = 0.0
|
46
|
-
for aFace in faces:
|
47
|
-
area = area + Face.Area(aFace)
|
48
|
-
return round(area, mantissa)
|
49
|
-
|
50
|
-
@staticmethod
|
51
|
-
def Box(origin: topologic.Vertex = None,
|
52
|
-
width: float = 1, length: float = 1, height: float = 1,
|
53
|
-
uSides: int = 1, vSides: int = 1, wSides: int = 1,
|
54
|
-
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
55
|
-
"""
|
56
|
-
Creates a box.
|
57
|
-
|
58
|
-
Parameters
|
59
|
-
----------
|
60
|
-
origin : topologic.Vertex , optional
|
61
|
-
The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
|
62
|
-
width : float , optional
|
63
|
-
The width of the box. The default is 1.
|
64
|
-
length : float , optional
|
65
|
-
The length of the box. The default is 1.
|
66
|
-
height : float , optional
|
67
|
-
The height of the box.
|
68
|
-
uSides : int , optional
|
69
|
-
The number of sides along the width. The default is 1.
|
70
|
-
vSides : int , optional
|
71
|
-
The number of sides along the length. The default is 1.
|
72
|
-
wSides : int , optional
|
73
|
-
The number of sides along the height. The default is 1.
|
74
|
-
direction : list , optional
|
75
|
-
The vector representing the up direction of the box. The default is [0, 0, 1].
|
76
|
-
placement : str , optional
|
77
|
-
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".
|
78
|
-
tolerance : float , optional
|
79
|
-
The desired tolerance. The default is 0.0001.
|
80
|
-
|
81
|
-
Returns
|
82
|
-
-------
|
83
|
-
topologic.Cell
|
84
|
-
The created box.
|
85
|
-
|
86
|
-
"""
|
87
|
-
return Cell.Prism(origin=origin, width=width, length=length, height=height,
|
88
|
-
uSides=uSides, vSides=vSides, wSides=wSides,
|
89
|
-
direction=direction, placement=placement, tolerance=tolerance)
|
90
|
-
|
91
|
-
@staticmethod
|
92
|
-
def ByFaces(faces: list, planarize: bool = False, tolerance: float = 0.0001, silent=False) -> topologic.Cell:
|
93
|
-
"""
|
94
|
-
Creates a cell from the input list of faces.
|
95
|
-
|
96
|
-
Parameters
|
97
|
-
----------
|
98
|
-
faces : list
|
99
|
-
The input list of faces.
|
100
|
-
planarize : bool, optional
|
101
|
-
If set to True, the input faces are planarized before building the cell. Otherwise, they are not. The default is False.
|
102
|
-
tolerance : float , optional
|
103
|
-
The desired tolerance. The default is 0.0001.
|
104
|
-
silent : bool , optional
|
105
|
-
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
|
106
|
-
|
107
|
-
Returns
|
108
|
-
-------
|
109
|
-
topologic.Cell
|
110
|
-
The created cell.
|
111
|
-
|
112
|
-
"""
|
113
|
-
from topologicpy.Vertex import Vertex
|
114
|
-
from topologicpy.Wire import Wire
|
115
|
-
from topologicpy.Face import Face
|
116
|
-
from topologicpy.Topology import Topology
|
117
|
-
|
118
|
-
if not isinstance(faces, list):
|
119
|
-
if not silent:
|
120
|
-
print("Cell.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
|
121
|
-
return None
|
122
|
-
faceList = [x for x in faces if isinstance(x, topologic.Face)]
|
123
|
-
if len(faceList) < 1:
|
124
|
-
if not silent:
|
125
|
-
print("Cell.ByFaces - Error: The input faces parameter does not contain valid faces. Returning None.")
|
126
|
-
return None
|
127
|
-
# Try the default method
|
128
|
-
cell = topologic.Cell.ByFaces(faceList, tolerance)
|
129
|
-
if isinstance(cell, topologic.Cell):
|
130
|
-
return cell
|
131
|
-
|
132
|
-
# Fuse all the vertices first and rebuild the faces
|
133
|
-
all_vertices = []
|
134
|
-
wires = []
|
135
|
-
for f in faceList:
|
136
|
-
w = Face.Wire(f)
|
137
|
-
wires.append(w)
|
138
|
-
all_vertices += Topology.Vertices(w)
|
139
|
-
all_vertices = Vertex.Fuse(all_vertices, tolerance=tolerance)
|
140
|
-
new_faces = []
|
141
|
-
for w in wires:
|
142
|
-
face_vertices = []
|
143
|
-
for v in Topology.Vertices(w):
|
144
|
-
index = Vertex.Index(v, all_vertices, tolerance=tolerance)
|
145
|
-
if not index == None:
|
146
|
-
face_vertices.append(all_vertices[index])
|
147
|
-
new_w = Wire.ByVertices(face_vertices)
|
148
|
-
if isinstance(new_w, topologic.Wire):
|
149
|
-
new_f = Face.ByWire(new_w, silent=True)
|
150
|
-
if isinstance(new_f, topologic.Face):
|
151
|
-
new_faces.append(new_f)
|
152
|
-
elif isinstance(new_f, list):
|
153
|
-
new_faces += new_f
|
154
|
-
faceList = new_faces
|
155
|
-
planarizedList = []
|
156
|
-
enlargedList = []
|
157
|
-
if planarize:
|
158
|
-
planarizedList = [Face.Planarize(f, tolerance=tolerance) for f in faceList]
|
159
|
-
enlargedList = [Face.ByOffset(f, offset=-tolerance*10) for f in planarizedList]
|
160
|
-
cell = topologic.Cell.ByFaces(enlargedList, tolerance)
|
161
|
-
faceList = Topology.SubTopologies(cell, subTopologyType="face")
|
162
|
-
finalFaces = []
|
163
|
-
for f in faceList:
|
164
|
-
centroid = Topology.Centroid(f)
|
165
|
-
n = Face.Normal(f)
|
166
|
-
v = Topology.Translate(centroid, n[0]*0.01, n[1]*0.01, n[2]*0.01)
|
167
|
-
if not Vertex.IsInternal(v, cell):
|
168
|
-
finalFaces.append(f)
|
169
|
-
finalFinalFaces = []
|
170
|
-
for f in finalFaces:
|
171
|
-
vertices = Face.Vertices(f)
|
172
|
-
w = Wire.Cycles(Face.ExternalBoundary(f), maxVertices=len(vertices))[0]
|
173
|
-
f1 = Face.ByWire(w, tolerance=tolerance, silent=True)
|
174
|
-
if isinstance(f1, topologic.Face):
|
175
|
-
finalFinalFaces.append(f1)
|
176
|
-
elif isinstance(f1, list):
|
177
|
-
finalFinalFaces += f1
|
178
|
-
cell = topologic.Cell.ByFaces(finalFinalFaces, tolerance)
|
179
|
-
if cell == None:
|
180
|
-
if not silent:
|
181
|
-
print("Cell.ByFaces - Error: The operation failed. Returning None.")
|
182
|
-
return None
|
183
|
-
else:
|
184
|
-
return cell
|
185
|
-
else:
|
186
|
-
cell = topologic.Cell.ByFaces(faces, tolerance)
|
187
|
-
if cell == None:
|
188
|
-
if not silent:
|
189
|
-
print("Cell.ByFaces - Error: The operation failed. Returning None.")
|
190
|
-
return None
|
191
|
-
else:
|
192
|
-
return cell
|
193
|
-
@staticmethod
|
194
|
-
def ByOffset(cell: topologic.Cell, offset: float = 1.0, tolerance: float = 0.0001) -> topologic.Face:
|
195
|
-
"""
|
196
|
-
Creates an offset cell from the input cell.
|
197
|
-
|
198
|
-
Parameters
|
199
|
-
----------
|
200
|
-
cell : topologic.Cell
|
201
|
-
The input cell.
|
202
|
-
offset : float , optional
|
203
|
-
The desired offset distance. The default is 1.0.
|
204
|
-
tolerance : float , optional
|
205
|
-
The desired tolerance. The default is 0.0001.
|
206
|
-
|
207
|
-
Returns
|
208
|
-
-------
|
209
|
-
topologic.Topology
|
210
|
-
The created offset topology. WARNING: This method may fail to create a cell if the offset creates self-intersecting faces. Always check the type being returned by this method.
|
211
|
-
|
212
|
-
"""
|
213
|
-
from topologicpy.Face import Face
|
214
|
-
from topologicpy.Topology import Topology
|
215
|
-
from topologicpy.Vector import Vector
|
216
|
-
|
217
|
-
vertices = Topology.Vertices(cell)
|
218
|
-
new_vertices = []
|
219
|
-
for v in vertices:
|
220
|
-
faces = Topology.SuperTopologies(v, hostTopology=cell, topologyType="face")
|
221
|
-
normals = []
|
222
|
-
for face in faces:
|
223
|
-
normal = Vector.SetMagnitude(Face.Normal(face), offset)
|
224
|
-
normals.append(normal)
|
225
|
-
sum_normal = Vector.Sum(normals)
|
226
|
-
new_v = Topology.TranslateByDirectionDistance(v, direction=sum_normal, distance=Vector.Magnitude(sum_normal))
|
227
|
-
new_vertices.append(new_v)
|
228
|
-
new_cell = Topology.SelfMerge(Topology.ReplaceVertices(cell, Topology.Vertices(cell), new_vertices), tolerance=tolerance)
|
229
|
-
return new_cell
|
230
|
-
|
231
|
-
@staticmethod
|
232
|
-
def ByShell(shell: topologic.Shell, planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
233
|
-
"""
|
234
|
-
Creates a cell from the input shell.
|
235
|
-
|
236
|
-
Parameters
|
237
|
-
----------
|
238
|
-
shell : topologic.Shell
|
239
|
-
The input shell. The shell must be closed for this method to succeed.
|
240
|
-
planarize : bool, optional
|
241
|
-
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
|
242
|
-
tolerance : float , optional
|
243
|
-
The desired tolerance. The default is 0.0001.
|
244
|
-
|
245
|
-
Returns
|
246
|
-
-------
|
247
|
-
topologic.Cell
|
248
|
-
The created cell.
|
249
|
-
|
250
|
-
"""
|
251
|
-
from topologicpy.Topology import Topology
|
252
|
-
if not isinstance(shell, topologic.Shell):
|
253
|
-
print("Cell.ByShell - Error: The input shell parameter is not a valid topologic shell. Returning None.")
|
254
|
-
return None
|
255
|
-
faces = Topology.SubTopologies(shell, subTopologyType="face")
|
256
|
-
return Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance)
|
257
|
-
|
258
|
-
@staticmethod
|
259
|
-
def ByThickenedFace(face: topologic.Face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
|
260
|
-
planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
261
|
-
"""
|
262
|
-
Creates a cell by thickening the input face.
|
263
|
-
|
264
|
-
Parameters
|
265
|
-
----------
|
266
|
-
face : topologic.Face
|
267
|
-
The input face to be thickened.
|
268
|
-
thickness : float , optional
|
269
|
-
The desired thickness. The default is 1.0.
|
270
|
-
bothSides : bool
|
271
|
-
If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. The default is True.
|
272
|
-
reverse : bool
|
273
|
-
If True, the cell will be lofted in the opposite direction of the normal to the face. The default is False.
|
274
|
-
planarize : bool, optional
|
275
|
-
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
|
276
|
-
tolerance : float , optional
|
277
|
-
The desired tolerance. The default is 0.0001.
|
278
|
-
|
279
|
-
Returns
|
280
|
-
-------
|
281
|
-
topologic.Cell
|
282
|
-
The created cell.
|
283
|
-
|
284
|
-
"""
|
285
|
-
from topologicpy.Edge import Edge
|
286
|
-
from topologicpy.Face import Face
|
287
|
-
from topologicpy.Cluster import Cluster
|
288
|
-
from topologicpy.Topology import Topology
|
289
|
-
|
290
|
-
if not isinstance(face, topologic.Face):
|
291
|
-
print("Cell.ByThickenedFace - Error: The input face parameter is not a valid topologic face. Returning None.")
|
292
|
-
return None
|
293
|
-
if reverse == True and bothSides == False:
|
294
|
-
thickness = -thickness
|
295
|
-
faceNormal = Face.Normal(face)
|
296
|
-
if bothSides:
|
297
|
-
bottomFace = Topology.Translate(face, -faceNormal[0]*0.5*thickness, -faceNormal[1]*0.5*thickness, -faceNormal[2]*0.5*thickness)
|
298
|
-
topFace = Topology.Translate(face, faceNormal[0]*0.5*thickness, faceNormal[1]*0.5*thickness, faceNormal[2]*0.5*thickness)
|
299
|
-
else:
|
300
|
-
bottomFace = face
|
301
|
-
topFace = Topology.Translate(face, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
|
302
|
-
|
303
|
-
cellFaces = [bottomFace, topFace]
|
304
|
-
bottomEdges = []
|
305
|
-
_ = bottomFace.Edges(None, bottomEdges)
|
306
|
-
for bottomEdge in bottomEdges:
|
307
|
-
topEdge = Topology.Translate(bottomEdge, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
|
308
|
-
sideEdge1 = Edge.ByVertices([bottomEdge.StartVertex(), topEdge.StartVertex()], tolerance=tolerance, silent=True)
|
309
|
-
sideEdge2 = Edge.ByVertices([bottomEdge.EndVertex(), topEdge.EndVertex()], tolerance=tolerance, silent=True)
|
310
|
-
cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
|
311
|
-
cellFaces.append(Face.ByWire(cellWire, tolerance=tolerance))
|
312
|
-
return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
|
313
|
-
|
314
|
-
@staticmethod
|
315
|
-
def ByThickenedShell(shell: topologic.Shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
|
316
|
-
planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
317
|
-
"""
|
318
|
-
Creates a cell by thickening the input shell. The shell must be open.
|
319
|
-
|
320
|
-
Parameters
|
321
|
-
----------
|
322
|
-
shell : topologic.Shell
|
323
|
-
The input shell to be thickened.
|
324
|
-
thickness : float , optional
|
325
|
-
The desired thickness. The default is 1.0.
|
326
|
-
bothSides : bool
|
327
|
-
If True, the cell will be lofted to each side of the shell. Otherwise, it will be lofted along the input direction. The default is True.
|
328
|
-
reverse : bool
|
329
|
-
If True, the cell will be lofted along the opposite of the input direction. The default is False.
|
330
|
-
planarize : bool, optional
|
331
|
-
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
|
332
|
-
tolerance : float , optional
|
333
|
-
The desired tolerance. The default is 0.0001.
|
334
|
-
|
335
|
-
Returns
|
336
|
-
-------
|
337
|
-
topologic.Cell
|
338
|
-
The created cell.
|
339
|
-
|
340
|
-
"""
|
341
|
-
from topologicpy.Edge import Edge
|
342
|
-
from topologicpy.Wire import Wire
|
343
|
-
from topologicpy.Face import Face
|
344
|
-
from topologicpy.Shell import Shell
|
345
|
-
from topologicpy.Cluster import Cluster
|
346
|
-
from topologicpy.Topology import Topology
|
347
|
-
if not isinstance(shell, topologic.Shell):
|
348
|
-
print("Cell.ByThickenedShell - Error: The input shell parameter is not a valid topologic Shell. Returning None.")
|
349
|
-
return None
|
350
|
-
if reverse == True and bothSides == False:
|
351
|
-
thickness = -thickness
|
352
|
-
if bothSides:
|
353
|
-
bottomShell = Topology.Translate(shell, -direction[0]*0.5*thickness, -direction[1]*0.5*thickness, -direction[2]*0.5*thickness)
|
354
|
-
topShell = Topology.Translate(shell, direction[0]*0.5*thickness, direction[1]*0.5*thickness, direction[2]*0.5*thickness)
|
355
|
-
else:
|
356
|
-
bottomShell = shell
|
357
|
-
topShell = Topology.Translate(shell, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
|
358
|
-
cellFaces = Shell.Faces(bottomShell) + Shell.Faces(topShell)
|
359
|
-
bottomWire = Shell.ExternalBoundary(bottomShell, tolerance=tolerance)
|
360
|
-
bottomEdges = Wire.Edges(bottomWire)
|
361
|
-
for bottomEdge in bottomEdges:
|
362
|
-
topEdge = Topology.Translate(bottomEdge, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
|
363
|
-
sideEdge1 = Edge.ByVertices([Edge.StartVertex(bottomEdge), Edge.StartVertex(topEdge)], tolerance=tolerance, silent=True)
|
364
|
-
sideEdge2 = Edge.ByVertices([Edge.EndVertex(bottomEdge), Edge.EndVertex(topEdge)], tolerance=tolerance, silent=True)
|
365
|
-
cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
|
366
|
-
cellFace = Face.ByWire(cellWire, tolerance=tolerance)
|
367
|
-
cellFaces.append(cellFace)
|
368
|
-
return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
|
369
|
-
|
370
|
-
@staticmethod
|
371
|
-
def ByWires(wires: list, close: bool = False, triangulate: bool = True, planarize: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent=False) -> topologic.Cell:
|
372
|
-
"""
|
373
|
-
Creates a cell by lofting through the input list of wires.
|
374
|
-
|
375
|
-
Parameters
|
376
|
-
----------
|
377
|
-
wires : topologic.Wire
|
378
|
-
The input list of wires.
|
379
|
-
close : bool , optional
|
380
|
-
If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
|
381
|
-
triangulate : bool , optional
|
382
|
-
If set to True, the faces will be triangulated. The default is True.
|
383
|
-
mantissa : int , optional
|
384
|
-
The desired length of the mantissa. The default is 6.
|
385
|
-
tolerance : float , optional
|
386
|
-
The desired tolerance. The default is 0.0001.
|
387
|
-
silent : bool , optional
|
388
|
-
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
|
389
|
-
|
390
|
-
Raises
|
391
|
-
------
|
392
|
-
Exception
|
393
|
-
Raises an exception if the two wires in the list do not have the same number of edges.
|
394
|
-
|
395
|
-
Returns
|
396
|
-
-------
|
397
|
-
topologic.Cell
|
398
|
-
The created cell.
|
399
|
-
|
400
|
-
"""
|
401
|
-
from topologicpy.Edge import Edge
|
402
|
-
from topologicpy.Wire import Wire
|
403
|
-
from topologicpy.Face import Face
|
404
|
-
from topologicpy.Shell import Shell
|
405
|
-
from topologicpy.Topology import Topology
|
406
|
-
|
407
|
-
if not isinstance(wires, list):
|
408
|
-
if not silent:
|
409
|
-
print("Cell.ByWires - Error: The input wires parameter is not a valid list. Returning None.")
|
410
|
-
return None
|
411
|
-
wires = [w for w in wires if isinstance(w, topologic.Wire)]
|
412
|
-
if len(wires) < 2:
|
413
|
-
if not silent:
|
414
|
-
print("Cell.ByWires - Error: The input wires parameter contains less than two valid topologic wires. Returning None.")
|
415
|
-
return None
|
416
|
-
faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
|
417
|
-
if close == True:
|
418
|
-
faces.append(Face.ByWire(wires[0], tolerance=tolerance))
|
419
|
-
if triangulate == True:
|
420
|
-
triangles = []
|
421
|
-
for face in faces:
|
422
|
-
if len(Topology.Vertices(face)) > 3:
|
423
|
-
triangles += Face.Triangulate(face, tolerance=tolerance)
|
424
|
-
else:
|
425
|
-
triangles += [face]
|
426
|
-
faces = triangles
|
427
|
-
for i in range(len(wires)-1):
|
428
|
-
wire1 = wires[i]
|
429
|
-
wire2 = wires[i+1]
|
430
|
-
w1_edges = []
|
431
|
-
_ = wire1.Edges(None, w1_edges)
|
432
|
-
w2_edges = []
|
433
|
-
_ = wire2.Edges(None, w2_edges)
|
434
|
-
if len(w1_edges) != len(w2_edges):
|
435
|
-
if not silent:
|
436
|
-
print("Cell.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
|
437
|
-
return None
|
438
|
-
if triangulate == True:
|
439
|
-
for j in range (len(w1_edges)):
|
440
|
-
e1 = w1_edges[j]
|
441
|
-
e2 = w2_edges[j]
|
442
|
-
e3 = None
|
443
|
-
e4 = None
|
444
|
-
try:
|
445
|
-
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
446
|
-
except:
|
447
|
-
try:
|
448
|
-
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
449
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e4], tolerance=tolerance), tolerance=tolerance))
|
450
|
-
except:
|
451
|
-
pass
|
452
|
-
try:
|
453
|
-
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
454
|
-
except:
|
455
|
-
try:
|
456
|
-
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
457
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance))
|
458
|
-
except:
|
459
|
-
pass
|
460
|
-
if e3 and e4:
|
461
|
-
e5 = Edge.ByVertices([e1.StartVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
462
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
|
463
|
-
faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
|
464
|
-
else:
|
465
|
-
for j in range (len(w1_edges)):
|
466
|
-
e1 = w1_edges[j]
|
467
|
-
e2 = w2_edges[j]
|
468
|
-
e3 = None
|
469
|
-
e4 = None
|
470
|
-
try:
|
471
|
-
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
472
|
-
except:
|
473
|
-
try:
|
474
|
-
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
475
|
-
except:
|
476
|
-
pass
|
477
|
-
try:
|
478
|
-
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
479
|
-
except:
|
480
|
-
try:
|
481
|
-
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
482
|
-
except:
|
483
|
-
pass
|
484
|
-
if e3 and e4:
|
485
|
-
try:
|
486
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance))
|
487
|
-
except:
|
488
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance))
|
489
|
-
elif e3:
|
490
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
|
491
|
-
elif e4:
|
492
|
-
faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
|
493
|
-
cell = Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance, silent=silent)
|
494
|
-
if not cell:
|
495
|
-
shell = Shell.ByFaces(faces, tolerance=tolerance)
|
496
|
-
if isinstance(shell, topologic.Shell):
|
497
|
-
geom = Topology.Geometry(shell, mantissa=mantissa)
|
498
|
-
cell = Topology.ByGeometry(geom['vertices'], geom['edges'], geom['faces'])
|
499
|
-
if not isinstance(cell, topologic.Cell):
|
500
|
-
print("Cell.ByWires - Error: Could not create a cell. Returning None.")
|
501
|
-
return None
|
502
|
-
return cell
|
503
|
-
|
504
|
-
@staticmethod
|
505
|
-
def ByWiresCluster(cluster: topologic.Cluster, close: bool = False, triangulate: bool = True, planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
506
|
-
"""
|
507
|
-
Creates a cell by lofting through the input cluster of wires.
|
508
|
-
|
509
|
-
Parameters
|
510
|
-
----------
|
511
|
-
cluster : topologic.Cluster
|
512
|
-
The input Cluster of wires.
|
513
|
-
close : bool , optional
|
514
|
-
If set to True, the last wire in the cluster of input wires will be connected to the first wire in the cluster of input wires. The default is False.
|
515
|
-
triangulate : bool , optional
|
516
|
-
If set to True, the faces will be triangulated. The default is True.
|
517
|
-
tolerance : float , optional
|
518
|
-
The desired tolerance. The default is 0.0001.
|
519
|
-
|
520
|
-
Raises
|
521
|
-
------
|
522
|
-
Exception
|
523
|
-
Raises an exception if the two wires in the list do not have the same number of edges.
|
524
|
-
|
525
|
-
Returns
|
526
|
-
-------
|
527
|
-
topologic.Cell
|
528
|
-
The created cell.
|
529
|
-
|
530
|
-
"""
|
531
|
-
if not isinstance(cluster, topologic.Cluster):
|
532
|
-
print("Cell.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
|
533
|
-
return None
|
534
|
-
wires = []
|
535
|
-
_ = cluster.Wires(None, wires)
|
536
|
-
return Cell.ByWires(wires, close=close, triangulate=triangulate, planarize=planarize, tolerance=tolerance)
|
537
|
-
|
538
|
-
@staticmethod
|
539
|
-
def Capsule(origin: topologic.Vertex = None, radius: float = 0.25, height: float = 1, uSides: int = 16, vSidesEnds:int = 8, vSidesMiddle: int = 1, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
540
|
-
"""
|
541
|
-
Creates a capsule shape. A capsule is a cylinder with hemispherical ends.
|
542
|
-
|
543
|
-
Parameters
|
544
|
-
----------
|
545
|
-
origin : topologic.Vertex , optional
|
546
|
-
The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
|
547
|
-
radius : float , optional
|
548
|
-
The radius of the capsule. The default is 0.25.
|
549
|
-
height : float , optional
|
550
|
-
The height of the capsule. The default is 1.
|
551
|
-
uSides : int , optional
|
552
|
-
The number of circle segments of the capsule. The default is 16.
|
553
|
-
vSidesEnds : int , optional
|
554
|
-
The number of vertical segments of the end hemispheres. The default is 8.
|
555
|
-
vSidesMiddle : int , optional
|
556
|
-
The number of vertical segments of the middle cylinder. The default is 1.
|
557
|
-
direction : list , optional
|
558
|
-
The vector representing the up direction of the capsule. The default is [0, 0, 1].
|
559
|
-
placement : str , optional
|
560
|
-
The description of the placement of the origin of the capsule. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
|
561
|
-
tolerance : float , optional
|
562
|
-
The desired tolerance. The default is 0.0001.
|
563
|
-
|
564
|
-
Returns
|
565
|
-
-------
|
566
|
-
topologic.Cell
|
567
|
-
The created cell.
|
568
|
-
|
569
|
-
"""
|
570
|
-
from topologicpy.Topology import Topology
|
571
|
-
from topologicpy.Cell import Cell
|
572
|
-
from topologicpy.Vertex import Vertex
|
573
|
-
if not origin:
|
574
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
575
|
-
if not isinstance(origin, topologic.Vertex):
|
576
|
-
print("Cell.Capsule - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
577
|
-
return None
|
578
|
-
cyl_height = height - radius*2
|
579
|
-
if cyl_height <= 0:
|
580
|
-
capsule = Cell.Sphere(origin=Vertex.Origin(), radius=radius, uSides= uSides, vSides=vSidesEnds*2)
|
581
|
-
else:
|
582
|
-
cyl = Cell.Cylinder(origin=Vertex.Origin(),
|
583
|
-
radius=radius,
|
584
|
-
height=cyl_height,
|
585
|
-
uSides=uSides, vSides=vSidesMiddle, direction=[0, 0, 1], placement="center", tolerance=tolerance)
|
586
|
-
o1 = Vertex.ByCoordinates(0, 0, cyl_height*0.5)
|
587
|
-
o2 = Vertex.ByCoordinates(0, 0, -cyl_height*0.5)
|
588
|
-
s1 = Cell.Sphere(origin=o1, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
|
589
|
-
s2 = Cell.Sphere(origin=o2, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
|
590
|
-
capsule = Topology.Union(cyl, s1, tolerance=tolerance)
|
591
|
-
capsule = Topology.Union(capsule, s2, tolerance=tolerance)
|
592
|
-
if placement == "bottom":
|
593
|
-
capsule = Topology.Translate(capsule, 0, 0, height/2)
|
594
|
-
if placement == "lowerleft":
|
595
|
-
capsule = Topology.Translate(capsule, 0, 0, height/2)
|
596
|
-
capsule = Topology.Translate(capsule, radius, radius)
|
597
|
-
|
598
|
-
capsule = Topology.Orient(capsule, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
599
|
-
capsule = Topology.Place(capsule, originA=Vertex.Origin(), originB=origin)
|
600
|
-
return capsule
|
601
|
-
|
602
|
-
@staticmethod
|
603
|
-
def Compactness(cell: topologic.Cell, reference = "sphere", mantissa: int = 6) -> float:
|
604
|
-
"""
|
605
|
-
Returns the compactness measure of the input cell. If the reference is "sphere", this is also known as 'sphericity' (https://en.wikipedia.org/wiki/Sphericity).
|
606
|
-
|
607
|
-
Parameters
|
608
|
-
----------
|
609
|
-
cell : topologic.Cell
|
610
|
-
The input cell.
|
611
|
-
reference : str , optional
|
612
|
-
The desired reference to which to compare this compactness. The options are "sphere" and "cube". It is case insensitive. The default is "sphere".
|
613
|
-
mantissa : int , optional
|
614
|
-
The desired length of the mantissa. The default is 6.
|
615
|
-
|
616
|
-
Returns
|
617
|
-
-------
|
618
|
-
float
|
619
|
-
The compactness of the input cell.
|
620
|
-
|
621
|
-
"""
|
622
|
-
from topologicpy.Face import Face
|
623
|
-
faces = []
|
624
|
-
_ = cell.Faces(None, faces)
|
625
|
-
area = 0.0
|
626
|
-
for aFace in faces:
|
627
|
-
area = area + abs(Face.Area(aFace))
|
628
|
-
volume = abs(topologic.CellUtility.Volume(cell))
|
629
|
-
compactness = 0
|
630
|
-
#From https://en.wikipedia.org/wiki/Sphericity
|
631
|
-
if area > 0:
|
632
|
-
if reference.lower() == "sphere":
|
633
|
-
compactness = (((math.pi)**(1/3))*((6*volume)**(2/3)))/area
|
634
|
-
else:
|
635
|
-
compactness = 6*(volume**(2/3))/area
|
636
|
-
else:
|
637
|
-
print("Cell.Compactness - Error: cell surface area is not positive. Returning None.")
|
638
|
-
return None
|
639
|
-
return round(compactness, mantissa)
|
640
|
-
|
641
|
-
@staticmethod
|
642
|
-
def Cone(origin: topologic.Vertex = None, baseRadius: float = 0.5, topRadius: float = 0, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
|
643
|
-
dirZ: float = 1, placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
644
|
-
"""
|
645
|
-
Creates a cone.
|
646
|
-
|
647
|
-
Parameters
|
648
|
-
----------
|
649
|
-
origin : topologic.Vertex , optional
|
650
|
-
The location of the origin of the cone. The default is None which results in the cone being placed at (0, 0, 0).
|
651
|
-
baseRadius : float , optional
|
652
|
-
The radius of the base circle of the cone. The default is 0.5.
|
653
|
-
topRadius : float , optional
|
654
|
-
The radius of the top circle of the cone. The default is 0.
|
655
|
-
height : float , optional
|
656
|
-
The height of the cone. The default is 1.
|
657
|
-
sides : int , optional
|
658
|
-
The number of sides of the cone. The default is 16.
|
659
|
-
direction : list , optional
|
660
|
-
The vector representing the up direction of the cone. The default is [0, 0, 1].
|
661
|
-
placement : str , optional
|
662
|
-
The description of the placement of the origin of the cone. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
663
|
-
tolerance : float , optional
|
664
|
-
The desired tolerance. The default is 0.0001.
|
665
|
-
|
666
|
-
Returns
|
667
|
-
-------
|
668
|
-
topologic.Cell
|
669
|
-
The created cone.
|
670
|
-
|
671
|
-
"""
|
672
|
-
from topologicpy.Vertex import Vertex
|
673
|
-
from topologicpy.Wire import Wire
|
674
|
-
from topologicpy.Face import Face
|
675
|
-
from topologicpy.Shell import Shell
|
676
|
-
from topologicpy.Cluster import Cluster
|
677
|
-
from topologicpy.Topology import Topology
|
678
|
-
def createCone(baseWire, topWire, baseVertex, topVertex, tolerance):
|
679
|
-
if baseWire == None and topWire == None:
|
680
|
-
raise Exception("Cell.Cone - Error: Both radii of the cone cannot be zero at the same time")
|
681
|
-
elif baseWire == None:
|
682
|
-
apex = baseVertex
|
683
|
-
wire = topWire
|
684
|
-
elif topWire == None:
|
685
|
-
apex = topVertex
|
686
|
-
wire = baseWire
|
687
|
-
else:
|
688
|
-
return topologic.CellUtility.ByLoft([baseWire, topWire])
|
689
|
-
vertices = []
|
690
|
-
_ = wire.Vertices(None,vertices)
|
691
|
-
faces = [Face.ByWire(wire, tolerance=tolerance)]
|
692
|
-
for i in range(0, len(vertices)-1):
|
693
|
-
w = Wire.ByVertices([apex, vertices[i], vertices[i+1]])
|
694
|
-
f = Face.ByWire(w, tolerance=tolerance)
|
695
|
-
faces.append(f)
|
696
|
-
w = Wire.ByVertices([apex, vertices[-1], vertices[0]])
|
697
|
-
f = Face.ByWire(w, tolerance=tolerance)
|
698
|
-
faces.append(f)
|
699
|
-
return Cell.ByFaces(faces, tolerance=tolerance)
|
700
|
-
if not origin:
|
701
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
702
|
-
if not isinstance(origin, topologic.Vertex):
|
703
|
-
print("Cell.Cone - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
704
|
-
return None
|
705
|
-
xOffset = 0
|
706
|
-
yOffset = 0
|
707
|
-
zOffset = 0
|
708
|
-
if placement.lower() == "center":
|
709
|
-
xOffset = 0
|
710
|
-
yOffset = 0
|
711
|
-
zOffset = -height*0.5
|
712
|
-
elif placement.lower() == "lowerleft":
|
713
|
-
xOffset = max(baseRadius, topRadius)
|
714
|
-
yOffset = max(baseRadius, topRadius)
|
715
|
-
zOffset = 0
|
716
|
-
|
717
|
-
baseZ = origin.Z() + zOffset
|
718
|
-
topZ = origin.Z() + zOffset + height
|
719
|
-
baseV = []
|
720
|
-
topV = []
|
721
|
-
for i in range(uSides):
|
722
|
-
angle = math.radians(360/uSides)*i
|
723
|
-
if baseRadius > 0:
|
724
|
-
baseX = math.cos(angle)*baseRadius + origin.X() + xOffset
|
725
|
-
baseY = math.sin(angle)*baseRadius + origin.Y() + yOffset
|
726
|
-
baseZ = origin.Z() + zOffset
|
727
|
-
baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
|
728
|
-
if topRadius > 0:
|
729
|
-
topX = math.cos(angle)*topRadius + origin.X() + xOffset
|
730
|
-
topY = math.sin(angle)*topRadius + origin.Y() + yOffset
|
731
|
-
topV.append(Vertex.ByCoordinates(topX,topY,topZ))
|
732
|
-
if baseRadius > 0:
|
733
|
-
baseWire = Wire.ByVertices(baseV)
|
734
|
-
else:
|
735
|
-
baseWire = None
|
736
|
-
if topRadius > 0:
|
737
|
-
topWire = Wire.ByVertices(topV)
|
738
|
-
else:
|
739
|
-
topWire = None
|
740
|
-
baseVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset)
|
741
|
-
topVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset+height)
|
742
|
-
cone = createCone(baseWire, topWire, baseVertex, topVertex, tolerance)
|
743
|
-
if cone == None:
|
744
|
-
print("Cell.Cone - Error: Could not create a cone. Returning None.")
|
745
|
-
return None
|
746
|
-
|
747
|
-
if vSides > 1:
|
748
|
-
cutting_planes = []
|
749
|
-
baseX = origin.X() + xOffset
|
750
|
-
baseY = origin.Y() + yOffset
|
751
|
-
size = max(baseRadius, topRadius)*3
|
752
|
-
for i in range(1, vSides):
|
753
|
-
baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
|
754
|
-
tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
|
755
|
-
cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
|
756
|
-
cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
|
757
|
-
shell = Cell.Shells(cone)[0]
|
758
|
-
shell = shell.Slice(cutting_planes_cluster)
|
759
|
-
cone = Cell.ByShell(shell)
|
760
|
-
cone = Topology.Orient(cone, origin=origin, dirA=[0, 0, 1], dirB=direction)
|
761
|
-
return cone
|
762
|
-
|
763
|
-
@staticmethod
|
764
|
-
def ContainmentStatus(cell: topologic.Cell, vertex: topologic.Vertex, tolerance: float = 0.0001) -> int:
|
765
|
-
"""
|
766
|
-
Returns the containment status of the input vertex in relationship to the input cell
|
767
|
-
|
768
|
-
Parameters
|
769
|
-
----------
|
770
|
-
cell : topologic.Cell
|
771
|
-
The input cell.
|
772
|
-
vertex : topologic.Vertex
|
773
|
-
The input vertex.
|
774
|
-
tolerance : float , optional
|
775
|
-
The desired tolerance. The default is 0.0001.
|
776
|
-
|
777
|
-
Returns
|
778
|
-
-------
|
779
|
-
int
|
780
|
-
Returns 0 if the vertex is inside, 1 if it is on the boundary of, and 2 if it is outside the input cell.
|
781
|
-
|
782
|
-
"""
|
783
|
-
if not isinstance(cell, topologic.Cell):
|
784
|
-
print("Cell.ContainmentStatus - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
785
|
-
return None
|
786
|
-
if not isinstance(vertex, topologic.Vertex):
|
787
|
-
print("Cell.ContainmentStatus - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
|
788
|
-
return None
|
789
|
-
try:
|
790
|
-
status = topologic.CellUtility.Contains(cell, vertex, tolerance)
|
791
|
-
if status == 0:
|
792
|
-
return 0
|
793
|
-
elif status == 1:
|
794
|
-
return 1
|
795
|
-
else:
|
796
|
-
return 2
|
797
|
-
except:
|
798
|
-
print("Cell.ContainmentStatus - Error: Could not determine containment status. Returning None.")
|
799
|
-
return None
|
800
|
-
|
801
|
-
@staticmethod
|
802
|
-
def Cylinder(origin: topologic.Vertex = None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
|
803
|
-
placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
804
|
-
"""
|
805
|
-
Creates a cylinder.
|
806
|
-
|
807
|
-
Parameters
|
808
|
-
----------
|
809
|
-
origin : topologic.Vertex , optional
|
810
|
-
The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
|
811
|
-
radius : float , optional
|
812
|
-
The radius of the cylinder. The default is 0.5.
|
813
|
-
height : float , optional
|
814
|
-
The height of the cylinder. The default is 1.
|
815
|
-
uSides : int , optional
|
816
|
-
The number of circle segments of the cylinder. The default is 16.
|
817
|
-
vSides : int , optional
|
818
|
-
The number of vertical segments of the cylinder. The default is 1.
|
819
|
-
direction : list , optional
|
820
|
-
The vector representing the up direction of the cylinder. The default is [0, 0, 1].
|
821
|
-
placement : str , optional
|
822
|
-
The description of the placement of the origin of the cylinder. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
|
823
|
-
tolerance : float , optional
|
824
|
-
The desired tolerance. The default is 0.0001.
|
825
|
-
|
826
|
-
Returns
|
827
|
-
-------
|
828
|
-
topologic.Cell
|
829
|
-
The created cell.
|
830
|
-
|
831
|
-
"""
|
832
|
-
from topologicpy.Vertex import Vertex
|
833
|
-
from topologicpy.Face import Face
|
834
|
-
from topologicpy.CellComplex import CellComplex
|
835
|
-
from topologicpy.Cluster import Cluster
|
836
|
-
from topologicpy.Topology import Topology
|
837
|
-
if not origin:
|
838
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
839
|
-
if not isinstance(origin, topologic.Vertex):
|
840
|
-
print("Cell.Cylinder - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
841
|
-
return None
|
842
|
-
xOffset = 0
|
843
|
-
yOffset = 0
|
844
|
-
zOffset = 0
|
845
|
-
if placement.lower() == "center":
|
846
|
-
zOffset = -height*0.5
|
847
|
-
elif placement.lower() == "lowerleft":
|
848
|
-
xOffset = radius
|
849
|
-
yOffset = radius
|
850
|
-
circle_origin = Vertex.ByCoordinates(origin.X() + xOffset, origin.Y() + yOffset, origin.Z() + zOffset)
|
851
|
-
|
852
|
-
baseWire = Wire.Circle(origin=circle_origin, radius=radius, sides=uSides, fromAngle=0, toAngle=360, close=True, direction=[0, 0, 1], placement="center", tolerance=tolerance)
|
853
|
-
baseFace = Face.ByWire(baseWire, tolerance=tolerance)
|
854
|
-
cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, reverse=True,
|
855
|
-
tolerance=tolerance)
|
856
|
-
if vSides > 1:
|
857
|
-
cutting_planes = []
|
858
|
-
baseX = origin.X() + xOffset
|
859
|
-
baseY = origin.Y() + yOffset
|
860
|
-
size = radius*3
|
861
|
-
for i in range(1, vSides):
|
862
|
-
baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
|
863
|
-
tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
|
864
|
-
cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
|
865
|
-
cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
|
866
|
-
cylinder = CellComplex.ExternalBoundary(cylinder.Slice(cutting_planes_cluster))
|
867
|
-
|
868
|
-
cylinder = Topology.Orient(cylinder, origin=origin, dirA=[0, 0, 1], dirB=direction)
|
869
|
-
return cylinder
|
870
|
-
|
871
|
-
@staticmethod
|
872
|
-
def Decompose(cell: topologic.Cell, tiltAngle: float = 10, tolerance: float = 0.0001) -> dict:
|
873
|
-
"""
|
874
|
-
Decomposes the input cell into its logical components. This method assumes that the positive Z direction is UP.
|
875
|
-
|
876
|
-
Parameters
|
877
|
-
----------
|
878
|
-
cell : topologic.Cell
|
879
|
-
the input cell.
|
880
|
-
tiltAngle : float , optional
|
881
|
-
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.
|
882
|
-
tolerance : float , optional
|
883
|
-
The desired tolerance. The default is 0.0001.
|
884
|
-
|
885
|
-
Returns
|
886
|
-
-------
|
887
|
-
dictionary
|
888
|
-
A dictionary with the following keys and values:
|
889
|
-
1. "verticalFaces": list of vertical faces
|
890
|
-
2. "topHorizontalFaces": list of top horizontal faces
|
891
|
-
3. "bottomHorizontalFaces": list of bottom horizontal faces
|
892
|
-
4. "inclinedFaces": list of inclined faces
|
893
|
-
5. "verticalApertures": list of vertical apertures
|
894
|
-
6. "topHorizontalApertures": list of top horizontal apertures
|
895
|
-
7. "bottomHorizontalApertures": list of bottom horizontal apertures
|
896
|
-
8. "inclinedApertures": list of inclined apertures
|
897
|
-
|
898
|
-
"""
|
899
|
-
from topologicpy.Face import Face
|
900
|
-
from topologicpy.Vector import Vector
|
901
|
-
from topologicpy.Aperture import Aperture
|
902
|
-
from topologicpy.Topology import Topology
|
903
|
-
|
904
|
-
def angleCode(f, up, tiltAngle):
|
905
|
-
dirA = Face.NormalAtParameters(f)
|
906
|
-
ang = round(Vector.Angle(dirA, up), 2)
|
907
|
-
if abs(ang - 90) < tiltAngle:
|
908
|
-
code = 0
|
909
|
-
elif abs(ang) < tiltAngle:
|
910
|
-
code = 1
|
911
|
-
elif abs(ang - 180) < tiltAngle:
|
912
|
-
code = 2
|
913
|
-
else:
|
914
|
-
code = 3
|
915
|
-
return code
|
916
|
-
|
917
|
-
def getApertures(topology):
|
918
|
-
apTopologies = []
|
919
|
-
apertures = Topology.Apertures(topology)
|
920
|
-
if isinstance(apertures, list):
|
921
|
-
for aperture in apertures:
|
922
|
-
apTopologies.append(Aperture.Topology(aperture))
|
923
|
-
return apTopologies
|
924
|
-
|
925
|
-
if not isinstance(cell, topologic.Cell):
|
926
|
-
print("Cell.Decompose - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
927
|
-
return None
|
928
|
-
verticalFaces = []
|
929
|
-
topHorizontalFaces = []
|
930
|
-
bottomHorizontalFaces = []
|
931
|
-
inclinedFaces = []
|
932
|
-
verticalApertures = []
|
933
|
-
topHorizontalApertures = []
|
934
|
-
bottomHorizontalApertures = []
|
935
|
-
inclinedApertures = []
|
936
|
-
tiltAngle = abs(tiltAngle)
|
937
|
-
faces = Cell.Faces(cell)
|
938
|
-
zList = []
|
939
|
-
for f in faces:
|
940
|
-
zList.append(f.Centroid().Z())
|
941
|
-
zMin = min(zList)
|
942
|
-
zMax = max(zList)
|
943
|
-
up = [0, 0, 1]
|
944
|
-
for aFace in faces:
|
945
|
-
aCode = angleCode(aFace, up, tiltAngle)
|
946
|
-
|
947
|
-
if aCode == 0:
|
948
|
-
verticalFaces.append(aFace)
|
949
|
-
verticalApertures += getApertures(aFace)
|
950
|
-
elif aCode == 1:
|
951
|
-
if abs(aFace.Centroid().Z() - zMin) < tolerance:
|
952
|
-
bottomHorizontalFaces.append(aFace)
|
953
|
-
bottomHorizontalApertures += getApertures(aFace)
|
954
|
-
else:
|
955
|
-
topHorizontalFaces.append(aFace)
|
956
|
-
topHorizontalApertures += getApertures(aFace)
|
957
|
-
elif aCode == 2:
|
958
|
-
if abs(aFace.Centroid().Z() - zMax) < tolerance:
|
959
|
-
topHorizontalFaces.append(aFace)
|
960
|
-
topHorizontalApertures += getApertures(aFace)
|
961
|
-
else:
|
962
|
-
bottomHorizontalFaces.append(aFace)
|
963
|
-
bottomHorizontalApertures += getApertures(aFace)
|
964
|
-
elif aCode == 3:
|
965
|
-
inclinedFaces.append(aFace)
|
966
|
-
inclinedApertures += getApertures(aFace)
|
967
|
-
d = {
|
968
|
-
"verticalFaces" : verticalFaces,
|
969
|
-
"topHorizontalFaces" : topHorizontalFaces,
|
970
|
-
"bottomHorizontalFaces" : bottomHorizontalFaces,
|
971
|
-
"inclinedFaces" : inclinedFaces,
|
972
|
-
"verticalApertures" : verticalApertures,
|
973
|
-
"topHorizontalApertures" : topHorizontalApertures,
|
974
|
-
"bottomHorizontalApertures" : bottomHorizontalApertures,
|
975
|
-
"inclinedApertures" : inclinedApertures
|
976
|
-
}
|
977
|
-
return d
|
978
|
-
|
979
|
-
@staticmethod
|
980
|
-
def Dodecahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
981
|
-
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
982
|
-
"""
|
983
|
-
Description
|
984
|
-
----------
|
985
|
-
Creates a dodecahedron. See https://en.wikipedia.org/wiki/Dodecahedron.
|
986
|
-
|
987
|
-
Parameters
|
988
|
-
----------
|
989
|
-
origin : topologic.Vertex , optional
|
990
|
-
The origin location of the dodecahedron. The default is None which results in the dodecahedron being placed at (0, 0, 0).
|
991
|
-
radius : float , optional
|
992
|
-
The radius of the dodecahedron's circumscribed sphere. The default is 0.5.
|
993
|
-
direction : list , optional
|
994
|
-
The vector representing the up direction of the dodecahedron. The default is [0, 0, 1].
|
995
|
-
placement : str , optional
|
996
|
-
The description of the placement of the origin of the dodecahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
997
|
-
tolerance : float , optional
|
998
|
-
The desired tolerance. The default is 0.0001.
|
999
|
-
|
1000
|
-
Returns
|
1001
|
-
-------
|
1002
|
-
topologic.Cell
|
1003
|
-
The created dodecahedron.
|
1004
|
-
|
1005
|
-
"""
|
1006
|
-
from topologicpy.Vertex import Vertex
|
1007
|
-
from topologicpy.Edge import Edge
|
1008
|
-
from topologicpy.Face import Face
|
1009
|
-
from topologicpy.Cluster import Cluster
|
1010
|
-
from topologicpy.Topology import Topology
|
1011
|
-
|
1012
|
-
if not origin:
|
1013
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1014
|
-
if not isinstance(origin, topologic.Vertex):
|
1015
|
-
print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1016
|
-
return None
|
1017
|
-
pen = Face.Circle(sides=5, radius=0.5)
|
1018
|
-
pentagons = [pen]
|
1019
|
-
edges = Topology.Edges(pen)
|
1020
|
-
for edge in edges:
|
1021
|
-
o = Topology.Centroid(edge)
|
1022
|
-
e_dir = Edge.Direction(edge)
|
1023
|
-
pentagons.append(Topology.Rotate(pen, origin=o, axis=e_dir, angle=116.565))
|
1024
|
-
|
1025
|
-
cluster = Cluster.ByTopologies(pentagons)
|
1026
|
-
|
1027
|
-
cluster2 = Topology.Rotate(cluster, origin=Vertex.Origin(), axis=[1, 0, 0], angle=180)
|
1028
|
-
cluster2 = Topology.Rotate(cluster2, origin=Vertex.Origin(), axis=[0, 0, 1], angle=36)
|
1029
|
-
vertices = Topology.Vertices(cluster2)
|
1030
|
-
zList = [Vertex.Z(v) for v in vertices]
|
1031
|
-
zList = list(set(zList))
|
1032
|
-
zList.sort()
|
1033
|
-
zoffset1 = zList[-1] - zList[0]
|
1034
|
-
zoffset2 = zList[1] - zList[0]
|
1035
|
-
cluster2 = Topology.Translate(cluster2, 0, 0, -zoffset1-zoffset2)
|
1036
|
-
pentagons += Topology.Faces(cluster2)
|
1037
|
-
dodecahedron = Cell.ByFaces(pentagons, tolerance=tolerance)
|
1038
|
-
centroid = Topology.Centroid(dodecahedron)
|
1039
|
-
dodecahedron = Topology.Translate(dodecahedron, -Vertex.X(centroid), -Vertex.Y(centroid), -Vertex.Z(centroid))
|
1040
|
-
vertices = Topology.Vertices(dodecahedron)
|
1041
|
-
d = Vertex.Distance(Vertex.Origin(), vertices[0])
|
1042
|
-
dodecahedron = Topology.Scale(dodecahedron, origin=Vertex.Origin(), x=radius/d, y=radius/d, z=radius/d)
|
1043
|
-
if placement == "bottom":
|
1044
|
-
dodecahedron = Topology.Translate(dodecahedron, 0, 0, radius)
|
1045
|
-
elif placement == "lowerleft":
|
1046
|
-
dodecahedron = Topology.Translate(dodecahedron, radius, radius, radius)
|
1047
|
-
|
1048
|
-
dodecahedron = Topology.Orient(dodecahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1049
|
-
dodecahedron = Topology.Place(dodecahedron, originA=Vertex.Origin(), originB=origin)
|
1050
|
-
return dodecahedron
|
1051
|
-
|
1052
|
-
@staticmethod
|
1053
|
-
def Edges(cell: topologic.Cell) -> list:
|
1054
|
-
"""
|
1055
|
-
Returns the edges of the input cell.
|
1056
|
-
|
1057
|
-
Parameters
|
1058
|
-
----------
|
1059
|
-
cell : topologic.Cell
|
1060
|
-
The input cell.
|
1061
|
-
|
1062
|
-
Returns
|
1063
|
-
-------
|
1064
|
-
list
|
1065
|
-
The list of edges.
|
1066
|
-
|
1067
|
-
"""
|
1068
|
-
if not isinstance(cell, topologic.Cell):
|
1069
|
-
print("Cell.Edges - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1070
|
-
return None
|
1071
|
-
edges = []
|
1072
|
-
_ = cell.Edges(None, edges)
|
1073
|
-
return edges
|
1074
|
-
|
1075
|
-
@staticmethod
|
1076
|
-
def Egg(origin: topologic.Vertex = None, height: float = 1.0, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
|
1077
|
-
placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
1078
|
-
"""
|
1079
|
-
Creates
|
1080
|
-
|
1081
|
-
Parameters
|
1082
|
-
----------
|
1083
|
-
origin : topologic.Vertex , optional
|
1084
|
-
The origin location of the sphere. The default is None which results in the
|
1085
|
-
|
1086
|
-
The
|
1087
|
-
uSides : int , optional
|
1088
|
-
The number of sides along the longitude of the
|
1089
|
-
vSides : int , optional
|
1090
|
-
The number of sides along the latitude of the
|
1091
|
-
direction : list , optional
|
1092
|
-
The vector representing the up direction of the
|
1093
|
-
placement : str , optional
|
1094
|
-
The description of the placement of the origin of the
|
1095
|
-
tolerance : float , optional
|
1096
|
-
The desired tolerance. The default is 0.0001.
|
1097
|
-
|
1098
|
-
Returns
|
1099
|
-
-------
|
1100
|
-
topologic.Cell
|
1101
|
-
The created
|
1102
|
-
|
1103
|
-
"""
|
1104
|
-
|
1105
|
-
from topologicpy.Vertex import Vertex
|
1106
|
-
from topologicpy.Topology import Topology
|
1107
|
-
from topologicpy.Dictionary import Dictionary
|
1108
|
-
|
1109
|
-
if not origin:
|
1110
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1111
|
-
if not isinstance(origin, topologic.Vertex):
|
1112
|
-
print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1113
|
-
return None
|
1114
|
-
|
1115
|
-
coords = [[0.0, 0.0, -0.5],
|
1116
|
-
[0.074748, 0.0, -0.494015],
|
1117
|
-
[0.140819, 0.0, -0.473222],
|
1118
|
-
[0.204118, 0.0, -0.438358],
|
1119
|
-
[0.259512, 0.0, -0.391913],
|
1120
|
-
[0.304837, 0.0, -0.335519],
|
1121
|
-
[0.338649, 0.0, -0.271416],
|
1122
|
-
[0.361307, 0.0, -0.202039],
|
1123
|
-
[0.375678, 0.0, -0.129109],
|
1124
|
-
[0.381294, 0.0, -0.053696],
|
1125
|
-
[0.377694, 0.0, 0.019874],
|
1126
|
-
[0.365135, 0.0, 0.091978],
|
1127
|
-
[0.341482, 0.0, 0.173973],
|
1128
|
-
[0.300154, 0.0, 0.276001],
|
1129
|
-
[0.252928, 0.0, 0.355989],
|
1130
|
-
[0.206605, 0.0, 0.405813],
|
1131
|
-
[0.157529, 0.0, 0.442299],
|
1132
|
-
[0.10604, 0.0, 0.472092],
|
1133
|
-
[0.05547, 0.0, 0.491784],
|
1134
|
-
[0.0, 0.0, 0.5]]
|
1135
|
-
verts = [Vertex.ByCoordinates(coord) for coord in coords]
|
1136
|
-
c = Wire.ByVertices(verts, close=False)
|
1137
|
-
new_verts = []
|
1138
|
-
for i in range(vSides+1):
|
1139
|
-
new_verts.append(Wire.VertexByParameter(c, i/vSides))
|
1140
|
-
c = Wire.ByVertices(new_verts, close=False)
|
1141
|
-
egg = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
1142
|
-
if egg.Type() == topologic.CellComplex.Type():
|
1143
|
-
egg = egg.ExternalBoundary()
|
1144
|
-
if egg.Type() == topologic.Shell.Type():
|
1145
|
-
egg = topologic.Cell.ByShell(egg)
|
1146
|
-
egg = Topology.Scale(egg, origin=Vertex.Origin(), x=height, y=height, z=height)
|
1147
|
-
if placement.lower() == "bottom":
|
1148
|
-
egg = Topology.Translate(egg, 0, 0, height/2)
|
1149
|
-
elif placement.lower() == "lowerleft":
|
1150
|
-
bb = Cell.BoundingBox(egg)
|
1151
|
-
d = Topology.Dictionary(bb)
|
1152
|
-
width = Dictionary.ValueAtKey(d, 'width')
|
1153
|
-
length = Dictionary.ValueAtKey(d, 'length')
|
1154
|
-
egg = Topology.Translate(egg, width*0.5, length*0.5, height*0.5)
|
1155
|
-
egg = Topology.Orient(egg, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1156
|
-
egg = Topology.Place(egg, originA=Vertex.Origin(), originB=origin)
|
1157
|
-
return egg
|
1158
|
-
|
1159
|
-
@staticmethod
|
1160
|
-
def ExternalBoundary(cell: topologic.Cell) -> topologic.Shell:
|
1161
|
-
"""
|
1162
|
-
Returns the external boundary of the input cell.
|
1163
|
-
|
1164
|
-
Parameters
|
1165
|
-
----------
|
1166
|
-
cell : topologic.Cell
|
1167
|
-
The input cell.
|
1168
|
-
|
1169
|
-
Returns
|
1170
|
-
-------
|
1171
|
-
topologic.Shell
|
1172
|
-
The external boundary of the input cell.
|
1173
|
-
|
1174
|
-
"""
|
1175
|
-
|
1176
|
-
if not isinstance(cell, topologic.Cell):
|
1177
|
-
print("Cell.ExternalBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1178
|
-
return None
|
1179
|
-
try:
|
1180
|
-
return cell.ExternalBoundary()
|
1181
|
-
except:
|
1182
|
-
print("Cell.ExternalBoundary - Error: Could not compute the external boundary. Returning None.")
|
1183
|
-
return None
|
1184
|
-
|
1185
|
-
@staticmethod
|
1186
|
-
def Faces(cell: topologic.Cell) -> list:
|
1187
|
-
"""
|
1188
|
-
Returns the faces of the input cell.
|
1189
|
-
|
1190
|
-
Parameters
|
1191
|
-
----------
|
1192
|
-
cell : topologic.Cell
|
1193
|
-
The input cell.
|
1194
|
-
|
1195
|
-
Returns
|
1196
|
-
-------
|
1197
|
-
list
|
1198
|
-
The list of faces.
|
1199
|
-
|
1200
|
-
"""
|
1201
|
-
if not isinstance(cell, topologic.Cell):
|
1202
|
-
print("Cell.Faces - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1203
|
-
return None
|
1204
|
-
faces = []
|
1205
|
-
_ = cell.Faces(None, faces)
|
1206
|
-
return faces
|
1207
|
-
|
1208
|
-
@staticmethod
|
1209
|
-
def Hyperboloid(origin: topologic.Cell = None, baseRadius: float = 0.5, topRadius: float = 0.5, height: float = 1, sides: int = 24, direction: list = [0, 0, 1],
|
1210
|
-
twist: float = 60, placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
1211
|
-
"""
|
1212
|
-
Creates a hyperboloid.
|
1213
|
-
|
1214
|
-
Parameters
|
1215
|
-
----------
|
1216
|
-
origin : topologic.Vertex , optional
|
1217
|
-
The location of the origin of the hyperboloid. The default is None which results in the hyperboloid being placed at (0, 0, 0).
|
1218
|
-
baseRadius : float , optional
|
1219
|
-
The radius of the base circle of the hyperboloid. The default is 0.5.
|
1220
|
-
topRadius : float , optional
|
1221
|
-
The radius of the top circle of the hyperboloid. The default is 0.5.
|
1222
|
-
height : float , optional
|
1223
|
-
The height of the cone. The default is 1.
|
1224
|
-
sides : int , optional
|
1225
|
-
The number of sides of the cone. The default is 24.
|
1226
|
-
direction : list , optional
|
1227
|
-
The vector representing the up direction of the hyperboloid. The default is [0, 0, 1].
|
1228
|
-
twist : float , optional
|
1229
|
-
The angle to twist the base cylinder. The default is 60.
|
1230
|
-
placement : str , optional
|
1231
|
-
The description of the placement of the origin of the hyperboloid. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1232
|
-
tolerance : float , optional
|
1233
|
-
The desired tolerance. The default is 0.0001.
|
1234
|
-
|
1235
|
-
Returns
|
1236
|
-
-------
|
1237
|
-
topologic.Cell
|
1238
|
-
The created hyperboloid.
|
1239
|
-
|
1240
|
-
"""
|
1241
|
-
from topologicpy.Cluster import Cluster
|
1242
|
-
from topologicpy.Vertex import Vertex
|
1243
|
-
from topologicpy.Face import Face
|
1244
|
-
from topologicpy.Topology import Topology
|
1245
|
-
|
1246
|
-
def createHyperboloid(baseVertices, topVertices, tolerance):
|
1247
|
-
baseWire = Wire.ByVertices(baseVertices, close=True)
|
1248
|
-
topWire = Wire.ByVertices(topVertices, close=True)
|
1249
|
-
baseFace = Face.ByWire(baseWire, tolerance=tolerance)
|
1250
|
-
topFace = Face.ByWire(topWire, tolerance=tolerance)
|
1251
|
-
faces = [baseFace, topFace]
|
1252
|
-
for i in range(0, len(baseVertices)-1):
|
1253
|
-
w = Wire.ByVertices([baseVertices[i], topVertices[i], topVertices[i+1]], close=True)
|
1254
|
-
f = Face.ByWire(w, tolerance=tolerance)
|
1255
|
-
faces.append(f)
|
1256
|
-
w = Wire.ByVertices([baseVertices[i+1], baseVertices[i], topVertices[i+1]], close=True)
|
1257
|
-
f = Face.ByWire(w, tolerance=tolerance)
|
1258
|
-
faces.append(f)
|
1259
|
-
w = Wire.ByVertices([baseVertices[-1], topVertices[-1], topVertices[0]], close=True)
|
1260
|
-
f = Face.ByWire(w, tolerance=tolerance)
|
1261
|
-
faces.append(f)
|
1262
|
-
w = Wire.ByVertices([baseVertices[0], baseVertices[-1], topVertices[0]], close=True)
|
1263
|
-
f = Face.ByWire(w, tolerance=tolerance)
|
1264
|
-
faces.append(f)
|
1265
|
-
returnTopology = Cell.ByFaces(faces, tolerance=tolerance)
|
1266
|
-
if returnTopology == None:
|
1267
|
-
returnTopology = Cluster.ByTopologies(faces)
|
1268
|
-
return returnTopology
|
1269
|
-
|
1270
|
-
if not origin:
|
1271
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1272
|
-
if not isinstance(origin, topologic.Vertex):
|
1273
|
-
print("Cell.Hyperboloid - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1274
|
-
return None
|
1275
|
-
w_origin = Vertex.Origin()
|
1276
|
-
baseV = []
|
1277
|
-
topV = []
|
1278
|
-
xOffset = 0
|
1279
|
-
yOffset = 0
|
1280
|
-
zOffset = 0
|
1281
|
-
if placement.lower() == "center":
|
1282
|
-
zOffset = -height*0.5
|
1283
|
-
elif placement.lower() == "lowerleft":
|
1284
|
-
xOffset = max(baseRadius, topRadius)
|
1285
|
-
yOffset = max(baseRadius, topRadius)
|
1286
|
-
baseZ = w_origin.Z() + zOffset
|
1287
|
-
topZ = w_origin.Z() + zOffset + height
|
1288
|
-
for i in range(sides):
|
1289
|
-
angle = math.radians(360/sides)*i
|
1290
|
-
if baseRadius > 0:
|
1291
|
-
baseX = math.sin(angle+math.radians(twist))*baseRadius + w_origin.X() + xOffset
|
1292
|
-
baseY = math.cos(angle+math.radians(twist))*baseRadius + w_origin.Y() + yOffset
|
1293
|
-
baseZ = w_origin.Z() + zOffset
|
1294
|
-
baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
|
1295
|
-
if topRadius > 0:
|
1296
|
-
topX = math.sin(angle-math.radians(twist))*topRadius + w_origin.X() + xOffset
|
1297
|
-
topY = math.cos(angle-math.radians(twist))*topRadius + w_origin.Y() + yOffset
|
1298
|
-
topV.append(Vertex.ByCoordinates(topX,topY,topZ))
|
1299
|
-
|
1300
|
-
hyperboloid = createHyperboloid(baseV, topV, tolerance)
|
1301
|
-
if hyperboloid == None:
|
1302
|
-
print("Cell.Hyperboloid - Error: Could not create a hyperboloid. Returning None.")
|
1303
|
-
return None
|
1304
|
-
|
1305
|
-
hyperboloid = Topology.Orient(hyperboloid, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1306
|
-
hyperboloid = Topology.Place(hyperboloid, originA=Vertex.Origin(), originB=origin)
|
1307
|
-
return hyperboloid
|
1308
|
-
|
1309
|
-
@staticmethod
|
1310
|
-
def Icosahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
1311
|
-
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1312
|
-
"""
|
1313
|
-
Description
|
1314
|
-
----------
|
1315
|
-
Creates an icosahedron. See https://en.wikipedia.org/wiki/Icosahedron.
|
1316
|
-
|
1317
|
-
Parameters
|
1318
|
-
----------
|
1319
|
-
origin : topologic.Vertex , optional
|
1320
|
-
The origin location of the icosahedron. The default is None which results in the icosahedron being placed at (0, 0, 0).
|
1321
|
-
radius : float , optional
|
1322
|
-
The radius of the icosahedron's circumscribed sphere. The default is 0.5.
|
1323
|
-
direction : list , optional
|
1324
|
-
The vector representing the up direction of the icosahedron. The default is [0, 0, 1].
|
1325
|
-
placement : str , optional
|
1326
|
-
The description of the placement of the origin of the icosahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1327
|
-
tolerance : float , optional
|
1328
|
-
The desired tolerance. The default is 0.0001.
|
1329
|
-
|
1330
|
-
Returns
|
1331
|
-
-------
|
1332
|
-
topologic.Cell
|
1333
|
-
The created icosahedron.
|
1334
|
-
|
1335
|
-
"""
|
1336
|
-
from topologicpy.Vertex import Vertex
|
1337
|
-
from topologicpy.Wire import Wire
|
1338
|
-
from topologicpy.Face import Face
|
1339
|
-
from topologicpy.Topology import Topology
|
1340
|
-
import math
|
1341
|
-
|
1342
|
-
if not origin:
|
1343
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1344
|
-
if not isinstance(origin, topologic.Vertex):
|
1345
|
-
print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1346
|
-
return None
|
1347
|
-
rect1 = Wire.Rectangle(width=(1+math.sqrt(5))/2, length=1)
|
1348
|
-
rect2 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
|
1349
|
-
rect2 = Topology.Rotate(rect2, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
|
1350
|
-
rect3 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
|
1351
|
-
rect3 = Topology.Rotate(rect3, origin=Vertex.Origin(), axis=[0, 1, 0], angle=90)
|
1352
|
-
vertices = Topology.Vertices(rect1)
|
1353
|
-
v1, v2, v3, v4 = vertices
|
1354
|
-
vertices = Topology.Vertices(rect2)
|
1355
|
-
v5, v6, v7, v8 = vertices
|
1356
|
-
vertices = Topology.Vertices(rect3)
|
1357
|
-
v9, v10, v11, v12 = vertices
|
1358
|
-
f1 = Face.ByVertices([v1,v8,v4])
|
1359
|
-
f2 = Face.ByVertices([v1,v4,v5])
|
1360
|
-
f3 = Face.ByVertices([v3,v2,v6])
|
1361
|
-
f4 = Face.ByVertices([v2,v3,v7])
|
1362
|
-
f5 = Face.ByVertices([v10,v9,v2])
|
1363
|
-
f6 = Face.ByVertices([v10,v9,v1])
|
1364
|
-
f7 = Face.ByVertices([v12,v11,v4])
|
1365
|
-
f8 = Face.ByVertices([v12,v11,v3])
|
1366
|
-
f9 = Face.ByVertices([v8,v7,v9])
|
1367
|
-
f10 = Face.ByVertices([v8,v7,v12])
|
1368
|
-
f11 = Face.ByVertices([v5,v6,v10])
|
1369
|
-
f12 = Face.ByVertices([v5,v6,v11])
|
1370
|
-
f13 = Face.ByVertices([v8,v1,v9])
|
1371
|
-
f14 = Face.ByVertices([v9,v2,v7])
|
1372
|
-
f15 = Face.ByVertices([v7,v3,v12])
|
1373
|
-
f16 = Face.ByVertices([v8,v12,v4])
|
1374
|
-
f17 = Face.ByVertices([v1,v5,v10])
|
1375
|
-
f18 = Face.ByVertices([v10,v2,v6])
|
1376
|
-
f19 = Face.ByVertices([v6,v3,v11])
|
1377
|
-
f20 = Face.ByVertices([v11,v4,v5])
|
1378
|
-
|
1379
|
-
icosahedron = Cell.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,
|
1380
|
-
f11,f12,f13,f14,f15,f16,f17,f18,f19,f20], tolerance=tolerance)
|
1381
|
-
sf = 1.051*0.5 # To insribe it in a sphere of radius 0.5
|
1382
|
-
icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
|
1383
|
-
sf = radius/0.5
|
1384
|
-
icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
|
1385
|
-
if placement == "bottom":
|
1386
|
-
icosahedron = Topology.Translate(icosahedron, 0, 0, radius)
|
1387
|
-
elif placement == "lowerleft":
|
1388
|
-
icosahedron = Topology.Translate(icosahedron, radius, radius, radius)
|
1389
|
-
|
1390
|
-
icosahedron = Topology.Orient(icosahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1391
|
-
icosahedron = Topology.Place(icosahedron, originA=Vertex.Origin(), originB=origin)
|
1392
|
-
return icosahedron
|
1393
|
-
|
1394
|
-
|
1395
|
-
@staticmethod
|
1396
|
-
def InternalBoundaries(cell: topologic.Cell) -> list:
|
1397
|
-
"""
|
1398
|
-
Returns the internal boundaries of the input cell.
|
1399
|
-
|
1400
|
-
Parameters
|
1401
|
-
----------
|
1402
|
-
cell : topologic.Cell
|
1403
|
-
The input cell.
|
1404
|
-
|
1405
|
-
Returns
|
1406
|
-
-------
|
1407
|
-
list
|
1408
|
-
The list of internal boundaries ([topologic.Shell]).
|
1409
|
-
|
1410
|
-
"""
|
1411
|
-
shells = []
|
1412
|
-
_ = cell.InternalBoundaries(shells)
|
1413
|
-
return shells
|
1414
|
-
|
1415
|
-
@staticmethod
|
1416
|
-
def InternalVertex(cell: topologic.Cell, tolerance: float = 0.0001):
|
1417
|
-
"""
|
1418
|
-
Creates a vertex that is guaranteed to be inside the input cell.
|
1419
|
-
|
1420
|
-
Parameters
|
1421
|
-
----------
|
1422
|
-
cell : topologic.Cell
|
1423
|
-
The input cell.
|
1424
|
-
tolerance : float , optional
|
1425
|
-
The desired tolerance. The default is 0.0001.
|
1426
|
-
|
1427
|
-
Returns
|
1428
|
-
-------
|
1429
|
-
topologic.Vertex
|
1430
|
-
The internal vertex.
|
1431
|
-
|
1432
|
-
"""
|
1433
|
-
|
1434
|
-
if not isinstance(cell, topologic.Cell):
|
1435
|
-
print("Cell.InternalVertex - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1436
|
-
return None
|
1437
|
-
try:
|
1438
|
-
return topologic.CellUtility.InternalVertex(cell, tolerance)
|
1439
|
-
except:
|
1440
|
-
print("Cell.InternalVertex - Error: Could not create an internal vertex. Returning None.")
|
1441
|
-
return None
|
1442
|
-
|
1443
|
-
@staticmethod
|
1444
|
-
def IsOnBoundary(cell: topologic.Cell, vertex: topologic.Vertex, tolerance: float = 0.0001) -> bool:
|
1445
|
-
"""
|
1446
|
-
Returns True if the input vertex is on the boundary of the input cell. Returns False otherwise.
|
1447
|
-
|
1448
|
-
Parameters
|
1449
|
-
----------
|
1450
|
-
cell : topologic.Cell
|
1451
|
-
The input cell.
|
1452
|
-
vertex : topologic.Vertex
|
1453
|
-
The input vertex.
|
1454
|
-
tolerance : float , optional
|
1455
|
-
The desired tolerance. The default is 0.0001.
|
1456
|
-
|
1457
|
-
Returns
|
1458
|
-
-------
|
1459
|
-
bool
|
1460
|
-
Returns True if the input vertex is inside the input cell. Returns False otherwise.
|
1461
|
-
|
1462
|
-
"""
|
1463
|
-
|
1464
|
-
if not isinstance(cell, topologic.Cell):
|
1465
|
-
print("Cell.IsOnBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1466
|
-
return None
|
1467
|
-
if not isinstance(vertex, topologic.Vertex):
|
1468
|
-
print("Cell.IsOnBoundary - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
|
1469
|
-
return None
|
1470
|
-
try:
|
1471
|
-
return (topologic.CellUtility.Contains(cell, vertex, tolerance) == 1)
|
1472
|
-
except:
|
1473
|
-
print("Cell.IsOnBoundary - Error: Could not determine if the input vertex is on the boundary of the input cell. Returning None.")
|
1474
|
-
return None
|
1475
|
-
|
1476
|
-
@staticmethod
|
1477
|
-
def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
1478
|
-
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1479
|
-
"""
|
1480
|
-
Description
|
1481
|
-
----------
|
1482
|
-
Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.
|
1483
|
-
|
1484
|
-
Parameters
|
1485
|
-
----------
|
1486
|
-
origin : topologic.Vertex , optional
|
1487
|
-
The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
|
1488
|
-
radius : float , optional
|
1489
|
-
The radius of the octahedron's circumscribed sphere. The default is 0.5.
|
1490
|
-
direction : list , optional
|
1491
|
-
The vector representing the up direction of the octahedron. The default is [0, 0, 1].
|
1492
|
-
placement : str , optional
|
1493
|
-
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".
|
1494
|
-
tolerance : float , optional
|
1495
|
-
The desired tolerance. The default is 0.0001.
|
1496
|
-
|
1497
|
-
Returns
|
1498
|
-
-------
|
1499
|
-
topologic.Cell
|
1500
|
-
The created octahedron.
|
1501
|
-
|
1502
|
-
"""
|
1503
|
-
|
1504
|
-
from topologicpy.Vertex import Vertex
|
1505
|
-
from topologicpy.Face import Face
|
1506
|
-
from topologicpy.Topology import Topology
|
1507
|
-
|
1508
|
-
if not origin:
|
1509
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1510
|
-
if not isinstance(origin, topologic.Vertex):
|
1511
|
-
print("Cell.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1512
|
-
return None
|
1513
|
-
|
1514
|
-
vb1 = Vertex.ByCoordinates(-0.5, 0, 0)
|
1515
|
-
vb2 = Vertex.ByCoordinates(0, -0.5, 0)
|
1516
|
-
vb3 = Vertex.ByCoordinates(0.5, 0, 0)
|
1517
|
-
vb4 = Vertex.ByCoordinates(0, 0.5, 0)
|
1518
|
-
top = Vertex.ByCoordinates(0, 0, 0.5)
|
1519
|
-
bottom = Vertex.ByCoordinates(0, 0, -0.5)
|
1520
|
-
f1 = Face.ByVertices([top, vb1, vb2])
|
1521
|
-
f2 = Face.ByVertices([top, vb2, vb3])
|
1522
|
-
f3 = Face.ByVertices([top, vb3, vb4])
|
1523
|
-
f4 = Face.ByVertices([top, vb4, vb1])
|
1524
|
-
f5 = Face.ByVertices([bottom, vb1, vb2])
|
1525
|
-
f6 = Face.ByVertices([bottom, vb2, vb3])
|
1526
|
-
f7 = Face.ByVertices([bottom, vb3, vb4])
|
1527
|
-
f8 = Face.ByVertices([bottom, vb4, vb1])
|
1528
|
-
|
1529
|
-
octahedron = Cell.ByFaces([f1, f2, f3, f4, f5, f6, f7, f8], tolerance=tolerance)
|
1530
|
-
octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
|
1531
|
-
if placement == "bottom":
|
1532
|
-
octahedron = Topology.Translate(octahedron, 0, 0, radius)
|
1533
|
-
elif placement == "lowerleft":
|
1534
|
-
octahedron = Topology.Translate(octahedron, radius, radius, radius)
|
1535
|
-
octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1536
|
-
octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
|
1537
|
-
return octahedron
|
1538
|
-
|
1539
|
-
@staticmethod
|
1540
|
-
def Pipe(edge: topologic.Edge, profile: topologic.Wire = None, radius: float = 0.5, sides: int = 16, startOffset: float = 0, endOffset: float = 0, endcapA: topologic.Topology = None, endcapB: topologic.Topology = None) -> dict:
|
1541
|
-
"""
|
1542
|
-
Description
|
1543
|
-
----------
|
1544
|
-
Creates a pipe along the input edge.
|
1545
|
-
|
1546
|
-
Parameters
|
1547
|
-
----------
|
1548
|
-
edge : topologic.Edge
|
1549
|
-
The centerline of the pipe.
|
1550
|
-
profile : topologic.Wire , optional
|
1551
|
-
The profile of the pipe. It is assumed that the profile is in the XY plane. If set to None, a circle of radius 0.5 will be used. The default is None.
|
1552
|
-
radius : float , optional
|
1553
|
-
The radius of the pipe. The default is 0.5.
|
1554
|
-
sides : int , optional
|
1555
|
-
The number of sides of the pipe. The default is 16.
|
1556
|
-
startOffset : float , optional
|
1557
|
-
The offset distance from the start vertex of the centerline edge. The default is 0.
|
1558
|
-
endOffset : float , optional
|
1559
|
-
The offset distance from the end vertex of the centerline edge. The default is 0.
|
1560
|
-
endcapA : topologic.Topology, optional
|
1561
|
-
The topology to place at the start vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the direction of the centerline edge.
|
1562
|
-
endcapB : topologic.Topology, optional
|
1563
|
-
The topology to place at the end vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the inverse direction of the centerline edge.
|
1564
|
-
|
1565
|
-
Returns
|
1566
|
-
-------
|
1567
|
-
dict
|
1568
|
-
A dictionary containing the pipe, the start endcap, and the end endcap if they have been specified. The dictionary has the following keys:
|
1569
|
-
'pipe'
|
1570
|
-
'endcapA'
|
1571
|
-
'endcapB'
|
1572
|
-
|
1573
|
-
"""
|
1574
|
-
|
1575
|
-
from topologicpy.Vertex import Vertex
|
1576
|
-
from topologicpy.Edge import Edge
|
1577
|
-
from topologicpy.Topology import Topology
|
1578
|
-
|
1579
|
-
if not isinstance(edge, topologic.Edge):
|
1580
|
-
print("Cell.Pipe - Error: The input edge parameter is not a valid topologic edge. Returning None.")
|
1581
|
-
return None
|
1582
|
-
length = Edge.Length(edge)
|
1583
|
-
origin = Edge.StartVertex(edge)
|
1584
|
-
startU = startOffset / length
|
1585
|
-
endU = 1.0 - (endOffset / length)
|
1586
|
-
sv = Edge.VertexByParameter(edge, startU)
|
1587
|
-
ev = Edge.VertexByParameter(edge, endU)
|
1588
|
-
x1 = sv.X()
|
1589
|
-
y1 = sv.Y()
|
1590
|
-
z1 = sv.Z()
|
1591
|
-
x2 = ev.X()
|
1592
|
-
y2 = ev.Y()
|
1593
|
-
z2 = ev.Z()
|
1594
|
-
dx = x2 - x1
|
1595
|
-
dy = y2 - y1
|
1596
|
-
dz = z2 - z1
|
1597
|
-
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
1598
|
-
baseV = []
|
1599
|
-
topV = []
|
1600
|
-
|
1601
|
-
if isinstance(profile, topologic.Wire):
|
1602
|
-
baseWire = Topology.Translate(profile, 0 , 0, sv.Z())
|
1603
|
-
topWire = Topology.Translate(profile, 0 , 0, sv.Z()+dist)
|
1604
|
-
else:
|
1605
|
-
for i in range(sides):
|
1606
|
-
angle = math.radians(360/sides)*i
|
1607
|
-
x = math.sin(angle)*radius + sv.X()
|
1608
|
-
y = math.cos(angle)*radius + sv.Y()
|
1609
|
-
z = sv.Z()
|
1610
|
-
baseV.append(Vertex.ByCoordinates(x, y, z))
|
1611
|
-
topV.append(Vertex.ByCoordinates(x, y, z+dist))
|
1612
|
-
|
1613
|
-
baseWire = Wire.ByVertices(baseV)
|
1614
|
-
topWire = Wire.ByVertices(topV)
|
1615
|
-
wires = [baseWire, topWire]
|
1616
|
-
pipe = Cell.ByWires(wires)
|
1617
|
-
phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
|
1618
|
-
if dist < 0.0001:
|
1619
|
-
theta = 0
|
1620
|
-
else:
|
1621
|
-
theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
|
1622
|
-
pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 1, 0], angle=theta)
|
1623
|
-
pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 0, 1], angle=phi)
|
1624
|
-
zzz = Vertex.ByCoordinates(0, 0, 0)
|
1625
|
-
if endcapA:
|
1626
|
-
origin = edge.StartVertex()
|
1627
|
-
x1 = origin.X()
|
1628
|
-
y1 = origin.Y()
|
1629
|
-
z1 = origin.Z()
|
1630
|
-
x2 = edge.EndVertex().X()
|
1631
|
-
y2 = edge.EndVertex().Y()
|
1632
|
-
z2 = edge.EndVertex().Z()
|
1633
|
-
dx = x2 - x1
|
1634
|
-
dy = y2 - y1
|
1635
|
-
dz = z2 - z1
|
1636
|
-
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
1637
|
-
phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
|
1638
|
-
if dist < 0.0001:
|
1639
|
-
theta = 0
|
1640
|
-
else:
|
1641
|
-
theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
|
1642
|
-
endcapA = Topology.Copy(endcapA)
|
1643
|
-
endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 1, 0], angle=theta)
|
1644
|
-
endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 0, 1], angle=phi+180)
|
1645
|
-
endcapA = Topology.Translate(endcapA, origin.X(), origin.Y(), origin.Z())
|
1646
|
-
if endcapB:
|
1647
|
-
origin = edge.EndVertex()
|
1648
|
-
x1 = origin.X()
|
1649
|
-
y1 = origin.Y()
|
1650
|
-
z1 = origin.Z()
|
1651
|
-
x2 = edge.StartVertex().X()
|
1652
|
-
y2 = edge.StartVertex().Y()
|
1653
|
-
z2 = edge.StartVertex().Z()
|
1654
|
-
dx = x2 - x1
|
1655
|
-
dy = y2 - y1
|
1656
|
-
dz = z2 - z1
|
1657
|
-
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
1658
|
-
phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
|
1659
|
-
if dist < 0.0001:
|
1660
|
-
theta = 0
|
1661
|
-
else:
|
1662
|
-
theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
|
1663
|
-
endcapB = Topology.Copy(endcapB)
|
1664
|
-
endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 1, 0], angle=theta)
|
1665
|
-
endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 0, 1], angle=phi+180)
|
1666
|
-
endcapB = Topology.Translate(endcapB, origin.X(), origin.Y(), origin.Z())
|
1667
|
-
return {'pipe': pipe, 'endcapA': endcapA, 'endcapB': endcapB}
|
1668
|
-
|
1669
|
-
@staticmethod
|
1670
|
-
def Prism(origin: topologic.Vertex = None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1,
|
1671
|
-
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1672
|
-
"""
|
1673
|
-
Description
|
1674
|
-
----------
|
1675
|
-
Creates a prism.
|
1676
|
-
|
1677
|
-
Parameters
|
1678
|
-
----------
|
1679
|
-
origin : topologic.Vertex , optional
|
1680
|
-
The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
|
1681
|
-
width : float , optional
|
1682
|
-
The width of the prism. The default is 1.
|
1683
|
-
length : float , optional
|
1684
|
-
The length of the prism. The default is 1.
|
1685
|
-
height : float , optional
|
1686
|
-
The height of the prism.
|
1687
|
-
uSides : int , optional
|
1688
|
-
The number of sides along the width. The default is 1.
|
1689
|
-
vSides : int , optional
|
1690
|
-
The number of sides along the length. The default is 1.
|
1691
|
-
wSides : int , optional
|
1692
|
-
The number of sides along the height. The default is 1.
|
1693
|
-
direction : list , optional
|
1694
|
-
The vector representing the up direction of the prism. The default is [0, 0, 1].
|
1695
|
-
placement : str , optional
|
1696
|
-
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".
|
1697
|
-
tolerance : float , optional
|
1698
|
-
The desired tolerance. The default is 0.0001.
|
1699
|
-
|
1700
|
-
Returns
|
1701
|
-
-------
|
1702
|
-
topologic.Cell
|
1703
|
-
The created prism.
|
1704
|
-
|
1705
|
-
"""
|
1706
|
-
def sliceCell(cell, width, length, height, uSides, vSides, wSides):
|
1707
|
-
origin = cell.Centroid()
|
1708
|
-
shells = []
|
1709
|
-
_ = cell.Shells(None, shells)
|
1710
|
-
shell = shells[0]
|
1711
|
-
wRect = Wire.Rectangle(origin=origin, width=width*1.2, length=length*1.2, direction=[0, 0, 1], placement="center")
|
1712
|
-
sliceFaces = []
|
1713
|
-
for i in range(1, wSides):
|
1714
|
-
sliceFaces.append(Topology.Translate(Face.ByWire(wRect, tolerance=tolerance), 0, 0, height/wSides*i - height*0.5))
|
1715
|
-
uRect = Wire.Rectangle(origin=origin, width=height*1.2, length=length*1.2, direction=[1, 0, 0], placement="center")
|
1716
|
-
for i in range(1, uSides):
|
1717
|
-
sliceFaces.append(Topology.Translate(Face.ByWire(uRect, tolerance=tolerance), width/uSides*i - width*0.5, 0, 0))
|
1718
|
-
vRect = Wire.Rectangle(origin=origin, width=height*1.2, length=width*1.2, direction=[0, 1, 0], placement="center")
|
1719
|
-
for i in range(1, vSides):
|
1720
|
-
sliceFaces.append(Topology.Translate(Face.ByWire(vRect, tolerance=tolerance), 0, length/vSides*i - length*0.5, 0))
|
1721
|
-
if len(sliceFaces) > 0:
|
1722
|
-
sliceCluster = topologic.Cluster.ByTopologies(sliceFaces)
|
1723
|
-
shell = Topology.Slice(topologyA=shell, topologyB=sliceCluster, tranDict=False, tolerance=tolerance)
|
1724
|
-
return Cell.ByShell(shell)
|
1725
|
-
return cell
|
1726
|
-
|
1727
|
-
from topologicpy.Vertex import Vertex
|
1728
|
-
from topologicpy.Face import Face
|
1729
|
-
from topologicpy.Topology import Topology
|
1730
|
-
|
1731
|
-
if not origin:
|
1732
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1733
|
-
if not isinstance(origin, topologic.Vertex):
|
1734
|
-
print("Cell.Prism - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1735
|
-
return None
|
1736
|
-
xOffset = 0
|
1737
|
-
yOffset = 0
|
1738
|
-
zOffset = 0
|
1739
|
-
if placement.lower() == "center":
|
1740
|
-
zOffset = -height*0.5
|
1741
|
-
elif placement.lower() == "lowerleft":
|
1742
|
-
xOffset = width*0.5
|
1743
|
-
yOffset = length*0.5
|
1744
|
-
vb1 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
|
1745
|
-
vb2 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
|
1746
|
-
vb3 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
|
1747
|
-
vb4 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
|
1748
|
-
|
1749
|
-
baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
|
1750
|
-
baseFace = Face.ByWire(baseWire, tolerance=tolerance)
|
1751
|
-
|
1752
|
-
prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False)
|
1753
|
-
|
1754
|
-
if uSides > 1 or vSides > 1 or wSides > 1:
|
1755
|
-
prism = sliceCell(prism, width, length, height, uSides, vSides, wSides)
|
1756
|
-
prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1757
|
-
return prism
|
1758
|
-
|
1759
|
-
@staticmethod
|
1760
|
-
def RemoveCollinearEdges(cell: topologic.Cell, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
|
1761
|
-
"""
|
1762
|
-
Removes any collinear edges in the input cell.
|
1763
|
-
|
1764
|
-
Parameters
|
1765
|
-
----------
|
1766
|
-
cell : topologic.Cell
|
1767
|
-
The input cell.
|
1768
|
-
angTolerance : float , optional
|
1769
|
-
The desired angular tolerance. The default is 0.1.
|
1770
|
-
tolerance : float , optional
|
1771
|
-
The desired tolerance. The default is 0.0001.
|
1772
|
-
|
1773
|
-
Returns
|
1774
|
-
-------
|
1775
|
-
topologic.Cell
|
1776
|
-
The created cell without any collinear edges.
|
1777
|
-
|
1778
|
-
"""
|
1779
|
-
from topologicpy.Face import Face
|
1780
|
-
|
1781
|
-
if not isinstance(cell, topologic.Cell):
|
1782
|
-
print("Cell.RemoveCollinearEdges - Error: The input cell parameter is not a valid cell. Returning None.")
|
1783
|
-
return None
|
1784
|
-
faces = Cell.Faces(cell)
|
1785
|
-
clean_faces = []
|
1786
|
-
for face in faces:
|
1787
|
-
clean_faces.append(Face.RemoveCollinearEdges(face, angTolerance=angTolerance, tolerance=tolerance))
|
1788
|
-
return Cell.ByFaces(clean_faces, tolerance=tolerance)
|
1789
|
-
|
1790
|
-
@staticmethod
|
1791
|
-
def Roof(face, angle: float = 45, epsilon: float = 0.01 , tolerance: float = 0.001):
|
1792
|
-
"""
|
1793
|
-
Creates a hipped roof through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
|
1794
|
-
This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel
|
1795
|
-
|
1796
|
-
Parameters
|
1797
|
-
----------
|
1798
|
-
face : topologic.Face
|
1799
|
-
The input face.
|
1800
|
-
angle : float , optioal
|
1801
|
-
The desired angle in degrees of the roof. The default is 45.
|
1802
|
-
epsilon : float , optional
|
1803
|
-
The desired epsilon (another form of tolerance for distance from plane). The default is 0.01. (This is set to a larger number as it was found to work better)
|
1804
|
-
tolerance : float , optional
|
1805
|
-
The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)
|
1806
|
-
|
1807
|
-
Returns
|
1808
|
-
-------
|
1809
|
-
cell
|
1810
|
-
The created roof.
|
1811
|
-
|
1812
|
-
"""
|
1813
|
-
from topologicpy.Shell import Shell
|
1814
|
-
from topologicpy.Cell import Cell
|
1815
|
-
from topologicpy.Topology import Topology
|
1816
|
-
|
1817
|
-
shell = Shell.Roof(face=face, angle=angle, epsilon=epsilon, tolerance=tolerance)
|
1818
|
-
faces = Topology.Faces(shell) + [face]
|
1819
|
-
cell = Cell.ByFaces(faces, tolerance=tolerance)
|
1820
|
-
if not cell:
|
1821
|
-
print("Cell.Roof - Error: Could not create a roof cell. Returning None.")
|
1822
|
-
return None
|
1823
|
-
return cell
|
1824
|
-
|
1825
|
-
@staticmethod
|
1826
|
-
def Sets(cells: list, superCells: list, tolerance: float = 0.0001) -> list:
|
1827
|
-
"""
|
1828
|
-
Classifies the input cells into sets based on their enclosure within the input list of super cells. The order of the sets follows the order of the input list of super cells.
|
1829
|
-
|
1830
|
-
Parameters
|
1831
|
-
----------
|
1832
|
-
inputCells : list
|
1833
|
-
The list of input cells.
|
1834
|
-
superCells : list
|
1835
|
-
The list of super cells.
|
1836
|
-
tolerance : float , optional
|
1837
|
-
The desired tolerance. The default is 0.0001.
|
1838
|
-
|
1839
|
-
Returns
|
1840
|
-
-------
|
1841
|
-
list
|
1842
|
-
The classified list of input cells based on their encolsure within the input list of super cells.
|
1843
|
-
|
1844
|
-
"""
|
1845
|
-
|
1846
|
-
from topologicpy.Vertex import Vertex
|
1847
|
-
from topologicpy.Topology import Topology
|
1848
|
-
|
1849
|
-
if not isinstance(cells, list):
|
1850
|
-
print("Cell.Sets - Error: The input cells parameter is not a valid list. Returning None.")
|
1851
|
-
return None
|
1852
|
-
if not isinstance(superCells, list):
|
1853
|
-
print("Cell.Sets - Error: The input superCells parameter is not a valid list. Returning None.")
|
1854
|
-
return None
|
1855
|
-
cells = [c for c in cells if isinstance(c, topologic.Cell)]
|
1856
|
-
if len(cells) < 1:
|
1857
|
-
print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
|
1858
|
-
return None
|
1859
|
-
superCells = [c for c in superCells if isinstance(c, topologic.Cell)]
|
1860
|
-
if len(cells) < 1:
|
1861
|
-
print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
|
1862
|
-
return None
|
1863
|
-
if len(superCells) == 0:
|
1864
|
-
cluster = cells[0]
|
1865
|
-
for i in range(1, len(cells)):
|
1866
|
-
oldCluster = cluster
|
1867
|
-
cluster = cluster.Union(cells[i])
|
1868
|
-
del oldCluster
|
1869
|
-
superCells = []
|
1870
|
-
_ = cluster.Cells(None, superCells)
|
1871
|
-
unused = []
|
1872
|
-
for i in range(len(cells)):
|
1873
|
-
unused.append(True)
|
1874
|
-
sets = []
|
1875
|
-
for i in range(len(superCells)):
|
1876
|
-
sets.append([])
|
1877
|
-
for i in range(len(cells)):
|
1878
|
-
if unused[i]:
|
1879
|
-
iv = Topology.InternalVertex(cells[i], tolerance=tolerance)
|
1880
|
-
for j in range(len(superCells)):
|
1881
|
-
if (Vertex.IsInternal(iv, superCells[j], tolerance)):
|
1882
|
-
sets[j].append(cells[i])
|
1883
|
-
unused[i] = False
|
1884
|
-
return sets
|
1885
|
-
|
1886
|
-
@staticmethod
|
1887
|
-
def Shells(cell: topologic.Cell) -> list:
|
1888
|
-
"""
|
1889
|
-
Returns the shells of the input cell.
|
1890
|
-
|
1891
|
-
Parameters
|
1892
|
-
----------
|
1893
|
-
cell : topologic.Cell
|
1894
|
-
The input cell.
|
1895
|
-
|
1896
|
-
Returns
|
1897
|
-
-------
|
1898
|
-
list
|
1899
|
-
The list of shells.
|
1900
|
-
|
1901
|
-
"""
|
1902
|
-
if not isinstance(cell, topologic.Cell):
|
1903
|
-
print("Cell.Shells - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1904
|
-
return None
|
1905
|
-
shells = []
|
1906
|
-
_ = cell.Shells(None, shells)
|
1907
|
-
return shells
|
1908
|
-
|
1909
|
-
@staticmethod
|
1910
|
-
def Sphere(origin: topologic.Vertex = None, radius: float = 0.5, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
|
1911
|
-
placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
1912
|
-
"""
|
1913
|
-
Creates a sphere.
|
1914
|
-
|
1915
|
-
Parameters
|
1916
|
-
----------
|
1917
|
-
origin : topologic.Vertex , optional
|
1918
|
-
The origin location of the sphere. The default is None which results in the sphere being placed at (0, 0, 0).
|
1919
|
-
radius : float , optional
|
1920
|
-
The radius of the sphere. The default is 0.5.
|
1921
|
-
uSides : int , optional
|
1922
|
-
The number of sides along the longitude of the sphere. The default is 16.
|
1923
|
-
vSides : int , optional
|
1924
|
-
The number of sides along the latitude of the sphere. The default is 8.
|
1925
|
-
direction : list , optional
|
1926
|
-
The vector representing the up direction of the sphere. The default is [0, 0, 1].
|
1927
|
-
placement : str , optional
|
1928
|
-
The description of the placement of the origin of the sphere. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1929
|
-
tolerance : float , optional
|
1930
|
-
The desired tolerance. The default is 0.0001.
|
1931
|
-
|
1932
|
-
Returns
|
1933
|
-
-------
|
1934
|
-
topologic.Cell
|
1935
|
-
The created sphere.
|
1936
|
-
|
1937
|
-
"""
|
1938
|
-
|
1939
|
-
from topologicpy.Vertex import Vertex
|
1940
|
-
from topologicpy.Topology import Topology
|
1941
|
-
|
1942
|
-
if not origin:
|
1943
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
1944
|
-
if not isinstance(origin, topologic.Vertex):
|
1945
|
-
print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1946
|
-
return None
|
1947
|
-
c = Wire.Circle(origin=Vertex.Origin(), radius=radius, sides=vSides, fromAngle=-90, toAngle=90, close=False, direction=[0, 0, 1], placement="center")
|
1948
|
-
c = Topology.Rotate(c, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
|
1949
|
-
sphere = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
1950
|
-
if sphere.Type() == topologic.CellComplex.Type():
|
1951
|
-
sphere = sphere.ExternalBoundary()
|
1952
|
-
if sphere.Type() == topologic.Shell.Type():
|
1953
|
-
sphere = topologic.Cell.ByShell(sphere)
|
1954
|
-
if placement.lower() == "bottom":
|
1955
|
-
sphere = Topology.Translate(sphere, 0, 0, radius)
|
1956
|
-
elif placement.lower() == "lowerleft":
|
1957
|
-
sphere = Topology.Translate(sphere, radius, radius, radius)
|
1958
|
-
sphere = Topology.Orient(sphere, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1959
|
-
sphere = Topology.Place(sphere, originA=Vertex.Origin(), originB=origin)
|
1960
|
-
return sphere
|
1961
|
-
|
1962
|
-
@staticmethod
|
1963
|
-
def SurfaceArea(cell: topologic.Cell, mantissa: int = 6) -> float:
|
1964
|
-
"""
|
1965
|
-
Returns the surface area of the input cell.
|
1966
|
-
|
1967
|
-
Parameters
|
1968
|
-
----------
|
1969
|
-
cell : topologic.Cell
|
1970
|
-
The cell.
|
1971
|
-
mantissa : int , optional
|
1972
|
-
The desired length of the mantissa. The default is 6.
|
1973
|
-
|
1974
|
-
Returns
|
1975
|
-
-------
|
1976
|
-
area : float
|
1977
|
-
The surface area of the input cell.
|
1978
|
-
|
1979
|
-
"""
|
1980
|
-
return Cell.Area(cell=cell, mantissa=mantissa)
|
1981
|
-
|
1982
|
-
@staticmethod
|
1983
|
-
def Tetrahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
1984
|
-
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1985
|
-
"""
|
1986
|
-
Description
|
1987
|
-
----------
|
1988
|
-
Creates a tetrahedron. See https://en.wikipedia.org/wiki/Tetrahedron.
|
1989
|
-
|
1990
|
-
Parameters
|
1991
|
-
----------
|
1992
|
-
origin : topologic.Vertex , optional
|
1993
|
-
The origin location of the tetrahedron. The default is None which results in the tetrahedron being placed at (0, 0, 0).
|
1994
|
-
radius : float , optional
|
1995
|
-
The radius of the tetrahedron's circumscribed sphere. The default is 0.5.
|
1996
|
-
direction : list , optional
|
1997
|
-
The vector representing the up direction of the tetrahedron. The default is [0, 0, 1].
|
1998
|
-
placement : str , optional
|
1999
|
-
The description of the placement of the origin of the tetrahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
2000
|
-
tolerance : float , optional
|
2001
|
-
The desired tolerance. The default is 0.0001.
|
2002
|
-
|
2003
|
-
Returns
|
2004
|
-
-------
|
2005
|
-
topologic.Cell
|
2006
|
-
The created tetrahedron.
|
2007
|
-
|
2008
|
-
"""
|
2009
|
-
|
2010
|
-
from topologicpy.Vertex import Vertex
|
2011
|
-
from topologicpy.Wire import Wire
|
2012
|
-
from topologicpy.Face import Face
|
2013
|
-
from topologicpy.Topology import Topology
|
2014
|
-
import math
|
2015
|
-
|
2016
|
-
if not origin:
|
2017
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
2018
|
-
if not isinstance(origin, topologic.Vertex):
|
2019
|
-
print("Cell.Tetrahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
2020
|
-
return None
|
2021
|
-
|
2022
|
-
vb1 = Vertex.ByCoordinates(math.sqrt(8/9), 0, -1/3)
|
2023
|
-
vb2 = Vertex.ByCoordinates(-math.sqrt(2/9), math.sqrt(2/3), -1/3)
|
2024
|
-
vb3 = Vertex.ByCoordinates(-math.sqrt(2/9), -math.sqrt(2/3), -1/3)
|
2025
|
-
vb4 = Vertex.ByCoordinates(0, 0, 1)
|
2026
|
-
f1 = Face.ByVertices([vb1, vb2, vb3])
|
2027
|
-
f2 = Face.ByVertices([vb4, vb1, vb2])
|
2028
|
-
f3 = Face.ByVertices([vb4, vb2, vb3])
|
2029
|
-
f4 = Face.ByVertices([vb4, vb3, vb1])
|
2030
|
-
tetrahedron = Cell.ByFaces([f1, f2, f3, f4])
|
2031
|
-
tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=0.5, y=0.5, z=0.5)
|
2032
|
-
tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
|
2033
|
-
|
2034
|
-
if placement.lower() == "lowerleft":
|
2035
|
-
tetrahedron = Topology.Translate(tetrahedron, radius, radius, radius)
|
2036
|
-
elif placement.lower() == "bottom":
|
2037
|
-
tetrahedron = Topology.Translate(tetrahedron, 0, 0, radius)
|
2038
|
-
tetrahedron = Topology.Place(tetrahedron, originA=Vertex.Origin(), originB=origin)
|
2039
|
-
tetrahedron = Topology.Orient(tetrahedron, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
2040
|
-
return tetrahedron
|
2041
|
-
|
2042
|
-
@staticmethod
|
2043
|
-
def Torus(origin: topologic.Vertex = None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
2044
|
-
"""
|
2045
|
-
Creates a torus.
|
2046
|
-
|
2047
|
-
Parameters
|
2048
|
-
----------
|
2049
|
-
origin : topologic.Vertex , optional
|
2050
|
-
The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
|
2051
|
-
majorRadius : float , optional
|
2052
|
-
The major radius of the torus. The default is 0.5.
|
2053
|
-
minorRadius : float , optional
|
2054
|
-
The minor radius of the torus. The default is 0.1.
|
2055
|
-
uSides : int , optional
|
2056
|
-
The number of sides along the longitude of the torus. The default is 16.
|
2057
|
-
vSides : int , optional
|
2058
|
-
The number of sides along the latitude of the torus. The default is 8.
|
2059
|
-
direction : list , optional
|
2060
|
-
The vector representing the up direction of the torus. The default is [0, 0, 1].
|
2061
|
-
placement : str , optional
|
2062
|
-
The description of the placement of the origin of the torus. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
2063
|
-
tolerance : float , optional
|
2064
|
-
The desired tolerance. The default is 0.0001.
|
2065
|
-
|
2066
|
-
Returns
|
2067
|
-
-------
|
2068
|
-
topologic.Cell
|
2069
|
-
The created torus.
|
2070
|
-
|
2071
|
-
"""
|
2072
|
-
|
2073
|
-
from topologicpy.Vertex import Vertex
|
2074
|
-
from topologicpy.Topology import Topology
|
2075
|
-
|
2076
|
-
if not origin:
|
2077
|
-
origin = Vertex.ByCoordinates(0, 0, 0)
|
2078
|
-
if not isinstance(origin, topologic.Vertex):
|
2079
|
-
print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
2080
|
-
return None
|
2081
|
-
c = Wire.Circle(origin=Vertex.Origin(), radius=minorRadius, sides=vSides, fromAngle=0, toAngle=360, close=False, direction=[0, 1, 0], placement="center")
|
2082
|
-
c = Topology.Translate(c, abs(majorRadius-minorRadius), 0, 0)
|
2083
|
-
torus = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
2084
|
-
if torus.Type() == topologic.Shell.Type():
|
2085
|
-
torus = topologic.Cell.ByShell(torus)
|
2086
|
-
if placement.lower() == "bottom":
|
2087
|
-
torus = Topology.Translate(torus, 0, 0, minorRadius)
|
2088
|
-
elif placement.lower() == "lowerleft":
|
2089
|
-
torus = Topology.Translate(torus, majorRadius, majorRadius, minorRadius)
|
2090
|
-
|
2091
|
-
torus = Topology.Orient(torus, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
2092
|
-
torus = Topology.Place(torus, originA=Vertex.Origin(), originB=origin)
|
2093
|
-
return torus
|
2094
|
-
|
2095
|
-
@staticmethod
|
2096
|
-
def Vertices(cell: topologic.Cell) -> list:
|
2097
|
-
"""
|
2098
|
-
Returns the vertices of the input cell.
|
2099
|
-
|
2100
|
-
Parameters
|
2101
|
-
----------
|
2102
|
-
cell : topologic.Cell
|
2103
|
-
The input cell.
|
2104
|
-
|
2105
|
-
Returns
|
2106
|
-
-------
|
2107
|
-
list
|
2108
|
-
The list of vertices.
|
2109
|
-
|
2110
|
-
"""
|
2111
|
-
if not isinstance(cell, topologic.Cell):
|
2112
|
-
print("Cell.Vertices - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
2113
|
-
return None
|
2114
|
-
vertices = []
|
2115
|
-
_ = cell.Vertices(None, vertices)
|
2116
|
-
return vertices
|
2117
|
-
|
2118
|
-
@staticmethod
|
2119
|
-
def Volume(cell: topologic.Cell, mantissa: int = 6) -> float:
|
2120
|
-
"""
|
2121
|
-
Returns the volume of the input cell.
|
2122
|
-
|
2123
|
-
Parameters
|
2124
|
-
----------
|
2125
|
-
cell : topologic.Cell
|
2126
|
-
The input cell.
|
2127
|
-
manitssa: int , optional
|
2128
|
-
The desired length of the mantissa. The default is 6.
|
2129
|
-
|
2130
|
-
Returns
|
2131
|
-
-------
|
2132
|
-
float
|
2133
|
-
The volume of the input cell.
|
2134
|
-
|
2135
|
-
"""
|
2136
|
-
if not isinstance(cell, topologic.Cell):
|
2137
|
-
print("Cell.Volume - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
2138
|
-
return None
|
2139
|
-
volume = None
|
2140
|
-
try:
|
2141
|
-
volume = round(topologic.CellUtility.Volume(cell), mantissa)
|
2142
|
-
except:
|
2143
|
-
print("Cell.Volume - Error: Could not compute the volume of the input cell. Returning None.")
|
2144
|
-
volume = None
|
2145
|
-
return volume
|
2146
|
-
|
2147
|
-
@staticmethod
|
2148
|
-
def Wires(cell: topologic.Cell) -> list:
|
2149
|
-
"""
|
2150
|
-
Returns the wires of the input cell.
|
2151
|
-
|
2152
|
-
Parameters
|
2153
|
-
----------
|
2154
|
-
cell : topologic.Cell
|
2155
|
-
The input cell.
|
2156
|
-
|
2157
|
-
Returns
|
2158
|
-
-------
|
2159
|
-
list
|
2160
|
-
The list of wires.
|
2161
|
-
|
2162
|
-
"""
|
2163
|
-
if not isinstance(cell, topologic.Cell):
|
2164
|
-
print("Cell.Wires - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
2165
|
-
return None
|
2166
|
-
wires = []
|
2167
|
-
_ = cell.Wires(None, wires)
|
2168
|
-
return wires
|
2169
|
-
|
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
|
+
from topologicpy.Wire import Wire
|
19
|
+
from topologicpy.Topology import Topology
|
20
|
+
import math
|
21
|
+
|
22
|
+
class Cell(Topology):
|
23
|
+
@staticmethod
|
24
|
+
def Area(cell: topologic.Cell, mantissa: int = 6) -> float:
|
25
|
+
"""
|
26
|
+
Returns the surface area of the input cell.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
----------
|
30
|
+
cell : topologic.Cell
|
31
|
+
The cell.
|
32
|
+
mantissa : int , optional
|
33
|
+
The desired length of the mantissa. The default is 6.
|
34
|
+
|
35
|
+
Returns
|
36
|
+
-------
|
37
|
+
float
|
38
|
+
The surface area of the input cell.
|
39
|
+
|
40
|
+
"""
|
41
|
+
from topologicpy.Face import Face
|
42
|
+
|
43
|
+
faces = []
|
44
|
+
_ = cell.Faces(None, faces)
|
45
|
+
area = 0.0
|
46
|
+
for aFace in faces:
|
47
|
+
area = area + Face.Area(aFace)
|
48
|
+
return round(area, mantissa)
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def Box(origin: topologic.Vertex = None,
|
52
|
+
width: float = 1, length: float = 1, height: float = 1,
|
53
|
+
uSides: int = 1, vSides: int = 1, wSides: int = 1,
|
54
|
+
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
55
|
+
"""
|
56
|
+
Creates a box.
|
57
|
+
|
58
|
+
Parameters
|
59
|
+
----------
|
60
|
+
origin : topologic.Vertex , optional
|
61
|
+
The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
|
62
|
+
width : float , optional
|
63
|
+
The width of the box. The default is 1.
|
64
|
+
length : float , optional
|
65
|
+
The length of the box. The default is 1.
|
66
|
+
height : float , optional
|
67
|
+
The height of the box.
|
68
|
+
uSides : int , optional
|
69
|
+
The number of sides along the width. The default is 1.
|
70
|
+
vSides : int , optional
|
71
|
+
The number of sides along the length. The default is 1.
|
72
|
+
wSides : int , optional
|
73
|
+
The number of sides along the height. The default is 1.
|
74
|
+
direction : list , optional
|
75
|
+
The vector representing the up direction of the box. The default is [0, 0, 1].
|
76
|
+
placement : str , optional
|
77
|
+
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".
|
78
|
+
tolerance : float , optional
|
79
|
+
The desired tolerance. The default is 0.0001.
|
80
|
+
|
81
|
+
Returns
|
82
|
+
-------
|
83
|
+
topologic.Cell
|
84
|
+
The created box.
|
85
|
+
|
86
|
+
"""
|
87
|
+
return Cell.Prism(origin=origin, width=width, length=length, height=height,
|
88
|
+
uSides=uSides, vSides=vSides, wSides=wSides,
|
89
|
+
direction=direction, placement=placement, tolerance=tolerance)
|
90
|
+
|
91
|
+
@staticmethod
|
92
|
+
def ByFaces(faces: list, planarize: bool = False, tolerance: float = 0.0001, silent=False) -> topologic.Cell:
|
93
|
+
"""
|
94
|
+
Creates a cell from the input list of faces.
|
95
|
+
|
96
|
+
Parameters
|
97
|
+
----------
|
98
|
+
faces : list
|
99
|
+
The input list of faces.
|
100
|
+
planarize : bool, optional
|
101
|
+
If set to True, the input faces are planarized before building the cell. Otherwise, they are not. The default is False.
|
102
|
+
tolerance : float , optional
|
103
|
+
The desired tolerance. The default is 0.0001.
|
104
|
+
silent : bool , optional
|
105
|
+
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
|
106
|
+
|
107
|
+
Returns
|
108
|
+
-------
|
109
|
+
topologic.Cell
|
110
|
+
The created cell.
|
111
|
+
|
112
|
+
"""
|
113
|
+
from topologicpy.Vertex import Vertex
|
114
|
+
from topologicpy.Wire import Wire
|
115
|
+
from topologicpy.Face import Face
|
116
|
+
from topologicpy.Topology import Topology
|
117
|
+
|
118
|
+
if not isinstance(faces, list):
|
119
|
+
if not silent:
|
120
|
+
print("Cell.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
|
121
|
+
return None
|
122
|
+
faceList = [x for x in faces if isinstance(x, topologic.Face)]
|
123
|
+
if len(faceList) < 1:
|
124
|
+
if not silent:
|
125
|
+
print("Cell.ByFaces - Error: The input faces parameter does not contain valid faces. Returning None.")
|
126
|
+
return None
|
127
|
+
# Try the default method
|
128
|
+
cell = topologic.Cell.ByFaces(faceList, tolerance)
|
129
|
+
if isinstance(cell, topologic.Cell):
|
130
|
+
return cell
|
131
|
+
|
132
|
+
# Fuse all the vertices first and rebuild the faces
|
133
|
+
all_vertices = []
|
134
|
+
wires = []
|
135
|
+
for f in faceList:
|
136
|
+
w = Face.Wire(f)
|
137
|
+
wires.append(w)
|
138
|
+
all_vertices += Topology.Vertices(w)
|
139
|
+
all_vertices = Vertex.Fuse(all_vertices, tolerance=tolerance)
|
140
|
+
new_faces = []
|
141
|
+
for w in wires:
|
142
|
+
face_vertices = []
|
143
|
+
for v in Topology.Vertices(w):
|
144
|
+
index = Vertex.Index(v, all_vertices, tolerance=tolerance)
|
145
|
+
if not index == None:
|
146
|
+
face_vertices.append(all_vertices[index])
|
147
|
+
new_w = Wire.ByVertices(face_vertices)
|
148
|
+
if isinstance(new_w, topologic.Wire):
|
149
|
+
new_f = Face.ByWire(new_w, silent=True)
|
150
|
+
if isinstance(new_f, topologic.Face):
|
151
|
+
new_faces.append(new_f)
|
152
|
+
elif isinstance(new_f, list):
|
153
|
+
new_faces += new_f
|
154
|
+
faceList = new_faces
|
155
|
+
planarizedList = []
|
156
|
+
enlargedList = []
|
157
|
+
if planarize:
|
158
|
+
planarizedList = [Face.Planarize(f, tolerance=tolerance) for f in faceList]
|
159
|
+
enlargedList = [Face.ByOffset(f, offset=-tolerance*10) for f in planarizedList]
|
160
|
+
cell = topologic.Cell.ByFaces(enlargedList, tolerance)
|
161
|
+
faceList = Topology.SubTopologies(cell, subTopologyType="face")
|
162
|
+
finalFaces = []
|
163
|
+
for f in faceList:
|
164
|
+
centroid = Topology.Centroid(f)
|
165
|
+
n = Face.Normal(f)
|
166
|
+
v = Topology.Translate(centroid, n[0]*0.01, n[1]*0.01, n[2]*0.01)
|
167
|
+
if not Vertex.IsInternal(v, cell):
|
168
|
+
finalFaces.append(f)
|
169
|
+
finalFinalFaces = []
|
170
|
+
for f in finalFaces:
|
171
|
+
vertices = Face.Vertices(f)
|
172
|
+
w = Wire.Cycles(Face.ExternalBoundary(f), maxVertices=len(vertices))[0]
|
173
|
+
f1 = Face.ByWire(w, tolerance=tolerance, silent=True)
|
174
|
+
if isinstance(f1, topologic.Face):
|
175
|
+
finalFinalFaces.append(f1)
|
176
|
+
elif isinstance(f1, list):
|
177
|
+
finalFinalFaces += f1
|
178
|
+
cell = topologic.Cell.ByFaces(finalFinalFaces, tolerance)
|
179
|
+
if cell == None:
|
180
|
+
if not silent:
|
181
|
+
print("Cell.ByFaces - Error: The operation failed. Returning None.")
|
182
|
+
return None
|
183
|
+
else:
|
184
|
+
return cell
|
185
|
+
else:
|
186
|
+
cell = topologic.Cell.ByFaces(faces, tolerance)
|
187
|
+
if cell == None:
|
188
|
+
if not silent:
|
189
|
+
print("Cell.ByFaces - Error: The operation failed. Returning None.")
|
190
|
+
return None
|
191
|
+
else:
|
192
|
+
return cell
|
193
|
+
@staticmethod
|
194
|
+
def ByOffset(cell: topologic.Cell, offset: float = 1.0, tolerance: float = 0.0001) -> topologic.Face:
|
195
|
+
"""
|
196
|
+
Creates an offset cell from the input cell.
|
197
|
+
|
198
|
+
Parameters
|
199
|
+
----------
|
200
|
+
cell : topologic.Cell
|
201
|
+
The input cell.
|
202
|
+
offset : float , optional
|
203
|
+
The desired offset distance. The default is 1.0.
|
204
|
+
tolerance : float , optional
|
205
|
+
The desired tolerance. The default is 0.0001.
|
206
|
+
|
207
|
+
Returns
|
208
|
+
-------
|
209
|
+
topologic.Topology
|
210
|
+
The created offset topology. WARNING: This method may fail to create a cell if the offset creates self-intersecting faces. Always check the type being returned by this method.
|
211
|
+
|
212
|
+
"""
|
213
|
+
from topologicpy.Face import Face
|
214
|
+
from topologicpy.Topology import Topology
|
215
|
+
from topologicpy.Vector import Vector
|
216
|
+
|
217
|
+
vertices = Topology.Vertices(cell)
|
218
|
+
new_vertices = []
|
219
|
+
for v in vertices:
|
220
|
+
faces = Topology.SuperTopologies(v, hostTopology=cell, topologyType="face")
|
221
|
+
normals = []
|
222
|
+
for face in faces:
|
223
|
+
normal = Vector.SetMagnitude(Face.Normal(face), offset)
|
224
|
+
normals.append(normal)
|
225
|
+
sum_normal = Vector.Sum(normals)
|
226
|
+
new_v = Topology.TranslateByDirectionDistance(v, direction=sum_normal, distance=Vector.Magnitude(sum_normal))
|
227
|
+
new_vertices.append(new_v)
|
228
|
+
new_cell = Topology.SelfMerge(Topology.ReplaceVertices(cell, Topology.Vertices(cell), new_vertices), tolerance=tolerance)
|
229
|
+
return new_cell
|
230
|
+
|
231
|
+
@staticmethod
|
232
|
+
def ByShell(shell: topologic.Shell, planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
233
|
+
"""
|
234
|
+
Creates a cell from the input shell.
|
235
|
+
|
236
|
+
Parameters
|
237
|
+
----------
|
238
|
+
shell : topologic.Shell
|
239
|
+
The input shell. The shell must be closed for this method to succeed.
|
240
|
+
planarize : bool, optional
|
241
|
+
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
|
242
|
+
tolerance : float , optional
|
243
|
+
The desired tolerance. The default is 0.0001.
|
244
|
+
|
245
|
+
Returns
|
246
|
+
-------
|
247
|
+
topologic.Cell
|
248
|
+
The created cell.
|
249
|
+
|
250
|
+
"""
|
251
|
+
from topologicpy.Topology import Topology
|
252
|
+
if not isinstance(shell, topologic.Shell):
|
253
|
+
print("Cell.ByShell - Error: The input shell parameter is not a valid topologic shell. Returning None.")
|
254
|
+
return None
|
255
|
+
faces = Topology.SubTopologies(shell, subTopologyType="face")
|
256
|
+
return Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance)
|
257
|
+
|
258
|
+
@staticmethod
|
259
|
+
def ByThickenedFace(face: topologic.Face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
|
260
|
+
planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
261
|
+
"""
|
262
|
+
Creates a cell by thickening the input face.
|
263
|
+
|
264
|
+
Parameters
|
265
|
+
----------
|
266
|
+
face : topologic.Face
|
267
|
+
The input face to be thickened.
|
268
|
+
thickness : float , optional
|
269
|
+
The desired thickness. The default is 1.0.
|
270
|
+
bothSides : bool
|
271
|
+
If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. The default is True.
|
272
|
+
reverse : bool
|
273
|
+
If True, the cell will be lofted in the opposite direction of the normal to the face. The default is False.
|
274
|
+
planarize : bool, optional
|
275
|
+
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
|
276
|
+
tolerance : float , optional
|
277
|
+
The desired tolerance. The default is 0.0001.
|
278
|
+
|
279
|
+
Returns
|
280
|
+
-------
|
281
|
+
topologic.Cell
|
282
|
+
The created cell.
|
283
|
+
|
284
|
+
"""
|
285
|
+
from topologicpy.Edge import Edge
|
286
|
+
from topologicpy.Face import Face
|
287
|
+
from topologicpy.Cluster import Cluster
|
288
|
+
from topologicpy.Topology import Topology
|
289
|
+
|
290
|
+
if not isinstance(face, topologic.Face):
|
291
|
+
print("Cell.ByThickenedFace - Error: The input face parameter is not a valid topologic face. Returning None.")
|
292
|
+
return None
|
293
|
+
if reverse == True and bothSides == False:
|
294
|
+
thickness = -thickness
|
295
|
+
faceNormal = Face.Normal(face)
|
296
|
+
if bothSides:
|
297
|
+
bottomFace = Topology.Translate(face, -faceNormal[0]*0.5*thickness, -faceNormal[1]*0.5*thickness, -faceNormal[2]*0.5*thickness)
|
298
|
+
topFace = Topology.Translate(face, faceNormal[0]*0.5*thickness, faceNormal[1]*0.5*thickness, faceNormal[2]*0.5*thickness)
|
299
|
+
else:
|
300
|
+
bottomFace = face
|
301
|
+
topFace = Topology.Translate(face, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
|
302
|
+
|
303
|
+
cellFaces = [bottomFace, topFace]
|
304
|
+
bottomEdges = []
|
305
|
+
_ = bottomFace.Edges(None, bottomEdges)
|
306
|
+
for bottomEdge in bottomEdges:
|
307
|
+
topEdge = Topology.Translate(bottomEdge, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
|
308
|
+
sideEdge1 = Edge.ByVertices([bottomEdge.StartVertex(), topEdge.StartVertex()], tolerance=tolerance, silent=True)
|
309
|
+
sideEdge2 = Edge.ByVertices([bottomEdge.EndVertex(), topEdge.EndVertex()], tolerance=tolerance, silent=True)
|
310
|
+
cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
|
311
|
+
cellFaces.append(Face.ByWire(cellWire, tolerance=tolerance))
|
312
|
+
return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
|
313
|
+
|
314
|
+
@staticmethod
|
315
|
+
def ByThickenedShell(shell: topologic.Shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
|
316
|
+
planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
317
|
+
"""
|
318
|
+
Creates a cell by thickening the input shell. The shell must be open.
|
319
|
+
|
320
|
+
Parameters
|
321
|
+
----------
|
322
|
+
shell : topologic.Shell
|
323
|
+
The input shell to be thickened.
|
324
|
+
thickness : float , optional
|
325
|
+
The desired thickness. The default is 1.0.
|
326
|
+
bothSides : bool
|
327
|
+
If True, the cell will be lofted to each side of the shell. Otherwise, it will be lofted along the input direction. The default is True.
|
328
|
+
reverse : bool
|
329
|
+
If True, the cell will be lofted along the opposite of the input direction. The default is False.
|
330
|
+
planarize : bool, optional
|
331
|
+
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
|
332
|
+
tolerance : float , optional
|
333
|
+
The desired tolerance. The default is 0.0001.
|
334
|
+
|
335
|
+
Returns
|
336
|
+
-------
|
337
|
+
topologic.Cell
|
338
|
+
The created cell.
|
339
|
+
|
340
|
+
"""
|
341
|
+
from topologicpy.Edge import Edge
|
342
|
+
from topologicpy.Wire import Wire
|
343
|
+
from topologicpy.Face import Face
|
344
|
+
from topologicpy.Shell import Shell
|
345
|
+
from topologicpy.Cluster import Cluster
|
346
|
+
from topologicpy.Topology import Topology
|
347
|
+
if not isinstance(shell, topologic.Shell):
|
348
|
+
print("Cell.ByThickenedShell - Error: The input shell parameter is not a valid topologic Shell. Returning None.")
|
349
|
+
return None
|
350
|
+
if reverse == True and bothSides == False:
|
351
|
+
thickness = -thickness
|
352
|
+
if bothSides:
|
353
|
+
bottomShell = Topology.Translate(shell, -direction[0]*0.5*thickness, -direction[1]*0.5*thickness, -direction[2]*0.5*thickness)
|
354
|
+
topShell = Topology.Translate(shell, direction[0]*0.5*thickness, direction[1]*0.5*thickness, direction[2]*0.5*thickness)
|
355
|
+
else:
|
356
|
+
bottomShell = shell
|
357
|
+
topShell = Topology.Translate(shell, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
|
358
|
+
cellFaces = Shell.Faces(bottomShell) + Shell.Faces(topShell)
|
359
|
+
bottomWire = Shell.ExternalBoundary(bottomShell, tolerance=tolerance)
|
360
|
+
bottomEdges = Wire.Edges(bottomWire)
|
361
|
+
for bottomEdge in bottomEdges:
|
362
|
+
topEdge = Topology.Translate(bottomEdge, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
|
363
|
+
sideEdge1 = Edge.ByVertices([Edge.StartVertex(bottomEdge), Edge.StartVertex(topEdge)], tolerance=tolerance, silent=True)
|
364
|
+
sideEdge2 = Edge.ByVertices([Edge.EndVertex(bottomEdge), Edge.EndVertex(topEdge)], tolerance=tolerance, silent=True)
|
365
|
+
cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
|
366
|
+
cellFace = Face.ByWire(cellWire, tolerance=tolerance)
|
367
|
+
cellFaces.append(cellFace)
|
368
|
+
return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
|
369
|
+
|
370
|
+
@staticmethod
|
371
|
+
def ByWires(wires: list, close: bool = False, triangulate: bool = True, planarize: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent=False) -> topologic.Cell:
|
372
|
+
"""
|
373
|
+
Creates a cell by lofting through the input list of wires.
|
374
|
+
|
375
|
+
Parameters
|
376
|
+
----------
|
377
|
+
wires : topologic.Wire
|
378
|
+
The input list of wires.
|
379
|
+
close : bool , optional
|
380
|
+
If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
|
381
|
+
triangulate : bool , optional
|
382
|
+
If set to True, the faces will be triangulated. The default is True.
|
383
|
+
mantissa : int , optional
|
384
|
+
The desired length of the mantissa. The default is 6.
|
385
|
+
tolerance : float , optional
|
386
|
+
The desired tolerance. The default is 0.0001.
|
387
|
+
silent : bool , optional
|
388
|
+
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
|
389
|
+
|
390
|
+
Raises
|
391
|
+
------
|
392
|
+
Exception
|
393
|
+
Raises an exception if the two wires in the list do not have the same number of edges.
|
394
|
+
|
395
|
+
Returns
|
396
|
+
-------
|
397
|
+
topologic.Cell
|
398
|
+
The created cell.
|
399
|
+
|
400
|
+
"""
|
401
|
+
from topologicpy.Edge import Edge
|
402
|
+
from topologicpy.Wire import Wire
|
403
|
+
from topologicpy.Face import Face
|
404
|
+
from topologicpy.Shell import Shell
|
405
|
+
from topologicpy.Topology import Topology
|
406
|
+
|
407
|
+
if not isinstance(wires, list):
|
408
|
+
if not silent:
|
409
|
+
print("Cell.ByWires - Error: The input wires parameter is not a valid list. Returning None.")
|
410
|
+
return None
|
411
|
+
wires = [w for w in wires if isinstance(w, topologic.Wire)]
|
412
|
+
if len(wires) < 2:
|
413
|
+
if not silent:
|
414
|
+
print("Cell.ByWires - Error: The input wires parameter contains less than two valid topologic wires. Returning None.")
|
415
|
+
return None
|
416
|
+
faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
|
417
|
+
if close == True:
|
418
|
+
faces.append(Face.ByWire(wires[0], tolerance=tolerance))
|
419
|
+
if triangulate == True:
|
420
|
+
triangles = []
|
421
|
+
for face in faces:
|
422
|
+
if len(Topology.Vertices(face)) > 3:
|
423
|
+
triangles += Face.Triangulate(face, tolerance=tolerance)
|
424
|
+
else:
|
425
|
+
triangles += [face]
|
426
|
+
faces = triangles
|
427
|
+
for i in range(len(wires)-1):
|
428
|
+
wire1 = wires[i]
|
429
|
+
wire2 = wires[i+1]
|
430
|
+
w1_edges = []
|
431
|
+
_ = wire1.Edges(None, w1_edges)
|
432
|
+
w2_edges = []
|
433
|
+
_ = wire2.Edges(None, w2_edges)
|
434
|
+
if len(w1_edges) != len(w2_edges):
|
435
|
+
if not silent:
|
436
|
+
print("Cell.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
|
437
|
+
return None
|
438
|
+
if triangulate == True:
|
439
|
+
for j in range (len(w1_edges)):
|
440
|
+
e1 = w1_edges[j]
|
441
|
+
e2 = w2_edges[j]
|
442
|
+
e3 = None
|
443
|
+
e4 = None
|
444
|
+
try:
|
445
|
+
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
446
|
+
except:
|
447
|
+
try:
|
448
|
+
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
449
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e4], tolerance=tolerance), tolerance=tolerance))
|
450
|
+
except:
|
451
|
+
pass
|
452
|
+
try:
|
453
|
+
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
454
|
+
except:
|
455
|
+
try:
|
456
|
+
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
457
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance))
|
458
|
+
except:
|
459
|
+
pass
|
460
|
+
if e3 and e4:
|
461
|
+
e5 = Edge.ByVertices([e1.StartVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
462
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
|
463
|
+
faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
|
464
|
+
else:
|
465
|
+
for j in range (len(w1_edges)):
|
466
|
+
e1 = w1_edges[j]
|
467
|
+
e2 = w2_edges[j]
|
468
|
+
e3 = None
|
469
|
+
e4 = None
|
470
|
+
try:
|
471
|
+
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
472
|
+
except:
|
473
|
+
try:
|
474
|
+
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
475
|
+
except:
|
476
|
+
pass
|
477
|
+
try:
|
478
|
+
e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
|
479
|
+
except:
|
480
|
+
try:
|
481
|
+
e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
|
482
|
+
except:
|
483
|
+
pass
|
484
|
+
if e3 and e4:
|
485
|
+
try:
|
486
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance))
|
487
|
+
except:
|
488
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance))
|
489
|
+
elif e3:
|
490
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
|
491
|
+
elif e4:
|
492
|
+
faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
|
493
|
+
cell = Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance, silent=silent)
|
494
|
+
if not cell:
|
495
|
+
shell = Shell.ByFaces(faces, tolerance=tolerance)
|
496
|
+
if isinstance(shell, topologic.Shell):
|
497
|
+
geom = Topology.Geometry(shell, mantissa=mantissa)
|
498
|
+
cell = Topology.ByGeometry(geom['vertices'], geom['edges'], geom['faces'])
|
499
|
+
if not isinstance(cell, topologic.Cell):
|
500
|
+
print("Cell.ByWires - Error: Could not create a cell. Returning None.")
|
501
|
+
return None
|
502
|
+
return cell
|
503
|
+
|
504
|
+
@staticmethod
|
505
|
+
def ByWiresCluster(cluster: topologic.Cluster, close: bool = False, triangulate: bool = True, planarize: bool = False, tolerance: float = 0.0001) -> topologic.Cell:
|
506
|
+
"""
|
507
|
+
Creates a cell by lofting through the input cluster of wires.
|
508
|
+
|
509
|
+
Parameters
|
510
|
+
----------
|
511
|
+
cluster : topologic.Cluster
|
512
|
+
The input Cluster of wires.
|
513
|
+
close : bool , optional
|
514
|
+
If set to True, the last wire in the cluster of input wires will be connected to the first wire in the cluster of input wires. The default is False.
|
515
|
+
triangulate : bool , optional
|
516
|
+
If set to True, the faces will be triangulated. The default is True.
|
517
|
+
tolerance : float , optional
|
518
|
+
The desired tolerance. The default is 0.0001.
|
519
|
+
|
520
|
+
Raises
|
521
|
+
------
|
522
|
+
Exception
|
523
|
+
Raises an exception if the two wires in the list do not have the same number of edges.
|
524
|
+
|
525
|
+
Returns
|
526
|
+
-------
|
527
|
+
topologic.Cell
|
528
|
+
The created cell.
|
529
|
+
|
530
|
+
"""
|
531
|
+
if not isinstance(cluster, topologic.Cluster):
|
532
|
+
print("Cell.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
|
533
|
+
return None
|
534
|
+
wires = []
|
535
|
+
_ = cluster.Wires(None, wires)
|
536
|
+
return Cell.ByWires(wires, close=close, triangulate=triangulate, planarize=planarize, tolerance=tolerance)
|
537
|
+
|
538
|
+
@staticmethod
|
539
|
+
def Capsule(origin: topologic.Vertex = None, radius: float = 0.25, height: float = 1, uSides: int = 16, vSidesEnds:int = 8, vSidesMiddle: int = 1, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
540
|
+
"""
|
541
|
+
Creates a capsule shape. A capsule is a cylinder with hemispherical ends.
|
542
|
+
|
543
|
+
Parameters
|
544
|
+
----------
|
545
|
+
origin : topologic.Vertex , optional
|
546
|
+
The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
|
547
|
+
radius : float , optional
|
548
|
+
The radius of the capsule. The default is 0.25.
|
549
|
+
height : float , optional
|
550
|
+
The height of the capsule. The default is 1.
|
551
|
+
uSides : int , optional
|
552
|
+
The number of circle segments of the capsule. The default is 16.
|
553
|
+
vSidesEnds : int , optional
|
554
|
+
The number of vertical segments of the end hemispheres. The default is 8.
|
555
|
+
vSidesMiddle : int , optional
|
556
|
+
The number of vertical segments of the middle cylinder. The default is 1.
|
557
|
+
direction : list , optional
|
558
|
+
The vector representing the up direction of the capsule. The default is [0, 0, 1].
|
559
|
+
placement : str , optional
|
560
|
+
The description of the placement of the origin of the capsule. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
|
561
|
+
tolerance : float , optional
|
562
|
+
The desired tolerance. The default is 0.0001.
|
563
|
+
|
564
|
+
Returns
|
565
|
+
-------
|
566
|
+
topologic.Cell
|
567
|
+
The created cell.
|
568
|
+
|
569
|
+
"""
|
570
|
+
from topologicpy.Topology import Topology
|
571
|
+
from topologicpy.Cell import Cell
|
572
|
+
from topologicpy.Vertex import Vertex
|
573
|
+
if not origin:
|
574
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
575
|
+
if not isinstance(origin, topologic.Vertex):
|
576
|
+
print("Cell.Capsule - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
577
|
+
return None
|
578
|
+
cyl_height = height - radius*2
|
579
|
+
if cyl_height <= 0:
|
580
|
+
capsule = Cell.Sphere(origin=Vertex.Origin(), radius=radius, uSides= uSides, vSides=vSidesEnds*2)
|
581
|
+
else:
|
582
|
+
cyl = Cell.Cylinder(origin=Vertex.Origin(),
|
583
|
+
radius=radius,
|
584
|
+
height=cyl_height,
|
585
|
+
uSides=uSides, vSides=vSidesMiddle, direction=[0, 0, 1], placement="center", tolerance=tolerance)
|
586
|
+
o1 = Vertex.ByCoordinates(0, 0, cyl_height*0.5)
|
587
|
+
o2 = Vertex.ByCoordinates(0, 0, -cyl_height*0.5)
|
588
|
+
s1 = Cell.Sphere(origin=o1, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
|
589
|
+
s2 = Cell.Sphere(origin=o2, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
|
590
|
+
capsule = Topology.Union(cyl, s1, tolerance=tolerance)
|
591
|
+
capsule = Topology.Union(capsule, s2, tolerance=tolerance)
|
592
|
+
if placement == "bottom":
|
593
|
+
capsule = Topology.Translate(capsule, 0, 0, height/2)
|
594
|
+
if placement == "lowerleft":
|
595
|
+
capsule = Topology.Translate(capsule, 0, 0, height/2)
|
596
|
+
capsule = Topology.Translate(capsule, radius, radius)
|
597
|
+
|
598
|
+
capsule = Topology.Orient(capsule, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
599
|
+
capsule = Topology.Place(capsule, originA=Vertex.Origin(), originB=origin)
|
600
|
+
return capsule
|
601
|
+
|
602
|
+
@staticmethod
|
603
|
+
def Compactness(cell: topologic.Cell, reference = "sphere", mantissa: int = 6) -> float:
|
604
|
+
"""
|
605
|
+
Returns the compactness measure of the input cell. If the reference is "sphere", this is also known as 'sphericity' (https://en.wikipedia.org/wiki/Sphericity).
|
606
|
+
|
607
|
+
Parameters
|
608
|
+
----------
|
609
|
+
cell : topologic.Cell
|
610
|
+
The input cell.
|
611
|
+
reference : str , optional
|
612
|
+
The desired reference to which to compare this compactness. The options are "sphere" and "cube". It is case insensitive. The default is "sphere".
|
613
|
+
mantissa : int , optional
|
614
|
+
The desired length of the mantissa. The default is 6.
|
615
|
+
|
616
|
+
Returns
|
617
|
+
-------
|
618
|
+
float
|
619
|
+
The compactness of the input cell.
|
620
|
+
|
621
|
+
"""
|
622
|
+
from topologicpy.Face import Face
|
623
|
+
faces = []
|
624
|
+
_ = cell.Faces(None, faces)
|
625
|
+
area = 0.0
|
626
|
+
for aFace in faces:
|
627
|
+
area = area + abs(Face.Area(aFace))
|
628
|
+
volume = abs(topologic.CellUtility.Volume(cell))
|
629
|
+
compactness = 0
|
630
|
+
#From https://en.wikipedia.org/wiki/Sphericity
|
631
|
+
if area > 0:
|
632
|
+
if reference.lower() == "sphere":
|
633
|
+
compactness = (((math.pi)**(1/3))*((6*volume)**(2/3)))/area
|
634
|
+
else:
|
635
|
+
compactness = 6*(volume**(2/3))/area
|
636
|
+
else:
|
637
|
+
print("Cell.Compactness - Error: cell surface area is not positive. Returning None.")
|
638
|
+
return None
|
639
|
+
return round(compactness, mantissa)
|
640
|
+
|
641
|
+
@staticmethod
|
642
|
+
def Cone(origin: topologic.Vertex = None, baseRadius: float = 0.5, topRadius: float = 0, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
|
643
|
+
dirZ: float = 1, placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
644
|
+
"""
|
645
|
+
Creates a cone.
|
646
|
+
|
647
|
+
Parameters
|
648
|
+
----------
|
649
|
+
origin : topologic.Vertex , optional
|
650
|
+
The location of the origin of the cone. The default is None which results in the cone being placed at (0, 0, 0).
|
651
|
+
baseRadius : float , optional
|
652
|
+
The radius of the base circle of the cone. The default is 0.5.
|
653
|
+
topRadius : float , optional
|
654
|
+
The radius of the top circle of the cone. The default is 0.
|
655
|
+
height : float , optional
|
656
|
+
The height of the cone. The default is 1.
|
657
|
+
sides : int , optional
|
658
|
+
The number of sides of the cone. The default is 16.
|
659
|
+
direction : list , optional
|
660
|
+
The vector representing the up direction of the cone. The default is [0, 0, 1].
|
661
|
+
placement : str , optional
|
662
|
+
The description of the placement of the origin of the cone. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
663
|
+
tolerance : float , optional
|
664
|
+
The desired tolerance. The default is 0.0001.
|
665
|
+
|
666
|
+
Returns
|
667
|
+
-------
|
668
|
+
topologic.Cell
|
669
|
+
The created cone.
|
670
|
+
|
671
|
+
"""
|
672
|
+
from topologicpy.Vertex import Vertex
|
673
|
+
from topologicpy.Wire import Wire
|
674
|
+
from topologicpy.Face import Face
|
675
|
+
from topologicpy.Shell import Shell
|
676
|
+
from topologicpy.Cluster import Cluster
|
677
|
+
from topologicpy.Topology import Topology
|
678
|
+
def createCone(baseWire, topWire, baseVertex, topVertex, tolerance):
|
679
|
+
if baseWire == None and topWire == None:
|
680
|
+
raise Exception("Cell.Cone - Error: Both radii of the cone cannot be zero at the same time")
|
681
|
+
elif baseWire == None:
|
682
|
+
apex = baseVertex
|
683
|
+
wire = topWire
|
684
|
+
elif topWire == None:
|
685
|
+
apex = topVertex
|
686
|
+
wire = baseWire
|
687
|
+
else:
|
688
|
+
return topologic.CellUtility.ByLoft([baseWire, topWire])
|
689
|
+
vertices = []
|
690
|
+
_ = wire.Vertices(None,vertices)
|
691
|
+
faces = [Face.ByWire(wire, tolerance=tolerance)]
|
692
|
+
for i in range(0, len(vertices)-1):
|
693
|
+
w = Wire.ByVertices([apex, vertices[i], vertices[i+1]])
|
694
|
+
f = Face.ByWire(w, tolerance=tolerance)
|
695
|
+
faces.append(f)
|
696
|
+
w = Wire.ByVertices([apex, vertices[-1], vertices[0]])
|
697
|
+
f = Face.ByWire(w, tolerance=tolerance)
|
698
|
+
faces.append(f)
|
699
|
+
return Cell.ByFaces(faces, tolerance=tolerance)
|
700
|
+
if not origin:
|
701
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
702
|
+
if not isinstance(origin, topologic.Vertex):
|
703
|
+
print("Cell.Cone - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
704
|
+
return None
|
705
|
+
xOffset = 0
|
706
|
+
yOffset = 0
|
707
|
+
zOffset = 0
|
708
|
+
if placement.lower() == "center":
|
709
|
+
xOffset = 0
|
710
|
+
yOffset = 0
|
711
|
+
zOffset = -height*0.5
|
712
|
+
elif placement.lower() == "lowerleft":
|
713
|
+
xOffset = max(baseRadius, topRadius)
|
714
|
+
yOffset = max(baseRadius, topRadius)
|
715
|
+
zOffset = 0
|
716
|
+
|
717
|
+
baseZ = origin.Z() + zOffset
|
718
|
+
topZ = origin.Z() + zOffset + height
|
719
|
+
baseV = []
|
720
|
+
topV = []
|
721
|
+
for i in range(uSides):
|
722
|
+
angle = math.radians(360/uSides)*i
|
723
|
+
if baseRadius > 0:
|
724
|
+
baseX = math.cos(angle)*baseRadius + origin.X() + xOffset
|
725
|
+
baseY = math.sin(angle)*baseRadius + origin.Y() + yOffset
|
726
|
+
baseZ = origin.Z() + zOffset
|
727
|
+
baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
|
728
|
+
if topRadius > 0:
|
729
|
+
topX = math.cos(angle)*topRadius + origin.X() + xOffset
|
730
|
+
topY = math.sin(angle)*topRadius + origin.Y() + yOffset
|
731
|
+
topV.append(Vertex.ByCoordinates(topX,topY,topZ))
|
732
|
+
if baseRadius > 0:
|
733
|
+
baseWire = Wire.ByVertices(baseV)
|
734
|
+
else:
|
735
|
+
baseWire = None
|
736
|
+
if topRadius > 0:
|
737
|
+
topWire = Wire.ByVertices(topV)
|
738
|
+
else:
|
739
|
+
topWire = None
|
740
|
+
baseVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset)
|
741
|
+
topVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset+height)
|
742
|
+
cone = createCone(baseWire, topWire, baseVertex, topVertex, tolerance)
|
743
|
+
if cone == None:
|
744
|
+
print("Cell.Cone - Error: Could not create a cone. Returning None.")
|
745
|
+
return None
|
746
|
+
|
747
|
+
if vSides > 1:
|
748
|
+
cutting_planes = []
|
749
|
+
baseX = origin.X() + xOffset
|
750
|
+
baseY = origin.Y() + yOffset
|
751
|
+
size = max(baseRadius, topRadius)*3
|
752
|
+
for i in range(1, vSides):
|
753
|
+
baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
|
754
|
+
tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
|
755
|
+
cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
|
756
|
+
cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
|
757
|
+
shell = Cell.Shells(cone)[0]
|
758
|
+
shell = shell.Slice(cutting_planes_cluster)
|
759
|
+
cone = Cell.ByShell(shell)
|
760
|
+
cone = Topology.Orient(cone, origin=origin, dirA=[0, 0, 1], dirB=direction)
|
761
|
+
return cone
|
762
|
+
|
763
|
+
@staticmethod
|
764
|
+
def ContainmentStatus(cell: topologic.Cell, vertex: topologic.Vertex, tolerance: float = 0.0001) -> int:
|
765
|
+
"""
|
766
|
+
Returns the containment status of the input vertex in relationship to the input cell
|
767
|
+
|
768
|
+
Parameters
|
769
|
+
----------
|
770
|
+
cell : topologic.Cell
|
771
|
+
The input cell.
|
772
|
+
vertex : topologic.Vertex
|
773
|
+
The input vertex.
|
774
|
+
tolerance : float , optional
|
775
|
+
The desired tolerance. The default is 0.0001.
|
776
|
+
|
777
|
+
Returns
|
778
|
+
-------
|
779
|
+
int
|
780
|
+
Returns 0 if the vertex is inside, 1 if it is on the boundary of, and 2 if it is outside the input cell.
|
781
|
+
|
782
|
+
"""
|
783
|
+
if not isinstance(cell, topologic.Cell):
|
784
|
+
print("Cell.ContainmentStatus - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
785
|
+
return None
|
786
|
+
if not isinstance(vertex, topologic.Vertex):
|
787
|
+
print("Cell.ContainmentStatus - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
|
788
|
+
return None
|
789
|
+
try:
|
790
|
+
status = topologic.CellUtility.Contains(cell, vertex, tolerance)
|
791
|
+
if status == 0:
|
792
|
+
return 0
|
793
|
+
elif status == 1:
|
794
|
+
return 1
|
795
|
+
else:
|
796
|
+
return 2
|
797
|
+
except:
|
798
|
+
print("Cell.ContainmentStatus - Error: Could not determine containment status. Returning None.")
|
799
|
+
return None
|
800
|
+
|
801
|
+
@staticmethod
|
802
|
+
def Cylinder(origin: topologic.Vertex = None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
|
803
|
+
placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
804
|
+
"""
|
805
|
+
Creates a cylinder.
|
806
|
+
|
807
|
+
Parameters
|
808
|
+
----------
|
809
|
+
origin : topologic.Vertex , optional
|
810
|
+
The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
|
811
|
+
radius : float , optional
|
812
|
+
The radius of the cylinder. The default is 0.5.
|
813
|
+
height : float , optional
|
814
|
+
The height of the cylinder. The default is 1.
|
815
|
+
uSides : int , optional
|
816
|
+
The number of circle segments of the cylinder. The default is 16.
|
817
|
+
vSides : int , optional
|
818
|
+
The number of vertical segments of the cylinder. The default is 1.
|
819
|
+
direction : list , optional
|
820
|
+
The vector representing the up direction of the cylinder. The default is [0, 0, 1].
|
821
|
+
placement : str , optional
|
822
|
+
The description of the placement of the origin of the cylinder. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
|
823
|
+
tolerance : float , optional
|
824
|
+
The desired tolerance. The default is 0.0001.
|
825
|
+
|
826
|
+
Returns
|
827
|
+
-------
|
828
|
+
topologic.Cell
|
829
|
+
The created cell.
|
830
|
+
|
831
|
+
"""
|
832
|
+
from topologicpy.Vertex import Vertex
|
833
|
+
from topologicpy.Face import Face
|
834
|
+
from topologicpy.CellComplex import CellComplex
|
835
|
+
from topologicpy.Cluster import Cluster
|
836
|
+
from topologicpy.Topology import Topology
|
837
|
+
if not origin:
|
838
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
839
|
+
if not isinstance(origin, topologic.Vertex):
|
840
|
+
print("Cell.Cylinder - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
841
|
+
return None
|
842
|
+
xOffset = 0
|
843
|
+
yOffset = 0
|
844
|
+
zOffset = 0
|
845
|
+
if placement.lower() == "center":
|
846
|
+
zOffset = -height*0.5
|
847
|
+
elif placement.lower() == "lowerleft":
|
848
|
+
xOffset = radius
|
849
|
+
yOffset = radius
|
850
|
+
circle_origin = Vertex.ByCoordinates(origin.X() + xOffset, origin.Y() + yOffset, origin.Z() + zOffset)
|
851
|
+
|
852
|
+
baseWire = Wire.Circle(origin=circle_origin, radius=radius, sides=uSides, fromAngle=0, toAngle=360, close=True, direction=[0, 0, 1], placement="center", tolerance=tolerance)
|
853
|
+
baseFace = Face.ByWire(baseWire, tolerance=tolerance)
|
854
|
+
cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, reverse=True,
|
855
|
+
tolerance=tolerance)
|
856
|
+
if vSides > 1:
|
857
|
+
cutting_planes = []
|
858
|
+
baseX = origin.X() + xOffset
|
859
|
+
baseY = origin.Y() + yOffset
|
860
|
+
size = radius*3
|
861
|
+
for i in range(1, vSides):
|
862
|
+
baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
|
863
|
+
tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
|
864
|
+
cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
|
865
|
+
cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
|
866
|
+
cylinder = CellComplex.ExternalBoundary(cylinder.Slice(cutting_planes_cluster))
|
867
|
+
|
868
|
+
cylinder = Topology.Orient(cylinder, origin=origin, dirA=[0, 0, 1], dirB=direction)
|
869
|
+
return cylinder
|
870
|
+
|
871
|
+
@staticmethod
|
872
|
+
def Decompose(cell: topologic.Cell, tiltAngle: float = 10, tolerance: float = 0.0001) -> dict:
|
873
|
+
"""
|
874
|
+
Decomposes the input cell into its logical components. This method assumes that the positive Z direction is UP.
|
875
|
+
|
876
|
+
Parameters
|
877
|
+
----------
|
878
|
+
cell : topologic.Cell
|
879
|
+
the input cell.
|
880
|
+
tiltAngle : float , optional
|
881
|
+
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.
|
882
|
+
tolerance : float , optional
|
883
|
+
The desired tolerance. The default is 0.0001.
|
884
|
+
|
885
|
+
Returns
|
886
|
+
-------
|
887
|
+
dictionary
|
888
|
+
A dictionary with the following keys and values:
|
889
|
+
1. "verticalFaces": list of vertical faces
|
890
|
+
2. "topHorizontalFaces": list of top horizontal faces
|
891
|
+
3. "bottomHorizontalFaces": list of bottom horizontal faces
|
892
|
+
4. "inclinedFaces": list of inclined faces
|
893
|
+
5. "verticalApertures": list of vertical apertures
|
894
|
+
6. "topHorizontalApertures": list of top horizontal apertures
|
895
|
+
7. "bottomHorizontalApertures": list of bottom horizontal apertures
|
896
|
+
8. "inclinedApertures": list of inclined apertures
|
897
|
+
|
898
|
+
"""
|
899
|
+
from topologicpy.Face import Face
|
900
|
+
from topologicpy.Vector import Vector
|
901
|
+
from topologicpy.Aperture import Aperture
|
902
|
+
from topologicpy.Topology import Topology
|
903
|
+
|
904
|
+
def angleCode(f, up, tiltAngle):
|
905
|
+
dirA = Face.NormalAtParameters(f)
|
906
|
+
ang = round(Vector.Angle(dirA, up), 2)
|
907
|
+
if abs(ang - 90) < tiltAngle:
|
908
|
+
code = 0
|
909
|
+
elif abs(ang) < tiltAngle:
|
910
|
+
code = 1
|
911
|
+
elif abs(ang - 180) < tiltAngle:
|
912
|
+
code = 2
|
913
|
+
else:
|
914
|
+
code = 3
|
915
|
+
return code
|
916
|
+
|
917
|
+
def getApertures(topology):
|
918
|
+
apTopologies = []
|
919
|
+
apertures = Topology.Apertures(topology)
|
920
|
+
if isinstance(apertures, list):
|
921
|
+
for aperture in apertures:
|
922
|
+
apTopologies.append(Aperture.Topology(aperture))
|
923
|
+
return apTopologies
|
924
|
+
|
925
|
+
if not isinstance(cell, topologic.Cell):
|
926
|
+
print("Cell.Decompose - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
927
|
+
return None
|
928
|
+
verticalFaces = []
|
929
|
+
topHorizontalFaces = []
|
930
|
+
bottomHorizontalFaces = []
|
931
|
+
inclinedFaces = []
|
932
|
+
verticalApertures = []
|
933
|
+
topHorizontalApertures = []
|
934
|
+
bottomHorizontalApertures = []
|
935
|
+
inclinedApertures = []
|
936
|
+
tiltAngle = abs(tiltAngle)
|
937
|
+
faces = Cell.Faces(cell)
|
938
|
+
zList = []
|
939
|
+
for f in faces:
|
940
|
+
zList.append(f.Centroid().Z())
|
941
|
+
zMin = min(zList)
|
942
|
+
zMax = max(zList)
|
943
|
+
up = [0, 0, 1]
|
944
|
+
for aFace in faces:
|
945
|
+
aCode = angleCode(aFace, up, tiltAngle)
|
946
|
+
|
947
|
+
if aCode == 0:
|
948
|
+
verticalFaces.append(aFace)
|
949
|
+
verticalApertures += getApertures(aFace)
|
950
|
+
elif aCode == 1:
|
951
|
+
if abs(aFace.Centroid().Z() - zMin) < tolerance:
|
952
|
+
bottomHorizontalFaces.append(aFace)
|
953
|
+
bottomHorizontalApertures += getApertures(aFace)
|
954
|
+
else:
|
955
|
+
topHorizontalFaces.append(aFace)
|
956
|
+
topHorizontalApertures += getApertures(aFace)
|
957
|
+
elif aCode == 2:
|
958
|
+
if abs(aFace.Centroid().Z() - zMax) < tolerance:
|
959
|
+
topHorizontalFaces.append(aFace)
|
960
|
+
topHorizontalApertures += getApertures(aFace)
|
961
|
+
else:
|
962
|
+
bottomHorizontalFaces.append(aFace)
|
963
|
+
bottomHorizontalApertures += getApertures(aFace)
|
964
|
+
elif aCode == 3:
|
965
|
+
inclinedFaces.append(aFace)
|
966
|
+
inclinedApertures += getApertures(aFace)
|
967
|
+
d = {
|
968
|
+
"verticalFaces" : verticalFaces,
|
969
|
+
"topHorizontalFaces" : topHorizontalFaces,
|
970
|
+
"bottomHorizontalFaces" : bottomHorizontalFaces,
|
971
|
+
"inclinedFaces" : inclinedFaces,
|
972
|
+
"verticalApertures" : verticalApertures,
|
973
|
+
"topHorizontalApertures" : topHorizontalApertures,
|
974
|
+
"bottomHorizontalApertures" : bottomHorizontalApertures,
|
975
|
+
"inclinedApertures" : inclinedApertures
|
976
|
+
}
|
977
|
+
return d
|
978
|
+
|
979
|
+
@staticmethod
|
980
|
+
def Dodecahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
981
|
+
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
982
|
+
"""
|
983
|
+
Description
|
984
|
+
----------
|
985
|
+
Creates a dodecahedron. See https://en.wikipedia.org/wiki/Dodecahedron.
|
986
|
+
|
987
|
+
Parameters
|
988
|
+
----------
|
989
|
+
origin : topologic.Vertex , optional
|
990
|
+
The origin location of the dodecahedron. The default is None which results in the dodecahedron being placed at (0, 0, 0).
|
991
|
+
radius : float , optional
|
992
|
+
The radius of the dodecahedron's circumscribed sphere. The default is 0.5.
|
993
|
+
direction : list , optional
|
994
|
+
The vector representing the up direction of the dodecahedron. The default is [0, 0, 1].
|
995
|
+
placement : str , optional
|
996
|
+
The description of the placement of the origin of the dodecahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
997
|
+
tolerance : float , optional
|
998
|
+
The desired tolerance. The default is 0.0001.
|
999
|
+
|
1000
|
+
Returns
|
1001
|
+
-------
|
1002
|
+
topologic.Cell
|
1003
|
+
The created dodecahedron.
|
1004
|
+
|
1005
|
+
"""
|
1006
|
+
from topologicpy.Vertex import Vertex
|
1007
|
+
from topologicpy.Edge import Edge
|
1008
|
+
from topologicpy.Face import Face
|
1009
|
+
from topologicpy.Cluster import Cluster
|
1010
|
+
from topologicpy.Topology import Topology
|
1011
|
+
|
1012
|
+
if not origin:
|
1013
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1014
|
+
if not isinstance(origin, topologic.Vertex):
|
1015
|
+
print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1016
|
+
return None
|
1017
|
+
pen = Face.Circle(sides=5, radius=0.5)
|
1018
|
+
pentagons = [pen]
|
1019
|
+
edges = Topology.Edges(pen)
|
1020
|
+
for edge in edges:
|
1021
|
+
o = Topology.Centroid(edge)
|
1022
|
+
e_dir = Edge.Direction(edge)
|
1023
|
+
pentagons.append(Topology.Rotate(pen, origin=o, axis=e_dir, angle=116.565))
|
1024
|
+
|
1025
|
+
cluster = Cluster.ByTopologies(pentagons)
|
1026
|
+
|
1027
|
+
cluster2 = Topology.Rotate(cluster, origin=Vertex.Origin(), axis=[1, 0, 0], angle=180)
|
1028
|
+
cluster2 = Topology.Rotate(cluster2, origin=Vertex.Origin(), axis=[0, 0, 1], angle=36)
|
1029
|
+
vertices = Topology.Vertices(cluster2)
|
1030
|
+
zList = [Vertex.Z(v) for v in vertices]
|
1031
|
+
zList = list(set(zList))
|
1032
|
+
zList.sort()
|
1033
|
+
zoffset1 = zList[-1] - zList[0]
|
1034
|
+
zoffset2 = zList[1] - zList[0]
|
1035
|
+
cluster2 = Topology.Translate(cluster2, 0, 0, -zoffset1-zoffset2)
|
1036
|
+
pentagons += Topology.Faces(cluster2)
|
1037
|
+
dodecahedron = Cell.ByFaces(pentagons, tolerance=tolerance)
|
1038
|
+
centroid = Topology.Centroid(dodecahedron)
|
1039
|
+
dodecahedron = Topology.Translate(dodecahedron, -Vertex.X(centroid), -Vertex.Y(centroid), -Vertex.Z(centroid))
|
1040
|
+
vertices = Topology.Vertices(dodecahedron)
|
1041
|
+
d = Vertex.Distance(Vertex.Origin(), vertices[0])
|
1042
|
+
dodecahedron = Topology.Scale(dodecahedron, origin=Vertex.Origin(), x=radius/d, y=radius/d, z=radius/d)
|
1043
|
+
if placement == "bottom":
|
1044
|
+
dodecahedron = Topology.Translate(dodecahedron, 0, 0, radius)
|
1045
|
+
elif placement == "lowerleft":
|
1046
|
+
dodecahedron = Topology.Translate(dodecahedron, radius, radius, radius)
|
1047
|
+
|
1048
|
+
dodecahedron = Topology.Orient(dodecahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1049
|
+
dodecahedron = Topology.Place(dodecahedron, originA=Vertex.Origin(), originB=origin)
|
1050
|
+
return dodecahedron
|
1051
|
+
|
1052
|
+
@staticmethod
|
1053
|
+
def Edges(cell: topologic.Cell) -> list:
|
1054
|
+
"""
|
1055
|
+
Returns the edges of the input cell.
|
1056
|
+
|
1057
|
+
Parameters
|
1058
|
+
----------
|
1059
|
+
cell : topologic.Cell
|
1060
|
+
The input cell.
|
1061
|
+
|
1062
|
+
Returns
|
1063
|
+
-------
|
1064
|
+
list
|
1065
|
+
The list of edges.
|
1066
|
+
|
1067
|
+
"""
|
1068
|
+
if not isinstance(cell, topologic.Cell):
|
1069
|
+
print("Cell.Edges - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1070
|
+
return None
|
1071
|
+
edges = []
|
1072
|
+
_ = cell.Edges(None, edges)
|
1073
|
+
return edges
|
1074
|
+
|
1075
|
+
@staticmethod
|
1076
|
+
def Egg(origin: topologic.Vertex = None, height: float = 1.0, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
|
1077
|
+
placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
1078
|
+
"""
|
1079
|
+
Creates an egg-shaped cell.
|
1080
|
+
|
1081
|
+
Parameters
|
1082
|
+
----------
|
1083
|
+
origin : topologic.Vertex , optional
|
1084
|
+
The origin location of the sphere. The default is None which results in the egg-shaped cell being placed at (0, 0, 0).
|
1085
|
+
height : float , optional
|
1086
|
+
The desired height of of the egg-shaped cell. The default is 1.0.
|
1087
|
+
uSides : int , optional
|
1088
|
+
The desired number of sides along the longitude of the egg-shaped cell. The default is 16.
|
1089
|
+
vSides : int , optional
|
1090
|
+
The desired number of sides along the latitude of the egg-shaped cell. The default is 8.
|
1091
|
+
direction : list , optional
|
1092
|
+
The vector representing the up direction of the egg-shaped cell. The default is [0, 0, 1].
|
1093
|
+
placement : str , optional
|
1094
|
+
The description of the placement of the origin of the egg-shaped cell. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1095
|
+
tolerance : float , optional
|
1096
|
+
The desired tolerance. The default is 0.0001.
|
1097
|
+
|
1098
|
+
Returns
|
1099
|
+
-------
|
1100
|
+
topologic.Cell
|
1101
|
+
The created egg-shaped cell.
|
1102
|
+
|
1103
|
+
"""
|
1104
|
+
|
1105
|
+
from topologicpy.Vertex import Vertex
|
1106
|
+
from topologicpy.Topology import Topology
|
1107
|
+
from topologicpy.Dictionary import Dictionary
|
1108
|
+
|
1109
|
+
if not origin:
|
1110
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1111
|
+
if not isinstance(origin, topologic.Vertex):
|
1112
|
+
print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1113
|
+
return None
|
1114
|
+
|
1115
|
+
coords = [[0.0, 0.0, -0.5],
|
1116
|
+
[0.074748, 0.0, -0.494015],
|
1117
|
+
[0.140819, 0.0, -0.473222],
|
1118
|
+
[0.204118, 0.0, -0.438358],
|
1119
|
+
[0.259512, 0.0, -0.391913],
|
1120
|
+
[0.304837, 0.0, -0.335519],
|
1121
|
+
[0.338649, 0.0, -0.271416],
|
1122
|
+
[0.361307, 0.0, -0.202039],
|
1123
|
+
[0.375678, 0.0, -0.129109],
|
1124
|
+
[0.381294, 0.0, -0.053696],
|
1125
|
+
[0.377694, 0.0, 0.019874],
|
1126
|
+
[0.365135, 0.0, 0.091978],
|
1127
|
+
[0.341482, 0.0, 0.173973],
|
1128
|
+
[0.300154, 0.0, 0.276001],
|
1129
|
+
[0.252928, 0.0, 0.355989],
|
1130
|
+
[0.206605, 0.0, 0.405813],
|
1131
|
+
[0.157529, 0.0, 0.442299],
|
1132
|
+
[0.10604, 0.0, 0.472092],
|
1133
|
+
[0.05547, 0.0, 0.491784],
|
1134
|
+
[0.0, 0.0, 0.5]]
|
1135
|
+
verts = [Vertex.ByCoordinates(coord) for coord in coords]
|
1136
|
+
c = Wire.ByVertices(verts, close=False)
|
1137
|
+
new_verts = []
|
1138
|
+
for i in range(vSides+1):
|
1139
|
+
new_verts.append(Wire.VertexByParameter(c, i/vSides))
|
1140
|
+
c = Wire.ByVertices(new_verts, close=False)
|
1141
|
+
egg = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
1142
|
+
if egg.Type() == topologic.CellComplex.Type():
|
1143
|
+
egg = egg.ExternalBoundary()
|
1144
|
+
if egg.Type() == topologic.Shell.Type():
|
1145
|
+
egg = topologic.Cell.ByShell(egg)
|
1146
|
+
egg = Topology.Scale(egg, origin=Vertex.Origin(), x=height, y=height, z=height)
|
1147
|
+
if placement.lower() == "bottom":
|
1148
|
+
egg = Topology.Translate(egg, 0, 0, height/2)
|
1149
|
+
elif placement.lower() == "lowerleft":
|
1150
|
+
bb = Cell.BoundingBox(egg)
|
1151
|
+
d = Topology.Dictionary(bb)
|
1152
|
+
width = Dictionary.ValueAtKey(d, 'width')
|
1153
|
+
length = Dictionary.ValueAtKey(d, 'length')
|
1154
|
+
egg = Topology.Translate(egg, width*0.5, length*0.5, height*0.5)
|
1155
|
+
egg = Topology.Orient(egg, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1156
|
+
egg = Topology.Place(egg, originA=Vertex.Origin(), originB=origin)
|
1157
|
+
return egg
|
1158
|
+
|
1159
|
+
@staticmethod
|
1160
|
+
def ExternalBoundary(cell: topologic.Cell) -> topologic.Shell:
|
1161
|
+
"""
|
1162
|
+
Returns the external boundary of the input cell.
|
1163
|
+
|
1164
|
+
Parameters
|
1165
|
+
----------
|
1166
|
+
cell : topologic.Cell
|
1167
|
+
The input cell.
|
1168
|
+
|
1169
|
+
Returns
|
1170
|
+
-------
|
1171
|
+
topologic.Shell
|
1172
|
+
The external boundary of the input cell.
|
1173
|
+
|
1174
|
+
"""
|
1175
|
+
|
1176
|
+
if not isinstance(cell, topologic.Cell):
|
1177
|
+
print("Cell.ExternalBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1178
|
+
return None
|
1179
|
+
try:
|
1180
|
+
return cell.ExternalBoundary()
|
1181
|
+
except:
|
1182
|
+
print("Cell.ExternalBoundary - Error: Could not compute the external boundary. Returning None.")
|
1183
|
+
return None
|
1184
|
+
|
1185
|
+
@staticmethod
|
1186
|
+
def Faces(cell: topologic.Cell) -> list:
|
1187
|
+
"""
|
1188
|
+
Returns the faces of the input cell.
|
1189
|
+
|
1190
|
+
Parameters
|
1191
|
+
----------
|
1192
|
+
cell : topologic.Cell
|
1193
|
+
The input cell.
|
1194
|
+
|
1195
|
+
Returns
|
1196
|
+
-------
|
1197
|
+
list
|
1198
|
+
The list of faces.
|
1199
|
+
|
1200
|
+
"""
|
1201
|
+
if not isinstance(cell, topologic.Cell):
|
1202
|
+
print("Cell.Faces - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1203
|
+
return None
|
1204
|
+
faces = []
|
1205
|
+
_ = cell.Faces(None, faces)
|
1206
|
+
return faces
|
1207
|
+
|
1208
|
+
@staticmethod
|
1209
|
+
def Hyperboloid(origin: topologic.Cell = None, baseRadius: float = 0.5, topRadius: float = 0.5, height: float = 1, sides: int = 24, direction: list = [0, 0, 1],
|
1210
|
+
twist: float = 60, placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
1211
|
+
"""
|
1212
|
+
Creates a hyperboloid.
|
1213
|
+
|
1214
|
+
Parameters
|
1215
|
+
----------
|
1216
|
+
origin : topologic.Vertex , optional
|
1217
|
+
The location of the origin of the hyperboloid. The default is None which results in the hyperboloid being placed at (0, 0, 0).
|
1218
|
+
baseRadius : float , optional
|
1219
|
+
The radius of the base circle of the hyperboloid. The default is 0.5.
|
1220
|
+
topRadius : float , optional
|
1221
|
+
The radius of the top circle of the hyperboloid. The default is 0.5.
|
1222
|
+
height : float , optional
|
1223
|
+
The height of the cone. The default is 1.
|
1224
|
+
sides : int , optional
|
1225
|
+
The number of sides of the cone. The default is 24.
|
1226
|
+
direction : list , optional
|
1227
|
+
The vector representing the up direction of the hyperboloid. The default is [0, 0, 1].
|
1228
|
+
twist : float , optional
|
1229
|
+
The angle to twist the base cylinder. The default is 60.
|
1230
|
+
placement : str , optional
|
1231
|
+
The description of the placement of the origin of the hyperboloid. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1232
|
+
tolerance : float , optional
|
1233
|
+
The desired tolerance. The default is 0.0001.
|
1234
|
+
|
1235
|
+
Returns
|
1236
|
+
-------
|
1237
|
+
topologic.Cell
|
1238
|
+
The created hyperboloid.
|
1239
|
+
|
1240
|
+
"""
|
1241
|
+
from topologicpy.Cluster import Cluster
|
1242
|
+
from topologicpy.Vertex import Vertex
|
1243
|
+
from topologicpy.Face import Face
|
1244
|
+
from topologicpy.Topology import Topology
|
1245
|
+
|
1246
|
+
def createHyperboloid(baseVertices, topVertices, tolerance):
|
1247
|
+
baseWire = Wire.ByVertices(baseVertices, close=True)
|
1248
|
+
topWire = Wire.ByVertices(topVertices, close=True)
|
1249
|
+
baseFace = Face.ByWire(baseWire, tolerance=tolerance)
|
1250
|
+
topFace = Face.ByWire(topWire, tolerance=tolerance)
|
1251
|
+
faces = [baseFace, topFace]
|
1252
|
+
for i in range(0, len(baseVertices)-1):
|
1253
|
+
w = Wire.ByVertices([baseVertices[i], topVertices[i], topVertices[i+1]], close=True)
|
1254
|
+
f = Face.ByWire(w, tolerance=tolerance)
|
1255
|
+
faces.append(f)
|
1256
|
+
w = Wire.ByVertices([baseVertices[i+1], baseVertices[i], topVertices[i+1]], close=True)
|
1257
|
+
f = Face.ByWire(w, tolerance=tolerance)
|
1258
|
+
faces.append(f)
|
1259
|
+
w = Wire.ByVertices([baseVertices[-1], topVertices[-1], topVertices[0]], close=True)
|
1260
|
+
f = Face.ByWire(w, tolerance=tolerance)
|
1261
|
+
faces.append(f)
|
1262
|
+
w = Wire.ByVertices([baseVertices[0], baseVertices[-1], topVertices[0]], close=True)
|
1263
|
+
f = Face.ByWire(w, tolerance=tolerance)
|
1264
|
+
faces.append(f)
|
1265
|
+
returnTopology = Cell.ByFaces(faces, tolerance=tolerance)
|
1266
|
+
if returnTopology == None:
|
1267
|
+
returnTopology = Cluster.ByTopologies(faces)
|
1268
|
+
return returnTopology
|
1269
|
+
|
1270
|
+
if not origin:
|
1271
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1272
|
+
if not isinstance(origin, topologic.Vertex):
|
1273
|
+
print("Cell.Hyperboloid - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1274
|
+
return None
|
1275
|
+
w_origin = Vertex.Origin()
|
1276
|
+
baseV = []
|
1277
|
+
topV = []
|
1278
|
+
xOffset = 0
|
1279
|
+
yOffset = 0
|
1280
|
+
zOffset = 0
|
1281
|
+
if placement.lower() == "center":
|
1282
|
+
zOffset = -height*0.5
|
1283
|
+
elif placement.lower() == "lowerleft":
|
1284
|
+
xOffset = max(baseRadius, topRadius)
|
1285
|
+
yOffset = max(baseRadius, topRadius)
|
1286
|
+
baseZ = w_origin.Z() + zOffset
|
1287
|
+
topZ = w_origin.Z() + zOffset + height
|
1288
|
+
for i in range(sides):
|
1289
|
+
angle = math.radians(360/sides)*i
|
1290
|
+
if baseRadius > 0:
|
1291
|
+
baseX = math.sin(angle+math.radians(twist))*baseRadius + w_origin.X() + xOffset
|
1292
|
+
baseY = math.cos(angle+math.radians(twist))*baseRadius + w_origin.Y() + yOffset
|
1293
|
+
baseZ = w_origin.Z() + zOffset
|
1294
|
+
baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
|
1295
|
+
if topRadius > 0:
|
1296
|
+
topX = math.sin(angle-math.radians(twist))*topRadius + w_origin.X() + xOffset
|
1297
|
+
topY = math.cos(angle-math.radians(twist))*topRadius + w_origin.Y() + yOffset
|
1298
|
+
topV.append(Vertex.ByCoordinates(topX,topY,topZ))
|
1299
|
+
|
1300
|
+
hyperboloid = createHyperboloid(baseV, topV, tolerance)
|
1301
|
+
if hyperboloid == None:
|
1302
|
+
print("Cell.Hyperboloid - Error: Could not create a hyperboloid. Returning None.")
|
1303
|
+
return None
|
1304
|
+
|
1305
|
+
hyperboloid = Topology.Orient(hyperboloid, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1306
|
+
hyperboloid = Topology.Place(hyperboloid, originA=Vertex.Origin(), originB=origin)
|
1307
|
+
return hyperboloid
|
1308
|
+
|
1309
|
+
@staticmethod
|
1310
|
+
def Icosahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
1311
|
+
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1312
|
+
"""
|
1313
|
+
Description
|
1314
|
+
----------
|
1315
|
+
Creates an icosahedron. See https://en.wikipedia.org/wiki/Icosahedron.
|
1316
|
+
|
1317
|
+
Parameters
|
1318
|
+
----------
|
1319
|
+
origin : topologic.Vertex , optional
|
1320
|
+
The origin location of the icosahedron. The default is None which results in the icosahedron being placed at (0, 0, 0).
|
1321
|
+
radius : float , optional
|
1322
|
+
The radius of the icosahedron's circumscribed sphere. The default is 0.5.
|
1323
|
+
direction : list , optional
|
1324
|
+
The vector representing the up direction of the icosahedron. The default is [0, 0, 1].
|
1325
|
+
placement : str , optional
|
1326
|
+
The description of the placement of the origin of the icosahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1327
|
+
tolerance : float , optional
|
1328
|
+
The desired tolerance. The default is 0.0001.
|
1329
|
+
|
1330
|
+
Returns
|
1331
|
+
-------
|
1332
|
+
topologic.Cell
|
1333
|
+
The created icosahedron.
|
1334
|
+
|
1335
|
+
"""
|
1336
|
+
from topologicpy.Vertex import Vertex
|
1337
|
+
from topologicpy.Wire import Wire
|
1338
|
+
from topologicpy.Face import Face
|
1339
|
+
from topologicpy.Topology import Topology
|
1340
|
+
import math
|
1341
|
+
|
1342
|
+
if not origin:
|
1343
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1344
|
+
if not isinstance(origin, topologic.Vertex):
|
1345
|
+
print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1346
|
+
return None
|
1347
|
+
rect1 = Wire.Rectangle(width=(1+math.sqrt(5))/2, length=1)
|
1348
|
+
rect2 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
|
1349
|
+
rect2 = Topology.Rotate(rect2, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
|
1350
|
+
rect3 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
|
1351
|
+
rect3 = Topology.Rotate(rect3, origin=Vertex.Origin(), axis=[0, 1, 0], angle=90)
|
1352
|
+
vertices = Topology.Vertices(rect1)
|
1353
|
+
v1, v2, v3, v4 = vertices
|
1354
|
+
vertices = Topology.Vertices(rect2)
|
1355
|
+
v5, v6, v7, v8 = vertices
|
1356
|
+
vertices = Topology.Vertices(rect3)
|
1357
|
+
v9, v10, v11, v12 = vertices
|
1358
|
+
f1 = Face.ByVertices([v1,v8,v4])
|
1359
|
+
f2 = Face.ByVertices([v1,v4,v5])
|
1360
|
+
f3 = Face.ByVertices([v3,v2,v6])
|
1361
|
+
f4 = Face.ByVertices([v2,v3,v7])
|
1362
|
+
f5 = Face.ByVertices([v10,v9,v2])
|
1363
|
+
f6 = Face.ByVertices([v10,v9,v1])
|
1364
|
+
f7 = Face.ByVertices([v12,v11,v4])
|
1365
|
+
f8 = Face.ByVertices([v12,v11,v3])
|
1366
|
+
f9 = Face.ByVertices([v8,v7,v9])
|
1367
|
+
f10 = Face.ByVertices([v8,v7,v12])
|
1368
|
+
f11 = Face.ByVertices([v5,v6,v10])
|
1369
|
+
f12 = Face.ByVertices([v5,v6,v11])
|
1370
|
+
f13 = Face.ByVertices([v8,v1,v9])
|
1371
|
+
f14 = Face.ByVertices([v9,v2,v7])
|
1372
|
+
f15 = Face.ByVertices([v7,v3,v12])
|
1373
|
+
f16 = Face.ByVertices([v8,v12,v4])
|
1374
|
+
f17 = Face.ByVertices([v1,v5,v10])
|
1375
|
+
f18 = Face.ByVertices([v10,v2,v6])
|
1376
|
+
f19 = Face.ByVertices([v6,v3,v11])
|
1377
|
+
f20 = Face.ByVertices([v11,v4,v5])
|
1378
|
+
|
1379
|
+
icosahedron = Cell.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,
|
1380
|
+
f11,f12,f13,f14,f15,f16,f17,f18,f19,f20], tolerance=tolerance)
|
1381
|
+
sf = 1.051*0.5 # To insribe it in a sphere of radius 0.5
|
1382
|
+
icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
|
1383
|
+
sf = radius/0.5
|
1384
|
+
icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
|
1385
|
+
if placement == "bottom":
|
1386
|
+
icosahedron = Topology.Translate(icosahedron, 0, 0, radius)
|
1387
|
+
elif placement == "lowerleft":
|
1388
|
+
icosahedron = Topology.Translate(icosahedron, radius, radius, radius)
|
1389
|
+
|
1390
|
+
icosahedron = Topology.Orient(icosahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1391
|
+
icosahedron = Topology.Place(icosahedron, originA=Vertex.Origin(), originB=origin)
|
1392
|
+
return icosahedron
|
1393
|
+
|
1394
|
+
|
1395
|
+
@staticmethod
|
1396
|
+
def InternalBoundaries(cell: topologic.Cell) -> list:
|
1397
|
+
"""
|
1398
|
+
Returns the internal boundaries of the input cell.
|
1399
|
+
|
1400
|
+
Parameters
|
1401
|
+
----------
|
1402
|
+
cell : topologic.Cell
|
1403
|
+
The input cell.
|
1404
|
+
|
1405
|
+
Returns
|
1406
|
+
-------
|
1407
|
+
list
|
1408
|
+
The list of internal boundaries ([topologic.Shell]).
|
1409
|
+
|
1410
|
+
"""
|
1411
|
+
shells = []
|
1412
|
+
_ = cell.InternalBoundaries(shells)
|
1413
|
+
return shells
|
1414
|
+
|
1415
|
+
@staticmethod
|
1416
|
+
def InternalVertex(cell: topologic.Cell, tolerance: float = 0.0001):
|
1417
|
+
"""
|
1418
|
+
Creates a vertex that is guaranteed to be inside the input cell.
|
1419
|
+
|
1420
|
+
Parameters
|
1421
|
+
----------
|
1422
|
+
cell : topologic.Cell
|
1423
|
+
The input cell.
|
1424
|
+
tolerance : float , optional
|
1425
|
+
The desired tolerance. The default is 0.0001.
|
1426
|
+
|
1427
|
+
Returns
|
1428
|
+
-------
|
1429
|
+
topologic.Vertex
|
1430
|
+
The internal vertex.
|
1431
|
+
|
1432
|
+
"""
|
1433
|
+
|
1434
|
+
if not isinstance(cell, topologic.Cell):
|
1435
|
+
print("Cell.InternalVertex - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1436
|
+
return None
|
1437
|
+
try:
|
1438
|
+
return topologic.CellUtility.InternalVertex(cell, tolerance)
|
1439
|
+
except:
|
1440
|
+
print("Cell.InternalVertex - Error: Could not create an internal vertex. Returning None.")
|
1441
|
+
return None
|
1442
|
+
|
1443
|
+
@staticmethod
|
1444
|
+
def IsOnBoundary(cell: topologic.Cell, vertex: topologic.Vertex, tolerance: float = 0.0001) -> bool:
|
1445
|
+
"""
|
1446
|
+
Returns True if the input vertex is on the boundary of the input cell. Returns False otherwise.
|
1447
|
+
|
1448
|
+
Parameters
|
1449
|
+
----------
|
1450
|
+
cell : topologic.Cell
|
1451
|
+
The input cell.
|
1452
|
+
vertex : topologic.Vertex
|
1453
|
+
The input vertex.
|
1454
|
+
tolerance : float , optional
|
1455
|
+
The desired tolerance. The default is 0.0001.
|
1456
|
+
|
1457
|
+
Returns
|
1458
|
+
-------
|
1459
|
+
bool
|
1460
|
+
Returns True if the input vertex is inside the input cell. Returns False otherwise.
|
1461
|
+
|
1462
|
+
"""
|
1463
|
+
|
1464
|
+
if not isinstance(cell, topologic.Cell):
|
1465
|
+
print("Cell.IsOnBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1466
|
+
return None
|
1467
|
+
if not isinstance(vertex, topologic.Vertex):
|
1468
|
+
print("Cell.IsOnBoundary - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
|
1469
|
+
return None
|
1470
|
+
try:
|
1471
|
+
return (topologic.CellUtility.Contains(cell, vertex, tolerance) == 1)
|
1472
|
+
except:
|
1473
|
+
print("Cell.IsOnBoundary - Error: Could not determine if the input vertex is on the boundary of the input cell. Returning None.")
|
1474
|
+
return None
|
1475
|
+
|
1476
|
+
@staticmethod
|
1477
|
+
def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
1478
|
+
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1479
|
+
"""
|
1480
|
+
Description
|
1481
|
+
----------
|
1482
|
+
Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.
|
1483
|
+
|
1484
|
+
Parameters
|
1485
|
+
----------
|
1486
|
+
origin : topologic.Vertex , optional
|
1487
|
+
The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
|
1488
|
+
radius : float , optional
|
1489
|
+
The radius of the octahedron's circumscribed sphere. The default is 0.5.
|
1490
|
+
direction : list , optional
|
1491
|
+
The vector representing the up direction of the octahedron. The default is [0, 0, 1].
|
1492
|
+
placement : str , optional
|
1493
|
+
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".
|
1494
|
+
tolerance : float , optional
|
1495
|
+
The desired tolerance. The default is 0.0001.
|
1496
|
+
|
1497
|
+
Returns
|
1498
|
+
-------
|
1499
|
+
topologic.Cell
|
1500
|
+
The created octahedron.
|
1501
|
+
|
1502
|
+
"""
|
1503
|
+
|
1504
|
+
from topologicpy.Vertex import Vertex
|
1505
|
+
from topologicpy.Face import Face
|
1506
|
+
from topologicpy.Topology import Topology
|
1507
|
+
|
1508
|
+
if not origin:
|
1509
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1510
|
+
if not isinstance(origin, topologic.Vertex):
|
1511
|
+
print("Cell.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1512
|
+
return None
|
1513
|
+
|
1514
|
+
vb1 = Vertex.ByCoordinates(-0.5, 0, 0)
|
1515
|
+
vb2 = Vertex.ByCoordinates(0, -0.5, 0)
|
1516
|
+
vb3 = Vertex.ByCoordinates(0.5, 0, 0)
|
1517
|
+
vb4 = Vertex.ByCoordinates(0, 0.5, 0)
|
1518
|
+
top = Vertex.ByCoordinates(0, 0, 0.5)
|
1519
|
+
bottom = Vertex.ByCoordinates(0, 0, -0.5)
|
1520
|
+
f1 = Face.ByVertices([top, vb1, vb2])
|
1521
|
+
f2 = Face.ByVertices([top, vb2, vb3])
|
1522
|
+
f3 = Face.ByVertices([top, vb3, vb4])
|
1523
|
+
f4 = Face.ByVertices([top, vb4, vb1])
|
1524
|
+
f5 = Face.ByVertices([bottom, vb1, vb2])
|
1525
|
+
f6 = Face.ByVertices([bottom, vb2, vb3])
|
1526
|
+
f7 = Face.ByVertices([bottom, vb3, vb4])
|
1527
|
+
f8 = Face.ByVertices([bottom, vb4, vb1])
|
1528
|
+
|
1529
|
+
octahedron = Cell.ByFaces([f1, f2, f3, f4, f5, f6, f7, f8], tolerance=tolerance)
|
1530
|
+
octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
|
1531
|
+
if placement == "bottom":
|
1532
|
+
octahedron = Topology.Translate(octahedron, 0, 0, radius)
|
1533
|
+
elif placement == "lowerleft":
|
1534
|
+
octahedron = Topology.Translate(octahedron, radius, radius, radius)
|
1535
|
+
octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1536
|
+
octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
|
1537
|
+
return octahedron
|
1538
|
+
|
1539
|
+
@staticmethod
|
1540
|
+
def Pipe(edge: topologic.Edge, profile: topologic.Wire = None, radius: float = 0.5, sides: int = 16, startOffset: float = 0, endOffset: float = 0, endcapA: topologic.Topology = None, endcapB: topologic.Topology = None) -> dict:
|
1541
|
+
"""
|
1542
|
+
Description
|
1543
|
+
----------
|
1544
|
+
Creates a pipe along the input edge.
|
1545
|
+
|
1546
|
+
Parameters
|
1547
|
+
----------
|
1548
|
+
edge : topologic.Edge
|
1549
|
+
The centerline of the pipe.
|
1550
|
+
profile : topologic.Wire , optional
|
1551
|
+
The profile of the pipe. It is assumed that the profile is in the XY plane. If set to None, a circle of radius 0.5 will be used. The default is None.
|
1552
|
+
radius : float , optional
|
1553
|
+
The radius of the pipe. The default is 0.5.
|
1554
|
+
sides : int , optional
|
1555
|
+
The number of sides of the pipe. The default is 16.
|
1556
|
+
startOffset : float , optional
|
1557
|
+
The offset distance from the start vertex of the centerline edge. The default is 0.
|
1558
|
+
endOffset : float , optional
|
1559
|
+
The offset distance from the end vertex of the centerline edge. The default is 0.
|
1560
|
+
endcapA : topologic.Topology, optional
|
1561
|
+
The topology to place at the start vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the direction of the centerline edge.
|
1562
|
+
endcapB : topologic.Topology, optional
|
1563
|
+
The topology to place at the end vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the inverse direction of the centerline edge.
|
1564
|
+
|
1565
|
+
Returns
|
1566
|
+
-------
|
1567
|
+
dict
|
1568
|
+
A dictionary containing the pipe, the start endcap, and the end endcap if they have been specified. The dictionary has the following keys:
|
1569
|
+
'pipe'
|
1570
|
+
'endcapA'
|
1571
|
+
'endcapB'
|
1572
|
+
|
1573
|
+
"""
|
1574
|
+
|
1575
|
+
from topologicpy.Vertex import Vertex
|
1576
|
+
from topologicpy.Edge import Edge
|
1577
|
+
from topologicpy.Topology import Topology
|
1578
|
+
|
1579
|
+
if not isinstance(edge, topologic.Edge):
|
1580
|
+
print("Cell.Pipe - Error: The input edge parameter is not a valid topologic edge. Returning None.")
|
1581
|
+
return None
|
1582
|
+
length = Edge.Length(edge)
|
1583
|
+
origin = Edge.StartVertex(edge)
|
1584
|
+
startU = startOffset / length
|
1585
|
+
endU = 1.0 - (endOffset / length)
|
1586
|
+
sv = Edge.VertexByParameter(edge, startU)
|
1587
|
+
ev = Edge.VertexByParameter(edge, endU)
|
1588
|
+
x1 = sv.X()
|
1589
|
+
y1 = sv.Y()
|
1590
|
+
z1 = sv.Z()
|
1591
|
+
x2 = ev.X()
|
1592
|
+
y2 = ev.Y()
|
1593
|
+
z2 = ev.Z()
|
1594
|
+
dx = x2 - x1
|
1595
|
+
dy = y2 - y1
|
1596
|
+
dz = z2 - z1
|
1597
|
+
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
1598
|
+
baseV = []
|
1599
|
+
topV = []
|
1600
|
+
|
1601
|
+
if isinstance(profile, topologic.Wire):
|
1602
|
+
baseWire = Topology.Translate(profile, 0 , 0, sv.Z())
|
1603
|
+
topWire = Topology.Translate(profile, 0 , 0, sv.Z()+dist)
|
1604
|
+
else:
|
1605
|
+
for i in range(sides):
|
1606
|
+
angle = math.radians(360/sides)*i
|
1607
|
+
x = math.sin(angle)*radius + sv.X()
|
1608
|
+
y = math.cos(angle)*radius + sv.Y()
|
1609
|
+
z = sv.Z()
|
1610
|
+
baseV.append(Vertex.ByCoordinates(x, y, z))
|
1611
|
+
topV.append(Vertex.ByCoordinates(x, y, z+dist))
|
1612
|
+
|
1613
|
+
baseWire = Wire.ByVertices(baseV)
|
1614
|
+
topWire = Wire.ByVertices(topV)
|
1615
|
+
wires = [baseWire, topWire]
|
1616
|
+
pipe = Cell.ByWires(wires)
|
1617
|
+
phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
|
1618
|
+
if dist < 0.0001:
|
1619
|
+
theta = 0
|
1620
|
+
else:
|
1621
|
+
theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
|
1622
|
+
pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 1, 0], angle=theta)
|
1623
|
+
pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 0, 1], angle=phi)
|
1624
|
+
zzz = Vertex.ByCoordinates(0, 0, 0)
|
1625
|
+
if endcapA:
|
1626
|
+
origin = edge.StartVertex()
|
1627
|
+
x1 = origin.X()
|
1628
|
+
y1 = origin.Y()
|
1629
|
+
z1 = origin.Z()
|
1630
|
+
x2 = edge.EndVertex().X()
|
1631
|
+
y2 = edge.EndVertex().Y()
|
1632
|
+
z2 = edge.EndVertex().Z()
|
1633
|
+
dx = x2 - x1
|
1634
|
+
dy = y2 - y1
|
1635
|
+
dz = z2 - z1
|
1636
|
+
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
1637
|
+
phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
|
1638
|
+
if dist < 0.0001:
|
1639
|
+
theta = 0
|
1640
|
+
else:
|
1641
|
+
theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
|
1642
|
+
endcapA = Topology.Copy(endcapA)
|
1643
|
+
endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 1, 0], angle=theta)
|
1644
|
+
endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 0, 1], angle=phi+180)
|
1645
|
+
endcapA = Topology.Translate(endcapA, origin.X(), origin.Y(), origin.Z())
|
1646
|
+
if endcapB:
|
1647
|
+
origin = edge.EndVertex()
|
1648
|
+
x1 = origin.X()
|
1649
|
+
y1 = origin.Y()
|
1650
|
+
z1 = origin.Z()
|
1651
|
+
x2 = edge.StartVertex().X()
|
1652
|
+
y2 = edge.StartVertex().Y()
|
1653
|
+
z2 = edge.StartVertex().Z()
|
1654
|
+
dx = x2 - x1
|
1655
|
+
dy = y2 - y1
|
1656
|
+
dz = z2 - z1
|
1657
|
+
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
1658
|
+
phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
|
1659
|
+
if dist < 0.0001:
|
1660
|
+
theta = 0
|
1661
|
+
else:
|
1662
|
+
theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
|
1663
|
+
endcapB = Topology.Copy(endcapB)
|
1664
|
+
endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 1, 0], angle=theta)
|
1665
|
+
endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 0, 1], angle=phi+180)
|
1666
|
+
endcapB = Topology.Translate(endcapB, origin.X(), origin.Y(), origin.Z())
|
1667
|
+
return {'pipe': pipe, 'endcapA': endcapA, 'endcapB': endcapB}
|
1668
|
+
|
1669
|
+
@staticmethod
|
1670
|
+
def Prism(origin: topologic.Vertex = None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1,
|
1671
|
+
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1672
|
+
"""
|
1673
|
+
Description
|
1674
|
+
----------
|
1675
|
+
Creates a prism.
|
1676
|
+
|
1677
|
+
Parameters
|
1678
|
+
----------
|
1679
|
+
origin : topologic.Vertex , optional
|
1680
|
+
The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
|
1681
|
+
width : float , optional
|
1682
|
+
The width of the prism. The default is 1.
|
1683
|
+
length : float , optional
|
1684
|
+
The length of the prism. The default is 1.
|
1685
|
+
height : float , optional
|
1686
|
+
The height of the prism.
|
1687
|
+
uSides : int , optional
|
1688
|
+
The number of sides along the width. The default is 1.
|
1689
|
+
vSides : int , optional
|
1690
|
+
The number of sides along the length. The default is 1.
|
1691
|
+
wSides : int , optional
|
1692
|
+
The number of sides along the height. The default is 1.
|
1693
|
+
direction : list , optional
|
1694
|
+
The vector representing the up direction of the prism. The default is [0, 0, 1].
|
1695
|
+
placement : str , optional
|
1696
|
+
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".
|
1697
|
+
tolerance : float , optional
|
1698
|
+
The desired tolerance. The default is 0.0001.
|
1699
|
+
|
1700
|
+
Returns
|
1701
|
+
-------
|
1702
|
+
topologic.Cell
|
1703
|
+
The created prism.
|
1704
|
+
|
1705
|
+
"""
|
1706
|
+
def sliceCell(cell, width, length, height, uSides, vSides, wSides):
|
1707
|
+
origin = cell.Centroid()
|
1708
|
+
shells = []
|
1709
|
+
_ = cell.Shells(None, shells)
|
1710
|
+
shell = shells[0]
|
1711
|
+
wRect = Wire.Rectangle(origin=origin, width=width*1.2, length=length*1.2, direction=[0, 0, 1], placement="center")
|
1712
|
+
sliceFaces = []
|
1713
|
+
for i in range(1, wSides):
|
1714
|
+
sliceFaces.append(Topology.Translate(Face.ByWire(wRect, tolerance=tolerance), 0, 0, height/wSides*i - height*0.5))
|
1715
|
+
uRect = Wire.Rectangle(origin=origin, width=height*1.2, length=length*1.2, direction=[1, 0, 0], placement="center")
|
1716
|
+
for i in range(1, uSides):
|
1717
|
+
sliceFaces.append(Topology.Translate(Face.ByWire(uRect, tolerance=tolerance), width/uSides*i - width*0.5, 0, 0))
|
1718
|
+
vRect = Wire.Rectangle(origin=origin, width=height*1.2, length=width*1.2, direction=[0, 1, 0], placement="center")
|
1719
|
+
for i in range(1, vSides):
|
1720
|
+
sliceFaces.append(Topology.Translate(Face.ByWire(vRect, tolerance=tolerance), 0, length/vSides*i - length*0.5, 0))
|
1721
|
+
if len(sliceFaces) > 0:
|
1722
|
+
sliceCluster = topologic.Cluster.ByTopologies(sliceFaces)
|
1723
|
+
shell = Topology.Slice(topologyA=shell, topologyB=sliceCluster, tranDict=False, tolerance=tolerance)
|
1724
|
+
return Cell.ByShell(shell)
|
1725
|
+
return cell
|
1726
|
+
|
1727
|
+
from topologicpy.Vertex import Vertex
|
1728
|
+
from topologicpy.Face import Face
|
1729
|
+
from topologicpy.Topology import Topology
|
1730
|
+
|
1731
|
+
if not origin:
|
1732
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1733
|
+
if not isinstance(origin, topologic.Vertex):
|
1734
|
+
print("Cell.Prism - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1735
|
+
return None
|
1736
|
+
xOffset = 0
|
1737
|
+
yOffset = 0
|
1738
|
+
zOffset = 0
|
1739
|
+
if placement.lower() == "center":
|
1740
|
+
zOffset = -height*0.5
|
1741
|
+
elif placement.lower() == "lowerleft":
|
1742
|
+
xOffset = width*0.5
|
1743
|
+
yOffset = length*0.5
|
1744
|
+
vb1 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
|
1745
|
+
vb2 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
|
1746
|
+
vb3 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
|
1747
|
+
vb4 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
|
1748
|
+
|
1749
|
+
baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
|
1750
|
+
baseFace = Face.ByWire(baseWire, tolerance=tolerance)
|
1751
|
+
|
1752
|
+
prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False)
|
1753
|
+
|
1754
|
+
if uSides > 1 or vSides > 1 or wSides > 1:
|
1755
|
+
prism = sliceCell(prism, width, length, height, uSides, vSides, wSides)
|
1756
|
+
prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
1757
|
+
return prism
|
1758
|
+
|
1759
|
+
@staticmethod
|
1760
|
+
def RemoveCollinearEdges(cell: topologic.Cell, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
|
1761
|
+
"""
|
1762
|
+
Removes any collinear edges in the input cell.
|
1763
|
+
|
1764
|
+
Parameters
|
1765
|
+
----------
|
1766
|
+
cell : topologic.Cell
|
1767
|
+
The input cell.
|
1768
|
+
angTolerance : float , optional
|
1769
|
+
The desired angular tolerance. The default is 0.1.
|
1770
|
+
tolerance : float , optional
|
1771
|
+
The desired tolerance. The default is 0.0001.
|
1772
|
+
|
1773
|
+
Returns
|
1774
|
+
-------
|
1775
|
+
topologic.Cell
|
1776
|
+
The created cell without any collinear edges.
|
1777
|
+
|
1778
|
+
"""
|
1779
|
+
from topologicpy.Face import Face
|
1780
|
+
|
1781
|
+
if not isinstance(cell, topologic.Cell):
|
1782
|
+
print("Cell.RemoveCollinearEdges - Error: The input cell parameter is not a valid cell. Returning None.")
|
1783
|
+
return None
|
1784
|
+
faces = Cell.Faces(cell)
|
1785
|
+
clean_faces = []
|
1786
|
+
for face in faces:
|
1787
|
+
clean_faces.append(Face.RemoveCollinearEdges(face, angTolerance=angTolerance, tolerance=tolerance))
|
1788
|
+
return Cell.ByFaces(clean_faces, tolerance=tolerance)
|
1789
|
+
|
1790
|
+
@staticmethod
|
1791
|
+
def Roof(face, angle: float = 45, epsilon: float = 0.01 , tolerance: float = 0.001):
|
1792
|
+
"""
|
1793
|
+
Creates a hipped roof through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
|
1794
|
+
This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel
|
1795
|
+
|
1796
|
+
Parameters
|
1797
|
+
----------
|
1798
|
+
face : topologic.Face
|
1799
|
+
The input face.
|
1800
|
+
angle : float , optioal
|
1801
|
+
The desired angle in degrees of the roof. The default is 45.
|
1802
|
+
epsilon : float , optional
|
1803
|
+
The desired epsilon (another form of tolerance for distance from plane). The default is 0.01. (This is set to a larger number as it was found to work better)
|
1804
|
+
tolerance : float , optional
|
1805
|
+
The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)
|
1806
|
+
|
1807
|
+
Returns
|
1808
|
+
-------
|
1809
|
+
cell
|
1810
|
+
The created roof.
|
1811
|
+
|
1812
|
+
"""
|
1813
|
+
from topologicpy.Shell import Shell
|
1814
|
+
from topologicpy.Cell import Cell
|
1815
|
+
from topologicpy.Topology import Topology
|
1816
|
+
|
1817
|
+
shell = Shell.Roof(face=face, angle=angle, epsilon=epsilon, tolerance=tolerance)
|
1818
|
+
faces = Topology.Faces(shell) + [face]
|
1819
|
+
cell = Cell.ByFaces(faces, tolerance=tolerance)
|
1820
|
+
if not cell:
|
1821
|
+
print("Cell.Roof - Error: Could not create a roof cell. Returning None.")
|
1822
|
+
return None
|
1823
|
+
return cell
|
1824
|
+
|
1825
|
+
@staticmethod
|
1826
|
+
def Sets(cells: list, superCells: list, tolerance: float = 0.0001) -> list:
|
1827
|
+
"""
|
1828
|
+
Classifies the input cells into sets based on their enclosure within the input list of super cells. The order of the sets follows the order of the input list of super cells.
|
1829
|
+
|
1830
|
+
Parameters
|
1831
|
+
----------
|
1832
|
+
inputCells : list
|
1833
|
+
The list of input cells.
|
1834
|
+
superCells : list
|
1835
|
+
The list of super cells.
|
1836
|
+
tolerance : float , optional
|
1837
|
+
The desired tolerance. The default is 0.0001.
|
1838
|
+
|
1839
|
+
Returns
|
1840
|
+
-------
|
1841
|
+
list
|
1842
|
+
The classified list of input cells based on their encolsure within the input list of super cells.
|
1843
|
+
|
1844
|
+
"""
|
1845
|
+
|
1846
|
+
from topologicpy.Vertex import Vertex
|
1847
|
+
from topologicpy.Topology import Topology
|
1848
|
+
|
1849
|
+
if not isinstance(cells, list):
|
1850
|
+
print("Cell.Sets - Error: The input cells parameter is not a valid list. Returning None.")
|
1851
|
+
return None
|
1852
|
+
if not isinstance(superCells, list):
|
1853
|
+
print("Cell.Sets - Error: The input superCells parameter is not a valid list. Returning None.")
|
1854
|
+
return None
|
1855
|
+
cells = [c for c in cells if isinstance(c, topologic.Cell)]
|
1856
|
+
if len(cells) < 1:
|
1857
|
+
print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
|
1858
|
+
return None
|
1859
|
+
superCells = [c for c in superCells if isinstance(c, topologic.Cell)]
|
1860
|
+
if len(cells) < 1:
|
1861
|
+
print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
|
1862
|
+
return None
|
1863
|
+
if len(superCells) == 0:
|
1864
|
+
cluster = cells[0]
|
1865
|
+
for i in range(1, len(cells)):
|
1866
|
+
oldCluster = cluster
|
1867
|
+
cluster = cluster.Union(cells[i])
|
1868
|
+
del oldCluster
|
1869
|
+
superCells = []
|
1870
|
+
_ = cluster.Cells(None, superCells)
|
1871
|
+
unused = []
|
1872
|
+
for i in range(len(cells)):
|
1873
|
+
unused.append(True)
|
1874
|
+
sets = []
|
1875
|
+
for i in range(len(superCells)):
|
1876
|
+
sets.append([])
|
1877
|
+
for i in range(len(cells)):
|
1878
|
+
if unused[i]:
|
1879
|
+
iv = Topology.InternalVertex(cells[i], tolerance=tolerance)
|
1880
|
+
for j in range(len(superCells)):
|
1881
|
+
if (Vertex.IsInternal(iv, superCells[j], tolerance)):
|
1882
|
+
sets[j].append(cells[i])
|
1883
|
+
unused[i] = False
|
1884
|
+
return sets
|
1885
|
+
|
1886
|
+
@staticmethod
|
1887
|
+
def Shells(cell: topologic.Cell) -> list:
|
1888
|
+
"""
|
1889
|
+
Returns the shells of the input cell.
|
1890
|
+
|
1891
|
+
Parameters
|
1892
|
+
----------
|
1893
|
+
cell : topologic.Cell
|
1894
|
+
The input cell.
|
1895
|
+
|
1896
|
+
Returns
|
1897
|
+
-------
|
1898
|
+
list
|
1899
|
+
The list of shells.
|
1900
|
+
|
1901
|
+
"""
|
1902
|
+
if not isinstance(cell, topologic.Cell):
|
1903
|
+
print("Cell.Shells - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
1904
|
+
return None
|
1905
|
+
shells = []
|
1906
|
+
_ = cell.Shells(None, shells)
|
1907
|
+
return shells
|
1908
|
+
|
1909
|
+
@staticmethod
|
1910
|
+
def Sphere(origin: topologic.Vertex = None, radius: float = 0.5, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
|
1911
|
+
placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
1912
|
+
"""
|
1913
|
+
Creates a sphere.
|
1914
|
+
|
1915
|
+
Parameters
|
1916
|
+
----------
|
1917
|
+
origin : topologic.Vertex , optional
|
1918
|
+
The origin location of the sphere. The default is None which results in the sphere being placed at (0, 0, 0).
|
1919
|
+
radius : float , optional
|
1920
|
+
The radius of the sphere. The default is 0.5.
|
1921
|
+
uSides : int , optional
|
1922
|
+
The number of sides along the longitude of the sphere. The default is 16.
|
1923
|
+
vSides : int , optional
|
1924
|
+
The number of sides along the latitude of the sphere. The default is 8.
|
1925
|
+
direction : list , optional
|
1926
|
+
The vector representing the up direction of the sphere. The default is [0, 0, 1].
|
1927
|
+
placement : str , optional
|
1928
|
+
The description of the placement of the origin of the sphere. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1929
|
+
tolerance : float , optional
|
1930
|
+
The desired tolerance. The default is 0.0001.
|
1931
|
+
|
1932
|
+
Returns
|
1933
|
+
-------
|
1934
|
+
topologic.Cell
|
1935
|
+
The created sphere.
|
1936
|
+
|
1937
|
+
"""
|
1938
|
+
|
1939
|
+
from topologicpy.Vertex import Vertex
|
1940
|
+
from topologicpy.Topology import Topology
|
1941
|
+
|
1942
|
+
if not origin:
|
1943
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1944
|
+
if not isinstance(origin, topologic.Vertex):
|
1945
|
+
print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1946
|
+
return None
|
1947
|
+
c = Wire.Circle(origin=Vertex.Origin(), radius=radius, sides=vSides, fromAngle=-90, toAngle=90, close=False, direction=[0, 0, 1], placement="center")
|
1948
|
+
c = Topology.Rotate(c, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
|
1949
|
+
sphere = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
1950
|
+
if sphere.Type() == topologic.CellComplex.Type():
|
1951
|
+
sphere = sphere.ExternalBoundary()
|
1952
|
+
if sphere.Type() == topologic.Shell.Type():
|
1953
|
+
sphere = topologic.Cell.ByShell(sphere)
|
1954
|
+
if placement.lower() == "bottom":
|
1955
|
+
sphere = Topology.Translate(sphere, 0, 0, radius)
|
1956
|
+
elif placement.lower() == "lowerleft":
|
1957
|
+
sphere = Topology.Translate(sphere, radius, radius, radius)
|
1958
|
+
sphere = Topology.Orient(sphere, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1959
|
+
sphere = Topology.Place(sphere, originA=Vertex.Origin(), originB=origin)
|
1960
|
+
return sphere
|
1961
|
+
|
1962
|
+
@staticmethod
|
1963
|
+
def SurfaceArea(cell: topologic.Cell, mantissa: int = 6) -> float:
|
1964
|
+
"""
|
1965
|
+
Returns the surface area of the input cell.
|
1966
|
+
|
1967
|
+
Parameters
|
1968
|
+
----------
|
1969
|
+
cell : topologic.Cell
|
1970
|
+
The cell.
|
1971
|
+
mantissa : int , optional
|
1972
|
+
The desired length of the mantissa. The default is 6.
|
1973
|
+
|
1974
|
+
Returns
|
1975
|
+
-------
|
1976
|
+
area : float
|
1977
|
+
The surface area of the input cell.
|
1978
|
+
|
1979
|
+
"""
|
1980
|
+
return Cell.Area(cell=cell, mantissa=mantissa)
|
1981
|
+
|
1982
|
+
@staticmethod
|
1983
|
+
def Tetrahedron(origin: topologic.Vertex = None, radius: float = 0.5,
|
1984
|
+
direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.Cell:
|
1985
|
+
"""
|
1986
|
+
Description
|
1987
|
+
----------
|
1988
|
+
Creates a tetrahedron. See https://en.wikipedia.org/wiki/Tetrahedron.
|
1989
|
+
|
1990
|
+
Parameters
|
1991
|
+
----------
|
1992
|
+
origin : topologic.Vertex , optional
|
1993
|
+
The origin location of the tetrahedron. The default is None which results in the tetrahedron being placed at (0, 0, 0).
|
1994
|
+
radius : float , optional
|
1995
|
+
The radius of the tetrahedron's circumscribed sphere. The default is 0.5.
|
1996
|
+
direction : list , optional
|
1997
|
+
The vector representing the up direction of the tetrahedron. The default is [0, 0, 1].
|
1998
|
+
placement : str , optional
|
1999
|
+
The description of the placement of the origin of the tetrahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
2000
|
+
tolerance : float , optional
|
2001
|
+
The desired tolerance. The default is 0.0001.
|
2002
|
+
|
2003
|
+
Returns
|
2004
|
+
-------
|
2005
|
+
topologic.Cell
|
2006
|
+
The created tetrahedron.
|
2007
|
+
|
2008
|
+
"""
|
2009
|
+
|
2010
|
+
from topologicpy.Vertex import Vertex
|
2011
|
+
from topologicpy.Wire import Wire
|
2012
|
+
from topologicpy.Face import Face
|
2013
|
+
from topologicpy.Topology import Topology
|
2014
|
+
import math
|
2015
|
+
|
2016
|
+
if not origin:
|
2017
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
2018
|
+
if not isinstance(origin, topologic.Vertex):
|
2019
|
+
print("Cell.Tetrahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
2020
|
+
return None
|
2021
|
+
|
2022
|
+
vb1 = Vertex.ByCoordinates(math.sqrt(8/9), 0, -1/3)
|
2023
|
+
vb2 = Vertex.ByCoordinates(-math.sqrt(2/9), math.sqrt(2/3), -1/3)
|
2024
|
+
vb3 = Vertex.ByCoordinates(-math.sqrt(2/9), -math.sqrt(2/3), -1/3)
|
2025
|
+
vb4 = Vertex.ByCoordinates(0, 0, 1)
|
2026
|
+
f1 = Face.ByVertices([vb1, vb2, vb3])
|
2027
|
+
f2 = Face.ByVertices([vb4, vb1, vb2])
|
2028
|
+
f3 = Face.ByVertices([vb4, vb2, vb3])
|
2029
|
+
f4 = Face.ByVertices([vb4, vb3, vb1])
|
2030
|
+
tetrahedron = Cell.ByFaces([f1, f2, f3, f4])
|
2031
|
+
tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=0.5, y=0.5, z=0.5)
|
2032
|
+
tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
|
2033
|
+
|
2034
|
+
if placement.lower() == "lowerleft":
|
2035
|
+
tetrahedron = Topology.Translate(tetrahedron, radius, radius, radius)
|
2036
|
+
elif placement.lower() == "bottom":
|
2037
|
+
tetrahedron = Topology.Translate(tetrahedron, 0, 0, radius)
|
2038
|
+
tetrahedron = Topology.Place(tetrahedron, originA=Vertex.Origin(), originB=origin)
|
2039
|
+
tetrahedron = Topology.Orient(tetrahedron, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
|
2040
|
+
return tetrahedron
|
2041
|
+
|
2042
|
+
@staticmethod
|
2043
|
+
def Torus(origin: topologic.Vertex = None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Cell:
|
2044
|
+
"""
|
2045
|
+
Creates a torus.
|
2046
|
+
|
2047
|
+
Parameters
|
2048
|
+
----------
|
2049
|
+
origin : topologic.Vertex , optional
|
2050
|
+
The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
|
2051
|
+
majorRadius : float , optional
|
2052
|
+
The major radius of the torus. The default is 0.5.
|
2053
|
+
minorRadius : float , optional
|
2054
|
+
The minor radius of the torus. The default is 0.1.
|
2055
|
+
uSides : int , optional
|
2056
|
+
The number of sides along the longitude of the torus. The default is 16.
|
2057
|
+
vSides : int , optional
|
2058
|
+
The number of sides along the latitude of the torus. The default is 8.
|
2059
|
+
direction : list , optional
|
2060
|
+
The vector representing the up direction of the torus. The default is [0, 0, 1].
|
2061
|
+
placement : str , optional
|
2062
|
+
The description of the placement of the origin of the torus. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
2063
|
+
tolerance : float , optional
|
2064
|
+
The desired tolerance. The default is 0.0001.
|
2065
|
+
|
2066
|
+
Returns
|
2067
|
+
-------
|
2068
|
+
topologic.Cell
|
2069
|
+
The created torus.
|
2070
|
+
|
2071
|
+
"""
|
2072
|
+
|
2073
|
+
from topologicpy.Vertex import Vertex
|
2074
|
+
from topologicpy.Topology import Topology
|
2075
|
+
|
2076
|
+
if not origin:
|
2077
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
2078
|
+
if not isinstance(origin, topologic.Vertex):
|
2079
|
+
print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
2080
|
+
return None
|
2081
|
+
c = Wire.Circle(origin=Vertex.Origin(), radius=minorRadius, sides=vSides, fromAngle=0, toAngle=360, close=False, direction=[0, 1, 0], placement="center")
|
2082
|
+
c = Topology.Translate(c, abs(majorRadius-minorRadius), 0, 0)
|
2083
|
+
torus = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
2084
|
+
if torus.Type() == topologic.Shell.Type():
|
2085
|
+
torus = topologic.Cell.ByShell(torus)
|
2086
|
+
if placement.lower() == "bottom":
|
2087
|
+
torus = Topology.Translate(torus, 0, 0, minorRadius)
|
2088
|
+
elif placement.lower() == "lowerleft":
|
2089
|
+
torus = Topology.Translate(torus, majorRadius, majorRadius, minorRadius)
|
2090
|
+
|
2091
|
+
torus = Topology.Orient(torus, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
2092
|
+
torus = Topology.Place(torus, originA=Vertex.Origin(), originB=origin)
|
2093
|
+
return torus
|
2094
|
+
|
2095
|
+
@staticmethod
|
2096
|
+
def Vertices(cell: topologic.Cell) -> list:
|
2097
|
+
"""
|
2098
|
+
Returns the vertices of the input cell.
|
2099
|
+
|
2100
|
+
Parameters
|
2101
|
+
----------
|
2102
|
+
cell : topologic.Cell
|
2103
|
+
The input cell.
|
2104
|
+
|
2105
|
+
Returns
|
2106
|
+
-------
|
2107
|
+
list
|
2108
|
+
The list of vertices.
|
2109
|
+
|
2110
|
+
"""
|
2111
|
+
if not isinstance(cell, topologic.Cell):
|
2112
|
+
print("Cell.Vertices - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
2113
|
+
return None
|
2114
|
+
vertices = []
|
2115
|
+
_ = cell.Vertices(None, vertices)
|
2116
|
+
return vertices
|
2117
|
+
|
2118
|
+
@staticmethod
|
2119
|
+
def Volume(cell: topologic.Cell, mantissa: int = 6) -> float:
|
2120
|
+
"""
|
2121
|
+
Returns the volume of the input cell.
|
2122
|
+
|
2123
|
+
Parameters
|
2124
|
+
----------
|
2125
|
+
cell : topologic.Cell
|
2126
|
+
The input cell.
|
2127
|
+
manitssa: int , optional
|
2128
|
+
The desired length of the mantissa. The default is 6.
|
2129
|
+
|
2130
|
+
Returns
|
2131
|
+
-------
|
2132
|
+
float
|
2133
|
+
The volume of the input cell.
|
2134
|
+
|
2135
|
+
"""
|
2136
|
+
if not isinstance(cell, topologic.Cell):
|
2137
|
+
print("Cell.Volume - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
2138
|
+
return None
|
2139
|
+
volume = None
|
2140
|
+
try:
|
2141
|
+
volume = round(topologic.CellUtility.Volume(cell), mantissa)
|
2142
|
+
except:
|
2143
|
+
print("Cell.Volume - Error: Could not compute the volume of the input cell. Returning None.")
|
2144
|
+
volume = None
|
2145
|
+
return volume
|
2146
|
+
|
2147
|
+
@staticmethod
|
2148
|
+
def Wires(cell: topologic.Cell) -> list:
|
2149
|
+
"""
|
2150
|
+
Returns the wires of the input cell.
|
2151
|
+
|
2152
|
+
Parameters
|
2153
|
+
----------
|
2154
|
+
cell : topologic.Cell
|
2155
|
+
The input cell.
|
2156
|
+
|
2157
|
+
Returns
|
2158
|
+
-------
|
2159
|
+
list
|
2160
|
+
The list of wires.
|
2161
|
+
|
2162
|
+
"""
|
2163
|
+
if not isinstance(cell, topologic.Cell):
|
2164
|
+
print("Cell.Wires - Error: The input cell parameter is not a valid topologic cell. Returning None.")
|
2165
|
+
return None
|
2166
|
+
wires = []
|
2167
|
+
_ = cell.Wires(None, wires)
|
2168
|
+
return wires
|
2169
|
+
|