topologicpy 0.8.14__py3-none-any.whl → 0.8.17__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/Topology.py CHANGED
@@ -1498,6 +1498,8 @@ class Topology():
1498
1498
  edges = [e for e in edges if e]
1499
1499
  faces = [f for f in faces if f]
1500
1500
 
1501
+ return_topology = None
1502
+
1501
1503
  if not vertices:
1502
1504
  return None
1503
1505
 
@@ -1527,7 +1529,7 @@ class Topology():
1527
1529
  if topologyType == "wire" and edges:
1528
1530
  topEdges = [Edge.ByVertices([topVerts[e[0]], topVerts[e[1]]], tolerance=tolerance) for e in edges]
1529
1531
  if topEdges:
1530
- returnTopology = topologyByEdges(topEdges, topologyType)
1532
+ return_topology = topologyByEdges(topEdges, topologyType)
1531
1533
  elif faces:
1532
1534
  for aFace in faces:
1533
1535
  faceEdges = [Edge.ByVertices([topVerts[aFace[i]], topVerts[aFace[i + 1]]], tolerance=tolerance) for i in range(len(aFace) - 1)]
@@ -1545,14 +1547,14 @@ class Topology():
1545
1547
  except:
1546
1548
  pass
1547
1549
  if topFaces:
1548
- returnTopology = topologyByFaces(topFaces, topologyType=topologyType, tolerance=tolerance)
1550
+ return_topology = topologyByFaces(topFaces, topologyType=topologyType, tolerance=tolerance)
1549
1551
  elif edges:
1550
1552
  topEdges = [Edge.ByVertices([topVerts[e[0]], topVerts[e[1]]], tolerance=tolerance) for e in edges]
1551
1553
  if topEdges:
1552
- returnTopology = topologyByEdges(topEdges, topologyType)
1554
+ return_topology = topologyByEdges(topEdges, topologyType)
1553
1555
  else:
1554
- returnTopology = Cluster.ByTopologies(topVerts)
1555
- return returnTopology
1556
+ return_topology = Cluster.ByTopologies(topVerts)
1557
+ return return_topology
1556
1558
 
1557
1559
  @staticmethod
