topologicpy 0.8.33__py3-none-any.whl → 0.8.35__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.
@@ -48,99 +48,6 @@ except:
48
48
  warnings.warn("CellComplex - Error: Could not import scipy.")
49
49
 
50
50
  class CellComplex():
51
- @staticmethod
52
- def AdjacencyDictionary(cellComplex, cellLabelKey: str = None, faceKey: str = None, includeWeights: bool = False, reverse: bool = False, mantissa: int = 6, silent: bool = False):
53
- """
54
- Returns the adjacency dictionary of the input Graph.
55
-
56
- Parameters
57
- ----------
58
- cellComplex : topologic_core.CellComplex
59
- The input cellComplex.
60
- cellLabelKey : str , optional
61
- The returned cells are labelled according to the dictionary values stored under this key.
62
- If the cellLabelKey does not exist, it will be created and the cells are labelled numerically and stored in the vertex dictionary under this key. The default is None.
63
- faceKey : str , optional
64
- If set, the faces' dictionaries will be searched for this key to set their weight. If the key is set to "Area" (case insensitive), the area of the shared faces will be used as its weight. If set to None, a weight of 1 will be used. The default is None.
65
- includeWeights : bool , optional
66
- If set to True, edge weights are included. Otherwise, they are not. The default is False.
67
- reverse : bool , optional
68
- If set to True, the cells are sorted in reverse order (only if cellLabelKey is set). Otherwise, they are not. The default is False.
69
-
70
- silent : bool , optional
71
- If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
72
-
73
- Returns
74
- -------
75
- dict
76
- The adjacency dictionary.
77
- """
78
- from topologicpy.Face import Face
79
- from topologicpy.Dictionary import Dictionary
80
- from topologicpy.Topology import Topology
81
- from topologicpy.Helper import Helper
82
-
83
- if not Topology.IsInstance(cellComplex, "CellComplex"):
84
- if not silent:
85
- print("CellComplex.AdjacencyDictionary - Error: The input cellComplex input parameter is not a valid cellComplex. Returning None.")
86
- return None
87
- if cellLabelKey == None:
88
- cellLabelKey = "__label__"
89
- if not isinstance(cellLabelKey, str):
90
- if not silent:
91
- print("CellComplex.AdjacencyDictionary - Error: The input cellLabelKey is not a valid string. Returning None.")
92
- return None
93
- all_cells = Topology.Cells(cellComplex)
94
- labels = []
95
- n = max(len(str(len(all_cells))), 3)
96
- for i, cell in enumerate(all_cells):
97
- d = Topology.Dictionary(cell)
98
- value = Dictionary.ValueAtKey(d, cellLabelKey)
99
- if value == None:
100
- value = str(i+1).zfill(n)
101
- if d == None:
102
- d = Dictionary.ByKeyValue(cellLabelKey, value)
103
- else:
104
- d = Dictionary.SetValueAtKey(d, cellLabelKey, value)
105
- cell = Topology.SetDictionary(cell, d)
106
- labels.append(value)
107
- all_cells = Helper.Sort(all_cells, labels)
108
- labels.sort()
109
- order = len(all_cells)
110
- adjDict = {}
111
- for i in range(order):
112
- cell = all_cells[i]
113
- cell_label = labels[i]
114
- adjCells = Topology.AdjacentTopologies(cell, hostTopology=cellComplex, topologyType="cell")
115
- temp_list = []
116
- for adjCell in adjCells:
117
- adj_label = Dictionary.ValueAtKey(Topology.Dictionary(adjCell), cellLabelKey)
118
- adj_index = labels.index(adj_label)
119
- if includeWeights == True:
120
- if faceKey == None:
121
- weight = 1
122
- elif "area" in faceKey.lower():
123
- shared_topologies = Topology.SharedTopologies(cell, adjCell)
124
- faces = shared_topologies.get("faces", [])
125
- weight = sum([Face.Area(face, mantissa=mantissa) for face in faces])
126
- else:
127
- shared_topologies = Topology.SharedTopologies(cell, adjCell)
128
- faces = shared_topologies.get("faces", [])
129
- weight = sum([Dictionary.ValueAtKey(Topology.Dictionary(face),faceKey, 0) for face in faces])
130
- if not adj_index == None:
131
- temp_list.append((adj_label, weight))
132
- else:
133
- if not adj_index == None:
134
- temp_list.append(adj_label)
135
- temp_list.sort()
136
- adjDict[cell_label] = temp_list
137
- if cellLabelKey == "__label__": # This is label we added, so remove it
138
- for cell in all_cells:
139
- d = Topology.Dictionary(cell)
140
- d = Dictionary.RemoveKey(d, cellLabelKey)
141
- cell = Topology.SetDictionary(cell, d)
142
- return adjDict
143
-
144
51
  @staticmethod
