From 0c0225543b001a0589fb017e8f6fd539e7288195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 22 Oct 2024 17:39:19 -0300 Subject: [PATCH] Add polyline tool for slabs. No preview yet. --- .../bonsai/bim/module/model/__init__.py | 1 + src/bonsai/bonsai/bim/module/model/slab.py | 127 +++++++++++++++++- .../bonsai/bim/module/model/workspace.py | 4 + .../api/geometry/add_slab_representation.py | 5 + 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/__init__.py b/src/bonsai/bonsai/bim/module/model/__init__.py index 10c0f06d48..3766a14e4b 100644 --- a/src/bonsai/bonsai/bim/module/model/__init__.py +++ b/src/bonsai/bonsai/bim/module/model/__init__.py @@ -100,6 +100,7 @@ classes = ( roof.GenerateHippedRoof, slab.DisableEditingExtrusionProfile, slab.DisableEditingSketchExtrusionProfile, + slab.DrawPolylineSlab, slab.EditExtrusionProfile, slab.EditSketchExtrusionProfile, slab.EnableEditingExtrusionProfile, diff --git a/src/bonsai/bonsai/bim/module/model/slab.py b/src/bonsai/bonsai/bim/module/model/slab.py index 7a6ac6e96a..30512789eb 100644 --- a/src/bonsai/bonsai/bim/module/model/slab.py +++ b/src/bonsai/bonsai/bim/module/model/slab.py @@ -32,7 +32,8 @@ import bonsai.core.root import bonsai.tool as tool from mathutils import Vector, Matrix from bonsai.bim.module.geometry.helper import Helper -from bonsai.bim.module.model.decorator import ProfileDecorator +from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator +from bonsai.bim.module.model.polyline import PolylineOperator from typing import Optional @@ -40,7 +41,7 @@ class DumbSlabGenerator: def __init__(self, relating_type: ifcopenshell.entity_instance): self.relating_type = relating_type - def generate(self): + def generate(self, draw_from_polyline=False): self.file = tool.Ifc.get() unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) thicknesses = [] @@ -60,6 +61,7 @@ class DumbSlabGenerator: props = bpy.context.scene.BIMModelProperties + self.polyline = None self.container = None self.container_obj = None if container := tool.Root.get_default_container(): @@ -72,7 +74,26 @@ class DumbSlabGenerator: self.rotation = 0 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 - return self.derive_from_cursor() + + if draw_from_polyline: + return self.derive_from_polyline() + else: + return self.derive_from_cursor() + + def derive_from_polyline(self): + polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline + polyline_points = polyline_data[0].polyline_points if polyline_data else [] + self.location = Vector((polyline_points[0].x, polyline_points[0].y, self.container_obj.location.z)) + self.polyline = [Vector((p.x, p.y, 0.0)) - self.location for p in polyline_points] + + if len(self.polyline) <= 2: + return + + # Always assume a closed polyline + if self.polyline[0] != self.polyline[-1]: + self.polyline.append(self.polyline[0]) + + return self.create_slab() def derive_from_cursor(self): self.location = bpy.context.scene.cursor.location @@ -112,6 +133,7 @@ class DumbSlabGenerator: context=self.body_context, depth=self.depth, x_angle=self.x_angle, + polyline=self.polyline, ) ifcopenshell.api.run( "geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation @@ -700,3 +722,102 @@ class SetArcIndex(bpy.types.Operator): group.add(selected_vertices, 1, "REPLACE") bpy.ops.object.mode_set(mode="EDIT") return {"FINISHED"} + + +class DrawPolylineSlab(bpy.types.Operator, PolylineOperator): + bl_idname = "bim.draw_polyline_slab" + bl_label = "Draw Polyline Slab" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return context.space_data.type == "VIEW_3D" + + def __init__(self): + super().__init__() + 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 create_slab_from_polyline(self, context): + if not self.relating_type: + return {"FINISHED"} + + DumbSlabGenerator(self.relating_type).generate(True) + + def modal(self, context, event): + if not self.relating_type: + self.report({"WARNING"}, "You need to select a slab type.") + PolylineDecorator.uninstall() + tool.Blender.update_viewport() + return {"FINISHED"} + + PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0]) + tool.Blender.update_viewport() + + self.handle_lock_axis(context, event) + + if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}: + self.handle_mouse_move(context, event) + return {"PASS_THROUGH"} + + # TODO Slab settings + # if event.value == "RELEASE" and event.type == "F": + # direction_sense = context.scene.BIMModelProperties.direction_sense + # context.scene.BIMModelProperties.direction_sense = ( + # "NEGATIVE" if direction_sense == "POSITIVE" else "POSITIVE" + # ) + + # if event.value == "RELEASE" and event.type == "O": + # offset_type = context.scene.BIMModelProperties.offset_type + # items = ["EXTERIOR", "CENTER", "INTERIOR"] + # index = items.index(offset_type) + # size = len(items) + # context.scene.BIMModelProperties.offset_type = items[((index + 1) % size)] + + # props = bpy.context.scene.BIMModelProperties + # slab_config = f"""Direction: {props.direction_sense} + # Offset Type: {props.offset_type} + # Offset Value: {props.offset} + # """ + + self.handle_instructions(context) + + self.handle_mouse_move(context, event, should_round=True) + + self.choose_axis(event) + + self.handle_snap_selection(context, event) + + if ( + not self.tool_state.is_input_on + and event.value == "RELEASE" + and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"} + ): + self.create_slab_from_polyline(context) + context.workspace.status_text_set(text=None) + ProductDecorator.uninstall() + PolylineDecorator.uninstall() + tool.Polyline.clear_polyline() + tool.Blender.update_viewport() + return {"FINISHED"} + + self.handle_keyboard_input(context, event) + + self.handle_inserting_polyline(context, event) + + cancel = self.handle_cancelation(context, event) + if cancel is not None: + ProductDecorator.uninstall() + return cancel + + return {"RUNNING_MODAL"} + + def invoke(self, context, event): + super().invoke(context, event) + ProductDecorator.install(context) + self.tool_state.use_default_container = True + self.tool_state.plane_method = "XY" + return {"RUNNING_MODAL"} diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index 9c9c43676b..deaf4244ef 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -935,6 +935,10 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): relating_type_id := tool.Blender.get_enum_safe(props, "relating_type_id") ) and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id))) == "LAYER2": bpy.ops.bim.draw_polyline_wall("INVOKE_DEFAULT") + elif ( + relating_type_id := tool.Blender.get_enum_safe(props, "relating_type_id") + ) and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id))) == "LAYER3": + bpy.ops.bim.draw_polyline_slab("INVOKE_DEFAULT") else: bpy.ops.bim.add_occurrence("INVOKE_DEFAULT") diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py index 5a46ba0f49..c30375dd6a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py @@ -19,6 +19,7 @@ import ifcopenshell.util.unit from ifcopenshell.util.data import Clipping from math import sin, cos +from mathutils import Vector from typing import Any, Optional, Union @@ -34,6 +35,7 @@ def add_slab_representation( # Planes are defined either by Clipping objects # or by dictionaries of arguments for `Clipping.parse` clippings: Optional[list[Union[Clipping, dict[str, Any]]]] = None, + polyline: Optional[list[Vector]] = None, ) -> ifcopenshell.entity_instance: usecase = Usecase() usecase.file = file @@ -42,6 +44,7 @@ def add_slab_representation( "depth": depth, "x_angle": x_angle, "clippings": clippings if clippings is not None else [], + "polyline": polyline, } return usecase.execute() @@ -59,6 +62,8 @@ class Usecase: def create_item(self): size = self.convert_si_to_unit(1) points = ((0.0, 0.0), (size, 0.0), (size, size), (0.0, size), (0.0, 0.0)) + if self.settings["polyline"]: + points = [(self.convert_si_to_unit(p[0]), self.convert_si_to_unit(p[1])) for p in self.settings["polyline"]] if self.file.schema == "IFC2X3": curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) else: