topologicpy 0.7.71__py3-none-any.whl → 0.7.72__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/BVH.py CHANGED
@@ -41,6 +41,8 @@ class BVH:
41
41
 
42
42
  def intersects(self, other):
43
43
  # Check if this AABB intersects with another AABB
44
+ if other == None:
45
+ return False
44
46
  return np.all(self.min_point <= other.max_point) and np.all(self.max_point >= other.min_point)
45
47
 
46
48
  def contains(self, point):
@@ -200,13 +202,13 @@ class BVH:
200
202
  cluster = Cluster.ByTopologies(vertices)
201
203
  bb = Topology.BoundingBox(cluster)
202
204
  d = Topology.Dictionary(bb)
203
- min_x = Dictionary.ValueAtKey(d, "minx")
204
- min_y = Dictionary.ValueAtKey(d, "miny")
205
- min_z = Dictionary.ValueAtKey(d, "minz")
206
- max_x = Dictionary.ValueAtKey(d, "maxx")
207
- max_y = Dictionary.ValueAtKey(d, "maxy")
208
- max_z = Dictionary.ValueAtKey(d, "maxz")
209
- query_aabb = BVH.AABB(min_point=(min_x, min_y, min_z), max_point=(max_x,max_y,max_z))
205
+ x_min = Dictionary.ValueAtKey(d, "xmin")
206
+ y_min = Dictionary.ValueAtKey(d, "ymin")
207
+ z_min = Dictionary.ValueAtKey(d, "zmin")
208
+ x_max = Dictionary.ValueAtKey(d, "zmax")
209
+ y_max = Dictionary.ValueAtKey(d, "ymax")
210
+ z_max = Dictionary.ValueAtKey(d, "zmax")
211
+ query_aabb = BVH.AABB(min_point=(x_min, y_min, z_min), max_point=(x_max, y_max, z_max))
210
212
  return query_aabb
211
213
 
212
214
  def Clashes(bvh, query):
@@ -103,6 +103,8 @@ class CellComplex():
103
103
  If set to True, any dictionaries in the cells are transferred to the CellComplex. Otherwise, they are not. The default is False.
104
104
  tolerance : float , optional
105
105
  The desired tolerance. The default is 0.0001.
106
+ silent : bool , optional
107
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
106
108
 
107
109
  Returns
108
110
  -------
@@ -192,7 +194,7 @@ class CellComplex():
192
194
  return CellComplex.ByCells(cells, tolerance)
193
195
 
194
196
  @staticmethod
195
- def ByFaces(faces: list, tolerance: float = 0.0001):
197
+ def ByFaces(faces: list, tolerance: float = 0.0001, silent: bool = False):
196
198
  """
197
199
  Creates a cellcomplex by merging the input faces.
198
200
 
@@ -202,6 +204,8 @@ class CellComplex():
202
204
  The input faces.
203
205
  tolerance : float , optional
204
206
  The desired tolerance. The default is 0.0001.
207
+ silent : bool , optional
208
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
205
209
 
206
210
  Returns
207
211
  -------
@@ -213,38 +217,45 @@ class CellComplex():
213
217
  from topologicpy.Topology import Topology
214
218
 
215
219
  if not isinstance(faces, list):
216
- print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
220
+ if not silent:
221
+ print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
217
222
  return None
218
223
  faces = [x for x in faces if Topology.IsInstance(x, "Face")]
219
224
  if len(faces) < 1:
220
- print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
225
+ if not silent:
226
+ print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
221
227
  return None
222
228
  try:
223
229
  cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False) # Hook to Core
224
230
  except:
225
231
  cellComplex = None
226
232
  if not cellComplex:
227
- print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
233
+ if not silent:
234
+ print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
228
235
  cellComplex = faces[0]
229
236
  for i in range(1,len(faces)):
230
237
  newCellComplex = None
231
238
  try:
232
239
  newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
233
240
  except:
234
- print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
241
+ if not silent:
242
+ print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
235
243
  if newCellComplex:
236
244
  cellComplex = newCellComplex
237
245
  if not Topology.Type(cellComplex) == Topology.TypeID("CellComplex"):
238
- print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
246
+ if not silent:
247
+ print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
239
248
  if Topology.Type(cellComplex) == Topology.TypeID("Cluster"):
240
249
  returnCellComplexes = Cluster.CellComplexes(cellComplex)
241
250
  if len(returnCellComplexes) > 0:
242
251
  return returnCellComplexes[0]
243
252
  else:
244
- print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
253
+ if not silent:
254
+ print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
245
255
  return None
246
256
  else:
247
- print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
257
+ if not silent:
258
+ print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
248
259
  return None
249
260
  else:
250
261
  return cellComplex
@@ -892,33 +903,33 @@ class CellComplex():
892
903
  x.append(Vertex.X(aVertex, mantissa=mantissa))
893
904
  y.append(Vertex.Y(aVertex, mantissa=mantissa))
894
905
  z.append(Vertex.Z(aVertex, mantissa=mantissa))
895
- minX = min(x)
896
- minY = min(y)
897
- minZ = min(z)
906
+ x_min = min(x)
907
+ y_min = min(y)
908
+ z_min = min(z)
898
909
  maxX = max(x)
899
910
  maxY = max(y)
900
911
  maxZ = max(z)
901
- return [minX, minY, minZ, maxX, maxY, maxZ]
912
+ return [x_min, y_min, z_min, maxX, maxY, maxZ]
902
913
 
903
914
  def slice(topology, uSides, vSides, wSides):
904
- minX, minY, minZ, maxX, maxY, maxZ = bb(topology)
905
- centroid = Vertex.ByCoordinates(minX+(maxX-minX)*0.5, minY+(maxY-minY)*0.5, minZ+(maxZ-minZ)*0.5)
906
- wOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), Vertex.Y(centroid, mantissa=mantissa), minZ)
907
- wFace = Face.Rectangle(origin=wOrigin, width=(maxX-minX)*1.1, length=(maxY-minY)*1.1)
915
+ x_min, y_min, z_min, maxX, maxY, maxZ = bb(topology)
916
+ centroid = Vertex.ByCoordinates(x_min+(maxX-x_min)*0.5, y_min+(maxY-y_min)*0.5, z_min+(maxZ-z_min)*0.5)
917
+ wOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), Vertex.Y(centroid, mantissa=mantissa), z_min)
918
+ wFace = Face.Rectangle(origin=wOrigin, width=(maxX-x_min)*1.1, length=(maxY-y_min)*1.1)
908
919
  wFaces = []
909
- wOffset = (maxZ-minZ)/wSides
920
+ wOffset = (maxZ-z_min)/wSides
910
921
  for i in range(wSides-1):
911
922
  wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
912
- uOrigin = Vertex.ByCoordinates(minX, Vertex.Y(centroid, mantissa=mantissa), Vertex.Z(centroid, mantissa=mantissa))
913
- uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-minZ)*1.1, length=(maxY-minY)*1.1, direction=[1,0,0])
923
+ uOrigin = Vertex.ByCoordinates(x_min, Vertex.Y(centroid, mantissa=mantissa), Vertex.Z(centroid, mantissa=mantissa))
924
+ uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-z_min)*1.1, length=(maxY-y_min)*1.1, direction=[1,0,0])
914
925
  uFaces = []
915
- uOffset = (maxX-minX)/uSides
926
+ uOffset = (maxX-x_min)/uSides
916
927
  for i in range(uSides-1):
917
928
  uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
918
- vOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), minY, Vertex.Z(centroid, mantissa=mantissa))
919
- vFace = Face.Rectangle(origin=vOrigin, width=(maxX-minX)*1.1, length=(maxZ-minZ)*1.1, direction=[0,1,0])
929
+ vOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), y_min, Vertex.Z(centroid, mantissa=mantissa))
930
+ vFace = Face.Rectangle(origin=vOrigin, width=(maxX-x_min)*1.1, length=(maxZ-z_min)*1.1, direction=[0,1,0])
920
931
  vFaces = []
921
- vOffset = (maxY-minY)/vSides
932
+ vOffset = (maxY-y_min)/vSides
922
933
  for i in range(vSides-1):
923
934
  vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
924
935
  all_faces = uFaces+vFaces+wFaces
topologicpy/Cluster.py CHANGED
@@ -166,7 +166,7 @@ class Cluster():
166
166
  from topologicpy.Topology import Topology
167
167
  from topologicpy.Helper import Helper
168
168
  import inspect
169
-
169
+
170
170
  if len(args) == 0:
171
171
  if not silent:
172
172
  print("Cluster.ByTopologies - Error: The input topologies parameter is an empty list. Returning None.")
topologicpy/Color.py CHANGED
@@ -18,6 +18,44 @@ import plotly.colors
18
18
  import math
19
19
 
20
20
  class Color:
21
+
22
+ @staticmethod
23
+ def AnyToHex(color):
24
+ """
25
+ Converts a color to a hexadecimal color string.
26
+
27
+ Parameters
28
+ ----------
29
+ color : list or str
30
+ The input color parameter which can be any of RGB, CMYK, CSS Named Color, or Hex
31
+
32
+ Returns
33
+ -------
34
+ str
35
+ A hexadecimal color string in the format '#RRGGBB'.
36
+ """
37
+ return_hex = None
38
+ if isinstance(color, list):
39
+ if len(color) == 4: # Probably CMYK
40
+ if all(0 <= x <= 1 for x in color[:4]):
41
+ return_hex = Color.CMYKToHex(color[:4])
42
+ elif len(color) == 3:
43
+ if all(0 <= x <= 255 for x in color[:3]):
44
+ return_hex = Color.RGBToHex(color[:3])
45
+ elif isinstance(color, str): # Probably a CSSColor
46
+ if color.lower() in [x.lower() for x in Color.CSSNamedColors()]:
47
+ rgb = Color.ByCSSNamedColor(color.lower())
48
+ return_hex = Color.RGBToHex(rgb)
49
+ else: # Probably alread a HEX or other Plotly-compatible string
50
+ return_hex = color
51
+
52
+ if not isinstance(return_hex, str):
53
+ print("Color.AnyToHex - Error: Could not recognize the input parameter. Returning None.")
54
+ return None
55
+
56
+ return return_hex.upper()
57
+
58
+
21
59
  @staticmethod
22
60
  def ByCSSNamedColor(color, alpha: float = None):
23
61
  """
