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 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
- topology = Topology.Flatten(topology, origin=origin, direction=normal)
1201
- vertices = Topology.Vertices(topology)
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.68'
1
+ __version__ = '0.7.70'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.7.68
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=fxoeDCxrcLyWoLPF5fGLt55KECYLVxsfKpDwS-qrc4k,385300
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=I67uOdeiH8-cKK0OUbKghJDGTP225H5-3Vtijyosvfk,109535
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=rThgAyePNkNR6LlX0lgEJZFWo7EEads1V9JdGDlZg7s,172596
29
+ topologicpy/Wire.py,sha256=LMJWcrRWtK0spuyODYANy6442lF7g-fi0KNVqcbYtJo,182071
30
30
  topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
31
- topologicpy/version.py,sha256=TQcBZ_0LJ13NMY5YrTufxmektelSYqtqEEsvl1QxCJE,23
32
- topologicpy-0.7.68.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
- topologicpy-0.7.68.dist-info/METADATA,sha256=pbI5CtiVUC2x6-LBgOhjVamnGJ9iybk_z18VcENQvJ8,10493
34
- topologicpy-0.7.68.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
35
- topologicpy-0.7.68.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
- topologicpy-0.7.68.dist-info/RECORD,,
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,,