From 8c25349b490829267fdeb9f7dce3c85d0ecf02e7 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 3 Oct 2022 20:49:44 +1100 Subject: [PATCH] Add new opening functions to BIM tool workspace UI --- .../blenderbim/bim/module/model/opening.py | 115 ++++++++++-------- .../blenderbim/bim/module/model/prop.py | 3 + .../blenderbim/bim/module/model/workspace.py | 37 ++++-- .../blenderbim/bim/module/void/__init__.py | 1 - .../blenderbim/bim/module/void/operator.py | 30 ++--- src/blenderbim/blenderbim/tool/model.py | 13 +- 6 files changed, 116 insertions(+), 83 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/opening.py b/src/blenderbim/blenderbim/bim/module/model/opening.py index e5714177e3..3fca869690 100644 --- a/src/blenderbim/blenderbim/bim/module/model/opening.py +++ b/src/blenderbim/blenderbim/bim/module/model/opening.py @@ -162,41 +162,66 @@ class AddElementOpening(bpy.types.Operator): return obj -def add_object(self, context): - props = context.scene.BIMModelProperties - bm = bmesh.new() - bmesh.ops.create_cube(bm, size=self.size) - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - mesh = bpy.data.meshes.new(name="Dumb Opening") - bm.to_mesh(mesh) - bm.free() - obj = object_data_add(context, mesh, operator=self) - obj.name = "Opening" - - has_deleted_opening = True - while has_deleted_opening: - has_deleted_opening = False - for i, opening in enumerate(props.openings): - if not opening.obj: - props.openings.remove(i) - has_deleted_opening = True - - new = props.openings.add() - new.obj = obj - - DecorationsHandler.install(context) - - class AddPotentialOpening(Operator, AddObjectHelper): bl_idname = "bim.add_potential_opening" bl_label = "Add Potential Opening" bl_options = {"REGISTER", "UNDO"} - size: FloatProperty(name="Size", default=0.5) + x: FloatProperty(name="X", default=0.5) + y: FloatProperty(name="Y", default=0.5) + z: FloatProperty(name="Z", default=0.5) + + def draw_settings(context, layout, tool): + row = self.layout.row() + row.prop(data=self, property="x", label="Size X") + row = self.layout.row() + row.prop(data=self, property="y") + row = self.layout.row() + row.prop(data=self, property="z") def execute(self, context): - add_object(self, context) + props = context.scene.BIMModelProperties + + new_matrix = None + if context.selected_objects and context.active_object: + new_matrix = context.active_object.matrix_world.copy() + new_matrix.col[3] = context.scene.cursor.location.to_4d().to_4d() + + x = self.x / 2 + y = self.y / 2 + z = self.z / 2 + verts = [ + Vector((-x, -y, -z)), + Vector((-x, -y, z)), + Vector((-x, y, -z)), + Vector((-x, y, z)), + Vector((x, -y, -z)), + Vector((x, -y, z)), + Vector((x, y, -z)), + Vector((x, y, z)), + ] + edges = [] + faces = [ + [0, 1, 3, 2], + [2, 3, 7, 6], + [6, 7, 5, 4], + [4, 5, 1, 0], + [2, 6, 4, 0], + [7, 3, 1, 5], + ] + mesh = bpy.data.meshes.new(name="Opening") + mesh.from_pydata(verts, edges, faces) + obj = object_data_add(context, mesh, operator=self) + obj.name = "Opening" + + if new_matrix: + obj.matrix_world = new_matrix + + tool.Model.clear_scene_openings() + + new = props.openings.add() + new.obj = obj + + DecorationsHandler.install(context) return {"FINISHED"} @@ -218,13 +243,7 @@ class AddPotentialHalfSpaceSolid(Operator, AddObjectHelper): obj = object_data_add(context, mesh, operator=self) obj.name = "HalfSpaceSolid" - has_deleted_opening = True - while has_deleted_opening: - has_deleted_opening = False - for i, opening in enumerate(props.openings): - if not opening.obj: - props.openings.remove(i) - has_deleted_opening = True + tool.Model.clear_scene_openings() new = props.openings.add() new.obj = obj @@ -263,13 +282,7 @@ class AddBoolean(Operator, tool.Ifc.Operator): matrix=obj1.matrix_world.inverted() @ obj2.matrix_world, ) - has_deleted_opening = True - while has_deleted_opening: - has_deleted_opening = False - for i, opening in enumerate(props.openings): - if not opening.obj: - props.openings.remove(i) - has_deleted_opening = True + tool.Model.clear_scene_openings() blenderbim.core.geometry.switch_representation( tool.Geometry, @@ -334,13 +347,7 @@ class ShowBooleans(Operator, tool.Ifc.Operator, AddObjectHelper): obj = object_data_add(bpy.context, mesh, operator=self) obj.name = "HalfSpaceSolid" - has_deleted_opening = True - while has_deleted_opening: - has_deleted_opening = False - for i, opening in enumerate(props.openings): - if not opening.obj: - props.openings.remove(i) - has_deleted_opening = True + tool.Model.clear_scene_openings() new = props.openings.add() new.obj = obj @@ -425,6 +432,7 @@ class HideOpenings(Operator, tool.Ifc.Operator): def _execute(self, context): props = bpy.context.scene.BIMModelProperties + to_delete = set() for obj in context.selected_objects: element = tool.Ifc.get_entity(obj) if not element: @@ -433,8 +441,11 @@ class HideOpenings(Operator, tool.Ifc.Operator): for opening in openings: opening_obj = tool.Ifc.get_object(opening) if opening_obj: - tool.Ifc.unlink(element=opening, obj=opening_obj) - bpy.data.objects.remove(opening_obj) + to_delete.add(opening_obj) + for opening_obj in to_delete: + tool.Ifc.unlink(element=opening, obj=opening_obj) + bpy.data.objects.remove(opening_obj) + tool.Model.clear_scene_openings() return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 284c7012fa..b9a5b54a95 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -200,3 +200,6 @@ class BIMModelProperties(PropertyGroup): ) length: bpy.props.FloatProperty(default=42.0) openings: bpy.props.CollectionProperty(type=ObjProperty) + x: bpy.props.FloatProperty(name="X", default=0.5) + y: bpy.props.FloatProperty(name="Y", default=0.5) + z: bpy.props.FloatProperty(name="Z", default=0.5) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 9091decd5a..120aacec02 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -18,10 +18,10 @@ import os import bpy +import blenderbim.tool as tool import blenderbim.bim.module.type.prop as type_prop from blenderbim.bim.helper import prop_with_search, close_operator_panel from bpy.types import WorkSpaceTool -from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.data import AuthoringData from blenderbim.bim.module.model import prop @@ -56,13 +56,13 @@ class BimTool(WorkSpaceTool): ("bim.hotkey", {"type": "O", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_O")]}), ) - def draw_settings(context, layout, tool): + def draw_settings(context, layout, something): props = context.scene.BIMModelProperties is_tool_header = context.region.type == "TOOL_HEADER" is_sidebar = context.region.type == "UI" row = layout.row(align=True) - if not IfcStore.get_file(): + if not tool.Ifc.get(): row.label(text="No IFC Project", icon="ERROR") return @@ -232,9 +232,9 @@ class BimTool(WorkSpaceTool): row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_O") if len(context.selected_objects) == 2: - row.operator("bim.add_opening", text="Void") + row.operator("bim.add_opening", text="Apply Void") else: - row.operator("bim.add_potential_opening", text="Void") + row.operator("bim.add_potential_opening", text="Add Void") if AuthoringData.data["is_voidable_element"]: if AuthoringData.data["has_visible_openings"]: row.operator("bim.edit_openings", icon="CHECKMARK", text="") @@ -265,7 +265,7 @@ class BimTool(WorkSpaceTool): row.label(text="Decomposition", icon="EVENT_D") -class Hotkey(bpy.types.Operator): +class Hotkey(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.hotkey" bl_label = "Hotkey" bl_options = {"REGISTER", "UNDO"} @@ -273,10 +273,7 @@ class Hotkey(bpy.types.Operator): @classmethod def poll(cls, context): - return IfcStore.get_file() - - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) + return tool.Ifc.get() def _execute(self, context): self.props = context.scene.BIMModelProperties @@ -288,6 +285,16 @@ class Hotkey(bpy.types.Operator): getattr(self, f"hotkey_{self.hotkey}")() return {"FINISHED"} + def draw(self, context): + props = context.scene.BIMModelProperties + if self.hotkey == "S_O": + row = self.layout.row() + row.prop(props, "x") + row = self.layout.row() + row.prop(props, "y") + row = self.layout.row() + row.prop(props, "z") + def hotkey_S_A(self): bpy.ops.bim.add_constr_type_instance() @@ -366,10 +373,16 @@ class Hotkey(bpy.types.Operator): bpy.ops.bim.extend_profile(join_type="V") def hotkey_S_O(self): - bpy.ops.bim.add_element_opening(voided_building_element="", filling_building_element="") + if len(bpy.context.selected_objects) == 2: + bpy.ops.bim.add_opening() + else: + bpy.ops.bim.add_potential_opening(x=self.props.x, y=self.props.y, z=self.props.z) def hotkey_A_D(self): bpy.ops.bim.toggle_decomposition_parenting() def hotkey_A_O(self): - bpy.ops.bim.toggle_opening_visibility() + if AuthoringData.data["has_visible_openings"]: + bpy.ops.bim.hide_openings() + else: + bpy.ops.bim.show_openings() diff --git a/src/blenderbim/blenderbim/bim/module/void/__init__.py b/src/blenderbim/blenderbim/bim/module/void/__init__.py index e264da4404..93ccc6b148 100644 --- a/src/blenderbim/blenderbim/bim/module/void/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/void/__init__.py @@ -24,7 +24,6 @@ classes = ( operator.RemoveOpening, operator.AddFilling, operator.RemoveFilling, - operator.ToggleOpeningVisibility, operator.ToggleDecompositionParenting, prop.VoidProperties, ui.BIM_PT_voids, diff --git a/src/blenderbim/blenderbim/bim/module/void/operator.py b/src/blenderbim/blenderbim/bim/module/void/operator.py index f3098cf960..fb6d2fecf7 100644 --- a/src/blenderbim/blenderbim/bim/module/void/operator.py +++ b/src/blenderbim/blenderbim/bim/module/void/operator.py @@ -37,9 +37,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator): obj1, obj2 = context.selected_objects element1 = tool.Ifc.get_entity(obj1) element2 = tool.Ifc.get_entity(obj2) - if element1 and element2: - return {"FINISHED"} - if not element1 and not element2: + if type(element1) == type(element2): return {"FINISHED"} if element2 and not element1: obj1, obj2 = obj2, obj1 @@ -50,6 +48,13 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator): return {"FINISHED"} if not obj1.data or not hasattr(obj1.data, "BIMMeshProperties"): return {"FINISHED"} + + has_visible_openings = False + for opening in [r.RelatedOpeningElement for r in element1.HasOpenings]: + if tool.Ifc.get_object(opening): + has_visible_openings = True + break + body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body") element2 = blenderbim.core.root.assign_class( tool.Ifc, @@ -73,8 +78,11 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator): should_sync_changes_first=False, ) Data.load(tool.Ifc.get(), element1.id()) - tool.Ifc.unlink(obj=obj2) - bpy.data.objects.remove(obj2) + + if not has_visible_openings: + tool.Ifc.unlink(obj=obj2) + bpy.data.objects.remove(obj2) + context.view_layer.objects.active = obj1 return {"FINISHED"} @@ -177,18 +185,6 @@ class RemoveFilling(bpy.types.Operator): return {"FINISHED"} -class ToggleOpeningVisibility(bpy.types.Operator): - bl_idname = "bim.toggle_opening_visibility" - bl_label = "Toggle Opening Visibility" - bl_options = {"REGISTER", "UNDO"} - - def execute(self, context): - for project in [c for c in context.view_layer.layer_collection.children if "IfcProject" in c.name]: - for collection in [c for c in project.children if "IfcOpeningElements" in c.name]: - collection.hide_viewport = not collection.hide_viewport - return {"FINISHED"} - - class ToggleDecompositionParenting(bpy.types.Operator): bl_idname = "bim.toggle_decomposition_parenting" bl_label = "Toggle Decomposition Parenting" diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index 720659dd7c..f9e09c0389 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -51,7 +51,7 @@ class Model(blenderbim.core.tool.Model): @classmethod def load_openings(cls, element, openings): if not openings: - return + return [] obj = tool.Ifc.get_object(element) ifc_import_settings = import_ifc.IfcImportSettings.factory() ifc_importer = import_ifc.IfcImporter(ifc_import_settings) @@ -69,3 +69,14 @@ class Model(blenderbim.core.tool.Model): opening_obj.matrix_parent_inverse = obj.matrix_world.inverted() ifc_importer.place_objects_in_collections() return ifc_importer.added_data.values() + + @classmethod + def clear_scene_openings(cls): + props = bpy.context.scene.BIMModelProperties + has_deleted_opening = True + while has_deleted_opening: + has_deleted_opening = False + for i, opening in enumerate(props.openings): + if not opening.obj: + props.openings.remove(i) + has_deleted_opening = True