@@ -262,6 +300,34 @@ class Color:
262
300
  rgbList.append(alpha)
263
301
  return rgbList
264
302
 
303
+ @staticmethod
304
+ def CMYKToHex(cmyk):
305
+ """
306
+ Convert a CMYK color (list of 4 values) to its hexadecimal representation.
307
+
308
+ Parameters
309
+ ----------
310
+ color : list
311
+ cmyk (list or tuple): CMYK color values as [C, M, Y, K], each in the range 0 to 1.
312
+
313
+ Returns
314
+ -------
315
+ str: The hexadecimal color string for Plotly (e.g., '#FFFFFF').
316
+ """
317
+ c, m, y, k = cmyk
318
+
319
+ # Convert CMYK to RGB
320
+ r = 255 * (1 - c) * (1 - k)
321
+ g = 255 * (1 - m) * (1 - k)
322
+ b = 255 * (1 - y) * (1 - k)
323
+
324
+ # Clamp RGB values to 0-255 range and convert to integers
325
+ r, g, b = int(round(r)), int(round(g)), int(round(b))
326
+
327
+ # Convert RGB to hex format
328
+ hex_color = "#{:02x}{:02x}{:02x}".format(r, g, b)
329
+ return hex_color
330
+
265
331
  @staticmethod
266
332
  def CSSNamedColor(color):
