From 47f013fc18392b8739465d9f311dae2ecf7bfd9d Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 10 Oct 2024 18:34:20 +1100 Subject: [PATCH] Add support for adding curvelike items --- .../bonsai/bim/module/geometry/__init__.py | 1 + .../bonsai/bim/module/geometry/operator.py | 58 ++++++++++++++++++- .../bonsai/bim/module/model/workspace.py | 4 ++ src/bonsai/bonsai/tool/geometry.py | 6 +- src/bonsai/bonsai/tool/model.py | 9 ++- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/geometry/__init__.py b/src/bonsai/bonsai/bim/module/geometry/__init__.py index 955ec2b8ee..d7aecb662a 100644 --- a/src/bonsai/bonsai/bim/module/geometry/__init__.py +++ b/src/bonsai/bonsai/bim/module/geometry/__init__.py @@ -20,6 +20,7 @@ import bpy from . import ui, prop, operator classes = ( + operator.AddCurvelikeItem, operator.AddMeshlikeItem, operator.AddRepresentation, operator.AddSweptAreaSolidItem, diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index d622ac3748..1b58579a63 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -2637,7 +2637,7 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator): if self.shape == "CUBE": curve = builder.rectangle(size=Vector((0.5, 0.5)) / unit_scale) elif self.shape == "CYLINDER": - curve = builder.circle(radius=0.25) + curve = builder.circle(radius=0.25 / unit_scale) item = builder.extrude( curve, magnitude=0.5 / unit_scale, @@ -2656,3 +2656,59 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator): obj.data.BIMMeshProperties.ifc_definition_id = item.id() tool.Geometry.import_item(obj) tool.Geometry.import_item_attributes(obj) + + +class AddCurvelikeItem(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.add_curvelike_item" + bl_label = "Add Curvelike Item" + bl_options = {"REGISTER", "UNDO"} + shape: bpy.props.StringProperty(name="Shape") + + def _execute(self, context): + props = context.scene.BIMGeometryProperties + + representation = tool.Geometry.get_active_representation(props.representation_obj) + is_2d = representation.ContextOfItems.ContextType == "Plan" + representation = ifcopenshell.util.representation.resolve_representation(representation) + + mesh = bpy.data.meshes.new("Tmp") + obj = bpy.data.objects.new("Tmp", mesh) + scene = bpy.context.scene + scene.collection.objects.link(obj) + bpy.context.view_layer.objects.active = obj + obj.select_set(True) + new = props.item_objs.add() + new.obj = obj + tool.Geometry.lock_object(obj) + + matrix = props.representation_obj.matrix_world.copy() + matrix.translation = context.scene.cursor.location + local_matrix = props.representation_obj.matrix_world.inverted() @ matrix + + obj.show_in_front = True + obj.matrix_world = matrix + tool.Geometry.record_object_position(obj) + + builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + + offset = local_matrix.translation.to_2d() / unit_scale + if not is_2d: + offset = offset.to_3d() + + if self.shape == "LINE": + if is_2d: + points = [Vector((0, 0)), Vector((0.5 / unit_scale, 0))] + else: + points = [Vector((0, 0, 0)), Vector((0.5 / unit_scale, 0, 0))] + item = builder.polyline(points=points, position_offset=offset) + elif self.shape == "CIRCLE": + item = builder.circle(radius=0.25 / unit_scale, center=offset) + + representation.Items = list(representation.Items) + [item] + tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj) + + obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}" + obj.data.BIMMeshProperties.ifc_definition_id = item.id() + tool.Geometry.import_item(obj) + tool.Geometry.import_item_attributes(obj) diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index de7e845834..730e3e545a 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -308,6 +308,10 @@ class BIM_MT_add_representation_item(Menu): "bim.add_swept_area_solid_item", icon="MESH_CYLINDER", text="Extruded Area Solid Cylinder" ).shape = "CYLINDER" + if ItemData.data["representation_type"] in ("Annotation2D"): + self.layout.operator("bim.add_curvelike_item", icon="IPO_CONSTANT", text="Polycurve").shape = "LINE" + self.layout.operator("bim.add_curvelike_item", icon="MESH_CIRCLE", text="Circle").shape = "CIRCLE" + class CreateObjectUI: @classmethod diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 5b9e078a89..7bc30b99d6 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -871,7 +871,7 @@ class Geometry(bonsai.core.tool.Geometry): @classmethod def is_movable(cls, item: ifcopenshell.entity_instance) -> bool: - return item.is_a("IfcSweptAreaSolid") or item.is_a("IfcConic") + return item.is_a("IfcSweptAreaSolid") @classmethod def is_profile_based(cls, data: bpy.types.Mesh) -> bool: @@ -1471,7 +1471,7 @@ class Geometry(bonsai.core.tool.Geometry): if not (obj := item_obj.obj): continue item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) - if (is_swept_area := item.is_a("IfcSweptAreaSolid")) or item.is_a("IfcConic"): + if (is_swept_area := item.is_a("IfcSweptAreaSolid")): if not tool.Ifc.is_moved(obj): continue has_changed = True @@ -1529,7 +1529,7 @@ class Geometry(bonsai.core.tool.Geometry): obj.matrix_world = rep_obj.matrix_world.copy() - if (is_swept_area := item.is_a("IfcSweptAreaSolid")) or item.is_a("IfcConic"): + if (is_swept_area := item.is_a("IfcSweptAreaSolid")): position = item.Position # Positional is optional only for SweptAreaSolid. if position or not is_swept_area: diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index f8f267030c..59c77e42bd 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -455,14 +455,13 @@ class Model(bonsai.core.tool.Model): if is_closed: cls.edges.append([len(cls.vertices) - 1, offset]) # Close the loop elif curve.is_a("IfcCircle"): - center = cls.convert_unit_to_si( - Matrix(ifcopenshell.util.placement.get_axis2placement(curve.Position).tolist()).translation - ) + circle_position = Matrix(ifcopenshell.util.placement.get_axis2placement(curve.Position).tolist()) + circle_position.translation *= cls.unit_scale radius = cls.convert_unit_to_si(curve.Radius) cls.vertices.extend( [ - position @ Vector((center[0], center[1] - radius, 0.0)), - position @ Vector((center[0], center[1] + radius, 0.0)), + position @ circle_position @ Vector((0, 0 - radius, 0.0)), + position @ circle_position @ Vector((0, 0 + radius, 0.0)), ] ) cls.circles.append([offset, offset + 1])