topologicpy 0.8.89__py3-none-any.whl → 0.8.91__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/Graph.py CHANGED
@@ -22,6 +22,7 @@ import warnings
22
22
 
23
23
  from collections import namedtuple
24
24
  from multiprocessing import Process, Queue
25
+ from typing import Any
25
26
 
26
27
  try:
27
28
  import numpy as np
@@ -5251,7 +5252,8 @@ class Graph:
5251
5252
  useInternalVertex: bool = False,
5252
5253
  storeBREP: bool =False,
5253
5254
  mantissa: int = 6,
5254
- tolerance: float = 0.0001):
5255
+ tolerance: float = 0.0001,
5256
+ silent: float = False):
5255
5257
  """
5256
5258
  Creates a graph.See https://en.wikipedia.org/wiki/Graph_(discrete_mathematics).
5257
5259
 
@@ -5305,6 +5307,8 @@ class Graph:
5305
5307
  If set to True, store the BRep of the subtopology in its representative vertex. Default is False.
5306
5308
  tolerance : float , optional
5307
5309
  The desired tolerance. Default is 0.0001.
5310
+ silent : bool , optional
5311
+ If set to True, error and warning messages are suppressed. Default is False.
5308
5312
 
5309
5313
  Returns
5310
5314
  -------
@@ -5319,6 +5323,10 @@ class Graph:
5319
5323
  from topologicpy.Topology import Topology
5320
5324
  from topologicpy.Aperture import Aperture
5321
5325
 
5326
+ if not Topology.IsInstance(topology, "topology"):
5327
+ if not silent:
5328
+ print("Graph.ByTopology - Error: The input topology parameter is not a valid topology. Returning None.")
5329
+ return None
5322
5330
  def _viaSharedTopologies(vt, sharedTops):
5323
5331
  verts = []
5324
5332
  eds = []
@@ -15533,7 +15541,16 @@ class Graph:
15533
15541
  return graph
15534
15542
 
15535
15543
  @staticmethod
15536
- def ShortestPath(graph, vertexA, vertexB, vertexKey="", edgeKey="Length", tolerance=0.0001):
15544
+ def ShortestPath(graph,
15545
+ vertexA,
15546
+ vertexB,
15547
+ vertexKey: str = "",
15548
+ edgeKey: str = "Length",
15549
+ transferDictionaries: bool = False,
15550
+ straighten: bool = False,
15551
+ face: Any = None,
15552
+ tolerance: float = 0.0001,
15553
+ silent: bool = False):
15537
15554
  """
15538
15555
  Returns the shortest path that connects the input vertices. The shortest path will take into consideration both the vertexKey and the edgeKey if both are specified and will minimize the total "cost" of the path. Otherwise, it will take into consideration only whatever key is specified.
15539
15556
 
@@ -15549,8 +15566,19 @@ class Graph:
15549
15566
  The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. Default is None.
15550
15567
  edgeKey : string , optional
15551
15568
  The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. Default is "length".
15569
+ transferDictionaries : bool , optional
15570
+ If set to True, the dictionaries from the graph vertices will be transferred to the vertices of the shortest path. Otherwise, they won't. Default is False.
15571
+ Note: Edge dictionaries are not transferred (In straightened paths, the path edges are no longer the same as
15572
+ the original graph edges. Thus, you must implement your own logic to transfer edge dictionaries if needed).
15573
+ straighten : bool , optional
15574
+ If set to True, the path will be straightened as much as possible while remaining inside the specified face.
15575
+ Thus, the face input must be a valid topologic Face that is planar and residing on the XY plane. Default is False.
15576
+ face : topologic_core.Face , optional
15577
+ The face on which the path resides. This is used for straightening the path. Default is None.
15552
15578
  tolerance : float , optional
15553
15579
  The desired tolerance. Default is 0.0001.
15580
+ silent : bool , optional
15581
+ If set to True, error and warning messages are suppressed. Default is False.
15554
15582
 
15555
15583
  Returns
15556
15584
  -------
@@ -15563,21 +15591,29 @@ class Graph:
15563
15591
  from topologicpy.Topology import Topology
15564
15592
 
15565
15593
  if not Topology.IsInstance(graph, "Graph"):