267
333
  """
@@ -355,7 +421,7 @@ class Color:
355
421
  "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
356
422
  "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
357
423
  "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
358
- "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple",
424
+ "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple",
359
425
  "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
360
426
  "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
361
427
  "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
topologicpy/Dictionary.py CHANGED
@@ -344,6 +344,97 @@ class Dictionary():
344
344
  values.append(pythonDictionary[key])
345
345
  return Dictionary.ByKeysValues(keys, values)
346
346
 
347
+ @staticmethod
348
+ def Filter(elements, dictionaries, searchType="any", key=None, value=None):
349
+ """
350
+ Filters the input list of dictionaries based on the input parameters.
351
+
352
+ Parameters
353
+ ----------
354
+ elements : list
355
+ The input list of elements to be filtered according to the input dictionaries.
356
+ dictionaries : list
357
+ The input list of dictionaries to be filtered.
358
+ searchType : str , optional
359
+ The type of search query to conduct in the topology's dictionary. This can be one of "any", "equal to", "contains", "starts with", "ends with", "not equal to", "does not contain". The default is "any".
360
+ key : str , optional
361
+ The dictionary key to search within. The default is None which means it will filter by topology type only.
362
+ value : str , optional
363
+ The value to search for at the specified key. The default is None which means it will filter by topology type only.
364
+
365
+ Returns
366
+ -------
367
+ dict
368
+ A dictionary of filtered and other elements. The dictionary has two keys:
369
+ - "filtered" The filtered dictionaries.
370
+ - "other" The other dictionaries that did not meet the filter criteria.
371
+ - "filteredIndices" The filtered indices of dictionaries.
372
+ - "otherIndices" The other indices of dictionaries that did not meet the filter criteria.
373
+
374
+ """
375
+
376
+ from topologicpy.Topology import Topology
377
+
378
+ def listToString(item):
379
+ returnString = ""
380
+ if isinstance(item, list):
381
+ if len(item) < 2:
382
+ return str(item[0])
383
+ else:
384
+ returnString = item[0]
385
+ for i in range(1, len(item)):
386
+ returnString = returnString+str(item[i])
387
+ return returnString
388
+
389
+ filteredDictionaries = []
390
+ otherDictionaries = []
391
+ filteredElements = []
392
+ otherElements = []
393
+ filteredIndices = []
394
+ otherIndices = []
395
+ for i, aDictionary in enumerate(dictionaries):
396
+ if not Topology.IsInstance(aDictionary, "Dictionary"):
397
+ continue
398
+ if value == "" or key == "" or value == None or key == None:
399
+ filteredDictionaries.append(aDictionary)
400
+ filteredIndices.append(i)
401
+ else:
402
+ if isinstance(value, list):
403
+ value.sort()
404
+ value = str(value)
405
+ value.replace("*",".+")
406
+ value = value.lower()
407
+ v = Dictionary.ValueAtKey(aDictionary, key)
408
+ if v == None:
409
+ otherDictionaries.append(aDictionary)
410
+ otherIndices.append(i)
411
+ otherElements.append(elements[i])
412
+ else:
413
+ v = str(v).lower()
414
+ if searchType.lower() == "equal to":
415
+ searchResult = (value == v)
416
+ elif searchType.lower() == "contains":
417
+ searchResult = (value in v)
418
+ elif searchType.lower() == "starts with":
419
+ searchResult = (value == v[0: len(value)])
420
+ elif searchType.lower() == "ends with":
421
+ searchResult = (value == v[len(v)-len(value):len(v)])
422
+ elif searchType.lower() == "not equal to":
423
+ searchResult = not (value == v)
424
+ elif searchType.lower() == "does not contain":
425
+ searchResult = not (value in v)
426
+ else:
427
+ searchResult = False
428
+ if searchResult == True:
429
+ filteredDictionaries.append(aDictionary)
430
+ filteredIndices.append(i)
431
+ filteredElements.append(elements[i])
432
+ else:
433
+ otherDictionaries.append(aDictionary)
434
+ otherIndices.append(i)
435
+ otherElements.append(elements[i])
436
+ return {"filteredDictionaries": filteredDictionaries, "otherDictionaries": otherDictionaries, "filteredIndices": filteredIndices, "otherIndices": otherIndices, "filteredElements": filteredElements, "otherElements": otherElements}
437
+
347
438
  @staticmethod
348
439
  def Keys(dictionary):
349
440
  """
