topologicpy 0.7.76__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/Face.py CHANGED
@@ -1281,7 +1281,7 @@ class Face():
1281
1281
  return return_list
1282
1282
 
1283
1283
  @staticmethod
1284
- def ExternalBoundary(face):
1284
+ def ExternalBoundary(face, silent=False):
1285
1285
  """
1286
1286
  Returns the external boundary (closed wire) of the input face.
1287
1287
 
@@ -1289,6 +1289,8 @@ class Face():
1289
1289
  ----------
1290
1290
  face : topologic_core.Face
1291
1291
  The input face.
1292
+ silent : bool , optional
1293
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1292
1294
 
1293
1295
  Returns
1294
1296
  -------
@@ -1300,6 +1302,10 @@ class Face():
1300
1302
  from topologicpy.Wire import Wire
1301
1303
  from topologicpy.Topology import Topology
1302
1304
 
1305
+ if not Topology.IsInstance(face, "face"):
1306
+ if not silent:
1307
+ print("Face.ExternalBoundary - Error: The input face parameter is not a topologic face. Returning None.")
1308
+ return None
1303
1309
  eb = face.ExternalBoundary() # Hook to Core
1304
1310
  return eb
1305
1311
 
topologicpy/Graph.py CHANGED
@@ -1880,9 +1880,9 @@ class Graph:
1880
1880
  else:
1881
1881
  node_keys = [nodeIDHeader, nodeLabelHeader, "mask"]+nodeFeaturesKeys
1882
1882
  if len(edgeFeaturesKeys) == 0:
1883
- edge_keys = [edgeLabelHeader, "mask", edgeFeaturesHeader]
1883
+ edge_keys = [edgeSRCHeader, edgeDSTHeader, edgeLabelHeader, "mask", edgeFeaturesHeader]
1884
1884
  else:
1885
- edge_keys = [edgeLabelHeader, "mask"]+edgeFeaturesKeys
1885
+ edge_keys = [edgeSRCHeader, edgeDSTHeader, edgeLabelHeader, "mask"]+edgeFeaturesKeys
1886
1886
  if len(graphFeaturesKeys) == 0:
1887
1887
  graph_keys = [graphIDHeader, graphLabelHeader, graphFeaturesHeader]
1888
1888
  else:
@@ -1975,11 +1975,11 @@ class Graph:
1975
1975
  mask = 0
1976
1976
  features = row[edgeFeaturesHeader]
1977
1977
  if len(edgeFeaturesKeys) == 0:
1978
- values = [label, mask, features]
1978
+ values = [src_id, dst_id, label, mask, features]
1979
1979
  else:
1980
1980
  featureList = features.split(",")
1981
1981
  featureList = [float(s) for s in featureList]
1982
- values = [label, mask]+featureList
1982
+ values = [src_id, dst_id, label, mask]+featureList
1983
1983
  if not (src_id == dst_id) and not [src_id, dst_id] in es and not [dst_id, src_id] in es:
1984
1984
  es.append([src_id, dst_id])
1985
1985
  try:
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]
@@ -2160,12 +2167,13 @@ class Topology():
2160
2167
  for key in keys:
2161
2168
  if python_dict[key].__class__ == ezdxf.acc.vector.Vec3:
2162
2169
  python_dict[key] = list(python_dict[key])
2170
+ rgb_list = None
2163
2171
  try:
2164
- r,g,b = entity.rgb
2172
+ rgb_list = entity.rgb
2165
2173
  except:
2166
2174
  rgb_list = get_layer_color(file.layers, entity.dxf.layer)
2167
- if rgb_list == None:
2168
- rgb_list = [0,0,0]
2175
+ if rgb_list == None:
2176
+ rgb_list = [0,0,0]
2169
2177
  python_dict['color'] = rgb_list
2170
2178
  python_dict['type'] = entity_type
2171
2179
  d = Dictionary.ByPythonDictionary(python_dict)
@@ -3634,6 +3642,325 @@ class Topology():
3634
3642
  """
