diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 0c79b36167..395e3f72aa 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -39,6 +39,7 @@ classes = ( opening.AddElementOpening, opening.AddPotentialHalfSpaceSolid, opening.AddPotentialOpening, + opening.EditOpenings, opening.HideBooleans, opening.HideOpenings, opening.RemoveBooleans, diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index c2d4dc0974..758281ffc6 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -40,6 +40,8 @@ class AuthoringData: cls.load_ifc_classes() cls.load_relating_types() cls.load_relating_types_browser() + cls.data["is_voidable_element"] = cls.is_voidable_element() + cls.data["has_visible_openings"] = cls.has_visible_openings() @classmethod def load_ifc_classes(cls): @@ -53,6 +55,20 @@ class AuthoringData: def load_relating_types_browser(cls): cls.data["relating_types_ids_browser"] = cls.relating_types_browser() + @classmethod + def is_voidable_element(cls): + element = tool.Ifc.get_entity(bpy.context.active_object) + return element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement") + + @classmethod + def has_visible_openings(cls): + element = tool.Ifc.get_entity(bpy.context.active_object) + if element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement"): + for opening in [r.RelatedOpeningElement for r in element.HasOpenings]: + if tool.Ifc.get_object(opening): + return True + return False + @classmethod def ifc_classes(cls): results = [] diff --git a/src/blenderbim/blenderbim/bim/module/model/opening.py b/src/blenderbim/blenderbim/bim/module/model/opening.py index 98aab49a8c..e5714177e3 100644 --- a/src/blenderbim/blenderbim/bim/module/model/opening.py +++ b/src/blenderbim/blenderbim/bim/module/model/opening.py @@ -240,10 +240,9 @@ class AddBoolean(Operator, tool.Ifc.Operator): def _execute(self, context): props = context.scene.BIMModelProperties - objs = context.selected_objects - if len(objs) != 2: + if len(context.selected_objects) != 2: return {"FINISHED"} - obj1, obj2 = objs + obj1, obj2 = context.selected_objects element1 = tool.Ifc.get_entity(obj1) element2 = tool.Ifc.get_entity(obj2) if element1 and element2: @@ -439,6 +438,38 @@ class HideOpenings(Operator, tool.Ifc.Operator): return {"FINISHED"} +class EditOpenings(Operator, tool.Ifc.Operator): + bl_idname = "bim.edit_openings" + bl_label = "Edit Openings" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + props = bpy.context.scene.BIMModelProperties + for obj in context.selected_objects: + element = tool.Ifc.get_entity(obj) + if not element: + continue + openings = [r.RelatedOpeningElement for r in element.HasOpenings] + for opening in openings: + opening_obj = tool.Ifc.get_object(opening) + if opening_obj: + tool.Geometry.run_geometry_update_representation(obj=opening_obj) + tool.Ifc.unlink(element=opening, obj=opening_obj) + bpy.data.objects.remove(opening_obj) + + body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + blenderbim.core.geometry.switch_representation( + tool.Geometry, + obj=obj, + representation=body, + should_reload=True, + enable_dynamic_voids=False, + is_global=True, + should_sync_changes_first=False, + ) + return {"FINISHED"} + + class DecorationsHandler: installed = None @@ -509,12 +540,12 @@ class DecorationsHandler: else: unselected_edges.append(edge_indices) - batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=unselected_edges) + batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=unselected_edges) self.shader.bind() self.shader.uniform_float("color", white) batch.draw(self.shader) - batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=selected_edges) + batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=selected_edges) self.shader.uniform_float("color", green) batch.draw(self.shader) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 1ff439f105..9091decd5a 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -230,7 +230,17 @@ class BimTool(WorkSpaceTool): row = layout.row(align=True) row.label(text="", icon="EVENT_SHIFT") - row.label(text="Opening", icon="EVENT_O") + row.label(text="", icon="EVENT_O") + if len(context.selected_objects) == 2: + row.operator("bim.add_opening", text="Void") + else: + row.operator("bim.add_potential_opening", text="Void") + if AuthoringData.data["is_voidable_element"]: + if AuthoringData.data["has_visible_openings"]: + row.operator("bim.edit_openings", icon="CHECKMARK", text="") + row.operator("bim.hide_openings", icon="CANCEL", text="") + else: + row.operator("bim.show_openings", icon="HIDE_OFF", text="") row = layout.row(align=True) row.label(text="Align") diff --git a/src/blenderbim/blenderbim/bim/module/void/operator.py b/src/blenderbim/blenderbim/bim/module/void/operator.py index fe6cc867b9..f3098cf960 100644 --- a/src/blenderbim/blenderbim/bim/module/void/operator.py +++ b/src/blenderbim/blenderbim/bim/module/void/operator.py @@ -25,72 +25,57 @@ from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.void.data import Data -class AddOpening(bpy.types.Operator): +class AddOpening(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_opening" bl_label = "Add Opening" bl_options = {"REGISTER", "UNDO"} - opening: bpy.props.StringProperty() - obj: bpy.props.StringProperty() - - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - self.file = IfcStore.get_file() - obj = context.scene.objects.get(self.obj, context.active_object) - if obj is None: + props = context.scene.BIMModelProperties + if len(context.selected_objects) != 2: return {"FINISHED"} - element_id = obj.BIMObjectProperties.ifc_definition_id - if not element_id: + obj1, obj2 = context.selected_objects + element1 = tool.Ifc.get_entity(obj1) + element2 = tool.Ifc.get_entity(obj2) + if element1 and element2: return {"FINISHED"} - element = self.file.by_id(element_id) - tool.Geometry.clear_cache(element) - if element.is_a("IfcOpeningElement"): - self.report({"WARNING"}, "An IfcOpeningElement can't be voided") + if not element1 and not element2: return {"FINISHED"} - - opening = bpy.data.objects.get(self.opening) - if opening is None: + if element2 and not element1: + obj1, obj2 = obj2, obj1 + element1, element2 = element2, element1 + if element1.is_a("IfcOpeningElement"): return {"FINISHED"} - opening.display_type = "WIRE" - if not opening.BIMObjectProperties.ifc_definition_id: - body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body") - if not body_context: - return {"FINISHED"} - bpy.ops.bim.assign_class(obj=opening.name, ifc_class="IfcOpeningElement", context_id=body_context.id()) - - # If the IfcOpeningElement already voids another object, remove the boolean modifier - opening_element = self.file.by_id(opening.BIMObjectProperties.ifc_definition_id) - if opening_element.VoidsElements: - other_obj = IfcStore.get_element(opening_element.VoidsElements[0].RelatingBuildingElement.id()) - try: - modifier = next(m for m in other_obj.modifiers if m.type == "BOOLEAN" and m.object == opening) - other_obj.modifiers.remove(modifier) - except StopIteration: - pass - - ifcopenshell.api.run( - "void.add_opening", - self.file, - **{ - "opening": opening_element, - "element": element, - }, + if obj2 not in [o.obj for o in props.openings]: + return {"FINISHED"} + if not obj1.data or not hasattr(obj1.data, "BIMMeshProperties"): + return {"FINISHED"} + body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body") + element2 = blenderbim.core.root.assign_class( + tool.Ifc, + tool.Collector, + tool.Root, + obj=obj2, + ifc_class="IfcOpeningElement", + should_add_representation=True, + context=body_context, ) - Data.load(self.file, element_id) + ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=element2, element=element1) - try: - modifier = next(m for m in obj.modifiers if m.type == "BOOLEAN" and m.object == opening) - except StopIteration: - modifier = obj.modifiers.new("IfcOpeningElement", "BOOLEAN") - modifier.object = opening - finally: - modifier.operation = "DIFFERENCE" - modifier.solver = "EXACT" - modifier.use_self = True - modifier.operand_type = "OBJECT" - - context.view_layer.objects.active = obj + representation = tool.Ifc.get().by_id(obj1.data.BIMMeshProperties.ifc_definition_id) + blenderbim.core.geometry.switch_representation( + tool.Geometry, + obj=obj1, + representation=representation, + should_reload=True, + enable_dynamic_voids=False, + is_global=True, + should_sync_changes_first=False, + ) + Data.load(tool.Ifc.get(), element1.id()) + tool.Ifc.unlink(obj=obj2) + bpy.data.objects.remove(obj2) + context.view_layer.objects.active = obj1 return {"FINISHED"}