@@ -579,7 +670,7 @@ class Dictionary():
579
670
  temp_value = attr.StringValue()
580
671
  topologies = None
581
672
  try:
582
- topologies = Topology.ByJSONString(temp_value, progressBar=False)
673
+ topologies = Topology.ByJSONString(temp_value)
583
674
  except:
584
675
  topologies = None
585
676
  if isinstance(topologies, list):
@@ -654,14 +745,21 @@ class Dictionary():
654
745
  if not silent == True:
655
746
  print("Dictionary.ValueAtKey - Error: The input key parameter is not a valid str. Returning None.")
656
747
  return None
657
- if isinstance(dictionary, dict):
658
- attr = dictionary[key]
659
- elif Topology.IsInstance(dictionary, "Dictionary"):
660
- attr = dictionary.ValueAtKey(key)
661
- else:
662
- return None
663
- return_value = Dictionary._ConvertAttribute(attr)
664
- return return_value
748
+ if Topology.IsInstance(dictionary, "Dictionary"):
749
+ dic = Dictionary.PythonDictionary(dictionary)
750
+ return dic.get(key, None)
751
+ elif isinstance(dictionary, dict):
752
+ return dictionary.get(key, None)
753
+ return None
754
+
755
+ # if isinstance(dictionary, dict):
756
+ # attr = dictionary[key]
757
+ # elif Topology.IsInstance(dictionary, "Dictionary"):
758
+ # attr = dictionary.ValueAtKey(key)
759
+ # else:
760
+ # return None
761
+ # return_value = Dictionary._ConvertAttribute(attr)
762
+ # return return_value
665
763
 
666
764
  @staticmethod
667
765
  def Values(dictionary):
@@ -693,7 +791,7 @@ class Dictionary():
693
791
  for key in keys:
694
792
  try:
695
793
  if isinstance(dictionary, dict):
696
- attr = dictionary[key]
794
+ attr = dictionary.get(key, None)
697
795
  elif Topology.IsInstance(dictionary, "Dictionary"):
698
796
  attr = Dictionary.ValueAtKey(dictionary,key)
699
797
  else:
topologicpy/Edge.py CHANGED
@@ -1143,6 +1143,8 @@ class Edge():
1143
1143
  from topologicpy.Vertex import Vertex
1144
1144
  from topologicpy.Topology import Topology
1145
1145
 
1146
+
1147
+
1146
1148
  def calculate_normal(start_vertex, end_vertex):
1147
1149
  start_vertex = np.array([float(x) for x in start_vertex])
1148
1150
  end_vertex = np.array([float(x) for x in end_vertex])
@@ -1150,8 +1152,11 @@ class Edge():
1150
1152
  # Calculate the direction vector of the edge
1151
1153
  direction_vector = end_vertex - start_vertex
1152
1154
 
1153
- # Handle the horizontal edge case (no Z component)
1154
- if np.isclose(direction_vector[2], 0):
1155
+ # Check if the edge is vertical (only Z component)
1156
+ if np.isclose(direction_vector[0], 0) and np.isclose(direction_vector[1], 0):
1157
+ # Choose an arbitrary perpendicular vector in the X-Y plane, e.g., [1, 0, 0]
1158
+ normal_vector = np.array([1.0, 0.0, 0.0])
1159
+ elif np.isclose(direction_vector[2], 0):
1155
1160
  # The edge lies in the X-Y plane; compute a perpendicular in the X-Y plane
1156
1161
  normal_vector = np.array([-direction_vector[1], direction_vector[0], 0.0])
1157
1162
  else:
@@ -1162,7 +1167,7 @@ class Edge():
1162
1167
  # Check if the normal vector is effectively zero before normalization
1163
1168
  if np.isclose(norm(normal_vector), 0):
1164
1169
  return normal_vector
1165
-
1170
+
1166
1171
  # Normalize the normal vector
1167
1172
  normal_vector /= norm(normal_vector)
1168
1173
  return normal_vector
@@ -1192,7 +1197,6 @@ class Edge():
1192
1197
 
1193
1198
  # Calculate the normal line
