topologicpy 0.8.2__py3-none-any.whl → 0.8.3__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/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
  """
@@ -6833,7 +6943,7 @@ class Topology():
6833
6943
  originB : topologic_core.Vertex , optional
6834
6944
  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
6945
  mantissa : int , optional
6836
- The desired length of the mantissa. The default is 6
6946
+ The desired length of the mantissa. The default is 6.
6837
6947
 
6838
6948
  Returns
6839
6949
  -------
@@ -6860,6 +6970,90 @@ class Topology():
6860
6970
  newTopology = None
6861
6971
  return newTopology
6862
6972
 
6973
+ @staticmethod
6974
+ def PrincipalAxes(topology, n: int = 10, mantissa: int = 6, silent: bool = False):
6975
+ """
6976
+ Returns the prinicipal axes (vectors) of the input topology.
6977
+ Please note that this is not a perfect algorithm and it can get confused based on the geometry of the input.
6978
+ Also, please note that there is no guarantee that three returned vectors match your expectation for an x,y,z axis order.
6979
+
6980
+ Parameters
6981
+ ----------
6982
+ topology : topologic_core.Topology
6983
+ The input topology.
6984
+ n : int , optional
6985
+ The number of segments to use to increase the number of points on each face. The default is 10.
6986
+ mantissa : int , optional
6987
+ The desired length of the mantissa. The default is 6.
6988
+ silent : bool , optional
6989
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
6990
+
6991
+ Returns
6992
+ -------
6993
+ list
6994
+ The list of x, y, and z vectors representing the principal axes of the topology.
6995
+
6996
+ """
6997
+ from topologicpy.Vertex import Vertex
6998
+ from topologicpy.CellComplex import CellComplex
6999
+ from topologicpy.Topology import Topology
7000
+ from topologicpy.Grid import Grid
7001
+ from topologicpy.Vector import Vector
7002
+ import numpy as np
7003
+
7004
+ def generate_floats(n):
7005
+ if n < 2:
7006
+ return [0.0] if n == 1 else []
7007
+ return [i / (n - 1) for i in range(n)]
7008
+
7009
+
7010
+ if not Topology.IsInstance(topology, "topology"):
7011
+ if not silent:
7012
+ print("Topology.CanonicalMatrix - Error: The input topology parameter is not a valid topology. Returning None.")
7013
+ return None
7014
+
7015
+ faces = Topology.Faces(topology)
7016
+ if len(faces) == 0:
7017
+ if not silent:
7018
+ print("Topology.CanonicalMatrix - Error: The input topology parameter does not contain any faces. Returning None.")
7019
+ return None
7020
+
7021
+ # Step 1: Derive a copy topology to work with.
7022
+ if Topology.IsInstance(topology, "CellComplex"):
7023
+ top = CellComplex.ExternalBoundary(topology)
7024
+ else:
7025
+ top = Topology.Copy(topology)
7026
+
7027
+ # Step 2: Increase the number of vertices by adding a grid of points on each face.
7028
+ faces = Topology.Faces(top)
7029
+ vertices = Topology.Vertices(top)
7030
+ r = generate_floats(n)
7031
+ for face in faces:
7032
+ vertices += Topology.Vertices(Grid.VerticesByParameters(face=face, uRange=r, vRange=r, clip=True))
7033
+ points = np.array([[Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa), Vertex.Z(v, mantissa=mantissa)] for v in vertices])
7034
+
7035
+ # Step 3: Align orientation using PCA
7036
+ # Compute PCA
7037
+ mean = points.mean(axis=0)
7038
+ centered_points = points - mean
7039
+ covariance_matrix = np.cov(centered_points.T)
7040
+ eigenvalues, eigenvectors = np.linalg.eigh(covariance_matrix)
7041
+
7042
+ # Sort eigenvectors by eigenvalues (largest first)
7043
+ sorted_indices = np.argsort(-eigenvalues)
7044
+ eigenvectors = eigenvectors[:, sorted_indices]
7045
+
7046
+ # Enforce consistent orientation by flipping eigenvectors
7047
+ for i in range(3): # Ensure each eigenvector points in a positive direction
7048
+ if np.dot(eigenvectors[:, i], [1, 0, 0]) < 0:
7049
+ eigenvectors[:, i] *= -1
7050
+
7051
+ # Retrieve and return the principal axes
7052
+ x_axis = Vector.ByCoordinates(*eigenvectors[:, 0])
7053
+ y_axis = Vector.ByCoordinates(*eigenvectors[:, 1])
7054
+ z_axis = Vector.ByCoordinates(*eigenvectors[:, 2])
7055
+ return x_axis, y_axis, z_axis
7056
+
6863
7057
  @staticmethod
6864
7058
  def RemoveCollinearEdges(topology, angTolerance: float = 0.1, tolerance: float = 0.0001, silent: bool = False):
6865
7059
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.2'
1
+ __version__ = '0.8.3'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.8.2
3
+ Version: 0.8.3
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
@@ -8,14 +8,14 @@ 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
20
  topologicpy/Plotly.py,sha256=Tvo0_zKVEHtPhsMNNvLy5G0HIys5FPAOyp_o4QN_I_A,115760
21
21
  topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
@@ -23,14 +23,14 @@ 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=xbiMgnRrO_My5cHVMajsIuQYU81zsDy6C-7ZQgF5OFU,454552
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=LokQdkRCwhhLh0zOp7jizhyDgbtAU-IWRIObrz2zLjA,22
32
+ topologicpy-0.8.3.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
+ topologicpy-0.8.3.dist-info/METADATA,sha256=Alf593hRuyD3EdVldbGvB1qklfCWf2sDcSvktEhmHn8,10512
34
+ topologicpy-0.8.3.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
35
+ topologicpy-0.8.3.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
+ topologicpy-0.8.3.dist-info/RECORD,,