15566
- print("Graph.ShortestPath - Error: The input graph is not a valid graph. Returning None.")
15594
+ if not silent:
15595
+ print("Graph.ShortestPath - Error: The input graph is not a valid graph. Returning None.")
15567
15596
  return None
15568
15597
  if not Topology.IsInstance(vertexA, "Vertex"):
15569
- print("Graph.ShortestPath - Error: The input vertexA is not a valid vertex. Returning None.")
15598
+ if not silent:
15599
+ print("Graph.ShortestPath - Error: The input vertexA is not a valid vertex. Returning None.")
15570
15600
  return None
15571
15601
  if not Topology.IsInstance(vertexB, "Vertex"):
15572
- print("Graph.ShortestPath - Error: The input vertexB is not a valid vertex. Returning None.")
15602
+ if not silent:
15603
+ print("Graph.ShortestPath - Error: The input vertexB is not a valid vertex. Returning None.")
15573
15604
  return None
15605
+ if straighten == True:
15606
+ if not Topology.IsInstance(face, "face"):
15607
+ if not silent:
15608
+ print("Graph.ShortestPath - Error: Straighten is set to True, but the face parameter is not a valid toopologic face. Returning None.")
15609
+ return None
15574
15610
  if edgeKey:
15575
15611
  if edgeKey.lower() == "length":
15576
15612
  edgeKey = "Length"
15577
15613
  try:
15578
15614
  gsv = Graph.NearestVertex(graph, vertexA)
15579
15615
  gev = Graph.NearestVertex(graph, vertexB)
15580
- shortest_path = graph.ShortestPath(gsv, gev, vertexKey, edgeKey)
15616
+ shortest_path = graph.ShortestPath(gsv, gev, vertexKey, edgeKey) # Hook to Core
15581
15617
  if not shortest_path == None:
15582
15618
  if Topology.IsInstance(shortest_path, "Edge"):
15583
15619
  shortest_path = Wire.ByEdges([shortest_path])
@@ -15586,6 +15622,14 @@ class Graph:
15586
15622
  if Topology.IsInstance(shortest_path, "Wire"):
15587
15623
  shortest_path = Wire.Reverse(shortest_path)
15588
15624
  shortest_path = Wire.OrientEdges(shortest_path, Wire.StartVertex(shortest_path), tolerance=tolerance)
15625
+ if Topology.IsInstance(shortest_path, "wire"):
15626
+ if straighten == True and Topology.IsInstance(face, "face"):
15627
+ shortest_path = Wire.StraightenInFace(shortest_path, face)
15628
+ if transferDictionaries == True:
15629
+ path_verts = Topology.Vertices(shortest_path)
15630
+ for p_v in path_verts:
15631
+ g_v = Graph.NearestVertex(graph, p_v)
15632
+ p_v = Topology.SetDictionary(p_v, Topology.Dictionary(g_v))
15589
15633
  return shortest_path
15590
15634
  except:
15591
15635
  return None
topologicpy/Wire.py CHANGED
@@ -331,16 +331,19 @@ class Wire():
331
331
 
332
332
  else:
333
333
  best_br = boundingRectangle
334
- x_min, y_min, maxX, maxY = best_br
334
+ x_min, y_min, x_max, y_max = best_br
335
335
  vb1 = Vertex.ByCoordinates(x_min, y_min, 0)
336
- vb2 = Vertex.ByCoordinates(maxX, y_min, 0)
337
- vb3 = Vertex.ByCoordinates(maxX, maxY, 0)
338
- vb4 = Vertex.ByCoordinates(x_min, maxY, 0)
336
+ vb2 = Vertex.ByCoordinates(x_max, y_min, 0)
337
+ vb3 = Vertex.ByCoordinates(x_max, y_max, 0)
338
+ vb4 = Vertex.ByCoordinates(x_min, y_max, 0)
339
339
 
340
340
  boundingRectangle = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True, tolerance=tolerance, silent=silent)
341
341
  boundingRectangle = Topology.Rotate(boundingRectangle, origin=origin, axis=[0, 0, 1], angle=-best_z)
342
342
  boundingRectangle = Topology.Unflatten(boundingRectangle, origin=f_origin, direction=normal)
