topologicpy 0.7.68__py3-none-any.whl → 0.7.70__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 +4 -1
- topologicpy/Plotly.py +4 -0
- topologicpy/Wire.py +219 -2
- topologicpy/version.py +1 -1
- {topologicpy-0.7.68.dist-info → topologicpy-0.7.70.dist-info}/METADATA +1 -1
- {topologicpy-0.7.68.dist-info → topologicpy-0.7.70.dist-info}/RECORD +9 -9
- {topologicpy-0.7.68.dist-info → topologicpy-0.7.70.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.68.dist-info → topologicpy-0.7.70.dist-info}/WHEEL +0 -0
- {topologicpy-0.7.68.dist-info → topologicpy-0.7.70.dist-info}/top_level.txt +0 -0
topologicpy/Graph.py
CHANGED
@@ -7845,6 +7845,7 @@ class Graph:
|
|
7845
7845
|
sagitta = 0,
|
7846
7846
|
absolute = False,
|
7847
7847
|
sides = 8,
|
7848
|
+
angle = 0,
|
7848
7849
|
vertexColor="black",
|
7849
7850
|
vertexSize=6,
|
7850
7851
|
vertexLabelKey=None,
|
@@ -7893,6 +7894,8 @@ class Graph:
|
|
7893
7894
|
For example, if the length of the edge is 10, the sagitta is set to 0.5, and absolute is set to False, the sagitta length will be 5. The default is True.
|
7894
7895
|
sides : int , optional
|
7895
7896
|
The number of sides of the arc. The default is 8.
|
7897
|
+
angle : float, optional
|
7898
|
+
An additional angle in degrees to rotate arcs (where sagitta is more than 0). The default is 0.
|
7896
7899
|
vertexColor : str , optional
|
7897
7900
|
The desired color of the output vertices. This can be any plotly color string and may be specified as:
|
7898
7901
|
- A hex string (e.g. '#ff0000')
|
@@ -7994,7 +7997,7 @@ class Graph:
|
|
7994
7997
|
print("Graph.Show - Error: The input graph is not a valid graph. Returning None.")
|
7995
7998
|
return None
|
7996
7999
|
|
7997
|
-
data= Plotly.DataByGraph(graph, sagitta=sagitta, absolute=absolute, sides=sides, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLabels=showVertexLabels, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLabels=showEdgeLabels, showEdgeLegend=showEdgeLegend, colorScale=colorScale, silent=silent)
|
8000
|
+
data= Plotly.DataByGraph(graph, sagitta=sagitta, absolute=absolute, sides=sides, angle=angle, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLabels=showVertexLabels, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLabels=showEdgeLabels, showEdgeLegend=showEdgeLegend, colorScale=colorScale, silent=silent)
|
7998
8001
|
fig = Plotly.FigureByData(data, width=width, height=height, xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize, backgroundColor=backgroundColor,
|
7999
8002
|
marginLeft=marginLeft, marginRight=marginRight, marginTop=marginTop, marginBottom=marginBottom, tolerance=tolerance)
|
8000
8003
|
Plotly.Show(fig, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
|
topologicpy/Plotly.py
CHANGED
@@ -276,6 +276,7 @@ class Plotly:
|
|
276
276
|
sagitta: float = 0,
|
277
277
|
absolute: bool = False,
|
278
278
|
sides: int = 8,
|
279
|
+
angle: float = 0,
|
279
280
|
vertexColor: str = "black",
|
280
281
|
vertexSize: float = 6,
|
281
282
|
vertexLabelKey: str = None,
|
@@ -454,6 +455,9 @@ class Plotly:
|
|
454
455
|
d = Topology.Dictionary(edge)
|
455
456
|
arc = Wire.ArcByEdge(edge, sagitta=sagitta, absolute=absolute, sides=sides, close=False, silent=silent)
|
456
457
|
if Topology.IsInstance(arc, "Wire"):
|
458
|
+
if not angle == 0:
|
459
|
+
direc = Edge.Direction(edge)
|
460
|
+
arc = Topology.Rotate(arc, origin=Topology.Centroid(edge), axis=direc, angle=angle)
|
457
461
|
arc_edges = Topology.Edges(arc)
|
458
462
|
for arc_edge in arc_edges:
|
459
463
|
arc_edge = Topology.SetDictionary(arc_edge, d, silent=True)
|
topologicpy/Wire.py
CHANGED
@@ -1076,6 +1076,223 @@ class Wire():
|
|
1076
1076
|
new_wire = Topology.SelfMerge(Topology.ByGeometry(vertices=g_vertices, edges=g_edges, faces=[]))
|
1077
1077
|
return new_wire
|
1078
1078
|
|
1079
|
+
|
1080
|
+
|
1081
|
+
@staticmethod
|
1082
|
+
def ConcaveHull(topology, k: int = 3, mantissa: int = 6, tolerance: float = 0.0001):
|
1083
|
+
"""
|
1084
|
+
Returns a wire representing the 2D convex hull of the input topology. The vertices of the topology are assumed to be coplanar.
|
1085
|
+
Code based on Moreira, A and Santos, M Y, "CONCAVE HULL: A K-NEAREST NEIGHBOURS APPROACH FOR THE COMPUTATION OF THE REGION OCCUPIED BY A SET OF POINTS"
|
1086
|
+
GRAPP 2007 - International Conference on Computer Graphics Theory and Applications.
|
1087
|
+
|
1088
|
+
Parameters
|
1089
|
+
----------
|
1090
|
+
topology : topologic_core.Topology
|
1091
|
+
The input topology.
|
1092
|
+
k : int, optional
|
1093
|
+
The number of nearest neighbors to consider for each point when building the hull.
|
1094
|
+
Must be at least 3 for the algorithm to function correctly. Increasing `k` will produce a smoother,
|
1095
|
+
less concave hull, while decreasing `k` may yield a more detailed, concave shape. The default is 3.
|
1096
|
+
mantissa : int , optional
|
1097
|
+
The desired length of the mantissa. The default is 6.
|
1098
|
+
tolerance : float , optional
|
1099
|
+
The desired tolerance. The default is 0.0001.
|
1100
|
+
|
1101
|
+
Returns
|
1102
|
+
-------
|
1103
|
+
topologic_core.Wire
|
1104
|
+
The convex hull of the input topology.
|
1105
|
+
"""
|
1106
|
+
|
1107
|
+
from topologicpy.Vertex import Vertex
|
1108
|
+
from topologicpy.Face import Face
|
1109
|
+
from topologicpy.Topology import Topology
|
1110
|
+
from math import atan2, sqrt, pi
|
1111
|
+
from random import sample
|
1112
|
+
|
1113
|
+
# Helper function to clean the list by removing duplicate points
|
1114
|
+
def clean_list(points_list):
|
1115
|
+
return list(set(points_list))
|
1116
|
+
|
1117
|
+
# Helper function to find the point with the minimum Y-coordinate
|
1118
|
+
def find_min_y_point(points):
|
1119
|
+
return min(points, key=lambda p: [p[1], p[0]])
|
1120
|
+
|
1121
|
+
# Helper function to find the k-nearest neighbors to a given point
|
1122
|
+
def nearest_points(points, reference_point, k):
|
1123
|
+
# Sort points by distance from the reference point and select the first k points
|
1124
|
+
sorted_points = sorted(points, key=lambda p: sqrt((p[0] - reference_point[0]) ** 2 + (p[1] - reference_point[1]) ** 2))
|
1125
|
+
return sorted_points[:k]
|
1126
|
+
|
1127
|
+
# Helper function to sort points by the angle relative to the previous direction
|
1128
|
+
def sort_by_angle(points, current_point, prev_angle):
|
1129
|
+
def angle_to(p):
|
1130
|
+
angle = atan2(p[1] - current_point[1], p[0] - current_point[0])
|
1131
|
+
angle_diff = (angle - prev_angle + 2 * pi) % (2 * pi)
|
1132
|
+
return angle_diff
|
1133
|
+
return sorted(points, key=angle_to)
|
1134
|
+
|
1135
|
+
# Helper function to check if two line segments intersect
|
1136
|
+
def intersects_q(line1, line2):
|
1137
|
+
def orientation(p, q, r):
|
1138
|
+
val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1])
|
1139
|
+
if val == 0: return 0
|
1140
|
+
return 1 if val > 0 else 2
|
1141
|
+
|
1142
|
+
p1, q1 = line1
|
1143
|
+
p2, q2 = line2
|
1144
|
+
o1 = orientation(p1, q1, p2)
|
1145
|
+
o2 = orientation(p1, q1, q2)
|
1146
|
+
o3 = orientation(p2, q2, p1)
|
1147
|
+
o4 = orientation(p2, q2, q1)
|
1148
|
+
|
1149
|
+
if o1 != o2 and o3 != o4:
|
1150
|
+
return True
|
1151
|
+
if o1 == 0 and on_segment(p1, p2, q1): return True
|
1152
|
+
if o2 == 0 and on_segment(p1, q2, q1): return True
|
1153
|
+
if o3 == 0 and on_segment(p2, p1, q2): return True
|
1154
|
+
if o4 == 0 and on_segment(p2, q1, q2): return True
|
1155
|
+
return False
|
1156
|
+
|
1157
|
+
# Helper function to check if point q lies on segment pr
|
1158
|
+
def on_segment(p, q, r):
|
1159
|
+
return (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and
|
1160
|
+
q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1]))
|
1161
|
+
|
1162
|
+
# Helper function to calculate the angle between two points
|
1163
|
+
def angle(p1, p2):
|
1164
|
+
return atan2(p2[1] - p1[1], p2[0] - p1[0])
|
1165
|
+
|
1166
|
+
# Helper function to determine if a point is inside a polygon (Ray Casting method)
|
1167
|
+
def point_in_polygon_q(point, polygon):
|
1168
|
+
x, y = point
|
1169
|
+
inside = False
|
1170
|
+
n = len(polygon)
|
1171
|
+
p1x, p1y = polygon[0]
|
1172
|
+
for i in range(1, n + 1):
|
1173
|
+
p2x, p2y = polygon[i % n]
|
1174
|
+
if min(p1y, p2y) < y <= max(p1y, p2y) and x <= max(p1x, p2x):
|
1175
|
+
if p1y != p2y:
|
1176
|
+
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
1177
|
+
if p1x == p2x or x <= xinters:
|
1178
|
+
inside = not inside
|
1179
|
+
p1x, p1y = p2x, p2y
|
1180
|
+
return inside
|
1181
|
+
|
1182
|
+
def concave_hull(points_list, k: int = 3):
|
1183
|
+
# Ensure k >= 3
|
1184
|
+
kk = max(k, 3)
|
1185
|
+
|
1186
|
+
# Remove duplicate points
|
1187
|
+
dataset = clean_list(points_list)
|
1188
|
+
|
1189
|
+
# If there are fewer than 3 unique points, no polygon can be formed
|
1190
|
+
if len(dataset) < 3:
|
1191
|
+
return None
|
1192
|
+
elif len(dataset) == 3:
|
1193
|
+
return dataset # If exactly 3 points, they form the polygon
|
1194
|
+
|
1195
|
+
# Ensure we have enough neighbors
|
1196
|
+
kk = min(kk, len(dataset) - 1)
|
1197
|
+
|
1198
|
+
# Find starting point (minimum Y value) and initialize hull
|
1199
|
+
first_point = find_min_y_point(dataset)
|
1200
|
+
hull = [first_point]
|
1201
|
+
current_point = first_point
|
1202
|
+
dataset.remove(first_point)
|
1203
|
+
prev_angle = 0
|
1204
|
+
step = 2
|
1205
|
+
|
1206
|
+
# Original code logic, with an update to calculate prev_angle
|
1207
|
+
while (current_point != first_point or step == 2) and len(dataset) > 0:
|
1208
|
+
# After 4 steps, re-add the starting point to check for closure
|
1209
|
+
if step == 5:
|
1210
|
+
dataset.append(first_point)
|
1211
|
+
|
1212
|
+
# Find the k-nearest points
|
1213
|
+
k_nearest_points = nearest_points(dataset, current_point, kk)
|
1214
|
+
|
1215
|
+
# Sort candidates based on angle
|
1216
|
+
c_points = sort_by_angle(k_nearest_points, current_point, prev_angle)
|
1217
|
+
|
1218
|
+
intersection_found = True
|
1219
|
+
i = 0
|
1220
|
+
|
1221
|
+
# Select the first candidate that does not intersect any polygon edges
|
1222
|
+
while intersection_found and i < len(c_points):
|
1223
|
+
candidate_point = c_points[i]
|
1224
|
+
i += 1
|
1225
|
+
|
1226
|
+
if candidate_point == first_point:
|
1227
|
+
last_point_check = 1
|
1228
|
+
else:
|
1229
|
+
last_point_check = 0
|
1230
|
+
|
1231
|
+
# Check for intersections with the existing edges
|
1232
|
+
j = 2
|
1233
|
+
intersection_found = False
|
1234
|
+
while not intersection_found and j < len(hull) - last_point_check:
|
1235
|
+
# Using hull[-1] and hull[-2] for last and second-to-last points
|
1236
|
+
intersection_found = intersects_q(
|
1237
|
+
(hull[-1], candidate_point),
|
1238
|
+
(hull[-1 - j], hull[-j])
|
1239
|
+
)
|
1240
|
+
j += 1
|
1241
|
+
|
1242
|
+
# If all candidates intersect, retry with a higher number of neighbors
|
1243
|
+
if intersection_found:
|
1244
|
+
return concave_hull(points_list, kk + 1)
|
1245
|
+
|
1246
|
+
# Update the hull with the selected candidate point
|
1247
|
+
current_point = candidate_point
|
1248
|
+
hull.append(current_point)
|
1249
|
+
|
1250
|
+
# Calculate the angle between the last two points in the hull to set `prev_angle`
|
1251
|
+
if len(hull) > 1:
|
1252
|
+
prev_angle = angle(hull[-1], hull[-2])
|
1253
|
+
|
1254
|
+
dataset.remove(current_point)
|
1255
|
+
step += 1
|
1256
|
+
|
1257
|
+
|
1258
|
+
# Check if all points are inside the constructed hull
|
1259
|
+
all_inside = True
|
1260
|
+
i = len(dataset) - 1
|
1261
|
+
while all_inside and i >= 0:
|
1262
|
+
all_inside = point_in_polygon_q(dataset[i], hull)
|
1263
|
+
i -= 1
|
1264
|
+
|
1265
|
+
# If any points are outside the hull, retry with a higher number of neighbors
|
1266
|
+
if not all_inside:
|
1267
|
+
return concave_hull(points_list, kk + 1)
|
1268
|
+
|
1269
|
+
# Return the completed hull if all points are inside
|
1270
|
+
return hull
|
1271
|
+
|
1272
|
+
f = None
|
1273
|
+
# Create a sample face and flatten
|
1274
|
+
while not Topology.IsInstance(f, "Face"):
|
1275
|
+
vertices = Topology.SubTopologies(topology=topology, subTopologyType="vertex")
|
1276
|
+
v = sample(vertices, 3)
|
1277
|
+
w = Wire.ByVertices(v)
|
1278
|
+
f = Face.ByWire(w, tolerance=tolerance, silent=True)
|
1279
|
+
if not f == None:
|
1280
|
+
origin = Topology.Centroid(f)
|
1281
|
+
normal = Face.Normal(f, mantissa=mantissa)
|
1282
|
+
f = Topology.Flatten(f, origin=origin, direction=normal)
|
1283
|
+
flat_topology = Topology.Flatten(topology, origin=origin, direction=normal)
|
1284
|
+
vertices = Topology.Vertices(flat_topology)
|
1285
|
+
points = []
|
1286
|
+
for v in vertices:
|
1287
|
+
points.append((Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa)))
|
1288
|
+
hull = concave_hull(points, k=k)
|
1289
|
+
hull_vertices = []
|
1290
|
+
for p in hull:
|
1291
|
+
hull_vertices.append(Vertex.ByCoordinates(p[0], p[1], 0))
|
1292
|
+
ch = Wire.ByVertices(hull_vertices, close=True)
|
1293
|
+
ch = Topology.Unflatten(ch, origin=origin, direction=normal)
|
1294
|
+
return ch
|
1295
|
+
|
1079
1296
|
@staticmethod
|
1080
1297
|
def ConvexHull(topology, mantissa: int = 6, tolerance: float = 0.0001):
|
1081
1298
|
"""
|
@@ -1197,8 +1414,8 @@ class Wire():
|
|
1197
1414
|
origin = Topology.Centroid(f)
|
1198
1415
|
normal = Face.Normal(f, mantissa=mantissa)
|
1199
1416
|
f = Topology.Flatten(f, origin=origin, direction=normal)
|
1200
|
-
|
1201
|
-
vertices = Topology.Vertices(
|
1417
|
+
flat_topology = Topology.Flatten(topology, origin=origin, direction=normal)
|
1418
|
+
vertices = Topology.Vertices(flat_topology)
|
1202
1419
|
points = []
|
1203
1420
|
for v in vertices:
|
1204
1421
|
points.append((Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa)))
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.7.
|
1
|
+
__version__ = '0.7.70'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.70
|
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
|
@@ -11,13 +11,13 @@ topologicpy/Dictionary.py,sha256=cURg452wwk2WeSxWY46ncgAUo5XD1c2c5EtO6ESZHaY,273
|
|
11
11
|
topologicpy/Edge.py,sha256=--D035FBJa9hfPSPrPsNQluq1NHx7d3IGnklxbH-9qo,66928
|
12
12
|
topologicpy/EnergyModel.py,sha256=AqTtmXE35SxvRXhG3vYAQd7GQDW-6HtjYPHua6ME4Eg,53762
|
13
13
|
topologicpy/Face.py,sha256=vKtqoXmxLCC23QNg7Mk5PfFZACnbKh04vWRDoFCz1jw,124344
|
14
|
-
topologicpy/Graph.py,sha256=
|
14
|
+
topologicpy/Graph.py,sha256=CfyYvm-oFCURM3XpctekI-4qsOG2B6lZxgjD3WZlysQ,385478
|
15
15
|
topologicpy/Grid.py,sha256=9N6PE84qCm40TRi2WtlVZSBwXXr47zHpscEpZHg_JW4,18205
|
16
16
|
topologicpy/Helper.py,sha256=i-AfI29NMsZXBaymjilfvxQbuS3wpYbpPw4RWu1YCHs,16358
|
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=
|
20
|
+
topologicpy/Plotly.py,sha256=cs0LBOVm6WerT_F4IcFvQSRq_fYB9eVwRKiH2ogBgAI,109785
|
21
21
|
topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
|
22
22
|
topologicpy/PyG.py,sha256=LU9LCCzjxGPUM31qbaJXZsTvniTtgugxJY7y612t4A4,109757
|
23
23
|
topologicpy/Shell.py,sha256=CyMXuHoua_-lGGOr-1bgZa1VGpNsBGOe-dmkwhek-xY,87639
|
@@ -26,11 +26,11 @@ topologicpy/Sun.py,sha256=42tDWMYpwRG7Z2Qjtp94eRgBuqySq7k8TgNUZDK7QxQ,36837
|
|
26
26
|
topologicpy/Topology.py,sha256=9DPTumt9kr9Su5b3RE8l_gLQSRalYHqf79tVHiy_aQM,400055
|
27
27
|
topologicpy/Vector.py,sha256=A1g83zDHep58iVPY8WQ8iHNrSOfGWFEzvVeDuMnjDNY,33078
|
28
28
|
topologicpy/Vertex.py,sha256=ZS6xK89JKokBKc0W8frdRhhuzR8c-dI1TTLt7pTf1iA,71032
|
29
|
-
topologicpy/Wire.py,sha256=
|
29
|
+
topologicpy/Wire.py,sha256=LMJWcrRWtK0spuyODYANy6442lF7g-fi0KNVqcbYtJo,182071
|
30
30
|
topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
|
31
|
-
topologicpy/version.py,sha256=
|
32
|
-
topologicpy-0.7.
|
33
|
-
topologicpy-0.7.
|
34
|
-
topologicpy-0.7.
|
35
|
-
topologicpy-0.7.
|
36
|
-
topologicpy-0.7.
|
31
|
+
topologicpy/version.py,sha256=B5ePMOWR_K7KFKMxVTSXxzaIt_LfFfLJBoG_ivXNV4A,23
|
32
|
+
topologicpy-0.7.70.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
33
|
+
topologicpy-0.7.70.dist-info/METADATA,sha256=sLG6R02M67ZtwLMcLs5dr-LiogkrAfuSoM4GUJa8vKw,10493
|
34
|
+
topologicpy-0.7.70.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
35
|
+
topologicpy-0.7.70.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
36
|
+
topologicpy-0.7.70.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|