See #5972. You can now add booleans in item editing mode, with support for all three boolean operators.

This commit is contained in:
Dion Moult
2025-01-17 20:54:34 +11:00
parent 0a87e92dcf
commit e1da08579c
3 changed files with 60 additions and 66 deletions
+46 -64
View File
@@ -532,77 +532,59 @@ class AddBoolean(Operator, tool.Ifc.Operator):
return True return True
def _execute(self, context): def _execute(self, context):
ifc_file = tool.Ifc.get() first_obj = tool.Blender.get_active_object()
ifc_objects: list[bpy.types.Object] = [] if not tool.Geometry.is_representation_item(first_obj):
non_ifc_objects: list[bpy.types.Object] = [] self.report({"INFO"}, "At least two representation items must be selected to add a boolean.")
for obj in context.selected_objects: return {"CANCELLED"}
if tool.Ifc.get_entity(obj): second_objs = [
ifc_objects.append(obj) o for o in tool.Blender.get_selected_objects() if o != first_obj and tool.Geometry.is_representation_item(o)
else: ]
non_ifc_objects.append(obj) if not second_objs:
self.report({"INFO"}, "At least two representation items must be selected to add a boolean.")
if not non_ifc_objects:
self.report({"INFO"}, "At least 1 non-ifc object should be selected to be added as a boolean.")
return {"CANCELLED"} return {"CANCELLED"}
if len(ifc_objects) != 1: props = context.scene.BIMBooleanProperties
self.report(
{"INFO"},
f"Only 1 IFC object need to be selected to add booleans to, currently selected {len(ifc_objects)} IFC objects.",
)
return {"CANCELLED"}
ifc_obj = ifc_objects[0] first_item = tool.Ifc.get().by_id(first_obj.data.BIMMeshProperties.ifc_definition_id)
representation = tool.Geometry.get_active_representation(ifc_obj) while True:
if not representation: is_part_of_boolean = False
self.report({"INFO"}, "No representation found for the selected IFC object.") for inverse in tool.Ifc.get().get_inverse(first_item):
return {"FINISHED"} if inverse.is_a("IfcBooleanResult"):
is_part_of_boolean = True
first_item = inverse
if not is_part_of_boolean:
break
# Apply objects as booleans. # Don't replace style or aspect relationships.
booleans = [] to_replace = set(
for boolean_obj in non_ifc_objects: [
if ( i
not boolean_obj.data for i in tool.Ifc.get().get_inverse(first_item)
or not isinstance(boolean_obj.data, bpy.types.Mesh) if i.is_a("IfcShapeRepresentation") or i.is_a("IfcBooleanResult")
or len(boolean_obj.data.polygons) <= 4 ]
): # It takes 4 faces to create a closed solid
mesh_data = {
"type": "IfcHalfSpaceSolid",
"matrix": ifc_obj.matrix_world.inverted() @ boolean_obj.matrix_world,
}
else:
mesh_data = {
"type": "Mesh",
"blender_obj": ifc_obj,
"blender_void": boolean_obj,
}
booleans_ = ifcopenshell.api.geometry.add_boolean(
ifc_file,
representation=representation,
operator="DIFFERENCE",
**mesh_data,
)
booleans.extend(booleans_)
element = tool.Ifc.get_entity(ifc_obj)
assert element
tool.Model.mark_manual_booleans(element, booleans)
bonsai.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=ifc_obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
) )
tool.Blender.remove_data_blocks(non_ifc_objects, remove_unused_data=True) first = first_item
tool.Model.purge_scene_openings()
return {"FINISHED"} booleans = set()
for second_obj in second_objs:
second = tool.Ifc.get().by_id(second_obj.data.BIMMeshProperties.ifc_definition_id)
for inverse in tool.Ifc.get().get_inverse(second):
if inverse.is_a("IfcShapeRepresentation"):
inverse.Items = list(set(inverse.Items) - {second})
first = tool.Ifc.get().create_entity("IfcBooleanResult", props.operator, first, second)
booleans.add(first)
for inverse in to_replace:
ifcopenshell.util.element.replace_attribute(inverse, first_item, first)
rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj
rep_element = tool.Ifc.get_entity(rep_obj)
tool.Model.mark_manual_booleans(rep_element, booleans)
tool.Geometry.reload_representation(rep_obj)
if props.is_editing:
bpy.ops.bim.enable_editing_booleans()
class ShowBooleans(Operator, tool.Ifc.Operator, AddObjectHelper): class ShowBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
+10 -1
View File
@@ -18,7 +18,7 @@
import bpy import bpy
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import PointerProperty, StringProperty, IntProperty, BoolProperty, CollectionProperty from bpy.props import PointerProperty, StringProperty, IntProperty, BoolProperty, CollectionProperty, EnumProperty
class Boolean(PropertyGroup): class Boolean(PropertyGroup):
@@ -36,3 +36,12 @@ class BIMBooleanProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False) is_editing: BoolProperty(name="Is Editing", default=False)
booleans: CollectionProperty(name="Booleans", type=Boolean) booleans: CollectionProperty(name="Booleans", type=Boolean)
active_boolean_index: IntProperty(name="Active Boolean Index") active_boolean_index: IntProperty(name="Active Boolean Index")
operator: EnumProperty(
items=[
("DIFFERENCE", "DIFFERENCE", ""),
("INTERSECTION", "INTERSECTION", ""),
("UNION", "UNION", ""),
],
name="Operator",
default="DIFFERENCE",
)
+4 -1
View File
@@ -156,7 +156,6 @@ class BIM_PT_booleans(Panel):
row.operator("bim.disable_editing_booleans", icon="CANCEL", text="") row.operator("bim.disable_editing_booleans", icon="CANCEL", text="")
else: else:
row.operator("bim.enable_editing_booleans", icon="IMPORT", text="") row.operator("bim.enable_editing_booleans", icon="IMPORT", text="")
row.operator("bim.add_boolean", text="", icon="ADD")
booleans_are_manual = len(manual_booleans) == len(total_booleans) booleans_are_manual = len(manual_booleans) == len(total_booleans)
op = row.operator( op = row.operator(
"bim.booleans_mark_as_manual", text="", icon="PINNED" if booleans_are_manual else "UNPINNED" "bim.booleans_mark_as_manual", text="", icon="PINNED" if booleans_are_manual else "UNPINNED"
@@ -173,6 +172,10 @@ class BIM_PT_booleans(Panel):
if not props.is_editing: if not props.is_editing:
return return
row = layout.row(align=True)
row.prop(props, "operator", text="")
row.operator("bim.add_boolean", text="", icon="ADD")
self.layout.template_list("BIM_UL_booleans", "", props, "booleans", props, "active_boolean_index") self.layout.template_list("BIM_UL_booleans", "", props, "booleans", props, "active_boolean_index")