diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 5e51267ffb..66fdb9e96f 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -384,8 +384,7 @@ class OverrideDeleteTrait: bpy.ops.bim.remove_opening(opening_id=element.id()) def remove_filling(self, element): - obj = IfcStore.get_element(element.id()) - bpy.ops.bim.remove_filling(obj=obj.name) + bpy.ops.bim.remove_filling(filling=element.id()) def remove_port(self, port): blenderbim.core.system.remove_port(tool.Ifc, tool.System, port=port) diff --git a/src/blenderbim/blenderbim/bim/module/void/data.py b/src/blenderbim/blenderbim/bim/module/void/data.py index 73909b93b2..8fa4b3f1fc 100644 --- a/src/blenderbim/blenderbim/bim/module/void/data.py +++ b/src/blenderbim/blenderbim/bim/module/void/data.py @@ -22,6 +22,50 @@ import blenderbim.tool as tool def refresh(): BooleansData.is_loaded = False + VoidsData.is_loaded = False + + +class VoidsData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"active_opening": cls.active_opening(), "openings": cls.openings(), "fillings": cls.fillings()} + cls.is_loaded = True + + @classmethod + def active_opening(cls): + element = tool.Ifc.get_entity(bpy.context.active_object) + if element and element.is_a("IfcOpeningElement"): + return element.id() + + @classmethod + def openings(cls): + element = tool.Ifc.get_entity(bpy.context.active_object) + if not element: + return [] + results = [] + for rel in getattr(element, "HasOpenings", []) or []: + has_fillings = [] + opening = rel.RelatedOpeningElement + for rel2 in getattr(opening, "HasFillings", []) or []: + filling = rel2.RelatedBuildingElement + has_fillings.append({"id": filling.id(), "Name": filling.Name or "Unnamed"}) + results.append({"id": opening.id(), "Name": opening.Name or "Unnamed", "HasFillings": has_fillings}) + return results + + @classmethod + def fillings(cls): + element = tool.Ifc.get_entity(bpy.context.active_object) + if not element: + return [] + results = [] + for rel in getattr(element, "HasFillings", []) or []: + filling = rel.RelatedBuildingElement + results.append({"id": filling.id(), "Name": filling.Name or "Unnamed"}) + return results + class BooleansData: diff --git a/src/blenderbim/blenderbim/bim/module/void/operator.py b/src/blenderbim/blenderbim/bim/module/void/operator.py index aba2441409..24e2222dbc 100644 --- a/src/blenderbim/blenderbim/bim/module/void/operator.py +++ b/src/blenderbim/blenderbim/bim/module/void/operator.py @@ -22,7 +22,6 @@ import ifcopenshell.util.representation import blenderbim.tool as tool import blenderbim.core.geometry from blenderbim.bim.ifc import IfcStore -from ifcopenshell.api.void.data import Data class AddOpening(bpy.types.Operator, tool.Ifc.Operator): @@ -89,7 +88,6 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator): is_global=True, should_sync_changes_first=False, ) - Data.load(tool.Ifc.get(), element1.id()) if not has_visible_openings: tool.Ifc.unlink(obj=obj2) @@ -128,7 +126,6 @@ class RemoveOpening(bpy.types.Operator, tool.Ifc.Operator): ) tool.Geometry.clear_cache(element) - Data.load(tool.Ifc.get(), obj.BIMObjectProperties.ifc_definition_id) return {"FINISHED"} @@ -153,37 +150,23 @@ class AddFilling(bpy.types.Operator): if not element_id or not opening_id or element_id == opening_id: return {"FINISHED"} ifcopenshell.api.run( - "void.add_filling", - self.file, - **{ - "opening": self.file.by_id(opening_id), - "element": self.file.by_id(element_id), - }, + "void.add_filling", self.file, opening=self.file.by_id(opening_id), element=self.file.by_id(element_id) ) - Data.load(self.file, element_id) return {"FINISHED"} -class RemoveFilling(bpy.types.Operator): +class RemoveFilling(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_filling" bl_label = "Remove Filling" bl_options = {"REGISTER", "UNDO"} - obj: bpy.props.StringProperty() - - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) + filling: bpy.props.IntProperty() def _execute(self, context): - obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object - if not obj: - return {"FINISHED"} - element = tool.Ifc.get_entity(obj) - if not element: - return {"FINISHED"} - for rel in element.FillsVoids: + filling = tool.Ifc.get().by_id(self.filling) + filling_obj = tool.Ifc.get_object(filling) + for rel in filling.FillsVoids: bpy.ops.bim.remove_opening(opening_id=rel.RelatingOpeningElement.id()) - ifcopenshell.api.run("void.remove_filling", tool.Ifc.get(), element=element) - Data.load(tool.Ifc.get(), element.id()) + ifcopenshell.api.run("void.remove_filling", tool.Ifc.get(), element=filling) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/void/ui.py b/src/blenderbim/blenderbim/bim/module/void/ui.py index 128073a4ed..c29154542c 100644 --- a/src/blenderbim/blenderbim/bim/module/void/ui.py +++ b/src/blenderbim/blenderbim/bim/module/void/ui.py @@ -17,10 +17,9 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import blenderbim.tool as tool from bpy.types import Panel -from ifcopenshell.api.void.data import Data -from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.void.data import BooleansData +from blenderbim.bim.module.void.data import BooleansData, VoidsData class BIM_PT_voids(Panel): @@ -34,71 +33,48 @@ class BIM_PT_voids(Panel): @classmethod def poll(cls, context): - if not context.active_object: + if not tool.Ifc.get_entity(context.active_object): return False - if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id): - return False - return IfcStore.get_file() + return True def draw(self, context): - props = context.active_object.BIMObjectProperties - file = IfcStore.get_file() - if props.ifc_definition_id not in Data.products: - Data.load(file, props.ifc_definition_id) - active_object_is_an_opening = file.by_id(props.ifc_definition_id).is_a("IfcOpeningElement") - row = self.layout.row(align=True) - if len(context.selected_objects) == 2: - op = row.operator("bim.add_opening", icon="ADD", text="Add Opening") - opening_id = None - obj_name = None - for obj in context.selected_objects: - if obj.BIMObjectProperties.ifc_definition_id in Data.openings: - opening_id = obj.BIMObjectProperties.ifc_definition_id - elif obj.BIMObjectProperties.ifc_definition_id in Data.fillings: - opening_id = Data.fillings[obj.BIMObjectProperties.ifc_definition_id]["FillsVoid"] - else: - obj_name = obj.name - if opening_id and obj_name: - op = row.operator("bim.remove_opening", icon="X", text="Remove Opening").opening_id = opening_id + if not VoidsData.is_loaded: + VoidsData.load() - opening_ids = Data.products[props.ifc_definition_id] - if not opening_ids and not active_object_is_an_opening: + props = context.active_object.BIMObjectProperties + + if len(context.selected_objects) == 2: row = self.layout.row(align=True) - row.label(text="No Openings", icon="SELECT_SUBTRACT") - for opening_id in opening_ids: - opening = Data.openings[opening_id] - if opening["HasFillings"]: - for filling_id in opening["HasFillings"]: - filling = Data.fillings.get(filling_id) - if filling is None: - continue - row = self.layout.row(align=True) - row.label(text=opening["Name"], icon="SELECT_SUBTRACT") - row.label(text=filling["Name"], icon="SELECT_INTERSECT") - else: - row = self.layout.row(align=True) - row.label(text=opening["Name"], icon="SELECT_SUBTRACT") - op = row.operator("bim.remove_opening", icon="X", text="").opening_id = opening_id - if props.ifc_definition_id in Data.openings: - for filling_id in Data.openings[props.ifc_definition_id]["HasFillings"]: - filling = Data.fillings.get(filling_id) - if filling is None: - continue + op = row.operator("bim.add_opening", icon="ADD", text="Add Opening") + + if VoidsData.data["active_opening"]: + row = self.layout.row() + op = row.operator("bim.remove_opening", icon="X", text="Remove Opening") + op.opening_id = VoidsData.data["active_opening"] + + if not VoidsData.data["fillings"]: + row = self.layout.row() + row.label(text="No Fillings", icon="SELECT_INTERSECT") + + for filling in VoidsData.data["fillings"]: row = self.layout.row(align=True) row.label(text=filling["Name"], icon="SELECT_INTERSECT") - op = row.operator("bim.remove_filling", icon="X", text="") - op.obj = IfcStore.get_element(filling_id).name - if active_object_is_an_opening: - pass - elif props.ifc_definition_id not in Data.fillings: - row = self.layout.row(align=True) - row.prop(context.scene.VoidProperties, "desired_opening", text="", icon="SELECT_INTERSECT") - row.operator("bim.add_filling", icon="ADD", text="") + row.operator("bim.remove_filling", icon="X", text="").filling = filling["id"] else: - opening = Data.openings[Data.fillings[props.ifc_definition_id]["FillsVoid"]] - row = self.layout.row(align=True) - row.label(text=opening["Name"], icon="SELECT_INTERSECT") - row.operator("bim.remove_filling", icon="X", text="") + if not VoidsData.data["openings"]: + row = self.layout.row() + row.label(text="No Openings", icon="SELECT_SUBTRACT") + + for opening in VoidsData.data["openings"]: + if opening["HasFillings"]: + for filling in opening["HasFillings"]: + row = self.layout.row(align=True) + row.label(text=opening["Name"], icon="SELECT_SUBTRACT") + row.label(text=filling["Name"], icon="SELECT_INTERSECT") + else: + row = self.layout.row(align=True) + row.label(text=opening["Name"], icon="SELECT_SUBTRACT") + row.operator("bim.remove_opening", icon="X", text="").opening_id = opening["id"] class BIM_PT_booleans(Panel):