From b9c6508aa727684a149bc5fed89bf901e50e11da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 15 Oct 2024 21:54:19 -0300 Subject: [PATCH] Polyline wall ability to switch axis while drawing. While drawing the polyline the user can press: - F to flip the wall. Changes the direction sense. - O to change the offset type between Exterior, Center and Interior. This will change the offset from reference line. Those attributes can be verified in the Object Materials panels of each wall after they are created. --- .../bonsai/bim/module/model/polyline.py | 4 +- src/bonsai/bonsai/bim/module/model/prop.py | 12 ++++ src/bonsai/bonsai/bim/module/model/wall.py | 44 ++++++++++++- src/bonsai/bonsai/tool/polyline.py | 64 +++++++++++++------ 4 files changed, 101 insertions(+), 23 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/polyline.py b/src/bonsai/bonsai/bim/module/model/polyline.py index 1afc147564..3f07752814 100644 --- a/src/bonsai/bonsai/bim/module/model/polyline.py +++ b/src/bonsai/bonsai/bim/module/model/polyline.py @@ -165,13 +165,13 @@ class PolylineOperator: self.tool_state.axis_method = None tool.Blender.update_viewport() - def handle_instructions(self, context): + def handle_instructions(self, context, custom_instructions): self.snap_info = f"""| Axis: {self.tool_state.axis_method} Plane: {self.tool_state.plane_method} Snap: {self.snapping_points[0][1]} """ - context.workspace.status_text_set(self.instructions + self.snap_info) + context.workspace.status_text_set(self.instructions + custom_instructions + self.snap_info) def handle_lock_axis(self, context, event): if event.value == "RELEASE" and event.type == "L": diff --git a/src/bonsai/bonsai/bim/module/model/prop.py b/src/bonsai/bonsai/bim/module/model/prop.py index 0d6b6220df..f44e6a01a8 100644 --- a/src/bonsai/bonsai/bim/module/model/prop.py +++ b/src/bonsai/bonsai/bim/module/model/prop.py @@ -149,6 +149,18 @@ class BIMModelProperties(PropertyGroup): type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page) type_name: bpy.props.StringProperty(name="Name", default="TYPEX") boundary_class: bpy.props.EnumProperty(items=get_boundary_class, name="Boundary Class") + direction_sense: bpy.props.EnumProperty( + items=[("POSITIVE", "Positive", ""), ("NEGATIVE", "Negative", "")], + name="Material Usage Direction Sense", + default="POSITIVE" + ) + offset_type: bpy.props.EnumProperty( + items=[("EXTERIOR", "Exterior", ""), ("CENTER", "Center", ""), ("INTERIOR", "Interior", "")], + name="Layer Offset Type", + default="EXTERIOR", + description="It's a convention that affects the offset to reference line" + ) + offset: bpy.props.FloatProperty(name="Offset", default=0.0, description="Material usage offset from reference line") class BIMArrayProperties(PropertyGroup): diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index aefb3af676..bfb97575c8 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -297,7 +297,8 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator): def __init__(self): super().__init__() self.relating_type = None - relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id + 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)) @@ -305,7 +306,25 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator): if not self.relating_type: return {"FINISHED"} + model_props = context.scene.BIMModelProperties + direction_sense = model_props.direction_sense + offset = model_props.offset + walls, is_polyline_closed = DumbWallGenerator(self.relating_type).generate(True) + for wall in walls: + model = IfcStore.get_file() + element = tool.Ifc.get_entity(wall["obj"]) + material = ifcopenshell.util.element.get_material(element) + material_set_usage = model.by_id(material.id()) + # if material.is_a("IfcMaterialLayerSetUsage"): + attributes = {"OffsetFromReferenceLine": offset, "DirectionSense": direction_sense} + ifcopenshell.api.run( + "material.edit_layer_usage", + model, + **{"usage": material_set_usage, "attributes": attributes}, + ) + DumbWallRecalculator().recalculate([wall["obj"]]) + if walls: if is_polyline_closed: for wall1, wall2 in zip(walls, walls[1:] + [walls[0]]): @@ -330,7 +349,28 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator): self.handle_mouse_move(context, event) return {"PASS_THROUGH"} - self.handle_instructions(context) + # Wall axis 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" + ) + tool.Polyline.create_wall_preview_vertices(context, self.relating_type) + + 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)] + tool.Polyline.create_wall_preview_vertices(context, self.relating_type) + + props = bpy.context.scene.BIMModelProperties + wall_config = f"""Direction: {props.direction_sense} + Offset Type: {props.offset_type} + Offset Value: {props.offset} + """ + self.handle_instructions(context, wall_config) self.handle_mouse_move(context, event, should_round=True) diff --git a/src/bonsai/bonsai/tool/polyline.py b/src/bonsai/bonsai/tool/polyline.py index 4a8aab457d..10f8cfc882 100644 --- a/src/bonsai/bonsai/tool/polyline.py +++ b/src/bonsai/bonsai/tool/polyline.py @@ -309,7 +309,7 @@ class Polyline(bonsai.core.tool.Polyline): def create_bmesh_from_vertices(vertices): bm = bmesh.new() - new_verts = [bm.verts.new(v) for v in base_vertices] + new_verts = [bm.verts.new(v) for v in polyline_vertices] if is_closed: new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)] new_edges.append( @@ -327,36 +327,61 @@ class Polyline(bonsai.core.tool.Polyline): if not layers["thickness"]: return thickness = layers["thickness"] + model_props = context.scene.BIMModelProperties + direction_sense = model_props.direction_sense + direction = 1 + if direction_sense == "NEGATIVE": + direction = -1 + offset_type = model_props.offset_type + offset = 0 + if offset_type == "CENTER": + offset = -thickness/2 + elif offset_type == "INTERIOR": + offset = -thickness - height = float(context.scene.BIMModelProperties.extrusion_depth) - rl = float(context.scene.BIMModelProperties.rl1) - x_angle = float(context.scene.BIMModelProperties.x_angle) + unit_system = tool.Drawing.get_unit_system() + factor = 1 + if unit_system == "IMPERIAL": + factor = 3.048 + if unit_system == "METRIC": + unit_length = bpy.context.scene.unit_settings.length_unit + if unit_length == "MILLIMETERS": + factor = 1000 + + # For the model properties, the offset value should just be converted + # However, for the wall preview logic that follows, offset and thickness must change direction + model_props.offset = offset * factor + thickness *= direction + offset *= direction + + height = float(model_props.extrusion_depth) + rl = float(model_props.rl1) + x_angle = float(model_props.x_angle) angle_distortion = height * tan(x_angle) - base_vertices = [] - top_vertices = [] + polyline_vertices = [] polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_points = polyline_data[0].polyline_points if polyline_data else [] if len(polyline_points) < 2: context.scene.BIMPolylineProperties.product_preview.clear() return for point in polyline_points: - base_vertices.append(Vector((point.x, point.y, point.z))) + polyline_vertices.append(Vector((point.x, point.y, point.z))) is_closed = False if ( - base_vertices[0].x == base_vertices[-1].x - and base_vertices[0].y == base_vertices[-1].y - and base_vertices[0].z == base_vertices[-1].z + polyline_vertices[0].x == polyline_vertices[-1].x + and polyline_vertices[0].y == polyline_vertices[-1].y + and polyline_vertices[0].z == polyline_vertices[-1].z ): is_closed = True - base_vertices.pop(-1) # Remove the last point. The edges are going to inform that the shape is closed. + polyline_vertices.pop(-1) # Remove the last point. The edges are going to inform that the shape is closed. - bm_base = create_bmesh_from_vertices(base_vertices) - - offset_base_verts = tool.Cad.offset_edges(bm_base, thickness) - top_vertices = tool.Cad.offset_edges(bm_base, angle_distortion) - offset_top_verts = tool.Cad.offset_edges(bm_base, angle_distortion + thickness) + bm_base = create_bmesh_from_vertices(polyline_vertices) + base_vertices = tool.Cad.offset_edges(bm_base, offset) + offset_base_verts = tool.Cad.offset_edges(bm_base, thickness + offset) + top_vertices = tool.Cad.offset_edges(bm_base, angle_distortion + offset) + offset_top_verts = tool.Cad.offset_edges(bm_base, angle_distortion + thickness + offset) if is_closed: base_vertices.append(base_vertices[0]) @@ -367,10 +392,11 @@ class Polyline(bonsai.core.tool.Polyline): if offset_base_verts is not None: context.scene.BIMPolylineProperties.product_preview.clear() for v in base_vertices: + new_v = Vector((v.co.x, v.co.y, v.co.z)) prop = context.scene.BIMPolylineProperties.product_preview.add() - prop.x = v.x - prop.y = v.y - prop.z = v.z + rl + prop.x = new_v.x + prop.y = new_v.y + prop.z = new_v.z + rl for v in offset_base_verts[::-1]: new_v = Vector((v.co.x, v.co.y, v.co.z))