topologicpy 0.8.26__py3-none-any.whl → 0.8.29__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/Face.py +38 -6
- topologicpy/Graph.py +198 -0
- topologicpy/Grid.py +16 -12
- topologicpy/Plotly.py +12 -7
- topologicpy/ShapeGrammar.py +492 -0
- topologicpy/Shell.py +36 -0
- topologicpy/Topology.py +301 -251
- topologicpy/Vertex.py +95 -0
- topologicpy/version.py +1 -1
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/METADATA +1 -1
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/RECORD +14 -13
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/WHEEL +1 -1
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,492 @@
|
|
1
|
+
# Copyright (C) 2025
|
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
|
+
|
19
|
+
class ShapeGrammar:
|
20
|
+
def __init__(self):
|
21
|
+
self.title = "Untitled" # Stores the title of the topology grammar.
|
22
|
+
self.description = "" # Stores the description of the grammar.
|
23
|
+
self.rules = [] # Stores transformation rules of the topology grammar.
|
24
|
+
# Operations
|
25
|
+
# Replace
|
26
|
+
replace = {"title": "Replace",
|
27
|
+
"description": "Replaces the input topology with the output topology.",
|
28
|
+
"uSides": None,
|
29
|
+
"vSides": None,
|
30
|
+
"wSides": None}
|
31
|
+
# Transform
|
32
|
+
transform = {"title": "Transform",
|
33
|
+
"description": "Transforms the input topology using the specified matrix.",
|
34
|
+
"uSides": None,
|
35
|
+
"vSides": None,
|
36
|
+
"wSides": None}
|
37
|
+
# Union
|
38
|
+
union = {"title": "Union",
|
39
|
+
"description": "Unions the input topology and the output topology.",
|
40
|
+
"uSides": None,
|
41
|
+
"vSides": None,
|
42
|
+
"wSides": None}
|
43
|
+
# Difference
|
44
|
+
difference = {"title": "Difference",
|
45
|
+
"description": "Subtracts the output topology from the input topology.",
|
46
|
+
"uSides": None,
|
47
|
+
"vSides": None,
|
48
|
+
"wSides": None}
|
49
|
+
# Difference
|
50
|
+
symdif = {"title": "Symmetric Difference",
|
51
|
+
"description": "Calculates the symmetrical difference of the input topology and the output topology.",
|
52
|
+
"uSides": None,
|
53
|
+
"vSides": None,
|
54
|
+
"wSides": None}
|
55
|
+
# Intersect
|
56
|
+
intersect = {"title": "Intersect",
|
57
|
+
"description": "Intersects the input topology and the output topology.",
|
58
|
+
"uSides": None,
|
59
|
+
"vSides": None,
|
60
|
+
"wSides": None}
|
61
|
+
# Merge
|
62
|
+
merge = {"title": "Merge",
|
63
|
+
"description": "Merges the input topology and the output topology.",
|
64
|
+
"uSides": None,
|
65
|
+
"vSides": None,
|
66
|
+
"wSides": None}
|
67
|
+
# Slice
|
68
|
+
slice = {"title": "Slice",
|
69
|
+
"description": "Slices the input topology using the output topology.",
|
70
|
+
"uSides": None,
|
71
|
+
"vSides": None,
|
72
|
+
"wSides": None}
|
73
|
+
# Impose
|
74
|
+
impose = {"title": "Impose",
|
75
|
+
"description": "Imposes the output topology on the input topology.",
|
76
|
+
"uSides": None,
|
77
|
+
"vSides": None,
|
78
|
+
"wSides": None}
|
79
|
+
# Imprint
|
80
|
+
imprint = {"title": "Imprint",
|
81
|
+
"description": "Imposes the output topology on the input topology.",
|
82
|
+
"uSides": None,
|
83
|
+
"vSides": None,
|
84
|
+
"wSides": None}
|
85
|
+
# Divide
|
86
|
+
divide = {"title": "Divide",
|
87
|
+
"description": "Divides the input topology along the x, y, and z axes using the specified number of sides (uSides, vSides, wSides)",
|
88
|
+
"uSides": 2,
|
89
|
+
"vSides": 2,
|
90
|
+
"wSides": 2}
|
91
|
+
self.operations = [replace, transform, union, difference, symdif, intersect, merge, slice, impose, imprint, divide]
|
92
|
+
|
93
|
+
def OperationTitles(self):
|
94
|
+
"""
|
95
|
+
Returns the list of available operation titles.
|
96
|
+
|
97
|
+
Parameters
|
98
|
+
----------
|
99
|
+
|
100
|
+
Returns
|
101
|
+
-------
|
102
|
+
list
|
103
|
+
The requested list of operation titles
|
104
|
+
"""
|
105
|
+
return [op["title"] for op in self.operations]
|
106
|
+
|
107
|
+
def OperationByTitle(self, title):
|
108
|
+
"""
|
109
|
+
Returns the operation given the input title string
|
110
|
+
|
111
|
+
Parameters
|
112
|
+
----------
|
113
|
+
title : str
|
114
|
+
The input operation str. See OperationTitles for list of operations.
|
115
|
+
|
116
|
+
Returns
|
117
|
+
-------
|
118
|
+
ShapeGrammar.Operation
|
119
|
+
The requested operation
|
120
|
+
"""
|
121
|
+
for op in self.operations:
|
122
|
+
op_title = op["title"]
|
123
|
+
if title.lower() in op_title.lower():
|
124
|
+
return op
|
125
|
+
return None
|
126
|
+
|
127
|
+
def AddRule(self,
|
128
|
+
input,
|
129
|
+
output,
|
130
|
+
title : str = "Untitled Rule",
|
131
|
+
description: str = "",
|
132
|
+
operation : dict = None,
|
133
|
+
matrix: list = None,
|
134
|
+
silent: bool = False):
|
135
|
+
"""
|
136
|
+
Adds a rule to the topology grammar.
|
137
|
+
|
138
|
+
Parameters
|
139
|
+
----------
|
140
|
+
input : topologic_core.Topology
|
141
|
+
The linput topology of the rule.
|
142
|
+
output : topologic_core.Topology
|
143
|
+
The output topology of the rule.
|
144
|
+
title : str , optional
|
145
|
+
The title of the rule. The default is "Untitled Rule"
|
146
|
+
description : str, optional
|
147
|
+
The description of the rule. The default is "".
|
148
|
+
operation : dict , optional
|
149
|
+
The desired rule operation. See Rule Operations. If set to None, the replacement rule is applied. The default is None.
|
150
|
+
matrix : list
|
151
|
+
The 4x4 transformation matrix that tranforms the output topology to the input topology. If set to None, no transformation is applied. The default is None.
|
152
|
+
silent : bool, optional
|
153
|
+
If True, suppresses error/warning messages. Default is False.
|
154
|
+
|
155
|
+
Returns
|
156
|
+
-------
|
157
|
+
None
|
158
|
+
This method does not return a value.
|
159
|
+
"""
|
160
|
+
from topologicpy.Topology import Topology
|
161
|
+
|
162
|
+
def is_4x4_matrix(matrix):
|
163
|
+
return (
|
164
|
+
isinstance(matrix, list) and
|
165
|
+
len(matrix) == 4 and
|
166
|
+
all(isinstance(row, list) and len(row) == 4 for row in matrix)
|
167
|
+
)
|
168
|
+
|
169
|
+
if not Topology.IsInstance(input, "Topology"):
|
170
|
+
if not silent:
|
171
|
+
print("ShapeGrammar.AddRule - Error: The input input parameter is not a valid topology. Returning None.")
|
172
|
+
return None
|
173
|
+
if not output == None:
|
174
|
+
if not Topology.IsInstance(output, "Topology"):
|
175
|
+
if not silent:
|
176
|
+
print("ShapeGrammar.AddRule - Error: The input output parameter is not a valid topology. Returning None.")
|
177
|
+
return None
|
178
|
+
if not operation == None:
|
179
|
+
if not operation in self.operations:
|
180
|
+
if not silent:
|
181
|
+
print("ShapeGrammar.AddRule - Error: The input operation parameter is not a valid operation. Returning None.")
|
182
|
+
return None
|
183
|
+
if not matrix == None:
|
184
|
+
if not is_4x4_matrix(matrix):
|
185
|
+
if not silent:
|
186
|
+
print("ShapeGrammar.AddRule - Error: The input matrix parameter is not a valid matrix. Returning None.")
|
187
|
+
return None
|
188
|
+
|
189
|
+
self.rules.append({"input":input,
|
190
|
+
"output": output,
|
191
|
+
"title": title,
|
192
|
+
"description": description,
|
193
|
+
"operation": operation,
|
194
|
+
"matrix": matrix
|
195
|
+
})
|
196
|
+
|
197
|
+
def ApplicableRules(self, topology, keys: list = None, silent: bool = False):
|
198
|
+
"""
|
199
|
+
Returns rules applicable to the input topology.
|
200
|
+
|
201
|
+
Parameters
|
202
|
+
----------
|
203
|
+
topology : topologic_core.Topology
|
204
|
+
The input topology
|
205
|
+
keys : list , optional
|
206
|
+
The list of dictionary keys to semantically match the rules. The default is None which means dictionaries are not considered.
|
207
|
+
silent : bool, optional
|
208
|
+
If True, suppresses error/warning messages. Default is False.
|
209
|
+
|
210
|
+
Returns
|
211
|
+
-------
|
212
|
+
list
|
213
|
+
The list of applicable rules.
|
214
|
+
"""
|
215
|
+
from topologicpy.Topology import Topology
|
216
|
+
from topologicpy.Dictionary import Dictionary
|
217
|
+
|
218
|
+
if not Topology.IsInstance(topology, "Topology"):
|
219
|
+
if not silent:
|
220
|
+
print("ShapeGrammar.ApplicableRules - Error: The input topology parameter is not a valid topology. Returning None.")
|
221
|
+
return None
|
222
|
+
|
223
|
+
ap_rules = []
|
224
|
+
ap_trans = []
|
225
|
+
d = Topology.Dictionary(topology)
|
226
|
+
for i, rule in enumerate(self.rules):
|
227
|
+
dict_status = True
|
228
|
+
input = rule["input"]
|
229
|
+
# If there is a list of keys specified, check that the values match
|
230
|
+
if isinstance(keys, list):
|
231
|
+
d_input = Topology.Dictionary(input)
|
232
|
+
for j, key in enumerate(keys):
|
233
|
+
if not Dictionary.ValueAtKey(d, key, None) == Dictionary.ValueAtKey(d_input, key, None):
|
234
|
+
dict_status = False
|
235
|
+
break
|
236
|
+
#If it passed the dictionary key test, then check topology similarity
|
237
|
+
if dict_status:
|
238
|
+
topology_status, mat = Topology.IsSimilar(rule["input"], topology)
|
239
|
+
if topology_status:
|
240
|
+
ap_rules.append(rule)
|
241
|
+
ap_trans.append(mat)
|
242
|
+
return ap_rules, ap_trans
|
243
|
+
|
244
|
+
def ApplyRule(self, topology, rule: dict = None, matrix: list = None, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
|
245
|
+
"""
|
246
|
+
Returns rules applicable to the input topology.
|
247
|
+
|
248
|
+
Parameters
|
249
|
+
----------
|
250
|
+
topology : topologic_core.Topology
|
251
|
+
The input topology
|
252
|
+
rule : dict , optional
|
253
|
+
The desired rule to apply. The default is None.
|
254
|
+
matrix : list
|
255
|
+
The 4x4 transformation matrix that tranforms the output topology to the input topology. If set to None, no transformation is applied. The default is None.
|
256
|
+
mantissa : int, optional
|
257
|
+
Decimal precision. Default is 6.
|
258
|
+
tolerance : float, optional
|
259
|
+
The desired Tolerance. Not used here but included for API compatibility. Default is 0.0001.
|
260
|
+
silent : bool, optional
|
261
|
+
If True, suppresses error/warning messages. Default is False.
|
262
|
+
|
263
|
+
Returns
|
264
|
+
-------
|
265
|
+
topologic_core.Topology
|
266
|
+
The transformed topology
|
267
|
+
"""
|
268
|
+
|
269
|
+
from topologicpy.Topology import Topology
|
270
|
+
from topologicpy.Cluster import Cluster
|
271
|
+
from topologicpy.Face import Face
|
272
|
+
from topologicpy.Vertex import Vertex
|
273
|
+
|
274
|
+
def is_4x4_matrix(matrix):
|
275
|
+
return (
|
276
|
+
isinstance(matrix, list) and
|
277
|
+
len(matrix) == 4 and
|
278
|
+
all(isinstance(row, list) and len(row) == 4 for row in matrix)
|
279
|
+
)
|
280
|
+
|
281
|
+
def bb(topology):
|
282
|
+
vertices = Topology.Vertices(topology)
|
283
|
+
x = []
|
284
|
+
y = []
|
285
|
+
z = []
|
286
|
+
for aVertex in vertices:
|
287
|
+
x.append(Vertex.X(aVertex, mantissa=mantissa))
|
288
|
+
y.append(Vertex.Y(aVertex, mantissa=mantissa))
|
289
|
+
z.append(Vertex.Z(aVertex, mantissa=mantissa))
|
290
|
+
x_min = min(x)
|
291
|
+
y_min = min(y)
|
292
|
+
z_min = min(z)
|
293
|
+
maxX = max(x)
|
294
|
+
maxY = max(y)
|
295
|
+
maxZ = max(z)
|
296
|
+
return [x_min, y_min, z_min, maxX, maxY, maxZ]
|
297
|
+
|
298
|
+
def slice(topology, uSides, vSides, wSides):
|
299
|
+
x_min, y_min, z_min, maxX, maxY, maxZ = bb(topology)
|
300
|
+
centroid = Vertex.ByCoordinates(x_min+(maxX-x_min)*0.5, y_min+(maxY-y_min)*0.5, z_min+(maxZ-z_min)*0.5)
|
301
|
+
wOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), Vertex.Y(centroid, mantissa=mantissa), z_min)
|
302
|
+
wFace = Face.Rectangle(origin=wOrigin, width=(maxX-x_min)*1.1, length=(maxY-y_min)*1.1)
|
303
|
+
wFaces = []
|
304
|
+
wOffset = (maxZ-z_min)/wSides
|
305
|
+
for i in range(wSides-1):
|
306
|
+
wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
|
307
|
+
uOrigin = Vertex.ByCoordinates(x_min, Vertex.Y(centroid, mantissa=mantissa), Vertex.Z(centroid, mantissa=mantissa))
|
308
|
+
uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-z_min)*1.1, length=(maxY-y_min)*1.1, direction=[1,0,0])
|
309
|
+
uFaces = []
|
310
|
+
uOffset = (maxX-x_min)/uSides
|
311
|
+
for i in range(uSides-1):
|
312
|
+
uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
|
313
|
+
vOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), y_min, Vertex.Z(centroid, mantissa=mantissa))
|
314
|
+
vFace = Face.Rectangle(origin=vOrigin, width=(maxX-x_min)*1.1, length=(maxZ-z_min)*1.1, direction=[0,1,0])
|
315
|
+
vFaces = []
|
316
|
+
vOffset = (maxY-y_min)/vSides
|
317
|
+
for i in range(vSides-1):
|
318
|
+
vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
|
319
|
+
all_faces = uFaces+vFaces+wFaces
|
320
|
+
if len(all_faces) > 0:
|
321
|
+
f_clus = Cluster.ByTopologies(uFaces+vFaces+wFaces)
|
322
|
+
return Topology.Slice(topology, f_clus, tolerance=tolerance)
|
323
|
+
else:
|
324
|
+
return topology
|
325
|
+
|
326
|
+
if not Topology.IsInstance(topology, "Topology"):
|
327
|
+
if not silent:
|
328
|
+
print("ShapeGrammar.ApplyRule - Error: The input topology parameter is not a valid topology. Returning None.")
|
329
|
+
return None
|
330
|
+
if not matrix == None:
|
331
|
+
if not is_4x4_matrix(matrix):
|
332
|
+
if not silent:
|
333
|
+
print("ShapeGrammar.ApplyRule - Error: The input matrix parameter is not a valid matrix. Returning None.")
|
334
|
+
return None
|
335
|
+
|
336
|
+
if not rule == None:
|
337
|
+
input = rule["input"]
|
338
|
+
output = rule["output"]
|
339
|
+
r_matrix = rule["matrix"]
|
340
|
+
operation = rule["operation"]
|
341
|
+
if not operation == None:
|
342
|
+
op_title = operation["title"]
|
343
|
+
else:
|
344
|
+
op_title = "None"
|
345
|
+
|
346
|
+
result_output = topology
|
347
|
+
temp_output = None
|
348
|
+
if not output == None:
|
349
|
+
temp_output = output
|
350
|
+
# Transform the output topology to the input topology to prepare it for final transformation
|
351
|
+
if not r_matrix == None and not output == None:
|
352
|
+
temp_output = Topology.Transform(output, r_matrix)
|
353
|
+
|
354
|
+
if "replace" in op_title.lower():
|
355
|
+
result_output = temp_output
|
356
|
+
elif "transform" in op_title.lower():
|
357
|
+
result_output = Topology.Transform(topology, r_matrix)
|
358
|
+
elif "union" in op_title.lower():
|
359
|
+
result_output = Topology.Union(input, temp_output)
|
360
|
+
elif "difference" in op_title.lower():
|
361
|
+
result_output = Topology.Difference(input, temp_output)
|
362
|
+
elif "symmetric difference" in op_title.lower():
|
363
|
+
result_output = Topology.SymmetricDifference(input, temp_output)
|
364
|
+
elif "intersect" in op_title.lower():
|
365
|
+
result_output = Topology.Intersect(input, temp_output)
|
366
|
+
elif "merge" in op_title.lower():
|
367
|
+
result_output = Topology.Merge(input, temp_output)
|
368
|
+
elif "slice" in op_title.lower():
|
369
|
+
result_output = Topology.Slice(input, temp_output)
|
370
|
+
elif "impose" in op_title.lower():
|
371
|
+
result_output = Topology.Impose(input, temp_output)
|
372
|
+
elif "imprint" in op_title.lower():
|
373
|
+
result_output = Topology.Imprint(input, temp_output)
|
374
|
+
elif "divide" in op_title.lower():
|
375
|
+
uSides = operation["uSides"]
|
376
|
+
vSides = operation["vSides"]
|
377
|
+
wSides = operation["wSides"]
|
378
|
+
if not uSides == None and not vSides == None and not wSides == None:
|
379
|
+
result_output = slice(input, uSides, vSides, wSides)
|
380
|
+
|
381
|
+
# Finally, transform the result to the input topology
|
382
|
+
if not matrix == None:
|
383
|
+
result_output = Topology.Transform(result_output, matrix)
|
384
|
+
|
385
|
+
return result_output
|
386
|
+
|
387
|
+
def FigureByInputOutput(self, input, output, silent: bool = False):
|
388
|
+
"""
|
389
|
+
Returns the Plotly figure of the input and output topologies as a rule.
|
390
|
+
|
391
|
+
Parameters
|
392
|
+
----------
|
393
|
+
input : topologic_core.Topology
|
394
|
+
The input topology
|
395
|
+
output : topologic_core.Topology
|
396
|
+
The output topology
|
397
|
+
silent : bool, optional
|
398
|
+
If True, suppresses error/warning messages. Default is False.
|
399
|
+
|
400
|
+
Returns
|
401
|
+
-------
|
402
|
+
This function does not return a value
|
403
|
+
"""
|
404
|
+
|
405
|
+
from topologicpy.Vertex import Vertex
|
406
|
+
from topologicpy.Cell import Cell
|
407
|
+
from topologicpy.Topology import Topology
|
408
|
+
from topologicpy.Dictionary import Dictionary
|
409
|
+
from topologicpy.Cluster import Cluster
|
410
|
+
from topologicpy.Plotly import Plotly
|
411
|
+
|
412
|
+
if not Topology.IsInstance(input, "Topology"):
|
413
|
+
if not silent:
|
414
|
+
print("ShapeGrammar.DrawInputOutput - Error: The input topology parameter is not a valid topology. Returning None.")
|
415
|
+
return None
|
416
|
+
if not Topology.IsInstance(output, "Topology"):
|
417
|
+
if not silent:
|
418
|
+
print("ShapeGrammar.DrawInputOutput - Error: The output topology parameter is not a valid topology. Returning None.")
|
419
|
+
return None
|
420
|
+
|
421
|
+
input_bb = Topology.BoundingBox(input)
|
422
|
+
input_centroid = Topology.Centroid(input_bb)
|
423
|
+
input_d = Topology.Dictionary(input_bb)
|
424
|
+
xmin = Dictionary.ValueAtKey(input_d, "xmin")
|
425
|
+
ymin = Dictionary.ValueAtKey(input_d, "ymin")
|
426
|
+
zmin = Dictionary.ValueAtKey(input_d, "zmin")
|
427
|
+
xmax = Dictionary.ValueAtKey(input_d, "xmax")
|
428
|
+
ymax = Dictionary.ValueAtKey(input_d, "ymax")
|
429
|
+
zmax = Dictionary.ValueAtKey(input_d, "zmax")
|
430
|
+
input_width = xmax-xmin
|
431
|
+
input_length = ymax-ymin
|
432
|
+
input_height = zmax-zmin
|
433
|
+
input_max = max(input_width, input_length, input_height)
|
434
|
+
sf = 1/input_max
|
435
|
+
temp_input = Topology.Translate(input, -Vertex.X(input_centroid), -Vertex.Y(input_centroid), -Vertex.Z(input_centroid))
|
436
|
+
temp_input = Topology.Scale(temp_input, x=sf, y=sf, z=sf)
|
437
|
+
temp_input = Topology.Translate(temp_input, 0.5, 0, 0)
|
438
|
+
|
439
|
+
output_bb = Topology.BoundingBox(output)
|
440
|
+
output_centroid = Topology.Centroid(output_bb)
|
441
|
+
output_d = Topology.Dictionary(output_bb)
|
442
|
+
xmin = Dictionary.ValueAtKey(output_d, "xmin")
|
443
|
+
ymin = Dictionary.ValueAtKey(output_d, "ymin")
|
444
|
+
zmin = Dictionary.ValueAtKey(output_d, "zmin")
|
445
|
+
xmax = Dictionary.ValueAtKey(output_d, "xmax")
|
446
|
+
ymax = Dictionary.ValueAtKey(output_d, "ymax")
|
447
|
+
zmax = Dictionary.ValueAtKey(output_d, "zmax")
|
448
|
+
output_width = xmax-xmin
|
449
|
+
output_length = ymax-ymin
|
450
|
+
output_height = zmax-zmin
|
451
|
+
output_max = max(output_width, output_length, output_height)
|
452
|
+
sf = 1/output_max
|
453
|
+
temp_output = Topology.Translate(output, -Vertex.X(output_centroid), -Vertex.Y(output_centroid), -Vertex.Z(output_centroid))
|
454
|
+
temp_output = Topology.Scale(temp_output, x=sf, y=sf, z=sf)
|
455
|
+
temp_output = Topology.Translate(temp_output, 2.5, 0, 0)
|
456
|
+
|
457
|
+
cyl = Cell.Cylinder(radius=0.04, height=0.4, placement="bottom")
|
458
|
+
cyl=Topology.Rotate(cyl, axis=[0,1,0], angle=90)
|
459
|
+
cyl = Topology.Translate(cyl, 1.25, 0, 0)
|
460
|
+
|
461
|
+
cone = Cell.Cone(baseRadius=0.1, topRadius=0, height=0.15, placement="bottom")
|
462
|
+
cone=Topology.Rotate(cone, axis=[0,1,0], angle=90)
|
463
|
+
cone = Topology.Translate(cone, 1.65, 0, 0)
|
464
|
+
cluster = Cluster.ByTopologies([temp_input, temp_output, cyl, cone])
|
465
|
+
cluster = Topology.Place(cluster, originA=Topology.Centroid(cluster), originB=Vertex.Origin())
|
466
|
+
data = Plotly.DataByTopology(cluster)
|
467
|
+
fig = Plotly.FigureByData(data)
|
468
|
+
return fig
|
469
|
+
|
470
|
+
def FigureByRule(self, rule, silent: bool = False):
|
471
|
+
"""
|
472
|
+
Returns the Plotly figure of the input rule.
|
473
|
+
|
474
|
+
Parameters
|
475
|
+
----------
|
476
|
+
rule : dict
|
477
|
+
The input rule
|
478
|
+
silent : bool, optional
|
479
|
+
If True, suppresses error/warning messages. Default is False.
|
480
|
+
|
481
|
+
Returns
|
482
|
+
-------
|
483
|
+
This function does not return a value
|
484
|
+
"""
|
485
|
+
from topologicpy.Topology import Topology
|
486
|
+
if not isinstance(rule, dict):
|
487
|
+
if not silent:
|
488
|
+
print("ShapeGrammar.DrawRule - Error: The input rule parameter is not a valid rule. Returning None.")
|
489
|
+
return None
|
490
|
+
input = rule["input"]
|
491
|
+
output = self.ApplyRule(input, rule)
|
492
|
+
return self.FigureByInputOutput(input, output)
|
topologicpy/Shell.py
CHANGED
@@ -1846,6 +1846,42 @@ class Shell():
|
|
1846
1846
|
final_result = Shell.ByFaces(final_faces, tolerance=tolerance)
|
1847
1847
|
return final_result
|
1848
1848
|
|
1849
|
+
@staticmethod
|
1850
|
+
def Square(origin= None, size: float = 1.0,
|
1851
|
+
uSides: int = 2, vSides: int = 2, direction: list = [0, 0, 1],
|
1852
|
+
placement: str = "center", tolerance: float = 0.0001):
|
1853
|
+
"""
|
1854
|
+
Creates a square.
|
1855
|
+
|
1856
|
+
Parameters
|
1857
|
+
----------
|
1858
|
+
origin : topologic_core.Vertex , optional
|
1859
|
+
The location of the origin of the square. The default is None which results in the square being placed at (0, 0, 0).
|
1860
|
+
size : float , optional
|
1861
|
+
The size of the square. The default is 1.0.
|
1862
|
+
length : float , optional
|
1863
|
+
The length of the square. The default is 1.0.
|
1864
|
+
uSides : int , optional
|
1865
|
+
The number of sides along the width. The default is 2.
|
1866
|
+
vSides : int , optional
|
1867
|
+
The number of sides along the length. The default is 2.
|
1868
|
+
direction : list , optional
|
1869
|
+
The vector representing the up direction of the square. The default is [0, 0, 1].
|
1870
|
+
placement : str , optional
|
1871
|
+
The description of the placement of the origin of the square. This can be "center", or "lowerleft". It is case insensitive. The default is "center".
|
1872
|
+
tolerance : float , optional
|
1873
|
+
The desired tolerance. The default is 0.0001.
|
1874
|
+
|
1875
|
+
Returns
|
1876
|
+
-------
|
1877
|
+
topologic_core.Shell
|
1878
|
+
The created shell square.
|
1879
|
+
|
1880
|
+
"""
|
1881
|
+
return Shell.Rectangle(origin=origin, width=size, length=size,
|
1882
|
+
uSides=uSides, vSides=vSides, direction=direction,
|
1883
|
+
placement=placement, tolerance=tolerance)
|
1884
|
+
|
1849
1885
|
@staticmethod
|
1850
1886
|
def Vertices(shell) -> list:
|
1851
1887
|
"""
|