topologicpy 0.8.47__py3-none-any.whl → 0.8.49__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/CellComplex.py +147 -0
- topologicpy/Graph.py +246 -11
- topologicpy/Plotly.py +1 -1
- topologicpy/Shell.py +137 -0
- topologicpy/Wire.py +15 -1
- topologicpy/version.py +1 -1
- {topologicpy-0.8.47.dist-info → topologicpy-0.8.49.dist-info}/METADATA +1 -1
- {topologicpy-0.8.47.dist-info → topologicpy-0.8.49.dist-info}/RECORD +11 -11
- {topologicpy-0.8.47.dist-info → topologicpy-0.8.49.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.47.dist-info → topologicpy-0.8.49.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.47.dist-info → topologicpy-0.8.49.dist-info}/top_level.txt +0 -0
topologicpy/CellComplex.py
CHANGED
@@ -193,6 +193,153 @@ class CellComplex():
|
|
193
193
|
cells = Topology.Cells(cluster)
|
194
194
|
return CellComplex.ByCells(cells, tolerance=tolerance)
|
195
195
|
|
196
|
+
@staticmethod
|
197
|
+
def ByDisjointedFaces(faces: list,
|
198
|
+
minOffset: float = 0,
|
199
|
+
maxOffset: float = 1.0,
|
200
|
+
minCells: float = 2,
|
201
|
+
maxCells: float = 10,
|
202
|
+
maxAttempts: int = 100,
|
203
|
+
patience: int = 5,
|
204
|
+
transferDictionaries: bool = False,
|
205
|
+
exclusive: bool = True,
|
206
|
+
tolerance: float = 0.0001,
|
207
|
+
silent: bool = False):
|
208
|
+
"""
|
209
|
+
Creates a CellComplex from a list of disjointed faces. The algorithm expands the faces by an offset to find intersections before building cells.
|
210
|
+
|
211
|
+
Parameters
|
212
|
+
----------
|
213
|
+
faces : list of topologic_core.Face
|
214
|
+
The linput ist of faces.
|
215
|
+
minOffset : float , optional
|
216
|
+
The minimum initial face offset to try. Default is 0.
|
217
|
+
maxOffset : float , optional
|
218
|
+
The final maximum face offset to try. Default is 1.0.
|
219
|
+
minCells : int , optional
|
220
|
+
The minimum number of cells to create. A CellComplex cannot have less than 2 cells. Default is 2.
|
221
|
+
maxCells : int , optional
|
222
|
+
The maximum number of cells to create. Default is 10.
|
223
|
+
maxAttempts : int , optional
|
224
|
+
The desired maximum number of attempts. Default is 100.
|
225
|
+
patience : int , optional
|
226
|
+
The desired number of attempts to wait with no change in the created number of cells. Default is 5.
|
227
|
+
transferDictionaries : bool , optional
|
228
|
+
If set to True, face dictionaries are inhertied. Default is False.
|
229
|
+
exclusive : bool , optional
|
230
|
+
Applies only if transferDictionaries is set to True. If set to True, only one source face contributes its dictionary to a target face. Default is True.
|
231
|
+
tolerance : float , optional
|
232
|
+
The desired tolerance. Default is 0.0001.
|
233
|
+
silent : bool , optional
|
234
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
235
|
+
|
236
|
+
Returns
|
237
|
+
-------
|
238
|
+
topologic_core.CellComplex
|
239
|
+
The created CellComplex
|
240
|
+
|
241
|
+
"""
|
242
|
+
|
243
|
+
from topologicpy.Face import Face
|
244
|
+
from topologicpy.Cell import Cell
|
245
|
+
from topologicpy.Topology import Topology
|
246
|
+
from topologicpy.Helper import Helper
|
247
|
+
|
248
|
+
def trim(cells, n):
|
249
|
+
volumes = [Cell.Volume(c) for c in cells]
|
250
|
+
return_cc = Helper.Sort(cells, volumes)
|
251
|
+
return_cc.reverse()
|
252
|
+
return return_cc[:n]
|
253
|
+
|
254
|
+
faces = [f for f in faces if Topology.IsInstance(f, "Face")]
|
255
|
+
if len(faces) == 0:
|
256
|
+
if not silent:
|
257
|
+
print("CellComplex.ByDisjointedFaces - Error: The input list of faces does not contain any valid topologic faces. Returning None.")
|
258
|
+
return None
|
259
|
+
if len(faces) < 3:
|
260
|
+
if not silent:
|
261
|
+
print("CellComplex.ByDisjointedFaces - Error: The input list of faces contains less than three topologic faces. Returning None.")
|
262
|
+
return None
|
263
|
+
if minOffset < 0:
|
264
|
+
if not silent:
|
265
|
+
print("CellComplex.ByDisjointedFaces - Error: The input minOffset parameter is less than 0. Returning None.")
|
266
|
+
return None
|
267
|
+
if minOffset > maxOffset:
|
268
|
+
if not silent:
|
269
|
+
print("CellComplex.ByDisjointedFaces - Error: The input minOffset parameter is greater than the input maxOffset parameter. Returning None.")
|
270
|
+
return None
|
271
|
+
if minCells < 2:
|
272
|
+
if not silent:
|
273
|
+
print("CellComplex.ByDisjointedFaces - Error: The input minCells parameter is less than 2. Returning None.")
|
274
|
+
return None
|
275
|
+
if minCells > maxCells:
|
276
|
+
if not silent:
|
277
|
+
print("CellComplex.ByDisjointedFaces - Error: The input minCells parameter is greater than the input maxCells parameter. Returning None.")
|
278
|
+
return None
|
279
|
+
if maxAttempts <= 0:
|
280
|
+
if not silent:
|
281
|
+
print("CellComplex.ByDisjointedFaces - Error: The input maxAttempts parameter is not greater than 0. Returning None.")
|
282
|
+
return None
|
283
|
+
if patience < 0:
|
284
|
+
if not silent:
|
285
|
+
print("CellComplex.ByDisjointedFaces - Error: The input patience parameter is not greater than or equal to 0. Returning None.")
|
286
|
+
return None
|
287
|
+
if patience > maxAttempts:
|
288
|
+
if not silent:
|
289
|
+
print("CellComplex.ByDisjointedFaces - Error: The input patience parameter is greater than the input maxAttempts parameter. Returning None.")
|
290
|
+
return None
|
291
|
+
cc = None
|
292
|
+
attempts = 0
|
293
|
+
increment = float(maxOffset) / float(maxAttempts)
|
294
|
+
cellComplexes = [] # List of all possible cellComplexes
|
295
|
+
patience_list = []
|
296
|
+
offset = minOffset
|
297
|
+
|
298
|
+
while attempts < maxAttempts:
|
299
|
+
expanded_faces = [Face.ByOffset(f, offset=-offset, silent=silent) for f in faces]
|
300
|
+
try:
|
301
|
+
cc = CellComplex.ByFaces(expanded_faces, silent=True)
|
302
|
+
if Topology.IsInstance(cc, "cellComplex"):
|
303
|
+
cells = Topology.Cells(cc)
|
304
|
+
n_cells = len(cells)
|
305
|
+
if minCells <= n_cells <= maxCells:
|
306
|
+
cellComplexes.append(cc)
|
307
|
+
elif n_cells > maxCells:
|
308
|
+
cells = trim(cells, maxCells)
|
309
|
+
try:
|
310
|
+
new_cc = CellComplex.ByCells(cells)
|
311
|
+
if Topology.IsInstance(new_cc, "CellComplex"):
|
312
|
+
cellComplexes.append(new_cc)
|
313
|
+
except:
|
314
|
+
pass
|
315
|
+
patience_list.append(n_cells)
|
316
|
+
except:
|
317
|
+
patience_list.append(0)
|
318
|
+
|
319
|
+
if len(patience_list) >= patience:
|
320
|
+
if len(set(patience_list)) == 1 and not patience_list[0] == 0:
|
321
|
+
if not silent:
|
322
|
+
print("CellComplex.ByDisjointedFaces - Warning: Ran out of patience.")
|
323
|
+
break
|
324
|
+
else:
|
325
|
+
patience_list = []
|
326
|
+
attempts += 1
|
327
|
+
offset += increment
|
328
|
+
|
329
|
+
if len(cellComplexes) == 0:
|
330
|
+
if not silent:
|
331
|
+
print("CellComplex.ByDisjointedFaces - Error: Could not create a CellComplex. Consider revising the input parameters. Returning None.")
|
332
|
+
return None
|
333
|
+
n_cells = [len(Topology.Cells(c)) for c in cellComplexes] # Get the number of cells in each cellComplex
|
334
|
+
cellComplexes = Helper.Sort(cellComplexes, n_cells) # Sort the cellComplexes by their number of cells
|
335
|
+
for cc in cellComplexes:
|
336
|
+
cells = Topology.Cells(cc)
|
337
|
+
cc = cellComplexes[-1] # Choose the last cellComplex (the one with the most number of cells)
|
338
|
+
if transferDictionaries == True:
|
339
|
+
cc_faces = Topology.Faces(cc)
|
340
|
+
cc_faces = Topology.Inherit(targets=cc_faces, sources=faces, exclusive=exclusive, tolerance=tolerance, silent=silent)
|
341
|
+
return cc
|
342
|
+
|
196
343
|
@staticmethod
|
197
344
|
def ByFaces(faces: list, tolerance: float = 0.0001, silent: bool = False):
|
198
345
|
"""
|
topologicpy/Graph.py
CHANGED
@@ -8159,24 +8159,28 @@ class Graph:
|
|
8159
8159
|
Returns:
|
8160
8160
|
A numpy array representing the adjacency matrix.
|
8161
8161
|
"""
|
8162
|
+
|
8162
8163
|
|
8164
|
+
|
8163
8165
|
# Get the number of nodes from the edge list.
|
8164
|
-
|
8165
|
-
|
8166
|
-
|
8166
|
+
if len(edge_list) > 0:
|
8167
|
+
flat_list = Helper.Flatten(edge_list)
|
8168
|
+
flat_list = [x for x in flat_list if not x == None]
|
8169
|
+
num_nodes = max(flat_list) + 1
|
8167
8170
|
|
8168
|
-
|
8169
|
-
|
8171
|
+
# Create an adjacency matrix.
|
8172
|
+
adjacency_matrix = np.zeros((num_nodes, num_nodes))
|
8170
8173
|
|
8171
|
-
|
8172
|
-
|
8173
|
-
|
8174
|
-
|
8174
|
+
# Fill in the adjacency matrix.
|
8175
|
+
for edge in edge_list:
|
8176
|
+
adjacency_matrix[edge[0], edge[1]] = 1
|
8177
|
+
adjacency_matrix[edge[1], edge[0]] = 1
|
8175
8178
|
|
8176
|
-
|
8179
|
+
return adjacency_matrix
|
8180
|
+
else:
|
8181
|
+
return None
|
8177
8182
|
|
8178
8183
|
def tree_from_edge_list(edge_list, root_index=0):
|
8179
|
-
|
8180
8184
|
adj_matrix = edge_list_to_adjacency_matrix(edge_list)
|
8181
8185
|
num_nodes = adj_matrix.shape[0]
|
8182
8186
|
root = _Tree(str(root_index))
|
@@ -12457,7 +12461,238 @@ class Graph:
|
|
12457
12461
|
net.show_buttons()
|
12458
12462
|
net.show(path, notebook=notebook)
|
12459
12463
|
return None
|
12464
|
+
|
12465
|
+
@staticmethod
|
12466
|
+
def Quotient(topology,
|
12467
|
+
topologyType: str = None,
|
12468
|
+
key: str = None,
|
12469
|
+
groupLabelKey: str = None,
|
12470
|
+
groupCountKey: str = "count",
|
12471
|
+
weighted: bool = False,
|
12472
|
+
edgeWeightKey: str = "weight",
|
12473
|
+
idKey: str = None,
|
12474
|
+
silent: bool = False):
|
12475
|
+
"""
|
12476
|
+
Construct the quotient graph induced by grouping sub-topologies (Cells/Faces/Edges/Vertices)
|
12477
|
+
by a dictionary value. Two groups are connected if any member of one is adjacent to any member
|
12478
|
+
of the other via Topology.AdjacentTopologies. If weighted=True, edge weights count the number
|
12479
|
+
of distinct member-level adjacencies across groups. See https://en.wikipedia.org/wiki/Quotient_graph
|
12480
|
+
|
12481
|
+
Parameters
|
12482
|
+
----------
|
12483
|
+
topology : topologic_core.Topology
|
12484
|
+
The input topology.
|
12485
|
+
topologyType : str
|
12486
|
+
The type of subtopology for which to search. This can be one of "vertex", "edge", "face", "cell". It is case-insensitive.
|
12487
|
+
key : str , optional
|
12488
|
+
Dictionary key used to form groups. If None, all items fall into one group.
|
12489
|
+
groupLabelKey : str , optional
|
12490
|
+
Vertex-dictionary key storing the group label. Default is "group_label".
|
12491
|
+
groupCountKey : str , optional
|
12492
|
+
Vertex-dictionary key storing the group size. Default is "count".
|
12493
|
+
weighted : bool , optional
|
12494
|
+
If True, store counts of cross-group adjacencies on edges under edgeWeightKey. Default is False.
|
12495
|
+
edgeWeightKey : str , optional
|
12496
|
+
Edge-dictionary key storing the weight when weighted=True. Default "weight".
|
12497
|
+
idKey : str , optional
|
12498
|
+
Optional dictionary key that uniquely identifies each sub-topology. If provided and present
|
12499
|
+
on both members, lookup is O(1) without calling Topology.IsSame. If missing, falls back to Topology.IsSame. Default is None.
|
12500
|
+
silent : bool , optional
|
12501
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
12502
|
+
|
12503
|
+
Returns
|
12504
|
+
-------
|
12505
|
+
topologic_core.Graph
|
12506
|
+
"""
|
12507
|
+
from topologicpy.Topology import Topology
|
12508
|
+
from topologicpy.Dictionary import Dictionary
|
12509
|
+
from topologicpy.Vertex import Vertex
|
12510
|
+
from topologicpy.Edge import Edge
|
12511
|
+
from topologicpy.Graph import Graph
|
12512
|
+
|
12513
|
+
if not Topology.IsInstance(topology, "Topology"):
|
12514
|
+
if not silent:
|
12515
|
+
print("Graph.Quotient - Error: The input topology parameter is not a valid Topology. Returning None.")
|
12516
|
+
return None
|
12517
|
+
if topologyType.lower() not in {"vertex", "edge", "face", "cell"}:
|
12518
|
+
if not silent:
|
12519
|
+
print("Graph.Quotient - Error: topologyType must be one of 'Cell','Face','Edge','Vertex'. Returning None.")
|
12520
|
+
return None
|
12521
|
+
if not isinstance(key, str):
|
12522
|
+
if not silent:
|
12523
|
+
print("Graph.Quotient - Error: The input key parameter is not a valid string. Returning None.")
|
12524
|
+
return None
|
12525
|
+
if groupLabelKey == None:
|
12526
|
+
groupLabelKey = key
|
12527
|
+
if not isinstance(groupLabelKey, str):
|
12528
|
+
if not silent:
|
12529
|
+
print("Graph.Quotient - Error: The input groupLabelKey parameter is not a valid string. Returning None.")
|
12530
|
+
return None
|
12531
|
+
if not isinstance(groupCountKey, str):
|
12532
|
+
if not silent:
|
12533
|
+
print("Graph.Quotient - Error: The input groupCountKey parameter is not a valid string. Returning None.")
|
12534
|
+
return None
|
12535
|
+
if not isinstance(weighted, bool):
|
12536
|
+
if not silent:
|
12537
|
+
print("Graph.Quotient - Error: The input weighted parameter is not a valid boolean. Returning None.")
|
12538
|
+
return None
|
12539
|
+
if not isinstance(edgeWeightKey, str):
|
12540
|
+
if not silent:
|
12541
|
+
print("Graph.Quotient - Error: The input edgeWeightKey parameter is not a valid string. Returning None.")
|
12542
|
+
return None
|
12460
12543
|
|
12544
|
+
# 1) Collect sub-topologies
|
12545
|
+
getters = {
|
12546
|
+
"vertex": Topology.Vertices,
|
12547
|
+
"edge": Topology.Edges,
|
12548
|
+
"face": Topology.Faces,
|
12549
|
+
"cell": Topology.Cells,
|
12550
|
+
}
|
12551
|
+
subs = getters[topologyType.lower()](topology)
|
12552
|
+
if not subs:
|
12553
|
+
if not silent:
|
12554
|
+
print("Graph.Quotient - Error: No subtopologies found. Returning None.")
|
12555
|
+
return None
|
12556
|
+
|
12557
|
+
# 2) Optional O(1) index via a unique idKey in dictionaries
|
12558
|
+
def _get_dict(st):
|
12559
|
+
try:
|
12560
|
+
return Topology.Dictionary(st)
|
12561
|
+
except Exception:
|
12562
|
+
return None
|
12563
|
+
|
12564
|
+
id_index = {}
|
12565
|
+
if idKey is not None:
|
12566
|
+
for i, st in enumerate(subs):
|
12567
|
+
d = _get_dict(st)
|
12568
|
+
if d is None:
|
12569
|
+
continue
|
12570
|
+
try:
|
12571
|
+
uid = Dictionary.ValueAtKey(d, idKey)
|
12572
|
+
except Exception:
|
12573
|
+
uid = None
|
12574
|
+
if uid is not None and uid not in id_index:
|
12575
|
+
id_index[uid] = i # first seen wins
|
12576
|
+
|
12577
|
+
# 3) Labels for grouping
|
12578
|
+
def _label(st):
|
12579
|
+
if key is None:
|
12580
|
+
return None
|
12581
|
+
d = _get_dict(st)
|
12582
|
+
if d is None:
|
12583
|
+
return None
|
12584
|
+
try:
|
12585
|
+
return Dictionary.ValueAtKey(d, key)
|
12586
|
+
except Exception:
|
12587
|
+
return None
|
12588
|
+
|
12589
|
+
labels = [_label(st) for st in subs]
|
12590
|
+
|
12591
|
+
# 4) Partition indices by label
|
12592
|
+
groups = {}
|
12593
|
+
for i, lbl in enumerate(labels):
|
12594
|
+
groups.setdefault(lbl, []).append(i)
|
12595
|
+
|
12596
|
+
group_labels = list(groups.keys())
|
12597
|
+
label_to_group_idx = {lbl: gi for gi, lbl in enumerate(group_labels)}
|
12598
|
+
item_to_group_idx = {i: label_to_group_idx[lbl] for lbl, idxs in groups.items() for i in idxs}
|
12599
|
+
|
12600
|
+
# Helper to resolve neighbor index j for a given neighbor topology nb
|
12601
|
+
def _neighbor_index(nb):
|
12602
|
+
# Try idKey shortcut
|
12603
|
+
if idKey is not None:
|
12604
|
+
d = _get_dict(nb)
|
12605
|
+
if d is not None:
|
12606
|
+
try:
|
12607
|
+
uid = Dictionary.ValueAtKey(d, idKey)
|
12608
|
+
except Exception:
|
12609
|
+
uid = None
|
12610
|
+
if uid is not None:
|
12611
|
+
j = id_index.get(uid)
|
12612
|
+
if j is not None:
|
12613
|
+
return j
|
12614
|
+
# Fallback: linear scan using Topology.IsSame
|
12615
|
+
for j, st in enumerate(subs):
|
12616
|
+
try:
|
12617
|
+
if Topology.IsSame(nb, st):
|
12618
|
+
return j
|
12619
|
+
except Exception:
|
12620
|
+
continue
|
12621
|
+
return None
|
12622
|
+
|
12623
|
+
# 5) Group adjacency with optional weights
|
12624
|
+
# Use a set to ensure each unordered member-pair is counted once
|
12625
|
+
seen_member_pairs = set()
|
12626
|
+
group_edges = {} # (a,b) -> weight
|
12627
|
+
|
12628
|
+
for i, st in enumerate(subs):
|
12629
|
+
try:
|
12630
|
+
neighs = Topology.AdjacentTopologies(st, topology, topologyType)
|
12631
|
+
except Exception:
|
12632
|
+
neighs = []
|
12633
|
+
gi = item_to_group_idx[i]
|
12634
|
+
for nb in neighs:
|
12635
|
+
j = _neighbor_index(nb)
|
12636
|
+
if j is None or j == i:
|
12637
|
+
continue
|
12638
|
+
aij = (i, j) if i < j else (j, i)
|
12639
|
+
if aij in seen_member_pairs:
|
12640
|
+
continue
|
12641
|
+
seen_member_pairs.add(aij)
|
12642
|
+
|
12643
|
+
gj = item_to_group_idx[j]
|
12644
|
+
if gi == gj:
|
12645
|
+
continue
|
12646
|
+
a, b = (gi, gj) if gi < gj else (gj, gi)
|
12647
|
+
group_edges[(a, b)] = group_edges.get((a, b), 0) + 1
|
12648
|
+
|
12649
|
+
# 6) One vertex per group at mean of member centroids
|
12650
|
+
group_vertices = []
|
12651
|
+
for lbl in group_labels:
|
12652
|
+
idxs = groups[lbl]
|
12653
|
+
pts = []
|
12654
|
+
for i in idxs:
|
12655
|
+
try:
|
12656
|
+
c = Topology.Centroid(subs[i])
|
12657
|
+
pts.append(c)
|
12658
|
+
except Exception:
|
12659
|
+
pass
|
12660
|
+
if pts:
|
12661
|
+
try:
|
12662
|
+
xs = [Vertex.X(p) for p in pts]
|
12663
|
+
ys = [Vertex.Y(p) for p in pts]
|
12664
|
+
zs = [Vertex.Z(p) for p in pts]
|
12665
|
+
v = Vertex.ByCoordinates(sum(xs)/len(xs), sum(ys)/len(ys), sum(zs)/len(zs))
|
12666
|
+
except Exception:
|
12667
|
+
v = Vertex.ByCoordinates(0, 0, 0)
|
12668
|
+
else:
|
12669
|
+
v = Vertex.ByCoordinates(0, 0, 0)
|
12670
|
+
|
12671
|
+
try:
|
12672
|
+
d = Dictionary.ByKeysValues([groupLabelKey, groupCountKey], [lbl, len(idxs)])
|
12673
|
+
v = Topology.SetDictionary(v, d)
|
12674
|
+
except Exception:
|
12675
|
+
pass
|
12676
|
+
|
12677
|
+
group_vertices.append(v)
|
12678
|
+
|
12679
|
+
# 7) Edges, with optional weights
|
12680
|
+
edges = []
|
12681
|
+
for (a, b), w in group_edges.items():
|
12682
|
+
try:
|
12683
|
+
e = Edge.ByStartVertexEndVertex(group_vertices[a], group_vertices[b])
|
12684
|
+
if weighted:
|
12685
|
+
try:
|
12686
|
+
d = Dictionary.ByKeysValues([edgeWeightKey], [w])
|
12687
|
+
e = Topology.SetDictionary(e, d)
|
12688
|
+
except Exception:
|
12689
|
+
pass
|
12690
|
+
edges.append(e)
|
12691
|
+
except Exception:
|
12692
|
+
continue
|
12693
|
+
|
12694
|
+
return Graph.ByVerticesEdges(group_vertices, edges)
|
12695
|
+
|
12461
12696
|
@staticmethod
|
12462
12697
|
def RemoveEdge(graph, edge, tolerance=0.0001):
|
12463
12698
|
"""
|
topologicpy/Plotly.py
CHANGED
@@ -558,7 +558,7 @@ class Plotly:
|
|
558
558
|
if not temp_color == None:
|
559
559
|
colors[m] = Color.AnyToHex(temp_color)
|
560
560
|
if not labelKey == None:
|
561
|
-
labels[m] = Dictionary.ValueAtKey(d, key=labelKey, defaultValue=" ")
|
561
|
+
labels[m] = str(Dictionary.ValueAtKey(d, key=labelKey, defaultValue=" "))
|
562
562
|
if not sizeKey == None:
|
563
563
|
sizes[m] = float(Dictionary.ValueAtKey(d, key=sizeKey, defaultValue=sizes[m]))
|
564
564
|
if sizes[m] == None:
|
topologicpy/Shell.py
CHANGED
@@ -1097,6 +1097,143 @@ class Shell():
|
|
1097
1097
|
return True
|
1098
1098
|
return False
|
1099
1099
|
|
1100
|
+
@staticmethod
|
1101
|
+
def MobiusStrip(origin = None,
|
1102
|
+
radius: float=0.5,
|
1103
|
+
height: float=1,
|
1104
|
+
uSides=32,
|
1105
|
+
vSides=1,
|
1106
|
+
twists: int = 1,
|
1107
|
+
direction: list = [0, 0, 1],
|
1108
|
+
placement: str = "center",
|
1109
|
+
tolerance: float = 0.0001,
|
1110
|
+
silent: bool = False):
|
1111
|
+
|
1112
|
+
|
1113
|
+
"""
|
1114
|
+
Creates a Möbius strip. See: https://en.wikipedia.org/wiki/M%C3%B6bius_strip
|
1115
|
+
|
1116
|
+
Parameters
|
1117
|
+
----------
|
1118
|
+
origin : topologic_core.Vertex , optional
|
1119
|
+
The location of the origin of the Möbius strip. Default is None which results in the Möbius strip being placed at (0, 0, 0).
|
1120
|
+
radius : float , optional
|
1121
|
+
The radius of the Möbius strip. Default is 0.5.
|
1122
|
+
height : float , optional
|
1123
|
+
The height of the Möbius strip. Default is 1.
|
1124
|
+
uSides : int , optional
|
1125
|
+
The number of circle segments of the Möbius strip. Default is 16.
|
1126
|
+
vSides : int , optional
|
1127
|
+
The number of vertical segments of the Möbius strip. Default is 1.
|
1128
|
+
twists : int , optional
|
1129
|
+
The number of twists (multiples of a 180 degree rotation) of the Möbius strip. Default is 1.
|
1130
|
+
direction : list , optional
|
1131
|
+
The vector representing the up direction of the Möbius strip. Default is [0, 0, 1].
|
1132
|
+
placement : str , optional
|
1133
|
+
Not implemented. The description of the placement of the origin of the Möbius strip. This can be "bottom", "center", or "lowerleft". It is case insensitive. Default is "bottom".
|
1134
|
+
tolerance : float , optional
|
1135
|
+
The desired tolerance. Default is 0.0001.
|
1136
|
+
silent : bool , optional
|
1137
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
1138
|
+
|
1139
|
+
Returns
|
1140
|
+
-------
|
1141
|
+
topologic_core.Cell
|
1142
|
+
The created cell.
|
1143
|
+
|
1144
|
+
"""
|
1145
|
+
from topologicpy.Vertex import Vertex
|
1146
|
+
from topologicpy.Edge import Edge
|
1147
|
+
from topologicpy.Wire import Wire
|
1148
|
+
from topologicpy.Topology import Topology
|
1149
|
+
|
1150
|
+
if not isinstance(radius, int) and not isinstance(radius, float):
|
1151
|
+
if not silent:
|
1152
|
+
print("Shell.MobiusStrip - Error: The radius input parameter is not a valid number. Returning None.")
|
1153
|
+
return None
|
1154
|
+
if not isinstance(height, int) and not isinstance(height, float):
|
1155
|
+
if not silent:
|
1156
|
+
print("Shell.MobiusStrip - Error: The height input parameter is not a valid number. Returning None.")
|
1157
|
+
return None
|
1158
|
+
if not isinstance(uSides, int):
|
1159
|
+
if not silent:
|
1160
|
+
print("Shell.MobiusStrip - Error: The uSides input parameter is not a valid integer. Returning None.")
|
1161
|
+
return None
|
1162
|
+
if not isinstance(vSides, int):
|
1163
|
+
if not silent:
|
1164
|
+
print("Shell.MobiusStrip - Error: The vSides input parameter is not a valid integer. Returning None.")
|
1165
|
+
return None
|
1166
|
+
if not isinstance(twists, int):
|
1167
|
+
if not silent:
|
1168
|
+
print("Shell.MobiusStrip - Error: The twists input parameter is not a valid integer. Returning None.")
|
1169
|
+
return None
|
1170
|
+
if radius <= tolerance:
|
1171
|
+
if not silent:
|
1172
|
+
print("Shell.MobiusStrip - Error: The radius input parameter must be a positive number greater than the tolerance input parameter. Returning None.")
|
1173
|
+
return None
|
1174
|
+
if height <= tolerance:
|
1175
|
+
if not silent:
|
1176
|
+
print("Shell.MobiusStrip - Error: The height input parameter must be a positive number greater than the tolerance input parameter. Returning None.")
|
1177
|
+
return None
|
1178
|
+
if uSides <= 3:
|
1179
|
+
if not silent:
|
1180
|
+
print("Shell.MobiusStrip - Error: The uSides input parameter must be a positive integer greater than 2. Returning None.")
|
1181
|
+
return None
|
1182
|
+
if vSides < 1:
|
1183
|
+
if not silent:
|
1184
|
+
print("Shell.MobiusStrip - Error: The vSides input parameter must be a positive integer greater than 0. Returning None.")
|
1185
|
+
return None
|
1186
|
+
if origin == None:
|
1187
|
+
origin = Vertex.Origin()
|
1188
|
+
if not Topology.IsInstance(origin, "Vertex"):
|
1189
|
+
if not silent:
|
1190
|
+
print("Shell.MobiusStrip - Error: The origin input parameter is not a valid topologic Vertex. Returning None.")
|
1191
|
+
return None
|
1192
|
+
if not isinstance(direction, list):
|
1193
|
+
if not silent:
|
1194
|
+
print("Shell.MobiusStrip - Error: The direction input parameter is not a valid list. Returning None.")
|
1195
|
+
return None
|
1196
|
+
if not len(direction) == 3:
|
1197
|
+
if not silent:
|
1198
|
+
print("Shell.MobiusStrip - Error: The direction input parameter is not a valid vector. Returning None.")
|
1199
|
+
return None
|
1200
|
+
|
1201
|
+
total_angle = 180*twists
|
1202
|
+
cir = Wire.Circle(origin=origin, radius=radius, sides=uSides)
|
1203
|
+
c_verts = Topology.Vertices(cir)
|
1204
|
+
wires = []
|
1205
|
+
for i, v in enumerate(c_verts):
|
1206
|
+
vb = Vertex.ByCoordinates(Vertex.X(v), Vertex.Y(v), -height*0.5)
|
1207
|
+
vt = Vertex.ByCoordinates(Vertex.X(v), Vertex.Y(v), height*0.5)
|
1208
|
+
e = Edge.ByVertices([vb, vt])
|
1209
|
+
d = Edge.Normal(Edge.ByVertices(Vertex.Origin(), v))
|
1210
|
+
angle = float(total_angle)/float(uSides)*float(i)
|
1211
|
+
e = Topology.Rotate(e, origin=v, axis=d, angle=angle)
|
1212
|
+
verts = []
|
1213
|
+
for j in range(vSides+1):
|
1214
|
+
vp = Edge.VertexByParameter(e, float(j)/float(vSides))
|
1215
|
+
verts.append(vp)
|
1216
|
+
w = Wire.ByVertices(verts, close=False, silent=True)
|
1217
|
+
wires.append(w)
|
1218
|
+
# Create the last wire
|
1219
|
+
if i == 0:
|
1220
|
+
e = Edge.ByVertices([vb, vt])
|
1221
|
+
e = Topology.Rotate(e, origin=v, axis=d, angle=total_angle)
|
1222
|
+
verts = []
|
1223
|
+
for j in range(vSides+1):
|
1224
|
+
vp = Edge.VertexByParameter(e, float(j)/float(vSides))
|
1225
|
+
verts.append(vp)
|
1226
|
+
last_wire = Wire.ByVertices(verts, close=False, silent=True)
|
1227
|
+
|
1228
|
+
wires.append(last_wire)
|
1229
|
+
m = Shell.ByWires(wires, silent=silent, tolerance=tolerance)
|
1230
|
+
if not Topology.IsInstance(m, "Shell"):
|
1231
|
+
if not silent:
|
1232
|
+
print("Shell.MobiusStrip - Error: Could not create a mobius strip. Returning None.")
|
1233
|
+
return None
|
1234
|
+
m = Topology.Orient(m, origin=origin, dirA=[0, 0, 1], dirB=direction)
|
1235
|
+
return m
|
1236
|
+
|
1100
1237
|
@staticmethod
|
1101
1238
|
def Paraboloid(origin= None, focalLength=0.125, width: float = 1, length: float = 1, uSides: int = 16, vSides: int = 16,
|
1102
1239
|
direction: list = [0, 0, 1], placement: str ="center", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
|
topologicpy/Wire.py
CHANGED
@@ -870,6 +870,7 @@ class Wire():
|
|
870
870
|
edges.append(e)
|
871
871
|
else:
|
872
872
|
if not silent:
|
873
|
+
print("Wire.ByVertices - Warning: Degenerate edge. Skipping.")
|
873
874
|
curframe = inspect.currentframe()
|
874
875
|
calframe = inspect.getouterframes(curframe, 2)
|
875
876
|
print('caller name:', calframe[1][3])
|
@@ -885,6 +886,7 @@ class Wire():
|
|
885
886
|
curframe = inspect.currentframe()
|
886
887
|
calframe = inspect.getouterframes(curframe, 2)
|
887
888
|
print('caller name:', calframe[1][3])
|
889
|
+
|
888
890
|
if len(edges) < 1:
|
889
891
|
if not silent:
|
890
892
|
print("Wire.ByVertices - Error: The number of edges is less than 1. Returning None.")
|
@@ -893,9 +895,21 @@ class Wire():
|
|
893
895
|
print('caller name:', calframe[1][3])
|
894
896
|
return None
|
895
897
|
elif len(edges) == 1:
|
898
|
+
if not silent:
|
899
|
+
print("Wire.ByVertices - Warning: The wire is made of only one edge.")
|
896
900
|
wire = Wire.ByEdges(edges, orient=False, silent=silent)
|
897
901
|
else:
|
898
902
|
wire = Topology.SelfMerge(Cluster.ByTopologies(edges), tolerance=tolerance)
|
903
|
+
if Topology.IsInstance(wire, "Edge"):
|
904
|
+
wire = Wire.ByEdges([wire], orient=False, silent=silent)
|
905
|
+
# Final Check
|
906
|
+
if not Topology.IsInstance(wire, "Wire"):
|
907
|
+
if not silent:
|
908
|
+
print("Wire.ByVertices - Error: Could not create a wire. Returning None.")
|
909
|
+
curframe = inspect.currentframe()
|
910
|
+
calframe = inspect.getouterframes(curframe, 2)
|
911
|
+
print('caller name:', calframe[1][3])
|
912
|
+
return None
|
899
913
|
return wire
|
900
914
|
|
901
915
|
@staticmethod
|
@@ -2989,7 +3003,7 @@ class Wire():
|
|
2989
3003
|
for i in range(1, sides):
|
2990
3004
|
vertices.append(Edge.VertexByParameter(edge, i*unitDistance))
|
2991
3005
|
vertices.append(Edge.EndVertex(edge))
|
2992
|
-
return Wire.ByVertices(vertices,
|
3006
|
+
return Wire.ByVertices(vertices, close=False, tolerance=tolerance)
|
2993
3007
|
|
2994
3008
|
@staticmethod
|
2995
3009
|
def LShape(origin=None,
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.8.
|
1
|
+
__version__ = '0.8.49'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.49
|
4
4
|
Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
|
5
5
|
Author-email: Wassim Jabi <wassim.jabi@gmail.com>
|
6
6
|
License: AGPL v3 License
|
@@ -3,7 +3,7 @@ topologicpy/Aperture.py,sha256=wNn5miB_IrGCBYuQ18HXQYRva20dUC3id4AJCulL7to,2723
|
|
3
3
|
topologicpy/BVH.py,sha256=JA4bb-9hgMfVZ_syzmSmTL3ueCq-0vMUGMPZxNcawAY,13023
|
4
4
|
topologicpy/CSG.py,sha256=09la1-xzS9vr-WnV7tpJ0I-mkZ-XY0MRSd5iB50Nfgw,15556
|
5
5
|
topologicpy/Cell.py,sha256=gldsBl3LnrvTRS4SnK9aAM8k_kS1aNovTjUNHxQRNKk,175319
|
6
|
-
topologicpy/CellComplex.py,sha256=
|
6
|
+
topologicpy/CellComplex.py,sha256=Kbz63rGeE08bJfMXFvB-AptoKHiaCK5OtiV1wz8Y-Fk,68081
|
7
7
|
topologicpy/Cluster.py,sha256=G49AuhJHQ1s819cB5MtVdmAGgkag19IC3dRP1ub1Wh4,58608
|
8
8
|
topologicpy/Color.py,sha256=hzSmgBWhiuYc55RSipkQNIgGtgyhC5BqY8AakNYEK-U,24486
|
9
9
|
topologicpy/Context.py,sha256=G3CwMvN8Jw2rnQRwB-n4MaQq_wLS0vPimbXKwsdMJ80,3055
|
@@ -12,27 +12,27 @@ topologicpy/Dictionary.py,sha256=Z4YQ88tONWd-0X0dENQ8IZqIOa9mbBqhJkTBsHmft2g,446
|
|
12
12
|
topologicpy/Edge.py,sha256=DifItuyabFDUFC7CVMlt2DeMFMNaGOqCg43iU9CPP0A,74029
|
13
13
|
topologicpy/EnergyModel.py,sha256=IiBJNx7F9-8TPMaQn1iQON1ZtTv2nT5kbZHxM_gBCTQ,53773
|
14
14
|
topologicpy/Face.py,sha256=aX9EcR3JGbLITElhd25J0Z8m9U8KkmbYivGg3oZN-Uw,202296
|
15
|
-
topologicpy/Graph.py,sha256=
|
15
|
+
topologicpy/Graph.py,sha256=vK5mj90cc_cwC5suodSuYDrXCX3njp_SV4SiTyQzvuE,685271
|
16
16
|
topologicpy/Grid.py,sha256=3OsBMyHh4w8gpFOTMKHMNTpo62V0CwRNu5cwm87yDUA,18421
|
17
17
|
topologicpy/Helper.py,sha256=aGmndgmEztjVNU-wW9OoHDel7wzapprM0TjA7f2AoS8,31188
|
18
18
|
topologicpy/Honeybee.py,sha256=C7Am0kCK3a5rt7Jpu2EIgqeR114ZJWtsx4_DBcr5hQA,21716
|
19
19
|
topologicpy/Matrix.py,sha256=bOofT34G3YHu9aMIWx60YHAJga4R0GbDjsZBUD4Hu_k,22706
|
20
20
|
topologicpy/Neo4j.py,sha256=J8jU_mr5-mWC0Lg_D2dMjMlx1rY_eh8ks_aubUuTdWw,22319
|
21
|
-
topologicpy/Plotly.py,sha256=
|
21
|
+
topologicpy/Plotly.py,sha256=RXGeEBwVs8unJaT9vv_FBmAFfKd-1Z5x3V8oeKE43Bs,122924
|
22
22
|
topologicpy/Polyskel.py,sha256=oVfM4lqSMPTjnkHfsRU9VI8Blt6Vf0LVPkD9ebz7Wmw,27082
|
23
23
|
topologicpy/PyG.py,sha256=wOsoBFxMgwZYWjj86OMkz_PJuQ02locV_djhSDD6dVc,109644
|
24
24
|
topologicpy/ShapeGrammar.py,sha256=KYsKDLXWdflAcYMAIz84AUF-GMkbTmaBDd2-ovbilqU,23336
|
25
|
-
topologicpy/Shell.py,sha256=
|
25
|
+
topologicpy/Shell.py,sha256=e6R7JdzYL1ubO0xXJs5P_UiiNHccN5SjQhQfGPPgHBg,96793
|
26
26
|
topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
|
27
27
|
topologicpy/Sun.py,sha256=8S6dhCKfOhUGVny-jEk87Q08anLYMB1JEBKRGCklvbQ,36670
|
28
28
|
topologicpy/Topology.py,sha256=x6NTJ8AFIeFq3emZRT7FA_6NgNd-aavd4i9ws704AAU,470541
|
29
29
|
topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
|
30
30
|
topologicpy/Vertex.py,sha256=0f6HouARKaCuxhdxsUEYi8T9giJycnWhQ8Cn70YILBA,84885
|
31
|
-
topologicpy/Wire.py,sha256=
|
31
|
+
topologicpy/Wire.py,sha256=gjgQUGHdBdXUIijgZc_VIW0E39w-smaVhhdl0jF63fQ,230466
|
32
32
|
topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
|
33
|
-
topologicpy/version.py,sha256=
|
34
|
-
topologicpy-0.8.
|
35
|
-
topologicpy-0.8.
|
36
|
-
topologicpy-0.8.
|
37
|
-
topologicpy-0.8.
|
38
|
-
topologicpy-0.8.
|
33
|
+
topologicpy/version.py,sha256=QtAkT5hlIePCtadXLl4JDdKbyvuiJ4-dBo60LPK6aDE,23
|
34
|
+
topologicpy-0.8.49.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
35
|
+
topologicpy-0.8.49.dist-info/METADATA,sha256=xrO1G5MVgzF8yZXxfihp6syuWrK7z_aBao9_chfUw6U,10535
|
36
|
+
topologicpy-0.8.49.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
37
|
+
topologicpy-0.8.49.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
38
|
+
topologicpy-0.8.49.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|