topologicpy 0.8.2__py3-none-any.whl → 0.8.4__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/Edge.py CHANGED
@@ -325,6 +325,50 @@ class Edge():
325
325
  edge = None
326
326
  return edge
327
327
 
328
+ @staticmethod
329
+ def ByOriginDirectionLength(origin = None, direction=[0,0,1], length: float = 1.0, tolerance: float = 0.0001, silent: bool = False):
330
+ """
331
+ Creates a straight edge from the input parameters.
332
+
333
+ Parameters
334
+ ----------
335
+ origin : topologic_core.Vertex
336
+ The origin (start vertex) of the edge.
337
+ direction : list , optional
338
+ The desired direction vector of the edge. The default is [0,0,1] (pointing up in the Z direction)
339
+ length: float , optional
340
+ The desired length of edge. The default is 1.0.
341
+ tolerance : float , optional
342
+ The desired tolerance to decide if an edge can be created. The default is 0.0001.
343
+ silent : bool , optional
344
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
345
+
346
+ Returns
347
+ -------
348
+ topologic_core.Edge
349
+ The created edge.
350
+
351
+ """
352
+ from topologicpy.Vertex import Vertex
353
+ from topologicpy.Topology import Topology
354
+
355
+ if origin == None:
356
+ origin = Vertex.Origin()
357
+
358
+ if not Topology.IsInstance(origin, "vertex"):
359
+ if not silent:
360
+ print("Edge.ByVertexDirectionLength - Error: The input vertex parameter is not a valid vertex. Returning None.")
361
+ return None
362
+
363
+ if length < tolerance:
364
+ if not silent:
365
+ print("Edge.ByVertexDirectionLength - Error: The input edge parameter must not be less than the input tolerance parameter. Returning None.")
366
+ return None
367
+
368
+ endVertex = Topology.TranslateByDirectionDistance(origin, direction=direction[:3], distance=length)
369
+ edge = Edge.ByVertices(origin, endVertex, tolerance=tolerance)
370
+ return edge
371
+
328
372
  @staticmethod
329
373
  def ByVertices(*args, tolerance: float = 0.0001, silent: bool = False):
330
374
  """
topologicpy/Matrix.py CHANGED
@@ -204,6 +204,46 @@ class Matrix:
204
204
  e_vectors = Helper.Sort(e_vectors, list(eigenvalues))
205
205
  return e_values, e_vectors
206
206
 
207
+ @staticmethod
208
+ def Invert(matA, silent: bool = False):
209
+ """
210
+ Inverts the input matrix.
211
+
212
+ Parameters
213
+ ----------
214
+ matA : list of list of float
215
+ The input matrix.
216
+ silent : bool , optional
217
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
218
+
219
+ Returns
220
+ -------
221
+ list of list of float
222
+ The resulting matrix after it has been inverted.
223
+
224
+ """
225
+ import numpy as np
226
+
227
+ if not isinstance(matA, list):
228
+ if not silent:
229
+ print("Matrix.Invert - Error: The input matA parameter is not a valid 4X4 matrix. Returning None.")
230
+ return None
231
+ np_matrix = np.array(matA)
232
+ if np_matrix.shape != (4, 4):
233
+ if not silent:
234
+ print("Matrix.Invert - Error: The input matA parameter is not a valid 4X4 matrix. Returning None.")
235
+ return None
236
+
237
+ # Check if the matrix is invertible
238
+ if np.isclose(np.linalg.det(np_matrix), 0):
239
+ if not silent:
240
+ print("Matrix.Invert - Error: The input matA parameter is not invertible. Returning None.")
241
+ return None
242
+
243
+ # Invert the matrix
244
+ inverted_matrix = np.linalg.inv(np_matrix)
245
+ return inverted_matrix.tolist()
246
+
207
247
  @staticmethod
208
248
  def Multiply(matA, matB):
209
249
  """
topologicpy/Plotly.py CHANGED
@@ -714,15 +714,6 @@ class Plotly:
714
714
  traces.append(trace)
715
715
  return traces
716
716
 
717
-
718
-
719
-
720
-
721
-
722
-
723
-
724
-
725
-
726
717
  @staticmethod
