Extract space generation algorithms to ifcopenshell.util

Move Blender-independent space generation algorithms from Bonsai
(GPL) to ifcopenshell.util (LGPL):

- ifcopenshell.util.shape.bisect_mesh_plane_vf: vectorized numpy
  triangle/plane intersection for mesh bisection
- ifcopenshell.util.element.iter_top_connections: walker for
  IfcRelConnectsElements(TOP) relationships
- ifcopenshell.util.space: new module with get_boundary_lines,
  get_space_polygon, get_auto_space_height and height detection
  helpers — all operating on IFC geometry without Blender

Bonsai's tool/spatial.py now delegates to these utilities via
thin wrappers, keeping only Blender-specific concerns (cache
management with depsgraph invalidation, UI property reads).

tool/wall.py iter_wall_slab_connections delegates to
ifcopenshell.util.element.iter_top_connections.

Added 22 tests: 6 for bisect_mesh_plane_vf, 10 for space
generation algorithms, 4 for iter_top_connections, 2 Bonsai
integration tests for cache behavior.

Generated with the assistance of an AI coding tool.
This commit is contained in:
CyrilWaechter
2026-07-27 09:58:24 +02:00
parent 2f7af12734
commit 3e6460eceb
7 changed files with 675 additions and 10 deletions
@@ -2007,3 +2007,24 @@ def get_material_profiles(element: ifcopenshell.entity_instance) -> list[Priorit
)
for material_profile in material.MaterialProfiles
]
def iter_top_connections(
element: ifcopenshell.entity_instance,
) -> Generator[tuple[ifcopenshell.entity_instance, ifcopenshell.entity_instance], None, None]:
"""Yield ``(connected_element, rel)`` tuples for every
``IfcRelConnectsElements`` with ``Description == "TOP"`` connecting
to this element.
Walks ``element.ConnectedFrom`` because the connecting element (e.g. a
slab) is the relating side of the TOP relationship.
:param element: The IFC element (typically a wall).
:return: Generator of ``(connected_element, rel)`` tuples.
"""
for rel in getattr(element, "ConnectedFrom", []) or ():
if not rel.is_a("IfcRelConnectsElements") or rel.Description != "TOP":
continue
connected = rel.RelatingElement
if connected is not None:
yield connected, rel