diff --git a/src/bonsai/bonsai/bim/module/model/data.py b/src/bonsai/bonsai/bim/module/model/data.py index eeec8ffe50..11762c6337 100644 --- a/src/bonsai/bonsai/bim/module/model/data.py +++ b/src/bonsai/bonsai/bim/module/model/data.py @@ -26,6 +26,7 @@ from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc import bonsai.tool as tool from math import degrees from natsort import natsorted +from typing import Union def refresh(): @@ -617,6 +618,7 @@ class ItemData: cls.data = {} cls.data["representation_identifier"] = cls.representation_identifier() cls.data["representation_type"] = cls.representation_type() + cls.data["profiles_enum"] = cls.profiles_enum() @classmethod def representation_identifier(cls): @@ -629,3 +631,25 @@ class ItemData: props = bpy.context.scene.BIMGeometryProperties rep = tool.Geometry.get_active_representation(props.representation_obj) return rep.RepresentationType + + @classmethod + def profiles_enum(cls) -> list[Union[tuple[str, str, str], None]]: + ifc_file = tool.Ifc.get() + profiles: list[Union[tuple[str, str, str], None]] = [] + profiles.append( + ( + "-", + "Use Unnamed Profile", + "If named profile is currently used, replace it with the unnamed version so it can be edited without affecting original profile.", + ) + ) + profiles.append(None) + + named_profiles: list[tuple[str, str, str]] = [] + for profile in ifc_file.by_type("IfcProfileDef"): + if (profile_name := profile.ProfileName) is None: + continue + named_profiles.append((str(profile.id()), profile_name, profile.is_a())) + named_profiles.sort(key=lambda x: x[1]) + profiles.extend(named_profiles) + return profiles diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index f5ead43824..0c00f0331b 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -282,6 +282,12 @@ class EditItemUI: return obj = context.active_object assert obj + + mesh_props = obj.data.BIMMeshProperties + if cls.get_item_object_class(obj) == "IfcExtrudedAreaSolid": + row = cls.layout.row() + row.prop(mesh_props, "item_profile") + for item_attribute in obj.data.BIMMeshProperties.item_attributes: row = cls.layout.row() draw_attribute(item_attribute, cls.layout) @@ -289,6 +295,10 @@ class EditItemUI: row = cls.layout.row() row.operator("bim.update_item_attributes", icon="FILE_REFRESH", text="") + @classmethod + def get_item_object_class(cls, obj: bpy.types.Object) -> str: + return obj.name.split("/")[1] + class BIM_MT_add_representation_item(Menu): bl_idname = "BIM_MT_add_representation_item" diff --git a/src/bonsai/bonsai/bim/prop.py b/src/bonsai/bonsai/bim/prop.py index 33e1812643..2cf44952bc 100644 --- a/src/bonsai/bonsai/bim/prop.py +++ b/src/bonsai/bonsai/bim/prop.py @@ -539,6 +539,14 @@ class BIMObjectProperties(PropertyGroup): rotation_checksum: StringProperty(name="Rotation Checksum") +def get_profiles(self: "BIMMeshProperties", context: bpy.types.Context): + from bonsai.bim.module.model.data import ItemData + + if not ItemData.is_loaded: + ItemData.load() + return ItemData.data["profiles_enum"] + + class BIMMeshProperties(PropertyGroup): ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_boolean_id: IntProperty(name="IFC Boolean ID") @@ -550,6 +558,7 @@ class BIMMeshProperties(PropertyGroup): subshape_type: EnumProperty(name="Subshape Type", items=[(i, i, "") for i in ("-", "PROFILE", "AXIS")]) ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter) item_attributes: CollectionProperty(name="Item Attributes", type=Attribute) + item_profile: EnumProperty(name="Item Profile", items=get_profiles) material_checksum: StringProperty(name="Material Checksum", default="[]") mesh_checksum: StringProperty(name="Mesh Checksum", default="") replaced_mesh: PointerProperty(type=bpy.types.Mesh, description="Original mesh to revert section cutaway") diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index f1e5b61638..694d15d794 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -27,6 +27,7 @@ import multiprocessing import ifcopenshell import ifcopenshell.api import ifcopenshell.api.grid +import ifcopenshell.api.profile import ifcopenshell.api.style import ifcopenshell.geom import ifcopenshell.guid @@ -50,7 +51,7 @@ from math import radians, pi from mathutils import Vector, Matrix from mathutils.bvhtree import BVHTree from bonsai.bim.ifc import IfcStore -from typing import Union, Iterable, Optional, Literal, Iterator, List, TYPE_CHECKING, get_args, Generator +from typing import Union, Iterable, Optional, Literal, Iterator, List, TYPE_CHECKING, get_args, Generator, cast from typing_extensions import TypeIs if TYPE_CHECKING: @@ -1675,13 +1676,38 @@ class Geometry(bonsai.core.tool.Geometry): bonsai.bim.helper.import_attributes2(item, props.item_attributes, callback=callback) + profile = None + if item.is_a("IfcExtrudedAreaSolid"): + profile = item.SweptArea + if profile is None or profile.ProfileName is None: + item_profile = "-" + else: + item_profile = str(profile.id()) + props.item_profile = item_profile + @classmethod def update_item_attributes(cls, obj: bpy.types.Object) -> None: props = obj.data.BIMMeshProperties + ifc_file = tool.Ifc.get() + item = tool.Ifc.get().by_id(props.ifc_definition_id) for attribute in props.item_attributes: setattr(item, attribute.name, attribute.get_value()) + if item.is_a("IfcSweptAreaSolid"): + item_profile = cast(str, props.item_profile) + profile = item.SweptArea + profile_name: Union[str, None] = profile.ProfileName + if item_profile == "-": + if profile_name is not None: + profile = ifcopenshell.util.element.copy_deep(ifc_file, profile) + profile.ProfileName = None + item.SweptArea = profile + else: + if profile_name is None: + ifcopenshell.api.profile.remove_profile(ifc_file, profile) + item.SweptArea = ifc_file.by_id(int(item_profile)) + @classmethod def import_item(cls, obj: bpy.types.Object) -> None: props = bpy.context.scene.BIMGeometryProperties