1194
1199
  normal_line_start, normal_line_end = calculate_normal_line(start_vertex, end_vertex)
1195
-
1196
1200
  # Create the normal edge in Topologic
1197
1201
  sv = Vertex.ByCoordinates(list(normal_line_start))
1198
1202
  ev = Vertex.ByCoordinates(list(normal_line_end))
topologicpy/Face.py CHANGED
@@ -2566,7 +2566,7 @@ class Face():
2566
2566
  internal_face_number = len(face_internal_boundaries)
2567
2567
  for i in range(internal_face_number):
2568
2568
  face_internal_boundary = face_internal_boundaries[i]
2569
- internal_vertices = Wire.Vertices(face_internal_boundary)
2569
+ internal_vertices = Topology.Vertices(face_internal_boundary)
2570
2570
  internal_vertex_number = len(internal_vertices)
2571
2571
  for j in range(internal_vertex_number):
2572
2572
  gmsh.model.geo.addPoint(Vertex.X(internal_vertices[j]), Vertex.Y(internal_vertices[j], mantissa=mantissa), Vertex.Z(internal_vertices[j], mantissa=mantissa), meshSize, current_vertex_number+j+1)
topologicpy/Graph.py CHANGED
@@ -3971,7 +3971,7 @@ class Graph:
3971
3971
  d = Graph.TopologicalDistance(graph, va, vb, tolerance)
3972
3972
  top_dist += d
3973
3973
  if top_dist == 0:
3974
- print("Topological Distance is Zero!!")
3974
+ print("Graph.ClosenessCentrality - Warning: Topological Distance is Zero.")
3975
3975
  scores.append(0)
3976
3976
  else:
3977
3977
  scores.append(round((n-1)/top_dist, mantissa))
@@ -7847,20 +7847,24 @@ class Graph:
7847
7847
  sides = 8,
7848
7848
  angle = 0,
7849
7849
  vertexColor="black",
7850
+ vertexColorKey=None,
7850
7851
  vertexSize=6,
7852
+ vertexSizeKey=None,
7851
7853
  vertexLabelKey=None,
7852
7854
  vertexGroupKey=None,
7853
7855
  vertexGroups=[],
7854
7856
  showVertices=True,
7855
- showVertexLabels=False,
7857
+ showVertexLabel=False,
7856
7858
  showVertexLegend=False,
7857
7859
  edgeColor="black",
7860
+ edgeColorKey=None,
7858
7861
  edgeWidth=1,
7862
+ edgeWidthKey=None,
7859
7863
  edgeLabelKey=None,
7860
7864
  edgeGroupKey=None,
7861
7865
  edgeGroups=[],
7862
7866
  showEdges=True,
7863
- showEdgeLabels=False,
7867
+ showEdgeLabel=False,
7864
7868
  showEdgeLegend=False,
7865
7869
  colorScale='viridis',
7866
7870
  renderer=None,
@@ -7904,8 +7908,12 @@ class Graph:
7904
7908
  - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
7905
7909
  - A named CSS color.
7906
7910
  The default is "black".
7911
+ vertexColorKey : str , optional
7912
+ The dictionary key under which to find the vertex color. The default is None.
7907
7913
  vertexSize : float , optional
7908
7914
  The desired size of the vertices. The default is 1.1.
7915
+ vertexSizeKey : str , optional
7916
+ The dictionary key under which to find the vertex size. The default is None.
7909
7917
  vertexLabelKey : str , optional
7910
7918
  The dictionary key to use to display the vertex label. The default is None.
7911
7919
  vertexGroupKey : str , optional
@@ -7914,7 +7922,7 @@ class Graph:
7914
7922
  The list of vertex groups against which to index the color of the vertex. The default is [].
7915
7923
  showVertices : bool , optional
7916
7924
  If set to True the vertices will be drawn. Otherwise, they will not be drawn. The default is True.
7917
- showVertexLabels : bool , optional
7925
+ showVertexLabel : bool , optional
7918
7926
  If set to True, the vertex labels are shown permenantely on screen. Otherwise, they are not. The default is False.
7919
7927
  showVertexLegend : bool , optional