727
718
  def DataByTopology(topology,
728
719
  showVertices=True,
@@ -1106,9 +1097,10 @@ class Plotly:
1106
1097
  faceColor = Dictionary.ValueAtKey(d, key=faceColorKey) or faceColor
1107
1098
  if not faceOpacityKey == None:
1108
1099
  d = Topology.Dictionary(topology)
1109
- d_opacity = Dictionary.ValueAtKey(d, key=faceOpacityKey) or faceOpacity
1110
- if 0 < d_opacity < 1:
1111
- faceOpacity = d_opacity
1100
+ d_opacity = Dictionary.ValueAtKey(d, key=faceOpacityKey)
1101
+ if not d_opacity == None:
1102
+ if 0 <= d_opacity <= 1:
1103
+ faceOpacity = d_opacity
1112
1104
  if Topology.IsInstance(topology, "Face"):
1113
1105
  tp_faces = [topology]
1114
1106
  else:
topologicpy/Topology.py CHANGED
@@ -4126,6 +4126,116 @@ class Topology():
4126
4126
  return None
4127
4127
  return Topology.ByXYZFile(file, frameIdKey=frameIdKey, vertexIdKey=frameIdKey)
4128
4128
 
4129
+ def CanonicalMatrix(topology, n: int = 10, normalize: bool = False, mantissa: int = 6, silent: bool = False):
4130
+ """
4131
+ Returns the canonical matrix of the input topology.
4132
+ The canonical matrix refers to a transformation matrix that maps an object's
4133
+ coordinate system to a canonical coordinate frame, where:
4134
+ . The origin of the object aligns with the world origin
4135
+ . The principal axes of the object align with the world axes
4136
+ This transformation is computed using Principal Component Analysis (PCA),
4137
+ leveraging the eigenvectors of the covariance matrix of the object's vertices
4138
+ and thus can give erroneous results. The transformation matrix may not yield an object oriented as expected.
4139
+
4140
+ Parameters
4141
+ ----------
4142
+ topology : topologic_core.Topology
4143
+ The input topology.
4144
+ n : int , optional
4145
+ The number of segments to use to increase the number of points on each face. The default is 10.
4146
+ normalize : bool , optional
4147
+ If set to True, the longest edge in the input topology is scaled to become of length 1. The default is False.
4148
+ mantissa : int , optional
4149
+ The desired length of the mantissa. The default is 6.
4150
+ silent : bool , optional
4151
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
4152
+
4153
+ Returns
4154
+ -------
4155
+ list
4156
+ The 4X4 canonical matrix.
4157
+
4158
+ """
4159
+ from topologicpy.Vertex import Vertex
4160
+ from topologicpy.Edge import Edge
4161
+ from topologicpy.CellComplex import CellComplex
4162
+ from topologicpy.Topology import Topology
4163
+ from topologicpy.Grid import Grid
4164
+ from topologicpy.Matrix import Matrix
4165
+ import numpy as np
4166
+
4167
+ def generate_floats(n):
4168
+ if n < 2:
4169
+ return [0.0] if n == 1 else []
4170
+ return [i / (n - 1) for i in range(n)]
4171
+
4172
+ if not Topology.IsInstance(topology, "topology"):
4173
+ if not silent:
4174
+ print("Topology.CanonicalMatrix - Error: The input topology parameter is not a valid topology. Returning None.")
4175
+ return None
4176
+
4177
+ faces = Topology.Faces(topology)
4178
+ if len(faces) == 0:
4179
+ if not silent:
4180
+ print("Topology.CanonicalMatrix - Error: The input topology parameter does not contain any faces. Returning None.")
4181
+ return None
4182
+
4183
+ # Step 1: Derive a copy topology to work with.
4184
+ if Topology.IsInstance(topology, "CellComplex"):
4185
+ top = CellComplex.ExternalBoundary(topology)
4186
+ else:
4187
+ top = Topology.Copy(topology)
4188
+
4189
+ # Step 2: Create a Translation Matrix to translate to origin
4190
+ centroid = Topology.Centroid(top)
4191
+ translation_matrix = Matrix.ByTranslation(-Vertex.X(centroid, mantissa=mantissa), -Vertex.Y(centroid, mantissa=mantissa), -Vertex.Z(centroid, mantissa=mantissa))
4192
+ translated_top = Topology.Translate(top, -Vertex.X(centroid, mantissa=mantissa), -Vertex.Y(centroid, mantissa=mantissa), -Vertex.Z(centroid, mantissa=mantissa))
4193
+
4194
+ # Step 3: Create a Scaling matrix to normalize size (e.g., largest edge length to 1)
4195
+ if normalize == False:
4196
+ scale_factor = 1.0
4197
+ else:
4198
+ edges = Topology.Edges(translated_top)
4199
+ max_edge_length = max([Edge.Length(edge, mantissa=mantissa) for edge in edges])
4200
+ scale_factor = 1.0 / max_edge_length if max_edge_length != 0 else 1.0
4201
+ scaling_matrix = Matrix.ByScaling(scaleX=scale_factor, scaleY=scale_factor, scaleZ=scale_factor)
4202
+ scaled_top = Topology.Scale(translated_top, origin=Vertex.Origin(), x=scale_factor, y=scale_factor, z=scale_factor)
4203
+
4204
+ # Step 4: Increase the number of vertices by adding a grid of points on each face.
4205
+ faces = Topology.Faces(scaled_top)
4206
+ vertices = Topology.Vertices(scaled_top)
4207
+ r = generate_floats(n)
4208
+ for face in faces:
4209
+ vertices += Topology.Vertices(Grid.VerticesByParameters(face=face, uRange=r, vRange=r, clip=True))
4210
+ points = np.array([[Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa), Vertex.Z(v, mantissa=mantissa)] for v in vertices])
4211
+
4212
+ # Step 5: Align orientation using PCA
4213
+ # Compute PCA
4214
+ mean = points.mean(axis=0)
4215
+ centered_points = points - mean
4216
+ covariance_matrix = np.cov(centered_points.T)
4217
+ eigenvalues, eigenvectors = np.linalg.eigh(covariance_matrix)
4218
+
4219
+ # Step 6: Sort eigenvectors by eigenvalues (largest first)
4220
+ sorted_indices = np.argsort(-eigenvalues)
4221
+ eigenvectors = eigenvectors[:, sorted_indices]
4222
+
4223
+ # Step 7: Enforce consistent orientation by flipping eigenvectors
4224
+ for i in range(3): # Ensure each eigenvector points in a positive direction
4225
+ if np.dot(eigenvectors[:, i], [1, 0, 0]) < 0:
4226
+ eigenvectors[:, i] *= -1
4227
+
4228
+ # Step 8: Create the rotation matrix
4229
+ rotation_matrix = np.eye(4)
4230
+ rotation_matrix[:3, :3] = eigenvectors.T # Use transpose to align points to axes
4231
+
4232
+ # Step 9: Rotate the object to align it
4233
+ transformation_matrix = Matrix.Multiply(scaling_matrix, translation_matrix)
4234
+ transformation_matrix = Matrix.Multiply(rotation_matrix.tolist(), transformation_matrix)
4235
+
4236
+ # Step 10: Return the resulting matrix
4237
+ return transformation_matrix
4238
+
4129
4239
  @staticmethod
4130
4240
  def CenterOfMass(topology):
4131
4241
  """
@@ -6492,9 +6602,9 @@ class Topology():
6492
6602
  return None
6493
6603
  return topologic.Topology.IsSame(topologyA, topologyB)
6494
6604
 
6495
- def IsVertexMatched(topologyA, topologyB, mantissa: int = 6, tolerance=0.0001, silent : bool = False):
6605
+ def IsVertexCongruent(topologyA, topologyB, mantissa: int = 6, tolerance=0.0001, silent : bool = False):
6496
6606
  """
6497
- Returns True if the input topologies are vertex matched (have same number of vertices and all vertices are coincedent within a tolerance). Returns False otherwise.
6607
+ 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.
6498
6608
 
6499
6609
  Parameters
6500
6610
  ----------
@@ -6512,7 +6622,7 @@ class Topology():
6512
6622
  Returns
6513
6623
  -------
6514
6624
  bool
6515
- True of the input topologies are vertex matched. False otherwise.
6625
+ True of the input topologies are vertex congruent. False otherwise.
6516
6626
 
6517
6627
  """
6518
6628
  from topologicpy.Vertex import Vertex
@@ -6573,6 +6683,104 @@ class Topology():
6573
6683
  coords_b = [Vertex.Coordinates(v, mantissa=mantissa) for v in vertices_b]
6574
6684
  return coordinates_match(coords_a, coords_b, tolerance=tolerance)
6575
6685
 
6686
+ @staticmethod
6687
+ def LargestFaces(topology, removeCoplanarFaces: bool = False, epsilon: float = 0.001, tolerance: float = 0.0001, silent: bool = False):
6688
+ """
6689
+ Returns the list of the largest faces found in the input topology.
6690
+
6691
+ Parameters
6692
+ ----------
6693
+ topology : topologic_core.Topology
6694
+ The input topology.
6695
+ removeCoplanarFaces : bool , optional
6696
+ If set to True, coplanar faces are removed to find the true largest faces. Otherwise they are not. The default is False.
6697
+ epsilon : float , optional
6698
+ The desired epsilon (another form of tolerance) for finding if two faces are coplanar. The default is 0.01.
6699
+ tolerance : float , optional
6700
+ The desired tolerance. The default is 0.0001.
6701
+ silent : bool , optional
6702
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
6703
+ Returns
6704
+ -------
6705
+ list
6706
+ The list of the largest faces found in the input topology.
6707
+
6708
+ """
6709
+ from topologicpy.Face import Face
6710
+
6711
+ if not Topology.IsInstance(topology, "topology"):
6712
+ if not silent:
6713
+ print("Topology.LaregestFaces - Error: The input topology parameter is not a valid topology. Returning None.")
6714
+ return None
6715
+ faces = Topology.Faces(topology)
6716
+ if len(faces) == 0:
6717
+ if not silent:
6718
+ print("Topology.LargestFaces - Error: The input topology parameter does not contain any faces. Returning None.")
6719
+ return None
6720
+ if len(faces) == 1:
6721
+ return faces
6722
+
6723
+ if removeCoplanarFaces == True:
6724
+ simple_topology = Topology.RemoveCoplanarFaces(topology)
6725
+ simple_topology = Topology.RemoveCollinearEdges(simple_topology)
6726
+ faces = Topology.Faces(simple_topology)
6727
+ face_areas = [Face.Area(face) for face in faces]
6728
+ max_area = max(face_areas)
6729
+ max_faces = []
6730
+ for i, face_area in enumerate(face_areas):
6731
+ if abs(max_area - face_area) < tolerance:
6732
+ max_faces.append(faces[i])
6733
+ return max_faces
6734
+
6735
+ @staticmethod
6736
+ def LongestEdges(topology, removeCoplanarFaces: bool = False, epsilon: float = 0.001, tolerance: float = 0.0001, silent: bool = False):
6737
+ """
6738
+ Returns the list of the longest edges found in the input topology.
6739
+
6740
+ Parameters
6741
+ ----------
6742
+ topology : topologic_core.Topology
6743
+ The input topology.
6744
+ removeCoplanarFaces : bool , optional
6745
+ If set to True, coplanar faces are removed to find the true longest straight edges. Otherwise they are not. The default is False.
6746
+ epsilon : float , optional
6747
+ The desired epsilon (another form of tolerance) for finding if two faces are coplanar. The default is 0.01.
6748
+ tolerance : float , optional
6749
+ The desired tolerance. The default is 0.0001.
6750
+ silent : bool , optional
6751
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
6752
+ Returns
6753
+ -------
6754
+ list
6755
+ The list of of the longest edges found in the input topology.
6756
+
6757
+ """
6758
+ from topologicpy.Edge import Edge
6759
+
6760
+ if not Topology.IsInstance(topology, "topology"):
6761
+ if not silent:
6762
+ print("Topology.LongestEdges - Error: The input topology parameter is not a valid topology. Returning None.")
6763
+ return None
6764
+ edges = Topology.Edges(topology)
6765
+ if len(edges) == 0:
6766
+ if not silent:
6767
+ print("Topology.LongestEdges - Error: The input topology parameter does not contain any edges. Returning None.")
6768
+ return None
6769
+ if len(edges) == 1:
6770
+ return edges
6771
+
6772
+ if removeCoplanarFaces == True:
6773
+ simple_topology = Topology.RemoveCoplanarFaces(topology)
6774
+ simple_topology = Topology.RemoveCollinearEdges(simple_topology)
6775
+ edges = Topology.Edges(simple_topology)
6776
+ edge_lengths = [Edge.Length(edge) for edge in edges]
6777
+ max_length = max(edge_lengths)
6778
+ max_edges = []
6779
+ for i, edge_length in enumerate(edge_lengths):
6780
+ if abs(max_length - edge_length) < tolerance:
6781
+ max_edges.append(edges[i])
6782
+ return max_edges
6783
+
6576
6784
  @staticmethod
6577
6785
  def MergeAll(topologies, tolerance=0.0001):
6578
6786
  """
