From 226736eb4113ddf82348e67c4413d30696620874 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 12 Mar 2025 18:41:18 +1100 Subject: [PATCH] Fix #3357. Remove Shift-E for parametric objects (superseded by tab) and you can now extend to anything not just slabs. This Shift-E still needs more polish as we figure out the best hotkeys and what's most natural to users when they select different combinations of objects, so expect the conditionals that govern when Shift-E does things to still change in the future. --- .../bonsai/bim/module/model/__init__.py | 3 +- src/bonsai/bonsai/bim/module/model/wall.py | 22 +++++++- .../bonsai/bim/module/model/workspace.py | 54 ++++--------------- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/__init__.py b/src/bonsai/bonsai/bim/module/model/__init__.py index 32573c6856..7eed142ee2 100644 --- a/src/bonsai/bonsai/bim/module/model/__init__.py +++ b/src/bonsai/bonsai/bim/module/model/__init__.py @@ -66,12 +66,13 @@ classes = ( product.SetActiveType, workspace.Hotkey, workspace.BIM_MT_add_representation_item, + wall.AddWallsFromSlab, wall.AlignWall, wall.ChangeExtrusionDepth, wall.ChangeExtrusionXAngle, wall.ChangeLayerLength, - wall.AddWallsFromSlab, wall.DrawPolylineWall, + wall.ExtendWallsToUnderside, wall.FlipWall, wall.MergeWall, wall.RecalculateWall, diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index 927aa571ec..eef3487217 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -36,7 +36,7 @@ import bonsai.core.geometry import bonsai.core.model as core import bonsai.tool as tool from bonsai.bim.ifc import IfcStore -from math import pi, sin, cos, degrees, radians +from math import pi, sin, cos, degrees from mathutils import Vector, Matrix from bonsai.bim.module.model.opening import FilledOpeningGenerator from bonsai.bim.module.model.decorator import PolylineDecorator, ProductDecorator @@ -58,6 +58,26 @@ class UnjoinWalls(bpy.types.Operator, tool.Ifc.Operator): core.unjoin_walls(tool.Ifc, tool.Blender, tool.Geometry, DumbWallJoiner(), tool.Model) +class ExtendWallsToUnderside(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.extend_walls_to_underside" + bl_label = "Extend Walls To Underside" + bl_description = "Extend and clip selected walls at the bottom faces of an object" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + slab = None + walls = [] + if (obj := tool.Blender.get_active_object(is_selected=True)) and (element := tool.Ifc.get_entity(obj)): + slab = obj + for obj in tool.Blender.get_selected_objects(include_active=False): + if (element := tool.Ifc.get_entity(obj)) and tool.Model.get_usage_type(element) == "LAYER2": + walls.append(obj) + if slab and walls: + core.extend_wall_to_slab(tool.Ifc, tool.Geometry, tool.Model, slab, walls) + else: + self.report({"ERROR"}, "Please select at least one LAYER2 element and an active element") + + class AlignWall(bpy.types.Operator): bl_idname = "bim.align_wall" bl_label = "Align Wall" diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index fc35945aea..21e1dce148 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -675,13 +675,12 @@ class CreateObjectUI: for _ in range(4): row2.operator("bim.launch_type_manager", text="", emboss=False) else: - op = box.operator( + box.operator( "bim.load_type_thumbnails", text="", icon="FILE_REFRESH", emboss=False, ) - op.ifc_class = ifc_class row = box.row(align=True) row.alignment = "CENTER" @@ -848,7 +847,7 @@ class EditObjectUI: elif AuthoringData.data["active_material_usage"] == "LAYER3": if "LAYER2" in AuthoringData.data["selected_material_usages"]: row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row - add_layout_hotkey_operator(cls.layout, "Extend Wall To Slab", "S_E", "", ui_context) + add_layout_hotkey_operator(cls.layout, "Extend To Underside", "S_E", "", ui_context) if AuthoringData.data["relating_type_data"].get("usage") == "LAYER2": row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row add_layout_hotkey_operator( @@ -907,6 +906,11 @@ class EditObjectUI: row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row add_layout_hotkey_operator(row, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__, ui_context) + else: + if "LAYER2" in AuthoringData.data["selected_material_usages"]: + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(cls.layout, "Extend To Undersideb", "S_E", "", ui_context) + if AuthoringData.data["is_flippable_element"]: cls.draw_flip(ui_context, row) @@ -1167,23 +1171,6 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): if not bpy.context.selected_objects or not (active_object := bpy.context.active_object): return - # NOTE: placing it before the other operations because railing can also be SweptSolid - # and it might conflict with one of the conditions below - if ( - tool.Model.is_parametric_railing_active() - and not tool.Model.get_railing_props(active_object).is_editing_path - ): - bpy.ops.bim.enable_editing_railing_path() - return - - elif tool.Model.is_parametric_roof_active() and not tool.Model.get_roof_props(active_object).is_editing_path: - # undo the unselection done above because roof has no usage type - bpy.ops.bim.enable_editing_roof_path() - return - - elif tool.Model.is_parametric_window_active() or tool.Model.is_parametric_door_active(): - return - selected_usages: dict[str, list[bpy.types.Object]] = {} for obj in bpy.context.selected_objects: element = tool.Ifc.get_entity(obj) @@ -1194,11 +1181,6 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): if not usage: representation = tool.Geometry.get_active_representation(obj) representation = tool.Geometry.resolve_mapped_representation(representation) - if representation and representation.RepresentationType == "SweptSolid": - usage = "SWEPTSOLID" - else: - obj.select_set(False) - continue selected_usages.setdefault(usage, []).append(obj) if len(bpy.context.selected_objects) == 1: @@ -1227,9 +1209,6 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): elif self.active_material_usage == "PROFILE": # Extend PROFILE to cursor bpy.ops.bim.extend_profile(join_type="T") - else: - # Edit SWEPTSOLID profile (assuming single profile for now) - bpy.ops.bim.enable_editing_extrusion_profile() elif self.active_material_usage == "LAYER2" and selected_usages.get("PROFILE", []): # Extend PROFILEs to LAYER2 @@ -1237,22 +1216,6 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): [o.select_set(False) for o in selected_usages.get("LAYER2", []) if o != bpy.context.active_object] bpy.ops.bim.extend_profile(join_type="T") - elif self.active_material_usage == "LAYER3" and selected_usages.get("LAYER2", []): - # Extend LAYER2s to LAYER3 - [o.select_set(False) for o in selected_usages.get("PROFILE", [])] - [o.select_set(False) for o in selected_usages.get("LAYER3", []) if o != bpy.context.active_object] - slab = None - walls = [] - if (obj := tool.Blender.get_active_object(is_selected=True)) and (element := tool.Ifc.get_entity(obj)) and tool.Model.get_usage_type(element) == "LAYER3": - slab = obj - for obj in tool.Blender.get_selected_objects(include_active=False): - if (element := tool.Ifc.get_entity(obj)) and tool.Model.get_usage_type(element) == "LAYER2": - walls.append(obj) - if slab and walls: - core.extend_wall_to_slab(tool.Ifc, tool.Geometry, tool.Model, slab, walls) - else: - self.report({"ERROR"}, "Please select at least one LAYER2 element and an active LAYER3 element") - elif self.active_material_usage == "LAYER2": # Extend LAYER2s to LAYER2 [o.select_set(False) for o in selected_usages.get("LAYER3", [])] @@ -1268,6 +1231,9 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): [o.select_set(False) for o in selected_usages.get("LAYER2", [])] bpy.ops.bim.extend_profile(join_type="T") + else: + bpy.ops.bim.extend_walls_to_underside() + def hotkey_S_F(self): if not bpy.context.selected_objects: return