7920
7928
  If set to True the vertex legend will be drawn. Otherwise, it will not be drawn. The default is False.
@@ -7926,8 +7934,12 @@ class Graph:
7926
7934
  - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
7927
7935
  - A named CSS color.
7928
7936
  The default is "black".
7937
+ edgeColorKey : str , optional
7938
+ The dictionary key under which to find the edge color. The default is None.
7929
7939
  edgeWidth : float , optional
7930
7940
  The desired thickness of the output edges. The default is 1.
7941
+ edgeWidthKey : str , optional
7942
+ The dictionary key under which to find the edge width. The default is None.
7931
7943
  edgeLabelKey : str , optional
7932
7944
  The dictionary key to use to display the edge label. The default is None.
7933
7945
  edgeGroupKey : str , optional
@@ -7936,7 +7948,7 @@ class Graph:
7936
7948
  The list of edge groups against which to index the color of the edge. The default is [].
7937
7949
  showEdges : bool , optional
7938
7950
  If set to True the edges will be drawn. Otherwise, they will not be drawn. The default is True.
7939
- showEdgeLabels : bool , optional
7951
+ showEdgeLabel : bool , optional
7940
7952
  If set to True, the edge labels are shown permenantely on screen. Otherwise, they are not. The default is False.
7941
7953
  showEdgeLegend : bool , optional
7942
7954
  If set to True the edge legend will be drawn. Otherwise, it will not be drawn. The default is False.
@@ -7997,7 +8009,7 @@ class Graph:
7997
8009
  print("Graph.Show - Error: The input graph is not a valid graph. Returning None.")
7998
8010
  return None
7999
8011
 
8000
- data= Plotly.DataByGraph(graph, sagitta=sagitta, absolute=absolute, sides=sides, angle=angle, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLabels=showVertexLabels, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLabels=showEdgeLabels, showEdgeLegend=showEdgeLegend, colorScale=colorScale, silent=silent)
8012
+ data= Plotly.DataByGraph(graph, sagitta=sagitta, absolute=absolute, sides=sides, angle=angle, vertexColor=vertexColor, vertexColorKey=vertexColorKey, vertexSize=vertexSize, vertexSizeKey=vertexSizeKey, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLabel=showVertexLabel, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeColorKey=edgeColorKey, edgeWidth=edgeWidth, edgeWidthKey=edgeWidthKey, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLabel=showEdgeLabel, showEdgeLegend=showEdgeLegend, colorScale=colorScale, silent=silent)
8001
8013
  fig = Plotly.FigureByData(data, width=width, height=height, xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize, backgroundColor=backgroundColor,
8002
8014
  marginLeft=marginLeft, marginRight=marginRight, marginTop=marginTop, marginBottom=marginBottom, tolerance=tolerance)
