topologicpy 0.8.10__py3-none-any.whl → 0.8.11__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/Face.py +55 -0
- topologicpy/Graph.py +213 -214
- topologicpy/Matrix.py +211 -5
- topologicpy/Plotly.py +8 -9
- topologicpy/Topology.py +341 -397
- topologicpy/Vector.py +71 -22
- topologicpy/Vertex.py +57 -0
- topologicpy/Wire.py +0 -119
- topologicpy/version.py +1 -1
- {topologicpy-0.8.10.dist-info → topologicpy-0.8.11.dist-info}/METADATA +1 -1
- {topologicpy-0.8.10.dist-info → topologicpy-0.8.11.dist-info}/RECORD +14 -14
- {topologicpy-0.8.10.dist-info → topologicpy-0.8.11.dist-info}/LICENSE +0 -0
- {topologicpy-0.8.10.dist-info → topologicpy-0.8.11.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.10.dist-info → topologicpy-0.8.11.dist-info}/top_level.txt +0 -0
topologicpy/Matrix.py
CHANGED
@@ -141,6 +141,192 @@ class Matrix:
|
|
141
141
|
[0,0,1,translateZ],
|
142
142
|
[0,0,0,1]]
|
143
143
|
|
144
|
+
@staticmethod
|
145
|
+
def ByVectors(vectorA: list, vectorB: list, orientationA: list = [1, 0, 0], orientationB: list = [1, 0, 0]):
|
146
|
+
"""
|
147
|
+
Creates a rotation matrix that aligns vectorA with vectorB and adjusts orientationA to match orientationB.
|
148
|
+
|
149
|
+
Parameters
|
150
|
+
----------
|
151
|
+
vectorA : list
|
152
|
+
The first input vector.
|
153
|
+
vectorB : list
|
154
|
+
The second input vector to align with.
|
155
|
+
orientationA : list
|
156
|
+
The orientation vector associated with vectorA.
|
157
|
+
orientationB : list
|
158
|
+
The orientation vector associated with vectorB.
|
159
|
+
|
160
|
+
Returns
|
161
|
+
-------
|
162
|
+
list
|
163
|
+
The 4x4 transformation matrix.
|
164
|
+
"""
|
165
|
+
from topologicpy.Vector import Vector
|
166
|
+
import numpy as np
|
167
|
+
|
168
|
+
def to_numpy(vector):
|
169
|
+
"""Converts a list or array-like to a numpy array."""
|
170
|
+
return np.array(vector, dtype=np.float64)
|
171
|
+
|
172
|
+
# Normalize input vectors and convert them to numpy arrays
|
173
|
+
vectorA = to_numpy(Vector.Normalize(vectorA))
|
174
|
+
vectorB = to_numpy(Vector.Normalize(vectorB))
|
175
|
+
orientationA = to_numpy(Vector.Normalize(orientationA))
|
176
|
+
orientationB = to_numpy(Vector.Normalize(orientationB))
|
177
|
+
|
178
|
+
# Step 1: Compute rotation matrix to align vectorA with vectorB
|
179
|
+
axis = np.cross(vectorA, vectorB)
|
180
|
+
angle = np.arccos(np.clip(np.dot(vectorA, vectorB), -1.0, 1.0))
|
181
|
+
|
182
|
+
if np.isclose(angle, 0): # Vectors are already aligned
|
183
|
+
rotation_matrix_normal = np.eye(3)
|
184
|
+
elif np.isclose(angle, np.pi): # Vectors are anti-parallel
|
185
|
+
# Choose a perpendicular axis for rotation
|
186
|
+
axis = to_numpy([1, 0, 0]) if not np.isclose(vectorA[0], 0) else to_numpy([0, 1, 0])
|
187
|
+
rotation_matrix_normal = (
|
188
|
+
np.eye(3)
|
189
|
+
- 2 * np.outer(vectorA, vectorA) # Reflect through the plane perpendicular to vectorA
|
190
|
+
)
|
191
|
+
else:
|
192
|
+
axis = axis / np.linalg.norm(axis)
|
193
|
+
K = np.array([
|
194
|
+
[0, -axis[2], axis[1]],
|
195
|
+
[axis[2], 0, -axis[0]],
|
196
|
+
[-axis[1], axis[0], 0]
|
197
|
+
])
|
198
|
+
rotation_matrix_normal = (
|
199
|
+
np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * np.dot(K, K)
|
200
|
+
)
|
201
|
+
|
202
|
+
# Step 2: Rotate orientationA using the first rotation matrix
|
203
|
+
rotated_orientationA = np.dot(rotation_matrix_normal, orientationA)
|
204
|
+
|
205
|
+
# Step 3: Compute rotation to align rotated_orientationA with orientationB in the plane of vectorB
|
206
|
+
projected_orientationA = rotated_orientationA - np.dot(rotated_orientationA, vectorB) * vectorB
|
207
|
+
projected_orientationB = orientationB - np.dot(orientationB, vectorB) * vectorB
|
208
|
+
|
209
|
+
if np.linalg.norm(projected_orientationA) < 1e-6 or np.linalg.norm(projected_orientationB) < 1e-6:
|
210
|
+
# If either projected vector is near zero, skip secondary rotation
|
211
|
+
rotation_matrix_orientation = np.eye(3)
|
212
|
+
else:
|
213
|
+
projected_orientationA = projected_orientationA / np.linalg.norm(projected_orientationA)
|
214
|
+
projected_orientationB = projected_orientationB / np.linalg.norm(projected_orientationB)
|
215
|
+
axis = np.cross(projected_orientationA, projected_orientationB)
|
216
|
+
angle = np.arccos(np.clip(np.dot(projected_orientationA, projected_orientationB), -1.0, 1.0))
|
217
|
+
if np.isclose(angle, 0): # Already aligned
|
218
|
+
rotation_matrix_orientation = np.eye(3)
|
219
|
+
else:
|
220
|
+
axis = axis / np.linalg.norm(axis)
|
221
|
+
K = np.array([
|
222
|
+
[0, -axis[2], axis[1]],
|
223
|
+
[axis[2], 0, -axis[0]],
|
224
|
+
[-axis[1], axis[0], 0]
|
225
|
+
])
|
226
|
+
rotation_matrix_orientation = (
|
227
|
+
np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * np.dot(K, K)
|
228
|
+
)
|
229
|
+
|
230
|
+
# Step 4: Combine the two rotation matrices
|
231
|
+
rotation_matrix = np.dot(rotation_matrix_orientation, rotation_matrix_normal)
|
232
|
+
|
233
|
+
# Convert to 4x4 transformation matrix
|
234
|
+
transform_matrix = np.eye(4)
|
235
|
+
transform_matrix[:3, :3] = rotation_matrix
|
236
|
+
|
237
|
+
return transform_matrix.tolist()
|
238
|
+
|
239
|
+
def ByVectors_old(vectorA: list, vectorB: list, orientationA: list = [1,0,0], orientationB: list = [1,0,0]):
|
240
|
+
"""
|
241
|
+
Creates a rotation matrix that aligns vectorA with vectorB and adjusts orientationA to match orientationB.
|
242
|
+
|
243
|
+
Parameters
|
244
|
+
----------
|
245
|
+
vectorA : list
|
246
|
+
The first input vector.
|
247
|
+
vectorB : list
|
248
|
+
The second input vector to align with.
|
249
|
+
orientationA : list
|
250
|
+
The orientation vector associated with vectorA.
|
251
|
+
orientationB : list
|
252
|
+
The orientation vector associated with vectorB.
|
253
|
+
|
254
|
+
Returns
|
255
|
+
-------
|
256
|
+
list
|
257
|
+
The 4x4 transformation matrix.
|
258
|
+
"""
|
259
|
+
from topologicpy.Vector import Vector
|
260
|
+
import numpy as np
|
261
|
+
|
262
|
+
# Normalize input vectors
|
263
|
+
vectorA = Vector.Normalize(vectorA)
|
264
|
+
vectorB = Vector.Normalize(vectorB)
|
265
|
+
orientationA = Vector.Normalize(orientationA)
|
266
|
+
orientationB = Vector.Normalize(orientationB)
|
267
|
+
|
268
|
+
# Step 1: Compute rotation matrix to align vectorA with vectorB
|
269
|
+
axis = np.cross(vectorA, vectorB)
|
270
|
+
angle = np.arccos(np.clip(np.dot(vectorA, vectorB), -1.0, 1.0))
|
271
|
+
|
272
|
+
if np.isclose(angle, 0): # Vectors are already aligned
|
273
|
+
rotation_matrix_normal = np.eye(3)
|
274
|
+
elif np.isclose(angle, np.pi): # Vectors are anti-parallel
|
275
|
+
# Choose a perpendicular axis for rotation
|
276
|
+
axis = np.array([1, 0, 0]) if not np.isclose(vectorA[0], 0) else np.array([0, 1, 0])
|
277
|
+
rotation_matrix_normal = (
|
278
|
+
np.eye(3)
|
279
|
+
- 2 * np.outer(vectorA, vectorA) # Reflect through the plane perpendicular to vectorA
|
280
|
+
)
|
281
|
+
else:
|
282
|
+
axis = Vector.Normalize(axis)
|
283
|
+
K = np.array([
|
284
|
+
[0, -axis[2], axis[1]],
|
285
|
+
[axis[2], 0, -axis[0]],
|
286
|
+
[-axis[1], axis[0], 0]
|
287
|
+
])
|
288
|
+
rotation_matrix_normal = (
|
289
|
+
np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * np.dot(K, K)
|
290
|
+
)
|
291
|
+
|
292
|
+
# Step 2: Rotate orientationA using the first rotation matrix
|
293
|
+
rotated_orientationA = np.dot(rotation_matrix_normal, orientationA)
|
294
|
+
|
295
|
+
# Step 3: Compute rotation to align rotated_orientationA with orientationB in the plane of vectorB
|
296
|
+
projected_orientationA = rotated_orientationA - np.dot(rotated_orientationA, vectorB) * vectorB
|
297
|
+
projected_orientationB = orientationB - np.dot(orientationB, vectorB) * vectorB
|
298
|
+
|
299
|
+
if np.linalg.norm(projected_orientationA) < 1e-6 or np.linalg.norm(projected_orientationB) < 1e-6:
|
300
|
+
# If either projected vector is near zero, skip secondary rotation
|
301
|
+
rotation_matrix_orientation = np.eye(3)
|
302
|
+
else:
|
303
|
+
projected_orientationA = Vector.Normalize(projected_orientationA)
|
304
|
+
projected_orientationB = Vector.Normalize(projected_orientationB)
|
305
|
+
axis = np.cross(projected_orientationA, projected_orientationB)
|
306
|
+
angle = np.arccos(np.clip(np.dot(projected_orientationA, projected_orientationB), -1.0, 1.0))
|
307
|
+
if np.isclose(angle, 0): # Already aligned
|
308
|
+
rotation_matrix_orientation = np.eye(3)
|
309
|
+
else:
|
310
|
+
axis = Vector.Normalize(axis)
|
311
|
+
K = np.array([
|
312
|
+
[0, -axis[2], axis[1]],
|
313
|
+
[axis[2, 0, -axis[0]]],
|
314
|
+
[-axis[1], axis[0], 0]
|
315
|
+
])
|
316
|
+
rotation_matrix_orientation = (
|
317
|
+
np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * np.dot(K, K)
|
318
|
+
)
|
319
|
+
|
320
|
+
# Step 4: Combine the two rotation matrices
|
321
|
+
rotation_matrix = np.dot(rotation_matrix_orientation, rotation_matrix_normal)
|
322
|
+
|
323
|
+
# Convert to 4x4 transformation matrix
|
324
|
+
transform_matrix = np.eye(4)
|
325
|
+
transform_matrix[:3, :3] = rotation_matrix
|
326
|
+
|
327
|
+
return transform_matrix.tolist()
|
328
|
+
|
329
|
+
|
144
330
|
@staticmethod
|
145
331
|
def EigenvaluesAndVectors(matrix, mantissa: int = 6, silent: bool = False):
|
146
332
|
import numpy as np
|
@@ -203,6 +389,25 @@ class Matrix:
|
|
203
389
|
e_vectors.append([round(x, mantissa) for x in eigenvector])
|
204
390
|
e_vectors = Helper.Sort(e_vectors, list(eigenvalues))
|
205
391
|
return e_values, e_vectors
|
392
|
+
|
393
|
+
@staticmethod
|
394
|
+
def Identity():
|
395
|
+
"""
|
396
|
+
Creates a 4x4 identity translation matrix.
|
397
|
+
|
398
|
+
Parameters
|
399
|
+
----------
|
400
|
+
|
401
|
+
Returns
|
402
|
+
-------
|
403
|
+
list
|
404
|
+
The created 4X4 identity matrix.
|
405
|
+
|
406
|
+
"""
|
407
|
+
return [[1,0,0,0],
|
408
|
+
[0,1,0,0],
|
409
|
+
[0,0,1,0],
|
410
|
+
[0,0,0,1]]
|
206
411
|
|
207
412
|
@staticmethod
|
208
413
|
def Invert(matA, silent: bool = False):
|
@@ -226,12 +431,13 @@ class Matrix:
|
|
226
431
|
|
227
432
|
if not isinstance(matA, list):
|
228
433
|
if not silent:
|
229
|
-
print(
|
434
|
+
print(matA, matA.__class__)
|
435
|
+
print("1. Matrix.Invert - Error: The input matA parameter is not a valid 4X4 matrix. Returning None.")
|
230
436
|
return None
|
231
437
|
np_matrix = np.array(matA)
|
232
438
|
if np_matrix.shape != (4, 4):
|
233
439
|
if not silent:
|
234
|
-
print("Matrix.Invert - Error: The input matA parameter is not a valid 4X4 matrix. Returning None.")
|
440
|
+
print("2. Matrix.Invert - Error: The input matA parameter is not a valid 4X4 matrix. Returning None.")
|
235
441
|
return None
|
236
442
|
|
237
443
|
# Check if the matrix is invertible
|
@@ -264,9 +470,9 @@ class Matrix:
|
|
264
470
|
|
265
471
|
"""
|
266
472
|
# Input validation
|
267
|
-
if not (isinstance(matA, list) and all(isinstance(row, list) for row in matA) and
|
268
|
-
|
269
|
-
|
473
|
+
# if not (isinstance(matA, list) and all(isinstance(row, list) for row in matA) and
|
474
|
+
# isinstance(matB, list) and all(isinstance(row, list) for row in matB)):
|
475
|
+
# raise ValueError("Both inputs must be 2D lists representing matrices.")
|
270
476
|
|
271
477
|
# Check matrix dimension compatibility
|
272
478
|
if len(matA[0]) != len(matB):
|
topologicpy/Plotly.py
CHANGED
@@ -492,11 +492,10 @@ class Plotly:
|
|
492
492
|
x = []
|
493
493
|
y = []
|
494
494
|
z = []
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
group = None
|
495
|
+
n = len(str(len(vertices)))
|
496
|
+
sizes = [size for i in range(len(vertices))]
|
497
|
+
labels = ["Vertex_"+str(i+1).zfill(n) for i in range(len(vertices))]
|
498
|
+
colors = [Color.AnyToHex(color) for i in range(len(vertices))]
|
500
499
|
if colorKey or sizeKey or labelKey or groupKey:
|
501
500
|
if groups:
|
502
501
|
if len(groups) > 0:
|
@@ -516,9 +515,9 @@ class Plotly:
|
|
516
515
|
x.append(v[0])
|
517
516
|
y.append(v[1])
|
518
517
|
z.append(v[2])
|
519
|
-
colors.append(Color.AnyToHex(color))
|
520
|
-
labels.append("Vertex_"+str(m+1).zfill(n))
|
521
|
-
sizes.append(max(size, 1.1))
|
518
|
+
#colors.append(Color.AnyToHex(color))
|
519
|
+
#labels.append("Vertex_"+str(m+1).zfill(n))
|
520
|
+
#sizes.append(max(size, 1.1))
|
522
521
|
if len(dictionaries) > 0:
|
523
522
|
d = dictionaries[m]
|
524
523
|
if d:
|
@@ -677,7 +676,7 @@ class Plotly:
|
|
677
676
|
z=z,
|
678
677
|
name=legendLabel,
|
679
678
|
showlegend=showLegend,
|
680
|
-
marker=dict(symbol="circle", size=marker_width),
|
679
|
+
marker=dict(symbol="circle", size=marker_width, color=color),
|
681
680
|
mode=mode,
|
682
681
|
line=dict(color=d_color, width=width),
|
683
682
|
legendgroup=legendGroup,
|