topologicpy 0.7.77__py3-none-any.whl → 0.7.78__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/Plotly.py CHANGED
@@ -474,9 +474,9 @@ class Plotly:
474
474
  x = []
475
475
  y = []
476
476
  z = []
477
- sizeList = []
477
+ sizes = []
478
478
  labels = []
479
- groupList = []
479
+ colors = []
480
480
  label = ""
481
481
  group = None
482
482
  if colorKey or sizeKey or labelKey or groupKey:
@@ -493,58 +493,54 @@ class Plotly:
493
493
  else:
494
494
  minGroup = 0
495
495
  maxGroup = 1
496
+ n = len(str(len(vertices)))
496
497
  for m, v in enumerate(vertices):
497
498
  x.append(v[0])
498
499
  y.append(v[1])
499
500
  z.append(v[2])
500
501
  label = " "
501
502
  group = None
503
+ colors.append(Color.AnyToHex(color))
504
+ labels.append("Vertex_"+str(m+1).zfill(n))
505
+ sizes.append(size)
502
506
  if len(dictionaries) > 0:
503
507
  d = dictionaries[m]
504
508
  if d:
505
509
  if not colorKey == None:
506
- d_color = Dictionary.ValueAtKey(d, key=colorKey) or color
507
- color = Color.AnyToHex(d_color)
508
- groupList.append(color)
510
+ colors[m] = Dictionary.ValueAtKey(d, key=colorKey) or colors[m]
509
511
  if not labelKey == None:
510
- label = str(Dictionary.ValueAtKey(d, key=labelKey)) or " "
512
+ labels[m] = str(Dictionary.ValueAtKey(d, key=labelKey)) or labels[m]
511
513
  if not sizeKey == None:
512
- size = Dictionary.ValueAtKey(d, key=sizeKey) or size
514
+ sizes[m] = Dictionary.ValueAtKey(d, key=sizeKey) or sizes[m]
513
515
  if not groupKey == None:
514
- group = Dictionary.ValueAtKey(d, key=groupKey) or " "
516
+ colors[m] = Dictionary.ValueAtKey(d, key=groupKey) or colors[m]
515
517
  try:
