diff --git a/src/blenderbim/blenderbim/bim/ifc.py b/src/blenderbim/blenderbim/bim/ifc.py index f8a3f978ff..0940ea7a2e 100644 --- a/src/blenderbim/blenderbim/bim/ifc.py +++ b/src/blenderbim/blenderbim/bim/ifc.py @@ -1,5 +1,5 @@ # BlenderBIM Add-on - OpenBIM Blender Add-on -# Copyright (C) 2020, 2021 Dion Moult +# Copyright (C) 2020, 2021, 2022 Dion Moult # # This file is part of BlenderBIM Add-on. # @@ -47,6 +47,7 @@ class IfcStore: last_transaction = "" history = [] future = [] + schema_identifiers = ["IFC4", "IFC2X3"] @staticmethod def purge(): @@ -63,6 +64,7 @@ class IfcStore: IfcStore.pset_template_file = None IfcStore.library_path = "" IfcStore.library_file = None + IfcStore.schema_identifiers = ["IFC4", "IFC2X3"] @staticmethod def get_file(): diff --git a/src/blenderbim/blenderbim/bim/module/debug/__init__.py b/src/blenderbim/blenderbim/bim/module/debug/__init__.py index 47e3d6fdda..74bfcc9b07 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/debug/__init__.py @@ -20,18 +20,20 @@ import bpy from . import ui, prop, operator classes = ( - operator.PrintIfcFile, - operator.PurgeIfcLinks, - operator.PrintObjectPlacement, - operator.ValidateIfcFile, - operator.ProfileImportIFC, operator.CreateAllShapes, operator.CreateShapeFromStepId, + operator.InspectFromObject, + operator.InspectFromStepId, + operator.ParseExpress, + operator.PrintIfcFile, + operator.PrintObjectPlacement, + operator.ProfileImportIFC, + operator.PurgeIfcLinks, + operator.RewindInspector, + operator.SelectExpressFile, operator.SelectHighPolygonMeshes, operator.SelectHighestPolygonMeshes, - operator.InspectFromStepId, - operator.InspectFromObject, - operator.RewindInspector, + operator.ValidateIfcFile, prop.BIMDebugProperties, ui.BIM_PT_debug, ) diff --git a/src/blenderbim/blenderbim/bim/module/debug/operator.py b/src/blenderbim/blenderbim/bim/module/debug/operator.py index 7b7bb9d803..d564943e72 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/operator.py +++ b/src/blenderbim/blenderbim/bim/module/debug/operator.py @@ -16,14 +16,18 @@ # 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 time import logging import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.representation +import blenderbim.tool as tool +import blenderbim.core.debug as core import blenderbim.bim.handler import blenderbim.bim.import_ifc as import_ifc +import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore @@ -296,3 +300,31 @@ class PrintObjectPlacement(bpy.types.Operator): def execute(self, context): print(ifcopenshell.util.placement.get_local_placement(IfcStore.get_file().by_id(self.step_id))) return {"FINISHED"} + + +class ParseExpress(bpy.types.Operator): + bl_idname = "bim.parse_express" + bl_label = "Parse Express" + + def execute(self, context): + core.parse_express(tool.Debug, context.scene.BIMDebugProperties.express_file) + blenderbim.bim.handler.refresh_ui_data() + return {"FINISHED"} + + +class SelectExpressFile(bpy.types.Operator): + bl_idname = "bim.select_express_file" + bl_label = "Select Express File" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Select an IFC EXPRESS definition" + filepath: bpy.props.StringProperty(subtype="FILE_PATH") + filter_glob: bpy.props.StringProperty(default="*.exp", options={"HIDDEN"}) + + def execute(self, context): + if os.path.exists(self.filepath) and "exp" in os.path.splitext(self.filepath)[1]: + context.scene.BIMDebugProperties.express_file = self.filepath + return {"FINISHED"} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} diff --git a/src/blenderbim/blenderbim/bim/module/debug/prop.py b/src/blenderbim/blenderbim/bim/module/debug/prop.py index bfff95ef20..08e7699a61 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/prop.py +++ b/src/blenderbim/blenderbim/bim/module/debug/prop.py @@ -39,3 +39,4 @@ class BIMDebugProperties(PropertyGroup): attributes: CollectionProperty(name="Attributes", type=Attribute) inverse_attributes: CollectionProperty(name="Inverse Attributes", type=Attribute) inverse_references: CollectionProperty(name="Inverse References", type=Attribute) + express_file: StringProperty(name="Express File") diff --git a/src/blenderbim/blenderbim/bim/module/debug/ui.py b/src/blenderbim/blenderbim/bim/module/debug/ui.py index 1a70377a28..22030d3e37 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/ui.py +++ b/src/blenderbim/blenderbim/bim/module/debug/ui.py @@ -38,6 +38,11 @@ class BIM_PT_debug(Panel): row.operator("bim.validate_ifc_file", icon="CHECKMARK", text="") row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="") + row = self.layout.row(align=True) + row.prop(props, "express_file", text="") + row.operator("bim.parse_express", icon="IMPORT", text="") + row.operator("bim.select_express_file", icon="FILE_FOLDER", text="") + row = layout.row() row.operator("bim.print_ifc_file") diff --git a/src/blenderbim/blenderbim/bim/module/project/data.py b/src/blenderbim/blenderbim/bim/module/project/data.py new file mode 100644 index 0000000000..1fe5e85226 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/project/data.py @@ -0,0 +1,39 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 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 bpy +import blenderbim.tool as tool +from blenderbim.bim.ifc import IfcStore + + +def refresh(): + ProjectData.is_loaded = False + + +class ProjectData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"export_schema": cls.get_export_schema()} + cls.is_loaded = True + + @classmethod + def get_export_schema(cls): + return [(s, s, "") for s in IfcStore.schema_identifiers] diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 3aa5423207..fb683d88af 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -57,7 +57,7 @@ class CreateProject(bpy.types.Operator): return {"FINISHED"} IfcStore.file = ifcopenshell.api.run( - "project.create_file", **{"version": context.scene.BIMProperties.export_schema} + "project.create_file", **{"version": context.scene.BIMProjectProperties.export_schema} ) self.file = IfcStore.get_file() diff --git a/src/blenderbim/blenderbim/bim/module/project/prop.py b/src/blenderbim/blenderbim/bim/module/project/prop.py index f5343d89fe..1db22e940c 100644 --- a/src/blenderbim/blenderbim/bim/module/project/prop.py +++ b/src/blenderbim/blenderbim/bim/module/project/prop.py @@ -18,18 +18,26 @@ import bpy import ifcopenshell.util.placement +from blenderbim.bim.module.project.data import ProjectData from blenderbim.bim.ifc import IfcStore from blenderbim.bim.prop import StrProperty from bpy.types import PropertyGroup from bpy.props import ( - StringProperty, BoolProperty, + CollectionProperty, + EnumProperty, FloatProperty, IntProperty, - CollectionProperty, + StringProperty, ) +def get_export_schema(self, context): + if not ProjectData.is_loaded: + ProjectData.load() + return ProjectData.data["export_schema"] + + def update_filter_mode(self, context): self.filter_categories.clear() if self.filter_mode == "NONE": @@ -123,6 +131,7 @@ class BIMProjectProperties(PropertyGroup): model_offset_coordinates: StringProperty(name="Model Offset Coordinates", default="0,0,0") links: CollectionProperty(name="Links", type=Link) active_link_index: IntProperty(name="Active Link Index") + export_schema: EnumProperty(items=get_export_schema, name="IFC Schema") def get_library_element_index(self, lib_element): return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element)) diff --git a/src/blenderbim/blenderbim/bim/module/project/ui.py b/src/blenderbim/blenderbim/bim/module/project/ui.py index de3ba45f37..4a8e924006 100644 --- a/src/blenderbim/blenderbim/bim/module/project/ui.py +++ b/src/blenderbim/blenderbim/bim/module/project/ui.py @@ -19,6 +19,7 @@ import os from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.project.data import ProjectData class BIM_PT_project(Panel): @@ -29,6 +30,9 @@ class BIM_PT_project(Panel): bl_context = "scene" def draw(self, context): + if not ProjectData.is_loaded: + ProjectData.load() + self.layout.use_property_decorate = False self.layout.use_property_split = True props = context.scene.BIMProperties @@ -148,8 +152,9 @@ class BIM_PT_project(Panel): def draw_create_project_ui(self, context): props = context.scene.BIMProperties + pprops = context.scene.BIMProjectProperties row = self.layout.row() - row.prop(props, "export_schema") + row.prop(pprops, "export_schema") row = self.layout.row() row.prop(context.scene.unit_settings, "system") row = self.layout.row() diff --git a/src/blenderbim/blenderbim/bim/prop.py b/src/blenderbim/blenderbim/bim/prop.py index 4145642be1..397e991e7d 100644 --- a/src/blenderbim/blenderbim/bim/prop.py +++ b/src/blenderbim/blenderbim/bim/prop.py @@ -248,7 +248,6 @@ class BIMProperties(PropertyGroup): default=os.path.join(cwd, "data") + os.path.sep, name="Data Directory", update=update_data_dir ) ifc_file: StringProperty(name="IFC File", update=update_ifc_file) - export_schema: EnumProperty(items=[("IFC4", "IFC4", ""), ("IFC2X3", "IFC2X3", "")], name="IFC Schema") last_transaction: StringProperty(name="Last Transaction") should_section_selected_objects: BoolProperty(name="Section Selected Objects", default=False) section_plane_colour: FloatVectorProperty( diff --git a/src/blenderbim/blenderbim/core/debug.py b/src/blenderbim/blenderbim/core/debug.py new file mode 100644 index 0000000000..6e3970499b --- /dev/null +++ b/src/blenderbim/blenderbim/core/debug.py @@ -0,0 +1,21 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 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 . + + +def parse_express(debug, filename): + debug.add_schema_identifier(debug.load_express(filename)) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index d3221a94af..29ceb85880 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -82,6 +82,12 @@ class Context: def set_context(cls, context): pass +@interface +class Debug: + def add_schema_identifier(cls, schema): pass + def load_express(cls, filename): pass + + @interface class Drawing: def disable_editing_text(cls, obj): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index 7de559ecbe..ffb79aecbb 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -21,6 +21,7 @@ from blenderbim.tool.blender import Blender from blenderbim.tool.brick import Brick from blenderbim.tool.collector import Collector from blenderbim.tool.context import Context +from blenderbim.tool.debug import Debug from blenderbim.tool.drawing import Drawing from blenderbim.tool.geometry import Geometry from blenderbim.tool.ifc import Ifc diff --git a/src/blenderbim/blenderbim/tool/debug.py b/src/blenderbim/blenderbim/tool/debug.py new file mode 100644 index 0000000000..17d0a7ee41 --- /dev/null +++ b/src/blenderbim/blenderbim/tool/debug.py @@ -0,0 +1,34 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 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 bpy +import ifcopenshell.express +import blenderbim.core.tool +from blenderbim.bim.ifc import IfcStore + + +class Debug(blenderbim.core.tool.Debug): + @classmethod + def add_schema_identifier(cls, schema): + IfcStore.schema_identifiers.append(schema.schema_name) + + @classmethod + def load_express(cls, filename): + schema = ifcopenshell.express.parse(filename) + ifcopenshell.register_schema(schema) + return schema diff --git a/src/blenderbim/pytest.ini b/src/blenderbim/pytest.ini index d66974581a..8e1e234f3f 100644 --- a/src/blenderbim/pytest.ini +++ b/src/blenderbim/pytest.ini @@ -5,6 +5,7 @@ markers = bimtester brick context + debug drawing geometry library diff --git a/src/blenderbim/test/bim/feature/debug.feature b/src/blenderbim/test/bim/feature/debug.feature new file mode 100644 index 0000000000..904d69c1ce --- /dev/null +++ b/src/blenderbim/test/bim/feature/debug.feature @@ -0,0 +1,13 @@ +@debug +Feature: Debug + +Scenario: Select express file + Given an empty Blender session + When I press "bim.select_express_file(filepath='{cwd}/test/files/test.exp')" + Then nothing happens + +Scenario: Parse express + Given an empty Blender session + And I press "bim.select_express_file(filepath='{cwd}/test/files/test.exp')" + When I press "bim.parse_express" + Then nothing happens diff --git a/src/blenderbim/test/core/bootstrap.py b/src/blenderbim/test/core/bootstrap.py index 895cde0335..3ee75fef62 100644 --- a/src/blenderbim/test/core/bootstrap.py +++ b/src/blenderbim/test/core/bootstrap.py @@ -63,6 +63,13 @@ def context(): prophet.verify() +@pytest.fixture +def debug(): + prophet = Prophecy(blenderbim.core.tool.Debug) + yield prophet + prophet.verify() + + @pytest.fixture def drawing(): prophet = Prophecy(blenderbim.core.tool.Drawing) diff --git a/src/blenderbim/test/core/test_debug.py b/src/blenderbim/test/core/test_debug.py new file mode 100644 index 0000000000..6a0278b840 --- /dev/null +++ b/src/blenderbim/test/core/test_debug.py @@ -0,0 +1,27 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 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 blenderbim.core.debug as subject +from test.core.bootstrap import debug + + +class TestParseExpress: + def test_run(self, debug): + debug.load_express("filename").should_be_called().will_return("schema") + debug.add_schema_identifier("schema").should_be_called() + subject.parse_express(debug, "filename") diff --git a/src/blenderbim/test/files/test.exp b/src/blenderbim/test/files/test.exp new file mode 100644 index 0000000000..23937a8ce8 --- /dev/null +++ b/src/blenderbim/test/files/test.exp @@ -0,0 +1,19 @@ +SCHEMA IFCROGUE; + +TYPE IfcDonkeyPowerMeasure = REAL; +END_TYPE; + +TYPE IfcLabel = STRING(255); +END_TYPE; + +ENTITY IfcBurbRoot + SUBTYPE OF (IfcRoot); + AcidDamage : OPTIONAL IfcDonkeyPowerMeasure; +END_ENTITY; + +ENTITY IfcRoot + ABSTRACT SUPERTYPE OF (IfcBurbRoot); + Name : OPTIONAL IfcLabel; +END_ENTITY; + +END_SCHEMA; diff --git a/src/blenderbim/test/tool/test_debug.py b/src/blenderbim/test/tool/test_debug.py new file mode 100644 index 0000000000..1ada44ad5f --- /dev/null +++ b/src/blenderbim/test/tool/test_debug.py @@ -0,0 +1,45 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 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 blenderbim.core.tool +import blenderbim.tool as tool +from test.bim.bootstrap import NewFile +from blenderbim.tool.debug import Debug as subject +from blenderbim.bim.ifc import IfcStore + + +class TestImplementsTool(NewFile): + def test_run(self): + assert isinstance(subject(), blenderbim.core.tool.Debug) + + +class TestAddSchemaIdentifier(NewFile): + def test_run(self): + subject.add_schema_identifier(TestLoadExpress().test_run()) + assert IfcStore.schema_identifiers[-1] == "IFCROGUE" + + +class TestLoadExpress(NewFile): + def test_run(self): + 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