From 9ea44f0a891c91df4b0269e0f5c132380771b266 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 21 Sep 2024 21:27:17 +1000 Subject: [PATCH] You can now add new swept area solid items --- .../bonsai/bim/module/geometry/__init__.py | 1 + .../bonsai/bim/module/geometry/helper.py | 2 + .../bonsai/bim/module/geometry/operator.py | 67 ++++++++++++++----- .../bonsai/bim/module/model/__init__.py | 2 +- .../bonsai/bim/module/model/workspace.py | 34 +++++++--- 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/geometry/__init__.py b/src/bonsai/bonsai/bim/module/geometry/__init__.py index 76a56373e2..a79476d967 100644 --- a/src/bonsai/bonsai/bim/module/geometry/__init__.py +++ b/src/bonsai/bonsai/bim/module/geometry/__init__.py @@ -22,6 +22,7 @@ from . import ui, prop, operator classes = ( operator.AddMeshlikeItem, operator.AddRepresentation, + operator.AddSweptAreaSolidItem, operator.CopyRepresentation, operator.DisableEditingRepresentationItemShapeAspect, operator.DisableEditingRepresentationItemStyle, diff --git a/src/bonsai/bonsai/bim/module/geometry/helper.py b/src/bonsai/bonsai/bim/module/geometry/helper.py index b74ed11e9d..6a0d2e189b 100644 --- a/src/bonsai/bonsai/bim/module/geometry/helper.py +++ b/src/bonsai/bonsai/bim/module/geometry/helper.py @@ -20,6 +20,7 @@ import bpy import bmesh import shapely import mathutils +import numpy as np import ifcopenshell import ifcopenshell.util.unit import ifcopenshell.util.shape @@ -294,6 +295,7 @@ class Helper: for curve in curves: geometry = ifcopenshell.geom.create_shape(settings, curve) v = ifcopenshell.util.shape.get_vertices(geometry, is_2d=True) + v = np.round(v, 4) # Round to nearest 0.1mm, otherwise things like circles don't polygonise reliably edges = ifcopenshell.util.shape.get_edges(geometry) boundary_lines = [shapely.LineString([v[e[0]], v[e[1]]]) for e in edges] unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines)) diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 666663eed2..d204754d8a 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -1774,7 +1774,6 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator): return {"FINISHED"} objs = context.selected_objects or [context.active_object] - print("objs are", objs) self.edited_objs = [] self.unchanged_objs_with_openings = [] @@ -1874,7 +1873,6 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator): ProfileDecorator.uninstall() profile = tool.Model.export_profile(obj) - print("got profile", profile) if not profile: @@ -1892,19 +1890,8 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator): ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_profile) tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj) - - tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get()) - tool.Loader.settings.context_settings = tool.Loader.create_settings() - tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True) - geometry = tool.Loader.create_generic_shape(item) - obj.data.clear_geometry() - tool.Loader.convert_geometry_to_mesh(geometry, obj.data) - if item.Position: - position = Matrix(ifcopenshell.util.placement.get_axis2placement(item.Position).tolist()) - position.translation *= unit_scale - position_i = position.inverted() - for vert in obj.data.vertices: - vert.co = position_i @ vert.co + tool.Geometry.import_item(obj) + tool.Geometry.import_item_attributes(obj) element = tool.Ifc.get_entity(bpy.context.scene.BIMGeometryProperties.representation_obj) # Only certain classes should have a footprint @@ -2471,3 +2458,53 @@ class AddMeshlikeItem(bpy.types.Operator, tool.Ifc.Operator): 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() + + +class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.add_swept_area_solid_item" + bl_label = "Add Swept Area Solid Item" + bl_options = {"REGISTER", "UNDO"} + shape: bpy.props.StringProperty(name="Shape") + + def _execute(self, context): + props = context.scene.BIMGeometryProperties + 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 + + matrix = props.representation_obj.matrix_world.copy() + matrix.translation = context.scene.cursor.location + local_matrix = props.representation_obj.matrix_world.inverted() @ matrix + + 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()) + 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) + item = builder.extrude( + curve, + magnitude=0.5 / unit_scale, + position=local_matrix.translation, + position_x_axis=local_matrix.col[0].to_3d(), + position_z_axis=local_matrix.col[2].to_3d(), + ) + + representation = tool.Geometry.get_active_representation(props.representation_obj) + representation = ifcopenshell.util.representation.resolve_representation(representation) + + 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/__init__.py b/src/bonsai/bonsai/bim/module/model/__init__.py index a4b55fbe53..df181799bd 100644 --- a/src/bonsai/bonsai/bim/module/model/__init__.py +++ b/src/bonsai/bonsai/bim/module/model/__init__.py @@ -64,7 +64,7 @@ classes = ( product.LoadTypeThumbnails, product.MirrorElements, workspace.Hotkey, - workspace.BIM_MT_add_meshlike_item, + workspace.BIM_MT_add_representation_item, wall.AlignWall, wall.ChangeExtrusionDepth, wall.ChangeExtrusionXAngle, diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index 4f80b0fa46..eb54f99dfa 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -272,7 +272,7 @@ class EditItemUI: row.label(text="Context: " + ItemData.data["representation_identifier"], icon="SCENE_DATA") row = cls.layout.row() row.label(text="Type: " + ItemData.data["representation_type"], icon="OUTLINER_OB_MESH") - cls.layout.menu("BIM_MT_add_meshlike_item", icon="ADD") + cls.layout.menu("BIM_MT_add_representation_item", icon="ADD") if not (obj := context.active_object) or not tool.Geometry.is_representation_item(obj): return for item_attribute in obj.data.BIMMeshProperties.item_attributes: @@ -283,17 +283,33 @@ class EditItemUI: row.operator("bim.update_item_attributes", icon="FILE_REFRESH", text="") -class BIM_MT_add_meshlike_item(Menu): - bl_idname = "BIM_MT_add_meshlike_item" +class BIM_MT_add_representation_item(Menu): + bl_idname = "BIM_MT_add_representation_item" bl_label = "Add Item" def draw(self, context): - self.layout.operator("bim.add_meshlike_item", icon="MESH_PLANE", text="Mesh Plane").shape = "PLANE" - self.layout.operator("bim.add_meshlike_item", icon="MESH_CUBE", text="Mesh Cube").shape = "CUBE" - self.layout.operator("bim.add_meshlike_item", icon="MESH_CIRCLE", text="Mesh Circle").shape = "CIRCLE" - self.layout.operator("bim.add_meshlike_item", icon="MESH_UVSPHERE", text="Mesh UV Sphere").shape = "UVSPHERE" - self.layout.operator("bim.add_meshlike_item", icon="MESH_ICOSPHERE", text="Mesh Icosphere").shape = "ICOSPHERE" - self.layout.operator("bim.add_meshlike_item", icon="MESH_CYLINDER", text="Mesh Cylinder").shape = "CYLINDER" + if not ItemData.is_loaded: + ItemData.load() + + if ItemData.data["representation_type"] in ("Tessellation", "Brep", "AdvancedBrep"): + self.layout.operator("bim.add_meshlike_item", icon="MESH_PLANE", text="Mesh Plane").shape = "PLANE" + self.layout.operator("bim.add_meshlike_item", icon="MESH_CUBE", text="Mesh Cube").shape = "CUBE" + self.layout.operator("bim.add_meshlike_item", icon="MESH_CIRCLE", text="Mesh Circle").shape = "CIRCLE" + self.layout.operator("bim.add_meshlike_item", icon="MESH_UVSPHERE", text="Mesh UV Sphere").shape = ( + "UVSPHERE" + ) + self.layout.operator("bim.add_meshlike_item", icon="MESH_ICOSPHERE", text="Mesh Icosphere").shape = ( + "ICOSPHERE" + ) + self.layout.operator("bim.add_meshlike_item", icon="MESH_CYLINDER", text="Mesh Cylinder").shape = "CYLINDER" + + if ItemData.data["representation_type"] in ("SolidModel", "SweptSolid"): + self.layout.operator( + "bim.add_swept_area_solid_item", icon="MESH_CUBE", text="Extruded Area Solid Cube" + ).shape = "CUBE" + self.layout.operator( + "bim.add_swept_area_solid_item", icon="MESH_CYLINDER", text="Extruded Area Solid Cylinder" + ).shape = "CYLINDER" class CreateObjectUI: