From 7597b1b29c77bb2da6d0e5cc86613f40eb623396 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 15 Aug 2024 13:57:00 +0500 Subject: [PATCH] bonsai, ifcopenshell - append IfcSurfaceStyles from other projects And other IfcPresentationStyles, though they are not currently supported by bonsai. Example - https://imgur.com/a/AHlvowp --- .../bonsai/bim/module/project/operator.py | 22 +++++++++++----- src/bonsai/bonsai/tool/project.py | 5 ++++ .../ifcopenshell/api/project/append_asset.py | 20 ++++++++++++++- .../test/api/project/test_append_asset.py | 25 +++++++++++++++++++ 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 3401a05a40..e3c92d7a79 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -201,8 +201,7 @@ class RefreshLibrary(bpy.types.Operator): types = IfcStore.library_file.wrapped_data.types_with_super() - element_classes = ["IfcTypeProduct", "IfcMaterial", "IfcCostSchedule", "IfcProfileDef"] - for importable_type in sorted(element_classes): + for importable_type in sorted(tool.Project.get_appendable_asset_types()): if importable_type in types: new = self.props.library_elements.add() new.name = importable_type @@ -384,9 +383,8 @@ class AppendEntireLibrary(bpy.types.Operator): self.file = IfcStore.get_file() self.library = IfcStore.library_file - lib_elements = ifcopenshell.util.selector.filter_elements( - self.library, "IfcTypeProduct, IfcMaterial, IfcCostSchedule, IfcProfileDef" - ) + query = ", ".join(tool.Project.get_appendable_asset_types()) + lib_elements = ifcopenshell.util.selector.filter_elements(self.library, query) for element in lib_elements: bpy.ops.bim.append_library_element(definition=element.id()) return {"FINISHED"} @@ -456,9 +454,11 @@ class AppendLibraryElement(bpy.types.Operator): obj = tool.Ifc.get_object(element_type) if obj is None: self.import_type_from_ifc(element_type, context) - elif element.is_a("IfcMaterial"): self.import_material_from_ifc(element, context) + elif element.is_a("IfcPresentationStyle"): + self.import_presentation_style_from_ifc(element, context) + try: context.scene.BIMProjectProperties.library_elements[self.prop_index].is_appended = True except: @@ -475,6 +475,16 @@ class AppendLibraryElement(bpy.types.Operator): ifc_importer.file = self.file self.import_material_styles(element, ifc_importer) + def import_presentation_style_from_ifc( + self, style: ifcopenshell.entity_instance, context: bpy.types.Context + ) -> None: + self.file = tool.Ifc.get() + logger = logging.getLogger("ImportIFC") + ifc_import_settings = import_ifc.IfcImportSettings.factory(context, tool.Ifc.get_path(), logger) + ifc_importer = import_ifc.IfcImporter(ifc_import_settings) + ifc_importer.file = self.file + ifc_importer.create_style(style) + def import_product_from_ifc(self, element: ifcopenshell.entity_instance, context: bpy.types.Context) -> None: self.file = IfcStore.get_file() logger = logging.getLogger("ImportIFC") diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py index c2a6c71d0c..5f0491a4fb 100644 --- a/src/bonsai/bonsai/tool/project.py +++ b/src/bonsai/bonsai/tool/project.py @@ -30,6 +30,7 @@ import bonsai.core.owner import bonsai.bim.schema import bonsai.tool as tool from bonsai.bim.ifc import IfcStore +from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES from pathlib import Path from typing import Optional @@ -204,3 +205,7 @@ class Project(bonsai.core.tool.Project): @classmethod def clear_recent_ifc_projects(cls) -> None: cls.write_recent_ifc_projects([]) + + @classmethod + def get_appendable_asset_types(cls) -> tuple[str, ...]: + return tuple(e for e in APPENDABLE_ASSET_TYPES if e != "IfcProduct") diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index 90d19ba0c3..0f5fc0ec8d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -25,7 +25,18 @@ import ifcopenshell.api.owner.settings import ifcopenshell.util.element import ifcopenshell.util.geolocation import ifcopenshell.util.placement -from typing import Optional, Any, Union +from typing import Optional, Any, Union, Literal, get_args + + +APPENDABLE_ASSET = Literal[ + "IfcTypeProduct", + "IfcProduct", + "IfcMaterial", + "IfcCostSchedule", + "IfcProfileDef", + "IfcPresentationStyle", +] +APPENDABLE_ASSET_TYPES = get_args(APPENDABLE_ASSET) def append_asset( @@ -154,6 +165,9 @@ class Usecase: elif self.settings["element"].is_a("IfcProfileDef"): self.target_class = "IfcProfileDef" return self.append_profile_def() + elif self.settings["element"].is_a("IfcPresentationStyle"): + self.target_class = "IfcPresentationStyle" + return self.append_presentation_style() def get_existing_element(self, element): if element.id() in self.added_elements: @@ -186,6 +200,10 @@ class Usecase: self.whitelisted_inverse_attributes = {"IfcProfileDef": ["HasProperties"]} return self.add_element(self.settings["element"]) + def append_presentation_style(self): + self.whitelisted_inverse_attributes = {} + return self.add_element(self.settings["element"]) + def append_type_product(self): self.whitelisted_inverse_attributes = { "IfcObjectDefinition": ["HasAssociations"], diff --git a/src/ifcopenshell-python/test/api/project/test_append_asset.py b/src/ifcopenshell-python/test/api/project/test_append_asset.py index 7de3457320..e467bd4937 100644 --- a/src/ifcopenshell-python/test/api/project/test_append_asset.py +++ b/src/ifcopenshell-python/test/api/project/test_append_asset.py @@ -408,6 +408,31 @@ class TestAppendAssetIFC2X3(test.bootstrap.IFC2X3): ) assert np.array_equal(ifcopenshell.util.placement.get_local_placement(new.ObjectPlacement), resulting_matrix) + def test_append_a_surface_style(self): + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + library = ifcopenshell.api.project.create_file(version=self.file.schema) + ifcopenshell.api.root.create_entity(library, ifc_class="IfcProject") + + style_name = "New Style" + style = ifcopenshell.api.style.add_style(library, style_name) + shading_attrs = {"SurfaceColour": {"Name": "", "Red": 1, "Green": 1, "Blue": 1}} + ifcopenshell.api.style.add_surface_style( + library, style, ifc_class="IfcSurfaceStyleShading", attributes=shading_attrs + ) + + new_style = ifcopenshell.api.project.append_asset(self.file, library=library, element=style) + assert self.file.by_type("IfcSurfaceStyle") == [new_style] + assert new_style.Name == style_name + style_elements = new_style.Styles + assert len(style_elements) == 1 + + # Check shading style. + shading_style = style_elements[0] + assert shading_style.is_a("IfcSurfaceStyleShading") + shading_colour_info = shading_style.SurfaceColour.get_info() + del shading_colour_info["id"], shading_colour_info["type"] + assert shading_colour_info == shading_attrs["SurfaceColour"] + class TestAppendAssetIFC4(test.bootstrap.IFC4, TestAppendAssetIFC2X3): # NOTE: breaks in IFC2X3 since IfcProfileDef doesn't have "HasProperties" inverse in ifc2x3