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