From 0593a84d34b6972770b31cfad1572bf8d4de91e4 Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Thu, 16 Jul 2026 18:26:22 +0300 Subject: [PATCH] Bonsai: let standalone IfcCurtainWall bound spaces and boundaries brunopostle reported that a curtain wall used as a standalone room side (not decomposed into IfcMember/IfcPlate children) was invisible to space/boundary generation, since only IfcWall, IfcColumn, IfcMember, IfcVirtualElement, and IfcPlate were treated as bounding classes. CyrilWaechter concretely proposed adding curtain wall to that detection list, while explicitly leaving structural elements (a separate, still-open design question) alone. IfcCurtainWall is not a schema subtype of IfcWall, so it was silently skipped by every class list that checked "is this a wall/column/etc." Three separate lists needed it: - tool.Spatial.is_bounding_class, used by "Generate Space" (cursor cutting-plane detection). - tool.Spatial.get_boundary_elements, used by "Generate Spaces From Walls" (bulk perimeter detection). - AddBoundary.auto_generate_boundaries' building_elements gather, used by "Add Boundary" when auto-detecting nearby elements for a single selected IfcSpace. The separate manual AddBoundary path (selecting an IfcSpace plus one IfcElement directly) was already class-agnostic and did not need a change; verified live that it already creates a boundary against an IfcCurtainWall today. Verified live in headless Blender: a synthetic 4m x 4m room with 3 IfcWalls and one standalone IfcCurtainWall (single object, not aggregated into members/plates) produced 0 spaces via "Generate Spaces From Walls" before this fix, and 1 correctly-bounded space after. The manual "Add Boundary" (IfcSpace + IfcCurtainWall selected) path created an IfcRelSpaceBoundary against the curtain wall both before and after, confirming it needed no change. IfcWindow remains untouched; CyrilWaechter was explicit that whether to auto-detect standalone windows/structural elements is a separate, unresolved design question. Fixes #3995 Generated with the assistance of an AI coding tool. --- src/bonsai/bonsai/bim/module/boundary/operator.py | 1 + src/bonsai/bonsai/tool/spatial.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/boundary/operator.py b/src/bonsai/bonsai/bim/module/boundary/operator.py index 45ee8c60e2..4d4b594a11 100644 --- a/src/bonsai/bonsai/bim/module/boundary/operator.py +++ b/src/bonsai/bonsai/bim/module/boundary/operator.py @@ -682,6 +682,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator): tool.Ifc.get().by_type("IfcWall") + tool.Ifc.get().by_type("IfcSlab") + tool.Ifc.get().by_type("IfcVirtualElement") + + tool.Ifc.get().by_type("IfcCurtainWall") ) for building_element in building_elements: diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index df87d493c7..650cad2f27 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -755,7 +755,7 @@ class Spatial(bonsai.core.tool.Spatial): @classmethod def is_bounding_class(cls, visible_element: ifcopenshell.entity_instance) -> bool: - for ifc_class in ["IfcWall", "IfcColumn", "IfcMember", "IfcVirtualElement", "IfcPlate"]: + for ifc_class in ["IfcWall", "IfcColumn", "IfcMember", "IfcVirtualElement", "IfcPlate", "IfcCurtainWall"]: if visible_element.is_a(ifc_class): return True return False @@ -936,7 +936,7 @@ class Spatial(bonsai.core.tool.Spatial): boundary_elements = [] for obj in selected_objects: subelement = tool.Ifc.get_entity(obj) - if subelement.is_a("IfcWall") or subelement.is_a("IfcColumn"): + if subelement.is_a("IfcWall") or subelement.is_a("IfcColumn") or subelement.is_a("IfcCurtainWall"): boundary_elements.append(subelement) return boundary_elements