topologicpy 0.8.91__py3-none-any.whl → 0.8.93__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- topologicpy/Cell.py +1 -1
- topologicpy/Topology.py +62 -43
- topologicpy/Wire.py +652 -64
- topologicpy/version.py +1 -1
- {topologicpy-0.8.91.dist-info → topologicpy-0.8.93.dist-info}/METADATA +1 -1
- {topologicpy-0.8.91.dist-info → topologicpy-0.8.93.dist-info}/RECORD +9 -9
- {topologicpy-0.8.91.dist-info → topologicpy-0.8.93.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.91.dist-info → topologicpy-0.8.93.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.91.dist-info → topologicpy-0.8.93.dist-info}/top_level.txt +0 -0
topologicpy/Cell.py
CHANGED
|
@@ -1753,7 +1753,7 @@ class Cell():
|
|
|
1753
1753
|
if placement.lower() == "bottom":
|
|
1754
1754
|
egg = Topology.Translate(egg, 0, 0, height/2)
|
|
1755
1755
|
elif placement.lower() == "lowerleft":
|
|
1756
|
-
bb =
|
|
1756
|
+
bb = Topology.BoundingBox(egg)
|
|
1757
1757
|
d = Topology.Dictionary(bb)
|
|
1758
1758
|
width = Dictionary.ValueAtKey(d, 'width')
|
|
1759
1759
|
length = Dictionary.ValueAtKey(d, 'length')
|
topologicpy/Topology.py
CHANGED
|
@@ -838,10 +838,15 @@ class Topology():
|
|
|
838
838
|
"""
|
|
839
839
|
|
|
840
840
|
from topologicpy.Dictionary import Dictionary
|
|
841
|
+
import inspect
|
|
841
842
|
|
|
842
843
|
if not Topology.IsInstance(topology, "Topology"):
|
|
843
844
|
if not silent:
|
|
844
845
|
print("Topology.Apertures - Error: the input topology parameter is not a valid topology. Returning None.")
|
|
846
|
+
print("Topology:", topology)
|
|
847
|
+
curframe = inspect.currentframe()
|
|
848
|
+
calframe = inspect.getouterframes(curframe, 2)
|
|
849
|
+
print('caller name:', calframe[1][3])
|
|
845
850
|
return None
|
|
846
851
|
|
|
847
852
|
apertures = []
|
|
@@ -2521,39 +2526,41 @@ class Topology():
|
|
|
2521
2526
|
f = Face.ByWires(eb, ib)
|
|
2522
2527
|
new_faces.append(f)
|
|
2523
2528
|
topology = Topology.SelfMerge(Cluster.ByTopologies(new_faces))
|
|
2524
|
-
if
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
if
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2529
|
+
if Topology.IsInstance(topology, "topology"):
|
|
2530
|
+
if removeCoplanarFaces:
|
|
2531
|
+
topology = Topology.RemoveCoplanarFaces(topology, epsilon=epsilon, tolerance=tolerance)
|
|
2532
|
+
if Topology.IsInstance(topology, "topology"):
|
|
2533
|
+
if transferDictionaries:
|
|
2534
|
+
element_dict = {
|
|
2535
|
+
"TOPOLOGIC_id": str(Topology.UUID(topology)),
|
|
2536
|
+
"TOPOLOGIC_name": getattr(element, 'Name', "Untitled"),
|
|
2537
|
+
"TOPOLOGIC_type": Topology.TypeAsString(topology),
|
|
2538
|
+
"IFC_global_id": getattr(element, 'GlobalId', 0),
|
|
2539
|
+
"IFC_name": getattr(element, 'Name', "Untitled"),
|
|
2540
|
+
"IFC_type": element_type
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
# Optionally add property sets
|
|
2544
|
+
psets = {}
|
|
2545
|
+
if hasattr(element, 'IsDefinedBy'):
|
|
2546
|
+
for rel in element.IsDefinedBy:
|
|
2547
|
+
if rel.is_a('IfcRelDefinesByProperties'):
|
|
2548
|
+
pdef = rel.RelatingPropertyDefinition
|
|
2549
|
+
if pdef and pdef.is_a('IfcPropertySet'):
|
|
2550
|
+
key = f"IFC_{pdef.Name}"
|
|
2551
|
+
props = {}
|
|
2552
|
+
for prop in pdef.HasProperties:
|
|
2553
|
+
if prop.is_a('IfcPropertySingleValue') and prop.NominalValue:
|
|
2554
|
+
props[f"IFC_{prop.Name}"] = prop.NominalValue.wrappedValue
|
|
2555
|
+
psets[key] = props
|
|
2556
|
+
|
|
2557
|
+
final_dict = Dictionary.ByPythonDictionary(element_dict)
|
|
2558
|
+
if psets:
|
|
2559
|
+
pset_dict = Dictionary.ByPythonDictionary(psets)
|
|
2560
|
+
final_dict = Dictionary.ByMergedDictionaries([final_dict, pset_dict])
|
|
2561
|
+
|
|
2562
|
+
topology = Topology.SetDictionary(topology, final_dict)
|
|
2563
|
+
topologies.append(topology)
|
|
2557
2564
|
if not it.next():
|
|
2558
2565
|
break
|
|
2559
2566
|
|
|
@@ -11904,7 +11911,7 @@ class Topology():
|
|
|
11904
11911
|
return Topology._Boolean(topologyA, topologyB, operation="union", tranDict=tranDict, tolerance=tolerance, silent=silent)
|
|
11905
11912
|
|
|
11906
11913
|
@staticmethod
|
|
11907
|
-
def UUID(topology, namespace="topologicpy"):
|
|
11914
|
+
def UUID(topology, namespace="topologicpy", silent: bool = False):
|
|
11908
11915
|
"""
|
|
11909
11916
|
Generate a UUID v5 based on the provided content and a fixed namespace.
|
|
11910
11917
|
|
|
@@ -11913,7 +11920,9 @@ class Topology():
|
|
|
11913
11920
|
topology : topologic_core.Topology
|
|
11914
11921
|
The input topology
|
|
11915
11922
|
namespace : str , optional
|
|
11916
|
-
The base namescape to use for generating the UUID
|
|
11923
|
+
The base namescape to use for generating the UUID.
|
|
11924
|
+
silent : bool , optional
|
|
11925
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
|
11917
11926
|
|
|
11918
11927
|
Returns
|
|
11919
11928
|
-------
|
|
@@ -11924,6 +11933,16 @@ class Topology():
|
|
|
11924
11933
|
import uuid
|
|
11925
11934
|
from topologicpy.Dictionary import Dictionary
|
|
11926
11935
|
from topologicpy.Graph import Graph
|
|
11936
|
+
import inspect
|
|
11937
|
+
|
|
11938
|
+
if not Topology.IsInstance(topology, "topology"):
|
|
11939
|
+
if not silent:
|
|
11940
|
+
print("Topology.UUID - Error: The input topology parameter is not a valid topology. Returning None.")
|
|
11941
|
+
print("Topology:", topology)
|
|
11942
|
+
curframe = inspect.currentframe()
|
|
11943
|
+
calframe = inspect.getouterframes(curframe, 2)
|
|
11944
|
+
print('caller name:', calframe[1][3])
|
|
11945
|
+
return None
|
|
11927
11946
|
|
|
11928
11947
|
predefined_namespace_dns = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
|
|
11929
11948
|
namespace_uuid = uuid.uuid5(predefined_namespace_dns, namespace)
|
|
@@ -11935,14 +11954,14 @@ class Topology():
|
|
|
11935
11954
|
final_str = verts_str+edges_str+dict_str
|
|
11936
11955
|
uuid_str = uuid.uuid5(namespace_uuid, final_str)
|
|
11937
11956
|
else:
|
|
11938
|
-
cellComplexes = Topology.CellComplexes(topology)
|
|
11939
|
-
cells = Topology.Cells(topology)
|
|
11940
|
-
shells = Topology.Shells(topology)
|
|
11941
|
-
faces = Topology.Faces(topology)
|
|
11942
|
-
wires = Topology.Wires(topology)
|
|
11943
|
-
edges = Topology.Edges(topology)
|
|
11944
|
-
vertices = Topology.Vertices(topology)
|
|
11945
|
-
apertures = Topology.Apertures(topology, subTopologyType="all")
|
|
11957
|
+
cellComplexes = Topology.CellComplexes(topology, silent=True) or []
|
|
11958
|
+
cells = Topology.Cells(topology, silent=True) or []
|
|
11959
|
+
shells = Topology.Shells(topology, silent=True) or []
|
|
11960
|
+
faces = Topology.Faces(topology, silent=True) or []
|
|
11961
|
+
wires = Topology.Wires(topology, silent=True) or []
|
|
11962
|
+
edges = Topology.Edges(topology, silent=True) or []
|
|
11963
|
+
vertices = Topology.Vertices(topology, silent=True) or []
|
|
11964
|
+
apertures = Topology.Apertures(topology, subTopologyType="all") or []
|
|
11946
11965
|
subTopologies = cellComplexes+cells+shells+faces+wires+edges+vertices+apertures
|
|
11947
11966
|
dictionaries = [Dictionary.PythonDictionary(Topology.Dictionary(topology))]
|
|
11948
11967
|
dictionaries += [Dictionary.PythonDictionary(Topology.Dictionary(s)) for s in subTopologies]
|
topologicpy/Wire.py
CHANGED
|
@@ -946,6 +946,168 @@ class Wire():
|
|
|
946
946
|
vertices = Topology.Vertices(cluster)
|
|
947
947
|
return Wire.ByVertices(vertices, close=close, tolerance=tolerance, silent=silent)
|
|
948
948
|
|
|
949
|
+
|
|
950
|
+
@staticmethod
|
|
951
|
+
def Cage(origin=None,
|
|
952
|
+
width: float = 1.0, length: float = 1.0, height: float = 1.0,
|
|
953
|
+
uSides: int = 2, vSides: int = 2, wSides: int = 2,
|
|
954
|
+
direction: list = [0, 0, 1], placement: str = "center",
|
|
955
|
+
mantissa: int = 6, tolerance: float = 0.0001):
|
|
956
|
+
"""
|
|
957
|
+
Creates a prismatic 3D cage as a Wire, with edges only on the outer
|
|
958
|
+
surfaces of the volume (no interior lines).
|
|
959
|
+
|
|
960
|
+
Parameters
|
|
961
|
+
----------
|
|
962
|
+
origin : topologic_core.Vertex , optional
|
|
963
|
+
The placement origin of the cage:
|
|
964
|
+
- If placement == "center": the geometric center of the cage
|
|
965
|
+
is placed at this origin.
|
|
966
|
+
- If placement == "corner": the minimum corner of the cage
|
|
967
|
+
is placed at this origin.
|
|
968
|
+
If None, the cage is created around (0, 0, 0) accordingly.
|
|
969
|
+
width : float , optional
|
|
970
|
+
The size of the cage in the local X direction. Default is 1.0.
|
|
971
|
+
length : float , optional
|
|
972
|
+
The size of the cage in the local Y direction. Default is 1.0.
|
|
973
|
+
height : float , optional
|
|
974
|
+
The size of the cage in the local Z direction. Default is 1.0.
|
|
975
|
+
uSides : int , optional
|
|
976
|
+
The number of subdivisions in the local X direction. Must be >= 1.
|
|
977
|
+
Default is 2.
|
|
978
|
+
vSides : int , optional
|
|
979
|
+
The number of subdivisions in the local Y direction. Must be >= 1.
|
|
980
|
+
Default is 2.
|
|
981
|
+
wSides : int , optional
|
|
982
|
+
The number of subdivisions in the local Z direction. Must be >= 1.
|
|
983
|
+
Default is 2.
|
|
984
|
+
direction : list , optional
|
|
985
|
+
The vector representing the up direction of the lattice. Default is [0, 0, 1].
|
|
986
|
+
placement : str , optional
|
|
987
|
+
The description of the placement of the origin of the lattice. This can be "bottom", "center", or "lowerleft". It is case insensitive. Default is "center".
|
|
988
|
+
mantissa : int , optional
|
|
989
|
+
The number of decimal places to round the result to. Default is 6.
|
|
990
|
+
tolerance : float , optional
|
|
991
|
+
The desired tolerance. Default is 0.0001.
|
|
992
|
+
|
|
993
|
+
Returns
|
|
994
|
+
-------
|
|
995
|
+
topologic_core.Wire or None
|
|
996
|
+
The resulting cage Wire, or None if inputs are invalid.
|
|
997
|
+
"""
|
|
998
|
+
from topologicpy.Vertex import Vertex
|
|
999
|
+
from topologicpy.Edge import Edge
|
|
1000
|
+
from topologicpy.Wire import Wire
|
|
1001
|
+
from topologicpy.Topology import Topology
|
|
1002
|
+
from topologicpy.Vector import Vector
|
|
1003
|
+
import math
|
|
1004
|
+
|
|
1005
|
+
# -------------------------
|
|
1006
|
+
# Validation
|
|
1007
|
+
# -------------------------
|
|
1008
|
+
if uSides < 1 or vSides < 1 or wSides < 1:
|
|
1009
|
+
print("Wire.Cage - Error: uSides, vSides, and wSides must be >= 1. Returning None.")
|
|
1010
|
+
return None
|
|
1011
|
+
|
|
1012
|
+
if origin is None:
|
|
1013
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
|
1014
|
+
|
|
1015
|
+
# Local origin at (0,0,0) for construction and rotation
|
|
1016
|
+
local_origin = Vertex.ByCoordinates(0, 0, 0)
|
|
1017
|
+
|
|
1018
|
+
# -------------------------
|
|
1019
|
+
# Local Placement Offsets
|
|
1020
|
+
# -------------------------
|
|
1021
|
+
# We construct the cage in a local coordinate system.
|
|
1022
|
+
if str(placement).lower() == "center":
|
|
1023
|
+
ox = -width * 0.5
|
|
1024
|
+
oy = -length * 0.5
|
|
1025
|
+
oz = -height * 0.5
|
|
1026
|
+
elif str(placement).lower() == "bottom":
|
|
1027
|
+
ox = -width * 0.5
|
|
1028
|
+
oy = -length * 0.5
|
|
1029
|
+
oz = 0
|
|
1030
|
+
else: # "lowerleft"
|
|
1031
|
+
ox = 0.0
|
|
1032
|
+
oy = 0.0
|
|
1033
|
+
oz = 0.0
|
|
1034
|
+
|
|
1035
|
+
# -------------------------
|
|
1036
|
+
# Step Sizes
|
|
1037
|
+
# -------------------------
|
|
1038
|
+
du = width / uSides
|
|
1039
|
+
dv = length / vSides
|
|
1040
|
+
dw = height / wSides
|
|
1041
|
+
|
|
1042
|
+
# -------------------------
|
|
1043
|
+
# Grid Coordinates (local)
|
|
1044
|
+
# -------------------------
|
|
1045
|
+
xs = [round(ox + i * du, mantissa) for i in range(uSides + 1)]
|
|
1046
|
+
ys = [round(oy + j * dv, mantissa) for j in range(vSides + 1)]
|
|
1047
|
+
zs = [round(oz + k * dw, mantissa) for k in range(wSides + 1)]
|
|
1048
|
+
|
|
1049
|
+
edges = []
|
|
1050
|
+
|
|
1051
|
+
# ------------------------------------------------------------------
|
|
1052
|
+
# X-direction edges on boundary surfaces (y,z)
|
|
1053
|
+
# Edge from (x_min, y_j, z_k) to (x_max, y_j, z_k)
|
|
1054
|
+
# Only if j is boundary OR k is boundary → lies on outer surface.
|
|
1055
|
+
# ------------------------------------------------------------------
|
|
1056
|
+
for j in range(vSides + 1):
|
|
1057
|
+
for k in range(wSides + 1):
|
|
1058
|
+
if j in (0, vSides) or k in (0, wSides):
|
|
1059
|
+
y = ys[j]
|
|
1060
|
+
z = zs[k]
|
|
1061
|
+
v0 = Vertex.ByCoordinates(xs[0], y, z)
|
|
1062
|
+
v1 = Vertex.ByCoordinates(xs[-1], y, z)
|
|
1063
|
+
edges.append(Edge.ByVertices(v0, v1))
|
|
1064
|
+
|
|
1065
|
+
# ------------------------------------------------------------------
|
|
1066
|
+
# Y-direction edges on boundary surfaces (x,z)
|
|
1067
|
+
# Edge from (x_i, y_min, z_k) to (x_i, y_max, z_k)
|
|
1068
|
+
# Only if i is boundary OR k is boundary.
|
|
1069
|
+
# ------------------------------------------------------------------
|
|
1070
|
+
for i in range(uSides + 1):
|
|
1071
|
+
for k in range(wSides + 1):
|
|
1072
|
+
if i in (0, uSides) or k in (0, wSides):
|
|
1073
|
+
x = xs[i]
|
|
1074
|
+
z = zs[k]
|
|
1075
|
+
v0 = Vertex.ByCoordinates(x, ys[0], z)
|
|
1076
|
+
v1 = Vertex.ByCoordinates(x, ys[-1], z)
|
|
1077
|
+
edges.append(Edge.ByVertices(v0, v1))
|
|
1078
|
+
|
|
1079
|
+
# ------------------------------------------------------------------
|
|
1080
|
+
# Z-direction edges on boundary surfaces (x,y)
|
|
1081
|
+
# Edge from (x_i, y_j, z_min) to (x_i, y_j, z_max)
|
|
1082
|
+
# Only if i is boundary OR j is boundary.
|
|
1083
|
+
# ------------------------------------------------------------------
|
|
1084
|
+
for i in range(uSides + 1):
|
|
1085
|
+
for j in range(vSides + 1):
|
|
1086
|
+
if i in (0, uSides) or j in (0, vSides):
|
|
1087
|
+
x = xs[i]
|
|
1088
|
+
y = ys[j]
|
|
1089
|
+
v0 = Vertex.ByCoordinates(x, y, zs[0])
|
|
1090
|
+
v1 = Vertex.ByCoordinates(x, y, zs[-1])
|
|
1091
|
+
edges.append(Edge.ByVertices(v0, v1))
|
|
1092
|
+
|
|
1093
|
+
# -------------------------
|
|
1094
|
+
# Build Wire in Local Space
|
|
1095
|
+
# -------------------------
|
|
1096
|
+
if not edges:
|
|
1097
|
+
print("Wire.Cage - Warning: No edges created. Returning None.")
|
|
1098
|
+
return None
|
|
1099
|
+
|
|
1100
|
+
cage = Wire.ByEdges(edges)
|
|
1101
|
+
|
|
1102
|
+
# -------------------------
|
|
1103
|
+
# Orient and Place
|
|
1104
|
+
# -------------------------
|
|
1105
|
+
cage = Topology.Orient(cage, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
|
1106
|
+
cage = Topology.Place(cage, originA=Vertex.Origin(), originB=origin)
|
|
1107
|
+
|
|
1108
|
+
return cage
|
|
1109
|
+
|
|
1110
|
+
|
|
949
1111
|
@staticmethod
|
|
950
1112
|
def Circle(origin= None, radius: float = 0.5, sides: int = 16, fromAngle: float = 0.0, toAngle: float = 360.0, close: bool = True, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001, silent: bool = False):
|
|
951
1113
|
"""
|
|
@@ -3199,6 +3361,124 @@ class Wire():
|
|
|
3199
3361
|
i_shape = Topology.Orient(i_shape, origin=origin, dirA=[0, 0, 1], dirB=direction)
|
|
3200
3362
|
return i_shape
|
|
3201
3363
|
|
|
3364
|
+
|
|
3365
|
+
|
|
3366
|
+
@staticmethod
|
|
3367
|
+
def Lattice(origin=None,
|
|
3368
|
+
width: float = 1.0, length: float = 1.0, height: float = 1.0,
|
|
3369
|
+
uSides: int = 2, vSides: int = 2, wSides: int = 2,
|
|
3370
|
+
direction: list = [0, 0, 1], placement: str = "center",
|
|
3371
|
+
mantissa: int = 6, tolerance: float = 0.0001):
|
|
3372
|
+
"""
|
|
3373
|
+
Creates a prismatic 3D lattice as a Wire.
|
|
3374
|
+
|
|
3375
|
+
Parameters
|
|
3376
|
+
----------
|
|
3377
|
+
origin : topologic_core.Vertex , optional
|
|
3378
|
+
Placement origin.
|
|
3379
|
+
width, length, height : float
|
|
3380
|
+
Lattice extents.
|
|
3381
|
+
uSides, vSides, wSides : int
|
|
3382
|
+
Divisions along X, Y, Z.
|
|
3383
|
+
direction : list , optional
|
|
3384
|
+
The vector representing the up direction of the lattice. Default is [0, 0, 1].
|
|
3385
|
+
placement : str , optional
|
|
3386
|
+
The description of the placement of the origin of the lattice. This can be "bottom", "center", or "lowerleft". It is case insensitive. Default is "center".
|
|
3387
|
+
mantissa : int , optional
|
|
3388
|
+
The number of decimal places to round the result to. Default is 6.
|
|
3389
|
+
tolerance : float , optional
|
|
3390
|
+
The desired tolerance. Default is 0.0001.
|
|
3391
|
+
|
|
3392
|
+
Returns
|
|
3393
|
+
-------
|
|
3394
|
+
topologic_core.Wire
|
|
3395
|
+
"""
|
|
3396
|
+
|
|
3397
|
+
from topologicpy.Vertex import Vertex
|
|
3398
|
+
from topologicpy.Edge import Edge
|
|
3399
|
+
from topologicpy.Wire import Wire
|
|
3400
|
+
from topologicpy.Topology import Topology
|
|
3401
|
+
from topologicpy.Vector import Vector
|
|
3402
|
+
import math
|
|
3403
|
+
|
|
3404
|
+
# -------------------------
|
|
3405
|
+
# Validation
|
|
3406
|
+
# -------------------------
|
|
3407
|
+
if uSides < 1 or vSides < 1 or wSides < 1:
|
|
3408
|
+
return None
|
|
3409
|
+
|
|
3410
|
+
if origin is None:
|
|
3411
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
|
3412
|
+
|
|
3413
|
+
# -------------------------
|
|
3414
|
+
# Placement Offsets
|
|
3415
|
+
# -------------------------
|
|
3416
|
+
if placement.lower() == "center":
|
|
3417
|
+
ox = -width * 0.5
|
|
3418
|
+
oy = -length * 0.5
|
|
3419
|
+
oz = -height * 0.5
|
|
3420
|
+
elif placement.lower() == "bottom":
|
|
3421
|
+
ox = -width * 0.5
|
|
3422
|
+
oy = -length * 0.5
|
|
3423
|
+
oz = 0
|
|
3424
|
+
else:
|
|
3425
|
+
ox = oy = oz = 0.0
|
|
3426
|
+
|
|
3427
|
+
# -------------------------
|
|
3428
|
+
# Step Sizes
|
|
3429
|
+
# -------------------------
|
|
3430
|
+
du = width / uSides
|
|
3431
|
+
dv = length / vSides
|
|
3432
|
+
dw = height / wSides
|
|
3433
|
+
|
|
3434
|
+
# -------------------------
|
|
3435
|
+
# Precompute Grid Coordinates
|
|
3436
|
+
# -------------------------
|
|
3437
|
+
xs = [round(ox + i * du, mantissa) for i in range(uSides + 1)]
|
|
3438
|
+
ys = [round(oy + j * dv, mantissa) for j in range(vSides + 1)]
|
|
3439
|
+
zs = [round(oz + k * dw, mantissa) for k in range(wSides + 1)]
|
|
3440
|
+
|
|
3441
|
+
edges = []
|
|
3442
|
+
|
|
3443
|
+
# -------------------------
|
|
3444
|
+
# X-Direction Lines
|
|
3445
|
+
# -------------------------
|
|
3446
|
+
for y in ys:
|
|
3447
|
+
for z in zs:
|
|
3448
|
+
v0 = Vertex.ByCoordinates(xs[0], y, z)
|
|
3449
|
+
v1 = Vertex.ByCoordinates(xs[-1], y, z)
|
|
3450
|
+
edges.append(Edge.ByVertices(v0, v1))
|
|
3451
|
+
|
|
3452
|
+
# -------------------------
|
|
3453
|
+
# Y-Direction Lines
|
|
3454
|
+
# -------------------------
|
|
3455
|
+
for x in xs:
|
|
3456
|
+
for z in zs:
|
|
3457
|
+
v0 = Vertex.ByCoordinates(x, ys[0], z)
|
|
3458
|
+
v1 = Vertex.ByCoordinates(x, ys[-1], z)
|
|
3459
|
+
edges.append(Edge.ByVertices(v0, v1))
|
|
3460
|
+
|
|
3461
|
+
# -------------------------
|
|
3462
|
+
# Z-Direction Lines
|
|
3463
|
+
# -------------------------
|
|
3464
|
+
for x in xs:
|
|
3465
|
+
for y in ys:
|
|
3466
|
+
v0 = Vertex.ByCoordinates(x, y, zs[0])
|
|
3467
|
+
v1 = Vertex.ByCoordinates(x, y, zs[-1])
|
|
3468
|
+
edges.append(Edge.ByVertices(v0, v1))
|
|
3469
|
+
|
|
3470
|
+
# -------------------------
|
|
3471
|
+
# Build Wire
|
|
3472
|
+
# -------------------------
|
|
3473
|
+
lattice = Wire.ByEdges(edges)
|
|
3474
|
+
|
|
3475
|
+
# -------------------------
|
|
3476
|
+
# Orient and Place
|
|
3477
|
+
# -------------------------
|
|
3478
|
+
lattice = Topology.Orient(lattice, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
|
3479
|
+
lattice = Topology.Place(lattice, originA=Vertex.Origin(), originB=origin)
|
|
3480
|
+
return lattice
|
|
3481
|
+
|
|
3202
3482
|
@staticmethod
|
|
3203
3483
|
def Length(wire, mantissa: int = 6) -> float:
|
|
3204
3484
|
"""
|
|
@@ -4964,34 +5244,32 @@ class Wire():
|
|
|
4964
5244
|
return sv
|
|
4965
5245
|
|
|
4966
5246
|
@staticmethod
|
|
4967
|
-
def
|
|
5247
|
+
def Straighten(wire, host, obstacles: list = None, portals: list = None,
|
|
5248
|
+
tolerance: float = 0.0001, silent: bool = False):
|
|
4968
5249
|
"""
|
|
4969
5250
|
Returns a new Wire obtained by recursively replacing segments of the
|
|
4970
|
-
input wire with the longest possible straight edge that
|
|
4971
|
-
embedded in the given
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
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.
|
|
5251
|
+
input wire with the longest possible straight edge that:
|
|
5252
|
+
1. Is fully embedded in the given host.
|
|
5253
|
+
2. Avoids intersection with an optional list of obstacle topologies.
|
|
5254
|
+
3. Continues to pass through (intersects) an optional list of portal
|
|
5255
|
+
topologies that the original input wire intersects.
|
|
4983
5256
|
|
|
4984
5257
|
Parameters
|
|
4985
5258
|
----------
|
|
4986
5259
|
wire : topologic_core.Wire
|
|
4987
5260
|
The input path wire whose vertices define the route to be
|
|
4988
5261
|
straightened.
|
|
4989
|
-
|
|
4990
|
-
The
|
|
5262
|
+
host : topologic_core.Topology
|
|
5263
|
+
The host within which the straightened edges must lie.
|
|
5264
|
+
obstacles : list, optional
|
|
5265
|
+
The list of topologies with which the straightened edges must not intersect.
|
|
5266
|
+
portals : list, optional
|
|
5267
|
+
The list of topologies with which the straightened edges must intersect.
|
|
5268
|
+
Portals with which the original wire does NOT intersect are ignored.
|
|
4991
5269
|
tolerance : float , optional
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
Default is
|
|
5270
|
+
The desired tolerance. Default is 0.0001.
|
|
5271
|
+
silent : bool , optional
|
|
5272
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
|
4995
5273
|
|
|
4996
5274
|
Returns
|
|
4997
5275
|
-------
|
|
@@ -5001,8 +5279,45 @@ class Wire():
|
|
|
5001
5279
|
from topologicpy.Vertex import Vertex
|
|
5002
5280
|
from topologicpy.Edge import Edge
|
|
5003
5281
|
from topologicpy.Wire import Wire
|
|
5004
|
-
from topologicpy.
|
|
5282
|
+
from topologicpy.Cluster import Cluster
|
|
5005
5283
|
from topologicpy.Topology import Topology
|
|
5284
|
+
|
|
5285
|
+
# Defensive defaults
|
|
5286
|
+
if obstacles is None:
|
|
5287
|
+
obstacles = []
|
|
5288
|
+
if portals is None:
|
|
5289
|
+
portals = []
|
|
5290
|
+
|
|
5291
|
+
# ----------------------------------------------------------------------
|
|
5292
|
+
# Basic validation
|
|
5293
|
+
# ----------------------------------------------------------------------
|
|
5294
|
+
if not Topology.IsInstance(wire, "Wire"):
|
|
5295
|
+
if not silent:
|
|
5296
|
+
print("Wire.Straighten - Error: The input wire parameter is not a valid Wire. Returning None.")
|
|
5297
|
+
return None
|
|
5298
|
+
|
|
5299
|
+
if not Topology.IsInstance(host, "Topology"):
|
|
5300
|
+
if not silent:
|
|
5301
|
+
print("Wire.Straighten - Error: The input host parameter is not a valid Topology. Returning None.")
|
|
5302
|
+
return None
|
|
5303
|
+
|
|
5304
|
+
if not isinstance(portals, list):
|
|
5305
|
+
if not silent:
|
|
5306
|
+
print("Wire.Straighten - Error: The input portals parameter is not a valid list. Returning None.")
|
|
5307
|
+
return None
|
|
5308
|
+
|
|
5309
|
+
if not isinstance(obstacles, list):
|
|
5310
|
+
if not silent:
|
|
5311
|
+
print("Wire.Straighten - Error: The input obstacles parameter is not a valid list. Returning None.")
|
|
5312
|
+
return None
|
|
5313
|
+
|
|
5314
|
+
# Filter valid obstacles and portals
|
|
5315
|
+
obstacle_list = [o for o in obstacles if Topology.IsInstance(o, "Topology")]
|
|
5316
|
+
portal_list = [p for p in portals if Topology.IsInstance(p, "Topology")]
|
|
5317
|
+
|
|
5318
|
+
# Make a cluster of the obstacles (if any)
|
|
5319
|
+
ob_cluster = Cluster.ByTopologies(obstacle_list) if obstacle_list else None
|
|
5320
|
+
|
|
5006
5321
|
# Get ordered vertices of the wire
|
|
5007
5322
|
vertices = Topology.Vertices(wire)
|
|
5008
5323
|
n = len(vertices)
|
|
@@ -5011,75 +5326,249 @@ class Wire():
|
|
|
5011
5326
|
# Nothing to straighten
|
|
5012
5327
|
return wire
|
|
5013
5328
|
|
|
5014
|
-
|
|
5329
|
+
# ----------------------------------------------------------------------
|
|
5330
|
+
# Helper: check if a straight edge between two vertices is valid
|
|
5331
|
+
# ----------------------------------------------------------------------
|
|
5332
|
+
def _edge_is_valid(v_start, v_end):
|
|
5015
5333
|
"""
|
|
5016
5334
|
Returns True if the straight edge between v_start and v_end is
|
|
5017
|
-
fully embedded in the
|
|
5018
|
-
returns None.
|
|
5335
|
+
fully embedded in the host and does not intersect the obstacles.
|
|
5019
5336
|
"""
|
|
5020
|
-
|
|
5337
|
+
# Avoid constructing degenerate edges
|
|
5338
|
+
if Topology.IsSame(v_start, v_end):
|
|
5021
5339
|
return True
|
|
5022
|
-
edge = Edge.ByStartVertexEndVertex(v_start, v_end)
|
|
5023
|
-
diff = Topology.Difference(edge, face)
|
|
5024
|
-
return diff is None
|
|
5025
5340
|
|
|
5026
|
-
|
|
5027
|
-
""
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5341
|
+
edge = Edge.ByStartVertexEndVertex(v_start, v_end, tolerance=tolerance)
|
|
5342
|
+
if not Topology.IsInstance(edge, "Edge"):
|
|
5343
|
+
return False
|
|
5344
|
+
|
|
5345
|
+
diff = Topology.Difference(edge, host)
|
|
5346
|
+
if diff is not None:
|
|
5347
|
+
# Part of the edge lies outside the host
|
|
5348
|
+
return False
|
|
5349
|
+
|
|
5350
|
+
if ob_cluster is not None:
|
|
5351
|
+
inter = Topology.Intersect(edge, ob_cluster)
|
|
5352
|
+
if inter is not None:
|
|
5353
|
+
# Edge hits an obstacle
|
|
5354
|
+
return False
|
|
5031
5355
|
|
|
5032
|
-
|
|
5033
|
-
|
|
5356
|
+
return True
|
|
5357
|
+
|
|
5358
|
+
# ----------------------------------------------------------------------
|
|
5359
|
+
# Helper: for a fixed start index, find the furthest valid vertex index
|
|
5360
|
+
# ----------------------------------------------------------------------
|
|
5361
|
+
def _find_longest_valid_index(start_idx, local_vertices):
|
|
5362
|
+
"""
|
|
5363
|
+
Given a list of vertices local_vertices (a sub-path),
|
|
5364
|
+
for a fixed start_idx, search for the largest index j >= start_idx+1
|
|
5365
|
+
such that the direct edge (local_vertices[start_idx], local_vertices[j])
|
|
5366
|
+
is valid.
|
|
5034
5367
|
"""
|
|
5035
|
-
|
|
5368
|
+
m = len(local_vertices)
|
|
5369
|
+
v_start = local_vertices[start_idx]
|
|
5036
5370
|
best_j = None
|
|
5037
5371
|
|
|
5038
|
-
for j in range(start_idx + 1,
|
|
5039
|
-
v_end =
|
|
5040
|
-
if
|
|
5372
|
+
for j in range(start_idx + 1, m):
|
|
5373
|
+
v_end = local_vertices[j]
|
|
5374
|
+
if _edge_is_valid(v_start, v_end):
|
|
5041
5375
|
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.
|
|
5376
|
+
# Do NOT break on failure: a further vertex might still be valid.
|
|
5044
5377
|
|
|
5045
5378
|
if best_j is None:
|
|
5046
5379
|
# Fallback: use the immediate next vertex to avoid stalling
|
|
5047
|
-
best_j = min(start_idx + 1,
|
|
5380
|
+
best_j = min(start_idx + 1, m - 1)
|
|
5048
5381
|
|
|
5049
5382
|
return best_j
|
|
5050
5383
|
|
|
5051
|
-
|
|
5384
|
+
# ----------------------------------------------------------------------
|
|
5385
|
+
# Helper: straighten a list of vertices (single segment, no portals)
|
|
5386
|
+
# ----------------------------------------------------------------------
|
|
5387
|
+
def _straighten_vertices(local_vertices):
|
|
5052
5388
|
"""
|
|
5053
|
-
|
|
5389
|
+
Straightens a simple path defined by local_vertices (no portal constraints).
|
|
5390
|
+
Returns a new list of vertices.
|
|
5391
|
+
"""
|
|
5392
|
+
m = len(local_vertices)
|
|
5393
|
+
if m <= 2:
|
|
5394
|
+
return local_vertices[:]
|
|
5395
|
+
|
|
5396
|
+
out_vertices = []
|
|
5397
|
+
idx = 0
|
|
5398
|
+
while idx < m - 1:
|
|
5399
|
+
out_vertices.append(local_vertices[idx])
|
|
5400
|
+
idx = _find_longest_valid_index(idx, local_vertices)
|
|
5401
|
+
|
|
5402
|
+
# Ensure the last vertex is present
|
|
5403
|
+
if not Topology.IsSame(out_vertices[-1], local_vertices[-1]):
|
|
5404
|
+
out_vertices.append(local_vertices[-1])
|
|
5405
|
+
|
|
5406
|
+
return out_vertices
|
|
5407
|
+
|
|
5408
|
+
# ----------------------------------------------------------------------
|
|
5409
|
+
# Portal support
|
|
5410
|
+
# ----------------------------------------------------------------------
|
|
5411
|
+
def _portal_cuts():
|
|
5412
|
+
"""
|
|
5413
|
+
Returns a sorted list of (u, v_on_wire) where:
|
|
5414
|
+
- u is the parameter along the wire in [0, 1]
|
|
5415
|
+
- v_on_wire is a vertex on the wire at the same location
|
|
5054
5416
|
|
|
5055
|
-
|
|
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.
|
|
5417
|
+
Only portals that actually intersect the original wire are considered.
|
|
5058
5418
|
"""
|
|
5059
|
-
|
|
5060
|
-
if start_idx == n - 1:
|
|
5061
|
-
out_vertices.append(vertices[start_idx])
|
|
5062
|
-
return
|
|
5419
|
+
cuts = []
|
|
5063
5420
|
|
|
5064
|
-
|
|
5065
|
-
|
|
5421
|
+
if not portal_list:
|
|
5422
|
+
return cuts
|
|
5066
5423
|
|
|
5067
|
-
|
|
5068
|
-
|
|
5424
|
+
for portal in portal_list:
|
|
5425
|
+
inter = Topology.Intersect(wire, portal)
|
|
5426
|
+
if not Topology.IsInstance(inter, "Topology"):
|
|
5427
|
+
# This portal does not intersect the wire, ignore it
|
|
5428
|
+
continue
|
|
5069
5429
|
|
|
5070
|
-
|
|
5071
|
-
|
|
5430
|
+
centroid = Topology.Centroid(inter)
|
|
5431
|
+
if not Topology.IsInstance(centroid, "Vertex"):
|
|
5432
|
+
continue
|
|
5072
5433
|
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5434
|
+
# First try parameter at centroid directly
|
|
5435
|
+
u_target = Wire.ParameterAtVertex(wire, centroid, silent=True)
|
|
5436
|
+
|
|
5437
|
+
if u_target is not None:
|
|
5438
|
+
v_on_wire = centroid
|
|
5439
|
+
else:
|
|
5440
|
+
# Fall back to the closest point on the wire
|
|
5441
|
+
shortest_edge = Topology.ShortestEdge(centroid, wire, silent=True)
|
|
5442
|
+
if not Topology.IsInstance(shortest_edge, "Edge"):
|
|
5443
|
+
# Can't locate a good cut point for this portal
|
|
5444
|
+
continue
|
|
5445
|
+
v_on_wire = Edge.EndVertex(shortest_edge)
|
|
5446
|
+
if not Topology.IsInstance(v_on_wire, "Vertex"):
|
|
5447
|
+
continue
|
|
5448
|
+
u_target = Wire.ParameterAtVertex(wire, v_on_wire, silent=True)
|
|
5449
|
+
|
|
5450
|
+
# If still None, skip this portal
|
|
5451
|
+
if u_target is None:
|
|
5452
|
+
continue
|
|
5453
|
+
|
|
5454
|
+
# Keep u in [0,1], ignoring exact endpoints
|
|
5455
|
+
if 0.0 < u_target < 1.0:
|
|
5456
|
+
cuts.append((u_target, v_on_wire))
|
|
5457
|
+
|
|
5458
|
+
# Sort by parameter, ensure uniqueness
|
|
5459
|
+
cuts = sorted(cuts, key=lambda x: x[0])
|
|
5460
|
+
unique_cuts = []
|
|
5461
|
+
last_u = None
|
|
5462
|
+
for u, v in cuts:
|
|
5463
|
+
if last_u is None or abs(u - last_u) > tolerance:
|
|
5464
|
+
unique_cuts.append((u, v))
|
|
5465
|
+
last_u = u
|
|
5466
|
+
|
|
5467
|
+
return unique_cuts
|
|
5468
|
+
|
|
5469
|
+
def _subdivide_by_portals():
|
|
5470
|
+
"""
|
|
5471
|
+
Splits the original wire into sub-wires between portal cuts.
|
|
5472
|
+
Each sub-wire is a wire segment between:
|
|
5473
|
+
start -> first portal,
|
|
5474
|
+
portal i -> portal i+1,
|
|
5475
|
+
last portal -> end.
|
|
5476
|
+
"""
|
|
5477
|
+
cuts = _portal_cuts()
|
|
5478
|
+
if not cuts:
|
|
5479
|
+
return [wire] # No usable portal intersections
|
|
5480
|
+
|
|
5481
|
+
# Extract only parameters, append 0.0 and 1.0 for full coverage
|
|
5482
|
+
params = [u for (u, _) in cuts]
|
|
5483
|
+
params = [0.0] + params + [1.0]
|
|
5076
5484
|
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
new_vertices.append(vertices[-1])
|
|
5485
|
+
verts_orig = Topology.Vertices(wire)
|
|
5486
|
+
sub_wires = []
|
|
5080
5487
|
|
|
5081
|
-
|
|
5082
|
-
|
|
5488
|
+
for a, b in zip(params[:-1], params[1:]):
|
|
5489
|
+
if b - a <= tolerance:
|
|
5490
|
+
continue # Degenerate segment
|
|
5491
|
+
|
|
5492
|
+
# Build the vertex list for this segment
|
|
5493
|
+
seg_vertices = []
|
|
5494
|
+
|
|
5495
|
+
# Start vertex at parameter a
|
|
5496
|
+
v_a = Wire.VertexByParameter(wire, a)
|
|
5497
|
+
if Topology.IsInstance(v_a, "Vertex"):
|
|
5498
|
+
seg_vertices.append(v_a)
|
|
5499
|
+
|
|
5500
|
+
# Intermediate original vertices whose parameter lies between a and b
|
|
5501
|
+
for v in verts_orig:
|
|
5502
|
+
u = Wire.ParameterAtVertex(wire, v, silent=True)
|
|
5503
|
+
if u is None:
|
|
5504
|
+
continue
|
|
5505
|
+
if a < u < b:
|
|
5506
|
+
seg_vertices.append(v)
|
|
5507
|
+
|
|
5508
|
+
# End vertex at parameter b
|
|
5509
|
+
v_b = Wire.VertexByParameter(wire, b)
|
|
5510
|
+
if Topology.IsInstance(v_b, "Vertex"):
|
|
5511
|
+
seg_vertices.append(v_b)
|
|
5512
|
+
|
|
5513
|
+
# Make sure we have at least two vertices
|
|
5514
|
+
if len(seg_vertices) >= 2:
|
|
5515
|
+
sub_wires.append(Wire.ByVertices(seg_vertices, close=False, silent=True))
|
|
5516
|
+
|
|
5517
|
+
if not sub_wires:
|
|
5518
|
+
# Fallback: return the original wire if subdivision failed
|
|
5519
|
+
return [wire]
|
|
5520
|
+
|
|
5521
|
+
return sub_wires
|
|
5522
|
+
|
|
5523
|
+
# ----------------------------------------------------------------------
|
|
5524
|
+
# Main logic
|
|
5525
|
+
# ----------------------------------------------------------------------
|
|
5526
|
+
# If there are portals, divide the wire into segments between portals,
|
|
5527
|
+
# then straighten each segment independently and reassemble.
|
|
5528
|
+
if portal_list:
|
|
5529
|
+
result_vertices = []
|
|
5530
|
+
sub_wires = _subdivide_by_portals()
|
|
5531
|
+
|
|
5532
|
+
for i, sub_wire in enumerate(sub_wires):
|
|
5533
|
+
if not Topology.IsInstance(sub_wire, "Wire"):
|
|
5534
|
+
continue
|
|
5535
|
+
|
|
5536
|
+
sub_verts = Topology.Vertices(sub_wire)
|
|
5537
|
+
if len(sub_verts) <= 2:
|
|
5538
|
+
straight_verts = sub_verts
|
|
5539
|
+
else:
|
|
5540
|
+
# IMPORTANT: recursive call with portals=[]
|
|
5541
|
+
straight_wire = Wire.Straighten(sub_wire, host=host,
|
|
5542
|
+
obstacles=obstacles,
|
|
5543
|
+
portals=[],
|
|
5544
|
+
tolerance=tolerance,
|
|
5545
|
+
silent=silent)
|
|
5546
|
+
if not Topology.IsInstance(straight_wire, "Wire"):
|
|
5547
|
+
straight_verts = sub_verts
|
|
5548
|
+
else:
|
|
5549
|
+
straight_verts = Topology.Vertices(straight_wire)
|
|
5550
|
+
|
|
5551
|
+
if not straight_verts:
|
|
5552
|
+
continue
|
|
5553
|
+
|
|
5554
|
+
# Avoid duplicate vertices between consecutive segments
|
|
5555
|
+
if not result_vertices:
|
|
5556
|
+
result_vertices.extend(straight_verts)
|
|
5557
|
+
else:
|
|
5558
|
+
result_vertices.extend(straight_verts[1:])
|
|
5559
|
+
|
|
5560
|
+
if len(result_vertices) < 2:
|
|
5561
|
+
# Fallback
|
|
5562
|
+
return wire
|
|
5563
|
+
|
|
5564
|
+
return Wire.ByVertices(result_vertices, close=False, silent=True)
|
|
5565
|
+
|
|
5566
|
+
# No portals: simple global straightening
|
|
5567
|
+
new_vertices = _straighten_vertices(vertices)
|
|
5568
|
+
if len(new_vertices) < 2:
|
|
5569
|
+
return wire
|
|
5570
|
+
|
|
5571
|
+
return Wire.ByVertices(new_vertices, close=False, silent=True)
|
|
5083
5572
|
|
|
5084
5573
|
@staticmethod
|
|
5085
5574
|
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):
|
|
@@ -5440,6 +5929,105 @@ class Wire():
|
|
|
5440
5929
|
|
|
5441
5930
|
return Wire.VertexByParameter(wire, u=compute_u(u))
|
|
5442
5931
|
|
|
5932
|
+
|
|
5933
|
+
|
|
5934
|
+
@staticmethod
|
|
5935
|
+
def ParameterAtVertex(wire, vertex, mantissa : int = 6, tolerance: float = 0.0001, silent: bool = False):
|
|
5936
|
+
"""
|
|
5937
|
+
Returns the u-parameter of a vertex located on a manifold wire.
|
|
5938
|
+
u ranges from 0.0 (start) to 1.0 (end).
|
|
5939
|
+
|
|
5940
|
+
Parameters
|
|
5941
|
+
----------
|
|
5942
|
+
wire : topologic_core.Wire
|
|
5943
|
+
The input wire.
|
|
5944
|
+
vertex : topologic_core.Vertex
|
|
5945
|
+
A vertex that lies somewhere on the wire.
|
|
5946
|
+
mantissa : int , optional
|
|
5947
|
+
The number of decimal places to round the result to. Default is 6.
|
|
5948
|
+
tolerance : float, optional
|
|
5949
|
+
Distance tolerance for matching the vertex to an edge. Default is 0.0001.
|
|
5950
|
+
silent : bool , optional
|
|
5951
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
|
5952
|
+
|
|
5953
|
+
Returns
|
|
5954
|
+
-------
|
|
5955
|
+
float or None
|
|
5956
|
+
The global u-parameter ∈ [0, 1] of the vertex, or None on error.
|
|
5957
|
+
"""
|
|
5958
|
+
from topologicpy.Topology import Topology
|
|
5959
|
+
from topologicpy.Vertex import Vertex
|
|
5960
|
+
from topologicpy.Edge import Edge
|
|
5961
|
+
from topologicpy.Wire import Wire
|
|
5962
|
+
|
|
5963
|
+
# --- Input validation ----------------------------------------------------
|
|
5964
|
+
if not Topology.IsInstance(wire, "Wire"):
|
|
5965
|
+
if not silent:
|
|
5966
|
+
print("Wire.ParameterAtVertex - Error: Input wire is not a valid wire. Returning None.")
|
|
5967
|
+
return None
|
|
5968
|
+
|
|
5969
|
+
if not Topology.IsInstance(vertex, "Vertex"):
|
|
5970
|
+
if not silent:
|
|
5971
|
+
print("Wire.ParameterAtVertex - Error: Input vertex is not a valid wertex. Returning None.")
|
|
5972
|
+
return None
|
|
5973
|
+
|
|
5974
|
+
if not Wire.IsManifold(wire):
|
|
5975
|
+
if not silent:
|
|
5976
|
+
print("Wire.ParameterAtVertex - Error: Input wire is non-manifold. Returning None.")
|
|
5977
|
+
return None
|
|
5978
|
+
|
|
5979
|
+
# --- Prepare wire edges ---------------------------------------------------
|
|
5980
|
+
edges = Wire.Edges(wire)
|
|
5981
|
+
if not edges:
|
|
5982
|
+
if not silent:
|
|
5983
|
+
print("Wire.ParameterAtVertex - Error: Wire has no edges. Returning None.")
|
|
5984
|
+
return None
|
|
5985
|
+
|
|
5986
|
+
edge_lengths = [Edge.Length(e) for e in edges]
|
|
5987
|
+
total_length = sum(edge_lengths)
|
|
5988
|
+
if total_length == 0:
|
|
5989
|
+
if not silent:
|
|
5990
|
+
print("Wire.ParameterAtVertex - Error: Wire has zero length. Returning None.")
|
|
5991
|
+
return None
|
|
5992
|
+
|
|
5993
|
+
# --- Special cases: endpoint vertices ------------------------------------
|
|
5994
|
+
if Vertex.Distance(vertex, Wire.StartVertex(wire)) <= tolerance:
|
|
5995
|
+
return 0.0
|
|
5996
|
+
if Vertex.Distance(vertex, Wire.EndVertex(wire)) <= tolerance:
|
|
5997
|
+
return 1.0
|
|
5998
|
+
|
|
5999
|
+
# --- Locate the edge containing the vertex -------------------------------
|
|
6000
|
+
accumulated = 0.0
|
|
6001
|
+
|
|
6002
|
+
for edge, e_length in zip(edges, edge_lengths):
|
|
6003
|
+
|
|
6004
|
+
# Check if vertex lies on this edge
|
|
6005
|
+
d = Vertex.Distance(vertex, edge)
|
|
6006
|
+
if d <= tolerance:
|
|
6007
|
+
|
|
6008
|
+
# Compute local parameter on this edge
|
|
6009
|
+
sv = Edge.StartVertex(edge)
|
|
6010
|
+
ev = Edge.EndVertex(edge)
|
|
6011
|
+
|
|
6012
|
+
# Local distances
|
|
6013
|
+
dist_sv = Vertex.Distance(sv, vertex)
|
|
6014
|
+
dist_ev = Vertex.Distance(ev, vertex)
|
|
6015
|
+
|
|
6016
|
+
if dist_sv + dist_ev == 0:
|
|
6017
|
+
local_u = 0.0
|
|
6018
|
+
else:
|
|
6019
|
+
local_u = dist_sv / (dist_sv + dist_ev)
|
|
6020
|
+
|
|
6021
|
+
# Global parameter u
|
|
6022
|
+
global_u = (accumulated + local_u * e_length) / total_length
|
|
6023
|
+
return round(global_u, mantissa)
|
|
6024
|
+
|
|
6025
|
+
accumulated += e_length
|
|
6026
|
+
|
|
6027
|
+
if not silent:
|
|
6028
|
+
print("Wire.ParameterAtVertex - Error: Vertex does not appear to lie on the wire. Returning None.")
|
|
6029
|
+
return None
|
|
6030
|
+
|
|
5443
6031
|
@staticmethod
|
|
5444
6032
|
def VertexByParameter(wire, u: float = 0):
|
|
5445
6033
|
"""
|
topologicpy/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.8.
|
|
1
|
+
__version__ = '0.8.93'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: topologicpy
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.93
|
|
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
|
|
@@ -2,7 +2,7 @@ topologicpy/ANN.py,sha256=gpflv4lFypOW789vO7mSkMLaMF_ZftVOCqCvtGr6-JA,47873
|
|
|
2
2
|
topologicpy/Aperture.py,sha256=wNn5miB_IrGCBYuQ18HXQYRva20dUC3id4AJCulL7to,2723
|
|
3
3
|
topologicpy/BVH.py,sha256=ts0Ru24ILjjfHa54SYNhMc8Jkyxwej1DV0Jv7P_6BoU,22513
|
|
4
4
|
topologicpy/CSG.py,sha256=09la1-xzS9vr-WnV7tpJ0I-mkZ-XY0MRSd5iB50Nfgw,15556
|
|
5
|
-
topologicpy/Cell.py,sha256=
|
|
5
|
+
topologicpy/Cell.py,sha256=8GGeRJDoWV2qr__x41axOTIqtkep3U3VXRzM3qsQpPA,198783
|
|
6
6
|
topologicpy/CellComplex.py,sha256=B8bAW6M5fClfXb9nSLDhrgtNRlU888Z4EcUzBZtBqss,68558
|
|
7
7
|
topologicpy/Cluster.py,sha256=Vi5qn9dc9FRdZk1X5KrrU5YS8r4YDDA19C_nKo1IfA0,63725
|
|
8
8
|
topologicpy/Color.py,sha256=hzSmgBWhiuYc55RSipkQNIgGtgyhC5BqY8AakNYEK-U,24486
|
|
@@ -26,14 +26,14 @@ topologicpy/ShapeGrammar.py,sha256=q_BvMKOBDW3GVSRjPLIGAZkHW2egw3mTOPzIyEpYOLg,2
|
|
|
26
26
|
topologicpy/Shell.py,sha256=2EPzDT_t0IAjBRYPDuKNAz_Ax_HaEkvNpXBxDkPdcTg,101084
|
|
27
27
|
topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
|
|
28
28
|
topologicpy/Sun.py,sha256=ezisiHfc2nd7A_8w0Ykq2VgbS0A9WNSg-tBwvfTQAVM,36735
|
|
29
|
-
topologicpy/Topology.py,sha256=
|
|
29
|
+
topologicpy/Topology.py,sha256=B8Ifgv2rWlfhjzoHeuNOofxML_zEIFDuc-nXUVQHV8g,549927
|
|
30
30
|
topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
|
|
31
31
|
topologicpy/Vertex.py,sha256=26TrlX9OCZUN-lMlZG3g4RHTWBqw69NW4AOEgRz_YMo,91269
|
|
32
|
-
topologicpy/Wire.py,sha256=
|
|
32
|
+
topologicpy/Wire.py,sha256=OzMTI5vxJ8XJPgwDAB31666Dz2Sp7H_U9oCnm81gPQA,272312
|
|
33
33
|
topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
|
|
34
|
-
topologicpy/version.py,sha256=
|
|
35
|
-
topologicpy-0.8.
|
|
36
|
-
topologicpy-0.8.
|
|
37
|
-
topologicpy-0.8.
|
|
38
|
-
topologicpy-0.8.
|
|
39
|
-
topologicpy-0.8.
|
|
34
|
+
topologicpy/version.py,sha256=RdWx3Y_QmIJKCA6l7kMMEkyg_LsudVQdkeSqFo0Zk8o,23
|
|
35
|
+
topologicpy-0.8.93.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
|
36
|
+
topologicpy-0.8.93.dist-info/METADATA,sha256=Pt_CPB53UTwU0aiDbVU9mUhHldLyJS4ir40Aj9vylZI,10535
|
|
37
|
+
topologicpy-0.8.93.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
+
topologicpy-0.8.93.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
|
39
|
+
topologicpy-0.8.93.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|