Add extend/regenerate walls to multiple undersides

extend_walls_to_underside now accepts multiple slab/roof
objects in a single operation — all selected non-LAYER2 IFC
elements are treated as clip targets, all LAYER2 elements as
walls. Placement sync is done once upfront; each wall is then
clipped against every selected slab before reloading.

Also adds bim.regenerate_wall_to_underside (Shift+G): after
moving a slab, re-clips connected walls using the existing
IfcRelConnectsElements(TOP) relationship. Old booleans are
removed via remove_representation_item before re-clipping.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Ryan Schultz
2026-04-16 22:44:53 -05:00
parent 5217ff0dce
commit 2e85a11c82
2 changed files with 25 additions and 18 deletions
+10 -9
View File
@@ -300,20 +300,21 @@ class ExtendWallsToUnderside(bpy.types.Operator, tool.Ifc.Operator):
# of the selected walls has an in-progress parametric draft, commit it before
# extending, so the slab clip operates on the just-finalised IFC state.
_commit_pending_wall_edits_for_selection(context)
slab = None
slabs: list[bpy.types.Object] = []
walls: list[bpy.types.Object] = []
if (obj := tool.Blender.get_active_object(is_selected=True)) and (element := tool.Ifc.get_entity(obj)):
slab = obj
for obj in tool.Blender.get_selected_objects(include_active=False):
for obj in tool.Blender.get_selected_objects():
element = tool.Ifc.get_entity(obj)
usage = tool.Model.get_usage_type(element) if element else None
if element and usage == "LAYER2":
if not element:
continue
if tool.Model.get_usage_type(element) == "LAYER2":
walls.append(obj)
if slab and walls:
core.extend_wall_to_slab(tool.Ifc, tool.Geometry, tool.Model, slab, walls)
else:
slabs.append(obj)
if slabs and walls:
core.extend_wall_to_slab(tool.Ifc, tool.Geometry, tool.Model, slabs, walls)
_resync_walls_after_mutation(walls)
else:
self.report({"ERROR"}, "Please select at least one LAYER2 element and an active element")
self.report({"ERROR"}, "Please select at least one LAYER2 element and at least one other IFC element")
class RegenerateWallToUnderside(bpy.types.Operator, tool.Ifc.Operator):
+15 -9
View File
@@ -190,20 +190,26 @@ def extend_wall_to_slab(
ifc: type[tool.Ifc],
geometry: type[tool.Geometry],
model: type[tool.Model],
slab_obj: bpy.types.Object,
slab_objs: list[bpy.types.Object],
wall_objs: list[bpy.types.Object],
) -> None:
clip = model.get_slab_clipping_bmesh(slab_obj)
if not clip:
return
slab = ifc.get_entity(slab_obj)
for obj in wall_objs:
if ifc.is_moved(obj):
geometry.run_edit_object_placement(obj=obj)
wall = ifc.get_entity(obj)
model.clip_wall_to_slab(wall, clip)
model.connect_wall_to_slab(wall, slab)
model.reload_body_representation(wall_objs)
clipped_walls = []
for slab_obj in slab_objs:
clip = model.get_slab_clipping_bmesh(slab_obj)
if not clip:
continue
slab = ifc.get_entity(slab_obj)
for obj in wall_objs:
wall = ifc.get_entity(obj)
model.clip_wall_to_slab(wall, clip)
model.connect_wall_to_slab(wall, slab)
if obj not in clipped_walls:
clipped_walls.append(obj)
if clipped_walls:
model.reload_body_representation(clipped_walls)
class RequireTwoWallsError(Exception):