topologicpy 0.8.9__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/Vector.py CHANGED
@@ -275,13 +275,13 @@ class Vector(list):
275
275
  return [x, y, z]
276
276
 
277
277
  @staticmethod
278
- def ByVertices(vertices, normalize: bool = True, mantissa: int = 6):
278
+ def ByVertices(*vertices, normalize: bool = True, mantissa: int = 6, silent: bool = False):
279
279
  """
280
280
  Creates a vector by the specified input list of vertices.
281
281
 
282
282
  Parameters
283
283
  ----------
284
- vertices : list
284
+ *vertices : list
285
285
  The the input list of topologic vertices. The first element in the list is considered the start vertex. The last element in the list is considered the end vertex.
286
286
  normalize : bool , optional
287
287
  If set to True, the resulting vector is normalized (i.e. its length is set to 1)
@@ -296,16 +296,59 @@ class Vector(list):
296
296
  """
297
297
  from topologicpy.Vertex import Vertex
298
298
  from topologicpy.Topology import Topology
299
+ from topologicpy.Helper import Helper
300
+ import inspect
299
301
 
300
- if not isinstance(vertices, list):
302
+ if len(vertices) == 0:
303
+ if not silent:
304
+ print("Vector.ByVertices - Error: The input vertices parameter is an empty list. Returning None.")
305
+ curframe = inspect.currentframe()
306
+ calframe = inspect.getouterframes(curframe, 2)
307
+ print('caller name:', calframe[1][3])
301
308
  return None
