From fae607f8ce69496e91c400a6c1abd523fe0c52db Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 16 Nov 2021 22:15:54 +1100 Subject: [PATCH] New UI to add / edit / remove library information and references. --- src/blenderbim/blenderbim/bim/__init__.py | 1 + .../blenderbim/bim/module/library/__init__.py | 47 ++++++ .../blenderbim/bim/module/library/data.py | 80 ++++++++++ .../blenderbim/bim/module/library/operator.py | 143 ++++++++++++++++++ .../blenderbim/bim/module/library/prop.py | 51 +++++++ .../blenderbim/bim/module/library/ui.py | 103 +++++++++++++ .../blenderbim/bim/module/type/prop.py | 4 +- src/blenderbim/blenderbim/core/library.py | 82 ++++++++++ src/blenderbim/blenderbim/core/tool.py | 17 ++- src/blenderbim/blenderbim/tool/__init__.py | 1 + src/blenderbim/blenderbim/tool/library.py | 76 ++++++++++ src/blenderbim/pytest.ini | 1 + .../test/bim/feature/library.feature | 105 +++++++++++++ src/blenderbim/test/core/bootstrap.py | 7 + src/blenderbim/test/core/test_library.py | 111 ++++++++++++++ src/blenderbim/test/tool/test_library.py | 134 ++++++++++++++++ 16 files changed, 961 insertions(+), 2 deletions(-) create mode 100644 src/blenderbim/blenderbim/bim/module/library/__init__.py create mode 100644 src/blenderbim/blenderbim/bim/module/library/data.py create mode 100644 src/blenderbim/blenderbim/bim/module/library/operator.py create mode 100644 src/blenderbim/blenderbim/bim/module/library/prop.py create mode 100644 src/blenderbim/blenderbim/bim/module/library/ui.py create mode 100644 src/blenderbim/blenderbim/core/library.py create mode 100644 src/blenderbim/blenderbim/tool/library.py create mode 100644 src/blenderbim/test/bim/feature/library.feature create mode 100644 src/blenderbim/test/core/test_library.py create mode 100644 src/blenderbim/test/tool/test_library.py diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index f829cf0ac6..cbbb5d674c 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -54,6 +54,7 @@ modules = { "pset": None, "qto": None, "classification": None, + "library": None, "constraint": None, "document": None, "pset_template": None, diff --git a/src/blenderbim/blenderbim/bim/module/library/__init__.py b/src/blenderbim/blenderbim/bim/module/library/__init__.py new file mode 100644 index 0000000000..cefd406a20 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/library/__init__.py @@ -0,0 +1,47 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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 +from . import ui, prop, operator + +classes = ( + operator.AddLibrary, + operator.RemoveLibrary, + operator.EnableEditingLibraryReferences, + operator.DisableEditingLibraryReferences, + operator.EnableEditingLibrary, + operator.DisableEditingLibrary, + operator.EditLibrary, + operator.AddLibraryReference, + operator.RemoveLibraryReference, + operator.EnableEditingLibraryReference, + operator.DisableEditingLibraryReference, + operator.EditLibraryReference, + prop.LibraryReference, + prop.BIMLibraryProperties, + ui.BIM_PT_libraries, + ui.BIM_UL_library_references, +) + + +def register(): + bpy.types.Scene.BIMLibraryProperties = bpy.props.PointerProperty(type=prop.BIMLibraryProperties) + + +def unregister(): + del bpy.types.Scene.BIMLibraryProperties diff --git a/src/blenderbim/blenderbim/bim/module/library/data.py b/src/blenderbim/blenderbim/bim/module/library/data.py new file mode 100644 index 0000000000..f7d9b1d0d9 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/library/data.py @@ -0,0 +1,80 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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 + + +def refresh(): + LibrariesData.is_loaded = False + + +class LibrariesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.is_loaded = True + cls.data = { + "libraries": cls.libraries(), + "library_attributes": cls.library_attributes(), + "reference_attributes": cls.reference_attributes(), + } + + @classmethod + def libraries(cls): + results = [] + for library in tool.Ifc.get().by_type("IfcLibraryInformation"): + results.append({"id": library.id(), "name": library.Name}) + return results + + @classmethod + def library_attributes(cls): + library_id = bpy.context.scene.BIMLibraryProperties.active_library_id + if not library_id: + return [] + results = [] + data = tool.Ifc.get().by_id(library_id).get_info() + if tool.Ifc.get_schema() == "IFC2X3": + del data["VersionDate"] + for key, value in data.items(): + if key in ["id", "type"]: + continue + if value is not None: + results.append({"name": key, "value": str(value)}) + return results + + @classmethod + def reference_attributes(cls): + props = bpy.context.scene.BIMLibraryProperties + try: + reference_id = props.references[props.active_reference_index].ifc_definition_id + except: + return [] + if not reference_id: + return [] + results = [] + data = tool.Ifc.get().by_id(reference_id).get_info() + del data["ReferencedLibrary"] + for key, value in data.items(): + if key in ["id", "type"]: + continue + if value is not None: + results.append({"name": key, "value": str(value)}) + return results diff --git a/src/blenderbim/blenderbim/bim/module/library/operator.py b/src/blenderbim/blenderbim/bim/module/library/operator.py new file mode 100644 index 0000000000..d6b1590b7e --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/library/operator.py @@ -0,0 +1,143 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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.api +import blenderbim.tool as tool +import blenderbim.core.library as core +import blenderbim.bim.handler +from blenderbim.bim.ifc import IfcStore + + +class Operator: + def execute(self, context): + IfcStore.execute_ifc_operator(self, context) + blenderbim.bim.handler.refresh_ui_data() + return {"FINISHED"} + + +class AddLibrary(bpy.types.Operator, Operator): + bl_idname = "bim.add_library" + bl_label = "Add Library" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.add_library(tool.Ifc) + + +class RemoveLibrary(bpy.types.Operator, Operator): + bl_idname = "bim.remove_library" + bl_label = "Remove Library" + bl_options = {"REGISTER", "UNDO"} + library: bpy.props.IntProperty() + + def _execute(self, context): + core.remove_library(tool.Ifc, library=tool.Ifc.get().by_id(self.library)) + + +class EnableEditingLibraryReferences(bpy.types.Operator, Operator): + bl_idname = "bim.enable_editing_library_references" + bl_label = "Enable Editing Library References" + bl_options = {"REGISTER", "UNDO"} + library: bpy.props.IntProperty() + + def _execute(self, context): + core.enable_editing_library_references(tool.Library, library=tool.Ifc.get().by_id(self.library)) + + +class DisableEditingLibraryReferences(bpy.types.Operator, Operator): + bl_idname = "bim.disable_editing_library_references" + bl_label = "Disable Editing Library References" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.disable_editing_library_references(tool.Library) + + +class EnableEditingLibrary(bpy.types.Operator, Operator): + bl_idname = "bim.enable_editing_library" + bl_label = "Enable Editing Library" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.enable_editing_library(tool.Library) + + +class DisableEditingLibrary(bpy.types.Operator, Operator): + bl_idname = "bim.disable_editing_library" + bl_label = "Disable Editing Library" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.disable_editing_library(tool.Library) + + +class EditLibrary(bpy.types.Operator, Operator): + bl_idname = "bim.edit_library" + bl_label = "Edit Library" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.edit_library(tool.Ifc, tool.Library) + + +class AddLibraryReference(bpy.types.Operator, Operator): + bl_idname = "bim.add_library_reference" + bl_label = "Add Library Reference" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.add_library_reference(tool.Ifc, tool.Library) + + +class RemoveLibraryReference(bpy.types.Operator, Operator): + bl_idname = "bim.remove_library_reference" + bl_label = "Remove Library Reference" + bl_options = {"REGISTER", "UNDO"} + reference: bpy.props.IntProperty() + + def _execute(self, context): + core.remove_library_reference(tool.Ifc, tool.Library, reference=tool.Ifc.get().by_id(self.reference)) + + +class EnableEditingLibraryReference(bpy.types.Operator, Operator): + bl_idname = "bim.enable_editing_library_reference" + bl_label = "Enable Editing Library Reference" + bl_options = {"REGISTER", "UNDO"} + reference: bpy.props.IntProperty() + + def _execute(self, context): + core.enable_editing_library_reference(tool.Library, reference=tool.Ifc.get().by_id(self.reference)) + + +class DisableEditingLibraryReference(bpy.types.Operator, Operator): + bl_idname = "bim.disable_editing_library_reference" + bl_label = "Disable Editing Library Reference" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.disable_editing_library_reference(tool.Library) + + +class EditLibraryReference(bpy.types.Operator, Operator): + bl_idname = "bim.edit_library_reference" + bl_label = "Edit Library Reference" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.edit_library_reference(tool.Ifc, tool.Library) diff --git a/src/blenderbim/blenderbim/bim/module/library/prop.py b/src/blenderbim/blenderbim/bim/module/library/prop.py new file mode 100644 index 0000000000..a3c1dd2edd --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/library/prop.py @@ -0,0 +1,51 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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 +from blenderbim.bim.prop import StrProperty, Attribute +from blenderbim.bim.module.library.data import LibrariesData +from bpy.types import PropertyGroup +from bpy.props import ( + PointerProperty, + StringProperty, + EnumProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + CollectionProperty, +) + + +def update_active_reference_index(self, context): + LibrariesData.is_loaded = False + + +class LibraryReference(PropertyGroup): + name: StringProperty(name="Name") + ifc_definition_id: IntProperty(name="IFC Definition ID") + + +class BIMLibraryProperties(PropertyGroup): + editing_mode: StringProperty(name="Editing Mode") + library_attributes: CollectionProperty(name="Library Attributes", type=Attribute) + active_library_id: IntProperty(name="Active Library Id") + reference_attributes: CollectionProperty(name="Library Attributes", type=Attribute) + active_reference_id: IntProperty(name="Active Reference Id") + references: CollectionProperty(type=LibraryReference, name="References") + active_reference_index: IntProperty(name="Active Reference Index", update=update_active_reference_index) diff --git a/src/blenderbim/blenderbim/bim/module/library/ui.py b/src/blenderbim/blenderbim/bim/module/library/ui.py new file mode 100644 index 0000000000..cf50adb8ca --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/library/ui.py @@ -0,0 +1,103 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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.bim.helper +import blenderbim.tool as tool +from bpy.types import Panel, UIList +from blenderbim.bim.module.library.data import LibrariesData + + +class BIM_PT_libraries(Panel): + bl_label = "IFC Libraries" + bl_idname = "BIM_PT_libraries" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + @classmethod + def poll(cls, context): + return tool.Ifc.get() + + def draw(self, context): + if not LibrariesData.is_loaded: + LibrariesData.load() + self.props = context.scene.BIMLibraryProperties + + if self.props.editing_mode == "LIBRARY": + self.draw_editable_library_ui() + elif self.props.editing_mode == "REFERENCE": + self.draw_editable_reference_ui() + elif self.props.editing_mode == "REFERENCES": + self.draw_editable_references_ui() + else: + self.draw_readonly_library_ui() + + def draw_editable_library_ui(self): + row = self.layout.row(align=True) + row.operator("bim.edit_library", icon="CHECKMARK") + row.operator("bim.disable_editing_library", text="", icon="CANCEL") + blenderbim.bim.helper.draw_attributes(self.props.library_attributes, self.layout) + + def draw_editable_references_ui(self): + row = self.layout.row(align=True) + row.operator("bim.enable_editing_library", icon="GREASEPENCIL") + row.operator("bim.disable_editing_library_references", text="", icon="CANCEL") + + for attribute in LibrariesData.data["library_attributes"]: + row = self.layout.row(align=True) + row.label(text=attribute["name"]) + row.label(text=attribute["value"]) + + row = self.layout.row(align=True) + row.operator("bim.add_library_reference", icon="ADD") + + self.layout.template_list( + "BIM_UL_library_references", "", self.props, "references", self.props, "active_reference_index" + ) + + for attribute in LibrariesData.data["reference_attributes"]: + row = self.layout.row(align=True) + row.label(text=attribute["name"]) + row.label(text=attribute["value"]) + + def draw_editable_reference_ui(self): + row = self.layout.row(align=True) + row.operator("bim.edit_library_reference", icon="CHECKMARK") + row.operator("bim.disable_editing_library_reference", text="", icon="CANCEL") + blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout) + + def draw_readonly_library_ui(self): + row = self.layout.row() + row.operator("bim.add_library", icon="ADD") + for library in LibrariesData.data["libraries"]: + row = self.layout.row(align=True) + row.label(text=library["name"]) + row.operator("bim.enable_editing_library_references", text="", icon="OUTLINER").library = library["id"] + row.operator("bim.remove_library", text="", icon="X").library = library["id"] + + +class BIM_UL_library_references(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + row = layout.row(align=True) + row.label(text=item.name) + op = row.operator("bim.enable_editing_library_reference", text="", icon="GREASEPENCIL") + op.reference = item.ifc_definition_id + op = row.operator("bim.remove_library_reference", text="", icon="X") + op.reference = item.ifc_definition_id diff --git a/src/blenderbim/blenderbim/bim/module/type/prop.py b/src/blenderbim/blenderbim/bim/module/type/prop.py index 52682dcf66..1b41f3af59 100644 --- a/src/blenderbim/blenderbim/bim/module/type/prop.py +++ b/src/blenderbim/blenderbim/bim/module/type/prop.py @@ -52,5 +52,7 @@ def update_relating_type_class(self, context): class BIMTypeProperties(PropertyGroup): is_editing_type: BoolProperty(name="Is Editing Type") - relating_type_class: EnumProperty(items=get_relating_type_class, name="Relating Type Class", update=update_relating_type_class) + relating_type_class: EnumProperty( + items=get_relating_type_class, name="Relating Type Class", update=update_relating_type_class + ) relating_type: EnumProperty(items=get_relating_type, name="Relating Type") diff --git a/src/blenderbim/blenderbim/core/library.py b/src/blenderbim/blenderbim/core/library.py new file mode 100644 index 0000000000..370dbb67b5 --- /dev/null +++ b/src/blenderbim/blenderbim/core/library.py @@ -0,0 +1,82 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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 add_library(ifc): + return ifc.run("library.add_library", name="Unnamed") + + +def remove_library(ifc, library=None): + ifc.run("library.remove_library", library=library) + + +def enable_editing_library_references(library_tool, library=None): + library_tool.set_editing_mode("REFERENCES") + library_tool.set_active_library(library) + library_tool.import_references(library) + + +def disable_editing_library_references(library): + library.clear_editing_mode() + + +def enable_editing_library(library): + library.set_editing_mode("LIBRARY") + library.import_library_attributes(library.get_active_library()) + + +def disable_editing_library(library): + library.set_editing_mode("REFERENCES") + + +def edit_library(ifc, library): + library.set_editing_mode("REFERENCES") + active_library = library.get_active_library() + attributes = library.export_library_attributes() + ifc.run("library.edit_library", library=active_library, attributes=attributes) + library.import_references(active_library) + + +def add_library_reference(ifc, library): + active_library = library.get_active_library() + reference = ifc.run("library.add_reference", library=active_library) + library.import_references(active_library) + return reference + + +def remove_library_reference(ifc, library, reference=None): + ifc.run("library.remove_reference", reference=reference) + library.import_references(library.get_active_library()) + + +def enable_editing_library_reference(library, reference=None): + library.set_editing_mode("REFERENCE") + library.set_active_reference(reference) + library.import_reference_attributes(reference) + + +def disable_editing_library_reference(library): + library.set_editing_mode("REFERENCES") + + +def edit_library_reference(ifc, library): + library.set_editing_mode("REFERENCES") + active_reference = library.get_active_reference() + attributes = library.export_reference_attributes() + ifc.run("library.edit_reference", reference=active_reference, attributes=attributes) + library.import_references(library.get_active_library()) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 9af5056751..6fb5153944 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -126,12 +126,27 @@ class Geometry: class Ifc: def get(cls): pass def get_entity(cls, obj): pass - def get_object(cls, obj): pass + def get_object(cls, entity): pass def link(cls, element, obj): pass def run(cls, command, **kwargs): pass def unlink(cls, element=None, obj=None): pass +@interface +class Library: + def clear_editing_mode(cls): pass + def export_library_attributes(cls): pass + def export_reference_attributes(cls): pass + def get_active_library(cls): pass + def get_active_reference(cls): pass + def import_library_attributes(cls, library): pass + def import_reference_attributes(cls, reference): pass + def import_references(cls, library): pass + def set_active_library(cls, library): pass + def set_active_reference(cls, reference): pass + def set_editing_mode(cls, mode): pass + + @interface class Material: def add_default_material_object(cls): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index c5672ddfaf..ea37429716 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -24,6 +24,7 @@ from blenderbim.tool.context import Context from blenderbim.tool.drawing import Drawing from blenderbim.tool.geometry import Geometry from blenderbim.tool.ifc import Ifc +from blenderbim.tool.library import Library from blenderbim.tool.material import Material from blenderbim.tool.misc import Misc from blenderbim.tool.owner import Owner diff --git a/src/blenderbim/blenderbim/tool/library.py b/src/blenderbim/blenderbim/tool/library.py new file mode 100644 index 0000000000..8147741d47 --- /dev/null +++ b/src/blenderbim/blenderbim/tool/library.py @@ -0,0 +1,76 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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.core.tool +import blenderbim.tool as tool + + +class Library(blenderbim.core.tool.Library): + @classmethod + def clear_editing_mode(cls): + bpy.context.scene.BIMLibraryProperties.editing_mode = "" + + @classmethod + def export_library_attributes(cls): + props = bpy.context.scene.BIMLibraryProperties + return blenderbim.bim.helper.export_attributes(props.library_attributes) + + @classmethod + def export_reference_attributes(cls): + props = bpy.context.scene.BIMLibraryProperties + return blenderbim.bim.helper.export_attributes(props.reference_attributes) + + @classmethod + def get_active_library(cls): + return tool.Ifc.get().by_id(bpy.context.scene.BIMLibraryProperties.active_library_id) + + @classmethod + def get_active_reference(cls): + return tool.Ifc.get().by_id(bpy.context.scene.BIMLibraryProperties.active_reference_id) + + @classmethod + def import_library_attributes(cls, library): + props = bpy.context.scene.BIMLibraryProperties + blenderbim.bim.helper.import_attributes2(library, props.library_attributes) + + @classmethod + def import_reference_attributes(cls, reference): + props = bpy.context.scene.BIMLibraryProperties + blenderbim.bim.helper.import_attributes2(reference, props.reference_attributes) + + @classmethod + def import_references(cls, library): + props = bpy.context.scene.BIMLibraryProperties + props.references.clear() + for reference in library.HasLibraryReferences: + new = props.references.add() + new.ifc_definition_id = reference.id() + new.name = reference.Name or "Unnamed" + + @classmethod + def set_active_library(cls, library): + bpy.context.scene.BIMLibraryProperties.active_library_id = library.id() + + @classmethod + def set_active_reference(cls, reference): + bpy.context.scene.BIMLibraryProperties.active_reference_id = reference.id() + + @classmethod + def set_editing_mode(cls, mode): + bpy.context.scene.BIMLibraryProperties.editing_mode = mode diff --git a/src/blenderbim/pytest.ini b/src/blenderbim/pytest.ini index 8970f6c5d8..d8e06d2763 100644 --- a/src/blenderbim/pytest.ini +++ b/src/blenderbim/pytest.ini @@ -7,6 +7,7 @@ markers = context drawing geometry + library material misc model diff --git a/src/blenderbim/test/bim/feature/library.feature b/src/blenderbim/test/bim/feature/library.feature new file mode 100644 index 0000000000..afe4964c5e --- /dev/null +++ b/src/blenderbim/test/bim/feature/library.feature @@ -0,0 +1,105 @@ +@library +Feature: Library + +Scenario: Add library + Given an empty IFC project + When I press "bim.add_library" + Then nothing happens + +Scenario: Remove library + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + When I press "bim.remove_library(library={library})" + Then nothing happens + +Scenario: Enable editing library references + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + When I press "bim.enable_editing_library_references(library={library})" + Then nothing happens + +Scenario: Disable editing library references + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + When I press "bim.disable_editing_library_references" + Then nothing happens + +Scenario: Enable editing library + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + When I press "bim.enable_editing_library" + Then nothing happens + +Scenario: Disable editing library + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + And I press "bim.enable_editing_library" + When I press "bim.disable_editing_library" + Then nothing happens + +Scenario: Edit library + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + And I press "bim.enable_editing_library" + When I press "bim.edit_library" + Then nothing happens + +Scenario: Add library reference + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + When I press "bim.add_library_reference" + Then nothing happens + +Scenario: Remove library reference + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + And I press "bim.add_library_reference" + And the variable "reference" is "{ifc}.by_type('IfcLibraryReference')[-1].id()" + When I press "bim.remove_library_reference(reference={reference})" + Then nothing happens + +Scenario: Enable editing library reference + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + And I press "bim.add_library_reference" + And the variable "reference" is "{ifc}.by_type('IfcLibraryReference')[-1].id()" + When I press "bim.enable_editing_library_reference(reference={reference})" + Then nothing happens + +Scenario: Disable editing library reference + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + And I press "bim.add_library_reference" + And the variable "reference" is "{ifc}.by_type('IfcLibraryReference')[-1].id()" + And I press "bim.enable_editing_library_reference(reference={reference})" + When I press "bim.disable_editing_library_reference()" + Then nothing happens + +Scenario: Edit library reference + Given an empty IFC project + And I press "bim.add_library" + And the variable "library" is "{ifc}.by_type('IfcLibraryInformation')[-1].id()" + And I press "bim.enable_editing_library_references(library={library})" + And I press "bim.add_library_reference" + And the variable "reference" is "{ifc}.by_type('IfcLibraryReference')[-1].id()" + And I press "bim.enable_editing_library_reference(reference={reference})" + When I press "bim.edit_library_reference()" + Then nothing happens diff --git a/src/blenderbim/test/core/bootstrap.py b/src/blenderbim/test/core/bootstrap.py index 2c2c3d7ca2..895cde0335 100644 --- a/src/blenderbim/test/core/bootstrap.py +++ b/src/blenderbim/test/core/bootstrap.py @@ -77,6 +77,13 @@ def geometry(): prophet.verify() +@pytest.fixture +def library(): + prophet = Prophecy(blenderbim.core.tool.Library) + yield prophet + prophet.verify() + + @pytest.fixture def material(): prophet = Prophecy(blenderbim.core.tool.Material) diff --git a/src/blenderbim/test/core/test_library.py b/src/blenderbim/test/core/test_library.py new file mode 100644 index 0000000000..f53f166328 --- /dev/null +++ b/src/blenderbim/test/core/test_library.py @@ -0,0 +1,111 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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.library as subject +from test.core.bootstrap import ifc, library + + +class TestAddLibrary: + def test_run(self, ifc): + ifc.run("library.add_library", name="Unnamed").should_be_called().will_return("library") + assert subject.add_library(ifc) == "library" + + +class TestRemoveLibrary: + def test_run(self, ifc): + ifc.run("library.remove_library", library="library").should_be_called() + subject.remove_library(ifc, library="library") + + +class TestEnableEditingLibraryReferences: + def test_run(self, library): + library.set_editing_mode("REFERENCES").should_be_called() + library.set_active_library("library").should_be_called() + library.import_references("library").should_be_called() + subject.enable_editing_library_references(library, library="library") + + +class TestDisableEditingLibraryReferences: + def test_run(self, library): + library.clear_editing_mode().should_be_called() + subject.disable_editing_library_references(library) + + +class TestEnableEditingLibrary: + def test_run(self, library): + library.set_editing_mode("LIBRARY").should_be_called() + library.get_active_library().should_be_called().will_return("library") + library.import_library_attributes("library").should_be_called() + subject.enable_editing_library(library) + + +class TestDisableEditingLibrary: + def test_run(self, library): + library.set_editing_mode("REFERENCES").should_be_called() + subject.disable_editing_library(library) + + +class TestEditLibrary: + def test_run(self, ifc, library): + library.set_editing_mode("REFERENCES").should_be_called() + library.get_active_library().should_be_called().will_return("library") + library.export_library_attributes().should_be_called().will_return("attributes") + ifc.run("library.edit_library", library="library", attributes="attributes").should_be_called() + library.import_references("library").should_be_called() + subject.edit_library(ifc, library) + + +class TestAddLibraryReference: + def test_run(self, ifc, library): + library.get_active_library().should_be_called().will_return("library") + ifc.run("library.add_reference", library="library").should_be_called() + library.import_references("library").should_be_called() + subject.add_library_reference(ifc, library) + + +class TestRemoveLibraryReference: + def test_run(self, ifc, library): + ifc.run("library.remove_reference", reference="reference").should_be_called() + library.get_active_library().should_be_called().will_return("library") + library.import_references("library").should_be_called() + subject.remove_library_reference(ifc, library, reference="reference") + + +class TestEnableEditingLibraryReference: + def test_run(self, library): + library.set_editing_mode("REFERENCE").should_be_called() + library.set_active_reference("reference").should_be_called() + library.import_reference_attributes("reference").should_be_called() + subject.enable_editing_library_reference(library, reference="reference") + + +class TestDisableEditingLibraryReference: + def test_run(self, library): + library.set_editing_mode("REFERENCES").should_be_called() + subject.disable_editing_library_reference(library) + + +class TestEditLibraryReference: + def test_run(self, ifc, library): + library.set_editing_mode("REFERENCES").should_be_called() + library.get_active_reference().should_be_called().will_return("reference") + library.export_reference_attributes().should_be_called().will_return("attributes") + ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called() + library.get_active_library().should_be_called().will_return("library") + library.import_references("library").should_be_called() + subject.edit_library_reference(ifc, library) diff --git a/src/blenderbim/test/tool/test_library.py b/src/blenderbim/test/tool/test_library.py new file mode 100644 index 0000000000..0f254f6a29 --- /dev/null +++ b/src/blenderbim/test/tool/test_library.py @@ -0,0 +1,134 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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 +import blenderbim.core.tool +import blenderbim.tool as tool +from test.bim.bootstrap import NewFile +from blenderbim.tool.library import Library as subject + + +class TestImplementsTool(NewFile): + def test_run(self): + assert isinstance(subject(), blenderbim.core.tool.Library) + + +class TestClearEditingMode(NewFile): + def test_run(self): + props = bpy.context.scene.BIMLibraryProperties + props.editing_mode = "foo" + subject.clear_editing_mode() + assert props.editing_mode == "" + + +class TestExportLibraryAttributes(NewFile): + def test_run(self): + TestImportLibraryAttributes().test_run() + assert subject.export_library_attributes() == { + "Name": "Name", + "Version": "Version", + "VersionDate": "VersionDate", + "Location": "Location", + "Description": "Description", + } + + +class TestExportReferenceAttributes(NewFile): + def test_run(self): + TestImportReferenceAttributes().test_run() + assert subject.export_reference_attributes() == { + "Location": "Location", + "Identification": "Identification", + "Name": "Name", + "Description": "Description", + "Language": "Language", + } + + +class TestGetActiveLibrary(NewFile): + def test_run(self): + TestSetActiveLibrary().test_run() + assert subject.get_active_library().is_a("IfcLibraryInformation") + + +class TestGetActiveReference(NewFile): + def test_run(self): + TestSetActiveReference().test_run() + assert subject.get_active_reference().is_a("IfcLibraryReference") + + +class TestImportLibraryAttributes(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + library = ifc.createIfcLibraryInformation("Name", "Version", None, "VersionDate", "Location", "Description") + subject.import_library_attributes(library) + props = bpy.context.scene.BIMLibraryProperties + assert props.library_attributes.get("Name").string_value == "Name" + assert props.library_attributes.get("Version").string_value == "Version" + assert props.library_attributes.get("VersionDate").string_value == "VersionDate" + assert props.library_attributes.get("Location").string_value == "Location" + assert props.library_attributes.get("Description").string_value == "Description" + + +class TestImportReferenceAttributes(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + reference = ifc.createIfcLibraryReference("Location", "Identification", "Name", "Description", "Language") + subject.import_reference_attributes(reference) + props = bpy.context.scene.BIMLibraryProperties + assert props.reference_attributes.get("Location").string_value == "Location" + assert props.reference_attributes.get("Identification").string_value == "Identification" + assert props.reference_attributes.get("Name").string_value == "Name" + assert props.reference_attributes.get("Description").string_value == "Description" + assert props.reference_attributes.get("Language").string_value == "Language" + + +class TestImportReferences(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + library = ifc.createIfcLibraryInformation() + reference = ifc.createIfcLibraryReference(Name="Reference", ReferencedLibrary=library) + subject.import_references(library) + props = bpy.context.scene.BIMLibraryProperties + assert props.references[0].ifc_definition_id == reference.id() + assert props.references[0].name == "Reference" + + +class TestSetActiveLibrary(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc.set(ifc) + library = ifc.createIfcLibraryInformation() + subject.set_active_library(library) + assert bpy.context.scene.BIMLibraryProperties.active_library_id == library.id() + + +class TestSetActiveReference(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc.set(ifc) + reference = ifc.createIfcLibraryReference() + subject.set_active_reference(reference) + assert bpy.context.scene.BIMLibraryProperties.active_reference_id == reference.id() + + +class TestSetEditingMode(NewFile): + def test_run(self): + subject.set_editing_mode("FOO") + assert bpy.context.scene.BIMLibraryProperties.editing_mode == "FOO"