topologicpy 0.8.71__py3-none-any.whl → 0.8.73__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/Topology.py CHANGED
@@ -224,9 +224,17 @@ class Topology():
224
224
  The input topology with the apertures added to it.
225
225
 
226
226
  """
227
+ from topologicpy.Vertex import Vertex
227
228
  from topologicpy.Dictionary import Dictionary
228
229
  from topologicpy.BVH import BVH
229
230
 
231
+ def best_candidate(aperture, candidates, tolerance=0.0001):
232
+ ap_iv = Topology.InternalVertex(aperture)
233
+ for candidate in candidates:
234
+ if Vertex.IsInternal(ap_iv, candidate, tolerance=tolerance):
235
+ return candidate
236
+ return None
237
+
230
238
  if not Topology.IsInstance(topology, "Topology"):
231
239
  print("Topology.AddApertures - Error: The input topology parameter is not a valid topology. Returning None.")
232
240
  return None
@@ -256,16 +264,20 @@ class Topology():
256
264
  bvh = BVH.ByTopologies(Topology.SubTopologies(topology, subTopologyType=subTopologyType), silent=True)
257
265
  used = []
258
266
  for aperture in apertures:
259
- result = BVH.Clashes(bvh, aperture)
260
- if isinstance(result, list):
261
- if len(result) > 0:
262
- subTopology = result[0]
263
- if Topology.IsInstance(subTopology, "topology"):
264
- used.append(subTopology)
265
- if exclusive == True:
266
- if subTopology in used:
267
- continue
268
- subTopology = Topology.AddContent(subTopology, [aperture], subTopologyType="self", tolerance=tolerance)
267
+ candidates = BVH.Clashes(bvh, aperture)
268
+ if isinstance(candidates, list):
269
+ if len(candidates) == 0:
270
+ return topology
271
+ elif len(candidates) == 1:
272
+ subTopology = candidates[0]
273
+ if len(candidates) > 0:
274
+ subTopology = best_candidate(aperture, candidates, tolerance=tolerance)
275
+ if Topology.IsInstance(subTopology, "topology"):
276
+ used.append(subTopology)
277
+ if exclusive == True:
278
+ if subTopology in used:
279
+ continue
280
+ subTopology = Topology.AddContent(subTopology, [aperture], subTopologyType="self", tolerance=tolerance)
269
281
  return topology
270
282
 
271
283
  @staticmethod
@@ -292,6 +304,8 @@ class Topology():
292
304
  """
293
305
 
294
306
  from topologicpy.Context import Context
307
+ from topologicpy.Dictionary import Dictionary
308
+
295
309
 
296
310
  if not Topology.IsInstance(topology, "Topology"):
297
311
  print("Topology.AddContent - Error: the input topology parameter is not a valid topology. Returning None.")
@@ -311,14 +325,20 @@ class Topology():
311
325
  if not subTopologyType.lower() in ["self", "cellcomplex", "cell", "shell", "face", "wire", "edge", "vertex"]:
312
326
  print("Topology.AddContent - Error: the input subtopology type parameter is not a recognized type. Returning None.")
313
327
  return None
328
+
329
+ copy_contents = [Topology.Copy(x) for x in contents]
330
+ for i, content in enumerate(contents):
331
+ d = Topology.Dictionary(content)
332
+ copy_contents[i] = Topology.SetDictionary(copy_contents[i], d)
333
+
314
334
  if subTopologyType.lower() == "self":
315
335
  context = Context.ByTopologyParameters(topology)
316
- for content in contents:
336
+ for content in copy_contents:
317
337
  content.AddContext(context) # Hook to Core
318
338
  topology.AddContent(content) # Hook to Core
319
339
  else:
320
340
  t = Topology.TypeID(subTopologyType)
321
- topology.AddContents(contents, t) # Hook to Core
341
+ topology.AddContents(copy_contents, t) # Hook to Core
322
342
  return topology
323
343
 
324
344
  @staticmethod
topologicpy/Vertex.py CHANGED
@@ -1135,22 +1135,11 @@ class Vertex():
1135
1135
  topology,
1136
1136
  maxLeafSize: int = 4,
1137
1137
  identify: bool = False,