8003
8015
  Plotly.Show(fig, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
topologicpy/Helper.py CHANGED
@@ -95,6 +95,117 @@ class Helper:
95
95
 
96
96
  return closest_index
97
97
 
98
+ @staticmethod
99
+ def ClusterByKeys(elements, dictionaries, *keys, silent=False):
100
+ """
101
+ Clusters the input list of elements and dictionaries based on the input key or keys.
102
+
103
+ Parameters
104
+ ----------
105
+ elements : list
106
+ The input list of elements to be clustered.
107
+ dictionaries : list[Topology.Dictionary]
108
+ The input list of dictionaries to be consulted for clustering. This is assumed to be in the same order as the list of elements.
109
+ keys : str or list or comma-separated str input parameters
110
+ The key or keys in the topology's dictionary to use for clustering.
111
+ silent : bool , optional
112
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
113
+
114
+
115
+ Returns
116
+ -------
117
+ dict
118
+ A dictionary containing the elements and the dictionaries, but clustered. The dictionary has two keys:
119
+ "elements": list
120
+ A nested list of elements where each item is a list of elements with the same key values.
121
+ "dictionaries": list
122
+ A nested list of dictionaries where each item is a list of dictionaries with the same key values.
123
+ """
124
+
125
+ from topologicpy.Dictionary import Dictionary
126
+ from topologicpy.Helper import Helper
127
+ import inspect
128
+
129
+ keys_list = list(keys)
130
+ keys_list = Helper.Flatten(keys_list)
131
+ keys_list = [x for x in keys_list if isinstance(x, str)]
132
+
133
+ if len(keys_list) == 0:
134
+ if not silent:
135
+ print("Helper.ClusterByKeys - Error: The input keys parameter is an empty list. Returning None.")
136
+ curframe = inspect.currentframe()
137
+ calframe = inspect.getouterframes(curframe, 2)
138
+ print('caller name:', calframe[1][3])
139
+ return None
140
+
141
+ if len(keys_list) == 0:
142
+ if not silent:
143
+ print("Helper.ClusterByKeys - Error: The input keys parameter does not contain any valid strings. Returning None.")
144
+ curframe = inspect.currentframe()
145
+ calframe = inspect.getouterframes(curframe, 2)
146
+ print('caller name:', calframe[1][3])
147
+ return None
148
+ if not (len(elements) == len(dictionaries)):
149
+ if not silent:
150
+ print("Helper.ClusterByKeys - Error: The input elements parameter does not have the same length as the input dictionaries parameter. Returning None.")
151
+ return None
152
+
153
+ elements_clusters = []
154
+ dict_clusters = []
155
+ values = []
156
+ new_dictionaries = []
157
+ for i, d in enumerate(dictionaries):
158
+ element = elements[i]
159
+
160
+ d_keys = Dictionary.Keys(d)
161
+ if len(d_keys) > 0:
162
+ values_list = []
163
+ for key in keys_list:
164
+ v = Dictionary.ValueAtKey(d, key)
165
+ if not v == None:
166
+ values_list.append(v)
167
+ values_list = str(values_list)
168
+ d = Dictionary.SetValueAtKey(d, "_clustering_key_", values_list)
169
+ new_dictionaries.append(d)
170
+ values.append(values_list)
171
+
172
+ values = list(set(values))
173
+ remaining_dictionaries = [x for x in new_dictionaries]
174
+ remaining_elements = [x for x in elements]
175
+ remaining_indices = [i for i, x in enumerate(elements)]
176
+
177
+ if len(values) == 0:
178
+ return {"elements": [elements], "dictionaries": [dictionaries]}
179
+ for value in values:
180
+ if len(remaining_dictionaries) == 0:
181
+ break
182
+ dict = Dictionary.Filter(remaining_elements, remaining_dictionaries, searchType="equal to", key="_clustering_key_", value=value)
183
+ filtered_indices = dict['filteredIndices']
184
+ final_dictionaries = []
185
+ final_elements = []
186
+ if len(filtered_indices) > 0:
187
+ for filtered_index in filtered_indices:
188
+ filtered_dictionary = remaining_dictionaries[filtered_index]
189
+ filtered_dictionary = Dictionary.RemoveKey(filtered_dictionary, "_clustering_key_")
190
+ final_dictionaries.append(filtered_dictionary)
191
+ filtered_element = remaining_elements[filtered_index]
192
+ final_elements.append(filtered_element)
193
+ dict_clusters.append(final_dictionaries)
194
+ elements_clusters.append(final_elements)
195
+ remaining_dictionaries = dict['otherDictionaries']
196
+ remaining_elements = dict['otherElements']
197
+ remaining_indices = dict['otherIndices']
198
+ if len(remaining_elements) > 0:
199
+ temp_dict_cluster = []
200
+ temp_element_cluster = []
201
+ for remaining_index in remaining_indices:
202
+ temp_element_cluster.append(remaining_elements[remaining_index])
203
+ temp_dict_cluster.append(remaining_dictionaries[remaining_index])
204
+ if len(temp_element_cluster) > 0:
205
+ dict_clusters.append(temp_dict_cluster)
206
+ elements_clusters.append(temp_element_cluster)
207
+ return {"elements": elements_clusters, "dictionaries": dict_clusters}
208
+
98
209
  @staticmethod
99
210
  def Flatten(listA):
100
211
  """