bim.add_boolean - support adding multiple booleans at once

Example - https://imgur.com/a/IWDqMRT
This commit is contained in:
Andrej730
2025-01-15 17:15:47 +05:00
parent 1f3fc3c2b9
commit 5d17554d04
+54 -24
View File
@@ -25,6 +25,7 @@ import logging
import numpy as np import numpy as np
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.geometry
import ifcopenshell.geom import ifcopenshell.geom
import ifcopenshell.util.shape import ifcopenshell.util.shape
import ifcopenshell.util.element import ifcopenshell.util.element
@@ -527,52 +528,81 @@ class AddBoolean(Operator, tool.Ifc.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not len(context.selected_objects) == 2: if not len(context.selected_objects) >= 2:
cls.poll_message_set("Exactly 2 objects need to be selected.") cls.poll_message_set("At least 2 objects need to be selected.")
return False return False
return True return True
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMModelProperties ifc_file = tool.Ifc.get()
obj1, obj2 = context.selected_objects ifc_objects: list[bpy.types.Object] = []
element1 = tool.Ifc.get_entity(obj1) non_ifc_objects: list[bpy.types.Object] = []
element2 = tool.Ifc.get_entity(obj2) for obj in context.selected_objects:
if not (bool(element1) ^ bool(element2)): if tool.Ifc.get_entity(obj):
self.report({"INFO"}, "One of the selected objects should be blender object and another IFC object.") ifc_objects.append(obj)
return {"FINISHED"} else:
non_ifc_objects.append(obj)
# element1 - IFC object, element2 - blender object. if not non_ifc_objects:
if element2 and not element1: self.report({"INFO"}, "At least 1 non-ifc object should be selected to be added as a boolean.")
obj1, obj2 = obj2, obj1 return {"CANCELLED"}
element1, element2 = element2, element1
representation = tool.Geometry.get_active_representation(obj1) if len(ifc_objects) != 1:
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]
representation = tool.Geometry.get_active_representation(ifc_obj)
if not representation: if not representation:
self.report({"INFO"}, "No representation found for the selected IFC object.") self.report({"INFO"}, "No representation found for the selected IFC object.")
return {"FINISHED"} return {"FINISHED"}
if not obj2.data or len(obj2.data.polygons) <= 4: # It takes 4 faces to create a closed solid # Apply objects as booleans.
mesh_data = {"type": "IfcHalfSpaceSolid", "matrix": obj1.matrix_world.inverted() @ obj2.matrix_world} booleans = []
else: for boolean_obj in non_ifc_objects:
mesh_data = {"type": "Mesh", "blender_obj": obj1, "blender_void": obj2} if (
not boolean_obj.data
or not isinstance(boolean_obj.data, bpy.types.Mesh)
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.run( booleans_ = ifcopenshell.api.geometry.add_boolean(
"geometry.add_boolean", tool.Ifc.get(), representation=representation, operator="DIFFERENCE", **mesh_data ifc_file,
) representation=representation,
operator="DIFFERENCE",
**mesh_data,
)
booleans.extend(booleans_)
tool.Model.mark_manual_booleans(element1, booleans) element = tool.Ifc.get_entity(ifc_obj)
assert element
tool.Model.mark_manual_booleans(element, booleans)
bonsai.core.geometry.switch_representation( bonsai.core.geometry.switch_representation(
tool.Ifc, tool.Ifc,
tool.Geometry, tool.Geometry,
obj=obj1, obj=ifc_obj,
representation=representation, representation=representation,
should_reload=True, should_reload=True,
is_global=True, is_global=True,
should_sync_changes_first=False, should_sync_changes_first=False,
) )
tool.Blender.remove_data_blocks([obj2], remove_unused_data=True) tool.Blender.remove_data_blocks(non_ifc_objects, remove_unused_data=True)
tool.Model.purge_scene_openings() tool.Model.purge_scene_openings()
return {"FINISHED"} return {"FINISHED"}