@@ -6833,7 +7041,7 @@ class Topology():
6833
7041
  originB : topologic_core.Vertex , optional
6834
7042
  The new location at which to place the topology. If set to None, the world origin (0, 0, 0) is used. The default is None.
6835
7043
  mantissa : int , optional
6836
- The desired length of the mantissa. The default is 6
7044
+ The desired length of the mantissa. The default is 6.
6837
7045
 
6838
7046
  Returns
6839
7047
  -------
@@ -6860,6 +7068,90 @@ class Topology():
6860
7068
  newTopology = None
6861
7069
  return newTopology
6862
7070
 
7071
+ @staticmethod
7072
+ def PrincipalAxes(topology, n: int = 10, mantissa: int = 6, silent: bool = False):
7073
+ """
7074
+ Returns the prinicipal axes (vectors) of the input topology.
7075
+ Please note that this is not a perfect algorithm and it can get confused based on the geometry of the input.
7076
+ Also, please note that there is no guarantee that three returned vectors match your expectation for an x,y,z axis order.
7077
+
7078
+ Parameters
7079
+ ----------
7080
+ topology : topologic_core.Topology
7081
+ The input topology.
7082
+ n : int , optional
7083
+ The number of segments to use to increase the number of points on each face. The default is 10.
7084
+ mantissa : int , optional
7085
+ The desired length of the mantissa. The default is 6.
7086
+ silent : bool , optional
7087
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
7088
+
7089
+ Returns
7090
+ -------
7091
+ list
7092
+ The list of x, y, and z vectors representing the principal axes of the topology.
7093
+
7094
+ """
7095
+ from topologicpy.Vertex import Vertex
7096
+ from topologicpy.CellComplex import CellComplex
7097
+ from topologicpy.Topology import Topology
7098
+ from topologicpy.Grid import Grid
7099
+ from topologicpy.Vector import Vector
7100
+ import numpy as np
7101
+
7102
+ def generate_floats(n):
7103
+ if n < 2:
7104
+ return [0.0] if n == 1 else []
7105
+ return [i / (n - 1) for i in range(n)]
7106
+
7107
+
7108
+ if not Topology.IsInstance(topology, "topology"):
7109
+ if not silent:
7110
+ print("Topology.CanonicalMatrix - Error: The input topology parameter is not a valid topology. Returning None.")
7111
+ return None
7112
+
7113
+ faces = Topology.Faces(topology)
7114
+ if len(faces) == 0:
7115
+ if not silent:
7116
+ print("Topology.CanonicalMatrix - Error: The input topology parameter does not contain any faces. Returning None.")
7117
+ return None
7118
+
7119
+ # Step 1: Derive a copy topology to work with.
7120
+ if Topology.IsInstance(topology, "CellComplex"):
7121
+ top = CellComplex.ExternalBoundary(topology)
7122
+ else:
7123
+ top = Topology.Copy(topology)
7124
+
7125
+ # Step 2: Increase the number of vertices by adding a grid of points on each face.
7126
+ faces = Topology.Faces(top)
7127
+ vertices = Topology.Vertices(top)
7128
+ r = generate_floats(n)
7129
+ for face in faces:
7130
+ vertices += Topology.Vertices(Grid.VerticesByParameters(face=face, uRange=r, vRange=r, clip=True))
7131
+ points = np.array([[Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa), Vertex.Z(v, mantissa=mantissa)] for v in vertices])
7132
+
7133
+ # Step 3: Align orientation using PCA
7134
+ # Compute PCA
7135
+ mean = points.mean(axis=0)
7136
+ centered_points = points - mean
7137
+ covariance_matrix = np.cov(centered_points.T)
7138
+ eigenvalues, eigenvectors = np.linalg.eigh(covariance_matrix)
7139
+
7140
+ # Sort eigenvectors by eigenvalues (largest first)
7141
+ sorted_indices = np.argsort(-eigenvalues)
7142
+ eigenvectors = eigenvectors[:, sorted_indices]
7143
+
7144
+ # Enforce consistent orientation by flipping eigenvectors
7145
+ for i in range(3): # Ensure each eigenvector points in a positive direction
7146
+ if np.dot(eigenvectors[:, i], [1, 0, 0]) < 0:
7147
+ eigenvectors[:, i] *= -1
7148
+
7149
+ # Retrieve and return the principal axes
7150
+ x_axis = Vector.ByCoordinates(*eigenvectors[:, 0])
7151
+ y_axis = Vector.ByCoordinates(*eigenvectors[:, 1])
7152
+ z_axis = Vector.ByCoordinates(*eigenvectors[:, 2])
7153
+ return x_axis, y_axis, z_axis
7154
+
6863
7155
  @staticmethod