302
- if not isinstance(normalize, bool):
309
+ if len(vertices) == 1:
310
+ vertices = vertices[0]
311
+ if isinstance(vertices, list):
312
+ if len(vertices) == 0:
313
+ if not silent:
314
+ print("Vector.ByVertices - Error: The input vertices parameter is an empty list. Returning None.")
315
+ curframe = inspect.currentframe()
316
+ calframe = inspect.getouterframes(curframe, 2)
317
+ print('caller name:', calframe[1][3])
318
+ return None
319
+ else:
320
+ vertexList = [x for x in vertices if Topology.IsInstance(x, "Vertex")]
321
+ if len(vertexList) == 0:
322
+ if not silent:
323
+ print("Vector.ByVertices - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
324
+ curframe = inspect.currentframe()
325
+ calframe = inspect.getouterframes(curframe, 2)
326
+ print('caller name:', calframe[1][3])
327
+ return None
328
+ else:
329
+ if not silent:
330
+ print("Vector.ByVertices - Warning: The input vertices parameter contains only one vertex. Returning None.")
331
+ curframe = inspect.currentframe()
332
+ calframe = inspect.getouterframes(curframe, 2)
333
+ print('caller name:', calframe[1][3])
334
+ return None
335
+ else:
336
+ vertexList = Helper.Flatten(list(vertices))
337
+ vertexList = [x for x in vertexList if Topology.IsInstance(x, "Vertex")]
338
+ if len(vertexList) == 0:
339
+ if not silent:
340
+ print("Vector.ByVertices - Error: The input parameters do not contain any valid vertices. Returning None.")
341
+ curframe = inspect.currentframe()
342
+ calframe = inspect.getouterframes(curframe, 2)
343
+ print('caller name:', calframe[1][3])
303
344
  return None
304
- vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
305
- if len(vertices) < 2:
345
+
346
+ if len(vertexList) < 2:
347
+ if not silent:
348
+ print("Vector.ByVertices - Error: The input parameters do not contain a minimum of two valid vertices. Returning None.")
306
349
  return None
307
- v1 = vertices[0]
308
- v2 = vertices[-1]
350
+ v1 = vertexList[0]
351
+ v2 = vertexList[-1]
309
352
  vector = [Vertex.X(v2, mantissa=mantissa)-Vertex.X(v1, mantissa=mantissa), Vertex.Y(v2, mantissa=mantissa)-Vertex.Y(v1, mantissa=mantissa), Vertex.Z(v2, mantissa=mantissa)-Vertex.Z(v1, mantissa=mantissa)]
310
353
  if normalize:
311
354
  vector = Vector.Normalize(vector)
@@ -345,11 +388,11 @@ class Vector(list):
345
388
  if not silent:
346
389
  print("Vector.Coordinates - Error: The input vectorB parameter is not a valid vector. Returning Nonne.")
347
390
  return None
348
- if abs(vectorA[0]) < tolerance and abs(vectorA[1]) < tolerance:
391
+ if abs(vectorA[0]) <= tolerance and abs(vectorA[1]) <= tolerance:
349
392
  if not silent:
350
393
  print("Vector.CompassAngle - Error: The input vectorA parameter is vertical in the Z Axis. Returning Nonne.")
351
394
  return None
352
- if abs(vectorB[0]) < tolerance and abs(vectorB[1]) < tolerance:
395
+ if abs(vectorB[0]) <= tolerance and abs(vectorB[1]) <= tolerance:
353
396
  if not silent:
354
397
  print("Vector.CompassAngle - Error: The input vectorB parameter is vertical in the Z Axis. Returning Nonne.")
355
398
  return None
@@ -404,7 +447,7 @@ class Vector(list):
404
447
  x, y, z = vector
405
448
 
406
449
  # Handle the origin
407
- if abs(x) < tolerance and abs(y) < tolerance and abs(z) < tolerance:
450
+ if abs(x) <= tolerance and abs(y) <= tolerance and abs(z) <= tolerance:
408
451
  return "Origin"
409
452
 
410
453
  # Normalize vector to prevent magnitude bias
@@ -412,9 +455,9 @@ class Vector(list):
412
455
  x, y, z = x / magnitude, y / magnitude, z / magnitude
413
456
 
414
457
  # Apply tolerance to components
415
- x = 0 if abs(x) < tolerance else x
416
- y = 0 if abs(y) < tolerance else y
417
- z = 0 if abs(z) < tolerance else z
458
+ x = 0 if abs(x) <= tolerance else x
459
+ y = 0 if abs(y) <= tolerance else y
460
+ z = 0 if abs(z) <= tolerance else z
418
461
 
419
462
  # Compass-based direction in the XY-plane
420
463
  if x == 0 and y > 0:
@@ -553,18 +596,18 @@ class Vector(list):
553
596
  if not silent:
554
597
  print("Vector.Cross - Error: The input vectorB parameter is not a valid vector. Returning None.")
555
598
  return None
556
- if Vector.Magnitude(vector=vectorA, mantissa=mantissa) < tolerance:
599
+ if Vector.Magnitude(vector=vectorA, mantissa=mantissa) <= tolerance:
557
600
  if not silent:
558
601
  print("Vector.Cross - Error: The magnitude of the input vectorA parameter is less than the input tolerance parameter. Returning None.")
559
602
  return None
560
- if Vector.Magnitude(vector=vectorB, mantissa=mantissa) < tolerance:
603
+ if Vector.Magnitude(vector=vectorB, mantissa=mantissa) <= tolerance:
561
604
  if not silent:
562
605
  print("Vector.Cross - Error: The magnitude of the input vectorB parameter is less than the input tolerance parameter. Returning None.")
563
606
  return None
564
607
  vecA = np.array(vectorA)
565
608
  vecB = np.array(vectorB)
566
609
  vecC = list(np.cross(vecA, vecB))
567
- if Vector.Magnitude(vecC) < tolerance:
610
+ if Vector.Magnitude(vecC) <= tolerance:
568
611
  return [0, 0, 0]
569
612
  return [round(vecC[0], mantissa), round(vecC[1], mantissa), round(vecC[2], mantissa)]
570
613
 
@@ -600,11 +643,11 @@ class Vector(list):
600
643
  if not silent:
601
644
  print("Vector.Dot - Error: The input vectorB parameter is not a valid vector. Returning None.")
602
645
  return None
603
- if Vector.Magnitude(vector=vectorA, mantissa=mantissa) < tolerance:
646
+ if Vector.Magnitude(vector=vectorA, mantissa=mantissa) <= tolerance:
604
647
  if not silent:
605
648
  print("Vector.Dot - Error: The magnitude of the input vectorA parameter is less than the input tolerance parameter. Returning None.")
606
649
  return None
607
- if Vector.Magnitude(vector=vectorB, mantissa=mantissa) < tolerance:
650
+ if Vector.Magnitude(vector=vectorB, mantissa=mantissa) <= tolerance:
608
651
  if not silent:
609
652
  print("Vector.Dot - Error: The magnitude of the input vectorB parameter is less than the input tolerance parameter. Returning None.")
610
653
  return None
@@ -673,22 +716,26 @@ class Vector(list):
673
716
  return False
674
717
 
675
718
  @staticmethod
676
- def IsCollinear(vectorA, vectorB):
719
+ def IsCollinear(vectorA, vectorB, tolerance=0.0001):
677
720
  """
678
- Returns True if the input vectors are collinear (parallel or anti-parallel). Returns False otherwise.
679
-
721
+ Returns True if the input vectors are collinear (parallel or anti-parallel)
722
+ within a given tolerance. Returns False otherwise.
723
+
680
724
  Parameters
681
725
  ----------
682
726
  vectorA : list
683
727
  The first input vector.
684
728
  vectorB : list
685
729
  The second input vector.
730
+ tolerance : float, optional
731
+ The desired tolerance for determining collinearity. The default is 0.0001.
686
732
 
687
733
  Returns
688
734
  -------
689
735
  bool
690
- True if the input vectors are collinear (parallel or anti-parallel). False otherwise.
691
-
736
+ True if the input vectors are collinear (parallel or anti-parallel).
737
+ False otherwise.
738
+
692
739
  """
693
740
  import numpy as np
694
741
 
@@ -700,17 +747,19 @@ class Vector(list):
700
747
  vector1_norm = vector1 / np.linalg.norm(vector1)
701
748
  vector2_norm = vector2 / np.linalg.norm(vector2)
702
749
 
703
- # Check if the angle between vectors is either 0 or 180 degrees
750
+ # Check if the dot product is within the tolerance of 1.0 or -1.0
704
751
  dot_product = np.dot(vector1_norm, vector2_norm)
705
- if np.isclose(dot_product, 1.0) or np.isclose(dot_product, -1.0):
752
+ if (1.0 - tolerance) <= dot_product <= (1.0 + tolerance) or \
753
+ (-1.0 - tolerance) <= dot_product <= (-1.0 + tolerance):
706
754
  return True
707
755
  else:
708
756
  return False
709
757
 
710
758
  @staticmethod
711
- def IsParallel(vectorA, vectorB):
759
+ def IsParallel(vectorA, vectorB, tolerance=0.0001):
712
760
  """
713
- Returns True if the input vectors are parallel. Returns False otherwise.
761
+ Returns True if the input vectors are parallel within a given tolerance.
762
+ Returns False otherwise.
714
763
 
715
764
  Parameters
716
765
  ----------
@@ -718,12 +767,13 @@ class Vector(list):
718
767
  The first input vector.
719
768
  vectorB : list
720
769
  The second input vector.
770
+ tolerance : float, optional
771
+ The desired tolerance for determining parallelism. The default is 0.0001.
721
772
 
722
773
  Returns
723
774
  -------
724
775
  bool
725
776
  True if the input vectors are parallel. False otherwise.
726
-
727
777
  """
728
778
  import numpy as np
729
779
 
@@ -735,14 +785,13 @@ class Vector(list):
735
785
  vector1_norm = vector1 / np.linalg.norm(vector1)
736
786
  vector2_norm = vector2 / np.linalg.norm(vector2)
737
787
 
738
- # Check if the angle between vectors is either 0 or 180 degrees
788
+ # Check if the dot product is within the tolerance of 1.0
739
789
  dot_product = np.dot(vector1_norm, vector2_norm)
740
- if np.isclose(dot_product, 1.0):
790
+ if (1.0 - tolerance) <= dot_product <= (1.0 + tolerance):
741
791
  return True
742
792
  else:
743
- # Compute bisecting vector
744
793
  return False
745
-
794
+
746
795
  @staticmethod
747
796
  def IsSame(vectorA, vectorB, tolerance=0.0001):
748
797
  """
@@ -763,7 +812,7 @@ class Vector(list):
763
812
  True if the input vectors are the same. False otherwise.
764
813
 
765
814
  """
766
- return all(abs(a - b) < tolerance for a, b in zip(vectorA, vectorB))
815
+ return all(abs(a - b) <= tolerance for a, b in zip(vectorA, vectorB))
767
816
 
768
817
  @staticmethod
769
818
  def Length(vector, mantissa: int = 6):
@@ -825,7 +874,7 @@ class Vector(list):
825
874
  The created vector that multiplies the input vector by the input magnitude.
826
875
 
827
876
  """
828
- if abs(magnitude) < tolerance:
877
+ if abs(magnitude) <= tolerance:
829
878
  return [0.0] * len(vector)
830
879
  scaled_vector = [component * (magnitude) for component in vector]
831
880
  return scaled_vector
topologicpy/Vertex.py CHANGED
@@ -801,7 +801,7 @@ class Vertex():
801
801
 
802
802
  other_vertex = vertices[i]
803
803
  distance = np.linalg.norm(np.array(vertex) - np.array(other_vertex))
804
- if distance < tolerance:
804
+ if distance <= tolerance:
805
805
  # Choose the coordinate with the least amount of decimal points
806
806
  if count_decimal_points(other_vertex) < count_decimal_points(fused_vertex):
807
807
  fused_vertex = other_vertex
@@ -870,7 +870,7 @@ class Vertex():
870
870
  incoming_edges = []
871
871
  for edge in edges:
872
872
  ev = Edge.EndVertex(edge)
873
- if Vertex.Distance(vertex, ev) < tolerance:
873
+ if Vertex.Distance(vertex, ev) <= tolerance:
874
874
  incoming_edges.append(edge)
875
875
  return incoming_edges
876
876
 
@@ -911,7 +911,7 @@ class Vertex():
911
911
  return i
912
912
  else:
913
913
  d = Vertex.Distance(vertex, vertices[i])
914
- if d < tolerance:
914
+ if d <= tolerance:
915
915
  return i
916
916
  return None
917
917
 
@@ -967,7 +967,7 @@ class Vertex():
967
967
 
968
968
  n_p = nearest_points[0]
969
969
  n_d = n_p[0]
970
- if n_d < tolerance:
970
+ if n_d <= tolerance:
971
971
  return n_p[1]
972
972
 
973
973
  # Calculate the weights for each nearest point based on inverse distance
@@ -1138,7 +1138,7 @@ class Vertex():
1138
1138
  return None
1139
1139
 
1140
1140
  if Topology.IsInstance(topology, "Vertex"):
1141
- return Vertex.Distance(vertex, topology) < tolerance
1141
+ return Vertex.Distance(vertex, topology) <= tolerance
1142
1142
  elif Topology.IsInstance(topology, "Edge"):
1143
1143
  try:
1144
1144
  parameter = Edge.ParameterAtVertex(topology, vertex)
@@ -1502,7 +1502,7 @@ class Vertex():
1502
1502
  outgoing_edges = []
1503
1503
  for edge in edges:
1504
1504
  sv = Edge.StartVertex(edge)
1505
- if Vertex.Distance(vertex, sv) < tolerance:
1505
+ if Vertex.Distance(vertex, sv) <= tolerance:
1506
1506
  outgoing_edges.append(edge)
1507
1507
  return outgoing_edges
1508
1508
 
@@ -1806,6 +1806,63 @@ class Vertex():
1806
1806
  separated_vertices[i] = Topology.SetDictionary(separated_vertices[i], d)
1807
1807
  return separated_vertices
1808
1808
 
1809
+ @staticmethod
1810
+ def Transform(vertex, matrix, mantissa: int = 6, silent: bool = False):
1811
+ """
1812
+ Transforms a 3D vertex using a 4x4 transformation matrix.
1813
+
1814
+ Parameters
1815
+ ----------
1816
+ vertex : topologic_core.Vertex
1817
+ The input vertex
1818
+ matrix : list
1819
+ The 4x4 transformation matrix.
1820
+ mantissa : int , optional
1821
+ The desired length of the mantissa. The default is 6.
1822
+ silent : bool , optional
1823
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1824
+
1825
+ Returns
1826
+ -------
1827
+ topologic_core.Vertex
1828
+ The transformed vertex.
1829
+ """
1830
+ from topologicpy.Topology import Topology
1831
+
1832
+ if not Topology.IsInstance(vertex, "vertex"):
1833
+ if not silent:
1834
+ print("Vertex.Transform - Error: The input vertex parameter is not a valid vertex. Returning None.")
1835
+ return None
1836
+
1837
+ if not isinstance(matrix, list):
1838
+ if not silent:
1839
+ print("Vertex.Transform - Error: The input matrix parameter is not a valid 4X4 matrix. Returning None.")
1840
+ return None
1841
+
1842
+ matrix = np.array(matrix) # Convert list to numpy array if necessary
1843
+
1844
+ # Check if the shape of the matrix is (4, 4)
1845
+ if not matrix.shape == (4, 4):
1846
+ if not silent:
1847
+ print("Vertex.Transform - Error: The input matrix parameter is not a valid 4X4 matrix. Returning None.")
1848
+ return None
1849
+
1850
+ # Convert the vertex to a 4D homogeneous coordinate
1851
+ coords = Vertex.Coordinates(vertex, mantissa=mantissa)
1852
+ homogeneous_coords= np.array([coords[0], coords[1], coords[2], 1.0])
1853
+
1854
+ # Perform matrix multiplication
1855
+ transformed_coords = np.dot(matrix, homogeneous_coords)
1856
+
1857
+ # Convert back to 3D by dividing by w
1858
+ if not np.isclose(transformed_coords[3], 0): # Avoid division by zero
1859
+ transformed_coords /= transformed_coords[3]
1860
+
1861
+ # Return the transformed (x', y', z') as a list
1862
+ coords = transformed_coords[:3].tolist()
1863
+ coords = [round(v, mantissa) for v in coords]
1864
+ return Vertex.ByCoordinates(coords)
1865
+
1809
1866
  @staticmethod
1810
1867
  def X(vertex, mantissa: int = 6) -> float:
1811
1868
  """