3635
3643
  return topologic.Topology.ByOcctShape(occtShape, "")
3636
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
+
3637
3964
  @staticmethod
3638
3965
  def ByBREPString(string):
3639
3966
  """
@@ -7696,6 +8023,8 @@ class Topology():
7696
8023
  if not silent:
7697
8024
  print("Topology.Show - Error: the input topologies parameter does not contain any valid topology. Returning None.")
7698
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
7699
8028
  data = []
7700
8029
  for topology in new_topologies:
7701
8030
  if Topology.IsInstance(topology, "Graph"):
@@ -8295,32 +8624,42 @@ class Topology():
8295
8624
  The list of subtopologies.
8296
8625
 
8297
8626
  """
8627
+ from topologicpy.Face import Face
8628
+
8298
8629
  if not Topology.IsInstance(topology, "Topology"):
8299
8630
  print("Topology.SubTopologies - Error: the input topology parameter is not a valid topology. Returning None.")
8300
8631
  return None
8301
8632
  if Topology.TypeAsString(topology).lower() == subTopologyType.lower():
8302
8633
  return [topology]
8634
+
8303
8635
  subTopologies = []
8304
- if subTopologyType.lower() == "vertex":
8305
- _ = topology.Vertices(None, subTopologies) # Hook to Core
8306
- elif subTopologyType.lower() == "edge":
8307
- _ = topology.Edges(None, subTopologies) # Hook to Core
8308
- elif subTopologyType.lower() == "wire":
8309
- _ = topology.Wires(None, subTopologies) # Hook to Core
8310
- elif subTopologyType.lower() == "face":
8311
- _ = topology.Faces(None, subTopologies) # Hook to Core
8312
- elif subTopologyType.lower() == "shell":
8313
- _ = topology.Shells(None, subTopologies) # Hook to Core
8314
- elif subTopologyType.lower() == "cell":
8315
- _ = topology.Cells(None, subTopologies) # Hook to Core
8316
- elif subTopologyType.lower() == "cellcomplex":
8317
- _ = topology.CellComplexes(None, subTopologies) # Hook to Core
8318
- elif subTopologyType.lower() == "cluster":
8319
- _ = topology.Clusters(None, subTopologies) # Hook to Core
8320
- elif subTopologyType.lower() == "aperture":
8321
- _ = topology.Apertures(None, subTopologies) # Hook to Core
8322
- if not subTopologies:
8323
- return [] # Make sure to return an empty list instead of None
8636
+
8637
+ # Spcecial case for faces to return vertices in CW/CCW order.
8638
+ if Topology.IsInstance(topology, "face") and (subTopologyType.lower() == "vertex" or subTopologyType.lower() == "edge"):
8639
+ wires = Face.Wires(topology)
8640
+ for wire in wires:
8641
+ subTopologies += Topology.SubTopologies(wire, subTopologyType=subTopologyType)
8642
+ else:
8643
+ if subTopologyType.lower() == "vertex":
8644
+ _ = topology.Vertices(None, subTopologies) # Hook to Core
8645
+ elif subTopologyType.lower() == "edge":
8646
+ _ = topology.Edges(None, subTopologies) # Hook to Core
8647
+ elif subTopologyType.lower() == "wire":
8648
+ _ = topology.Wires(None, subTopologies) # Hook to Core
8649
+ elif subTopologyType.lower() == "face":
8650
+ _ = topology.Faces(None, subTopologies) # Hook to Core
8651
+ elif subTopologyType.lower() == "shell":
8652
+ _ = topology.Shells(None, subTopologies) # Hook to Core
8653
+ elif subTopologyType.lower() == "cell":
8654
+ _ = topology.Cells(None, subTopologies) # Hook to Core
8655
+ elif subTopologyType.lower() == "cellcomplex":
8656
+ _ = topology.CellComplexes(None, subTopologies) # Hook to Core
8657
+ elif subTopologyType.lower() == "cluster":
8658
+ _ = topology.Clusters(None, subTopologies) # Hook to Core
8659
+ elif subTopologyType.lower() == "aperture":
8660
+ _ = topology.Apertures(None, subTopologies) # Hook to Core
8661
+ if not subTopologies:
8662
+ return [] # Make sure to return an empty list instead of None
8324
8663
  return subTopologies