1558
1560
  def ByBIMPath(path, guidKey: str = "guid", colorKey: str = "color", typeKey: str = "type",
@@ -3073,6 +3075,118 @@ class Topology():
3073
3075
  json_dict = json.loads(string)
3074
3076
  return Topology.ByJSONDictionary(json_dict, tolerance=tolerance)
3075
3077
 
3078
+ @staticmethod
3079
+ def ByMeshData(dictionary, transferDictionaries: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
3080
+ """
3081
+ Create a cluster by the input python dictionary of vertices, edges, faces, and cells.
3082
+
3083
+ Parameters
3084
+ ----------
3085
+ dictionary : dict
3086
+ The input python dictionary of vertices, edges, faces, and cells in the form of:
3087
+ { 'mode': int, The expected mode of input face data:
3088
+ 0: The faces list is indexed into the vertices list.
3089
+ 1: The faces list is indexesd into the edges list.
3090
+ 'vertices': list, (list of coordinates)
3091
+ 'edges': list, (list of indices into the list of vertices)
3092
+ 'faces': list, (list of indices into the list of edges)
3093
+ 'cells': list, (list of indices into the list of faces)
3094
+ 'vertex_dict': list of dicts,
3095
+ 'edge_dicts': list of dicts,
3096
+ 'face_dicts', list of dicts,
3097
+ 'cell_dicts', list of dicts
3098
+ }
3099
+ transferDictionaries : bool , optional
3100
+ If set to True, the python dictionaries will be transferred to the coorespoding topologies. The default is False.
3101
+ mantissa : int , optional
3102
+ The desired length of the mantissa. The default is 6.
3103
+ tolerance : float , optional
3104
+ The desired tolerance. The default is 0.0001.
3105
+ silent : bool , optional
3106
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
3107
+
3108
+ Returns
3109
+ -------
3110
+ topologic_core.Cluster
3111
+ The created cluster of vertices, edges, faces, and cells.
3112
+
3113
+ """
3114
+ from topologicpy.Vertex import Vertex
3115
+ from topologicpy.Edge import Edge
3116
+ from topologicpy.Face import Face
3117
+ from topologicpy.Cell import Cell
3118
+ from topologicpy.Cluster import Cluster
3119
+ from topologicpy.Dictionary import Dictionary
3120
+ from topologicpy.Helper import Helper
3121
+
3122
+ if not type(dictionary) == type({}):
3123
+ if not silent:
3124
+ print("Topology.ByMeshData - Error: The input dictionary parameter is not a valid dictionary. Returning None.")
3125
+ return None
3126
+
3127
+ vertices = dictionary['vertices']
3128
+ edges = dictionary['edges']
3129
+ faces = dictionary['faces']
3130
+ cells = dictionary['cells']
3131
+ if not 'mode' in dictionary.keys():
3132
+ mode = 0
3133
+ else:
3134
+ mode = dictionary['mode']
3135
+ if transferDictionaries == True:
3136
+ vertex_dicts = dictionary['vertex_dicts']
3137
+ edge_dicts = dictionary['edge_dicts']
3138
+ face_dicts = dictionary['face_dicts']
3139
+ cell_dicts = dictionary['cell_dicts']
3140
+ else:
3141
+ vertex_dicts = [{}]*len(vertices)
3142
+ edge_dicts = [{}]*len(edges)
3143
+ face_dicts = [{}]*len(faces)
3144
+ cell_dicts = [{}]*len(cells)
3145
+
3146
+ top_verts = []
3147
+ for i, vertex in enumerate(vertices):
3148
+ top_vertex = Vertex.ByCoordinates(vertex)
3149
+ if Topology.IsInstance(top_vertex, "Vertex"):
3150
+ if transferDictionaries:
3151
+ d = Dictionary.ByPythonDictionary(vertex_dicts[i])
3152
+ top_vertex = Topology.SetDictionary(top_vertex, d, silent=True)
3153
+ top_verts.append(top_vertex)
3154
+
3155
+ top_edges = []
3156
+ for i, edge in enumerate(edges):
3157
+ top_edge = Edge.ByVertices(top_verts[edge[0]], top_verts[edge[1]], tolerance=tolerance, silent=silent)
3158
+ if Topology.IsInstance(top_edge, "Edge"):
3159
+ if transferDictionaries:
3160
+ d = Dictionary.ByPythonDictionary(edge_dicts[i])
3161
+ top_edge = Topology.SetDictionary(top_edge, d, silent=True)
3162
+ top_edges.append(top_edge)
3163
+
3164
+ top_faces = []
3165
+ for i, face in enumerate(faces):
3166
+ if mode == 0:
3167
+ face_vertices = [top_verts[v] for v in face]
3168
+ top_face = Face.ByVertices(face_vertices, tolerance=tolerance, silent=silent)
3169
+ else:
3170
+ face_edges = [top_edges[e] for e in face]
3171
+ top_face = Face.ByEdges(face_edges, tolerance=tolerance, silent=silent)
3172
+ if Topology.IsInstance(top_face, "Face"):
3173
+ if transferDictionaries:
3174
+ d = Dictionary.ByPythonDictionary(face_dicts[i])
3175
+ top_face = Topology.SetDictionary(top_face, d, silent=True)
3176
+ top_faces.append(top_face)
3177
+
3178
+ top_cells = []
3179
+ for i, cell in enumerate(cells):
3180
+ cell_faces = [top_faces[f] for f in cell]
3181
+ top_cell = Cell.ByFaces(cell_faces, tolerance=tolerance, silent=silent)
3182
+ if Topology.IsInstance(top_cell, "Cell"):
3183
+ if transferDictionaries:
3184
+ d = Dictionary.ByPythonDictionary(cell_dicts[i])
3185
+ top_cell = Topology.SetDictionary(top_cell, d, silent=True)
3186
+ top_cells.append(top_cell)
3187
+
3188
+ return Cluster.ByTopologies(top_verts+top_edges+top_faces+top_cells)
3189
+
3076
3190
  @staticmethod
3077
3191
  def ByOBJFile(objFile, mtlFile = None,
3078
3192
  defaultColor: list = [255,255,255],
@@ -3301,7 +3415,7 @@ class Topology():
3301
3415
  vertex = list(map(float, parts[1:4]))
3302
3416
  vertex = [round(coord, mantissa) for coord in vertex]
3303
3417
  if transposeAxes == True:
3304
- vertex = [vertex[0], vertex[2], vertex[1]]
3418
+ vertex = [vertex[0], -vertex[2], vertex[1]]
3305
3419
  vertices.append(vertex)
3306
3420
  elif parts[0] == 'vt':
3307
3421
  texture = list(map(float, parts[1:3]))
@@ -5891,7 +6005,7 @@ class Topology():
5891
6005
  return flat_topology
5892
6006
 
5893
6007
  @staticmethod
5894
- def Geometry(topology, mantissa: int = 6):
6008
+ def Geometry(topology, transferDictionaries: bool = False, mantissa: int = 6, silent: bool = False):
5895
6009
  """
5896
6010
  Returns the geometry (mesh data format) of the input topology as a dictionary of vertices, edges, and faces.
5897
6011
 
@@ -5899,8 +6013,12 @@ class Topology():
5899
6013
  ----------
5900
6014
  topology : topologic_core.Topology
5901
6015
  The input topology.
6016
+ transferDictionaries : bool , optional
6017
+ If set to True, vertex, edge, and face dictionaries will be included in the output. Otherwise, they are not. The default is False.
5902
6018
  mantissa : int , optional
5903
6019
  The desired length of the mantissa. The default is 6.
6020
+ silent : bool , optional
6021
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
5904
6022
 
5905
6023
  Returns
5906
6024
  -------
@@ -5911,22 +6029,34 @@ class Topology():
5911
6029
  from topologicpy.Vertex import Vertex
5912
6030
  from topologicpy.Edge import Edge
5913
6031
  from topologicpy.Face import Face
6032
+ from topologicpy.Dictionary import Dictionary
5914
6033
 
5915
6034
  vertices = []
5916
6035
  edges = []
5917
6036
  faces = []
5918
- if topology == None:
5919
- return [None, None, None]
6037
+ vertex_dicts = []
6038
+ edge_dicts = []
6039
+ face_dicts = []
6040
+ if not Topology.IsInstance(topology, "topology"):
6041
+ if not silent:
6042
+ print("Topology.Geometry - Error: The input topology parameter is not a valid topology. Retruning None.")
6043
+ return None
5920
6044
  topVerts = []
5921
6045
  if Topology.Type(topology) == Topology.TypeID("Vertex"): #input is a vertex, just add it and process it
5922
6046
  topVerts.append(topology)
5923
6047
  else:
5924
6048
  topVerts = Topology.Vertices(topology)
5925
6049
  for aVertex in topVerts:
6050
+ py_dict = {}
6051
+ if transferDictionaries == True:
6052
+ d = Topology.Dictionary(aVertex)
6053
+ if len(Dictionary.Keys(d)) > 0:
6054
+ py_dict = Dictionary.PythonDictionary(d)
5926
6055
  try:
5927
6056
  vertices.index(Vertex.Coordinates(aVertex, mantissa=mantissa)) # Vertex already in list
5928
6057
  except:
5929
6058
  vertices.append(Vertex.Coordinates(aVertex, mantissa=mantissa)) # Vertex not in list, add it.
6059
+ vertex_dicts.append(py_dict)
5930
6060
  topEdges = []
5931
6061
  if (Topology.Type(topology) == Topology.TypeID("Edge")): #Input is an Edge, just add it and process it
5932
6062
  topEdges.append(topology)
@@ -5949,13 +6079,18 @@ class Topology():
5949
6079
  e.append(svIndex)
5950
6080
  e.append(evIndex)
5951
6081
  edges.append(e)
6082
+ py_dict = {}
6083
+ if transferDictionaries == True:
6084
+ d = Topology.Dictionary(anEdge)
6085
+ if len(Dictionary.Keys(d)) > 0:
6086
+ py_dict = Dictionary.PythonDictionary(d)
6087
+ edge_dicts.append(py_dict)
5952
6088
  topFaces = []
5953
6089
  if (Topology.Type(topology) == Topology.TypeID("Face")): # Input is a Face, just add it and process it
5954
6090
  topFaces.append(topology)
5955
6091
  elif (Topology.Type(topology) > Topology.TypeID("Face")):
5956
6092
  _ = topology.Faces(None, topFaces)
5957
6093
  for aFace in topFaces:
5958
- f_dir = Face.Normal(aFace)
5959
6094
  ib = []
5960
6095
  _ = aFace.InternalBoundaries(ib)
5961
6096
  if(len(ib) > 0):
@@ -5984,7 +6119,13 @@ class Topology():
5984
6119
  fVertexIndex = len(vertices)-1
5985
6120
  f.append(fVertexIndex)
5986
6121
  faces.append(f)
5987
- return {"vertices":vertices, "edges":edges, "faces":faces}
6122
+ py_dict = {}
6123
+ if transferDictionaries == True:
6124
+ d = Topology.Dictionary(aFace)
6125
+ if len(Dictionary.Keys(d)) > 0:
6126
+ py_dict = Dictionary.PythonDictionary(d)
6127
+ face_dicts.append(py_dict)
6128
+ return {"vertices":vertices, "edges":edges, "faces":faces, "vertex_dicts": vertex_dicts, "edge_dicts": edge_dicts, "face_dicts": face_dicts}
5988
6129
 
5989
6130
  @staticmethod
5990
6131
  def HighestType(topology):
@@ -6264,7 +6405,7 @@ class Topology():
6264
6405
  @staticmethod
6265
6406
  def IsSimilar(topologyA, topologyB, removeCoplanarFaces: bool = False, mantissa: int = 6, epsilon: float = 0.1, tolerance: float = 0.0001, silent: bool = False):
6266
6407
  """
6267
- Returns True if the input topologies are similar. False otherwise. See https://en.wikipedia.org/wiki/Similarity_(geometry).
6408
+ Calculates if the input topologies are similar. See https://en.wikipedia.org/wiki/Similarity_(geometry).
6268
6409
 
6269
6410
  Parameters
6270
6411
  ----------
@@ -6286,7 +6427,8 @@ class Topology():
6286
6427
  Returns
6287
6428
  -------
6288
6429
  [bool, list]
6289
- True if the input topologies are similar., False otherwise and the matrix needed to tranform topologyA to match topologyB
6430
+ True if the input topologies are similar, False otherwise and the matrix needed to tranform topologyA to match topologyB.
6431
+ If the topologies are not similar, the transformation matrix is None.
6290
6432
 
6291
6433
  """
6292
6434
  from topologicpy.Vertex import Vertex
@@ -6308,7 +6450,6 @@ class Topology():
6308
6450
  if removeCoplanarFaces == True:
6309
6451
  topologyA = Topology.RemoveCoplanarFaces(topologyA, epsilon=epsilon, tolerance=tolerance)
6310
6452
  topologyB = Topology.RemoveCoplanarFaces(topologyB, epsilon=epsilon, tolerance=tolerance)
6311
- Topology.Show(topologyA, topologyB)
6312
6453
  len_vertices_a = len(Topology.Vertices(topologyA))
6313
6454
  if len_vertices_a < 1 and not Topology.IsInstance(topologyA, "vertex"):
6314
6455
  if not silent:
@@ -6398,90 +6539,58 @@ class Topology():
6398
6539
  if len(faces_a) > 0 and len(faces_b) > 0:
6399
6540
  largest_faces_a = Topology.LargestFaces(topologyA)
6400
6541
  largest_faces_b = Topology.LargestFaces(topologyB)
6401
-
6542
+ else:
6543
+ if not silent:
6544
+ print("Topology.IsSimilar - Error: The topologies do not have faces. Returning None.")
6545
+ return False, None
6402
6546
 
6403
6547
  # Process largest faces
6404
- largest_faces_a = Topology.LargestFaces(topologyA)
6405
- largest_faces_b = Topology.LargestFaces(topologyB)
6406
- face_a_d = Dictionary.ByKeyValue("faceColor", "red")
6407
- face_b_d = Dictionary.ByKeyValue("faceColor", "blue")
6408
6548
  for face_a in largest_faces_a:
6549
+ l_edge_a = Topology.LongestEdges(face_a)[0]
6550
+ length_a = Edge.Length(l_edge_a)
6409
6551
  centroid_a = Topology.Centroid(face_a)
6410
- normal_a = Face.Normal(face_a)
6552
+ origin_a = Vertex.Coordinates(centroid_a)
6553
+ zaxis_a = Face.Normal(face_a)
6411
6554
  third_vertex_a = Face.ThirdVertex(face_a) # Pick a third vertex for orientation
6412
- orientation_a = Vector.Normalize(Vector.ByVertices(centroid_a, third_vertex_a))
6555
+ xaxis_a = Vector.Normalize(Vector.ByVertices(centroid_a, third_vertex_a))
6556
+ yaxis_a = Vector.Cross(xaxis_a, zaxis_a)
6557
+ # Build Coordinate System matrix. The origin will be (0,0,0) once the trans matrix is applied.
6558
+ cs_a = [[0,0,0]]+[xaxis_a]+[yaxis_a]+[zaxis_a]
6559
+ tran_matrix_a = Matrix.ByTranslation(translateX=-origin_a[0], translateY=-origin_a[1], translateZ=-origin_a[2])
6413
6560
 
6561
+ # Check against the faces of B:
6414
6562
  for face_b in largest_faces_b:
6415
- face_copy_b = Topology.SetDictionary(Topology.Copy(face_b), face_b_d)
6563
+ l_edge_b = Topology.LongestEdges(face_b)[0]
6564
+ length_b = Edge.Length(l_edge_b)
6565
+ scale_factor = length_b/length_a
6566
+ scale_matrix = Matrix.ByScaling(scaleX=scale_factor, scaleY=scale_factor, scaleZ=scale_factor)
6416
6567
  centroid_b = Topology.Centroid(face_b)
6417
- normal_b = Face.Normal(face_b)
6568
+ origin_b = Vertex.Coordinates(centroid_b)
6569
+ zaxis_b = Face.Normal(face_b)
6418
6570
  third_vertex_b = Face.ThirdVertex(face_b)
6419
- orientation_b = Vector.Normalize(Vector.ByVertices(centroid_b, third_vertex_b))
6420
-
6571
+ xaxis_b = Vector.Normalize(Vector.ByVertices(centroid_b, third_vertex_b))
6572
+ yaxis_b = Vector.Cross(xaxis_b, zaxis_b)
6573
+ cs_b = [origin_b]+[xaxis_b]+[yaxis_b]+[zaxis_b]
6421
6574
  # Compute transformation matrix
6422
- rotation_matrix = Matrix.ByVectors(normal_a, normal_b, orientation_a, orientation_b)
6423
- scaling_factor = (Face.Area(face_b) / Face.Area(face_a)) ** (1/2)
6424
- scaling_matrix = Matrix.ByScaling(scaling_factor, scaling_factor, scaling_factor)
6425
-
6426
- translation_matrix = Matrix.ByTranslation(
6427
- Vertex.X(centroid_b) - Vertex.X(centroid_a),
6428
- Vertex.Y(centroid_b) - Vertex.Y(centroid_a),
6429
- Vertex.Z(centroid_b) - Vertex.Z(centroid_a)
6430
- )
6431
- combined_matrix = Matrix.Multiply(rotation_matrix, scaling_matrix)
6432
- combined_matrix = Matrix.Multiply(translation_matrix, combined_matrix)
6433
-
6434
- transformed_centroid_a = Topology.Transform(centroid_a, combined_matrix)
6435
-
6436
- # One last translation to synchronise the two centroids.
6437
- translation_matrix = Matrix.ByTranslation(
6438
- Vertex.X(centroid_b) - Vertex.X(transformed_centroid_a),
6439
- Vertex.Y(centroid_b) - Vertex.Y(transformed_centroid_a),
6440
- Vertex.Z(centroid_b) - Vertex.Z(transformed_centroid_a)
6441
- )
6442
- combined_matrix = Matrix.Multiply(translation_matrix, combined_matrix)
6575
+ # translate to origin, scale, then transform coordinate systems
6576
+ combined_matrix = Matrix.Multiply(scale_matrix, tran_matrix_a)
6577
+ matching_matrix = Matrix.ByCoordinateSystems(cs_a, cs_b)
6578
+ combined_matrix = Matrix.Multiply(matching_matrix, combined_matrix)
6443
6579
  # Apply transformation and compare
6444
- transformedA = Topology.Transform(topologyA, combined_matrix)
6445
- transformed_face_a = Topology.Transform(face_a, combined_matrix)
6446
- transformed_face_a = Topology.SetDictionary(face_a, face_a_d)
6447
-
6448
- Topology.Show(transformedA, topologyB, transformed_face_a, face_copy_b, faceColorKey="faceColor", vertexSizeKey="vertexSize")
6449
- if Topology.IsVertexCongruent(transformedA, topologyB, mantissa=mantissa, epsilon=epsilon, tolerance=tolerance, silent=silent):
6450
- return True, combined_matrix
6580
+ try:
6581
+ transformedA = Topology.Transform(topologyA, combined_matrix)
6582
+ status = Topology.IsVertexCongruent(transformedA, topologyB, mantissa=mantissa, epsilon=epsilon, tolerance=tolerance, silent=silent)
6583
+ if status:
6584
+ return True, combined_matrix
6585
+ except:
6586
+ pass
6451
6587
 
6452
6588
  return False, None
6453
- # for l_f_a in largest_faces_a:
6454
- # l_e_a = Face.NormalEdge(l_f_a)
6455
- # centroid_a = Edge.StartVertex(l_e_a)
6456
- # dir_a = Vector.Normalize(Edge.Direction(l_e_a))
6457
- # trans_matrix_a = Matrix.ByTranslation(-Vertex.X(centroid_a), -Vertex.Y(centroid_a), -Vertex.Z(centroid_a))
6458
- # sf_a = 1/Face.Area(l_f_a)
6459
- # scaling_matrix_a = Matrix.ByScaling(sf_a, sf_a, sf_a)
6460
- # for l_f_b in largest_faces_b:
6461
- # l_e_b = Face.NormalEdge(l_f_b)
6462
- # centroid_b = Edge.StartVertex(l_e_b)
6463
- # dir_b = Vector.Normalize(Edge.Direction(l_e_b))
6464
- # rotation_matrix = Matrix.ByVectors(dir_a, dir_b)
6465
- # sf_b = 1/Face.Area(l_f_b)
6466
- # scaling_matrix_b = Matrix.ByScaling(sf_b, sf_b, sf_b)
6467
- # trans_matrix_b = Matrix.ByTranslation(-Vertex.X(centroid_b), -Vertex.Y(centroid_b), -Vertex.Z(centroid_b))
6468
- # combined_matrix_a = Matrix.Multiply(rotation_matrix, scaling_matrix_a)
6469
- # combined_matrix_a = Matrix.Multiply(combined_matrix_a, trans_matrix_a)
6470
- # combined_matrix_b = Matrix.Multiply(scaling_matrix_b, trans_matrix_b)
6471
- # top_a = Topology.Transform(topologyA, combined_matrix_a)
6472
- # top_b = Topology.Transform(topologyB, combined_matrix_b)
6473
- # Topology.Show(top_a, top_b)
6474
- # if Topology.IsVertexCongruent(top_a, top_b, mantissa=mantissa, epsilon=epsilon, tolerance=tolerance, silent=silent):
6475
- # Topology.Show(top_a, top_b)
6476
- # final_matrix = Matrix.Multiply(Matrix.Invert(combined_matrix_b), combined_matrix_a)
6477
- # return True, final_matrix
6478
6589
 
6479
- return False, None
6480
-
6481
6590
  @staticmethod
6482
6591
  def IsVertexCongruent(topologyA, topologyB, mantissa: int = 6, epsilon: float = 0.1, tolerance: float = 0.0001, silent : bool = False):
6483
6592
  """
6484
- Returns True if the input topologies are vertex matched (have same number of vertices and all vertices are congruent within a tolerance). Returns False otherwise.
6593
+ Returns True if the input topologies are vertex congruent (have same number of vertices and all vertices are congruent within a tolerance). Returns False otherwise.
6485
6594
 
6486
6595
  Parameters
6487
6596
  ----------
@@ -6491,6 +6600,9 @@ class Topology():
6491
6600
  The second input topology.
6492
6601
  mantissa : int , optional
6493
6602
  The desired length of the mantissa. The default is 6.
6603
+ epsilon : float , optional
6604
+ The desired accepted tolerance for the number of matched number of vertices. For example, an epsilon of 0.1 indicates that
6605
+ the algorithm will return True even if 10% of the vertices do not match. The default is 0.1.
6494
6606
  tolerance : float , optional
6495
6607
  The desired tolerance. The default is 0.0001.
6496
6608
  silent : bool , optional
@@ -6504,46 +6616,27 @@ class Topology():
6504
6616
  """
6505
6617
  from topologicpy.Vertex import Vertex
6506
6618
 
6507
- def coordinates_unmatched_ratio(list1, list2, tolerance):
6619
+
6620
+ def coordinates_unmatched_ratio(list1, list2, mantissa, tolerance):
6508
6621
  """
6509
- Calculates the percentage of coordinates in list1 that do not have a corresponding
6510
- coordinate in list2 within a specified tolerance, with each match being unique.
6511
-
6512
- Parameters
6513
- ----------
6514
- list1 : list of list of float
6515
- The first list of coordinates, where each coordinate is [x, y, z].
6516
- list2 : list of list of float
6517
- The second list of coordinates, where each coordinate is [x, y, z].
6518
- tolerance : float
6519
- The maximum distance within which two coordinates are considered matching.
6520
-
6521
- Returns
6522
- -------
6523
- float
6524
- The percentage of coordinates in list1 that did not find a match in list2.
6622
+ Optimized version using KDTree for faster nearest-neighbor search.
6525
6623
  """
6526
- def distance(coord1, coord2):
6527
- """Calculate the Euclidean distance between two coordinates."""
6528
- return np.linalg.norm(np.array(coord1) - np.array(coord2))
6529
-
6624
+ from scipy.spatial import KDTree
6625
+ import numpy as np
6626
+ rounded_list1 = [np.round(coord, mantissa) for coord in list1]
6627
+ rounded_list2 = [np.round(coord, mantissa) for coord in list2]
6628
+
6629
+ tree = KDTree(rounded_list2)
6530
6630
  unmatched_count = 0
6531
- unmatched = list2.copy()
6532
-
6533
- for coord1 in list1:
6534
- match_found = False
6535
- for i, coord2 in enumerate(unmatched):
6536
- if distance(coord1, coord2) <= tolerance:
6537
- unmatched.pop(i) # Remove matched coordinate
6538
- match_found = True
6539
- break
6540
- if not match_found:
6631
+
6632
+ for coord in rounded_list1:
6633
+ distances, indices = tree.query(coord, k=1)
6634
+ if distances > tolerance:
6541
6635
  unmatched_count += 1
6542
-
6543
- total_coordinates = len(list1)
6544
- unmatched_ratio = (unmatched_count / total_coordinates)
6545
6636
 
6546
- return unmatched_ratio
6637
+ total_coordinates = len(list1)
6638
+ unmatched_ratio = unmatched_count / total_coordinates
6639
+ return round(unmatched_ratio, mantissa)
6547
6640
 
6548
6641
  if not Topology.IsInstance(topologyA, "topology"):
6549
6642
  if not silent:
@@ -6568,11 +6661,12 @@ class Topology():
6568
6661
  return None
6569
6662
  # Number of vertices
6570
6663
  max_len = max([len_vertices_a, len_vertices_b])
6571
- if abs(len_vertices_a - len_vertices_a)/max_len >= epsilon:
6664
+ ratio = round(float(abs(len_vertices_a - len_vertices_b))/float(max_len), mantissa)
6665
+ if ratio > epsilon:
6572
6666
  return False
6573
6667
  coords_a = [Vertex.Coordinates(v, mantissa=mantissa) for v in vertices_a]
6574
6668
  coords_b = [Vertex.Coordinates(v, mantissa=mantissa) for v in vertices_b]
6575
- unmatched_ratio = coordinates_unmatched_ratio(coords_a, coords_b, tolerance=tolerance)
6669
+ unmatched_ratio = coordinates_unmatched_ratio(coords_a, coords_b, mantissa=mantissa, tolerance=tolerance)
6576
6670
  if unmatched_ratio <= epsilon:
6577
6671
  return True
6578
6672
  return False
@@ -6706,6 +6800,161 @@ class Topology():
6706
6800
  return None
6707
6801
  return Topology.SelfMerge(Cluster.ByTopologies(topologyList), tolerance=tolerance)
6708
6802
 
6803
+ @staticmethod
6804
+ def MeshData(topology, mode: int = 1, transferDictionaries: bool = False, mantissa: int = 6, silent: bool = False):
6805
+ """
6806
+ Creates a mesh data python dictionary from the input topology.
6807
+
6808
+ Parameters
6809
+ ----------
6810
+ topology : topologic_core.Topology
6811
+ The input topology.
6812
+ mode : int , optional
6813
+ The desired mode of conversion:
6814
+ 0: The faces list indexes into the vertices list.
6815
+ 1: The faces list indexes into the edges list.
6816
+ The default is 1.
6817
+ transferDictionaries : bool , optional
6818
+ If set to True, the python dictionaries will be transferred to the coorespoding topologies. The default is False.
6819
+ mantissa : int , optional
6820
+ The desired length of the mantissa. The default is 6.
6821
+ tolerance : float , optional
6822
+ The desired tolerance. The default is 0.0001.
6823
+ silent : bool , optional
6824
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
6825
+
6826
+ Returns
6827
+ -------
6828
+ dict
6829
+ The created mesh data python dictionary of vertices, edges, faces, and cells in the form of:
6830
+ { 'mode' : int (the mode of the face data)
6831
+ 'vertices': list, (list of coordinates)
6832
+ 'edges': list, (list of indices into the list of vertices)
6833
+ 'faces': list, (list of indices into the list of edges or list of vertices based on mode)
6834
+ 'cells': list, (list of indices into the list of faces)
6835
+ 'vertex_dict': list of dicts,
6836
+ 'edge_dicts': list of dicts,
6837
+ 'face_dicts', list of dicts,
6838
+ 'cell_dicts', list of dicts
6839
+ }
6840
+
6841
+ """
6842
+ from topologicpy.Vertex import Vertex
6843
+ from topologicpy.Dictionary import Dictionary
6844
+
6845
+ vertex_dicts = []
6846
+ edge_dicts = []
6847
+ face_dicts = []
6848
+ cell_dicts = []
6849
+ top = Topology.Copy(topology)
6850
+ top = Topology.Triangulate(top, transferDictionaries=transferDictionaries)
6851
+ vertices = Topology.Vertices(top)
6852
+ edges = Topology.Edges(top)
6853
+ faces = Topology.Faces(top)
6854
+ if Topology.IsInstance(top, "Vertex"):
6855
+ vertices = [top]
6856
+ edges = []
6857
+ faces = []
6858
+ cells = []
6859
+ elif Topology.IsInstance(top, "Edge"):
6860
+ vertices = Topology.Vertices(top)
6861
+ edges = [top]
6862
+ faces = []
6863
+ cells = []
6864
+ elif Topology.IsInstance(top, "Wire"):
6865
+ vertices = Topology.Vertices(top)
6866
+ edges = Topology.Edges(top)
6867
+ faces = []
6868
+ cells = []
6869
+ elif Topology.IsInstance(top, "Face"):
6870
+ vertices = Topology.Vertices(top)
6871
+ edges = Topology.Edges(top)
6872
+ faces = [top]
6873
+ cells = []
6874
+ elif Topology.IsInstance(top, "Shell"):
6875
+ vertices = Topology.Vertices(top)
6876
+ edges = Topology.Edges(top)
6877
+ faces = Topology.Faces(top)
6878
+ cells = []
6879
+ elif Topology.IsInstance(top, "Cell"):
6880
+ vertices = Topology.Vertices(top)
6881
+ edges = Topology.Edges(top)
6882
+ faces = Topology.Faces(top)
6883
+ cells = [top]
6884
+ elif Topology.IsInstance(top, "CellComplex"):
6885
+ vertices = Topology.Vertices(top)
6886
+ edges = Topology.Edges(top)
6887
+ faces = Topology.Faces(top)
6888
+ cells = Topology.Cells(top)
6889
+ elif Topology.IsInstance(top, "Cluster"):
6890
+ vertices = Topology.Vertices(top)
6891
+ edges = Topology.Edges(top)
6892
+ faces = Topology.Faces(top)
6893
+ cells = Topology.Cells(top)
6894
+ else:
6895
+ if not silent:
6896
+ print("Topology.MeshData - Error: The input topology parameter is not a valid topology. Returning None.")
6897
+ return None
6898
+
6899
+ m_verts = []
6900
+ m_edges = []
6901
+ m_faces = []
6902
+ m_cells = []
6903
+ key = "_n_"
6904
+
6905
+ for i, v in enumerate(vertices):
6906
+ d = Topology.Dictionary(v)
6907
+ v = Topology.SetDictionary(v, Dictionary.ByKeyValue(key, i))
6908
+ m_verts.append(Vertex.Coordinates(v, mantissa=mantissa))
6909
+ vertex_dicts.append(Dictionary.PythonDictionary(d))
6910
+
6911
+ for i, e in enumerate(edges):
6912
+ d = Topology.Dictionary(e)
6913
+ e = Topology.SetDictionary(e, Dictionary.ByKeyValue(key, i), silent=True)
6914
+ sv, ev = Topology.Vertices(e)
6915
+ sv_n = Dictionary.ValueAtKey(Topology.Dictionary(sv), key)
6916
+ ev_n = Dictionary.ValueAtKey(Topology.Dictionary(ev), key)
6917
+ m_edges.append([sv_n, ev_n])
6918
+ edge_dicts.append(Dictionary.PythonDictionary(d))
6919
+
6920
+ for i, f in enumerate(faces):
6921
+ d = Topology.Dictionary(f)
6922
+ f = Topology.SetDictionary(f, Dictionary.ByKeyValue(key, i), silent=True)
6923
+ if mode == 1:
6924
+ f_edges = Topology.Edges(f)
6925
+ edge_indices = []
6926
+ for f_edge in f_edges:
6927
+ edge_indices.append(Dictionary.ValueAtKey(Topology.Dictionary(f_edge), key))
6928
+ m_faces.append(edge_indices)
6929
+ else:
6930
+ f_vertices = Topology.Vertices(f)
6931
+ vertex_indices = []
6932
+ for f_vertex in f_vertices:
6933
+ vertex_indices.append(Dictionary.ValueAtKey(Topology.Dictionary(f_vertex), key))
6934
+ m_faces.append(vertex_indices)
6935
+ face_dicts.append(Dictionary.PythonDictionary(d))
6936
+
6937
+ for i, c in enumerate(cells):
6938
+ d = Topology.Dictionary(c)
6939
+ c = Topology.SetDictionary(c, Dictionary.ByKeyValue(key, i), silent=True)
6940
+ c_faces = Topology.Faces(c)
6941
+ face_indices = []
6942
+ for c_face in c_faces:
6943
+ face_indices.append(Dictionary.ValueAtKey(Topology.Dictionary(c_face), key))
6944
+ m_cells.append(face_indices)
6945
+ cell_dicts.append(Dictionary.PythonDictionary(d))
6946
+
6947
+ return {"mode": mode,
6948
+ "vertices": m_verts,
6949
+ "edges": m_edges,
6950
+ "faces": m_faces,
6951
+ "cells": m_cells,
6952
+ "vertex_dicts": vertex_dicts,
6953
+ "edge_dicts": edge_dicts,
6954
+ "face_dicts": face_dicts,
6955
+ "cell_dicts": cell_dicts
6956
+ }
6957
+
6709
6958
  @staticmethod
6710
6959
  def Move(topology, x=0, y=0, z=0):
6711
6960
  """
@@ -9791,7 +10040,7 @@ class Topology():
9791
10040
 
9792
10041
 
9793
10042
  @staticmethod
9794
- def Triangulate(topology, transferDictionaries: bool = False, mode: int = 0, meshSize: float = None, tolerance: float = 0.0001):
10043
+ def Triangulate(topology, transferDictionaries: bool = False, mode: int = 0, meshSize: float = None, tolerance: float = 0.0001, silent: bool = False):
9795
10044
  """
9796
10045
  Triangulates the input topology.
9797
10046
 
@@ -9818,6 +10067,8 @@ class Topology():
9818
10067
  calculated automatically and set to 10% of the overall size of the face.
9819
10068
  tolerance : float , optional
9820
10069
  The desired tolerance. The default is 0.0001.
10070
+ silent : bool , optional
10071
+ If set to True, no warnings or errors will be printed. The default is False.
9821
10072
 
9822
10073
  Returns
9823
10074
  -------
@@ -9832,28 +10083,33 @@ class Topology():
9832
10083
  from topologicpy.Cluster import Cluster
9833
10084
 
9834
10085
  if not Topology.IsInstance(topology, "Topology"):
9835
- print("Topology.Triangulate - Error: The input parameter is not a valid topology. Returning None.")
10086
+ if not silent:
10087
+ print("Topology.Triangulate - Error: The input parameter is not a valid topology. Returning None.")
9836
10088
  return None
9837
10089
  t = Topology.Type(topology)
9838
10090
  if (t == Topology.TypeID("Vertex")) or (t == Topology.TypeID("Edge")) or (t == Topology.TypeID("Wire")):
10091
+ if not silent:
10092
+ print("Topology.Triangulate - Warning: The input topology parameter contains no faces. Returning the original topology.")
9839
10093
  return topology
9840
10094
  elif t == Topology.TypeID("Cluster"):
9841
10095
  temp_topologies = []
9842
10096
  cellComplexes = Topology.SubTopologies(topology, subTopologyType="cellcomplex") or []
9843
10097
  for cc in cellComplexes:
9844
- temp_topologies.append(Topology.Triangulate(cc, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance))
10098
+ temp_topologies.append(Topology.Triangulate(cc, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance, silent=silent))
9845
10099
  cells = Cluster.FreeCells(topology, tolerance=tolerance) or []
9846
10100
  for c in cells:
9847
- temp_topologies.append(Topology.Triangulate(c, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance))
10101
+ temp_topologies.append(Topology.Triangulate(c, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance, silent=silent))
9848
10102
  shells = Cluster.FreeShells(topology, tolerance=tolerance) or []
9849
10103
  for s in shells:
9850
- temp_topologies.append(Topology.Triangulate(s, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance))
10104
+ temp_topologies.append(Topology.Triangulate(s, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance, silent=silent))
9851
10105
  faces = Cluster.FreeFaces(topology, tolerance=tolerance) or []
9852
10106
  for f in faces:
9853
- temp_topologies.append(Topology.Triangulate(f, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance))
10107
+ temp_topologies.append(Topology.Triangulate(f, transferDictionaries=transferDictionaries, mode=mode, meshSize=meshSize, tolerance=tolerance, silent=silent))
9854
10108
  if len(temp_topologies) > 0:
9855
10109
  return Cluster.ByTopologies(temp_topologies)
9856
10110
  else:
10111
+ if not silent:
10112
+ print("Topology.Triangulate - Warning: The input topology parameter contains no faces. Returning the original topology.")
9857
10113
  return topology
9858
10114
  topologyFaces = []
9859
10115
  _ = topology.Faces(None, topologyFaces)
@@ -9866,7 +10122,7 @@ class Topology():
9866
10122
  triFaces = [aFace]
9867
10123
  for triFace in triFaces:
9868
10124
  if transferDictionaries:
9869
- selectors.append(Topology.SetDictionary(Face.Centroid(triFace), Topology.Dictionary(aFace)))
10125
+ selectors.append(Topology.SetDictionary(Topology.Centroid(triFace), Topology.Dictionary(aFace), silent=True))
9870
10126
  faceTriangles.append(triFace)
9871
10127
  return_topology = None
9872
10128
  if t == Topology.TypeID("Face") or t == Topology.TypeID("Shell"): # Face or Shell