343
- dictionary = Dictionary.ByKeysValues(["zrot"], [best_z])
343
+ dictionary = Dictionary.ByKeysValues(["zrot", "xmin", "ymin", "xmax", "ymax", "width", "length"],
344
+ [best_z, x_min, y_min, x_max, y_max, (x_max - x_min), (y_max - y_min)])
345
+
346
+ #dictionary = Dictionary.ByKeysValues(["zrot"], [best_z])
344
347
  boundingRectangle = Topology.SetDictionary(boundingRectangle, dictionary)
345
348
  return boundingRectangle
346
349
 
@@ -2426,7 +2429,254 @@ class Wire():
2426
2429
  # Unflatten the wire
2427
2430
  return_wire = Topology.Unflatten(flat_wire, origin=Vertex.Origin(), direction=normal)
2428
2431
  return return_wire
2429
-
2432
+
2433
+ @staticmethod
2434
+ def Funnel(face,
2435
+ vertexA,
2436
+ vertexB,
2437
+ portals,
2438
+ tolerance: float = 0.0001,
2439
+ silent: float = False):
2440
+ """
2441
+ Returns a Wire representing a smoothed path inside the given face using
2442
+ the funnel (string-pulling) algorithm.
2443
+
2444
+ The algorithm assumes that a corridor has already been computed, and is
2445
+ provided as an ordered list of "portals" (pairs of vertices) that lie
2446
+ on the face between the start and end locations.
2447
+
2448
+ Parameters
2449
+ ----------
2450
+ face : topologic_core.Face
2451
+ The planar face on which navigation occurs. All vertices must lie
2452
+ on this face.
2453
+ vertexA : topologic_core.Vertex
2454
+ The start point of the path.
2455
+ vertexB : topologic_core.Vertex
2456
+ The end point of the path.
2457
+ portals : list of tuple(Vertex, Vertex)
2458
+ Ordered list of corridor edges. Each item is (leftVertex, rightVertex)
2459
+ describing the visible "portal" between two consecutive regions along
2460
+ the navmesh path.
2461
+ tolerance : float , optional
2462
+ Numerical tolerance used when comparing orientations and distances.
2463
+ Default is 0.0001.
2464
+ silent : bool , optional
2465
+ If set to True, error and warning messages are suppressed. Default is False.
2466
+
2467
+ Returns
2468
+ -------
2469
+ wire : topologic_core.Wire
2470
+ A Wire representing the smoothed path from startVertex to endVertex
2471
+ that stays inside the navigation corridor on the face.
2472
+ """
2473
+ from topologicpy.Dictionary import Dictionary
2474
+ from topologicpy.Vertex import Vertex
2475
+ from topologicpy.Face import Face
2476
+ from topologicpy.Topology import Topology
2477
+
2478
+ if not Topology.IsInstance(face, "face"):
2479
+ if not silent:
2480
+ print("Wire.Funnel - Error: The input face parameter is not a topologic face. Returning None.")
2481
+ return None
2482
+ if not Topology.IsInstance(vertexA, "vertex"):
2483
+ if not silent:
2484
+ print("Wire.Funnel - Error: The input vertexA parameter is not a topologic vertex. Returning None.")
2485
+ return None
2486
+ if not Topology.IsInstance(vertexB, "vertex"):
2487
+ if not silent:
2488
+ print("Wire.Funnel - Error: The input vertexB parameter is not a topologic vertex. Returning None.")
2489
+ return None
2490
+
2491
+ # ------------------------------------------------------------
2492
+ # 1. Basic helpers
2493
+ # ------------------------------------------------------------
2494
+ def _norm(v):
2495
+ return math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
2496
+
2497
+ def _normalize(v):
2498
+ n = _norm(v)
2499
+ if n < tolerance:
2500
+ return (0.0, 0.0, 0.0)
2501
+ return (v[0] / n, v[1] / n, v[2] / n)
2502
+
2503
+ def _dot(a, b):
2504
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
2505
+
2506
+ def _cross(a, b):
2507
+ return (
2508
+ a[1] * b[2] - a[2] * b[1],
2509
+ a[2] * b[0] - a[0] * b[2],
2510
+ a[0] * b[1] - a[1] * b[0],
2511
+ )
2512
+
2513
+ def _sub(a, b):
2514
+ return (a[0] - b[0], a[1] - b[1], a[2] - b[2])
2515
+
2516
+ def _tri_area2(a2, b2, c2):
2517
+ """
2518
+ Twice the signed area of triangle (a, b, c) in 2D.
2519
+ Positive => c is to the left of ab
2520
+ Negative => c is to the right of ab
2521
+ """
2522
+ return (b2[0] - a2[0]) * (c2[1] - a2[1]) - (b2[1] - a2[1]) * (c2[0] - a2[0])
2523
+
2524
+ def _coords3d(v):
2525
+ x, y, z = Vertex.Coordinates(v)
2526
+ return (x, y, z)
2527
+
2528
+ # ------------------------------------------------------------
2529
+ # 2. Build a local 2D coordinate system on the face
2530
+ # ------------------------------------------------------------
2531
+ # Face normal
2532
+ n_vec = Face.Normal(face) # [nx, ny, nz]
2533
+ n = _normalize((n_vec[0], n_vec[1], n_vec[2]))
2534
+
2535
+ # Choose an arbitrary vector not parallel to n
2536
+ if abs(n[0]) < 0.9:
2537
+ arbitrary = (1.0, 0.0, 0.0)
2538
+ else:
2539
+ arbitrary = (0.0, 1.0, 0.0)
2540
+
2541
+ u = _normalize(_cross(n, arbitrary)) # tangent
2542
+ v = _cross(n, u) # bitangent, already orthogonal and normalized
2543
+
2544
+ def _project_to_2d(vertex):
2545
+ p = _coords3d(vertex)
2546
+ # project onto basis (u, v)
2547
+ return (_dot(p, u), _dot(p, v))
2548
+
2549
+ # Precompute 2D coords for start, end and all portal vertices
2550
+ start2d = _project_to_2d(vertexA)
2551
+ end2d = _project_to_2d(vertexB)
2552
+
2553
+ portal2d = []
2554
+ for l_v, r_v in portals:
2555
+ portal2d.append((_project_to_2d(l_v), _project_to_2d(r_v)))
2556
+
2557
+ # ------------------------------------------------------------
2558
+ # 3. Funnel algorithm in 2D
2559
+ # (based on classic Recast / string-pulling implementation)
2560
+ # ------------------------------------------------------------
2561
+ path_vertices = [vertexA]
2562
+
2563
+ apex2d = start2d
2564
+ apexVertex = vertexA
2565
+ apexIndex = -1
2566
+
2567
+ left2d = start2d
2568
+ right2d = start2d
2569
+ leftVertex = vertexA
2570
+ rightVertex = vertexB
2571
+ leftIndex = -1
2572
+ rightIndex = -1
2573
+
2574
+ n_portals = len(portals)
2575
+ i = 0
2576
+
2577
+ # We will process all portals, and then a final "portal" at the goal (end, end)
2578
+ while i <= n_portals:
2579
+ if i < n_portals:
2580
+ newLeft2d, newRight2d = portal2d[i]
2581
+ newLeftVertex, newRightVertex = portals[i]
2582
+ else:
2583
+ # last "portal" is the goal point itself
2584
+ newLeft2d = end2d
2585
+ newRight2d = end2d
2586
+ newLeftVertex = vertexB
2587
+ newRightVertex = vertexB
2588
+
2589
+ # --------------------------------------------------------
2590
+ # Update right side of funnel
2591
+ # --------------------------------------------------------
2592
+ area_apex_right_newRight = _tri_area2(apex2d, right2d, newRight2d)
2593
+ if area_apex_right_newRight <= tolerance:
2594
+ # New right vertex is "inside" or tightening the funnel
2595
+ area_apex_left_newRight = _tri_area2(apex2d, left2d, newRight2d)
2596
+ if (apexVertex == rightVertex) or (area_apex_left_newRight > tolerance):
2597
+ # Tighten the funnel on the right side
2598
+ right2d = newRight2d
2599
+ rightVertex = newRightVertex
2600
+ rightIndex = i
2601
+ else:
2602
+ # Right over left, so left becomes the new apex
2603
+ path_vertices.append(leftVertex)
2604
+ apex2d = _project_to_2d(leftVertex)
2605
+ apexVertex = leftVertex
2606
+ apexIndex = leftIndex
2607
+
2608
+ # Reset funnel
2609
+ left2d = apex2d
2610
+ right2d = apex2d
2611
+ leftVertex = apexVertex
2612
+ rightVertex = apexVertex
2613
+ leftIndex = apexIndex
2614
+ rightIndex = apexIndex
2615
+
2616
+ # Restart from the new apex
2617
+ i = apexIndex + 1
2618
+ continue
2619
+
2620
+ # --------------------------------------------------------
2621
+ # Update left side of funnel
2622
+ # --------------------------------------------------------
2623
+ area_apex_left_newLeft = _tri_area2(apex2d, left2d, newLeft2d)
2624
+ if area_apex_left_newLeft >= -tolerance:
2625
+ # New left vertex is "inside" or tightening the funnel
2626
+ area_apex_right_newLeft = _tri_area2(apex2d, right2d, newLeft2d)
2627
+ if (apexVertex == leftVertex) or (area_apex_right_newLeft < -tolerance):
2628
+ # Tighten funnel on the left side
2629
+ left2d = newLeft2d
2630
+ leftVertex = newLeftVertex
2631
+ leftIndex = i
2632
+ else:
2633
+ # Left over right, so right becomes the new apex
2634
+ path_vertices.append(rightVertex)
2635
+ apex2d = _project_to_2d(rightVertex)
2636
+ apexVertex = rightVertex
2637
+ apexIndex = rightIndex
2638
+
2639
+ # Reset funnel
2640
+ left2d = apex2d
2641
+ right2d = apex2d
2642
+ leftVertex = apexVertex
2643
+ rightVertex = apexVertex
2644
+ leftIndex = apexIndex
2645
+ rightIndex = apexIndex
2646
+
2647
+ # Restart from the new apex
2648
+ i = apexIndex + 1
2649
+ continue
2650
+
2651
+ i += 1
2652
+
2653
+ # Finally, add the end point if it is not already in the path
2654
+ if path_vertices[-1] is not vertexB:
2655
+ path_vertices.append(vertexB)
2656
+
2657
+ # ------------------------------------------------------------
2658
+ # 4. Build and return the Topologic wire
2659
+ # ------------------------------------------------------------
2660
+ return_wire = Wire.ByVertices(path_vertices, close=False, silent=True)
2661
+ bb = Wire.BoundingRectangle(face)
2662
+ d = Topology.Dictionary(bb)
2663
+ width = Dictionary.ValueAtKey(d, "width")
2664
+ length = Dictionary.ValueAtKey(d, "length")
2665
+ size = max(width, length)
2666
+ percentage = 0.25 # Start with 25% of the total size
2667
+ is_ok = False
2668
+ while is_ok == False and percentage > 0:
2669
+ new_wire = Wire.Simplify(return_wire, tolerance=size*percentage, silent=True)
2670
+ test_wire = Topology.Scale(new_wire, Topology.Centroid(new_wire), 0.95, 0.95, 1)
2671
+ result = Topology.Difference(test_wire, face, tolerance=tolerance, silent=True)
2672
+ if result is None:
2673
+ is_ok = True
2674
+ return_wire = new_wire
2675
+ percentage -= 0.01
2676
+ print("Wire.Funnel - Result:", result)
2677
+ print("Wire.Funnel - Percentage:", percentage)
2678
+ return new_wire
2679
+
2430
2680
  @staticmethod
