diff --git a/src/blenderbim/blenderbim/bim/module/boundary/data.py b/src/blenderbim/blenderbim/bim/module/boundary/data.py new file mode 100644 index 0000000000..683d539611 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/boundary/data.py @@ -0,0 +1,46 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2023 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import os +import bpy +import ifcopenshell +import ifcopenshell.util.schema +import blenderbim.tool as tool + + +def refresh(): + SpaceBoundariesData.is_loaded = False + + +class SpaceBoundariesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"boundaries": cls.boundaries()} + cls.is_loaded = True + + @classmethod + def boundaries(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object) + for rel in element.BoundedBy or []: + description = f"{rel.id()} > {rel.RelatedBuildingElement.is_a()}/{rel.RelatedBuildingElement.Name}" + results.append({"id": rel.id(), "description": description}) + return results diff --git a/src/blenderbim/blenderbim/bim/module/boundary/operator.py b/src/blenderbim/blenderbim/bim/module/boundary/operator.py index 33ce113dc7..ef455d7c86 100644 --- a/src/blenderbim/blenderbim/bim/module/boundary/operator.py +++ b/src/blenderbim/blenderbim/bim/module/boundary/operator.py @@ -21,7 +21,6 @@ import logging import ifcopenshell.api import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore -from ifcopenshell.api.boundary.data import Data import blenderbim.bim.import_ifc as import_ifc @@ -49,7 +48,7 @@ class Loader: if not boundary.ConnectionGeometry: return None surface = boundary.ConnectionGeometry.SurfaceOnRelatingElement - # workaround for invalid geometry provided by Revit. See https://github.com/IfcOpenShell/IfcOpenShell/issues/635#issuecomment-770366838 + # Workaround for invalid geometry provided by Revit. See https://github.com/Autodesk/revit-ifc/issues/270 if surface.is_a("IfcCurveBoundedPlane") and not getattr(surface, "InnerBoundaries", None): surface.InnerBoundaries = () shape = ifcopenshell.geom.create_shape(self.settings, surface) @@ -69,8 +68,7 @@ class Loader: self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings) self.ifc_importer.file = self.ifc_file - def load_boundary(self, boundary_id, blender_space): - boundary = self.ifc_file.by_id(boundary_id) + def load_boundary(self, boundary, blender_space): obj = tool.Ifc.get_object(boundary) if obj: return obj @@ -90,12 +88,8 @@ class LoadProjectSpaceBoundaries(bpy.types.Operator): def execute(self, context): loader = Loader() - if not Data.is_loaded: - Data.load(tool.Ifc.get()) - for space_id, boundaries_id in Data.spaces.items(): - blender_space = tool.Ifc.get_object(tool.Ifc.get().by_id(space_id)) - for boundary_id in boundaries_id: - loader.load_boundary(boundary_id, blender_space) + for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"): + loader.load_boundary(rel, tool.Ifc.get_object(rel.RelatingSpace)) return {"FINISHED"} @@ -107,10 +101,9 @@ class LoadBoundary(bpy.types.Operator): def execute(self, context): loader = Loader() - blender_space = context.active_object for obj in context.visible_objects: obj.select_set(False) - obj = loader.load_boundary(self.boundary_id, blender_space) + obj = loader.load_boundary(tool.Ifc.get().by_id(self.boundary_id), context.active_object) obj.select_set(True) bpy.context.view_layer.objects.active = obj return {"FINISHED"} @@ -122,13 +115,10 @@ class LoadSpaceBoundaries(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - blender_space = context.active_object - oprops = context.active_object.BIMObjectProperties loader = Loader() - if not Data.is_loaded: - Data.load(tool.Ifc.get()) - for boundary_id in Data.spaces.get(oprops.ifc_definition_id, []): - loader.load_boundary(boundary_id, blender_space) + element = tool.Ifc.get_entity(context.active_object) + for rel in element.BoundedBy or []: + loader.load_boundary(rel, context.active_object) return {"FINISHED"} @@ -140,10 +130,8 @@ class SelectProjectBoundaries(bpy.types.Operator): def execute(self, context): for obj in context.visible_objects: obj.select_set(False) - ifc_file = tool.Ifc.get() - for boundary_id in Data.boundaries: - boundary = ifc_file.by_id(boundary_id) - obj = tool.Ifc.get_object(boundary) + for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"): + obj = tool.Ifc.get_object(rel) if obj: obj.select_set(True) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/boundary/ui.py b/src/blenderbim/blenderbim/bim/module/boundary/ui.py index c012aff9a0..c989960804 100644 --- a/src/blenderbim/blenderbim/bim/module/boundary/ui.py +++ b/src/blenderbim/blenderbim/bim/module/boundary/ui.py @@ -21,7 +21,7 @@ import blenderbim.bim.helper from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore import blenderbim.tool as tool -from ifcopenshell.api.boundary.data import Data +from blenderbim.bim.module.boundary.data import SpaceBoundariesData class BIM_PT_SceneBoundaries(Panel): @@ -129,28 +129,22 @@ class BIM_PT_SpaceBoundaries(Panel): return False if not IfcStore.get_element(props.ifc_definition_id): return False - valid_classes = ("IfcSpace", "IfcExternalSpatialElement") - entity = IfcStore.get_file().by_id(props.ifc_definition_id) - for ifc_class in valid_classes: - if entity.is_a(ifc_class): + element = tool.Ifc.get_entity(context.active_object) + for ifc_class in ("IfcSpace", "IfcExternalSpatialElement"): + if element.is_a(ifc_class): return True return False def draw(self, context): + if not SpaceBoundariesData.is_loaded: + SpaceBoundariesData.load() + self.props = context.active_object.BIMObjectProperties self.ifc_file = tool.Ifc.get() row = self.layout.row() row.operator("bim.load_space_boundaries") - if not Data.is_loaded: - Data.load(self.ifc_file) - for boundary_id in Data.spaces.get(self.props.ifc_definition_id, []): - boundary_data = Data.boundaries[boundary_id] - building_element = self.ifc_file.by_id(boundary_data["RelatedBuildingElement"]) + for boundary in SpaceBoundariesData.data["boundaries"]: row = self.layout.row() - if building_element: - bld_el_description = f"{building_element.is_a()}/{building_element.Name}" - else: - bld_el_description = None - row.label(text=f"{boundary_id} > {bld_el_description}", icon="GHOST_ENABLED") + row.label(text=boundary["description"], icon="GHOST_ENABLED") op = row.operator("bim.load_boundary", text="", icon="RESTRICT_SELECT_OFF") - op.boundary_id = boundary_id + op.boundary_id = boundary["id"] diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py index 6730609afa..135ee071d1 100644 --- a/src/blenderbim/blenderbim/core/drawing.py +++ b/src/blenderbim/blenderbim/core/drawing.py @@ -261,4 +261,4 @@ def sync_references(ifc, collector, drawing_tool, drawing=None): drawing_tool.sync_object_representation(reference_obj) def select_assigned_product(drawing): - drawing.select_assigned_product() \ No newline at end of file + drawing.select_assigned_product() diff --git a/src/blenderbim/test/bim/feature/patch.feature b/src/blenderbim/test/bim/feature/patch.feature index 473c45cbb7..dc89edf2d0 100644 --- a/src/blenderbim/test/bim/feature/patch.feature +++ b/src/blenderbim/test/bim/feature/patch.feature @@ -7,7 +7,7 @@ Scenario: Execute IFC Patch And I set "scene.BIMPatchProperties.ifc_patch_input" to "{cwd}/test/files/basic.ifc" And I set "scene.BIMPatchProperties.ifc_patch_output" to "{cwd}/test/files/basic-patched.ifc" And I set "scene.BIMPatchProperties.ifc_patch_args" to "[123454321,0,0,0]" - When I press "bim.execute_ifc_patch" + When I press "bim.execute_ifc_patch(use_json_for_args=True)" Then the file "{cwd}/test/files/basic-patched.ifc" should contain "123454321" Scenario: Run migrate patch diff --git a/src/blenderbim/test/tool/test_debug.py b/src/blenderbim/test/tool/test_debug.py index 31e69d4bea..099a6ccd54 100644 --- a/src/blenderbim/test/tool/test_debug.py +++ b/src/blenderbim/test/tool/test_debug.py @@ -33,7 +33,9 @@ class TestImplementsTool(NewFile): class TestAddSchemaIdentifier(NewFile): def test_run(self): - subject.add_schema_identifier(TestLoadExpress().test_run()) + cwd = os.path.dirname(os.path.realpath(__file__)) + schema = subject.load_express(os.path.join(cwd, "..", "files", "test.exp")) + subject.add_schema_identifier(schema) assert IfcStore.schema_identifiers[-1] == "IFCROGUE" @@ -42,7 +44,6 @@ class TestLoadExpress(NewFile): cwd = os.path.dirname(os.path.realpath(__file__)) schema = subject.load_express(os.path.join(cwd, "..", "files", "test.exp")) assert schema.schema_name == "IFCROGUE" - return schema class TestPurgeHdf5Cache(NewFile):