From b9423b191adc29fcd29c618bc35a4db726f1cf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Fri, 27 Dec 2024 11:27:30 -0300 Subject: [PATCH] Add `AddWallsFromSlab` operator. This operator allows users to create walls from a slab. When the wall tool is active, pressing `Shift + A` will generate walls based on the exterior polygon of the selected slab. --- .../bonsai/bim/module/model/__init__.py | 1 + src/bonsai/bonsai/bim/module/model/wall.py | 63 +++++++++++++++++-- .../bonsai/bim/module/model/workspace.py | 11 ++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/__init__.py b/src/bonsai/bonsai/bim/module/model/__init__.py index 0756930afa..f30e194204 100644 --- a/src/bonsai/bonsai/bim/module/model/__init__.py +++ b/src/bonsai/bonsai/bim/module/model/__init__.py @@ -69,6 +69,7 @@ classes = ( wall.ChangeExtrusionDepth, wall.ChangeExtrusionXAngle, wall.ChangeLayerLength, + wall.AddWallsFromSlab, wall.DrawPolylineWall, wall.FlipWall, wall.MergeWall, diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index 9aa4f516d6..4390193519 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -280,6 +280,39 @@ class ChangeLayerLength(bpy.types.Operator, tool.Ifc.Operator): return {"FINISHED"} +class AddWallsFromSlab(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.draw_walls_from_slab" + bl_label = "Draw Slab From Wall" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return context.space_data.type == "VIEW_3D" + + def __init__(self): + self.relating_type = None + props = bpy.context.scene.BIMModelProperties + relating_type_id = props.relating_type_id + if relating_type_id: + self.relating_type = tool.Ifc.get().by_id(int(relating_type_id)) + + def _execute(self, context): + if not self.relating_type: + return {"FINISHED"} + slab = tool.Ifc.get_entity(context.active_object) + if not slab.is_a("IfcSlab"): + self.report( + {"WARNING"}, + "Please select a slab.", + ) + return {"FINISHED"} + walls = DumbWallGenerator(self.relating_type).generate("SLAB") + + if walls: + for wall1, wall2 in zip(walls, walls[1:] + [walls[0]]): + DumbWallJoiner().join_V(wall2["obj"], wall1["obj"]) + + class DrawPolylineWall(bpy.types.Operator, PolylineOperator): bl_idname = "bim.draw_polyline_wall" bl_label = "Draw Polyline Wall" @@ -305,7 +338,7 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator): direction_sense = model_props.direction_sense offset = model_props.offset - walls, is_polyline_closed = DumbWallGenerator(self.relating_type).generate(True) + walls, is_polyline_closed = DumbWallGenerator(self.relating_type).generate("POLYLINE") for wall in walls: model = IfcStore.get_file() element = tool.Ifc.get_entity(wall["obj"]) @@ -515,7 +548,7 @@ class DumbWallGenerator: self.relating_type = relating_type self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - def generate(self, draw_from_polyline=False): + def generate(self, insertion_type="CURSOR"): self.file = IfcStore.get_file() self.layers = tool.Model.get_material_layer_parameters(self.relating_type) if not self.layers["thickness"]: @@ -539,9 +572,11 @@ class DumbWallGenerator: self.location = Vector((0, 0, 0)) self.x_angle = 0 if tool.Cad.is_x(props.x_angle, 0, tolerance=0.001) else props.x_angle - if draw_from_polyline: + if insertion_type == "POLYLINE": return self.derive_from_polyline() - else: + elif insertion_type == "SLAB": + return self.derive_from_slab() + elif insertion_type == "CURSOR": return self.derive_from_cursor() def has_sketch(self): @@ -570,6 +605,26 @@ class DumbWallGenerator: walls.append(self.create_wall_from_2_points(coords)) return walls, is_polyline_closed + def derive_from_slab(self): + slab_obj = bpy.context.active_object + slab = tool.Ifc.get_entity(slab_obj) + container = ifcopenshell.util.element.get_container(slab) + self.container_obj = tool.Ifc.get_object(container) + elevation = self.container_obj.location.z + representation = ifcopenshell.util.representation.get_representation(slab, "Model", "Body", "MODEL_VIEW") + extrusion = tool.Model.get_extrusion(representation) + polyline_points = extrusion.SweptArea.OuterCurve.Points.CoordList + polyline_points = [[(v * self.unit_scale) for v in p] for p in polyline_points] + polyline_points = [slab_obj.matrix_world @ Vector((p[0], p[1], elevation)) for p in polyline_points] + is_polyline_closed = True + walls = [] + for i in range(len(polyline_points) - 1): + vec1 = polyline_points[i] + vec2 = polyline_points[i+1] + coords = (vec1, vec2) + walls.append(self.create_wall_from_2_points(coords)) + return walls + def derive_from_sketch(self): objs = [] strokes = [] diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index ec787be62d..5327417c31 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -963,6 +963,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): if bpy.context.scene.BIMGeometryProperties.mode == "ITEM": bpy.ops.wm.call_menu(name="BIM_MT_add_representation_item") else: + # Slab from walls walls = False for obj in bpy.context.selected_objects: walls = tool.Ifc.get_entity(obj).is_a("IfcWall") @@ -973,6 +974,16 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): ): bpy.ops.bim.draw_slab_from_wall("INVOKE_DEFAULT") return {"FINISHED"} + # Walls from slab + slab = tool.Ifc.get_entity(bpy.context.active_object) + if ( + slab + and slab.is_a("IfcSlab") + and relating_type_id + and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id))) == "LAYER2" + ): + bpy.ops.bim.draw_walls_from_slab("INVOKE_DEFAULT") + return {"FINISHED"} for obj in tool.Blender.get_selected_objects(): obj.select_set(False)