145
52
  def Box(origin= None,
146
53
  width: float = 1.0, length: float = 1.0, height: float = 1.0,
@@ -1143,7 +1050,7 @@ class CellComplex():
1143
1050
  shells = Topology.Shells(cellComplex)
1144
1051
  return shells
1145
1052
 
1146
-
1053
+ @staticmethod
1147
1054
  def _grow_connected_group(seed_idx, group_size, adjacency, visited_global):
1148
1055
  """
1149
1056
  Attempts to grow a group of the given size starting from seed_idx using adjacency.
@@ -1170,98 +1077,6 @@ class CellComplex():
1170
1077
 
1171
1078
  return group if len(group) == group_size else None
1172
1079
 
1173
- def SubCombinations(cellComplex,
1174
- minCells: int = 2,
1175
- maxCells: int = None,
1176
- maxCombinations: int = 100,
1177
- timeLimit: int = 10,
1178
- silent: bool = False):
1179
- """
1180
- Creates sub-combination cellComplexes of the input cellComplex. Warning: This is prone to combinatorial explosion.
1181
-
1182
- Parameters
1183
- ----------
1184
- cellComplex : topologic_core.cellComplex
1185
- The input cellComplex
1186
- minCells : int , optional
1187
- The minimum number of cells to include in a combination. The default is 2.
1188
- maxCells : int , optional
1189
- The maximum number of cells to include in a combinations. The default is None which means the maximum will be set to the number of cells in the cellComplex minus 1.
1190
- maxCombinations : int , optional
1191
- The maximum number of combinations to create. The default is 100.
1192
- timeLimit : int , optional
1193
- The time limit in seconds. The default is 10 seconds. Note that this time limit only applies to creating the combination indices and not the actual CellComplexes.
1194
- tolerance : float , optional
1195
- The tolerance for computing if the input vertex is external to the input topology. The default is 0.0001.
1196
- silent : bool , optional
1197
- If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1198
-
1199
- Returns
1200
- -------
1201
- list
1202
- The list of created CellComplex sub-combinations.
1203
-
1204
- """
1205
- from topologicpy.Cluster import Cluster
1206
- from topologicpy.Topology import Topology
1207
- from topologicpy.Dictionary import Dictionary
1208
- import random
1209
- import time
1210
-
1211
- if not Topology.IsInstance(cellComplex, "CellComplex"):
1212
- if not silent:
1213
- print("CellComplex.SubCombinations - Error: The cellComplex input parameter is not a valid cellComplex. Returning None.")
1214
- return None
1215
-
1216
-
1217
- start_time = time.time()
1218
- all_cells = CellComplex.Cells(cellComplex)
1219
- num_cells = len(all_cells)
1220
- indices = list(range(num_cells))
1221
- cell_label_key = "index"
1222
- # 1. Assign a unique index to each cell's dictionary
1223
- all_cells = [Topology.SetDictionary(cell, Dictionary.ByKeyValue(cell_label_key, i)) for i, cell in enumerate(all_cells)]
1224
- if maxCells == None:
1225
- maxCells = len(all_cells) - 1
1226
- # 2. Allocate counts per group size
1227
- group_sizes = list(range(minCells, maxCells + 1))
1228
- combinations_per_size = maxCombinations // len(group_sizes)
1229
-
1230
- # 3. Build adjacency dict
1231
- adjacency = CellComplex.AdjacencyDictionary(cellComplex, cellLabelKey=cell_label_key, faceKey=None, includeWeights=False)
1232
-
1233
- results = []
1234
- seen_groups = set() # sets of sorted indices
1235
-
1236
- # 4. Start from longest group size
1237
- for group_size in reversed(group_sizes):
1238
- remaining = combinations_per_size
1239
- tries = 0
1240
- max_tries = combinations_per_size * 10 # fallback guard
1241
- while remaining > 0 and time.time() - start_time < timeLimit and tries < max_tries:
1242
- random.shuffle(indices)
1243
- for seed in indices:
1244
- if time.time() - start_time > timeLimit:
1245
- break
1246
- group = CellComplex._grow_connected_group(seed, group_size, adjacency, visited_global=seen_groups)
1247
- if group:
1248
- key = tuple(sorted(group))
1249
- if key not in seen_groups:
1250
- tries += 1
1251
- seen_groups.add(key)
1252
- results.append(group)
1253
- remaining -= 1
1254
- if remaining <= 0 or tries >= max_tries:
1255
- break
1256
- # 5. Build CellComplex SubCombinations
1257
- return_combinations = []
1258
- for i, result in enumerate(reversed(results)):
1259
- cells = [all_cells[i] for i in result]
1260
- combination = Topology.SelfMerge(Cluster.ByTopologies(cells))
1261
- if Topology.IsInstance(combination, "CellComplex"):
1262
- return_combinations.append(combination)
1263
- return return_combinations
1264
-
1265
1080
  @staticmethod
1266
1081
  def Tetrahedron(origin = None, length: float = 1, depth: int = 1, direction=[0,0,1], placement="center", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
1267
1082
  """
topologicpy/Dictionary.py CHANGED
@@ -46,6 +46,125 @@ class Dictionary():
46
46
  dictionaries.append(Dictionary.ByKeysValues(keys, values))
47
47
  return dictionaries
48
48
  '''
49
+ @staticmethod
50
+ def AdjacencyDictionary(topology, subTopologyType: str = None, labelKey: str = None, weightKey: str = None, includeWeights: bool = False, mantissa: int = 6, silent: bool = False):
51
+ """
52
+ Returns the adjacency dictionary of the input Shell.
53
+
54
+ Parameters
55
+ ----------
56
+ topology : topologic_core.Topology
57
+ The input topology.
58
+ subTopologyType : str , optional
59
+ The type of subTopology on which to base the adjacency dictionary.
60
+ labelKey : str , optional
61
+ The returned subTopologies are labelled according to the dictionary values stored under this key.
62
+ If the labelKey does not exist, it will be created and the subTopologies are labelled numerically and stored in the subTopologies' dictionary under this key. The default is None.
63
+ weightKey : str , optional
64
+ If set, the sharedTopologies' dictionaries will be searched for this key to set their weight. If the key is set to "Area" or "Length" (case insensitive), the area of shared faces or the length of the shared edges will be used as its weight. If set to None, a weight of 1 will be used. The default is None.
65
+ includeWeights : bool , optional
66
+ If set to True, edge weights are included. Otherwise, they are not. The default is False.
67
+ mantissa : int , optional
68
+ The desired length of the mantissa. The default is 6.
69
+ silent : bool , optional
70
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
71
+
72
+ Returns
73
+ -------
74
+ dict
75
+ The adjacency dictionary.
76
+ """
77
+ from topologicpy.Edge import Edge
78
+ from topologicpy.Dictionary import Dictionary
79
+ from topologicpy.Topology import Topology
80
+ from topologicpy.Graph import Graph
81
+ from topologicpy.Helper import Helper
82
+
83
+ if not Topology.IsInstance(topology, "Topology") and not Topology.IsInstance(topology, "Graph"):
84
+ if not silent:
85
+ print("Dictionary.AdjacencyDictionary - Error: The input topology input parameter is not a valid topology. Returning None.")
86
+ return None
87
+ # Special Case for Graphs
88
+
89
+ if Topology.IsInstance(topology, "Graph"):
90
+ return Graph.AdjacencyDictionary(topology, vertexLabelKey=labelKey, edgeKey=weightKey, includeWeights=includeWeights, mantissa=mantissa)
91
+ if labelKey == None:
92
+ labelKey = "__label__"
93
+ if not isinstance(labelKey, str):
94
+ if not silent:
95
+ print("Dictionary.AdjacencyDictionary - Error: The input labelKey is not a valid string. Returning None.")
96
+ return None
97
+ if Topology.IsInstance(topology, "cellcomplex"):
98
+ if subTopologyType == None:
99
+ subTopologyType = "cell"
100
+ all_subtopologies = Topology.SubTopologies(topology, subTopologyType=subTopologyType, silent=silent)
101
+ elif Topology.IsInstance(topology, "cell") or Topology.IsInstance(topology, "shell"):
102
+ if subTopologyType == None:
103
+ subTopologyType = "face"
104
+ all_subtopologies = Topology.SubTopologies(topology, subTopologyType=subTopologyType, silent=silent)
105
+ elif Topology.IsInstance(topology, "face") or Topology.IsInstance(topology, "wire"):
106
+ if subTopologyType == None:
107
+ subTopologyType = "edge"
108
+ all_subtopologies = Topology.SubTopologies(topology, subTopologyType=subTopologyType, silent=silent)
109
+ labels = []
110
+ n = max(len(str(len(all_subtopologies))), 3)
111
+ for i, subtopology in enumerate(all_subtopologies):
112
+ d = Topology.Dictionary(subtopology)
113
+ value = Dictionary.ValueAtKey(d, labelKey)
114
+ if value == None:
115
+ value = str(i+1).zfill(n)
116
+ if d == None:
117
+ d = Dictionary.ByKeyValue(labelKey, value)
118
+ else:
119
+ d = Dictionary.SetValueAtKey(d, labelKey, value)
120
+ subtopology = Topology.SetDictionary(subtopology, d)
121
+ labels.append(value)
122
+ all_subtopologies = Helper.Sort(all_subtopologies, labels)
123
+ labels.sort()
124
+ order = len(all_subtopologies)
125
+ adjDict = {}
126
+ for i in range(order):
127
+ subtopology = all_subtopologies[i]
128
+ subt_label = labels[i]
129
+ adjacent_topologies = Topology.AdjacentTopologies(subtopology, hostTopology=topology, topologyType=subTopologyType)
130
+ temp_list = []
131
+ for adj_topology in adjacent_topologies:
132
+ adj_label = Dictionary.ValueAtKey(Topology.Dictionary(adj_topology), labelKey)
133
+ adj_index = labels.index(adj_label)
134
+ if includeWeights == True:
135
+ if weightKey == None:
136
+ weight = 1
137
+ elif "length" in weightKey.lower():
138
+ shared_topologies = Topology.SharedTopologies(subtopology, adj_topology)
139
+ edges = shared_topologies.get("edges", [])
140
+ weight = sum([Edge.Length(edge, mantissa=mantissa) for edge in edges])
141
+ elif "area" in weightKey.lower():
142
+ shared_topologies = Topology.SharedTopologies(subtopology, adj_topology)
143
+ faces = shared_topologies.get("faces", [])
144
+ weight = sum([Edge.Length(edge, mantissa=mantissa) for face in faces])
145
+ else:
146
+ shared_topologies = Topology.SharedTopologies(subtopology, adj_topology)
147
+ vertices = shared_topologies.get("vertices", [])
148
+ edges = shared_topologies.get("edges", [])
149
+ wires = shared_topologies.get("wires", [])
150
+ faces = shared_topologies.get("faces", [])
151
+ everything = vertices+edges+wires+faces
152
+ weight = sum([Dictionary.ValueAtKey(Topology.Dictionary(x),weightKey, 0) for x in everything])
153
+ weight = round(weight, mantissa)
154
+ if not adj_index == None:
155
+ temp_list.append((adj_label, weight))
156
+ else:
157
+ if not adj_index == None:
158
+ temp_list.append(adj_label)
159
+ temp_list.sort()
160
+ adjDict[subt_label] = temp_list
161
+ if labelKey == "__label__": # This is label we added, so remove it
162
+ for subtopology in all_subtopologies:
163
+ d = Topology.Dictionary(subtopology)
164
+ d = Dictionary.RemoveKey(d, labelKey)
165
+ subtopology = Topology.SetDictionary(subtopology, d)
166
+ return adjDict
167
+
49
168
  @staticmethod
50
169
  def ByKeyValue(key, value):
51
170
  """
topologicpy/Graph.py CHANGED
@@ -523,7 +523,7 @@ class Graph:
523
523
  _ = graph.AddVertices(vertices, tolerance) # Hook to Core
524
524
  return graph
525
525
 
526
- def AdjacencyDictionary(graph, vertexLabelKey: str = None, edgeKey: str = "Length", includeWeights: bool = False, reverse: bool = False, mantissa: int = 6):
526
+ def AdjacencyDictionary(graph, vertexLabelKey: str = None, edgeKey: str = "Length", includeWeights: bool = False, mantissa: int = 6):
527
527
  """
528
528
  Returns the adjacency dictionary of the input Graph.
529
529
 
@@ -537,9 +537,7 @@ class Graph:
537
537
  edgeKey : str , optional
538
538
  If set, the edges' dictionaries will be searched for this key to set their weight. If the key is set to "length" (case insensitive), the length of the edge will be used as its weight. If set to None, a weight of 1 will be used. The default is "Length".
539
539
  includeWeights : bool , optional
540
- If set to True, edge weights are included. Otherwise, they are not. The default is False.
541
- reverse : bool , optional
542
- If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
540
+ If set to True, edge weights are included. Otherwise, they are not. The default is False.
543
541
  mantissa : int , optional
544
542
  The desired length of the mantissa. The default is 6.
545
543
 
@@ -578,9 +576,6 @@ class Graph:
578
576
  labels.append(value)
579
577
  vertices = Helper.Sort(vertices, labels)
580
578
  labels.sort()
581
- if reverse == True:
582
- vertices.reverse()
583
- labels.reverse()
584
579
  order = len(vertices)
585
580
  adjDict = {}
586
581
  for i in range(order):
topologicpy/Helper.py CHANGED
@@ -286,6 +286,58 @@ class Helper:
286
286
  flat_list = flat_list + Helper.Flatten(item)
287
287
  return flat_list
288
288
 
289
+ @staticmethod
290
+ def Grow(seed_idx, group_size, adjacency, visited_global):
291
+ """
292
+ Attempts to grow a spatially connected group of a specified size starting from a given seed index.
293
+
294
+ This method uses a breadth-first search strategy to explore neighboring indices from the seed index,
295
+ guided by the provided adjacency dictionary. It avoids reusing indices that are globally visited.
296
+ The growth continues until the desired group size is reached or no further expansion is possible.
297
+
298
+ Parameters
299
+ ----------
300
+ seed_idx : int
301
+ The index from which to start growing the group.
302
+ group_size : int
303
+ The target size of the group to be grown.
304
+ adjacency : dict
305
+ A dictionary mapping each index to a list of adjacent indices. This defines the connectivity.
306
+ visited_global : set
307
+ A set of indices that have already been used in previously grown groups and should be avoided.
308
+
309
+ Returns
310
+ -------
311
+ list[int] or None
312
+ A list of indices representing a connected group of the specified size if successful, otherwise None.
313
+
314
+ Notes
315
+ -----
316
+ This method is intended for internal use in functions that generate connected subgroups
317
+ of spatial elements (e.g., cells) based on adjacency. The result may vary between runs due to random shuffling
318
+ of neighbor order to diversify outputs.
319
+ """
320
+ from collections import deque
321
+ import random
322
+
323
+ group = [seed_idx]
324
+ visited = set(group)
325
+ queue = deque([seed_idx])
326
+
327
+ while queue and len(group) < group_size:
328
+ current = queue.popleft()
329
+ neighbors = adjacency.get(current, [])
330
+ random.shuffle(neighbors)
331
+ for neighbor in neighbors:
332
+ if neighbor not in visited and neighbor not in visited_global:
333
+ group.append(neighbor)
334
+ visited.add(neighbor)
335
+ queue.append(neighbor)
336
+ if len(group) >= group_size:
337
+ break
338
+
339
+ return group if len(group) == group_size else None
340
+
289
341
  @staticmethod
290
342
  def Iterate(listA):
291
343
  """
topologicpy/Shell.py CHANGED
@@ -739,42 +739,6 @@ class Shell():
739
739
  _ = shell.Faces(None, faces)
740
740
  return faces
741
741
 
742
- @staticmethod
743
- def IsOnBoundary(shell, vertex, tolerance: float = 0.0001) -> bool:
744
- """
745
- Returns True if the input vertex is on the boundary of the input shell. Returns False otherwise. On the boundary is defined as being on the boundary of one of the shell's external or internal boundaries
746
-
747
- Parameters
748
- ----------
749
- shell : topologic_core.Shell
750
- The input shell.
751
- vertex : topologic_core.Vertex
752
- The input vertex.
753
- tolerance : float , optional
754
- The desired tolerance. The default is 0.0001.
755
-
756
- Returns
757
- -------
758
- bool
759
- Returns True if the input vertex is inside the input shell. Returns False otherwise.
760
-
761
- """
762
- from topologicpy.Vertex import Vertex
763
- from topologicpy.Topology import Topology
764
-
765
- if not Topology.IsInstance(shell, "Shell"):
766
- return None
767
- if not Topology.IsInstance(vertex, "Vertex"):
768
- return None
769
- boundary = Shell.ExternalBoundary(shell, tolerance=tolerance)
770
- if Vertex.IsInternal(vertex, boundary, tolerance=tolerance):
771
- return True
772
- internal_boundaries = Shell.InternalBoundaries(shell, tolerance=tolerance)
773
- for ib in internal_boundaries:
774
- if Vertex.IsInternal(vertex, ib, tolerance=tolerance):
775
- return True
776
- return False
777
-
778
742
  @staticmethod
779
743
  def HyperbolicParaboloidRectangularDomain(origin= None,
780
744
  llVertex= None,
@@ -1097,6 +1061,41 @@ class Shell():
1097
1061
  """
1098
1062
  return shell.IsClosed()
1099
1063
 
1064
+ @staticmethod
1065
+ def IsOnBoundary(shell, vertex, tolerance: float = 0.0001) -> bool:
1066
+ """
1067
+ Returns True if the input vertex is on the boundary of the input shell. Returns False otherwise. On the boundary is defined as being on the boundary of one of the shell's external or internal boundaries
1068
+
1069
+ Parameters
1070
+ ----------
1071
+ shell : topologic_core.Shell
1072
+ The input shell.
1073
+ vertex : topologic_core.Vertex
1074
+ The input vertex.
1075
+ tolerance : float , optional
1076
+ The desired tolerance. The default is 0.0001.
1077
+
1078
+ Returns
1079
+ -------
1080
+ bool
1081
+ Returns True if the input vertex is inside the input shell. Returns False otherwise.
1082
+
1083
+ """
1084
+ from topologicpy.Vertex import Vertex
1085
+ from topologicpy.Topology import Topology
1086
+
1087
+ if not Topology.IsInstance(shell, "Shell"):
1088
+ return None
1089
+ if not Topology.IsInstance(vertex, "Vertex"):
1090
+ return None
1091
+ boundary = Shell.ExternalBoundary(shell, tolerance=tolerance)
1092
+ if Vertex.IsInternal(vertex, boundary, tolerance=tolerance):
1093
+ return True
1094
+ internal_boundaries = Shell.InternalBoundaries(shell, tolerance=tolerance)
1095
+ for ib in internal_boundaries:
1096
+ if Vertex.IsInternal(vertex, ib, tolerance=tolerance):
1097
+ return True
1098
+ return False
1100
1099
 
1101
1100
  @staticmethod
1102
1101
  def Paraboloid(origin= None, focalLength=0.125, width: float = 1, length: float = 1, uSides: int = 16, vSides: int = 16,
@@ -1644,62 +1643,6 @@ class Shell():
1644
1643
  else:
1645
1644
  return None
1646
1645
 
1647
- def Skeleton(face, tolerance: float = 0.001):
1648
- """
1649
- Creates a shell through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
1650
- This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel
1651
-
1652
- Parameters
1653
- ----------
1654
- face : topologic_core.Face
1655
- The input face.
1656
- tolerance : float , optional
1657
- The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)
1658
-
1659
- Returns
1660
- -------
1661
- topologic_core.Shell
1662
- The created straight skeleton.
1663
-
1664
- """
1665
- from topologicpy.Wire import Wire
1666
- from topologicpy.Face import Face
1667
- from topologicpy.Topology import Topology
1668
- import topologic_core as topologic
1669
- import math
1670
-
1671
- if not Topology.IsInstance(face, "Face"):
1672
- return None
1673
- roof = Wire.Skeleton(face, tolerance=tolerance)
1674
- if not (Topology.IsInstance(roof, "Wire") or Topology.IsInstance(roof, "Cluster")):
1675
- print("Shell.Skeleton - Error: Could not create base skeleton wire. Returning None.")
1676
- return None
1677
- br = Wire.BoundingRectangle(roof) #This works even if it is a Cluster not a Wire
1678
- if not Topology.IsInstance(br, "Wire"):
1679
- print("Shell.Skeleton - Error: Could not create a bounding rectangle wire. Returning None.")
1680
- return None
1681
- br = Topology.Scale(br, Topology.Centroid(br), 1.5, 1.5, 1)
1682
- bf = Face.ByWire(br, tolerance=tolerance)
1683
- if not Topology.IsInstance(bf, "Face"):
1684
- print("Shell.Skeleton - Error: Could not create a bounding rectangle face. Returning None.")
1685
- return None
1686
- large_shell = Topology.Boolean(bf, roof, operation="slice", tolerance=tolerance)
1687
- if not large_shell:
1688
- return None
1689
- faces = Topology.Faces(large_shell)
1690
- if not faces:
1691
- return None
1692
- final_faces = []
1693
- for f in faces:
1694
- internalBoundaries = Face.InternalBoundaries(f)
1695
- if len(internalBoundaries) == 0:
1696
- final_faces.append(f)
1697
- shell = Shell.ByFaces(final_faces, tolerance=tolerance)
1698
- if not Topology.IsInstance(shell, "Shell"):
1699
- print("Shell.Skeleton - Error: Could not create shell. Returning None.")
1700
- return None
1701
- return shell
1702
-
1703
1646
  @staticmethod
1704
1647
  def Simplify(shell, simplifyBoundary: bool = True, mantissa: int = 6, tolerance: float = 0.0001):
1705
1648
  """
@@ -1846,6 +1789,63 @@ class Shell():
1846
1789
  final_result = Shell.ByFaces(final_faces, tolerance=tolerance)
1847
1790
  return final_result
1848
1791
 
1792
+ @staticmethod
1793
+ def Skeleton(face, tolerance: float = 0.001):
1794
+ """
1795
+ Creates a shell through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
1796
+ This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel
1797
+
1798
+ Parameters
1799
+ ----------
1800
+ face : topologic_core.Face
1801
+ The input face.
1802
+ tolerance : float , optional
1803
+ The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)
1804
+
1805
+ Returns
1806
+ -------
1807
+ topologic_core.Shell
1808
+ The created straight skeleton.
1809
+
1810
+ """
1811
+ from topologicpy.Wire import Wire
1812
+ from topologicpy.Face import Face
1813
+ from topologicpy.Topology import Topology
1814
+ import topologic_core as topologic
1815
+ import math
1816
+
1817
+ if not Topology.IsInstance(face, "Face"):
1818
+ return None
1819
+ roof = Wire.Skeleton(face, tolerance=tolerance)
1820
+ if not (Topology.IsInstance(roof, "Wire") or Topology.IsInstance(roof, "Cluster")):
1821
+ print("Shell.Skeleton - Error: Could not create base skeleton wire. Returning None.")
1822
+ return None
1823
+ br = Wire.BoundingRectangle(roof) #This works even if it is a Cluster not a Wire
1824
+ if not Topology.IsInstance(br, "Wire"):
1825
+ print("Shell.Skeleton - Error: Could not create a bounding rectangle wire. Returning None.")
1826
+ return None
1827
+ br = Topology.Scale(br, Topology.Centroid(br), 1.5, 1.5, 1)
1828
+ bf = Face.ByWire(br, tolerance=tolerance)
1829
+ if not Topology.IsInstance(bf, "Face"):
1830
+ print("Shell.Skeleton - Error: Could not create a bounding rectangle face. Returning None.")
1831
+ return None
1832
+ large_shell = Topology.Boolean(bf, roof, operation="slice", tolerance=tolerance)
1833
+ if not large_shell:
1834
+ return None
1835
+ faces = Topology.Faces(large_shell)
1836
+ if not faces:
1837
+ return None
1838
+ final_faces = []
1839
+ for f in faces:
1840
+ internalBoundaries = Face.InternalBoundaries(f)
1841
+ if len(internalBoundaries) == 0:
1842
+ final_faces.append(f)
1843
+ shell = Shell.ByFaces(final_faces, tolerance=tolerance)
1844
+ if not Topology.IsInstance(shell, "Shell"):
1845
+ print("Shell.Skeleton - Error: Could not create shell. Returning None.")
1846
+ return None
1847
+ return shell
1848
+
1849
1849
  @staticmethod
1850
1850
  def Square(origin= None, size: float = 1.0,
1851
1851
  uSides: int = 2, vSides: int = 2, direction: list = [0, 0, 1],
@@ -1882,6 +1882,33 @@ class Shell():
1882
1882
  uSides=uSides, vSides=vSides, direction=direction,
1883
1883
  placement=placement, tolerance=tolerance)
1884
1884
 
1885
+ @staticmethod
1886
+ def _grow_connected_group(seed_idx, group_size, adjacency, visited_global):
1887
+ """
1888
+ Attempts to grow a group of the given size starting from seed_idx using adjacency.
1889
+ Returns a list of indices if successful, else None.
1890
+ """
1891
+ from collections import deque
1892
+ import random
1893
+
1894
+ group = [seed_idx]
1895
+ visited = set(group)
1896
+ queue = deque([seed_idx])
1897
+
1898
+ while queue and len(group) < group_size:
1899
+ current = queue.popleft()
1900
+ neighbors = adjacency.get(current, [])
1901
+ random.shuffle(neighbors)
1902
+ for neighbor in neighbors:
1903
+ if neighbor not in visited and neighbor not in visited_global:
1904
+ group.append(neighbor)
1905
+ visited.add(neighbor)
1906
+ queue.append(neighbor)
1907
+ if len(group) >= group_size:
1908
+ break
1909
+
1910
+ return group if len(group) == group_size else None
1911
+
1885
1912
  @staticmethod
1886
1913
  def Vertices(shell) -> list:
1887
1914
  """
topologicpy/Topology.py CHANGED
@@ -7897,13 +7897,6 @@ class Topology():
7897
7897
  calframe = inspect.getouterframes(curframe, 2)
7898
7898
  print('caller name:', calframe[1][3])
7899
7899
  return topology
7900
- # if len(dictionary.Keys()) < 1:
7901
- # if not silent:
7902
- # print("Topology.SetDictionary - Warning: the input dictionary parameter is empty. Returning original input.")
7903
- # curframe = inspect.currentframe()
7904
- # calframe = inspect.getouterframes(curframe, 2)
7905
- # print('caller name:', calframe[1][3])
7906
- # return topology
7907
7900
  _ = topology.SetDictionary(dictionary)
7908
7901
  return topology
7909
7902
 
@@ -8861,6 +8854,177 @@ class Topology():
8861
8854
  pass
8862
8855
  return returnTopology
8863
8856
 
8857
+ @staticmethod
8858
+ def SubCombinations(topology,
8859
+ subTopologyType=None,
8860
+ minSize: int = 2,
8861
+ maxSize: int = None,
8862
+ maxCombinations: int = 100,
8863
+ timeLimit: int = 10,
8864
+ removeCoplanarFaces: bool = False,
8865
+ removeCollinearEdges: bool = False,
8866
+ tolerance: float = 0.001,
8867
+ silent: bool = False):
8868
+ """
8869
+ Creates connected sub-combination topologies of the input topology. Warning: This is prone to combinatorial explosion.
8870
+
8871
+ Parameters
8872
+ ----------
8873
+ topology : topologic_core.Topology
8874
+ The input topology. This cannot be vertex or edge.
8875
+ subTopologyType : str , optional except when topology is a Cluster
8876
+ The type of subTopology to include in the combinations.
8877
+ If the input is a Cluster, you must specify the subTopologyType.
8878
+ The options are (case insensitive):
8879
+ - "CellComplex"
8880
+ - "Shell"
8881
+ - "Wire"
8882
+ If set to None, it will be set according to the input topology type as follows:
8883
+ - "Cluster" -> User-defined
8884
+ - "CellComplex" -> "CellComplexes"
8885
+ - "Cell" -> "Shells"
8886
+ - "Shell" -> "Shells"
8887
+ - "Face" -> "Wires"
8888
+ - "Wire" -> "Wires"
8889
+ - "Edge" -> None
8890
+ - "Vertex" -> None
8891
+ - "Graph" -> "Graphs"
8892
+ minSize : int , optional
8893
+ The minimum number of subtopologies to include in a combination. This number cannot be less than 2. The default is 2.
8894
+ maxSize : int , optional
8895
+ The maximum number of faces to include in a combinations. This number cannot be less than the number of subtopologies minus 1. The default is None which means the maximum will be set to the number of subtopologies minus 1.
8896
+ maxCombinations : int , optional
8897
+ The maximum number of combinations to create. The default is 100.
8898
+ timeLimit : int , optional
8899
+ The time limit in seconds. The default is 10 seconds. Note that this time limit only applies to creating the combination indices and not the actual Shells.
8900
+ removeCoplanarFaces : bool , optional
8901
+ If set to True, coplanar faces are removed. Otherwise they are not. The default is False.
8902
+ removeCollinearEdges : bool , optional
8903
+ If set to True, collinear edges are removed. Otherwise they are not. The default is False.
8904
+ tolerance : float , optional
8905
+ The desired tolerance. The default is 0.0001.
8906
+ silent : bool , optional
8907
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
8908
+
8909
+ Returns
8910
+ -------
8911
+ list
8912
+ The list of created sub-combinations.
8913
+
8914
+ """
8915
+ from topologicpy.Cluster import Cluster
8916
+ from topologicpy.Graph import Graph
8917
+ from topologicpy.Topology import Topology
8918
+ from topologicpy.Dictionary import Dictionary
8919
+ from topologicpy.Helper import Helper
8920
+ import random
8921
+ import time
8922
+
8923
+ if Topology.IsInstance(topology, "cluster") and subTopologyType is None:
8924
+ if not silent:
8925
+ print("Topology.SubCombinations - Error: The topology input parameter is a cluster but the subTopologyType is not specified. Returning None.")
8926
+ return None
8927
+
8928
+ if Topology.IsInstance(topology, "vertex") or Topology.IsInstance(topology, "edge"):
8929
+ if not silent:
8930
+ print("Topology.SubCombinations - Error: The topology input parameter cannot be a vertex or an edge. Returning None.")
8931
+ return None
8932
+
8933
+ mapping = {
8934
+ "cellcomplex": "cell",
8935
+ "cell": "face",
8936
+ "shell": "face",
8937
+ "face": "edge",
8938
+ "wire": "edge",
8939
+ "graph": "vertex"
8940
+ }
8941
+
8942
+ type_string = Topology.TypeAsString(topology).lower()
8943
+ if type_string not in mapping:
8944
+ if not silent:
8945
+ print(f"Topology.SubCombinations - Error: The input topology type ({type_string}) is not supported. Returning None.")
8946
+ return None
8947
+
8948
+ if subTopologyType is None:
8949
+ subTopologyType = mapping[type_string]
8950
+ subTopologyType = subTopologyType.lower()
8951
+
8952
+ if subTopologyType not in ["cell", "face", "edge", "vertex"]:
8953
+ if not silent:
8954
+ print(f"Topology.SubCombinations - Error: Unknown subTopologyType: {subTopologyType}. Returning None.")
8955
+ return None
8956
+
8957
+ if ((type_string in ["cell", "shell"]) and subTopologyType not in ["face", "edge"]) or \
8958
+ ((type_string in ["wire", "face"]) and subTopologyType != "edge"):
8959
+ if not silent:
8960
+ print(f"Topology.SubCombinations - Error: The subTopology type {subTopologyType} is not appropriate for topology of type {type_string}. Returning None.")
8961
+ return None
8962
+
8963
+ all_subtopologies = Topology.SubTopologies(topology, subTopologyType=subTopologyType, silent=silent)
8964
+ num_subtopologies = len(all_subtopologies)
8965
+ if num_subtopologies < 2:
8966
+ if not silent:
8967
+ print("Topology.SubCombinations - Warning: Not enough subtopologies found. Returning empty list.")
8968
+ return []
8969
+
8970
+ indices = list(range(num_subtopologies))
8971
+ minSize = max(2, minSize)
8972
+ maxSize = num_subtopologies - 1 if maxSize is None else min(maxSize, num_subtopologies - 1)
8973
+
8974
+ subt_label_key = "__index__"
8975
+ all_subtopologies = [Topology.SetDictionary(subt, Dictionary.ByKeyValue(subt_label_key, i)) for i, subt in enumerate(all_subtopologies)]
8976
+
8977
+ group_sizes = list(range(minSize, maxSize + 1))
8978
+ num_sizes = len(group_sizes)
8979
+ per_size_combinations = maxCombinations // num_sizes
8980
+ per_size_time = timeLimit / num_sizes
8981
+
8982
+ adjacency = Dictionary.AdjacencyDictionary(topology, subTopologyType=subTopologyType, labelKey=subt_label_key, weightKey=None, includeWeights=False)
8983
+
8984
+ results = []
8985
+ seen_groups = set()
8986
+
8987
+ for group_size in reversed(group_sizes):
8988
+ size_start_time = time.time()
8989
+ remaining = per_size_combinations
8990
+ tries = 0
8991
+ max_tries = per_size_combinations * 10
8992
+
8993
+ while remaining > 0 and (time.time() - size_start_time) < per_size_time and tries < max_tries:
8994
+ random.shuffle(indices)
8995
+ for seed in indices:
8996
+ if (time.time() - size_start_time) > per_size_time:
8997
+ break
8998
+ group = Helper.Grow(seed, group_size, adjacency, visited_global=seen_groups)
8999
+ if group:
9000
+ key = tuple(sorted(group))
9001
+ if key not in seen_groups:
9002
+ tries += 1
9003
+ seen_groups.add(key)
9004
+ results.append(group)
9005
+ remaining -= 1
9006
+ if remaining <= 0 or tries >= max_tries:
9007
+ break
9008
+
9009
+ return_combinations = []
9010
+ for result in reversed(results):
9011
+ subtopologies = [all_subtopologies[i] for i in result]
9012
+ # Special case for graphs
9013
+ if Topology.IsInstance(topology, "Graph"):
9014
+ edges = Graph.Edges(topology, vertices = subtopologies)
9015
+ combination = Graph.ByVerticesEdges(subtopologies, edges)
9016
+ else:
9017
+ combination = Topology.SelfMerge(Cluster.ByTopologies(subtopologies), tolerance=tolerance)
9018
+ if removeCollinearEdges:
9019
+ if Topology.IsInstance(combination, "face") or Topology.IsInstance(combination, "wire"):
9020
+ combination = Topology.RemoveCollinearEdges(combination, tolerance=tolerance)
9021
+ if removeCoplanarFaces:
9022
+ if Topology.IsInstance(combination, "cellcomplex") or Topology.IsInstance(combination, "cell") or Topology.IsInstance(combination, "shell"):
9023
+ combination = Topology.RemoveCoplanarFaces(combination, tolerance=tolerance)
9024
+ return_combinations.append(combination)
9025
+
9026
+ return return_combinations
9027
+
8864
9028
  @staticmethod
8865
9029
  def Taper(topology, origin=None, ratioRange: list = [0, 1], triangulate: bool = False, mantissa: int = 6, tolerance: float = 0.0001):
8866
9030
  """
@@ -9188,7 +9352,7 @@ class Topology():
9188
9352
  return Topology.SubTopologies(topology=topology, subTopologyType="cluster")
9189
9353
 
9190
9354
  @staticmethod
9191
- def SubTopologies(topology, subTopologyType="vertex"):
9355
+ def SubTopologies(topology, subTopologyType="vertex", silent: bool = False):
9192
9356
  """
9193
9357
  Returns the subtopologies of the input topology as specified by the subTopologyType input string.
9194
9358
 
@@ -9198,7 +9362,8 @@ class Topology():
9198
9362
  The input topology.
9199
9363
  subTopologyType : str , optional
9200
9364
  The requested subtopology type. This can be one of "vertex", "edge", "wire", "face", "shell", "cell", "cellcomplex", "cluster". It is case insensitive. The default is "vertex".
9201
-
9365
+ silent : bool , optional
9366
+ If set to True, no warnings or errors will be printed. The default is False.
9202
9367
  Returns
9203
9368
  -------
9204
9369
  list
@@ -9206,15 +9371,28 @@ class Topology():
9206
9371
 
9207
9372
  """
9208
9373
  from topologicpy.Face import Face
9374
+ from topologicpy.Graph import Graph
9209
9375
 
9210
- if not Topology.IsInstance(topology, "Topology"):
9211
- print("Topology.SubTopologies - Error: the input topology parameter is not a valid topology. Returning None.")
9376
+ if not Topology.IsInstance(topology, "Topology") and not Topology.IsInstance(topology, "Graph"):
9377
+ if not silent:
9378
+ print("Topology.SubTopologies - Error: the input topology parameter is not a valid topology. Returning None.")
9212
9379
  return None
9213
9380
  if Topology.TypeAsString(topology).lower() == subTopologyType.lower():
9214
9381
  return [topology]
9215
9382
 
9216
9383
  subTopologies = []
9217
9384
 
9385
+ # Special case for Graphs
9386
+ if Topology.IsInstance(topology, "graph"):
9387
+ if subTopologyType.lower() == "vertex":
9388
+ return Graph.Vertices(topology)
9389
+ elif subTopologyType.lower() == "edge":
9390
+ return Graph.Edges(topology)
9391
+ else:
9392
+ if not silent:
9393
+ print(f"Topology.SubTopologies - Error: the input subTopologyType parameter {subTopologyType} is not a valid subTopology of Graphs. Returning None.")
9394
+ return None
9395
+
9218
9396
  # Spcecial case for faces to return vertices in CW/CCW order.
9219
9397
  if Topology.IsInstance(topology, "face") and (subTopologyType.lower() == "vertex" or subTopologyType.lower() == "edge"):
9220
9398
  wires = Face.Wires(topology)
topologicpy/Wire.py CHANGED
@@ -4629,7 +4629,7 @@ class Wire():
4629
4629
  """
4630
4630
  sv, ev = Wire.StartEndVertices(wire)
4631
4631
  return sv
4632
-
4632
+
4633
4633
  @staticmethod
4634
4634
  def Trapezoid(origin= None, widthA: float = 1.0, widthB: float = 0.75, offsetA: float = 0.0, offsetB: float = 0.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
4635
4635
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.33'
1
+ __version__ = '0.8.35'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.33
3
+ Version: 0.8.35
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,18 +3,18 @@ topologicpy/Aperture.py,sha256=wNn5miB_IrGCBYuQ18HXQYRva20dUC3id4AJCulL7to,2723
3
3
  topologicpy/BVH.py,sha256=ti-23A2HIxaqnJ3C9GWhCjQev9qQwdrSfZVfXVZujYE,13127
4
4
  topologicpy/CSG.py,sha256=hqFPg3RvAnRgzwyyWbc4N80ZYO9AfvbWn0RsjXvaz8k,15695
5
5
  topologicpy/Cell.py,sha256=WQyi2xKT5PFovGZzu1oiw0ICuJultsOXnNbfELrMLTI,173467
6
- topologicpy/CellComplex.py,sha256=nrQmlqr0wUkd6CrpaMRfgBo6O8Crgk1rM1jDEphiDmc,70051
6
+ topologicpy/CellComplex.py,sha256=muhqLpnlZXZKVSF3j_v4uF1LbdVyrZqmLqH_uoACYRU,60919
7
7
  topologicpy/Cluster.py,sha256=Wv81yPlQ3Qlnylpvs2aBVNV77M0Z9oHTUfTeLbAuX74,58790
8
8
  topologicpy/Color.py,sha256=ZVVQRKGjebY9aOU1gpN_AbssdRRiVKlZV3f8TrsTNgg,20307
9
9
  topologicpy/Context.py,sha256=G3CwMvN8Jw2rnQRwB-n4MaQq_wLS0vPimbXKwsdMJ80,3055
10
10
  topologicpy/DGL.py,sha256=HQXy9iDnrvWGDxaBfe5pRbweQ2zLBvAf6UdjfhKkQYI,139041
11
- topologicpy/Dictionary.py,sha256=Lf24WHW8q_RCq0l8VpT3XJTn6UuStY66JI4Lb4W08jI,34126
11
+ topologicpy/Dictionary.py,sha256=Rdd8RwBkuoqndcDWOHfO5oZHngoF2brNdWEB7P5Bns4,40900
12
12
  topologicpy/Edge.py,sha256=6M7UMPZj_JXXH9mFieEcQu3haYQ6Rn64yRcL0b_axl8,73484
13
13
  topologicpy/EnergyModel.py,sha256=Pyb28gDDwhzlQIH0xqAygqS0P3SJxWyyV7OWS_AAfRs,53856
14
14
  topologicpy/Face.py,sha256=1r6j4DOhDJEVyRTuFhxh1wmRMEvqZe8FQ0_Yy-J0pzg,202021
15
- topologicpy/Graph.py,sha256=0w3GP1CuJyE6xq1mALB93R3s4OFJnF6k9HkRZDhwkoQ,581043
15
+ topologicpy/Graph.py,sha256=VNJsJpJW2fJADzgT_yYDljEc_2YjGgHBmWjIqcVplNE,580758
16
16
  topologicpy/Grid.py,sha256=EbI2NcYhQDpD5mItd7A1Lpr8Puuf87vZPWuoh7_gChQ,18483
17
- topologicpy/Helper.py,sha256=JdvC30WMrla46mTj5TdwCV_bRv-6y8vK5Bkx0prluy4,29100
17
+ topologicpy/Helper.py,sha256=9YdcJ8P6HYi-3v0lV_QNQw-PwUcl3TnBrw2QPBdNiqo,31287
18
18
  topologicpy/Honeybee.py,sha256=yctkwfdupKnp7bAOjP1Z4YaYpRrWoMEb4gz9Z5zaWwE,21751
19
19
  topologicpy/Matrix.py,sha256=BHGDRkBn1pf5DkRoY8feAhDGHTF3bjFM4jluiEb_A0w,22779
20
20
  topologicpy/Neo4j.py,sha256=vNMaqTWerwr-3luLjYEXNhf8T97aFee6x5sIKBHY73s,22392
@@ -22,17 +22,17 @@ topologicpy/Plotly.py,sha256=5E6M2N0MbdkA5iMZ_Y71PaPB9wilEneYVUba8RqeKe0,119400
22
22
  topologicpy/Polyskel.py,sha256=oVfM4lqSMPTjnkHfsRU9VI8Blt6Vf0LVPkD9ebz7Wmw,27082
23
23
  topologicpy/PyG.py,sha256=zvV6jtnol_aFiN6JRoMpYwBVfOU2aFs9gdWSdEo6mtU,109757
24
24
  topologicpy/ShapeGrammar.py,sha256=ay2kOkMvoa_KqGd_ZCLgDk0hmyMniI636N_YbMsyM60,23259
25
- topologicpy/Shell.py,sha256=h8S2nP1e0JtMxeOdAFZVhOYTJWTW8vlZRM5lxK0gu2o,89577
25
+ topologicpy/Shell.py,sha256=4zqC5m7RRQAmeIugqH098vSfBuW77L9ao5erdh8gA1Y,90585
26
26
  topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
27
27
  topologicpy/Sun.py,sha256=_VBBAUIDhvpkp72JBZlv7k9qx9jYubm3yM56UZ1Nc6c,36837
28
- topologicpy/Topology.py,sha256=SFC3YBiLDH5zoZgJ1fPyZ72W_fTSbjkMOoXPI2mexU8,457623
28
+ topologicpy/Topology.py,sha256=Bc16DUPrigdTiN_ShS6JViHRjsmR3jxChRBTe12S7Cw,466405
29
29
  topologicpy/Vector.py,sha256=mx7fgABdioikPWM9HzXKzmqfx3u_XBcU_jlLD4qK2x8,42407
30
30
  topologicpy/Vertex.py,sha256=sY9M7sMep7iTGiRYlmr8ed18Yz_RLyH6-Fm_L6SJTTw,84688
31
- topologicpy/Wire.py,sha256=5k6KflNQv2HGI1ncWLywwjqdNzJGJT2IsPbU7qnfM2Q,231723
31
+ topologicpy/Wire.py,sha256=lxlooqXjSWntwlg_h6AepNpOw_JPDIUpQKkrvvNYERk,231719
32
32
  topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
33
- topologicpy/version.py,sha256=n8OQUN0fcpz2N9lYYXEoI8M_7OTKzN9D-4I2JYQIW6s,23
34
- topologicpy-0.8.33.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
35
- topologicpy-0.8.33.dist-info/METADATA,sha256=UeGTrzifYBHgIosB2QHKADZQ3Kd8PmoG4ZRUAgWhp6A,10535
36
- topologicpy-0.8.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- topologicpy-0.8.33.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
38
- topologicpy-0.8.33.dist-info/RECORD,,
33
+ topologicpy/version.py,sha256=3ZOp-2mi5vS_FqQuNhjU7Os4xS6SwCO3uIAj_kgDx28,23
34
+ topologicpy-0.8.35.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
35
+ topologicpy-0.8.35.dist-info/METADATA,sha256=vRbd2Mu3HprTBjKk-chx8zZXvRdPIm7x0I_oPvNhdaA,10535
36
+ topologicpy-0.8.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ topologicpy-0.8.35.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
38
+ topologicpy-0.8.35.dist-info/RECORD,,