516
- if group == " ":
517
- pass
518
- elif type(group) == int or type(group) == float:
519
- if group < minGroup:
520
- group = minGroup
521
- if group > maxGroup:
522
- group = maxGroup
523
- color = Color.ByValueInRange(group, minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
524
- color = Color.AnyToHEX(color)
525
- groupList.append(Color.AnyToHex(color))
518
+ if type(colors[m]) == int or type(colors[m]) == float:
519
+ if colors[m] < minGroup:
520
+ colors[m] = minGroup
521
+ if colors[m] > maxGroup:
522
+ colors[m] = maxGroup
523
+ temp_color = Color.ByValueInRange(colors[m], minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
524
+ colors[m] = Color.AnyToHEX(temp_color)
526
525
 
527
526
  else:
528
- color = Color.ByValueInRange(groups.index(group), minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
529
- color = Color.AnyToHex(color)
530
- groupList.append(Color.AnyToHex(color))
527
+ temp_color = Color.ByValueInRange(groups.index(colors[m]), minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
528
+ colors[m] = Color.AnyToHex(temp_color)
531
529
  except:
532
530
  #groupList.append(Color.AnyToHex([0,0,0]))
533
531
  pass
534
- labels.append(label)
535
- sizeList.append(size)
536
532
  else:
537
533
  for v in vertices:
538
534
  x.append(v[0])
539
535
  y.append(v[1])
540
536
  z.append(v[2])
541
537
 
542
- if len(list(set(groupList))) < 2:
543
- groupList = Color.AnyToHex(color)
538
+ if len(list(set(colors))) < 2:
539
+ colors = Color.AnyToHex(color)
544
540
  if len(labels) < 1:
545
- labels = ""
546
- if len(sizeList) < 1:
547
- sizeList = size
541
+ labels = "Vertex_1"
542
+ if len(sizes) < 1:
543
+ sizes = size
548
544
  if showVertexLabel == True:
549
545
  mode = "markers+text"
550
546
  else:
@@ -554,7 +550,9 @@ class Plotly:
554
550
  z=z,
555
551
  name=legendLabel,
556
552
  showlegend=showLegend,
557
- marker=dict(color=groupList, size=sizeList, opacity=1),
553
+ marker=dict(color=colors,
554
+ size=sizes,
555
+ opacity=1),
558
556
  mode=mode,
559
557
  legendgroup=legendGroup,
560
558
  legendrank=legendRank,
@@ -601,13 +599,17 @@ class Plotly:
601
599
  else:
602
600
  minGroup = 0
603
601
  maxGroup = 1
602
+
604
603
 
605
604
  if colorKey or widthKey or labelKey or groupKey:
606
605
  keys = [x for x in [colorKey, widthKey, labelKey, groupKey] if not x == None]
607
606
  temp_dict = Helper.ClusterByKeys(edges, dictionaries, keys, silent=False)
608
607
  dict_clusters = temp_dict["dictionaries"]
609
608
  elements_clusters = temp_dict['elements']
609
+ n = len(str(len(elements_clusters)))
610
+ labels = []
610
611
  for j, elements_cluster in enumerate(elements_clusters):
612
+ labels.append("Edge_"+str(j+1).zfill(n))
611
613
  d = dict_clusters[j][0] # All dicitonaries have same values in dictionaries, so take first one.
612
614
  if d:
613
615
  if not colorKey == None:
@@ -616,7 +618,9 @@ class Plotly:
616
618
  if not labelKey == None:
617
619
  label = str(Dictionary.ValueAtKey(d, key=labelKey)) or ""
618
620
  if not widthKey == None:
619
- width = Dictionary.ValueAtKey(d, key=widthKey) or width
621
+ e_width = Dictionary.ValueAtKey(d, key=widthKey)
622
+ if not e_width == None:
623
+ width = e_width
620
624
  if not groupKey == None:
621
625
  group = Dictionary.ValueAtKey(d, key=groupKey)
622
626
  if not group == None:
@@ -629,6 +633,7 @@ class Plotly:
629
633
  else:
630
634
  d_color = Color.ByValueInRange(groups.index(group), minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
631
635
  color = d_color
636
+
632
637
  x = []
633
638
  y = []
634
639
  z = []
@@ -639,15 +644,19 @@ class Plotly:
639
644
  y+=[sv[1], ev[1], None] # y-coordinates of edge ends
640
645
  z+=[sv[2], ev[2], None] # z-coordinates of edge ends
641
646
  if showEdgeLabel == True:
642
- mode = "lines+text"
647
+ mode = "markers+lines+text"
648
+ else:
649
+ mode = "markers+lines"
650
+ if isinstance(width, list):
651
+ marker_width = width[0]*0.25
643
652
  else:
644
- mode = "lines"
653
+ marker_width = width*0.25
645
654
  trace = go.Scatter3d(x=x,
646
655
  y=y,
647
656
  z=z,
648
657
  name=label,
649
658
  showlegend=showLegend,
650
- marker_size=0,
659
+ marker=dict(symbol="circle", size=marker_width),
651
660
  mode=mode,
652
661
  line=dict(color=color, width=width),
653
662
  legendgroup=legendGroup,
@@ -869,9 +878,9 @@ class Plotly:
869
878
  k = []
870
879
  labels = []
871
880
  groupList = []
872
- faceColorList = []
873
881
  label = ""
874
882
  group = ""
883
+ color = Color.AnyToHex(color)
875
884
  if colorKey or labelKey or groupKey:
876
885
  if groups:
877
886
  if len(groups) > 0:
@@ -886,36 +895,40 @@ class Plotly:
886
895
  else:
887
896
  minGroup = 0
888
897
  maxGroup = 1
898
+ n = len(str(len(faces)))
889
899
  for m, f in enumerate(faces):
890
900
  i.append(f[0])
891
901
  j.append(f[1])
892
902
  k.append(f[2])
893
903
  label = ""
894
904
  group = None
905
+ groupList.append(Color.AnyToHex(color)) # Store a default color for that face
906
+ labels.append("Mace_"+str(m+1).zfill(n))
895
907
  if len(dictionaries) > 0:
896
908
  d = dictionaries[m]
897
909
  if d:
910
+ if not colorKey == None:
911
+ d_color = Dictionary.ValueAtKey(d, key=colorKey) or color
912
+ groupList[m] = Color.AnyToHex(d_color) #Replace the default color by the dictionary color.
898
913
  if not labelKey == None:
899
- label = str(Dictionary.ValueAtKey(d, key=labelKey)) or ""
914
+ label = Dictionary.ValueAtKey(d, key=labelKey)
915
+ if not label == None:
916
+ labels[m] = str(label) # Replace the default label with the dictionary label
900
917
  if not groupKey == None:
901
918
  group = Dictionary.ValueAtKey(d, key=groupKey) or None
902
919
 
903
920
  if group == None:
904
- f_color = Color.AnyToHex(color)
905
- groupList.append(f_color)
921
+ pass # do nothing because the default color will be used.
906
922
  elif type(group) == int or type(group) == float:
907
923
  if group < minGroup:
908
924
  group = minGroup
909
925
  if group > maxGroup:
910
926
  group = maxGroup
911
927
  f_color = Color.ByValueInRange(group, minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
912
- f_color = Color.AnyToHex(f_color)
913
- groupList.append(f_color)
928
+ groupList[m] = Color.AnyToHex(f_color) # Replace the default color by the group value.
914
929
  else:
915
930
  f_color = Color.ByValueInRange(groups.index(group), minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
916
- f_color = Color.AnyToHex(f_color)
917
- groupList.append(f_color)
918
- labels.append(label)
931
+ groupList[m] = Color.AnyToHex(f_color)
919
932
  else:
920
933
  for f in faces:
921
934
  i.append(f[0])
@@ -926,7 +939,6 @@ class Plotly:
926
939
  groupList = None
927
940
  if len(labels) < 1:
928
941
  labels = ""
929
- color = Color.AnyToHex(color)
930
942
  fData = go.Mesh3d(
931
943
  x = x,
932
944
  y = y,
@@ -1030,6 +1042,7 @@ class Plotly:
1030
1042
  if edgeColorKey or edgeWidthKey or edgeLabelKey or edgeGroupKey:
1031
1043
  for tp_edge in tp_edges:
1032
1044
  e_dictionaries.append(Topology.Dictionary(tp_edge))
1045
+
1033
1046
  e_cluster = Cluster.ByTopologies(tp_edges)
1034
1047
  geo = Topology.Geometry(e_cluster, mantissa=mantissa)
1035
1048
  vertices = geo['vertices']
topologicpy/Topology.py CHANGED
@@ -1509,7 +1509,9 @@ class Topology():
1509
1509
  faces : list , optional
1510
1510
  The input list of faces in the form of [i, j, k, l, ...] where the items in the list are vertex indices. The face is assumed to be closed to the last vertex is connected to the first vertex automatically.
1511
1511
  topologyType : str , optional
1512
- The desired topology type. The options are: "Vertex", "Edge", "Wire", "Face", "Shell", "Cell", "CellComplex". If set to None, a "Cluster" will be returned. The default is None.
1512
+ The desired highest topology type. The options are: "Vertex", "Edge", "Wire", "Face", "Shell", "Cell", "CellComplex".
1513
+ It is case insensitive. If any of these options are selected, the returned topology will only contain this type either a single topology
1514
+ or as a Cluster of these types of topologies. If set to None, a "Cluster" will be returned of vertices, edges, and/or faces. The default is None.
1513
1515
  tolerance : float , optional
1514
1516
  The desired tolerance. The default is 0.0001.
1515
1517
  silent : bool , optional
@@ -1555,7 +1557,7 @@ class Topology():
1555
1557
  output = Shell.ByFaces(faces, tolerance=tolerance) # This can return a list
1556
1558
  if Topology.IsInstance(output, "Shell"):
1557
1559
  return output
1558
- elif topologyType == None:
1560
+ elif topologyType == None or topologyType== "face":
1559
1561
  output = Cluster.ByTopologies(faces)
1560
1562
 
1561
1563
  return output
@@ -1587,15 +1589,20 @@ class Topology():
1587
1589
  topologyType = topologyType.lower()
1588
1590
 
1589
1591
  if topologyType == "vertex":
1590
- if len(topVerts) >= 1:
1592
+ if len(topVerts) == 0:
1593
+ return None
1594
+ if len(topVerts) == 1:
1591
1595
  return topVerts[0]
1592
1596
  else:
1593
- return None
1597
+ return Cluster.ByTopologies(topVerts)
1594
1598
  elif topologyType == "edge":
1595
- if len(edges) >= 1 and len(vertices) >= 2:
1599
+ if len(edges) == 0:
1600
+ return None
1601
+ if len(edges) == 1 and len(vertices) >= 2:
1596
1602
  return Edge.ByVertices(topVerts[edges[0][0]], topVerts[edges[0][1]], tolerance=tolerance)
1597
1603
  else:
1598
- return None
1604
+ topEdges = [Edge.ByVertices([topVerts[e[0]], topVerts[e[1]]], tolerance=tolerance) for e in edges]
1605
+ return Cluster.ByTopologies(topEdges)
1599
1606
 
1600
1607
  if topologyType == "wire" and edges:
1601
1608
  topEdges = [Edge.ByVertices([topVerts[e[0]], topVerts[e[1]]], tolerance=tolerance) for e in edges]
@@ -3635,6 +3642,325 @@ class Topology():
3635
3642
  """
3636
3643
  return topologic.Topology.ByOcctShape(occtShape, "")
3637
3644
 
3645
+ @staticmethod
3646
+ def ByPDFFile(file, wires=False, faces=False, includeTypes=[], excludeTypes=[], edgeColorKey="edge_color", edgeWidthKey="edge_width", faceColorKey="face_color", faceOpacityKey="face_opacity", tolerance=0.0001, silent=False):
3647
+ """
3648
+ Import PDF file and convert its entities to topologies.
3649
+
3650
+ Parameters
3651
+ ----------
3652
+ file : file
3653
+ The input PDF file
3654
+ wires : bool , optional
3655
+ If set to True, wires will be constructed when possible. The default is True.
3656
+ faces : bool , optional
3657
+ If set to True, faces will be constructed when possible. The default is True.
3658
+ includeTypes : list , optional
3659
+ A list of PDF object types to include in the returned result. The default is [] which means all PDF objects will be included.
3660
+ The possible strings to include in this list are: ["line", "curve", "rectangle", "quadrilateral"]
3661
+ excludeTypes : list , optional
3662
+ A list of PDF object types to exclude from the returned result. The default is [] which mean no PDF object type will be excluded.
3663
+ The possible strings to include in this list are: ["line", "curve", "rectangle", "quadrilateral"]
3664
+ edgeColorKey : str , optional
3665
+ The dictionary key under which to store the edge color. The default is None.
3666
+ edgeWidthKey : str , optional
3667
+ The dictionary key under which to store the edge width. The default is None.
3668
+ faceColorKey : str , optional
3669
+ The dictionary key under which to store the face color. The default is None.
3670
+ faceOpacityKey : str , optional
3671
+ The dictionary key under which to store the face opacity. The default is None.
3672
+ tolerance : float , optional
3673
+ The desired tolerance. The default is 0.0001.
3674
+ silent : bool , optional
3675
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
3676
+
3677
+ Returns
3678
+ -------
3679
+ list
3680
+ A list of Topologic entities (edges, wires, faces, clusters) with attached dictionaries.
3681
+
3682
+ """
3683
+ from topologicpy.Vertex import Vertex
3684
+ from topologicpy.Edge import Edge
3685
+ from topologicpy.Wire import Wire
3686
+ from topologicpy.Face import Face
3687
+ from topologicpy.Cluster import Cluster
3688
+ from topologicpy.Dictionary import Dictionary
3689
+ import os
3690
+ import warnings
3691
+
3692
+ def interpolate_bezier(control_points, num_points=50):
3693
+ """
3694
+ Interpolate a cubic Bezier curve given control points.
3695
+
3696
+ Args:
3697
+ control_points (list): List of control points (pymupdf.Point objects).
3698
+ num_points (int): Number of points to interpolate along the curve.
3699
+
3700
+ Returns:
3701
+ list: A list of interpolated points (pymupdf.Point objects).
3702
+ """
3703
+ p0, p1, p2, p3 = control_points
3704
+ points = [
3705
+ pymupdf.Point(
3706
+ (1 - t)**3 * p0.x + 3 * (1 - t)**2 * t * p1.x + 3 * (1 - t) * t**2 * p2.x + t**3 * p3.x,
3707
+ (1 - t)**3 * p0.y + 3 * (1 - t)**2 * t * p1.y + 3 * (1 - t) * t**2 * p2.y + t**3 * p3.y
3708
+ )
3709
+ for t in (i / num_points for i in range(num_points + 1))
3710
+ ]
3711
+ return points
3712
+
3713
+ def map_types(type_list):
3714
+ type_mapping = {
3715
+ "line": "l",
3716
+ "curve": "c",
3717
+ "rect": "re",
3718
+ "quad": "qu"
3719
+ }
3720
+ return [type_mapping[key] for key in type_mapping if any(key in item for item in type_list)]
3721
+
3722
+ def remove_overlap(list1, list2):
3723
+ set1, set2 = set(list1), set(list2)
3724
+ # Remove common elements from both sets
3725
+ set1 -= set2
3726
+ set2 -= set(list1)
3727
+ return list(set1), list(set2)
3728
+
3729
+ try:
3730
+ import pymupdf # PyMuPDF
3731
+ except:
3732
+ if not silent:
3733
+ print("Topology.ByPDFFile - Warning: Installing required PyMuPDF library.")
3734
+ try:
3735
+ os.system("pip install PyMuPDF")
3736
+ except:
3737
+ os.system("pip install PyMuPDF --user")
3738
+ try:
3739
+ import pymupdf
3740
+ if not silent:
3741
+ print("Topology.ByPDFFile - Information: PyMUDF library installed correctly.")
3742
+ except:
3743
+ if not silent:
3744
+ warnings.warn("Topology.ByPDFFile - Error: Could not import PyMuPDF. Please try to install PyMuPDF manually. Returning None.")
3745
+ return None
3746
+ if not file:
3747
+ if not silent:
3748
+ print("Topology.ByPDFFile - Error: Could not open the PDF file. Returning None.")
3749
+ return None
3750
+
3751
+ if includeTypes == None:
3752
+ includeTypes = []
3753
+ if excludeTypes == None:
3754
+ excludeTypes = []
3755
+ includeTypes = [item for item in includeTypes if isinstance(item, str)]
3756
+ excludeTypes = [item for item in excludeTypes if isinstance(item, str)]
3757
+ in_types = map_types([c.lower() for c in includeTypes])
3758
+ ex_types = map_types([c.lower() for c in excludeTypes])
3759
+ in_types_1, ex_types_1 = remove_overlap(in_types, ex_types)
3760
+ if not len(in_types_1) == len(in_types) or not len(ex_types_1) == len(ex_types):
3761
+ if not silent:
3762
+ print("Topology.ByPDFFile - Warning: Ther includeTypes and excludeTypes input parameters contain overlapping elements. These have been excluded.")
3763
+ in_types = in_types_1
3764
+
3765
+ topologic_entities = []
3766
+ for page_num, page in enumerate(file, 1):
3767
+ matrix = pymupdf.Matrix(1, 1) # Identity matrix for default transformation
3768
+ paths = page.get_drawings()
3769
+ for path in paths:
3770
+ if not path.get("stroke_opacity") == 0:
3771
+ close = path.get("closePath")
3772
+ components = []
3773
+ edge_color = path.get("color", [0, 0, 0]) or [0, 0, 0]
3774
+ edge_color = [int(255 * c) for c in edge_color] # Convert stroke color to 0-255 range
3775
+ edge_width = path.get("width", 1) or 1
3776
+ face_color = path.get("fill", [1, 1, 1]) or [1,1,1]
3777
+ face_color = [int(255 * c) for c in face_color]
3778
+ face_opacity = path.get("fill_opacity", 1) or 1
3779
+
3780
+ # Create the dictionary for line width, color, fill color, and fill opacity
3781
+ keys = [edgeWidthKey, edgeColorKey, faceColorKey, faceOpacityKey]
3782
+ values = [
3783
+ edge_width,
3784
+ edge_color, # Convert stroke color to 0-255 range
3785
+ face_color, # Convert fill color to 0-255 range
3786
+ face_opacity
3787
+ ]
3788
+ dictionary = Dictionary.ByKeysValues(keys, values)
3789
+ items = path.get("items") or []
3790
+ for it, item in enumerate(items):
3791
+ if (item[0] in in_types or len(in_types) == 0) and (not item[0] in ex_types):
3792
+ if item[0] == "l": # Line
3793
+ start_point = pymupdf.Point(item[1][0], item[1][1]).transform(matrix)
3794
+ end_point = pymupdf.Point(item[2][0], item[2][1]).transform(matrix)
3795
+ start_vertex = Vertex.ByCoordinates(start_point.x, -start_point.y, 0)
3796
+ end_vertex = Vertex.ByCoordinates(end_point.x, -end_point.y, 0)
3797
+ d = Vertex.Distance(start_vertex, end_vertex)
3798
+ if d > tolerance:
3799
+ edge = Edge.ByStartVertexEndVertex(start_vertex, end_vertex, tolerance=tolerance, silent=silent)
3800
+ if not edge == None:
3801
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3802
+ components.append(edge)
3803
+ if it == 0:
3804
+ v_f_p = pymupdf.Point(item[1][0], item[1][1]).transform(matrix)
3805
+ very_first_vertex = Vertex.ByCoordinates(v_f_p.x, -v_f_p.y, 0)
3806
+ if it == len(items)-1 and path.get("closePath", False) == True:
3807
+ v_l_p = pymupdf.Point(item[2][0], item[2][1]).transform(matrix)
3808
+ very_last_vertex = Vertex.ByCoordinates(v_l_p.x, -v_l_p.y, 0)
3809
+ edge = Edge.ByStartVertexEndVertex(very_last_vertex, very_first_vertex, tolerance=tolerance, silent=True)
3810
+ if not edge == None:
3811
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3812
+ components.append(edge)
3813
+
3814
+ elif item[0] == "c": # Bezier curve (approximated by segments)
3815
+ control_points = [pymupdf.Point(p[0], p[1]).transform(matrix) for p in item[1:]]
3816
+ bezier_points = interpolate_bezier(control_points)
3817
+ vertices = [Vertex.ByCoordinates(pt.x, -pt.y, 0) for pt in bezier_points]
3818
+ for i in range(len(vertices)-1):
3819
+ start_vertex = vertices[i]
3820
+ end_vertex = vertices[i+1]
3821
+ edge = Edge.ByStartVertexEndVertex(start_vertex, end_vertex, tolerance=tolerance, silent=False)
3822
+ if not edge == None:
3823
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3824
+ components.append(edge)
3825
+ elif item[0] == "re": # Rectangle
3826
+ x0, y0, x1, y1 = item[1]
3827
+ vertices = [
3828
+ Vertex.ByCoordinates(x0, -y0, 0),
3829
+ Vertex.ByCoordinates(x1, -y0, 0),
3830
+ Vertex.ByCoordinates(x1, -y1, 0),
3831
+ Vertex.ByCoordinates(x0, -y1, 0)
3832
+ ]
3833
+ for i in range(len(vertices)-1):
3834
+ start_vertex = vertices[i]
3835
+ end_vertex = vertices[i+1]
3836
+ edge = Edge.ByStartVertexEndVertex(start_vertex, end_vertex, tolerance=tolerance, silent=False)
3837
+ if not edge == None:
3838
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3839
+ components.append(edge)
3840
+ edge = Edge.ByStartVertexEndVertex(vertices[-1], vertices[0], tolerance=tolerance, silent=False)
3841
+ if not edge == None:
3842
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3843
+ components.append(edge)
3844
+
3845
+ elif item[0] == "qu": # Quadrilateral
3846
+ quad_points = [pymupdf.Point(pt[0], pt[1]).transform(matrix) for pt in item[1]]
3847
+ vertices = [Vertex.ByCoordinates(pt.x, -pt.y, 0) for pt in quad_points]
3848
+ for i in range(len(vertices)-1):
3849
+ start_vertex = vertices[i]
3850
+ end_vertex = vertices[i+1]
3851
+ edge = Edge.ByStartVertexEndVertex(start_vertex, end_vertex, tolerance=tolerance, silent=False)
3852
+ if not edge == None:
3853
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3854
+ components.append(edge)
3855
+ edge = Edge.ByStartVertexEndVertex(vertices[-1], vertices[0], tolerance=tolerance, silent=False)
3856
+ if not edge == None:
3857
+ edge = Topology.SetDictionary(edge, dictionary) # Set dictionary
3858
+ components.append(edge)
3859
+
3860
+ if len(components) > 0:
3861
+ if len(components) == 1:
3862
+ tp_object = components[0]
3863
+ elif len(components) > 1:
3864
+ tp_object = Cluster.ByTopologies(components)
3865
+ if wires == True or faces == True:
3866
+ tp_object = Topology.SelfMerge(tp_object)
3867
+ if faces == True:
3868
+ if Topology.IsInstance(tp_object, "wire"):
3869
+ if Wire.IsClosed(tp_object):
3870
+ tp_object = Face.ByWire(tp_object, silent=True) or tp_object
3871
+ tp_object = Topology.SetDictionary(tp_object, dictionary)
3872
+ edges = Topology.Edges(tp_object)
3873
+ for edge in edges:
3874
+ edge = Topology.SetDictionary(edge, dictionary)
3875
+ topologic_entities.append(tp_object)
3876
+
3877
+ return topologic_entities
3878
+
3879
+ @staticmethod
3880
+ def ByPDFPath(path, wires=False, faces=False, includeTypes=[], excludeTypes=[], edgeColorKey="edge_color", edgeWidthKey="edge_width", faceColorKey="face_color", faceOpacityKey="face_opacity", tolerance=0.0001, silent=False):
3881
+ """
3882
+ Import PDF file and convert its entities to topologies.
3883
+
3884
+ Parameters
3885
+ ----------
3886
+ path : path
3887
+ The input path to the PDF file
3888
+ wires : bool , optional
3889
+ If set to True, wires will be constructed when possible. The default is True.
3890
+ faces : bool , optional
3891
+ If set to True, faces will be constructed when possible. The default is True.
3892
+ includeTypes : list , optional
3893
+ A list of PDF object types to include in the returned result. The default is [] which means all PDF objects will be included.
3894
+ The possible strings to include in this list are: ["line", "curve", "rectangle", "quadrilateral"]
3895
+ excludeTypes : list , optional
3896
+ A list of PDF object types to exclude from the returned result. The default is [] which mean no PDF object type will be excluded.
3897
+ The possible strings to include in this list are: ["line", "curve", "rectangle", "quadrilateral"]
3898
+ edgeColorKey : str , optional
3899
+ The dictionary key under which to store the edge color. The default is None.
3900
+ edgeWidthKey : str , optional
3901
+ The dictionary key under which to store the edge width. The default is None.
3902
+ faceColorKey : str , optional
3903
+ The dictionary key under which to store the face color. The default is None.
3904
+ faceOpacityKey : str , optional
3905
+ The dictionary key under which to store the face opacity. The default is None.
3906
+ tolerance : float , optional
3907
+ The desired tolerance. The default is 0.0001.
3908
+ silent : bool , optional
3909
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
3910
+
3911
+ Returns
3912
+ -------
3913
+ list
3914
+ A list of Topologic entities (edges, wires, faces, clusters) with attached dictionaries.
3915
+
3916
+ """
3917
+ import os
3918
+ import warnings
3919
+ try:
3920
+ import pymupdf # PyMuPDF
3921
+ except:
3922
+ if not silent:
3923
+ print("Topology.ByPDFPath - Warning: Installing required PyMuPDF library.")
3924
+ try:
3925
+ os.system("pip install PyMuPDF")
3926
+ except:
3927
+ os.system("pip install PyMuPDF --user")
3928
+ try:
3929
+ import pymupdf
3930
+ if not silent:
3931
+ print("Topology.ByPDFPath - Information: PyMUDF library installed correctly.")
3932
+ except:
3933
+ if not silent:
3934
+ warnings.warn("Topology.ByPDFPath - Error: Could not import PyMuPDF. Please try to install PyMuPDF manually. Returning None.")
3935
+ return None
3936
+ if not isinstance(path, str):
3937
+ if not silent:
3938
+ print("Topology.ByPDFPath - Error: the input path is not a valid path. Returning None.")
3939
+ return None
3940
+ if not os.path.exists(path):
3941
+ if not silent:
3942
+ print("Topology.ByPDFPath - Error: The specified path does not exist. Returning None.")
3943
+ return None
3944
+ pdf_file = pymupdf.open(path)
3945
+ if not pdf_file:
3946
+ if not silent:
3947
+ print("Topology.ByPDFPath - Error: Could not open the PDF file. Returning None.")
3948
+ return None
3949
+
3950
+ topologies = Topology.ByPDFFile(file=pdf_file,
3951
+ wires=wires,
3952
+ faces=faces,
3953
+ includeTypes = includeTypes,
3954
+ excludeTypes = excludeTypes,
3955
+ edgeColorKey=edgeColorKey,
3956
+ edgeWidthKey=edgeWidthKey,
3957
+ faceColorKey=faceColorKey,
3958
+ faceOpacityKey=faceOpacityKey,
3959
+ tolerance=tolerance,
3960
+ silent=silent)
3961
+ pdf_file.close()
3962
+ return topologies
3963
+
3638
3964
  @staticmethod
3639
3965
  def ByBREPString(string):
3640
3966
  """
@@ -7697,6 +8023,8 @@ class Topology():
7697
8023
  if not silent:
7698
8024
  print("Topology.Show - Error: the input topologies parameter does not contain any valid topology. Returning None.")
7699
8025
  return None
8026
+ if camera[0] == 0 and camera[1] == 0 and up == [0,0,1]:
8027
+ up = [0,1,0] #default to positive Y axis being up if looking down or up at the XY plane
7700
8028
  data = []
7701
8029
  for topology in new_topologies:
7702
8030
  if Topology.IsInstance(topology, "Graph"):
topologicpy/Vertex.py CHANGED
@@ -34,6 +34,57 @@ except:
34
34
  warnings.warn("Vertex - Error: Could not import numpy.")
35
35
 
36
36
  class Vertex():
37
+ @staticmethod
38
+ def AlignCoordinates(vertex, xList=None, yList=None, zList=None, transferDictionary=False, mantissa=6, silent=False):
39
+ """
40
+ Aligns the coordinates of the input vertex with the list of x,y, and z coordinates.
41
+
42
+ Parameters
43
+ ----------
44
+ vertex : topologic_core.Vertex
45
+ The input vertex.
46
+ xList : list , optional
47
+ The input numerical list of x-coordinates. The default is None.
48
+ yList : list , optional
49
+ The input numerical list of y-coordinates. The default is None.
50
+ zList : list , optional
51
+ The input numerical list of z-coordinates. The default is None.
52
+ transferDictionary : bool , optional
53
+ if set to True, the dictionary of the input vertex is transferred to the new vertex.
54
+ mantissa : int , optional
55
+ The desired length of the mantissa. The default is 6.
56
+ silent : bool , optional
57
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
58
+
59
+ Returns
60
+ -------
61
+ topologic_core.Vertex
62
+ The created vertex aligned to the input list of x,y, and z coordinates.
63
+
64
+ """
65
+ from topologicpy.Topology import Topology
66
+ from topologicpy.Helper import Helper
67
+
68
+ if not Topology.IsInstance(vertex, "vertex"):
69
+ if not silent:
70
+ print("Vertex.AlignCoordinates - Error: The input vertex parameter is not a topologic vertex. Returning None.")
71
+ return None
72
+
73
+ closest_x, closest_y, closest_z = Vertex.Coordinates(vertex, mantissa=mantissa)
74
+ if isinstance(xList, list):
75
+ if len(xList) > 0:
76
+ closest_x = xList[Helper.ClosestMatch(closest_x, xList)]
77
+ if isinstance(yList, list):
78
+ if len(yList) > 0:
79
+ closest_y = yList[Helper.ClosestMatch(closest_y, yList)]
80
+ if isinstance(zList, list):
81
+ if len(zList) > 0:
82
+ closest_z = zList[Helper.ClosestMatch(closest_z, zList)]
83
+ return_vertex = Vertex.ByCoordinates(closest_x, closest_y, closest_z)
84
+ if transferDictionary == True:
85
+ return_vertex = Topology.SetDictionary(return_vertex, Topology.Dictionary(vertex), silent=silent)
86
+ return return_vertex
87
+
37
88
  @staticmethod
38
89
  def AreCollinear(vertices: list, mantissa: int = 6, tolerance: float = 0.0001):
39
90
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.7.77'
1
+ __version__ = '0.7.78'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.7.77
3
+ Version: 0.7.78
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
@@ -17,20 +17,20 @@ topologicpy/Helper.py,sha256=Sv35czP_j0oLDeJcN8usswUm4U3auiK1LQ_Z_HBvxxg,21716
17
17
  topologicpy/Honeybee.py,sha256=HfTaEV1R8K1xOVQQy9sBOhBTF_ap8A2RxZOYhirp_Mw,21835
18
18
  topologicpy/Matrix.py,sha256=umgR7An919-wGInXJ1wpqnoQ2jCPdyMe2rcWTZ16upk,8079
19
19
  topologicpy/Neo4j.py,sha256=t52hgE9cVsqkGc7m7fjRsLnyfRHakVHwdvF4ms7ow78,22342
20
- topologicpy/Plotly.py,sha256=VNUHRUNu4NUmf3HScQRml3TcwHe_ZoqXjqwttGSuU88,112642
20
+ topologicpy/Plotly.py,sha256=cI1I6F-hGuN4dG-O-QvByipP0H1n5WsNG7_d0Y6_FNQ,113509
21
21
  topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
22
22
  topologicpy/PyG.py,sha256=LU9LCCzjxGPUM31qbaJXZsTvniTtgugxJY7y612t4A4,109757
23
23
  topologicpy/Shell.py,sha256=8OJjlWk9eCZ3uGOTht6ZVrcMczCafw-YWoDGueaz7eg,87673
24
24
  topologicpy/Speckle.py,sha256=AlsGlSDuKRtX5jhVsPNSSjjbZis079HbUchDH_5RJmE,18187
25
25
  topologicpy/Sun.py,sha256=42tDWMYpwRG7Z2Qjtp94eRgBuqySq7k8TgNUZDK7QxQ,36837
26
- topologicpy/Topology.py,sha256=NwJVI2_X7mvDouuDniIOwx67uL8xOhifjKSlitY3QEM,407275
26
+ topologicpy/Topology.py,sha256=W4cUFsRurb20PkoN_xJ_JfPCzqF1WWxmVdRcbjINi2w,425776
27
27
  topologicpy/Vector.py,sha256=A1g83zDHep58iVPY8WQ8iHNrSOfGWFEzvVeDuMnjDNY,33078
28
- topologicpy/Vertex.py,sha256=ZS6xK89JKokBKc0W8frdRhhuzR8c-dI1TTLt7pTf1iA,71032
28
+ topologicpy/Vertex.py,sha256=mcLJaWFCct03dkVmT25wAl6k0k2EaYvB1PWNO9WQHWg,73465
29
29
  topologicpy/Wire.py,sha256=pLhdos_kZwvIdzsQlVGb2jtZ4dK8ATTmEsZHDnAGBxk,185972
30
30
  topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
31
- topologicpy/version.py,sha256=sHWG-4WQq90h5-L0FW6KGjszP6rHlEFGr3Ne1xb3AB0,23
32
- topologicpy-0.7.77.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
- topologicpy-0.7.77.dist-info/METADATA,sha256=SdpjwqIH1H5l6-QgeWLo59SF8T7-y8jp0lsi2PeUtYY,10513
34
- topologicpy-0.7.77.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
35
- topologicpy-0.7.77.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
- topologicpy-0.7.77.dist-info/RECORD,,
31
+ topologicpy/version.py,sha256=EWhF8BAhkJGx-tk4Su0QKSe1yJ5s9S0qkdf3KEnEEqE,23
32
+ topologicpy-0.7.78.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
+ topologicpy-0.7.78.dist-info/METADATA,sha256=aVX5f6Z7-BS3b71jkQKden7ENVrgrXpkZgLkNdRUcH0,10513
34
+ topologicpy-0.7.78.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
35
+ topologicpy-0.7.78.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
+ topologicpy-0.7.78.dist-info/RECORD,,