6864
7156
  def RemoveCollinearEdges(topology, angTolerance: float = 0.1, tolerance: float = 0.0001, silent: bool = False):
6865
7157
  """
@@ -7978,7 +8270,6 @@ class Topology():
7978
8270
  l = None
7979
8271
  return l
7980
8272
 
7981
-
7982
8273
  @staticmethod
7983
8274
  def SharedFaces(topologyA, topologyB):
7984
8275
  """
@@ -8005,7 +8296,56 @@ class Topology():
8005
8296
  except:
8006
8297
  l = None
8007
8298
  return l
8008
-
8299
+
8300
+ @staticmethod
8301
+ def ShortestEdges(topology, removeCoplanarFaces: bool = False, epsilon: float = 0.001, tolerance: float = 0.0001, silent: bool = False):
8302
+ """
8303
+ Returns the list of the shortest edges found in the input topology.
8304
+
8305
+ Parameters
8306
+ ----------
8307
+ topology : topologic_core.Topology
8308
+ The input topology.
8309
+ removeCoplanarFaces : bool , optional
8310
+ If set to True, coplanar faces are removed to find the true shortest straight edges. Otherwise they are not. The default is False.
8311
+ epsilon : float , optional
8312
+ The desired epsilon (another form of tolerance) for finding if two faces are coplanar. The default is 0.01.
8313
+ tolerance : float , optional
8314
+ The desired tolerance. The default is 0.0001.
8315
+ silent : bool , optional
8316
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
8317
+ Returns
8318
+ -------
8319
+ list
8320
+ The list of the shortest edges found in the input topology.
8321
+
8322
+ """
8323
+ from topologicpy.Edge import Edge
8324
+
8325
+ if not Topology.IsInstance(topology, "topology"):
8326
+ if not silent:
8327
+ print("Topology.ShortestEdges - Error: The input topology parameter is not a valid topology. Returning None.")
8328
+ return None
8329
+ edges = Topology.Edges(topology)
8330
+ if len(edges) == 0:
8331
+ if not silent:
8332
+ print("Topology.ShortestEdges - Error: The input topology parameter does not contain any edges. Returning None.")
8333
+ return None
8334
+ if len(edges) == 1:
8335
+ return edges[0]
8336
+
8337
+ if removeCoplanarFaces == True:
8338
+ simple_topology = Topology.RemoveCoplanarFaces(topology)
8339
+ simple_topology = Topology.RemoveCollinearEdges(simple_topology)
8340
+ edges = Topology.Edges(simple_topology)
8341
+ edge_lengths = [Edge.Length(edge) for edge in edges]
8342
+ min_length = min(edge_lengths)
8343
+ min_edges = []
8344
+ for i, edge_length in enumerate(edge_lengths):
8345
+ if abs(min_length - edge_length) < tolerance:
8346
+ min_edges.append(edges[i])
8347
+ return min_edges
8348
+
8009
8349
  @staticmethod
