topologicpy 0.7.12__py3-none-any.whl → 0.7.14__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/Cell.py CHANGED
@@ -566,7 +566,7 @@ class Cell():
566
566
  from topologicpy.Topology import Topology
567
567
  from topologicpy.Cell import Cell
568
568
  from topologicpy.Vertex import Vertex
569
- if not origin:
569
+ if not Topology.IsInstance(origin, "Vertex"):
570
570
  origin = Vertex.ByCoordinates(0, 0, 0)
571
571
  if not Topology.IsInstance(origin, "Vertex"):
572
572
  print("Cell.Capsule - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -652,8 +652,10 @@ class Cell():
652
652
  The radius of the top circle of the cone. The default is 0.
653
653
  height : float , optional
654
654
  The height of the cone. The default is 1.
655
- sides : int , optional
656
- The number of sides of the cone. The default is 16.
655
+ uSides : int , optional
656
+ The number of circle segments of the cylinder. The default is 16.
657
+ vSides : int , optional
658
+ The number of vertical segments of the cylinder. The default is 1.
657
659
  direction : list , optional
658
660
  The vector representing the up direction of the cone. The default is [0, 0, 1].
659
661
  placement : str , optional
@@ -697,7 +699,7 @@ class Cell():
697
699
  f = Face.ByWire(w, tolerance=tolerance)
698
700
  faces.append(f)
699
701
  return Cell.ByFaces(faces, tolerance=tolerance)
700
- if not origin:
702
+ if not Topology.IsInstance(origin, "Vertex"):
701
703
  origin = Vertex.ByCoordinates(0, 0, 0)
702
704
  if not Topology.IsInstance(origin, "Vertex"):
703
705
  print("Cell.Cone - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -839,7 +841,7 @@ class Cell():
839
841
  from topologicpy.CellComplex import CellComplex
840
842
  from topologicpy.Cluster import Cluster
841
843
  from topologicpy.Topology import Topology
842
- if not origin:
844
+ if not Topology.IsInstance(origin, "Vertex"):
843
845
  origin = Vertex.ByCoordinates(0, 0, 0)
844
846
  if not Topology.IsInstance(origin, "Vertex"):
845
847
  print("Cell.Cylinder - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1014,7 +1016,7 @@ class Cell():
1014
1016
  from topologicpy.Cluster import Cluster
1015
1017
  from topologicpy.Topology import Topology
1016
1018
 
1017
- if not origin:
1019
+ if not Topology.IsInstance(origin, "Vertex"):
1018
1020
  origin = Vertex.ByCoordinates(0, 0, 0)
1019
1021
  if not Topology.IsInstance(origin, "Vertex"):
1020
1022
  print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1113,7 +1115,7 @@ class Cell():
1113
1115
  from topologicpy.Topology import Topology
1114
1116
  from topologicpy.Dictionary import Dictionary
1115
1117
 
1116
- if not origin:
1118
+ if not Topology.IsInstance(origin, "Vertex"):
1117
1119
  origin = Vertex.ByCoordinates(0, 0, 0)
1118
1120
  if not Topology.IsInstance(origin, "Vertex"):
1119
1121
  print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1280,7 +1282,7 @@ class Cell():
1280
1282
  returnTopology = Cluster.ByTopologies(faces)
1281
1283
  return returnTopology
1282
1284
 
1283
- if not origin:
1285
+ if not Topology.IsInstance(origin, "Vertex"):
1284
1286
  origin = Vertex.ByCoordinates(0, 0, 0)
1285
1287
  if not Topology.IsInstance(origin, "Vertex"):
1286
1288
  print("Cell.Hyperboloid - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1352,7 +1354,7 @@ class Cell():
1352
1354
  from topologicpy.Topology import Topology
1353
1355
  import math
1354
1356
 
1355
- if not origin:
1357
+ if not Topology.IsInstance(origin, "Vertex"):
1356
1358
  origin = Vertex.ByCoordinates(0, 0, 0)
1357
1359
  if not Topology.IsInstance(origin, "Vertex"):
1358
1360
  print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1520,7 +1522,7 @@ class Cell():
1520
1522
  from topologicpy.Face import Face
1521
1523
  from topologicpy.Topology import Topology
1522
1524
 
1523
- if not origin:
1525
+ if not Topology.IsInstance(origin, "Vertex"):
1524
1526
  origin = Vertex.ByCoordinates(0, 0, 0)
1525
1527
  if not Topology.IsInstance(origin, "Vertex"):
1526
1528
  print("Cell.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1751,7 +1753,7 @@ class Cell():
1751
1753
  from topologicpy.Face import Face
1752
1754
  from topologicpy.Topology import Topology
1753
1755
 
1754
- if not origin:
1756
+ if not Topology.IsInstance(origin, "Vertex"):
1755
1757
  origin = Vertex.ByCoordinates(0, 0, 0)
1756
1758
  if not Topology.IsInstance(origin, "Vertex"):
1757
1759
  print("Cell.Prism - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1965,7 +1967,7 @@ class Cell():
1965
1967
  from topologicpy.Wire import Wire
1966
1968
  from topologicpy.Topology import Topology
1967
1969
 
1968
- if not origin:
1970
+ if not Topology.IsInstance(origin, "Vertex"):
1969
1971
  origin = Vertex.ByCoordinates(0, 0, 0)
1970
1972
  if not Topology.IsInstance(origin, "Vertex"):
1971
1973
  print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -2039,7 +2041,7 @@ class Cell():
2039
2041
  from topologicpy.Topology import Topology
2040
2042
  import math
2041
2043
 
2042
- if not origin:
2044
+ if not Topology.IsInstance(origin, "Vertex"):
2043
2045
  origin = Vertex.ByCoordinates(0, 0, 0)
2044
2046
  if not Topology.IsInstance(origin, "Vertex"):
2045
2047
  print("Cell.Tetrahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -2100,7 +2102,7 @@ class Cell():
2100
2102
  from topologicpy.Wire import Wire
2101
2103
  from topologicpy.Topology import Topology
2102
2104
 
2103
- if not origin:
2105
+ if not Topology.IsInstance(origin, "Vertex"):
2104
2106
  origin = Vertex.ByCoordinates(0, 0, 0)
2105
2107
  if not Topology.IsInstance(origin, "Vertex"):
2106
2108
  print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -678,6 +678,29 @@ class CellComplex():
678
678
  """
679
679
  return cellComplex.ExternalBoundary()
680
680
 
681
+ @staticmethod
682
+ def ExternalBoundary(cellComplex):
683
+ """
684
+ Returns the external boundary (cell) of the input cellComplex.
685
+
686
+ Parameters
687
+ ----------
688
+ cellComplex : topologic_core.CellComplex
689
+ The input cellComplex.
690
+
691
+ Returns
692
+ -------
693
+ topologic_core.Cell
694
+ The external boundary of the input cellComplex.
695
+
696
+ """
697
+ from topologicpy.Topology import Topology
698
+
699
+ if not Topology.IsInstance(cellComplex, "CellComplex"):
700
+ print("CellComplex.ExternalBoundary - Error: The input cellComplex parameter is not a valid cellComplex. Returning None.")
701
+ return None
702
+ return cellComplex.ExternalBoundary()
703
+
681
704
  @staticmethod
682
705
  def ExternalFaces(cellComplex) -> list:
683
706
  """
@@ -795,7 +818,7 @@ class CellComplex():
795
818
  from topologicpy.Face import Face
796
819
  from topologicpy.Topology import Topology
797
820
 
798
- if not origin:
821
+ if not Topology.IsInstance(origin, "Vertex"):
799
822
  origin = Vertex.ByCoordinates(0, 0, 0)
800
823
  if not Topology.IsInstance(origin, "Vertex"):
801
824
  print("CellComplex.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
topologicpy/Edge.py CHANGED
@@ -444,50 +444,98 @@ class Edge():
444
444
  return Edge.ByVertices([sve, eve], tolerance=tolerance, silent=True)
445
445
 
446
446
  @staticmethod
447
- def ExtendToEdge2D(edgeA, edgeB, tolerance: float = 0.0001):
447
+ def ExtendToEdge(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
448
448
  """
449
- Extends the first input edge to meet the second input edge. This works only in the XY plane. Z coordinates are ignored.
449
+ Extends the first input edge to meet the second input edge.
450
450
 
451
451
  Parameters
452
452
  ----------
453
453
  edgeA : topologic_core.Edge
454
- The first input edge.
454
+ The first input edge. This edge will be extended to meet edgeB.
455
455
  edgeB : topologic_core.Edge
456
- The second input edge.
456
+ The second input edge. This edge will be used to extend edgeA.
457
457
  tolerance : float , optional
458
458
  The desired tolerance. The default is 0.0001.
459
+ silent : bool , optional
460
+ If set to True, not error or warning messages are printed. Otherwise, they are. The default is False.
459
461
 
460
462
  Returns
461
463
  -------
462
464
  topologic_core.Edge
463
- The extended edge.
465
+ The trimmed edge.
464
466
 
465
467
  """
466
-
467
468
  from topologicpy.Vertex import Vertex
469
+ from topologicpy.Vector import Vector
468
470
  from topologicpy.Topology import Topology
469
471
 
470
472
  if not Topology.IsInstance(edgeA, "Edge"):
471
- print("Edge.ExtendToEdge2D - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
473
+ if not silent:
474
+ print("Edge.ExtendToEdge - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
472
475
  return None
473
476
  if not Topology.IsInstance(edgeB, "Edge"):
474
- print("Edge.ExtendToEdge2D - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
477
+ if not silent:
478
+ print("Edge.ExtendToEdge - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
475
479
  return None
480
+ if not Edge.IsCoplanar(edgeA, edgeB, mantissa=mantissa, tolerance=tolerance):
481
+ if not silent:
482
+ print("Edge.ExtendToEdge - Error: The input edges are not coplanar. Returning the original edge.")
483
+ return edgeA
484
+ if not Edge.IsParallel(edgeA, edgeB, tolerance=tolerance):
485
+ if not silent:
486
+ print("Edge.ExtendToEdge - Error: The input edges are parallel. Returning the original edge.")
487
+ return edgeA
488
+
476
489
  sva = Edge.StartVertex(edgeA)
477
490
  eva = Edge.EndVertex(edgeA)
478
- intVertex = Edge.Intersect2D(edgeA, edgeB)
479
- if intVertex and not (Vertex.IsInternal(intVertex, edgeA)):
480
- e1 = Edge.ByVertices([sva, intVertex], tolerance=tolerance, silent=True)
481
- e2 = Edge.ByVertices([eva, intVertex], tolerance=tolerance, silent=True)
482
- l1 = Edge.Length(e1)
483
- l2 = Edge.Length(e2)
484
- if l1 > l2:
485
- return e1
486
- else:
487
- return e2
488
- print("Edge.ExtendToEdge2D - Error: The operation failed. Returning None.")
491
+ d1 = Vertex.Distance(sva, edgeB)
492
+ d2 = Vertex.Distance(eva, edgeB)
493
+ edge_direction = Edge.Direction(edgeA)
494
+ if d1 < d2:
495
+ v1 = eva
496
+ v2 = sva
497
+ edge_direction = Vector.Reverse(edge_direction)
498
+ else:
499
+ v1 = sva
500
+ v2 = eva
501
+
502
+ d = max(d1, d2)*2
503
+ v2 = Topology.TranslateByDirectionDistance(v2, direction=edge_direction, distance=d)
504
+ new_edge = Edge.ByVertices(v1, v2)
505
+
506
+ svb = Edge.StartVertex(edgeB)
507
+ evb = Edge.EndVertex(edgeB)
508
+
509
+ intVertex = Topology.Intersect(new_edge, edgeB, tolerance=tolerance)
510
+ if intVertex:
511
+ return Edge.ByVertices([v1, intVertex], tolerance=tolerance, silent=True)
512
+ print("Edge.ExtendToEdge - Error: The operation failed. Returning None.")
489
513
  return None
490
514
 
515
+ @staticmethod
516
+ def ExternalBoundary(edge):
517
+ """
518
+ Returns the external boundary (cluster of end vertices) of the input edge.
519
+
520
+ Parameters
521
+ ----------
522
+ edge : topologic_core.Edge
523
+ The input edge.
524
+
525
+ Returns
526
+ -------
527
+ topologic_core.Cluster
528
+ The external boundary of the input edge. This is a cluster of the edge's end vertices.
529
+
530
+ """
531
+ from topologicpy.Topology import Topology
532
+ from topologicpy.Cluster import Cluster
533
+
534
+ if not Topology.IsInstance(edge, "Edge"):
535
+ print("Edge.ExternalBoundary - Error: The input edge parameter is not a valid edge. Returning None.")
536
+ return None
537
+ return Cluster.ByTopologies([Edge.StartVertex(edge), Edge.EndVertex(edge)])
538
+
491
539
  @staticmethod
492
540
  def Index(edge, edges: list, strict: bool = False, tolerance: float = 0.0001) -> int:
493
541
  """
@@ -542,67 +590,6 @@ class Edge():
542
590
  return i
543
591
  return None
544
592
 
545
- @staticmethod
546
- def Intersect2D(edgeA, edgeB, mantissa: int = 6, silent: bool = False):
547
- """
548
- Returns the intersection of the two input edges as a topologic_core.Vertex. This works only in the XY plane. Z coordinates are ignored.
549
-
550
- Parameters
551
- ----------
552
- edgeA : topologic_core.Edge
553
- The first input edge.
554
- edgeB : topologic_core.Edge
555
- The second input edge.
556
- mantissa : int , optional
557
- The desired length of the mantissa. The default is 6.
558
- silent : bool , optional
559
- If set to False, error and warning messages are displayed. Otherwise they are not. The default is False.
560
-
561
- Returns
562
- -------
563
- topologic_core.Vertex
564
- The intersection of the two input edges.
565
-
566
- """
567
- from topologicpy.Vertex import Vertex
568
- from topologicpy.Topology import Topology
569
-
570
- if not Topology.IsInstance(edgeA, "Edge"):
571
- if not silent:
572
- print("Edge.Intersect2D - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
573
- return None
574
- if not Topology.IsInstance(edgeB, "Edge"):
575
- if not silent:
576
- print("Edge.Intersect2D - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
577
- return None
578
- sva = Edge.StartVertex(edgeA)
579
- eva = Edge.EndVertex(edgeA)
580
- svb = Edge.StartVertex(edgeB)
581
- evb = Edge.EndVertex(edgeB)
582
- # Line AB represented as a1x + b1y = c1
583
- a1 = Vertex.Y(eva, mantissa=mantissa) - Vertex.Y(sva, mantissa=mantissa)
584
- b1 = Vertex.X(sva, mantissa=mantissa) - Vertex.X(eva, mantissa=mantissa)
585
- c1 = a1*(Vertex.X(sva, mantissa=mantissa)) + b1*(Vertex.Y(sva, mantissa=mantissa))
586
-
587
- # Line CD represented as a2x + b2y = c2
588
- a2 = Vertex.Y(evb, mantissa=mantissa) - Vertex.Y(svb, mantissa=mantissa)
589
- b2 = Vertex.X(svb, mantissa=mantissa) - Vertex.X(evb, mantissa=mantissa)
590
- c2 = a2*(Vertex.X(svb, mantissa=mantissa)) + b2*(Vertex.Y(svb, mantissa=mantissa))
591
-
592
- determinant = a1*b2 - a2*b1
593
-
594
- if (determinant == 0):
595
- # The lines are parallel. This is simplified
596
- # by returning a pair of FLT_MAX
597
- if not silent:
598
- print("Edge.Intersect2D - Warning: The input edgeA and edgeB parameters are parallel edges. Returning None.")
599
- return None
600
- else:
601
- x = (b2*c1 - b1*c2)/determinant
602
- y = (a1*c2 - a2*c1)/determinant
603
- return Vertex.ByCoordinates(x,y,0)
604
-
605
-
606
593
  @staticmethod
607
594
  def IsCollinear(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001) -> bool:
608
595
  """
@@ -686,7 +673,65 @@ class Edge():
686
673
  return bool(distance_start < tolerance) and bool(distance_end < tolerance)
687
674
 
688
675
  @staticmethod
689
- def IsParallel(edgeA, edgeB, mantissa: int = 6, angTolerance: float = 0.1) -> bool:
676
+ def IsCoplanar(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001):
677
+ """
678
+ Return True if the two input edges are coplanar. Returns False otherwise.
679
+
680
+ Parameters
681
+ ----------
682
+ edgeA : topologic_core.Edge
683
+ The first input edge.
684
+ edgeB : topologic_core.Edge
685
+ The second input edge.
686
+ mantissa : int , optional
687
+ The desired length of the mantissa. The default is 6.
688
+ tolerance : float , optional
689
+ The desired tolerance. The default is 0.0001.
690
+
691
+ Returns
692
+ -------
693
+ bool
694
+ True if the two edges are coplanar. False otherwise.
695
+
696
+ """
697
+ import numpy as np
698
+ from topologicpy.Vertex import Vertex
699
+ from topologicpy.Topology import Topology
700
+
701
+ if not Topology.IsInstance(edgeA, "Edge"):
702
+ print("Edge.IsCoplanar - Error: The input parameter edgeA is not a valid edge. Returning None")
703
+ return None
704
+ if not Topology.IsInstance(edgeB, "Edge"):
705
+ print("Edge.IsCoplanar - Error: The input parameter edgeB is not a valid edge. Returning None")
706
+ return None
707
+ if Edge.Length(edgeA) < tolerance:
708
+ print("Edge.IsCoplanar - Error: The length of edgeA is less than the tolerance. Returning None")
709
+ return None
710
+ if Edge.Length(edgeB) < tolerance:
711
+ print("Edge.IsCoplanar - Error: The length of edgeB is less than the tolerance. Returning None")
712
+ return None
713
+
714
+ # Extract points
715
+ sva, eva = [Topology.Vertices(edgeA)[0], Topology.Vertices(edgeA)[-1]]
716
+ p1 = Vertex.Coordinates(sva, mantissa=mantissa)
717
+ p2 = Vertex.Coordinates(eva, mantissa=mantissa)
718
+ svb, evb = [Topology.Vertices(edgeB)[0], Topology.Vertices(edgeB)[-1]]
719
+ p3 = Vertex.Coordinates(svb, mantissa=mantissa)
720
+ p4 = Vertex.Coordinates(evb, mantissa=mantissa)
721
+
722
+ # Create vectors
723
+ v1 = np.subtract(p2, p1)
724
+ v2 = np.subtract(p4, p3)
725
+ v3 = np.subtract(p3, p1)
726
+
727
+ # Calculate the scalar triple product
728
+ scalar_triple_product = np.dot(np.cross(v1, v2), v3)
729
+
730
+ # Check for coplanarity
731
+ return np.isclose(scalar_triple_product, 0, atol=tolerance)
732
+
733
+ @staticmethod
734
+ def IsParallel(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001) -> bool:
690
735
  """
691
736
  Return True if the two input edges are parallel. Returns False otherwise.
692
737
 
@@ -707,7 +752,9 @@ class Edge():
707
752
  True if the two edges are collinear. False otherwise.
708
753
 
709
754
  """
755
+ from topologicpy.Vertex import Vertex
710
756
  from topologicpy.Topology import Topology
757
+ import numpy as np
711
758
 
712
759
  if not Topology.IsInstance(edgeA, "Edge"):
713
760
  print("Edge.IsParallel - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
@@ -715,10 +762,38 @@ class Edge():
715
762
  if not Topology.IsInstance(edgeB, "Edge"):
716
763
  print("Edge.IsParallel - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
717
764
  return None
718
- ang = Edge.Angle(edgeA, edgeB, mantissa=mantissa, bracket=True)
719
- if abs(ang) < angTolerance or abs(180 - ang) < angTolerance:
720
- return True
721
- return False
765
+
766
+ def are_lines_parallel(line1, line2, tolerance=0.0001):
767
+ """
768
+ Determines if two lines in 3D space are parallel.
769
+
770
+ Parameters:
771
+ line1 (tuple): A tuple of two points defining the first line. Each point is a tuple of (x, y, z).
772
+ line2 (tuple): A tuple of two points defining the second line. Each point is a tuple of (x, y, z).
773
+
774
+ Returns:
775
+ bool: True if the lines are parallel, False otherwise.
776
+ """
777
+ def vector_from_points(p1, p2):
778
+ return np.array([p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]])
779
+
780
+ # Get direction vectors for both lines
781
+ vec1 = vector_from_points(line1[0], line1[1])
782
+ vec2 = vector_from_points(line2[0], line2[1])
783
+
784
+ # Compute the cross product of the direction vectors
785
+ cross_product = np.cross(vec1, vec2)
786
+
787
+ # Two vectors are parallel if their cross product is a zero vector
788
+ return np.allclose(cross_product, 0, atol=tolerance)
789
+
790
+ x1, y1, z1 = Vertex.Coordinates(Edge.StartVertex(edgeA), mantissa=mantissa)
791
+ x2, y2, z2 = Vertex.Coordinates(Edge.EndVertex(edgeA), mantissa=mantissa)
792
+ x3, y3, z3 = Vertex.Coordinates(Edge.StartVertex(edgeA), mantissa=mantissa)
793
+ x4, y4, z4 = Vertex.Coordinates(Edge.EndVertex(edgeA), mantissa=mantissa)
794
+ line1 = ((x1, y1, z1), (x2, y2, z2))
795
+ line2 = ((x3, y3, z3), (x4, y4, z4))
796
+ return are_lines_parallel(line1, line2, tolerance=tolerance)
722
797
 
723
798
  @staticmethod
724
799
  def Length(edge, mantissa: int = 6) -> float:
@@ -770,7 +845,7 @@ class Edge():
770
845
  1. "center" which places the center of the edge at the origin.
771
846
  2. "start" which places the start of the edge at the origin.
772
847
  3. "end" which places the end of the edge at the origin.
773
- The default is "center".
848
+ The default is "center". It is case insensitive.
774
849
  tolerance : float , optional
775
850
  The desired tolerance. The default is 0.0001.
776
851
  Returns
@@ -783,7 +858,7 @@ class Edge():
783
858
  from topologicpy.Vector import Vector
784
859
  from topologicpy.Topology import Topology
785
860
 
786
- if origin == None:
861
+ if not Topology.IsInstance(origin, "Vertex"):
787
862
  origin = Vertex.Origin()
788
863
  if not Topology.IsInstance(origin, "Vertex"):
789
864
  print("Edge.Line - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -1126,18 +1201,22 @@ class Edge():
1126
1201
  return Edge.ByVertices([sve, eve], tolerance=tolerance, silent=True)
1127
1202
 
1128
1203
  @staticmethod
1129
- def TrimByEdge2D(edgeA, edgeB, reverse: bool = False, tolerance: float = 0.0001):
1204
+ def TrimByEdge(edgeA, edgeB, reverse: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
1130
1205
  """
1131
- Trims the first input edge by the second input edge. This works only in the XY plane. Z coordinates are ignored.
1206
+ Trims the first input edge by the second input edge.
1132
1207
 
1133
1208
  Parameters
1134
1209
  ----------
1135
1210
  edgeA : topologic_core.Edge
1136
- The first input edge.
1211
+ The first input edge. This edge will be trimmed by edgeB.
1137
1212
  edgeB : topologic_core.Edge
1138
- The second input edge.
1213
+ The second input edge. This edge will be used to trim edgeA.
1214
+ reverse : bool , optional
1215
+ If set to True, which segment is preserved is reversed. Otherwise, it is not. The default is False.
1139
1216
  tolerance : float , optional
1140
1217
  The desired tolerance. The default is 0.0001.
1218
+ silent : bool , optional
1219
+ If set to True, not error or warning messages are printed. Otherwise, they are. The default is False.
1141
1220
 
1142
1221
  Returns
1143
1222
  -------
@@ -1149,14 +1228,45 @@ class Edge():
1149
1228
  from topologicpy.Topology import Topology
1150
1229
 
1151
1230
  if not Topology.IsInstance(edgeA, "Edge"):
1152
- print("Edge.TrimByEdge2D - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
1231
+ if not silent:
1232
+ print("Edge.TrimByEdge - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
1153
1233
  return None
1154
1234
  if not Topology.IsInstance(edgeB, "Edge"):
1155
- print("Edge.TrimByEdge2D - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
1235
+ if not silent:
1236
+ print("Edge.TrimByEdge - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
1156
1237
  return None
1238
+ if not Edge.IsCoplanar(edgeA, edgeB, mantissa=mantissa, tolerance=tolerance):
1239
+ if not silent:
1240
+ print("Edge.TrimByEdge - Error: The input edges are not coplanar. Returning the original edge.")
1241
+ return edgeA
1242
+ if not Edge.IsParallel(edgeA, edgeB, tolerance=tolerance):
1243
+ if not silent:
1244
+ print("Edge.TrimByEdge - Error: The input edges are parallel. Returning the original edge.")
1245
+ return edgeA
1246
+
1247
+ sva = Edge.StartVertex(edgeA)
1248
+ eva = Edge.EndVertex(edgeA)
1249
+ svb = Edge.StartVertex(edgeB)
1250
+ evb = Edge.EndVertex(edgeB)
1251
+ intVertex = None
1252
+ if Edge.IsCollinear(edgeA, edgeB):
1253
+ if Vertex.IsInternal(svb, edgeA):
1254
+ intVertex = svb
1255
+ elif Vertex.IsInternal(evb, edgeA):
1256
+ intVertex = evb
1257
+ else:
1258
+ intVertex = None
1259
+ if intVertex:
1260
+ if reverse:
1261
+ return Edge.ByVertices([eva, intVertex], tolerance=tolerance, silent=True)
1262
+ else:
1263
+ return Edge.ByVertices([sva, intVertex], tolerance=tolerance, silent=True)
1264
+ else:
1265
+ return None
1266
+
1157
1267
  sva = Edge.StartVertex(edgeA)
1158
1268
  eva = Edge.EndVertex(edgeA)
1159
- intVertex = Edge.Intersect2D(edgeA, edgeB)
1269
+ intVertex = Topology.Intersect(edgeA, edgeB)
1160
1270
  if intVertex and (Vertex.IsInternal(intVertex, edgeA)):
1161
1271
  if reverse:
1162
1272
  return Edge.ByVertices([eva, intVertex], tolerance=tolerance, silent=True)
@@ -1195,7 +1305,7 @@ class Edge():
1195
1305
  if not Topology.IsInstance(edge, "Edge"):
1196
1306
  print("Edge.TrimByEdge2D - Error: The input edge parameter is not a valid topologic edge. Returning None.")
1197
1307
  return None
1198
- if not origin:
1308
+ if not Topology.IsInstance(origin, "Vertex"):
1199
1309
  origin = edge.StartVertex()
1200
1310
  if not Topology.IsInstance(origin, "Vertex"):
1201
1311
  print("Edge.TrimByEdge2D - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
topologicpy/Face.py CHANGED
@@ -341,7 +341,7 @@ class Face():
341
341
  print("Face.ByShell - Error: The input shell parameter is not a valid toplogic shell. Returning None.")
342
342
  return None
343
343
 
344
- if origin == None:
344
+ if not Topology.IsInstance(origin, "Vertex"):
345
345
  origin = Topology.Centroid(shell)
346
346
  if not Topology.IsInstance(origin, "Vertex"):
347
347
  print("Face.ByShell - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
@@ -683,7 +683,7 @@ class Face():
683
683
  """
684
684
  from topologicpy.Topology import Topology
685
685
  from topologicpy.Vertex import Vertex
686
- if not origin:
686
+ if not Topology.IsInstance(origin, "Vertex"):
687
687
  origin = Vertex.Origin()
688
688
 
689
689
  c = Face.Circle(origin=origin, radius=radius, sides=sides, direction=[0, 0, 1], placement="center", tolerance=tolerance)
@@ -780,7 +780,7 @@ class Face():
780
780
  return round(compactness, mantissa)
781
781
 
782
782
  @staticmethod
783
- def CompassAngle(face, north: list = None, mantissa: int = 6) -> float:
783
+ def CompassAngle(face, north: list = None, mantissa: int = 6, tolerance: float = 0.0001) -> float:
784
784
  """
785
785
  Returns the horizontal compass angle in degrees between the normal vector of the input face and the input vector. The angle is measured in counter-clockwise fashion. Only the first two elements of the vectors are considered.
786
786
 
@@ -809,7 +809,7 @@ class Face():
809
809
  if not north:
810
810
  north = Vector.North()
811
811
  dirA = Face.NormalAtParameters(face,mantissa=mantissa)
812
- return Vector.CompassAngle(vectorA=dirA, vectorB=north, mantissa=mantissa)
812
+ return Vector.CompassAngle(vectorA=dirA, vectorB=north, mantissa=mantissa, tolerance=tolerance)
813
813
 
814
814
  @staticmethod
815
815
  def Edges(face) -> list:
@@ -1216,7 +1216,7 @@ class Face():
1216
1216
  return inverted_face
1217
1217
 
1218
1218
  @staticmethod
1219
- def IsCoplanar(faceA, faceB, tolerance: float = 0.0001) -> bool:
1219
+ def IsCoplanar(faceA, faceB, mantissa: int = 6, tolerance: float = 0.0001) -> bool:
1220
1220
  """
1221
1221
  Returns True if the two input faces are coplanar. Returns False otherwise.
1222
1222
 
@@ -1226,13 +1226,10 @@ class Face():
1226
1226
  The first input face.
1227
1227
  faceB : topologic_core.Face
1228
1228
  The second input face
1229
+ mantissa : int , optional
1230
+ The length of the desired mantissa. The default is 6.
1229
1231
  tolerance : float , optional
1230
- The desired tolerance. The deafault is 0.0001.
1231
-
1232
- Raises
1233
- ------
1234
- Exception
1235
- Raises an exception if the angle between the two input faces cannot be determined.
1232
+ The desired tolerance. The default is 0.0001.
1236
1233
 
1237
1234
  Returns
1238
1235
  -------
@@ -1240,8 +1237,23 @@ class Face():
1240
1237
  True if the two input faces are coplanar. False otherwise.
1241
1238
 
1242
1239
  """
1243
- from topologicpy.Vector import Vector
1244
1240
  from topologicpy.Topology import Topology
1241
+ import warnings
1242
+
1243
+ try:
1244
+ import numpy as np
1245
+ except:
1246
+ print("Face.IsCoplanar - Information: Installing required numpy library.")
1247
+ try:
1248
+ os.system("pip install numpy")
1249
+ except:
1250
+ os.system("pip install numpy --user")
1251
+ try:
1252
+ import numpy as np
1253
+ print("Face.IsCoplanar - Information: numpy library installed successfully.")
1254
+ except:
1255
+ warnings.warn("Face.IsCoplanar - Error:: Could not import numpy. Please install the numpy library manually. Returning None.")
1256
+ return None
1245
1257
 
1246
1258
  if not Topology.IsInstance(faceA, "Face"):
1247
1259
  print("Face.IsInide - Error: The input faceA parameter is not a valid topologic face. Returning None.")
@@ -1249,9 +1261,25 @@ class Face():
1249
1261
  if not Topology.IsInstance(faceB, "Face"):
1250
1262
  print("Face.IsInide - Error: The input faceB parameter is not a valid topologic face. Returning None.")
1251
1263
  return None
1252
- dirA = Face.NormalAtParameters(faceA, 0.5, 0.5, "xyz", 3)
1253
- dirB = Face.NormalAtParameters(faceB, 0.5, 0.5, "xyz", 3)
1254
- return Vector.IsCollinear(dirA, dirB)
1264
+
1265
+ def normalize_plane_coefficients(plane):
1266
+ norm = np.linalg.norm(plane[:3]) # Normalize using the first three coefficients (a, b, c)
1267
+ if norm == 0:
1268
+ return plane
1269
+ return [coef / norm for coef in plane]
1270
+
1271
+ def are_planes_coplanar(plane1, plane2, tolerance):
1272
+ normalized_plane1 = normalize_plane_coefficients(plane1)
1273
+ normalized_plane2 = normalize_plane_coefficients(plane2)
1274
+ return np.allclose(normalized_plane1, normalized_plane2, atol=tolerance)
1275
+
1276
+ eq_a = Face.PlaneEquation(faceA, mantissa=mantissa)
1277
+ plane_a = [eq_a['a'], eq_a['b'], eq_a['c'], eq_a['d']]
1278
+ plane_a = normalize_plane_coefficients(plane_a)
1279
+ eq_b = Face.PlaneEquation(faceB, mantissa=mantissa)
1280
+ plane_b = [eq_b['a'], eq_b['b'], eq_b['c'], eq_b['d']]
1281
+ plane_b = normalize_plane_coefficients(plane_b)
1282
+ return are_planes_coplanar(plane_a, plane_b, tolerance=tolerance)
1255
1283
 
1256
1284
  @staticmethod
1257
1285
  def Isovist(face, vertex, obstacles: list = [], direction: list = [0,1,0], fov: float = 360, mantissa: int = 6, tolerance: float = 0.0001):