2431
2681
  def InteriorAngles(wire, tolerance: float = 0.0001, mantissa: int = 6) -> list:
2432
2682
  """
@@ -4188,7 +4438,7 @@ class Wire():
4188
4438
  if not silent:
4189
4439
  print("Wire.Simplify - Warning: Could not generate enough vertices for a simplified wire. Returning the original wire.")
4190
4440
  wire
4191
- new_wire = Wire.ByVertices(new_vertices, close=Wire.IsClosed(wire), tolerance=tolerance)
4441
+ new_wire = Wire.ByVertices(new_vertices, close=Wire.IsClosed(wire), tolerance=tolerance, silent=True)
4192
4442
  if not Topology.IsInstance(new_wire, "wire"):
4193
4443
  if not silent:
4194
4444
  print("Wire.Simplify - Warning: Could not generate a simplified wire. Returning the original wire.")
@@ -4713,6 +4963,124 @@ class Wire():
4713
4963
  sv, ev = Wire.StartEndVertices(wire, silent=silent)
4714
4964
  return sv
4715
4965
 
4966
+ @staticmethod
4967
+ def StraightenInFace(wire, face, tolerance: float = 0.0001):
4968
+ """
4969
+ Returns a new Wire obtained by recursively replacing segments of the
4970
+ input wire with the longest possible straight edge that is fully
4971
+ embedded in the given face.
4972
+
4973
+ For each starting vertex v_i along the wire, this method searches for
4974
+ the furthest vertex v_j (j > i) such that the straight Edge between
4975
+ v_i and v_j satisfies:
4976
+
4977
+ Topology.Difference(edge, face) == None
4978
+
4979
+ i.e. the edge lies completely within (or on the boundary of) the face.
4980
+ All edges of the original wire between vertex indices i and j are then
4981
+ replaced by this straight edge, and the process is repeated recursively
4982
+ from index j.
4983
+
4984
+ Parameters
4985
+ ----------
4986
+ wire : topologic_core.Wire
4987
+ The input path wire whose vertices define the route to be
4988
+ straightened.
4989
+ face : topologic_core.Face
4990
+ The face within which the straightened edges must lie.
4991
+ tolerance : float , optional
4992
+ Numerical tolerance used for internal robustness checks. The
4993
+ Topology.Difference call itself is left with its default tolerance.
4994
+ Default is 1e-6.
4995
+
4996
+ Returns
4997
+ -------
4998
+ wire : topologic_core.Wire
4999
+ A new Wire whose vertices define the recursively straightened path.
5000
+ """
5001
+ from topologicpy.Vertex import Vertex
5002
+ from topologicpy.Edge import Edge
5003
+ from topologicpy.Wire import Wire
5004
+ from topologicpy.Face import Face
5005
+ from topologicpy.Topology import Topology
5006
+ # Get ordered vertices of the wire
5007
+ vertices = Topology.Vertices(wire)
5008
+ n = len(vertices)
5009
+
5010
+ if n <= 2:
5011
+ # Nothing to straighten
5012
+ return wire
5013
+
5014
+ def _edge_inside_face(v_start, v_end):
5015
+ """
5016
+ Returns True if the straight edge between v_start and v_end is
5017
+ fully embedded in the face, i.e. Topology.Difference(edge, face)
5018
+ returns None.
5019
+ """
5020
+ if v_start is v_end:
5021
+ return True
5022
+ edge = Edge.ByStartVertexEndVertex(v_start, v_end)
5023
+ diff = Topology.Difference(edge, face)
5024
+ return diff is None
5025
+
5026
+ def _find_longest_valid_index(start_idx):
5027
+ """
5028
+ For a fixed start_idx, search for the largest index j >= start_idx+1
5029
+ such that the direct edge (vertices[start_idx], vertices[j]) is
5030
+ fully inside the face.
5031
+
5032
+ If for any reason no such j exists (which should not happen if the
5033
+ original wire lies in the face), it falls back to start_idx + 1.
5034
+ """
5035
+ v_start = vertices[start_idx]
5036
+ best_j = None
5037
+
5038
+ for j in range(start_idx + 1, n):
5039
+ v_end = vertices[j]
5040
+ if _edge_inside_face(v_start, v_end):
5041
+ best_j = j
5042
+ # Do NOT break on failure: a further vertex might still
5043
+ # be reachable by a straight edge that stays in the face.
5044
+
5045
+ if best_j is None:
5046
+ # Fallback: use the immediate next vertex to avoid stalling
5047
+ best_j = min(start_idx + 1, n - 1)
5048
+
5049
+ return best_j
5050
+
5051
+ def _straighten_recursive(start_idx, out_vertices):
5052
+ """
5053
+ Recursive helper.
5054
+
5055
+ Appends the chosen vertices to out_vertices. At each step, it
5056
+ decides how far it can jump from start_idx with a single straight
5057
+ edge inside the face, then recurses from that new index.
5058
+ """
5059
+ # Base case: we are at the last vertex
5060
+ if start_idx == n - 1:
5061
+ out_vertices.append(vertices[start_idx])
5062
+ return
5063
+
5064
+ # Find furthest valid index reachable from start_idx
5065
+ next_idx = _find_longest_valid_index(start_idx)
5066
+
5067
+ # Add the starting vertex for this segment
5068
+ out_vertices.append(vertices[start_idx])
5069
+
5070
+ # Recurse from the chosen furthest index
5071
+ _straighten_recursive(next_idx, out_vertices)
5072
+
5073
+ # Run the recursion
5074
+ new_vertices = []
5075
+ _straighten_recursive(0, new_vertices)
5076
+
5077
+ # In case of any numerical quirks, ensure the last original vertex is present
5078
+ if new_vertices[-1] is not vertices[-1]:
5079
+ new_vertices.append(vertices[-1])
5080
+
5081
+ # Build the new straightened wire
5082
+ return Wire.ByVertices(new_vertices, close=False)
5083
+
4716
5084
  @staticmethod
4717
5085
  def Trapezoid(origin= None, widthA: float = 1.0, widthB: float = 0.75, offsetA: float = 0.0, offsetB: float = 0.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
4718
5086
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.89'
1
+ __version__ = '0.8.91'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.89
3
+ Version: 0.8.91
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
@@ -12,7 +12,7 @@ topologicpy/Dictionary.py,sha256=goODXIM6AoC5Qn_d8LGc5pRoxZKgIWbkn3IOEbsQ4c4,449
12
12
  topologicpy/Edge.py,sha256=aiRd1xZgG2GGYHxva0bM-kDy3AVmwGA_S2pMur8EeMg,74911
13
13
  topologicpy/EnergyModel.py,sha256=MEai1GF1hINeH5bhclJj_lpMU3asFTvW2RlPm40GNj4,57794
14
14
  topologicpy/Face.py,sha256=qAl36LcwiyRclMM2pI9NyWHzmgNlaykXiJx1wu10RmA,201317
15
- topologicpy/Graph.py,sha256=AfX4M_55zGBOm5RwpVEkr3JHv-hITFh9ckW1_jsLxFs,808052
15
+ topologicpy/Graph.py,sha256=Tokxg55wHmx5hd9eai7EMRUdptADLt7HmJNrhq00UZk,810723
16
16
  topologicpy/Grid.py,sha256=3OsBMyHh4w8gpFOTMKHMNTpo62V0CwRNu5cwm87yDUA,18421
17
17
  topologicpy/Helper.py,sha256=NsmMlbbKFPRX6jfoko-ZQVQ7MBsfVp9FD0ZvC2U7q-8,32002
18
18
  topologicpy/Honeybee.py,sha256=dBk01jIvxjQMGHqSarM1Cukv16ot4Op7Dwlitn2OMoc,48990
@@ -29,11 +29,11 @@ topologicpy/Sun.py,sha256=ezisiHfc2nd7A_8w0Ykq2VgbS0A9WNSg-tBwvfTQAVM,36735
29
29
  topologicpy/Topology.py,sha256=E_AyPPCIx_Eq-UT74QS3LKFXIwdwekRjJJGTo1CRMRY,548577
30
30
  topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
31
31
  topologicpy/Vertex.py,sha256=26TrlX9OCZUN-lMlZG3g4RHTWBqw69NW4AOEgRz_YMo,91269
32
- topologicpy/Wire.py,sha256=au0ZkuuZgVzHYE5E1fRwflRT3win0yTivHKOhonAzUk,234116
32
+ topologicpy/Wire.py,sha256=Rhqw0CGEWIMVL1ICQqkCp9G-VnhhHLhEiDDR00fAn_s,248919
33
33
  topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
34
- topologicpy/version.py,sha256=YhsAc7e0YNllH7mN5LamhWpA_q-FmHWAFByeNTu5xTk,23
35
- topologicpy-0.8.89.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
36
- topologicpy-0.8.89.dist-info/METADATA,sha256=55cNepIhl59TBcxtlcTmihxcT8dcait0fIEuKHXUq3A,10535
37
- topologicpy-0.8.89.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
- topologicpy-0.8.89.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
39
- topologicpy-0.8.89.dist-info/RECORD,,
34
+ topologicpy/version.py,sha256=Pd3D7UyrHnkE76qM_AOq2A2ifXXq1OxDwEg8Q1gGwk8,23
35
+ topologicpy-0.8.91.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
36
+ topologicpy-0.8.91.dist-info/METADATA,sha256=S_NaUxOVkvDCJJ4xVVdtRSLZnkW0tzPwic19V2FLkuw,10535
37
+ topologicpy-0.8.91.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ topologicpy-0.8.91.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
39
+ topologicpy-0.8.91.dist-info/RECORD,,