8010
8350
  def Show(*topologies,
8011
8351
  nameKey = "name",
@@ -8424,6 +8764,55 @@ class Topology():
8424
8764
  figure = Plotly.AddColorBar(figure, values=cbValues, nTicks=cbTicks, xPosition=cbX, width=cbWidth, outlineWidth=cbOutlineWidth, title=cbTitle, subTitle=cbSubTitle, units=cbUnits, colorScale=colorScale, mantissa=mantissa)
8425
8765
  Plotly.Show(figure=figure, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
8426
8766
 
8767
+ @staticmethod
8768
+ def SmallestFaces(topology, removeCoplanarFaces: bool = False, epsilon: float = 0.001, tolerance: float = 0.0001, silent: bool = False):
8769
+ """
8770
+ Returns the list of the smallest faces found in the input topology.
8771
+
8772
+ Parameters
8773
+ ----------
8774
+ topology : topologic_core.Topology
8775
+ The input topology.
8776
+ removeCoplanarFaces : bool , optional
8777
+ If set to True, coplanar faces are removed to find the true smallest faces. Otherwise they are not. The default is False.
8778
+ epsilon : float , optional
8779
+ The desired epsilon (another form of tolerance) for finding if two faces are coplanar. The default is 0.01.
8780
+ tolerance : float , optional
8781
+ The desired tolerance. The default is 0.0001.
8782
+ silent : bool , optional
8783
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
8784
+ Returns
8785
+ -------
8786
+ list
8787
+ The list of the smallest faces found in the input topology.
8788
+
8789
+ """
8790
+ from topologicpy.Face import Face
8791
+
8792
+ if not Topology.IsInstance(topology, "topology"):
8793
+ if not silent:
8794
+ print("Topology.SmallestFaces - Error: The input topology parameter is not a valid topology. Returning None.")
8795
+ return None
8796
+ faces = Topology.Faces(topology)
8797
+ if len(faces) == 0:
8798
+ if not silent:
8799
+ print("Topology.SmallestFaces - Error: The input topology parameter does not contain any faces. Returning None.")
8800
+ return None
8801
+ if len(faces) == 1:
8802
+ return faces
8803
+
8804
+ if removeCoplanarFaces == True:
8805
+ simple_topology = Topology.RemoveCoplanarFaces(topology)
8806
+ simple_topology = Topology.RemoveCollinearEdges(simple_topology)
8807
+ faces = Topology.Faces(simple_topology)
8808
+ face_areas = [Face.Area(face) for face in faces]
8809
+ min_area = min(face_areas)
8810
+ min_faces = []
8811
+ for i, face_area in enumerate(face_areas):
8812
+ if abs(min_area - face_area) < tolerance:
8813
+ min_faces.append(faces[i])
8814
+ return min_faces
8815
+
8427
8816
  @staticmethod
8428
8817
  def SortBySelectors(topologies, selectors, exclusive=False, tolerance=0.0001):
8429
8818
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.2'
1
+ __version__ = '0.8.4'
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: topologicpy
3
- Version: 0.8.2
3
+ Version: 0.8.4
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
@@ -115,12 +115,12 @@ To cite one of the main papers that defines topologicpy, you can use:
115
115
 
116
116
  Or you can import the following .bib formatted references into your favourite reference manager
117
117
  ```
118
- @misc{Jabi2024,
118
+ @misc{Jabi2025,
119
119
  author = {Wassim Jabi},
120
120
  doi = {https://doi.org/10.5281/zenodo.11555173},
121
121
  title = {topologicpy},
122
122
  url = {http://pypi.org/projects/topologicpy},
123
- year = {2024},
123
+ year = {2025},
124
124
  }
125
125
  ```
126
126
  ```
@@ -140,6 +140,6 @@ Or you can import the following .bib formatted references into your favourite re
140
140
  }
141
141
  ```
142
142
 
143
- topologicpy: © 2024 Wassim Jabi
143
+ topologicpy: © 2025 Wassim Jabi
144
144
 
145
- Topologic: © 2024 Cardiff University and UCL
145
+ Topologic: © 2025 Cardiff University and UCL
@@ -8,29 +8,29 @@ topologicpy/Color.py,sha256=q9xsGmxFMz7sQKmygwSVS12GaTRB-OT0-_i6t3-cthE,20307
8
8
  topologicpy/Context.py,sha256=ppApYKngZZCQBFWaxIMi2z2dokY23c935IDCBosxDAE,3055
9
9
  topologicpy/DGL.py,sha256=M_znFtyPBJJz-iXLYZs2wwBj24fhevIo739dGha0chM,139041
10
10
  topologicpy/Dictionary.py,sha256=t0O7Du-iPq46FyKqZfcjHfsUK1E8GS_e67R2V5cpkbw,33186
11
- topologicpy/Edge.py,sha256=4Rl6M9ex66Q9-kNvi_Lg7UlAwdvMzpeFlxlieI3amn0,69511
11
+ topologicpy/Edge.py,sha256=lWwJdQkAhiH5POB7TN6HSLv03g2jXHzBU7e2fE3eAno,71340
12
12
  topologicpy/EnergyModel.py,sha256=UoQ9Jm-hYsN383CbcLKw-y6BKitRHj0uyh84yQ-8ACg,53856
13
13
  topologicpy/Face.py,sha256=D1g4O5i5QMPZvIoX06Z-FsyNsYBDkCiHWJp00uqnr5o,180742
14
14
  topologicpy/Graph.py,sha256=T_NC-Gvf7F7DWdfWvQB7sQ_v790lTT74SLKTl-UhSZE,492072
15
15
  topologicpy/Grid.py,sha256=2s9cSlWldivn1i9EUz4OOokJyANveqmRe_vR93CAndI,18245
16
16
  topologicpy/Helper.py,sha256=DAAE_Ie_ekeMnCvcW08xXRwSAGCkjrS4lbz-o3ELuY4,27172
17
17
  topologicpy/Honeybee.py,sha256=Y_El6M8x3ixvvIe_VcRiwj_4C89ZZg5_WlT7adbCkpw,21849
18
- topologicpy/Matrix.py,sha256=QOQ7W12344hMLXfSTlJcluOm7od25LImWKKb2O0ADmg,9572
18
+ topologicpy/Matrix.py,sha256=ydw0EH4rZcGBFeLmBHPIyuk57DVKKL3M1GcArkFsYxM,10941
19
19
  topologicpy/Neo4j.py,sha256=BKOF29fRgXmdpMGkrNzuYbyqgCJ6ElPPMYlfTxXiVbc,22392
20
- topologicpy/Plotly.py,sha256=Tvo0_zKVEHtPhsMNNvLy5G0HIys5FPAOyp_o4QN_I_A,115760
20
+ topologicpy/Plotly.py,sha256=xfd_c2Mcam5KP-gDD-esl42RVXW5TSJsUCCqhUg1VFk,115788
21
21
  topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
22
22
  topologicpy/PyG.py,sha256=LU9LCCzjxGPUM31qbaJXZsTvniTtgugxJY7y612t4A4,109757
23
23
  topologicpy/Shell.py,sha256=fLRnQ79vtdBDRW1Xn8Gaap34XheGbw7UBFd-ALJ2Y1g,87978
24
24
  topologicpy/Speckle.py,sha256=AlsGlSDuKRtX5jhVsPNSSjjbZis079HbUchDH_5RJmE,18187
25
25
  topologicpy/Sun.py,sha256=42tDWMYpwRG7Z2Qjtp94eRgBuqySq7k8TgNUZDK7QxQ,36837
26
- topologicpy/Topology.py,sha256=-7OsjGeJ64NKcEONee9SqOeh1YQ6yGHEzIPxycQCUdA,445348
26
+ topologicpy/Topology.py,sha256=IHLJxh0TQ7P8Git4qfFDlvas81JXaKQGjxwgBzIX1T0,463367
27
27
  topologicpy/Vector.py,sha256=Cl7besf20cAGmyNPh-9gbFAHnRU5ZWSMChJ3VyFIDs4,35416
28
28
  topologicpy/Vertex.py,sha256=tv6C-rbuNgXHDGgVLT5fbalynLdXqlUuiCDKtkeQ0vk,77814
29
29
  topologicpy/Wire.py,sha256=Gl3Jpygwp8775SG57ua5r5ffTHcN4FOAkeI87yP1cok,234001
30
30
  topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
31
- topologicpy/version.py,sha256=K2rsUy8S7UHe3tStL6NCs5z92apbvbkrpGIEjcCNCNs,22
32
- topologicpy-0.8.2.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
- topologicpy-0.8.2.dist-info/METADATA,sha256=DBa_orJTbm3dIrzhCaCiTfsaqfvkAg1jaxCdeHFoeQA,10512
34
- topologicpy-0.8.2.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
35
- topologicpy-0.8.2.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
- topologicpy-0.8.2.dist-info/RECORD,,
31
+ topologicpy/version.py,sha256=PtH7P3DXedvpK4LWSSxCTW38cmEXvmG6EvUpXZC-eLo,22
32
+ topologicpy-0.8.4.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
+ topologicpy-0.8.4.dist-info/METADATA,sha256=WsO5JswYFeodLDlAb5Jv9TnaIhSl0z2ge2fhH-3hLpw,10512
34
+ topologicpy-0.8.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
+ topologicpy-0.8.4.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
+ topologicpy-0.8.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.7.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5