1138
- tolerance: float = 0.0006,
1139
- silent: bool = True,
1140
- ) -> bool:
1138
+ tolerance: float = 0.0001,
1139
+ silent: bool = False,
1140
+ ):
1141
1141
  """
1142
- Returns True if `vertex` lies inside (or on the boundary of) `topology`.
1143
-
1144
- Broad-phase:
1145
- - Build a BVH over Cells (3D) or Faces (2D) using BVH.ByTopologies.
1146
- - Query via BVH.Clashes(bvh, vertex) to get only nearby primitives.
1147
-
1148
- Narrow-phase (geometric, no Topology.IsInternal):
1149
- - Boundary snap: if Vertex.Distance(vertex, primitive) <= tolerance -> inside.
1150
- - 3D: Cast a ray (+X direction). For each candidate Cell, intersect the ray with its Faces.
1151
- For each intersected Face: project face polygon(s) to 2D (dominant-axis drop) and
1152
- test point-in-polygon (outer minus holes). Odd number of valid intersections => inside.
1153
- - 2D: Project the Face loops to 2D and do point-in-polygon (outer minus holes).
1142
+ Returns True if the input vertex lies inside the input topology.
1154
1143
 
1155
1144
  Parameters
1156
1145
  ----------
@@ -1166,9 +1155,9 @@ class Vertex():
1166
1155
  identify: bool, optional
1167
1156
  If set to True, a tuple is returned where the identified subTopology is returned (e.g. (True, edge)). Default is False.
1168
1157
  tolerance : float, optional
1169
- Distance and numeric tolerance. Default 1e-7.
1170
- silent : bool, optional
1171
- Suppress non-critical prints.
1158
+ The desired tolerance. Default 0.0001.
1159
+ silent : bool , optional
1160
+ If set to True, error and warning messages are suppressed. Default is False.
1172
1161
 
1173
1162
  Returns
1174
1163
  -------
@@ -1191,11 +1180,6 @@ class Vertex():
1191
1180
  # --------------------------
1192
1181
  # Utilities
1193
1182
  # --------------------------
1194
- def v_coords(v):
1195
- return Vertex.X(v), Vertex.Y(v), Vertex.Z(v)
1196
-
1197
- def vec_sub(a, b):
1198
- return (a[0]-b[0], a[1]-b[1], a[2]-b[2])
1199
1183
 
1200
1184
  def vec_dot(a, b):
1201
1185
  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
@@ -1212,29 +1196,6 @@ class Vertex():
1212
1196
  return (0.0, 0.0, 0.0)
1213
1197
  return (a[0]/l, a[1]/l, a[2]/l)
1214
1198
 
1215
- # Plane from 3 points
1216
- def face_plane(face):
1217
- # Returns (n, d) where plane: n·X + d = 0; n is unit normal.
1218
- # Use first 3 distinct points of the external boundary.
1219
- w_ext = Face.ExternalBoundary(face)
1220
- verts = Wire.Vertices(w_ext)
1221
- pts = [v_coords(v) for v in verts]
1222
- # Find non-collinear triplet
1223
- p0 = pts[0]
1224
- n = None
1225
- for i in range(1, len(pts)-1):
1226
- v1 = vec_sub(pts[i], p0)
1227
- v2 = vec_sub(pts[i+1], p0)
1228
- n_try = vec_cross(v1, v2)
1229
- if vec_len(n_try) > 1e-15:
1230
- n = vec_norm(n_try)
1231
- break
1232
- if n is None:
1233
- # Degenerate face; assign arbitrary normal
1234
- n = (1.0, 0.0, 0.0)
1235
- d = -vec_dot(n, p0)
1236
- return n, d
1237
-
1238
1199
  def dominant_axis(n):
1239
1200
  # Return axis to drop when projecting to 2D (index 0=x,1=y,2=z)
1240
1201
  ax = abs(n[0]); ay = abs(n[1]); az = abs(n[2])
@@ -1252,13 +1213,6 @@ class Vertex():
1252
1213
  else:
1253
1214
  return (p[0], p[1])
1254
1215
 
1255
- def ray_plane_intersection(orig, dirv, n, d):
1256
- # Solve n·(orig + t*dir) + d = 0 -> t = -(n·orig + d) / (n·dir)
1257
- ndotdir = vec_dot(n, dirv)
1258
- if abs(ndotdir) < 1e-15:
1259
- return None # parallel
1260
- t = -(vec_dot(n, orig) + d) / ndotdir
1261
- return t
1262
1216
 
1263
1217
  # 2D point-in-polygon (ray crossing). Polygon is list of 2D points (closed or open).
1264
1218
  def pip_ray_cross_2d(pt, poly):
@@ -1300,38 +1254,6 @@ class Vertex():
1300
1254
  return False
1301
1255
  return True
1302
1256
 
1303
- def face_loops_2d(face, drop_axis):
1304
- # Returns (outer2d, [hole2d,...])
1305
- w_ext = Face.ExternalBoundary(face)
1306
- outer_vs = Wire.Vertices(w_ext)
1307
- outer = [project_point(v_coords(v), drop_axis) for v in outer_vs]
1308
-
1309
- holes_2d = []
1310
- try:
1311
- inner_wires = Face.InternalBoundaries(face) or []
1312
- except Exception:
1313
- inner_wires = []
1314
- for w in inner_wires:
1315
- vs = Wire.Vertices(w)
1316
- holes_2d.append([project_point(v_coords(v), drop_axis) for v in vs])
1317
- return outer, holes_2d
1318
-
1319
- # Ray casting against one face; returns hit-point (3D) if the ray hits inside the face.
1320
- def ray_hits_face(orig, dirv, face, tol=1e-12):
1321
- n, d = face_plane(face)
1322
- t = ray_plane_intersection(orig, dirv, n, d)
1323
- if t is None or t < -tol:
1324
- return None # behind or parallel
1325
- # Intersection point
1326
- ip = (orig[0] + t*dirv[0], orig[1] + t*dirv[1], orig[2] + t*dirv[2])
1327
- # Project to 2D in face's dominant plane and do PIP against loops
1328
- ax = dominant_axis(n)
1329
- outer2d, holes2d = face_loops_2d(face, ax)
1330
- ip2d = project_point(ip, ax)
1331
- if polygon_with_holes_contains_2d(ip2d, outer2d, holes2d):
1332
- return ip
1333
- return None
1334
-
1335
1257
  # 2D containment in an Edge
1336
1258
  def point_in_vertex(vtx, vertex, tol):
1337
1259
  # Boundary snap first
@@ -1348,7 +1270,12 @@ class Vertex():
1348
1270
 
1349
1271
  # 2D containment in a Face (vertex assumed coplanar or nearly so)
1350
1272
  def point_in_face(vtx, face, tol):
1351
- status = topologic.FaceUtility.IsInside(face, vtx, tol)
1273
+ dist = Vertex.PerpendicularDistance(vtx, face)
1274
+ if dist <= tol:
1275
+ vtx2 = Vertex.Project(vtx, face)
1276
+ status = topologic.FaceUtility.IsInside(face, vtx2, tol)
1277
+ else:
1278
+ status = False
1352
1279
  return status
1353
1280
 
1354
1281
  # 3D containment in a Cell via ray casting (+X direction)
@@ -1360,7 +1287,7 @@ class Vertex():
1360
1287
  # Check if inside AABB
1361
1288
  # --------------------------
1362
1289
  points = [Vertex.Coordinates(v) for v in Topology.Vertices(topology)]
1363
- aabb = AABB.from_points(points)
1290
+ aabb = AABB.from_points(points, pad=tolerance)
1364
1291
  if not aabb.contains_point(Vertex.Coordinates(vertex)):
1365
1292
  return False
1366
1293
 
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.71'
1
+ __version__ = '0.8.73'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.71
3
+ Version: 0.8.73
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
@@ -26,14 +26,14 @@ topologicpy/ShapeGrammar.py,sha256=KYsKDLXWdflAcYMAIz84AUF-GMkbTmaBDd2-ovbilqU,2
26
26
  topologicpy/Shell.py,sha256=JBTw3T1CjJt3ZPTjG1eBU2Oe2n_helW9ioML_bYK2_U,98545
27
27
  topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
28
28
  topologicpy/Sun.py,sha256=8S6dhCKfOhUGVny-jEk87Q08anLYMB1JEBKRGCklvbQ,36670
29
- topologicpy/Topology.py,sha256=9reHlXzUXYbXt0FJbThL3Wr0JEF2oAHuNRerU2BshxM,473587
29
+ topologicpy/Topology.py,sha256=BC9JYfxComyAzmXrPdhmXlaFBGmZ_x7-oHl579E4qUY,474458
30
30
  topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
31
- topologicpy/Vertex.py,sha256=eO74ZcuQUmiEHy5-Xyn7YDlt3R7YKoqB-BEwF-TUcDM,94101
31
+ topologicpy/Vertex.py,sha256=rdtuOvgySIamN_xTKrETUQYu0fukXHR9EDJ91WeZLMI,90855
32
32
  topologicpy/Wire.py,sha256=gjgQUGHdBdXUIijgZc_VIW0E39w-smaVhhdl0jF63fQ,230466
33
33
  topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
34
- topologicpy/version.py,sha256=49ezdJH-CaZn_7E1pYgol2MJ6KVA4JWjsEILF4g69nI,23
35
- topologicpy-0.8.71.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
36
- topologicpy-0.8.71.dist-info/METADATA,sha256=eJ0CeTYd_RvHdNXNjmyK1IRQzZqzuEDmqSuwbdLAGEY,10535
37
- topologicpy-0.8.71.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
- topologicpy-0.8.71.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
39
- topologicpy-0.8.71.dist-info/RECORD,,
34
+ topologicpy/version.py,sha256=6R_YGb083s4RIeitSxsh0BGKzotjy9BcSCbEAIQwb9I,23
35
+ topologicpy-0.8.73.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
36
+ topologicpy-0.8.73.dist-info/METADATA,sha256=-GgQ5YJnlTUt5r8E0HUKhSTb6Da1CSIGWq7iFcYTUQE,10535
37
+ topologicpy-0.8.73.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ topologicpy-0.8.73.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
39
+ topologicpy-0.8.73.dist-info/RECORD,,