topologicpy 0.7.76__py3-none-any.whl → 0.7.77__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/Topology.py CHANGED
@@ -2160,12 +2160,13 @@ class Topology():
2160
2160
  for key in keys:
2161
2161
  if python_dict[key].__class__ == ezdxf.acc.vector.Vec3:
2162
2162
  python_dict[key] = list(python_dict[key])
2163
+ rgb_list = None
2163
2164
  try:
2164
- r,g,b = entity.rgb
2165
+ rgb_list = entity.rgb
2165
2166
  except:
2166
2167
  rgb_list = get_layer_color(file.layers, entity.dxf.layer)
2167
- if rgb_list == None:
2168
- rgb_list = [0,0,0]
2168
+ if rgb_list == None:
2169
+ rgb_list = [0,0,0]
2169
2170
  python_dict['color'] = rgb_list
2170
2171
  python_dict['type'] = entity_type
2171
2172
  d = Dictionary.ByPythonDictionary(python_dict)
@@ -8295,32 +8296,42 @@ class Topology():
8295
8296
  The list of subtopologies.
8296
8297
 
8297
8298
  """
8299
+ from topologicpy.Face import Face
8300
+
8298
8301
  if not Topology.IsInstance(topology, "Topology"):
8299
8302
  print("Topology.SubTopologies - Error: the input topology parameter is not a valid topology. Returning None.")
8300
8303
  return None
8301
8304
  if Topology.TypeAsString(topology).lower() == subTopologyType.lower():
8302
8305
  return [topology]
8306
+
8303
8307
  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
8308
+
8309
+ # Spcecial case for faces to return vertices in CW/CCW order.
8310
+ if Topology.IsInstance(topology, "face") and (subTopologyType.lower() == "vertex" or subTopologyType.lower() == "edge"):
8311
+ wires = Face.Wires(topology)
8312
+ for wire in wires:
8313
+ subTopologies += Topology.SubTopologies(wire, subTopologyType=subTopologyType)
8314
+ else:
8315
+ if subTopologyType.lower() == "vertex":
8316
+ _ = topology.Vertices(None, subTopologies) # Hook to Core
8317
+ elif subTopologyType.lower() == "edge":
8318
+ _ = topology.Edges(None, subTopologies) # Hook to Core
8319
+ elif subTopologyType.lower() == "wire":
8320
+ _ = topology.Wires(None, subTopologies) # Hook to Core
8321
+ elif subTopologyType.lower() == "face":
8322
+ _ = topology.Faces(None, subTopologies) # Hook to Core
8323
+ elif subTopologyType.lower() == "shell":
8324
+ _ = topology.Shells(None, subTopologies) # Hook to Core
8325
+ elif subTopologyType.lower() == "cell":
8326
+ _ = topology.Cells(None, subTopologies) # Hook to Core
8327
+ elif subTopologyType.lower() == "cellcomplex":
8328
+ _ = topology.CellComplexes(None, subTopologies) # Hook to Core
8329
+ elif subTopologyType.lower() == "cluster":
8330
+ _ = topology.Clusters(None, subTopologies) # Hook to Core
8331
+ elif subTopologyType.lower() == "aperture":
8332
+ _ = topology.Apertures(None, subTopologies) # Hook to Core
8333
+ if not subTopologies:
8334
+ return [] # Make sure to return an empty list instead of None
8324
8335
  return subTopologies
8325
8336
 
8326
8337
 
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.77'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.7.76
3
+ Version: 0.7.77
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,8 +10,8 @@ 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
@@ -23,14 +23,14 @@ 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=NwJVI2_X7mvDouuDniIOwx67uL8xOhifjKSlitY3QEM,407275
27
27
  topologicpy/Vector.py,sha256=A1g83zDHep58iVPY8WQ8iHNrSOfGWFEzvVeDuMnjDNY,33078
28
28
  topologicpy/Vertex.py,sha256=ZS6xK89JKokBKc0W8frdRhhuzR8c-dI1TTLt7pTf1iA,71032
29
- topologicpy/Wire.py,sha256=eVet2OToVsXi9AkDYo35LpfMPqJ6aKGD6QkiU4-Jvs8,182271
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=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,,