8325
8664
 
8326
8665
 
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/Wire.py CHANGED
@@ -3242,18 +3242,27 @@ class Wire():
3242
3242
  return roof
3243
3243
 
3244
3244
  @staticmethod
3245
- def Simplify(wire, tolerance=0.0001):
3245
+ def Simplify(wire, method='douglas-peucker', tolerance=0.0001, silent=False):
3246
3246
  """
3247
- Simplifies the input wire edges based on the Douglas Peucker algorthim. See https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
3248
- Part of this code was contributed by gaoxipeng. See https://github.com/wassimj/topologicpy/issues/35
3247
+ Simplifies the input wire edges based on the selected algorithm: Douglas-Peucker or Visvalingam–Whyatt.
3249
3248
 
3250
3249
  Parameters
3251
3250
  ----------
3252
3251
  wire : topologic_core.Wire
3253
3252
  The input wire.
3253
+ method : str, optional
3254
+ The simplification method to use: 'douglas-peucker' or 'visvalingam-whyatt' or 'reumann-witkam'.
3255
+ The default is 'douglas-peucker'.
3254
3256
  tolerance : float , optional
3255
- The desired tolerance. The default is 0.0001. Edges shorter than this length will be removed.
3256
-
3257
+ The desired tolerance.
3258
+ If using the douglas-peucker method, edge lengths shorter than this amount will be removed.
3259
+ If using the visvalingam-whyatt method, triangulare areas less than is amount will be removed.
3260
+ If using the Reumann-Witkam method, the tolerance specifies the maximum perpendicular distance allowed
3261
+ between any point and the current line segment; points falling within this distance are discarded.
3262
+ The default is 0.0001.
3263
+ silent : bool , optional
3264
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
3265
+
3257
3266
  Returns
3258
3267
  -------
3259
3268
  topologic_core.Wire
@@ -3264,7 +3273,7 @@ class Wire():
3264
3273
  from topologicpy.Edge import Edge
3265
3274
  from topologicpy.Cluster import Cluster
3266
3275
  from topologicpy.Topology import Topology
3267
-
3276
+
3268
3277
  def perpendicular_distance(point, line_start, line_end):
3269
3278
  # Calculate the perpendicular distance from a point to a line segment
3270
3279
  x0 = point.X()
@@ -3284,15 +3293,12 @@ class Wire():
3284
3293
  points = wire
3285
3294
  else:
3286
3295
  points = Topology.Vertices(wire)
3287
- # points.insert(0, points.pop())
3288
3296
  if len(points) <= 2:
3289
3297
  return points
3290
3298
 
3291
- # Use the first and last points in the list as the starting and ending points
3292
3299
  start_point = points[0]
3293
3300
  end_point = points[-1]
3294
3301
 
3295
- # Find the point with the maximum distance
3296
3302
  max_distance = 0
3297
3303
  max_index = 0
3298
3304
 
@@ -3302,19 +3308,79 @@ class Wire():
3302
3308
  max_distance = d
3303
3309
  max_index = i
3304
3310
 
3305
- # If the maximum distance is less than the tolerance, no further simplification is needed
3306
3311
  if max_distance <= tolerance:
3307
3312
  return [start_point, end_point]
3308
3313
 
3309
- # Recursively simplify
3310
3314
  first_segment = douglas_peucker(points[:max_index + 1], tolerance)
3311
3315
  second_segment = douglas_peucker(points[max_index:], tolerance)
3312
3316
 
3313
- # Merge the two simplified segments
3314
3317
  return first_segment[:-1] + second_segment
3315
-
3318
+
3319
+ def visvalingam_whyatt(wire, tolerance):
3320
+ if isinstance(wire, list):
3321
+ points = wire
3322
+ else:
3323
+ points = Topology.Vertices(wire)
3324
+
3325
+ if len(points) <= 2:
3326
+ return points
3327
+
3328
+ # Calculate the effective area for each point except the first and last
3329
+ def effective_area(p1, p2, p3):
3330
+ # Triangle area formed by p1, p2, and p3
3331
+ return 0.5 * abs(p1.X() * (p2.Y() - p3.Y()) + p2.X() * (p3.Y() - p1.Y()) + p3.X() * (p1.Y() - p2.Y()))
3332
+
3333
+ # Keep track of effective areas
3334
+ areas = [None] # First point has no area
3335
+ for i in range(1, len(points) - 1):
3336
+ area = effective_area(points[i - 1], points[i], points[i + 1])
3337
+ areas.append((area, i))
3338
+ areas.append(None) # Last point has no area
3339
+
3340
+ # Sort points by area in ascending order
3341
+ sorted_areas = sorted([(area, idx) for area, idx in areas[1:-1] if area is not None])
3342
+
3343
+ # Remove points with area below the tolerance threshold
3344
+ remove_indices = {idx for area, idx in sorted_areas if area < tolerance}
3345
+
3346
+ # Construct the simplified list of points
3347
+ simplified_points = [point for i, point in enumerate(points) if i not in remove_indices]
3348
+
3349
+ return simplified_points
3350
+
3351
+ def reumann_witkam(wire, tolerance):
3352
+ if isinstance(wire, list):
3353
+ points = wire
3354
+ else:
3355
+ points = Topology.Vertices(wire)
3356
+
3357
+ if len(points) <= 2:
3358
+ return points
3359
+
3360
+ simplified_points = [points[0]]
3361
+ start_point = points[0]
3362
+ i = 1
3363
+
3364
+ while i < len(points) - 1:
3365
+ end_point = points[i]
3366
+ next_point = points[i + 1]
3367
+ dist = perpendicular_distance(next_point, start_point, end_point)
3368
+
3369
+ # If the next point is outside the tolerance corridor, add the current end_point
3370
+ if dist > tolerance:
3371
+ simplified_points.append(end_point)
3372
+ start_point = end_point
3373
+
3374
+ i += 1
3375
+
3376
+ # Always add the last point
3377
+ simplified_points.append(points[-1])
3378
+
3379
+ return simplified_points
3380
+
3316
3381
  if not Topology.IsInstance(wire, "Wire"):
3317
- print("Wire.Simplify = Error: The input wire parameter is not a Wire. Returning None.")
3382
+ if not silent:
3383
+ print("Wire.Simplify = Error: The input wire parameter is not a Wire. Returning None.")
3318
3384
  return None
3319
3385
  if not Wire.IsManifold(wire):
3320
3386
  wires = Wire.Split(wire)
@@ -3324,12 +3390,31 @@ class Wire():
3324
3390
  if Edge.Length(w) > tolerance:
3325
3391
  new_wires.append(w)
3326
3392
  elif Topology.IsInstance(w, "Wire"):
3327
- new_wires.append(Wire.Simplify(w, tolerance=tolerance))
3393
+ new_wires.append(Wire.Simplify(w, method=method, tolerance=tolerance, silent=silent))
3328
3394
  return_wire = Topology.SelfMerge(Cluster.ByTopologies(new_wires))
3329
3395
  return return_wire
3396
+
3397
+ new_vertices = []
3398
+ if 'douglas' in method.lower(): #douglas-peucker
3399
+ new_vertices = douglas_peucker(wire, tolerance=tolerance)
3400
+ elif 'vis' in method.lower(): # 'visvalingam-whyatt'
3401
+ new_vertices = visvalingam_whyatt(wire, tolerance=tolerance)
3402
+ elif 'reu' in method.lower(): # 'reumann-witkam'
3403
+ new_vertices = reumann_witkam(wire, tolerance=tolerance)
3404
+ else:
3405
+ if not silent:
3406
+ print(f"Wire.Simplify - Warning: Unknown method ({method}). Please use 'douglas-peucker' or 'visvalingam-whyatt' or 'reumann-witkam'. Defaulting to 'douglas-peucker'.")
3407
+ new_vertices = douglas_peucker(wire, tolerance=tolerance)
3330
3408
 
3331
- new_vertices = douglas_peucker(wire, tolerance=tolerance)
3409
+ if len(new_vertices) < 2:
3410
+ if not silent:
3411
+ print("Wire.Simplify - Warning: Could not generate enough vertices for a simplified wire. Returning the original wire.")
3412
+ wire
3332
3413
  new_wire = Wire.ByVertices(new_vertices, close=Wire.IsClosed(wire))
3414
+ if not Topology.IsInstance(new_wire, "wire"):
3415
+ if not silent:
3416
+ print("Wire.Simplify - Warning: Could not generate a simplified wire. Returning the original wire.")
3417
+ return wire
3333
3418
  return new_wire
3334
3419
 
3335
3420
  @staticmethod
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.7.76'
1
+ __version__ = '0.7.78'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.7.76
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
@@ -36,6 +36,7 @@ Requires-Dist: tqdm
36
36
  Requires-Dist: plotly
37
37
  Requires-Dist: lark
38
38
  Requires-Dist: specklepy
39
+ Requires-Dist: webcolors
39
40
  Requires-Dist: topologic_core>=7.0.1
40
41
  Provides-Extra: test
41
42
  Requires-Dist: pytest-xdist>=2.4.0; extra == "test"
@@ -10,27 +10,27 @@ topologicpy/DGL.py,sha256=Dd6O08D-vSxpjHYgKm45JpKiaeGvWlg1BRMzYMAXGNc,138991
10
10
  topologicpy/Dictionary.py,sha256=0AsGoz48pGTye_F4KcJopNjD9STeQ50LHc6PPvERFaA,31932
11
11
  topologicpy/Edge.py,sha256=9u9SdUxuenLUIK26xwFvPoYV34p0dCfXmHHBxdgvAdM,67164
12
12
  topologicpy/EnergyModel.py,sha256=AqTtmXE35SxvRXhG3vYAQd7GQDW-6HtjYPHua6ME4Eg,53762
13
- topologicpy/Face.py,sha256=9E3AaTCkcENwmTbFgyZKkdmhjVXHO_q9clQSji2685M,124363
14
- topologicpy/Graph.py,sha256=BuSg8ilbZd2Qqoenmtw5FrkSe1fDLFS6jshljvq4dOs,416822
13
+ topologicpy/Face.py,sha256=mS7LI3qIo2jfQbibpInArRfXMmTZd7xU8IdvRYUIGSU,124747
14
+ topologicpy/Graph.py,sha256=ZhMVB6ntuhIgTrKLUPryeAJFFBF0cnRsNgESbaohOiw,416914
15
15
  topologicpy/Grid.py,sha256=9N6PE84qCm40TRi2WtlVZSBwXXr47zHpscEpZHg_JW4,18205
16
16
  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=J1eTBp8esd9jwcQ4fY82uTjJtj6r9WiaGIZmevRxxDQ,406736
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
29
- topologicpy/Wire.py,sha256=eVet2OToVsXi9AkDYo35LpfMPqJ6aKGD6QkiU4-Jvs8,182271
28
+ topologicpy/Vertex.py,sha256=mcLJaWFCct03dkVmT25wAl6k0k2EaYvB1PWNO9WQHWg,73465
29
+ topologicpy/Wire.py,sha256=pLhdos_kZwvIdzsQlVGb2jtZ4dK8ATTmEsZHDnAGBxk,185972
30
30
  topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
31
- topologicpy/version.py,sha256=3M8WghUyEKh4Nub9zc52IBDUVRayyZVk2cK9zf5M-VY,23
32
- topologicpy-0.7.76.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
- topologicpy-0.7.76.dist-info/METADATA,sha256=iMOr84bFL_JI4Gr37GGBrxyIb6i7TOZxJaCKcIHHj_0,10488
34
- topologicpy-0.7.76.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
35
- topologicpy-0.7.76.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
- topologicpy-0.7.76.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,,