From 89023df4e588322d61e5f5f0bb08dcab782707f0 Mon Sep 17 00:00:00 2001 From: jakob-beetz Date: Fri, 7 Jan 2022 09:37:19 +0100 Subject: [PATCH 01/19] Update readme.md --- win/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win/readme.md b/win/readme.md index e8e4c5107a..11b127e1f0 100644 --- a/win/readme.md +++ b/win/readme.md @@ -68,7 +68,7 @@ Using the official Open CASCADE release instead of community edition Before building the dependencies, enable the OCCT usage: ``` > set IFCOS_USE_OCCT=TRUE -> buid-deps.cmd +> build-deps.cmd ``` Please note that this option is not yet available in the MSYS build scripts. @@ -80,7 +80,7 @@ Let's say you have already installed 64-bit Python 3.5.1 to `C:\Python3`. Before building the dependencies, disable the script from installing Python: ``` > set IFCOS_INSTALL_PYTHON=FALSE -> buid-deps.cmd +> build-deps.cmd ``` After building the dependencies, append Python version and installation directory information to the BuildDepsCache file From 662cd6232c46217c4900ff3aebf032ecd2703f28 Mon Sep 17 00:00:00 2001 From: carlos Date: Wed, 29 Jun 2022 17:37:42 +0200 Subject: [PATCH 02/19] Preventing edge case (vertical slabs & horizontal walls) --- .../blenderbim/bim/module/model/slab.py | 19 ++++++++++++++++++- .../blenderbim/bim/module/model/wall.py | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index 622f447e83..327ab18c18 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -166,7 +166,7 @@ def calculate_quantities(usecase_path, ifc_file, settings): obj = settings["blender_object"] product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id) parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") - if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3": + if not parametric or "Engine" not in parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3": return qto = ifcopenshell.api.run( "pset.add_qto", ifc_file, should_run_listeners=False, product=product, name="Qto_SlabBaseQuantities" @@ -342,6 +342,23 @@ class DumbSlabPlaner: return new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers]) material = ifcopenshell.util.element.get_material(settings["related_object"]) + + relating_type = settings["relating_type"] + if hasattr(relating_type, "HasPropertySets"): + psets = relating_type.HasPropertySets + if psets is not None: + for pset in psets: + if hasattr(pset, "HasProperties"): + pset_props = pset.HasProperties + if pset_props is not None: + for prop in pset_props: + if prop.Name == "LayerSetDirection": + if hasattr(prop, "NominalValue"): + nominal_value = prop.NominalValue + if hasattr(nominal_value, "wrappedValue"): + if nominal_value.wrappedValue == "AXIS2": + return + if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS3": self.change_thickness(settings["related_object"], new_thickness) diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index 368c907350..441c6abd52 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -861,7 +861,7 @@ def calculate_quantities(usecase_path, ifc_file, settings): obj = settings["blender_object"] product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id) parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") - if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2": + if not parametric or "Engine" not in parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2": return qto = ifcopenshell.api.run( "pset.add_qto", ifc_file, should_run_listeners=False, product=product, name="Qto_WallBaseQuantities" @@ -941,6 +941,23 @@ class DumbWallPlaner: return new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers]) material = ifcopenshell.util.element.get_material(settings["related_object"]) + + relating_type = settings["relating_type"] + if hasattr(relating_type, "HasPropertySets"): + psets = relating_type.HasPropertySets + if psets is not None: + for pset in psets: + if hasattr(pset, "HasProperties"): + pset_props = pset.HasProperties + if pset_props is not None: + for prop in pset_props: + if prop.Name == "LayerSetDirection": + if hasattr(prop, "NominalValue"): + nominal_value = prop.NominalValue + if hasattr(nominal_value, "wrappedValue"): + if nominal_value.wrappedValue == "AXIS3": + return + if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS2": self.change_thickness(settings["related_object"], new_thickness) From 319f63c86179ec5dc2ee68cfdcf88b2b39ee6461 Mon Sep 17 00:00:00 2001 From: carlos Date: Wed, 29 Jun 2022 17:39:40 +0200 Subject: [PATCH 03/19] Preventing error when IfcSpatialElement has been deleted and an IFC Type instance is added --- src/blenderbim/blenderbim/bim/module/model/mep.py | 10 +++++++++- src/blenderbim/blenderbim/bim/module/model/profile.py | 10 +++++++++- src/blenderbim/blenderbim/bim/module/model/slab.py | 10 +++++++++- src/blenderbim/blenderbim/bim/module/model/wall.py | 10 +++++++++- src/blenderbim/blenderbim/tool/collector.py | 3 ++- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/mep.py b/src/blenderbim/blenderbim/bim/module/model/mep.py index 3fe07c3fe8..571ea0a3cf 100644 --- a/src/blenderbim/blenderbim/bim/module/model/mep.py +++ b/src/blenderbim/blenderbim/bim/module/model/mep.py @@ -114,5 +114,13 @@ class MepGenerator: tool.Ifc.run("system.assign_port", element=element, port=port) tool.Ifc.run("geometry.edit_object_placement", product=port, matrix=obj.matrix_world @ mat, is_si=True) - obj.select_set(True) + try: + obj.select_set(True) + except RuntimeError: + def msg(self, context): + txt = "The created object could not be assigned to a collection. " + txt += "Has any IfcSpatialElement been deleted?" + self.layout.label(text=txt) + + bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR") return obj diff --git a/src/blenderbim/blenderbim/bim/module/model/profile.py b/src/blenderbim/blenderbim/bim/module/model/profile.py index 1c3e6bbe26..b074ef6dac 100644 --- a/src/blenderbim/blenderbim/bim/module/model/profile.py +++ b/src/blenderbim/blenderbim/bim/module/model/profile.py @@ -162,7 +162,15 @@ class DumbProfileGenerator: pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric") ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbProfile"}) MaterialData.load(self.file) - obj.select_set(True) + try: + obj.select_set(True) + except RuntimeError: + def msg(self, context): + txt = "The created object could not be assigned to a collection. " + txt += "Has any IfcSpatialElement been deleted?" + self.layout.label(text=txt) + + bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR") return obj diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index 327ab18c18..6a96490d18 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -306,7 +306,15 @@ class DumbSlabGenerator: pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric") ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer3"}) MaterialData.load(self.file) - obj.select_set(True) + try: + obj.select_set(True) + except RuntimeError: + def msg(self, context): + txt = "The created object could not be assigned to a collection. " + txt += "Has any IfcSpatialElement been deleted?" + self.layout.label(text=txt) + + bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR") return obj diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index 441c6abd52..b3662208c1 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -817,7 +817,15 @@ class DumbWallGenerator: pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric") ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer2"}) MaterialData.load(self.file) - obj.select_set(True) + try: + obj.select_set(True) + except RuntimeError: + def msg(self, context): + txt = "The created object could not be assigned to a collection. " + txt += "Has any IfcSpatialElement been deleted?" + self.layout.label(text=txt) + + bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR") return obj diff --git a/src/blenderbim/blenderbim/tool/collector.py b/src/blenderbim/blenderbim/tool/collector.py index e202ab50cb..8b08e14f0b 100644 --- a/src/blenderbim/blenderbim/tool/collector.py +++ b/src/blenderbim/blenderbim/tool/collector.py @@ -82,7 +82,8 @@ class Collector(blenderbim.core.tool.Collector): if obj.users_collection != (object_collection,): for collection in obj.users_collection: collection.objects.unlink(obj) - object_collection.objects.link(obj) + if object_collection is not None: + object_collection.objects.link(obj) if collection_collection and collection_collection.children.find(object_collection.name) == -1: if bpy.context.scene.collection.children.find(object_collection.name) != -1: From 7321138d576b801054c213a8ea55cd5ffc1b3c03 Mon Sep 17 00:00:00 2001 From: carlos Date: Wed, 29 Jun 2022 17:43:28 +0200 Subject: [PATCH 04/19] Adding a preview popup for IFC Type additions --- .../blenderbim/bim/module/model/__init__.py | 7 +- .../blenderbim/bim/module/model/data.py | 90 +++++++++++++++++-- .../blenderbim/bim/module/model/product.py | 81 ++++++++++++++++- .../blenderbim/bim/module/model/prop.py | 50 ++++++++--- .../blenderbim/bim/module/model/workspace.py | 14 +-- 5 files changed, 207 insertions(+), 35 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index c226210dae..5a43a63b56 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -17,11 +17,13 @@ # along with BlenderBIM Add-on. If not, see . import bpy -from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, workspace +from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, workspace, profile classes = ( product.AddEmptyType, product.AddTypeInstance, + product.DisplayIFCTypes, + product.AddIFCTypeInstance, product.AlignProduct, product.DynamicallyVoidProduct, workspace.Hotkey, @@ -32,6 +34,7 @@ classes = ( opening.AddElementOpening, profile.ExtendProfile, prop.BIMModelProperties, + prop.IfcTypeInfo, ui.BIM_PT_authoring, ui.BIM_PT_authoring_architectural, grid.BIM_OT_add_object, @@ -51,6 +54,7 @@ def register(): if not bpy.app.background: bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True) bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties) + bpy.types.Scene.IfcTypeInfo = bpy.props.CollectionProperty(type=prop.IfcTypeInfo) bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button) bpy.types.VIEW3D_MT_mesh_add.append(stair.add_object_button) bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button) @@ -68,6 +72,7 @@ def unregister(): if not bpy.app.background: bpy.utils.unregister_tool(workspace.BimTool) del bpy.types.Scene.BIMModelProperties + del bpy.types.Scene.IfcTypeInfo bpy.app.handlers.load_post.remove(handler.load_post) bpy.types.VIEW3D_MT_mesh_add.remove(grid.add_object_button) bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button) diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index 393fe73273..3048360ab6 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -18,6 +18,10 @@ import bpy import blenderbim.tool as tool +from blenderbim.bim.ifc import IfcStore + + +preview_icon_ids = {} def refresh(): @@ -31,10 +35,23 @@ class AuthoringData: @classmethod def load(cls): cls.is_loaded = True - cls.data = { - "ifc_classes": cls.ifc_classes(), - "relating_types": cls.relating_types(), - } + if not hasattr(cls, "data"): + cls.data = {} + cls.load_ifc_classes() + cls.load_relating_types() + cls.load_preview_ifc_types() + + @classmethod + def load_ifc_classes(cls): + cls.data["ifc_classes"] = cls.ifc_classes() + + @classmethod + def load_relating_types(cls): + cls.data["relating_types"] = cls.relating_types() + + @classmethod + def load_preview_ifc_types(cls): + cls.data["preview_ifc_types"] = preview_icon_ids @classmethod def ifc_classes(cls): @@ -49,16 +66,71 @@ class AuthoringData: return results @classmethod - def relating_types(cls): - ifc_classes = cls.ifc_classes() + def ifc_class_entities(cls, ifc_class=None): + ifc_classes = cls.data["ifc_classes"] if not ifc_classes: return [] results = [] - ifc_class = bpy.context.scene.BIMModelProperties.ifc_class + if ifc_class is None: + ifc_class = bpy.context.scene.BIMModelProperties.ifc_class if not ifc_class and ifc_classes: ifc_class = ifc_classes[0][0] if ifc_class: - elements = [(str(e.id()), e.Name, e.Description or "") for e in tool.Ifc.get().by_type(ifc_class)] - results.extend(sorted(elements, key=lambda s: s[1])) + elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: s.Name) + results.extend(elements) return results return [] + + @classmethod + def relating_types(cls, ifc_class=None): + return [(str(e.id()), e.Name, e.Description or "") for e in cls.ifc_class_entities(ifc_class=ifc_class)] + + @classmethod + def assetize_relating_type_from_selection(cls): + props = bpy.context.scene.BIMModelProperties + ifc_class = props.ifc_class + relating_type_id = props.relating_type + ifc_class_occurrences = cls.ifc_class_entities(ifc_class=ifc_class) + ifc_class_occurrences = [entity for entity in ifc_class_occurrences if entity.id() == int(relating_type_id)] + if len(ifc_class_occurrences) == 0: + return + ifc_class_entity = ifc_class_occurrences[0] + obj = tool.Ifc.get_object(ifc_class_entity) + relating_type = ifc_class_entity.Name + to_be_deleted = False + if obj.type == 'EMPTY': + bpy.ops.bim.add_type_instance() + new_obj = bpy.context.selected_objects[-1] + if new_obj is not None: + to_be_deleted = True + obj = new_obj + obj.asset_mark() + obj.asset_generate_preview() + icon_id = obj.preview.icon_id + if ifc_class not in cls.data["preview_ifc_types"]: + cls.data["preview_ifc_types"][ifc_class] = {} + cls.data["preview_ifc_types"][ifc_class][relating_type] = {"icon_id": icon_id, "object": obj} + if to_be_deleted: + for col in obj.users_collection: + col.objects.unlink(obj) + + @staticmethod + def ifc_type_info(ifc_class): + ifc_type_infos = [element for element in bpy.context.scene.IfcTypeInfo if element.ifc_class == ifc_class] + return None if len(ifc_type_infos) == 0 else ifc_type_infos[0] + + @classmethod + def new_ifc_type_instance(cls, ifc_class, relating_type_id): + props = bpy.context.scene.BIMModelProperties + props.ifc_class = ifc_class + props.relating_type = str(relating_type_id) + bpy.ops.bim.add_type_instance() + + @staticmethod + def relating_type_name_by_id(ifc_class, relating_type_id): + file = IfcStore.get_file() + try: + ifc_class_entity = file.by_id(int(relating_type_id)) + except (RuntimeError, ValueError): + return None + return ifc_class_entity.Name if ifc_class_entity.is_a() == ifc_class else None diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index b56712216d..f18b748cf8 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -29,6 +29,7 @@ import blenderbim.core.type import blenderbim.core.geometry from . import wall, slab, profile, mep from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.model.data import AuthoringData from ifcopenshell.api.pset.data import Data as PsetData from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper @@ -152,7 +153,8 @@ class AddTypeInstance(bpy.types.Operator): context.view_layer.objects.active = obj return {"FINISHED"} - def generate_layered_element(self, ifc_class, relating_type): + @staticmethod + def generate_layered_element(ifc_class, relating_type): layer_set_direction = None parametric = ifcopenshell.util.element.get_psets(relating_type).get("EPset_Parametric") @@ -174,6 +176,83 @@ class AddTypeInstance(bpy.types.Operator): pass # Dumb block generator? Eh? :) +class DisplayIFCTypes(bpy.types.Operator): + bl_idname = "bim.display_ifc_types" + bl_label = "Browse IFC Types" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Display all possible IFC types for new instances" + + def execute(self, context): + bpy.ops.object.mode_set(mode="OBJECT") + return {"FINISHED"} + + def invoke(self, context, event): + if not AuthoringData.is_loaded: + AuthoringData.load() + props = context.scene.BIMModelProperties + props.relating_type = props.relating_type + min_width = 250 + width = max([min_width, int(context.region.width / 7)]) + return context.window_manager.invoke_popup(self, width=width) + + def draw(self, context): + layout = self.layout + props = context.scene.BIMModelProperties + split = layout.split(align=True, factor=0.6) + col = split.column(align=True) + row = col.row() + row.label(text="Select IFC Type to add:") + col.row().separator(factor=2) + enabled = True + if AuthoringData.data["ifc_classes"]: + row = col.row() + row.prop(data=props, property="ifc_class", text="", icon="FILE_VOLUME") + col.row().separator() + else: + enabled = False + if AuthoringData.data["relating_types"]: + row = col.row() + row.prop(data=props, property="relating_type", text="", icon="FILE_3D") + col.row().separator() + else: + enabled = False + col.row().separator(factor=4) + row = col.row() + op = row.operator("bim.add_ifc_type_instance", icon="ADD") + row.enabled = enabled + op.ifc_class = props.ifc_class + op.relating_type_id = props.relating_type + col = split.column() + box = col.box() + if enabled: + box.template_icon(icon_value=props.icon_id, scale=6.) + + +class AddIFCTypeInstance(bpy.types.Operator): + bl_idname = "bim.add_ifc_type_instance" + bl_label = "Add" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Add an instance of this IFC type" + ifc_class: bpy.props.StringProperty() + relating_type_id: bpy.props.StringProperty() + + def close_panel(self, event): + x, y = event.mouse_x, event.mouse_y + bpy.context.window.cursor_warp(10, 10) + move_back = lambda: bpy.context.window.cursor_warp(x, y) + bpy.app.timers.register(move_back, first_interval=0.001) + + def invoke(self, context, event): + self.close_panel(event) + return self.execute(context) + + def execute(self, context): + props = context.scene.BIMModelProperties + props.ifc_class, props.relating_type = self.ifc_class, self.relating_type_id + bpy.ops.bim.add_type_instance() + return {'FINISHED'} + + class AlignProduct(bpy.types.Operator): bl_idname = "bim.align_product" bl_label = "Align Product" diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 1cbe8eb3df..5da1e67535 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -21,17 +21,8 @@ import ifcopenshell.util.type from blenderbim.bim.module.model.data import AuthoringData from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.ifc import IfcStore +import blenderbim.tool as tool from bpy.types import PropertyGroup -from bpy.props import ( - PointerProperty, - StringProperty, - EnumProperty, - BoolProperty, - IntProperty, - FloatProperty, - FloatVectorProperty, - CollectionProperty, -) def get_ifc_class(self, context): @@ -46,15 +37,50 @@ def get_relating_type(self, context): return AuthoringData.data["relating_types"] +def update_icon_id(self, context): + ifc_class = self.ifc_class + relating_type_id = self.relating_type + relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id) + if ((ifc_class not in AuthoringData.data["preview_ifc_types"] + or relating_type not in AuthoringData.data["preview_ifc_types"][ifc_class]) + and relating_type is not None): + AuthoringData.assetize_relating_type_from_selection() + props = bpy.context.scene.BIMModelProperties + props.icon_id = AuthoringData.data["preview_ifc_types"][ifc_class][relating_type]["icon_id"] + + def update_ifc_class(self, context): - AuthoringData.is_loaded = False + AuthoringData.load_ifc_classes() + AuthoringData.load_relating_types() + self.relating_type = AuthoringData.data["relating_types"][0][0] + update_icon_id(self, context) + + +def update_relating_type(self, context): + AuthoringData.load_relating_types() + update_icon_id(self, context) class BIMModelProperties(PropertyGroup): ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class) - relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type") + relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type", update=update_relating_type) + icon_id: bpy.props.IntProperty() occurrence_name_style: bpy.props.EnumProperty( items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], name="Occurrence Name Style", ) occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") + + +def get_ifc_type_info_relating_types(self, context): + ifc_class = self.ifc_class + return AuthoringData.relating_types(ifc_class=ifc_class) + + +class IfcTypeInfo(PropertyGroup): + ifc_class: bpy.props.StringProperty(name="IFC class", description="IFC class") + relating_type: bpy.props.EnumProperty( + name="Relating type", description="Relating type", items=get_ifc_type_info_relating_types + ) + + diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 20239c2855..1f65486c12 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -37,7 +37,6 @@ class BimTool(WorkSpaceTool): bl_keymap = ( # ("bim.wall_tool_op", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}), # ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": []}), - ("bim.hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}), ("bim.hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}), ("bim.join_wall", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("join_type", "L")]}), ("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}), @@ -61,20 +60,14 @@ class BimTool(WorkSpaceTool): return props = context.scene.BIMModelProperties if AuthoringData.data["ifc_classes"]: - row.prop(props, "ifc_class", text="") + row.operator("bim.display_ifc_types", icon="COLLAPSEMENU") else: row.label(text="No IFC Class") - if AuthoringData.data["relating_types"]: - row.prop(props, "relating_type", text="") - else: + if not AuthoringData.data["relating_types"]: row.label(text="No Relating Type") row.label(text="", icon="BLANK1") - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="Add Type Instance", icon="EVENT_A") - if AuthoringData.data["ifc_classes"]: if props.ifc_class == "IfcWallType": row = layout.row() @@ -155,9 +148,6 @@ class Hotkey(bpy.types.Operator): getattr(self, f"hotkey_{self.hotkey}")() return {"FINISHED"} - def hotkey_S_A(self): - bpy.ops.bim.add_type_instance() - def hotkey_S_C(self): if self.has_ifc_class and self.props.ifc_class == "IfcWallType": bpy.ops.bim.align_wall(align_type="CENTERLINE") From 618bed38890c3a172ebd0a591763bd3dfd1ac2c1 Mon Sep 17 00:00:00 2001 From: carlos Date: Sun, 10 Jul 2022 07:46:30 +0200 Subject: [PATCH 05/19] Improved version with optional cascading --- .../blenderbim/bim/module/model/__init__.py | 4 +- .../blenderbim/bim/module/model/data.py | 66 +++-- .../blenderbim/bim/module/model/product.py | 253 +++++++++++++++--- .../blenderbim/bim/module/model/prop.py | 42 ++- .../blenderbim/bim/module/model/ui.py | 41 +-- .../blenderbim/bim/module/model/workspace.py | 53 +++- 6 files changed, 349 insertions(+), 110 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 5a43a63b56..3aecc33fc7 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -23,7 +23,8 @@ classes = ( product.AddEmptyType, product.AddTypeInstance, product.DisplayIFCTypes, - product.AddIFCTypeInstance, + product.SelectTypeInstance, + product.TypeInstanceHelp, product.AlignProduct, product.DynamicallyVoidProduct, workspace.Hotkey, @@ -36,7 +37,6 @@ classes = ( prop.BIMModelProperties, prop.IfcTypeInfo, ui.BIM_PT_authoring, - ui.BIM_PT_authoring_architectural, grid.BIM_OT_add_object, stair.BIM_OT_add_object, opening.BIM_OT_add_object, diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index 3048360ab6..13dd02d98b 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -31,6 +31,7 @@ def refresh(): class AuthoringData: data = {} is_loaded = False + updating = False @classmethod def load(cls): @@ -85,22 +86,33 @@ class AuthoringData: def relating_types(cls, ifc_class=None): return [(str(e.id()), e.Name, e.Description or "") for e in cls.ifc_class_entities(ifc_class=ifc_class)] + @staticmethod + def new_ifc_type_info(ifc_class): + ifc_type_info = bpy.context.scene.IfcTypeInfo.add() + ifc_type_info.name = ifc_class + return ifc_type_info + @classmethod - def assetize_relating_type_from_selection(cls): - props = bpy.context.scene.BIMModelProperties - ifc_class = props.ifc_class - relating_type_id = props.relating_type - ifc_class_occurrences = cls.ifc_class_entities(ifc_class=ifc_class) - ifc_class_occurrences = [entity for entity in ifc_class_occurrences if entity.id() == int(relating_type_id)] - if len(ifc_class_occurrences) == 0: - return - ifc_class_entity = ifc_class_occurrences[0] - obj = tool.Ifc.get_object(ifc_class_entity) + def assetize_ifc_class(cls, ifc_class=None): + if ifc_class is None: + props = bpy.context.scene.BIMModelProperties + ifc_class = props.ifc_class + ifc_type_info = cls.ifc_type_info(ifc_class) + _ = cls.new_ifc_type_info(ifc_class) if ifc_type_info is None else ifc_type_info + ifc_class_occurrences = cls.ifc_class_entities(ifc_class) + for ifc_class_entity in ifc_class_occurrences: + obj = tool.Ifc.get_object(ifc_class_entity) + cls.assetize_object(obj, ifc_class, ifc_class_entity) + ifc_type_info = cls.ifc_type_info(ifc_class) + ifc_type_info.fully_loaded = True + + @classmethod + def assetize_object(cls, obj, ifc_class, ifc_class_entity, from_selection=False): relating_type = ifc_class_entity.Name to_be_deleted = False if obj.type == 'EMPTY': - bpy.ops.bim.add_type_instance() - new_obj = bpy.context.selected_objects[-1] + kwargs = {} if from_selection else {'ifc_class': ifc_class, 'relating_type_id': ifc_class_entity.id()} + new_obj = cls.new_ifc_type_instance(**kwargs) if new_obj is not None: to_be_deleted = True obj = new_obj @@ -114,17 +126,37 @@ class AuthoringData: for col in obj.users_collection: col.objects.unlink(obj) + @classmethod + def assetize_relating_type_from_selection(cls): + props = bpy.context.scene.BIMModelProperties + ifc_class = props.ifc_class + relating_type_id = props.relating_type + ifc_class_occurrences = cls.ifc_class_entities(ifc_class=ifc_class) + ifc_class_occurrences = [entity for entity in ifc_class_occurrences if entity.id() == int(relating_type_id)] + if len(ifc_class_occurrences) == 0: + return False + ifc_class_entity = ifc_class_occurrences[0] + obj = tool.Ifc.get_object(ifc_class_entity) + if obj is None: + return False + cls.assetize_object(obj, ifc_class, ifc_class_entity, from_selection=True) + return True + @staticmethod def ifc_type_info(ifc_class): - ifc_type_infos = [element for element in bpy.context.scene.IfcTypeInfo if element.ifc_class == ifc_class] + ifc_type_infos = [element for element in bpy.context.scene.IfcTypeInfo if element.name == ifc_class] return None if len(ifc_type_infos) == 0 else ifc_type_infos[0] @classmethod - def new_ifc_type_instance(cls, ifc_class, relating_type_id): - props = bpy.context.scene.BIMModelProperties - props.ifc_class = ifc_class - props.relating_type = str(relating_type_id) + def new_ifc_type_instance(cls, ifc_class=None, relating_type_id=None): + if ifc_class is not None: + cls.updating = True + props = bpy.context.scene.BIMModelProperties + props.ifc_class = ifc_class + props.relating_type = str(relating_type_id) + cls.updating = False bpy.ops.bim.add_type_instance() + return bpy.context.selected_objects[-1] @staticmethod def relating_type_name_by_id(ifc_class, relating_type_id): diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 34a5f665c6..3e8e5c5dd1 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -17,6 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import math import mathutils import ifcopenshell import ifcopenshell.api @@ -30,9 +31,11 @@ import blenderbim.core.geometry from . import wall, slab, profile, mep from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.data import AuthoringData +from blenderbim.bim.helper import prop_with_search from ifcopenshell.api.pset.data import Data as PsetData from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper +from . import prop class AddEmptyType(bpy.types.Operator, AddObjectHelper): @@ -54,13 +57,26 @@ def add_empty_type_button(self, context): self.layout.operator(AddEmptyType.bl_idname, icon="FILE_3D") +def close_operator_panel(event): + x, y = event.mouse_x, event.mouse_y + bpy.context.window.cursor_warp(10, 10) + move_back = lambda: bpy.context.window.cursor_warp(x, y) + bpy.app.timers.register(move_back, first_interval=0.001) + + class AddTypeInstance(bpy.types.Operator): bl_idname = "bim.add_type_instance" - bl_label = "Add Type Instance" + bl_label = "Add" bl_options = {"REGISTER", "UNDO"} - bl_description = "Add the selected Type Instance to the model" + bl_description = "Add Type Instance to the model" ifc_class: bpy.props.StringProperty() relating_type: bpy.props.IntProperty() + from_invoke: bpy.props.BoolProperty(default=False) + + def invoke(self, context, event): + if self.from_invoke: + close_operator_panel(event) + return self.execute(context) def execute(self, context): return IfcStore.execute_ifc_operator(self, context) @@ -176,7 +192,7 @@ class AddTypeInstance(bpy.types.Operator): class DisplayIFCTypes(bpy.types.Operator): bl_idname = "bim.display_ifc_types" - bl_label = "Browse IFC Types" + bl_label = "Browse IFC Construction Types" bl_options = {"REGISTER", "UNDO"} bl_description = "Display all possible IFC types for new instances" @@ -188,67 +204,232 @@ class DisplayIFCTypes(bpy.types.Operator): if not AuthoringData.is_loaded: AuthoringData.load() props = context.scene.BIMModelProperties - props.relating_type = props.relating_type + if props.unfold_relating_type: + ifc_class = props.ifc_class + ifc_type_info = AuthoringData.ifc_type_info(ifc_class) + if ifc_type_info is None or not ifc_type_info.fully_loaded: + AuthoringData.assetize_ifc_class(ifc_class) + else: + prop.update_relating_type(props, context) min_width = 250 - width = max([min_width, int(context.region.width / 7)]) + width_scaling = 6. ** -1 + width = max([min_width, int(width_scaling * context.region.width)]) return context.window_manager.invoke_popup(self, width=width) def draw(self, context): - layout = self.layout props = context.scene.BIMModelProperties + if props.unfold_relating_type: + self.draw_by_class(props) + else: + self.draw_by_class_and_relating_type(props) + + def draw_header(self, props): + layout = self.layout split = layout.split(align=True, factor=0.6) - col = split.column(align=True) - row = col.row() - row.label(text="Select IFC Type to add:") - col.row().separator(factor=2) + col1 = split.column(align=True) + row = col1.row() + row.prop(data=props, property="unfold_relating_type", text="Preview All Relating Types") + col1.row().separator(factor=0.5) + row = col1.row() + row.label(text="Select IFC Construction Type:") + col1.row().separator(factor=2) enabled = True if AuthoringData.data["ifc_classes"]: - row = col.row() - row.prop(data=props, property="ifc_class", text="", icon="FILE_VOLUME") - col.row().separator() + row = col1.row() + row.label(text="", icon="FILE_VOLUME") + prop_with_search(row, props, "ifc_class", text="") + col1.row().separator() else: enabled = False + col2 = split.column(align=True) + subsplit = col2.split(factor=0.9) + subcol = [subsplit.column() for _ in range(2)][-1] + subcol.operator("bim.type_instance_help", text="", icon="QUESTION") + col2.row().separator(factor=1) + return {"enabled": enabled, "layout": layout, "col1": col1, "col2": col2} + + def draw_by_class(self, props): + header_data = self.draw_header(props) + enabled, layout = [header_data[key] for key in ["enabled", "layout"]] + ifc_class = props.ifc_class + num_cols = 3 + layout.row().separator(factor=0.25) + flow = layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True) + relating_types = AuthoringData.relating_types() + num_types = len(relating_types) + for idx, (rt_id, name, desc) in enumerate(relating_types): + outer_col = flow.column() + box = outer_col.box() + row = box.row() + row.label(text=name, icon="FILE_3D") + row.alignment = "CENTER" + row = box.row() + if enabled: + preview_ifc_types = AuthoringData.data["preview_ifc_types"] + if ifc_class in preview_ifc_types: + preview_ifc_class = preview_ifc_types[ifc_class] + if name in preview_ifc_class: + icon_id = preview_ifc_class[name]["icon_id"] + row.template_icon(icon_value=icon_id, scale=6.) + outer_col.row().separator(factor=0.5) + row = outer_col.row() + split = row.split(factor=0.5) + col = split.column() + op = col.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") + op.ifc_class = ifc_class + op.relating_type_id = rt_id + col = split.column() + op = col.operator("bim.add_type_instance", icon="ADD") + op.from_invoke = True + op.ifc_class = ifc_class + if rt_id.isnumeric(): + op.relating_type = int(rt_id) + factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 0.5 + outer_col.row().separator(factor=factor) + last_row_cols = num_types % num_cols + if last_row_cols != 0: + for _ in range(num_cols - last_row_cols): + flow.column() + + def draw_by_class_and_relating_type(self, props): + header_data = self.draw_header(props) + enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]] + ifc_class = props.ifc_class if AuthoringData.data["relating_types"]: - row = col.row() - row.prop(data=props, property="relating_type", text="", icon="FILE_3D") - col.row().separator() + row = col1.row() + row.label(text="", icon="FILE_3D") + prop_with_search(row, props, "relating_type", text="") + col1.row().separator() else: enabled = False - col.row().separator(factor=4) - row = col.row() - op = row.operator("bim.add_ifc_type_instance", icon="ADD") + col1.row().separator(factor=4.75) + row = col1.row() row.enabled = enabled - op.ifc_class = props.ifc_class + op = row.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") + op.ifc_class = ifc_class op.relating_type_id = props.relating_type - col = split.column() - box = col.box() + op = row.operator("bim.add_type_instance", icon="ADD") + op.from_invoke = True + op.ifc_class = ifc_class + relating_type = props.relating_type + if relating_type.isnumeric(): + op.relating_type = int(relating_type) + box = col2.box() if enabled: box.template_icon(icon_value=props.icon_id, scale=6.) -class AddIFCTypeInstance(bpy.types.Operator): - bl_idname = "bim.add_ifc_type_instance" - bl_label = "Add" +class SelectTypeInstance(bpy.types.Operator): + bl_idname = "bim.select_type_instance" + bl_label = "Select" bl_options = {"REGISTER", "UNDO"} - bl_description = "Add an instance of this IFC type" + bl_description = "Pick Type Instance as selection for subsequent operations" ifc_class: bpy.props.StringProperty() relating_type_id: bpy.props.StringProperty() - def close_panel(self, event): - x, y = event.mouse_x, event.mouse_y - bpy.context.window.cursor_warp(10, 10) - move_back = lambda: bpy.context.window.cursor_warp(x, y) - bpy.app.timers.register(move_back, first_interval=0.001) - def invoke(self, context, event): - self.close_panel(event) + close_operator_panel(event) return self.execute(context) def execute(self, context): props = context.scene.BIMModelProperties - props.ifc_class, props.relating_type = self.ifc_class, self.relating_type_id - bpy.ops.bim.add_type_instance() - return {'FINISHED'} + if self.ifc_class != "": + props.ifc_class = self.ifc_class + if self.relating_type_id != "": + props.relating_type = self.relating_type_id + return {"FINISHED"} + + +class TypeInstanceHelp(bpy.types.Operator): + bl_idname = "bim.type_instance_help" + bl_label = "IFC Construction Type Help" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Click to read some contextual help" + + def execute(self, context): + return {"FINISHED"} + + def invoke(self, context, event): + return context.window_manager.invoke_popup(self, width=525) + + def draw(self, context): + layout = self.layout + layout.row().separator(factor=0.5) + row = layout.row() + row.alignment = "CENTER" + row.label(text="BlenderBIM Help", icon="BLENDER") + row = layout.row() + row.alignment = "CENTER" + row.label(text="[IFC Construction Type Browser]") + layout.row().separator(factor=0.5) + row = self.col_with_margins(layout.row()).row() + row.label(text="When to use:", icon="KEYTYPE_MOVING_HOLD_VEC") + self.draw_lines(layout, self.message_purpose) + layout.row().separator() + row = self.col_with_margins(layout.row()).row() + row.label(text="Overall workflow:", icon="KEYTYPE_MOVING_HOLD_VEC") + self.draw_lines(layout, self.message_overall) + layout.row().separator() + row = self.col_with_margins(layout.row()).row() + row.label(text="UI panel hints:", icon="KEYTYPE_MOVING_HOLD_VEC") + self.draw_lines(layout, self.message_ui) + layout.row().separator(factor=1.5) + row = self.col_with_margins(layout.row()).row() + row.label(text="Further help:", icon="KEYTYPE_MOVING_HOLD_VEC") + layout.row().separator(factor=0.5) + row = self.col_with_margins(layout).row() + op = row.operator("bim.open_upstream", text="Homepage", icon="HOME") + op.page = "home" + op = row.operator("bim.open_upstream", text="Docs", icon="DOCUMENTS") + op.page = "docs" + op = row.operator("bim.open_upstream", text="Wiki", icon="CURRENT_FILE") + op.page = "wiki" + op = row.operator("bim.open_upstream", text="Community", icon="COMMUNITY") + op.page = "community" + layout.row().separator() + + @staticmethod + def col_with_margins(layout, margin_left=0.025, margin_right=None): + margin_right = margin_left if margin_right is None else margin_right + split = layout.split(factor=margin_left, align=True) + col = [split.column() for _ in range(2)][-1] + subsplit = col.split(factor=(1. - margin_right), align=True) + subcol = subsplit.column() + subsplit.column().label(text="") + return subcol + + def draw_lines(self, layout, lines): + box = self.col_with_margins(layout).box() + for line in lines: + row = box.row() + row.label(text="", icon="RIGHTARROW_THIN") + row.label(text=line) + + @property + def message_purpose(self): + return [ + 'Available IFC Construction Types can be previewed and added through the button', + '"Browse IFC Construction Types", which appears when an IFC Project Library,', + 'containing in turn definitions of construction types, is loaded. In order to ', + 'manage loaded IFC Project Libraries, navigate to [Scene Properties] -> [IFC', + 'Project Setup] -> [IFC Project Library] under the Properties panel.' + ] + + @property + def message_overall(self): + return [ + 'Choose an IFC Construction Type by picking 1) an IFC Class and 2) a Relating Type. ', + 'Then, click on the "Add" button to directly add one instance of the chosen type to', + 'the model, or alternatively click on the "Select" button to be able to later add ', + 'several instances with SHIFT + A.' + ] + + @property + def message_ui(self): + return [ + 'If "Preview All Relating Types" is marked, a preview for every Relating Type will', + 'be shown at once. Not advisable on large projects with dozens of types per class.' + ] class AlignProduct(bpy.types.Operator): diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 5da1e67535..0276c6e13e 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -44,16 +44,25 @@ def update_icon_id(self, context): if ((ifc_class not in AuthoringData.data["preview_ifc_types"] or relating_type not in AuthoringData.data["preview_ifc_types"][ifc_class]) and relating_type is not None): - AuthoringData.assetize_relating_type_from_selection() + if not AuthoringData.assetize_relating_type_from_selection(): + return props = bpy.context.scene.BIMModelProperties props.icon_id = AuthoringData.data["preview_ifc_types"][ifc_class][relating_type]["icon_id"] def update_ifc_class(self, context): - AuthoringData.load_ifc_classes() - AuthoringData.load_relating_types() - self.relating_type = AuthoringData.data["relating_types"][0][0] - update_icon_id(self, context) + if not AuthoringData.updating: + AuthoringData.load_ifc_classes() + AuthoringData.load_relating_types() + props = context.scene.BIMModelProperties + if props.unfold_relating_type: + ifc_class = props.ifc_class + ifc_type_info = AuthoringData.ifc_type_info(ifc_class) + if ifc_type_info is None or not ifc_type_info.fully_loaded: + AuthoringData.assetize_ifc_class(ifc_class) + else: + self.relating_type = AuthoringData.data["relating_types"][0][0] + update_icon_id(self, context) def update_relating_type(self, context): @@ -61,26 +70,45 @@ def update_relating_type(self, context): update_icon_id(self, context) +def update_unfold_relating_type(self, context): + update_ifc_class(self, context) + + # if self.unfold_relating_type: + # props = context.scene.BIMModelProperties + # ifc_class = props.ifc_class + # ifc_type_info = AuthoringData.ifc_type_info(ifc_class) + # if ifc_type_info is None or not ifc_type_info.fully_loaded: + # AuthoringData.assetize_ifc_class(ifc_class) + # else: + # update_ifc_class(self, context) + + class BIMModelProperties(PropertyGroup): ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class) relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type", update=update_relating_type) icon_id: bpy.props.IntProperty() + unfold_relating_type: bpy.props.BoolProperty(update=update_unfold_relating_type) occurrence_name_style: bpy.props.EnumProperty( items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], name="Occurrence Name Style", ) occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") + getter_enum = { + "ifc_class": get_ifc_class, + "relating_type": get_relating_type + } def get_ifc_type_info_relating_types(self, context): - ifc_class = self.ifc_class + ifc_class = self.name return AuthoringData.relating_types(ifc_class=ifc_class) class IfcTypeInfo(PropertyGroup): - ifc_class: bpy.props.StringProperty(name="IFC class", description="IFC class") + name: bpy.props.StringProperty(name="IFC class", description="IFC class") relating_type: bpy.props.EnumProperty( name="Relating type", description="Relating type", items=get_ifc_type_info_relating_types ) + fully_loaded: bpy.props.BoolProperty(default=False) diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 8a198ce7a1..dcdcf6655d 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -16,54 +16,15 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . -import blenderbim.bim.module.type.prop as type_prop from bpy.types import Panel -from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.model.data import AuthoringData class BIM_PT_authoring(Panel): - bl_idname = "BIM_PT_authoring" - bl_label = "Authoring" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "BlenderBIM" - - @classmethod - def poll(cls, context): - return IfcStore.get_file() - - def draw(self, context): - if not AuthoringData.is_loaded: - AuthoringData.load() - - props = context.scene.BIMModelProperties - col = self.layout.column(align=True) - enabled = True - - if AuthoringData.data["ifc_classes"]: - col.prop(props, "ifc_class", text="", icon="FILE_VOLUME") - else: - col.label(text="No IFC Class", icon="FILE_VOLUME") - enabled = False - if AuthoringData.data["relating_types"]: - col.prop(props, "relating_type", text="", icon="FILE_3D") - else: - col.label(text="No Relating Type", icon="FILE_3D") - enabled = False - row = col.row() - row.operator("bim.add_type_instance", icon="ADD") - row.enabled = enabled - - -class BIM_PT_authoring_architectural(Panel): bl_label = "Architectural" - bl_idname = "BIM_PT_authoring_architectural" - bl_options = {"DEFAULT_CLOSED"} + bl_idname = "BIM_PT_authoring" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "BlenderBIM" - bl_parent_id = "BIM_PT_authoring" def draw(self, context): row = self.layout.row(align=True) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 1f65486c12..003ea26aa3 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -37,6 +37,7 @@ class BimTool(WorkSpaceTool): bl_keymap = ( # ("bim.wall_tool_op", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}), # ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": []}), + ("bim.hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}), ("bim.hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}), ("bim.join_wall", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("join_type", "L")]}), ("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}), @@ -54,19 +55,52 @@ class BimTool(WorkSpaceTool): if not AuthoringData.is_loaded and IfcStore.get_file(): AuthoringData.load() + props = context.scene.BIMModelProperties + is_tool_header = context.region.type == "TOOL_HEADER" + ifc_classes = AuthoringData.data["ifc_classes"] + relating_types = AuthoringData.data["relating_types"] + row = layout.row(align=True) if not IfcStore.get_file(): row.label(text="No IFC Project", icon="ERROR") return - props = context.scene.BIMModelProperties - if AuthoringData.data["ifc_classes"]: - row.operator("bim.display_ifc_types", icon="COLLAPSEMENU") - else: - row.label(text="No IFC Class") - if not AuthoringData.data["relating_types"]: - row.label(text="No Relating Type") - row.label(text="", icon="BLANK1") + if is_tool_header: + row.operator("bim.type_instance_help", text="", icon="QUESTION") + + if ifc_classes and is_tool_header: + row.label(text="", icon="BLANK1") + row.operator("bim.display_ifc_types", icon="COLLAPSEMENU") + + ifc_class = props.ifc_class + relating_type = AuthoringData.relating_type_name_by_id(ifc_class, props.relating_type) + + if is_tool_header: + row.label(text="", icon="BLANK1") + row = layout.row(align=True) + row.label(text="", icon="EVENT_SHIFT") + row.label(text="", icon="EVENT_A") + if ifc_classes: + row.label(text=f" Add") + row.label(text="", icon="FILE_VOLUME") + row.label(text=ifc_class) + row.label(text="", icon="FILE_3D") + row.label(text=f"{relating_type} ") + else: + row.label(text=f" Add instance") + else: + txt_ifc_class = ifc_class if ifc_classes else "No IFC Class" + txt_relating_type = relating_type if relating_types else "No Relating Type" + row = layout.row(align=True) + row.label(text="Selected IFC Type:") + row = layout.row(align=True) + row.label(text=txt_ifc_class, icon="FILE_VOLUME") + row = layout.row(align=True) + row.label(text=txt_relating_type, icon="FILE_3D") + row = layout.row(align=True) + row.label(text="", icon="EVENT_SHIFT") + row.label(text="", icon="EVENT_A") + row.label(text=f" Add Type Instance") if AuthoringData.data["ifc_classes"]: if props.ifc_class == "IfcWallType": @@ -148,6 +182,9 @@ class Hotkey(bpy.types.Operator): getattr(self, f"hotkey_{self.hotkey}")() return {"FINISHED"} + def hotkey_S_A(self): + bpy.ops.bim.add_type_instance() + def hotkey_S_C(self): if self.has_ifc_class and self.props.ifc_class == "IfcWallType": bpy.ops.bim.align_wall(align_type="CENTERLINE") From 493faf22ffcc1fbc17f78acfcf001c8e0e102f14 Mon Sep 17 00:00:00 2001 From: carlos Date: Mon, 11 Jul 2022 07:52:10 +0200 Subject: [PATCH 06/19] Move col_with_margins utility to helper.py --- src/blenderbim/blenderbim/bim/helper.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index 6b11944f87..75fa6de74d 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -118,6 +118,17 @@ def prop_with_search(layout, data, prop_name, **kwargs): op.prop_name = prop_name +def col_with_margins(layout, margin_left=0.025, margin_right=None): + margin_right = margin_left if margin_right is None else margin_right + split = layout.split(factor=margin_left, align=True) + cols = [split.column() for _ in range(2)] + cols[0].label(text="") + subsplit = cols[-1].split(factor=(1. - margin_right), align=True) + subcol = subsplit.column() + subsplit.column().label(text="") + return subcol + + class IfcHeaderExtractor: def __init__(self, filepath: str): self.filepath = filepath From f9f9746a9a65a99aa16d57d65a8aa507e07632a9 Mon Sep 17 00:00:00 2001 From: carlos Date: Mon, 11 Jul 2022 07:53:14 +0200 Subject: [PATCH 07/19] Element Type Browser UI refactoring and improved wording --- .../blenderbim/bim/module/model/product.py | 103 +++++++++--------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 3e8e5c5dd1..90c7663171 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -31,7 +31,7 @@ import blenderbim.core.geometry from . import wall, slab, profile, mep from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.data import AuthoringData -from blenderbim.bim.helper import prop_with_search +from blenderbim.bim.helper import prop_with_search, col_with_margins from ifcopenshell.api.pset.data import Data as PsetData from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper @@ -192,7 +192,7 @@ class AddTypeInstance(bpy.types.Operator): class DisplayIFCTypes(bpy.types.Operator): bl_idname = "bim.display_ifc_types" - bl_label = "Browse IFC Construction Types" + bl_label = "Browse Element Types" bl_options = {"REGISTER", "UNDO"} bl_description = "Display all possible IFC types for new instances" @@ -212,7 +212,7 @@ class DisplayIFCTypes(bpy.types.Operator): else: prop.update_relating_type(props, context) min_width = 250 - width_scaling = 6. ** -1 + width_scaling = 5 ** -1 width = max([min_width, int(width_scaling * context.region.width)]) return context.window_manager.invoke_popup(self, width=width) @@ -225,19 +225,21 @@ class DisplayIFCTypes(bpy.types.Operator): def draw_header(self, props): layout = self.layout - split = layout.split(align=True, factor=0.6) + inner_layout = col_with_margins(layout, margin_left=0.004) + inner_layout.row().separator(factor=0.75) + split = inner_layout.split(align=True, factor=2./3) col1 = split.column(align=True) row = col1.row() row.prop(data=props, property="unfold_relating_type", text="Preview All Relating Types") - col1.row().separator(factor=0.5) + col1.row().separator(factor=1) row = col1.row() - row.label(text="Select IFC Construction Type:") - col1.row().separator(factor=2) + row.label(text="Select Element Type:") + col1.row().separator(factor=1.5) enabled = True if AuthoringData.data["ifc_classes"]: - row = col1.row() - row.label(text="", icon="FILE_VOLUME") - prop_with_search(row, props, "ifc_class", text="") + subsplit = col1.split(factor=1./3) + subsplit.column().row().label(text="IfcElementType:", icon="FILE_VOLUME") + prop_with_search(subsplit.column(), props, "ifc_class", text="") col1.row().separator() else: enabled = False @@ -246,7 +248,7 @@ class DisplayIFCTypes(bpy.types.Operator): subcol = [subsplit.column() for _ in range(2)][-1] subcol.operator("bim.type_instance_help", text="", icon="QUESTION") col2.row().separator(factor=1) - return {"enabled": enabled, "layout": layout, "col1": col1, "col2": col2} + return {"enabled": enabled, "layout": inner_layout, "col1": col1, "col2": col2} def draw_by_class(self, props): header_data = self.draw_header(props) @@ -254,6 +256,8 @@ class DisplayIFCTypes(bpy.types.Operator): ifc_class = props.ifc_class num_cols = 3 layout.row().separator(factor=0.25) + layout.row().label(text=f"Available {ifc_class}(s):", icon="FILE_3D") + layout.row().separator(factor=0.25) flow = layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True) relating_types = AuthoringData.relating_types() num_types = len(relating_types) @@ -271,8 +275,8 @@ class DisplayIFCTypes(bpy.types.Operator): if name in preview_ifc_class: icon_id = preview_ifc_class[name]["icon_id"] row.template_icon(icon_value=icon_id, scale=6.) - outer_col.row().separator(factor=0.5) - row = outer_col.row() + box.row().separator(factor=0.25) + row = box.row() split = row.split(factor=0.5) col = split.column() op = col.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") @@ -284,7 +288,8 @@ class DisplayIFCTypes(bpy.types.Operator): op.ifc_class = ifc_class if rt_id.isnumeric(): op.relating_type = int(rt_id) - factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 0.5 + box.row().separator(factor=0.05) + factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 1.5 outer_col.row().separator(factor=factor) last_row_cols = num_types % num_cols if last_row_cols != 0: @@ -296,9 +301,9 @@ class DisplayIFCTypes(bpy.types.Operator): enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]] ifc_class = props.ifc_class if AuthoringData.data["relating_types"]: - row = col1.row() - row.label(text="", icon="FILE_3D") - prop_with_search(row, props, "relating_type", text="") + subsplit = col1.split(factor=1. / 3) + subsplit.column().row().label(text=f"{ifc_class}:", icon="FILE_3D") + prop_with_search(subsplit.column(), props, "relating_type", text="") col1.row().separator() else: enabled = False @@ -314,9 +319,13 @@ class DisplayIFCTypes(bpy.types.Operator): relating_type = props.relating_type if relating_type.isnumeric(): op.relating_type = int(relating_type) - box = col2.box() + col2.row().separator(factor=1.25) + split = col2.split(factor=0.025) + col = [split.column() for _ in range(2)][-1] + box = col.box() if enabled: - box.template_icon(icon_value=props.icon_id, scale=6.) + box.template_icon(icon_value=props.icon_id, scale=5.6) + col1.row().separator(factor=1) class SelectTypeInstance(bpy.types.Operator): @@ -342,7 +351,7 @@ class SelectTypeInstance(bpy.types.Operator): class TypeInstanceHelp(bpy.types.Operator): bl_idname = "bim.type_instance_help" - bl_label = "IFC Construction Type Help" + bl_label = "Element Types Help" bl_options = {"REGISTER", "UNDO"} bl_description = "Click to read some contextual help" @@ -350,7 +359,7 @@ class TypeInstanceHelp(bpy.types.Operator): return {"FINISHED"} def invoke(self, context, event): - return context.window_manager.invoke_popup(self, width=525) + return context.window_manager.invoke_popup(self, width=510) def draw(self, context): layout = self.layout @@ -360,24 +369,24 @@ class TypeInstanceHelp(bpy.types.Operator): row.label(text="BlenderBIM Help", icon="BLENDER") row = layout.row() row.alignment = "CENTER" - row.label(text="[IFC Construction Type Browser]") + row.label(text="[Element Type Browser]") layout.row().separator(factor=0.5) - row = self.col_with_margins(layout.row()).row() + row = col_with_margins(layout.row()).row() row.label(text="When to use:", icon="KEYTYPE_MOVING_HOLD_VEC") self.draw_lines(layout, self.message_purpose) layout.row().separator() - row = self.col_with_margins(layout.row()).row() + row = col_with_margins(layout.row()).row() row.label(text="Overall workflow:", icon="KEYTYPE_MOVING_HOLD_VEC") self.draw_lines(layout, self.message_overall) layout.row().separator() - row = self.col_with_margins(layout.row()).row() + row = col_with_margins(layout.row()).row() row.label(text="UI panel hints:", icon="KEYTYPE_MOVING_HOLD_VEC") self.draw_lines(layout, self.message_ui) layout.row().separator(factor=1.5) - row = self.col_with_margins(layout.row()).row() - row.label(text="Further help:", icon="KEYTYPE_MOVING_HOLD_VEC") + row = col_with_margins(layout.row()).row() + row.label(text="Further support:", icon="KEYTYPE_MOVING_HOLD_VEC") layout.row().separator(factor=0.5) - row = self.col_with_margins(layout).row() + row = col_with_margins(layout).row() op = row.operator("bim.open_upstream", text="Homepage", icon="HOME") op.page = "home" op = row.operator("bim.open_upstream", text="Docs", icon="DOCUMENTS") @@ -388,47 +397,39 @@ class TypeInstanceHelp(bpy.types.Operator): op.page = "community" layout.row().separator() - @staticmethod - def col_with_margins(layout, margin_left=0.025, margin_right=None): - margin_right = margin_left if margin_right is None else margin_right - split = layout.split(factor=margin_left, align=True) - col = [split.column() for _ in range(2)][-1] - subsplit = col.split(factor=(1. - margin_right), align=True) - subcol = subsplit.column() - subsplit.column().label(text="") - return subcol - def draw_lines(self, layout, lines): - box = self.col_with_margins(layout).box() + box = col_with_margins(layout).box() for line in lines: row = box.row() - row.label(text="", icon="RIGHTARROW_THIN") - row.label(text=line) + row.label(text=f" {line}") @property def message_purpose(self): return [ - 'Available IFC Construction Types can be previewed and added through the button', - '"Browse IFC Construction Types", which appears when an IFC Project Library,', - 'containing in turn definitions of construction types, is loaded. In order to ', - 'manage loaded IFC Project Libraries, navigate to [Scene Properties] -> [IFC', - 'Project Setup] -> [IFC Project Library] under the Properties panel.' + 'Available Element Types can be previewed and added through the button "Browse', + 'Element Types", which appears when an IfcProjectLibrary, containing in turn', + 'IfcElementType entities, is loaded. In order to manage loaded libraries, navigate', + 'to [Scene Properties] -> [IFC Project Setup] -> [IFC Project Library], under the', + 'Properties panel.' ] @property def message_overall(self): return [ - 'Choose an IFC Construction Type by picking 1) an IFC Class and 2) a Relating Type. ', - 'Then, click on the "Add" button to directly add one instance of the chosen type to', - 'the model, or alternatively click on the "Select" button to be able to later add ', + 'Choose a certain Element Type by determining:', + ' 1) An existing subtype of IfcElementType.', + ' 2) Its entity name, as stored within the IfcProjectLibrary.', + 'Then, click on the "Add" button to directly add one instance of the chosen type to ', + 'the model, or alternatively click on the "Select" button to be able to later add', 'several instances with SHIFT + A.' ] @property def message_ui(self): return [ - 'If "Preview All Relating Types" is marked, a preview for every Relating Type will', - 'be shown at once. Not advisable on large projects with dozens of types per class.' + 'If "Preview All Relating Types" is marked, previews for every existing entity of', + 'the selected IFC class will be shown at once. Not advisable on large projects, with', + 'dozens of entities for a given IfcElementType.' ] From 3f24d54e8fb90fb9e51b7ac80663dd73421c8c6c Mon Sep 17 00:00:00 2001 From: carlos Date: Thu, 14 Jul 2022 13:40:57 +0200 Subject: [PATCH 08/19] Distinguish bt active and selected constr type + renaming ifc_class -> constr_type --- .../blenderbim/bim/module/model/__init__.py | 8 +- .../blenderbim/bim/module/model/data.py | 169 +++++++++------- .../blenderbim/bim/module/model/product.py | 184 ++++++++---------- .../blenderbim/bim/module/model/prop.py | 118 ++++++----- .../blenderbim/bim/module/model/workspace.py | 37 ++-- 5 files changed, 268 insertions(+), 248 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 3aecc33fc7..2cd8a2c6fe 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -22,7 +22,7 @@ from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, classes = ( product.AddEmptyType, product.AddTypeInstance, - product.DisplayIFCTypes, + product.DisplayConstrTypes, product.SelectTypeInstance, product.TypeInstanceHelp, product.AlignProduct, @@ -35,7 +35,7 @@ classes = ( opening.AddElementOpening, profile.ExtendProfile, prop.BIMModelProperties, - prop.IfcTypeInfo, + prop.ConstrTypeInfo, ui.BIM_PT_authoring, grid.BIM_OT_add_object, stair.BIM_OT_add_object, @@ -54,7 +54,7 @@ def register(): if not bpy.app.background: bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True) bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties) - bpy.types.Scene.IfcTypeInfo = bpy.props.CollectionProperty(type=prop.IfcTypeInfo) + bpy.types.Scene.ConstrTypeInfo = bpy.props.CollectionProperty(type=prop.ConstrTypeInfo) bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button) bpy.types.VIEW3D_MT_mesh_add.append(stair.add_object_button) bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button) @@ -72,7 +72,7 @@ def unregister(): if not bpy.app.background: bpy.utils.unregister_tool(workspace.BimTool) del bpy.types.Scene.BIMModelProperties - del bpy.types.Scene.IfcTypeInfo + del bpy.types.Scene.ConstrTypeInfo bpy.app.handlers.load_post.remove(handler.load_post) bpy.types.VIEW3D_MT_mesh_add.remove(grid.add_object_button) bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button) diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index 13dd02d98b..b27d49963f 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -31,31 +31,35 @@ def refresh(): class AuthoringData: data = {} is_loaded = False - updating = False @classmethod def load(cls): cls.is_loaded = True if not hasattr(cls, "data"): cls.data = {} - cls.load_ifc_classes() - cls.load_relating_types() - cls.load_preview_ifc_types() + cls.load_constr_classes() + cls.load_constr_types() + cls.load_constr_types_browser() + cls.load_preview_constr_types() @classmethod - def load_ifc_classes(cls): - cls.data["ifc_classes"] = cls.ifc_classes() + def load_constr_classes(cls): + cls.data["constr_classes"] = cls.constr_classes() @classmethod - def load_relating_types(cls): - cls.data["relating_types"] = cls.relating_types() + def load_constr_types(cls): + cls.data["constr_types_ids"] = cls.constr_types() @classmethod - def load_preview_ifc_types(cls): - cls.data["preview_ifc_types"] = preview_icon_ids + def load_constr_types_browser(cls): + cls.data["constr_types_ids_browser"] = cls.constr_types_browser() @classmethod - def ifc_classes(cls): + def load_preview_constr_types(cls): + cls.data["preview_constr_types"] = preview_icon_ids + + @classmethod + def constr_classes(cls): results = [] classes = { e.is_a() @@ -67,102 +71,133 @@ class AuthoringData: return results @classmethod - def ifc_class_entities(cls, ifc_class=None): - ifc_classes = cls.data["ifc_classes"] - if not ifc_classes: + def constr_class_entities(cls, constr_class=None): + constr_classes = cls.data["constr_classes"] + if not constr_classes: return [] results = [] - if ifc_class is None: - ifc_class = bpy.context.scene.BIMModelProperties.ifc_class - if not ifc_class and ifc_classes: - ifc_class = ifc_classes[0][0] - if ifc_class: - elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: s.Name) + if constr_class is None: + props = bpy.context.scene.BIMModelProperties + constr_class = props.constr_class + if not constr_class and constr_classes: + constr_class = constr_classes[0][0] + if constr_class: + elements = sorted(tool.Ifc.get().by_type(constr_class), key=lambda s: s.Name) results.extend(elements) return results return [] @classmethod - def relating_types(cls, ifc_class=None): - return [(str(e.id()), e.Name, e.Description or "") for e in cls.ifc_class_entities(ifc_class=ifc_class)] + def constr_types(cls, constr_class=None): + return [ + (str(e.id()), e.Name, e.Description or "") for e in cls.constr_class_entities(constr_class=constr_class) + ] + + @classmethod + def constr_types_browser(cls): + props = bpy.context.scene.BIMModelProperties + return cls.constr_types(constr_class=props.constr_class_browser) @staticmethod - def new_ifc_type_info(ifc_class): - ifc_type_info = bpy.context.scene.IfcTypeInfo.add() - ifc_type_info.name = ifc_class - return ifc_type_info + def new_constr_type_info(constr_class): + constr_type_info = bpy.context.scene.ConstrTypeInfo.add() + constr_type_info.name = constr_class + return constr_type_info @classmethod - def assetize_ifc_class(cls, ifc_class=None): - if ifc_class is None: + def assetize_constr_class(cls, constr_class=None): + if constr_class is None: props = bpy.context.scene.BIMModelProperties - ifc_class = props.ifc_class - ifc_type_info = cls.ifc_type_info(ifc_class) - _ = cls.new_ifc_type_info(ifc_class) if ifc_type_info is None else ifc_type_info - ifc_class_occurrences = cls.ifc_class_entities(ifc_class) - for ifc_class_entity in ifc_class_occurrences: - obj = tool.Ifc.get_object(ifc_class_entity) - cls.assetize_object(obj, ifc_class, ifc_class_entity) - ifc_type_info = cls.ifc_type_info(ifc_class) - ifc_type_info.fully_loaded = True + constr_class = props.constr_class + constr_type_info = cls.constr_type_info(constr_class) + _ = cls.new_constr_type_info(constr_class) if constr_type_info is None else constr_type_info + constr_class_occurrences = cls.constr_class_entities(constr_class) + for constr_class_entity in constr_class_occurrences: + preview_constr_types = cls.data["preview_constr_types"] + + ### handle asset regeneration when library entity is updated ¿? + if (constr_class not in preview_constr_types + or str(constr_class_entity.id()) not in preview_constr_types[constr_class]): + obj = tool.Ifc.get_object(constr_class_entity) + cls.assetize_object(obj, constr_class, constr_class_entity) + constr_type_info = cls.constr_type_info(constr_class) + constr_type_info.fully_loaded = True @classmethod - def assetize_object(cls, obj, ifc_class, ifc_class_entity, from_selection=False): - relating_type = ifc_class_entity.Name + def assetize_object(cls, obj, constr_class, constr_class_entity, from_selection=False): + constr_type_id = constr_class_entity.id() to_be_deleted = False if obj.type == 'EMPTY': - kwargs = {} if from_selection else {'ifc_class': ifc_class, 'relating_type_id': ifc_class_entity.id()} - new_obj = cls.new_ifc_type_instance(**kwargs) + kwargs = {} + if not from_selection: + kwargs.update({'constr_class': constr_class, 'constr_type_id': constr_type_id}) + new_obj = cls.new_constr_type_instance(**kwargs) if new_obj is not None: to_be_deleted = True obj = new_obj obj.asset_mark() obj.asset_generate_preview() icon_id = obj.preview.icon_id - if ifc_class not in cls.data["preview_ifc_types"]: - cls.data["preview_ifc_types"][ifc_class] = {} - cls.data["preview_ifc_types"][ifc_class][relating_type] = {"icon_id": icon_id, "object": obj} + if constr_class not in cls.data["preview_constr_types"]: + cls.data["preview_constr_types"][constr_class] = {} + cls.data["preview_constr_types"][constr_class][str(constr_type_id)] = {"icon_id": icon_id, "object": obj} if to_be_deleted: for col in obj.users_collection: col.objects.unlink(obj) @classmethod - def assetize_relating_type_from_selection(cls): + def assetize_constr_type_from_selection(cls): props = bpy.context.scene.BIMModelProperties - ifc_class = props.ifc_class - relating_type_id = props.relating_type - ifc_class_occurrences = cls.ifc_class_entities(ifc_class=ifc_class) - ifc_class_occurrences = [entity for entity in ifc_class_occurrences if entity.id() == int(relating_type_id)] - if len(ifc_class_occurrences) == 0: + constr_class_browser = props.constr_class_browser + constr_type_id_browser = props.constr_type_id_browser + constr_class_occurrences = cls.constr_class_entities(constr_class=constr_class_browser) + constr_class_occurrences = [ + entity for entity in constr_class_occurrences if entity.id() == int(constr_type_id_browser) + ] + if len(constr_class_occurrences) == 0: return False - ifc_class_entity = ifc_class_occurrences[0] - obj = tool.Ifc.get_object(ifc_class_entity) + constr_class_entity = constr_class_occurrences[0] + obj = tool.Ifc.get_object(constr_class_entity) if obj is None: return False - cls.assetize_object(obj, ifc_class, ifc_class_entity, from_selection=True) + cls.assetize_object(obj, constr_class_browser, constr_class_entity, from_selection=True) return True @staticmethod - def ifc_type_info(ifc_class): - ifc_type_infos = [element for element in bpy.context.scene.IfcTypeInfo if element.name == ifc_class] - return None if len(ifc_type_infos) == 0 else ifc_type_infos[0] + def constr_type_info(constr_class): + constr_type_infos = [element for element in bpy.context.scene.ConstrTypeInfo if element.name == constr_class] + return None if len(constr_type_infos) == 0 else constr_type_infos[0] @classmethod - def new_ifc_type_instance(cls, ifc_class=None, relating_type_id=None): - if ifc_class is not None: - cls.updating = True - props = bpy.context.scene.BIMModelProperties - props.ifc_class = ifc_class - props.relating_type = str(relating_type_id) - cls.updating = False - bpy.ops.bim.add_type_instance() + def new_constr_type_instance(cls, constr_class=None, constr_type_id=None): + props = bpy.context.scene.BIMModelProperties + if constr_class is None: + bpy.ops.bim.add_type_instance( + constr_class=props.constr_class_browser, constr_type_id=int(props.constr_type_id_browser) + ) + else: + props.constr_class = constr_class + props.constr_type_id = str(constr_type_id) + bpy.ops.bim.add_type_instance() return bpy.context.selected_objects[-1] @staticmethod - def relating_type_name_by_id(ifc_class, relating_type_id): + def constr_type_name_by_id(constr_class, constr_type_id): file = IfcStore.get_file() try: - ifc_class_entity = file.by_id(int(relating_type_id)) + constr_class_entity = file.by_id(int(constr_type_id)) except (RuntimeError, ValueError): return None - return ifc_class_entity.Name if ifc_class_entity.is_a() == ifc_class else None + return constr_class_entity.Name if constr_class_entity.is_a() == constr_class else None + + @classmethod + def consolidate_constr_type(cls): + props = bpy.context.scene.BIMModelProperties + props.constr_class = props.constr_class_browser + props.constr_type_id = props.constr_type_id_browser + + @classmethod + def setup_constr_type_browser(cls): + props = bpy.context.scene.BIMModelProperties + props.constr_class_browser = props.constr_class + props.constr_type_id_browser = props.constr_type_id diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 90c7663171..fc5d7ec2aa 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -69,8 +69,8 @@ class AddTypeInstance(bpy.types.Operator): bl_label = "Add" bl_options = {"REGISTER", "UNDO"} bl_description = "Add Type Instance to the model" - ifc_class: bpy.props.StringProperty() - relating_type: bpy.props.IntProperty() + constr_class: bpy.props.StringProperty() + constr_type_id: bpy.props.IntProperty() from_invoke: bpy.props.BoolProperty(default=False) def invoke(self, context, event): @@ -83,25 +83,29 @@ class AddTypeInstance(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMModelProperties - ifc_class = self.ifc_class or props.ifc_class - relating_type_id = self.relating_type or props.relating_type + constr_class = self.constr_class or props.constr_class + constr_type_id = self.constr_type_id or props.constr_type_id - if not ifc_class or not relating_type_id: + if not constr_class or not constr_type_id: return {"FINISHED"} + if self.from_invoke: + props.constr_class = self.constr_class + props.constr_type_id = str(self.constr_type_id) + self.file = IfcStore.get_file() - instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema)[0] - relating_type = self.file.by_id(int(relating_type_id)) - material = ifcopenshell.util.element.get_material(relating_type) + instance_class = ifcopenshell.util.type.get_applicable_entities(constr_class, self.file.schema)[0] + constr_type = self.file.by_id(int(constr_type_id)) + material = ifcopenshell.util.element.get_material(constr_type) if material and material.is_a("IfcMaterialProfileSet"): - if profile.DumbProfileGenerator(relating_type).generate(): + if profile.DumbProfileGenerator(constr_type).generate(): return {"FINISHED"} elif material and material.is_a("IfcMaterialLayerSet"): - if self.generate_layered_element(ifc_class, relating_type): + if self.generate_layered_element(constr_class, constr_type): return {"FINISHED"} - if relating_type.is_a("IfcFlowSegmentType") and not relating_type.RepresentationMaps: - if mep.MepGenerator(relating_type).generate(): + if constr_type.is_a("IfcFlowSegmentType") and not constr_type.RepresentationMaps: + if mep.MepGenerator(constr_type).generate(): return {"FINISHED"} building_obj = None @@ -130,14 +134,14 @@ class AddTypeInstance(bpy.types.Operator): ] mesh = bpy.data.meshes.new(name="Instance") mesh.from_pydata(verts, edges, faces) - obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(relating_type, instance_class), mesh) + obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(constr_type, instance_class), mesh) obj.location = context.scene.cursor.location collection = context.view_layer.active_layer_collection.collection collection.objects.link(obj) collection_obj = bpy.data.objects.get(collection.name) bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class) element = tool.Ifc.get_entity(obj) - blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type) + blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=constr_type) if building_obj: if instance_class in ["IfcWindow", "IfcDoor"]: @@ -152,7 +156,7 @@ class AddTypeInstance(bpy.types.Operator): obj.location[2] = collection_obj.location[2] - min([v[2] for v in obj.bound_box]) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - for port in ifcopenshell.util.system.get_ports(relating_type): + for port in ifcopenshell.util.system.get_ports(constr_type): mat = ifcopenshell.util.placement.get_local_placement(port.ObjectPlacement) mat[0][3] *= unit_scale mat[1][3] *= unit_scale @@ -190,11 +194,11 @@ class AddTypeInstance(bpy.types.Operator): pass # Dumb block generator? Eh? :) -class DisplayIFCTypes(bpy.types.Operator): - bl_idname = "bim.display_ifc_types" - bl_label = "Browse Element Types" +class DisplayConstrTypes(bpy.types.Operator): + bl_idname = "bim.display_constr_types" + bl_label = "Browse Construction Types" bl_options = {"REGISTER", "UNDO"} - bl_description = "Display all possible IFC types for new instances" + bl_description = "Display all available Construction Types to add new instances" def execute(self, context): bpy.ops.object.mode_set(mode="OBJECT") @@ -203,14 +207,15 @@ class DisplayIFCTypes(bpy.types.Operator): def invoke(self, context, event): if not AuthoringData.is_loaded: AuthoringData.load() + AuthoringData.setup_constr_type_browser() props = context.scene.BIMModelProperties if props.unfold_relating_type: - ifc_class = props.ifc_class - ifc_type_info = AuthoringData.ifc_type_info(ifc_class) - if ifc_type_info is None or not ifc_type_info.fully_loaded: - AuthoringData.assetize_ifc_class(ifc_class) + constr_class = props.constr_class_browser + constr_type_info = AuthoringData.constr_type_info(constr_class) + if constr_type_info is None or not constr_type_info.fully_loaded: + AuthoringData.assetize_constr_class(constr_class) else: - prop.update_relating_type(props, context) + prop.update_constr_type(props, context) min_width = 250 width_scaling = 5 ** -1 width = max([min_width, int(width_scaling * context.region.width)]) @@ -219,9 +224,9 @@ class DisplayIFCTypes(bpy.types.Operator): def draw(self, context): props = context.scene.BIMModelProperties if props.unfold_relating_type: - self.draw_by_class(props) + self.draw_by_constr_class(props) else: - self.draw_by_class_and_relating_type(props) + self.draw_by_constr_class_and_type(props) def draw_header(self, props): layout = self.layout @@ -230,16 +235,16 @@ class DisplayIFCTypes(bpy.types.Operator): split = inner_layout.split(align=True, factor=2./3) col1 = split.column(align=True) row = col1.row() - row.prop(data=props, property="unfold_relating_type", text="Preview All Relating Types") + row.prop(data=props, property="unfold_relating_type", text="Preview All Construction Types") col1.row().separator(factor=1) row = col1.row() - row.label(text="Select Element Type:") + row.label(text="Select Construction Type:") col1.row().separator(factor=1.5) enabled = True - if AuthoringData.data["ifc_classes"]: + if AuthoringData.data["constr_classes"]: subsplit = col1.split(factor=1./3) - subsplit.column().row().label(text="IfcElementType:", icon="FILE_VOLUME") - prop_with_search(subsplit.column(), props, "ifc_class", text="") + subsplit.column().row().label(text="Construction Class:", icon="FILE_VOLUME") + prop_with_search(subsplit.column(), props, "constr_class_browser", text="") col1.row().separator() else: enabled = False @@ -250,18 +255,18 @@ class DisplayIFCTypes(bpy.types.Operator): col2.row().separator(factor=1) return {"enabled": enabled, "layout": inner_layout, "col1": col1, "col2": col2} - def draw_by_class(self, props): + def draw_by_constr_class(self, props): header_data = self.draw_header(props) enabled, layout = [header_data[key] for key in ["enabled", "layout"]] - ifc_class = props.ifc_class + constr_class_browser = props.constr_class_browser num_cols = 3 layout.row().separator(factor=0.25) - layout.row().label(text=f"Available {ifc_class}(s):", icon="FILE_3D") + layout.row().label(text="Construction Types:", icon="FILE_3D") layout.row().separator(factor=0.25) flow = layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True) - relating_types = AuthoringData.relating_types() - num_types = len(relating_types) - for idx, (rt_id, name, desc) in enumerate(relating_types): + constr_types_browser = AuthoringData.constr_types_browser() + num_types = len(constr_types_browser) + for idx, (constr_type_id_browser, name, desc) in enumerate(constr_types_browser): outer_col = flow.column() box = outer_col.box() row = box.row() @@ -269,26 +274,25 @@ class DisplayIFCTypes(bpy.types.Operator): row.alignment = "CENTER" row = box.row() if enabled: - preview_ifc_types = AuthoringData.data["preview_ifc_types"] - if ifc_class in preview_ifc_types: - preview_ifc_class = preview_ifc_types[ifc_class] - if name in preview_ifc_class: - icon_id = preview_ifc_class[name]["icon_id"] + preview_constr_types = AuthoringData.data["preview_constr_types"] + if constr_class_browser in preview_constr_types: + preview_constr_class = preview_constr_types[constr_class_browser] + if constr_type_id_browser in preview_constr_class: + icon_id = preview_constr_class[constr_type_id_browser]["icon_id"] row.template_icon(icon_value=icon_id, scale=6.) - box.row().separator(factor=0.25) + box.row().separator(factor=0.2) row = box.row() split = row.split(factor=0.5) col = split.column() op = col.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") - op.ifc_class = ifc_class - op.relating_type_id = rt_id + op.constr_class = constr_class_browser + op.constr_type_id = constr_type_id_browser col = split.column() op = col.operator("bim.add_type_instance", icon="ADD") op.from_invoke = True - op.ifc_class = ifc_class - if rt_id.isnumeric(): - op.relating_type = int(rt_id) - box.row().separator(factor=0.05) + op.constr_class = constr_class_browser + if constr_type_id_browser.isnumeric(): + op.constr_type_id = int(constr_type_id_browser) factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 1.5 outer_col.row().separator(factor=factor) last_row_cols = num_types % num_cols @@ -296,14 +300,15 @@ class DisplayIFCTypes(bpy.types.Operator): for _ in range(num_cols - last_row_cols): flow.column() - def draw_by_class_and_relating_type(self, props): + def draw_by_constr_class_and_type(self, props): header_data = self.draw_header(props) enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]] - ifc_class = props.ifc_class - if AuthoringData.data["relating_types"]: + constr_class_browser = props.constr_class_browser + constr_type_id_browser = props.constr_type_id_browser + if AuthoringData.data["constr_types_ids_browser"]: subsplit = col1.split(factor=1. / 3) - subsplit.column().row().label(text=f"{ifc_class}:", icon="FILE_3D") - prop_with_search(subsplit.column(), props, "relating_type", text="") + subsplit.column().row().label(text="Construction Type:", icon="FILE_3D") + prop_with_search(subsplit.column(), props, "constr_type_id_browser", text="") col1.row().separator() else: enabled = False @@ -311,14 +316,13 @@ class DisplayIFCTypes(bpy.types.Operator): row = col1.row() row.enabled = enabled op = row.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") - op.ifc_class = ifc_class - op.relating_type_id = props.relating_type + op.constr_class = constr_class_browser + op.constr_type_id = constr_type_id_browser op = row.operator("bim.add_type_instance", icon="ADD") op.from_invoke = True - op.ifc_class = ifc_class - relating_type = props.relating_type - if relating_type.isnumeric(): - op.relating_type = int(relating_type) + op.constr_class = constr_class_browser + if constr_type_id_browser.isnumeric(): + op.constr_type_id = int(constr_type_id_browser) col2.row().separator(factor=1.25) split = col2.split(factor=0.025) col = [split.column() for _ in range(2)][-1] @@ -333,8 +337,8 @@ class SelectTypeInstance(bpy.types.Operator): bl_label = "Select" bl_options = {"REGISTER", "UNDO"} bl_description = "Pick Type Instance as selection for subsequent operations" - ifc_class: bpy.props.StringProperty() - relating_type_id: bpy.props.StringProperty() + constr_class: bpy.props.StringProperty() + constr_type_id: bpy.props.StringProperty() def invoke(self, context, event): close_operator_panel(event) @@ -342,16 +346,17 @@ class SelectTypeInstance(bpy.types.Operator): def execute(self, context): props = context.scene.BIMModelProperties - if self.ifc_class != "": - props.ifc_class = self.ifc_class - if self.relating_type_id != "": - props.relating_type = self.relating_type_id + if self.constr_class != "": + props.constr_class = self.constr_class + AuthoringData.load_constr_types() + if self.constr_type_id != "": + props.constr_type_id = self.constr_type_id return {"FINISHED"} class TypeInstanceHelp(bpy.types.Operator): bl_idname = "bim.type_instance_help" - bl_label = "Element Types Help" + bl_label = "Construction Types Help" bl_options = {"REGISTER", "UNDO"} bl_description = "Click to read some contextual help" @@ -367,22 +372,13 @@ class TypeInstanceHelp(bpy.types.Operator): row = layout.row() row.alignment = "CENTER" row.label(text="BlenderBIM Help", icon="BLENDER") - row = layout.row() - row.alignment = "CENTER" - row.label(text="[Element Type Browser]") layout.row().separator(factor=0.5) + row = col_with_margins(layout.row()).row() - row.label(text="When to use:", icon="KEYTYPE_MOVING_HOLD_VEC") - self.draw_lines(layout, self.message_purpose) + row.label(text="Overview:", icon="KEYTYPE_MOVING_HOLD_VEC") + self.draw_lines(layout, self.message_summary) layout.row().separator() - row = col_with_margins(layout.row()).row() - row.label(text="Overall workflow:", icon="KEYTYPE_MOVING_HOLD_VEC") - self.draw_lines(layout, self.message_overall) - layout.row().separator() - row = col_with_margins(layout.row()).row() - row.label(text="UI panel hints:", icon="KEYTYPE_MOVING_HOLD_VEC") - self.draw_lines(layout, self.message_ui) - layout.row().separator(factor=1.5) + row = col_with_margins(layout.row()).row() row.label(text="Further support:", icon="KEYTYPE_MOVING_HOLD_VEC") layout.row().separator(factor=0.5) @@ -404,32 +400,10 @@ class TypeInstanceHelp(bpy.types.Operator): row.label(text=f" {line}") @property - def message_purpose(self): + def message_summary(self): return [ - 'Available Element Types can be previewed and added through the button "Browse', - 'Element Types", which appears when an IfcProjectLibrary, containing in turn', - 'IfcElementType entities, is loaded. In order to manage loaded libraries, navigate', - 'to [Scene Properties] -> [IFC Project Setup] -> [IFC Project Library], under the', - 'Properties panel.' - ] - - @property - def message_overall(self): - return [ - 'Choose a certain Element Type by determining:', - ' 1) An existing subtype of IfcElementType.', - ' 2) Its entity name, as stored within the IfcProjectLibrary.', - 'Then, click on the "Add" button to directly add one instance of the chosen type to ', - 'the model, or alternatively click on the "Select" button to be able to later add', - 'several instances with SHIFT + A.' - ] - - @property - def message_ui(self): - return [ - 'If "Preview All Relating Types" is marked, previews for every existing entity of', - 'the selected IFC class will be shown at once. Not advisable on large projects, with', - 'dozens of entities for a given IfcElementType.' + 'The Construction Type Browser allows to preview and add new instances to the model.', + 'For further support, please click on the Documentation link below.' ] diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 0276c6e13e..788aac4d14 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -17,97 +17,107 @@ # along with BlenderBIM Add-on. If not, see . import bpy -import ifcopenshell.util.type from blenderbim.bim.module.model.data import AuthoringData -from blenderbim.bim.prop import StrProperty, Attribute -from blenderbim.bim.ifc import IfcStore -import blenderbim.tool as tool from bpy.types import PropertyGroup -def get_ifc_class(self, context): +def get_constr_class(self, context): if not AuthoringData.is_loaded: AuthoringData.load() - return AuthoringData.data["ifc_classes"] + return AuthoringData.data["constr_classes"] -def get_relating_type(self, context): +def get_constr_type(self, context): if not AuthoringData.is_loaded: AuthoringData.load() - return AuthoringData.data["relating_types"] + return AuthoringData.data["constr_types_ids"] + + +def get_constr_type_browser(self, context): + if not AuthoringData.is_loaded: + AuthoringData.load() + return AuthoringData.data["constr_types_ids_browser"] def update_icon_id(self, context): - ifc_class = self.ifc_class - relating_type_id = self.relating_type - relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id) - if ((ifc_class not in AuthoringData.data["preview_ifc_types"] - or relating_type not in AuthoringData.data["preview_ifc_types"][ifc_class]) - and relating_type is not None): - if not AuthoringData.assetize_relating_type_from_selection(): + constr_class_browser = self.constr_class_browser + constr_type_id_browser = self.constr_type_id_browser + constr_type_browser = AuthoringData.constr_type_name_by_id(constr_class_browser, constr_type_id_browser) + if ((constr_class_browser not in AuthoringData.data["preview_constr_types"] + or constr_type_id_browser not in AuthoringData.data["preview_constr_types"][constr_class_browser]) + and constr_type_browser is not None): + if not AuthoringData.assetize_constr_type_from_selection(): return props = bpy.context.scene.BIMModelProperties - props.icon_id = AuthoringData.data["preview_ifc_types"][ifc_class][relating_type]["icon_id"] + props.icon_id = AuthoringData.data["preview_constr_types"][constr_class_browser][constr_type_id_browser]["icon_id"] -def update_ifc_class(self, context): - if not AuthoringData.updating: - AuthoringData.load_ifc_classes() - AuthoringData.load_relating_types() - props = context.scene.BIMModelProperties - if props.unfold_relating_type: - ifc_class = props.ifc_class - ifc_type_info = AuthoringData.ifc_type_info(ifc_class) - if ifc_type_info is None or not ifc_type_info.fully_loaded: - AuthoringData.assetize_ifc_class(ifc_class) - else: - self.relating_type = AuthoringData.data["relating_types"][0][0] - update_icon_id(self, context) +def update_constr_class(self, context): + AuthoringData.load_constr_classes() + AuthoringData.load_constr_types() + self.constr_type_id = AuthoringData.data["constr_types_ids"][0][0] -def update_relating_type(self, context): - AuthoringData.load_relating_types() +def update_constr_class_browser(self, context): + AuthoringData.load_constr_classes() + AuthoringData.load_constr_types_browser() + props = context.scene.BIMModelProperties + if props.unfold_relating_type: + constr_class_browser = props.constr_class_browser + constr_type_info = AuthoringData.constr_type_info(constr_class_browser) + if constr_type_info is None or not constr_type_info.fully_loaded: + curr_selection = props.constr_class, props.constr_type_id + AuthoringData.assetize_constr_class(constr_class_browser) + props.constr_class, props.constr_type_id = curr_selection + else: + self.constr_type_id_browser = AuthoringData.data["constr_types_ids_browser"][0][0] + + +def update_constr_type(self, context): + AuthoringData.load_constr_types() + + +def update_constr_type_browser(self, context): + AuthoringData.load_constr_types_browser() update_icon_id(self, context) -def update_unfold_relating_type(self, context): - update_ifc_class(self, context) - - # if self.unfold_relating_type: - # props = context.scene.BIMModelProperties - # ifc_class = props.ifc_class - # ifc_type_info = AuthoringData.ifc_type_info(ifc_class) - # if ifc_type_info is None or not ifc_type_info.fully_loaded: - # AuthoringData.assetize_ifc_class(ifc_class) - # else: - # update_ifc_class(self, context) +def update_unfold_constr_type(self, context): + update_constr_class_browser(self, context) class BIMModelProperties(PropertyGroup): - ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class) - relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type", update=update_relating_type) + constr_class: bpy.props.EnumProperty(items=get_constr_class, name="Construction Class", update=update_constr_class) + constr_class_browser: bpy.props.EnumProperty( + items=get_constr_class, name="Construction Class", update=update_constr_class_browser + ) + constr_type_id: bpy.props.EnumProperty( + items=get_constr_type, name="Construction Type", update=update_constr_type + ) + constr_type_id_browser: bpy.props.EnumProperty( + items=get_constr_type_browser, name="Construction Type", update=update_constr_type_browser + ) icon_id: bpy.props.IntProperty() - unfold_relating_type: bpy.props.BoolProperty(update=update_unfold_relating_type) + unfold_relating_type: bpy.props.BoolProperty(update=update_unfold_constr_type) occurrence_name_style: bpy.props.EnumProperty( items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], name="Occurrence Name Style", ) occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") getter_enum = { - "ifc_class": get_ifc_class, - "relating_type": get_relating_type + "constr_class_browser": get_constr_class, + "constr_type_browser": get_constr_type_browser } -def get_ifc_type_info_relating_types(self, context): - ifc_class = self.name - return AuthoringData.relating_types(ifc_class=ifc_class) +def get_constr_type_info(self, context): + return AuthoringData.relating_types(constr_class=self.name) -class IfcTypeInfo(PropertyGroup): - name: bpy.props.StringProperty(name="IFC class", description="IFC class") - relating_type: bpy.props.EnumProperty( - name="Relating type", description="Relating type", items=get_ifc_type_info_relating_types +class ConstrTypeInfo(PropertyGroup): + name: bpy.props.StringProperty(name="Construction class") + constr_type: bpy.props.EnumProperty( + name="Construction type", items=get_constr_type_info ) fully_loaded: bpy.props.BoolProperty(default=False) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 003ea26aa3..1531dad146 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -57,53 +57,54 @@ class BimTool(WorkSpaceTool): props = context.scene.BIMModelProperties is_tool_header = context.region.type == "TOOL_HEADER" - ifc_classes = AuthoringData.data["ifc_classes"] - relating_types = AuthoringData.data["relating_types"] - row = layout.row(align=True) if not IfcStore.get_file(): row.label(text="No IFC Project", icon="ERROR") return + constr_classes = AuthoringData.data["constr_classes"] + constr_types_ids = AuthoringData.data["constr_types_ids"] + if is_tool_header: row.operator("bim.type_instance_help", text="", icon="QUESTION") - if ifc_classes and is_tool_header: + if constr_classes and is_tool_header: row.label(text="", icon="BLANK1") - row.operator("bim.display_ifc_types", icon="COLLAPSEMENU") + row.operator("bim.display_constr_types", icon="COLLAPSEMENU") - ifc_class = props.ifc_class - relating_type = AuthoringData.relating_type_name_by_id(ifc_class, props.relating_type) + constr_class = props.constr_class + constr_type_id = props.constr_type_id + constr_type = AuthoringData.constr_type_name_by_id(constr_class, constr_type_id) if is_tool_header: row.label(text="", icon="BLANK1") row = layout.row(align=True) row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_A") - if ifc_classes: + if constr_classes: row.label(text=f" Add") row.label(text="", icon="FILE_VOLUME") - row.label(text=ifc_class) + row.label(text=constr_class) row.label(text="", icon="FILE_3D") - row.label(text=f"{relating_type} ") + row.label(text=f"{constr_type} ") else: row.label(text=f" Add instance") else: - txt_ifc_class = ifc_class if ifc_classes else "No IFC Class" - txt_relating_type = relating_type if relating_types else "No Relating Type" + txt_constr_class = constr_class if constr_classes else "No Construction Class" + txt_constr_type = constr_type if constr_types_ids else "No Construction Type" row = layout.row(align=True) - row.label(text="Selected IFC Type:") + row.label(text="Selected Construction Type:") row = layout.row(align=True) - row.label(text=txt_ifc_class, icon="FILE_VOLUME") + row.label(text=txt_constr_class, icon="FILE_VOLUME") row = layout.row(align=True) - row.label(text=txt_relating_type, icon="FILE_3D") + row.label(text=txt_constr_type, icon="FILE_3D") row = layout.row(align=True) row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_A") row.label(text=f" Add Type Instance") - if AuthoringData.data["ifc_classes"]: - if props.ifc_class == "IfcWallType": + if AuthoringData.data["constr_classes"]: + if constr_class == "IfcWallType": row = layout.row() row.label(text="Join") row = layout.row(align=True) @@ -125,7 +126,7 @@ class BimTool(WorkSpaceTool): row.label(text="", icon="EVENT_SHIFT") row.label(text="Split", icon="EVENT_S") - if props.ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"): + if constr_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"): row = layout.row() row.label(text="Join") row = layout.row(align=True) From 1b4da58a15387b5b60044b875501ed94661d221b Mon Sep 17 00:00:00 2001 From: carlos Date: Thu, 14 Jul 2022 13:59:47 +0200 Subject: [PATCH 09/19] Restore IFCFileSelector UI rendering --- src/blenderbim/blenderbim/bim/module/project/operator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 18a8ed0cf9..1c910fd21b 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -109,6 +109,7 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector): IfcStore.library_file = ifcopenshell.open(data["filepath"]) def draw(self, context): + IFCFileSelector.draw(self, context) self.layout.prop(self, "append_all", text= "Append Entire Library") From 7be93b53869d87bb1f2a117b1e9578a711449144 Mon Sep 17 00:00:00 2001 From: carlos Date: Fri, 15 Jul 2022 01:09:08 +0200 Subject: [PATCH 10/19] Add testing to construction type previews --- .../blenderbim/bim/module/model/data.py | 6 +- src/blenderbim/test/bim/feature/model.feature | 15 ++++ src/blenderbim/test/bim/test_feature.py | 68 +++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index b27d49963f..0e426b7766 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -112,8 +112,8 @@ class AuthoringData: constr_type_info = cls.constr_type_info(constr_class) _ = cls.new_constr_type_info(constr_class) if constr_type_info is None else constr_type_info constr_class_occurrences = cls.constr_class_entities(constr_class) + preview_constr_types = cls.data["preview_constr_types"] for constr_class_entity in constr_class_occurrences: - preview_constr_types = cls.data["preview_constr_types"] ### handle asset regeneration when library entity is updated ¿? if (constr_class not in preview_constr_types @@ -190,6 +190,10 @@ class AuthoringData: return None return constr_class_entity.Name if constr_class_entity.is_a() == constr_class else None + def constr_type_id_by_name(cls, constr_class, constr_type): + constr_types = [ct for ct in cls.constr_types(constr_class=constr_class) if ct.Name == constr_type] + return None if len(constr_types) == 0 else constr_types[0] + @classmethod def consolidate_constr_type(cls): props = bpy.context.scene.BIMModelProperties diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index 49b85e9641..7bb0a45e13 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -48,6 +48,21 @@ Scenario: Add type instance - add a mesh where existing instances have changed c Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" +Scenario: Assetize one object + Given I load the demo construction library + And I set "scene.BIMModelProperties.constr_class" to "IfcBeamType" + And I set "scene.BIMModelProperties.constr_class" to "DEMO1" + When I make an asset from the selected construction type + Then the object "IfcBeam/Beam" does not exist + And the construction type "IfcBeamType"/"DEMO1" has a preview + +Scenario: Assetize one class + Given I load the demo construction library + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + When I make assets from the selected construction class + Then objects starting with "IfcWall/" do not exist + And all construction types for "IfcWallType" have a preview + Scenario: Add grid Given an empty IFC project When I press "mesh.add_grid" diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 4cd6dbcd5a..bfd8a8bc87 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -23,6 +23,7 @@ import ifcopenshell import blenderbim.tool as tool import blenderbim.bim from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.model.data import AuthoringData from pytest_bdd import scenarios, given, when, then, parsers from mathutils import Vector @@ -251,6 +252,19 @@ def the_object_name_exists(name) -> bpy.types.Object: assert False, f'The object "{name}" does not exist' return obj +@then(parsers.parse('the object "{name}" does not exist')) +def the_object_name_not_exists(name): + obj = bpy.data.objects.get(name) + if obj: + assert False, f'The object "{name}" exists' + assert True + +@then(parsers.parse('objects starting with "{name}" do not exist')) +def objects_not_exist_starting_with(name): + objs = [obj for obj in bpy.data.objects if obj.name.startswith(name)] + if len(objs) > 0: + assert False, f'{len(objs)} objects starting with "{name}" exist' + assert True @then(parsers.parse('the object "{name1}" and "{name2}" are different elements')) def the_object_name1_and_name2_are_different_elements(name1, name2): @@ -552,3 +566,57 @@ def the_file_name_should_contain_value(name, value): @then(parsers.parse('the object "{name}" has no modifiers')) def the_object_name_has_no_modifiers(name): assert len(the_object_name_exists(name).modifiers) == 0 + + +@then(parsers.parse('the construction type "{constr_class}"/"{constr_type}" has a preview')) +def the_construction_type_has_a_preview(constr_class, constr_type): + if "preview_constr_types" not in AuthoringData: + assert False, 'There are no previews loaded' + preview_constr_types = AuthoringData.data["preview_constr_types"] + if constr_class not in preview_constr_types: + assert False, f'Construction class {constr_class} has no available previews' + constr_type_id = AuthoringData.constr_type_id_by_name(constr_class, constr_type) + if constr_type_id is None: + assert False, f'No construction type {constr_class}/{constr_type} was found' + if constr_type_id not in preview_constr_types[constr_class]: + assert False, f'Construction type {constr_class}/{constr_type} has no available previews' + preview_data = preview_constr_types[constr_class][constr_type_id] + if 'icon_id' not in preview_data: + assert False, f'Construction type {constr_class}/{constr_type} has a preview, but no assigned icon_id' + icon_id = preview_data["icon_id"] + if type(icon_id) is not int: + assert False, f'Construction type {constr_class}/{constr_type} has an invalid icon_id {icon_id}' + if icon_id == 0: + assert False, f'Construction type {constr_class}/{constr_type} has the default null value for icon_id' + assert True + + +@then(parsers.parse('all construction types for "{constr_class}" have a preview')) +def all_construction_types_have_a_preview(constr_class): + if "preview_constr_types" not in AuthoringData: + assert False, 'There are no previews loaded' + preview_constr_types = AuthoringData.data["preview_constr_types"] + if constr_class not in preview_constr_types: + assert False, f'Construction class {constr_class} has no available previews' + constr_class_occurrences = AuthoringData.constr_class_entities(constr_class) + for constr_class_entity in constr_class_occurrences: + the_construction_type_has_a_preview(constr_class, constr_class_entity.Name) + + +@given("I load the demo construction library") +@when("I load the demo construction library") +def i_add_a_construction_library(): + lib_path = '../../blenderbim/bim/data/libraries/IFC4 Demo Library.ifc' + bpy.ops.bim.select_library_file(filepath=lib_path, append_all=True) + + +@given("I make an asset from the selected construction type") +@when("I make an asset from the selected construction type") +def i_assetize_from_selected_constr_type(): + AuthoringData.assetize_constr_type_from_selection() + + +@given("I make assets from the selected construction class") +@when("I make assets from the selected construction class") +def i_assetize_from_selected_constr_class(): + AuthoringData.assetize_constr_class() From e0e0572ff5951b50574b18322d39ad0082a57a80 Mon Sep 17 00:00:00 2001 From: carlos Date: Sat, 23 Jul 2022 00:37:31 +0200 Subject: [PATCH 11/19] Minor renaming and bug fixing --- .../blenderbim/bim/module/model/__init__.py | 2 +- .../blenderbim/bim/module/model/data.py | 3 ++- .../blenderbim/bim/module/model/product.py | 21 +++++++++---------- .../blenderbim/bim/module/model/prop.py | 16 ++++++++++++++ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 2cd8a2c6fe..6319a9c061 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -23,7 +23,7 @@ classes = ( product.AddEmptyType, product.AddTypeInstance, product.DisplayConstrTypes, - product.SelectTypeInstance, + product.SelectConstructionType, product.TypeInstanceHelp, product.AlignProduct, product.DynamicallyVoidProduct, diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index 0e426b7766..da86dd6ac8 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -190,8 +190,9 @@ class AuthoringData: return None return constr_class_entity.Name if constr_class_entity.is_a() == constr_class else None + @classmethod def constr_type_id_by_name(cls, constr_class, constr_type): - constr_types = [ct for ct in cls.constr_types(constr_class=constr_class) if ct.Name == constr_type] + constr_types = [ct[0] for ct in cls.constr_types(constr_class=constr_class) if ct[1] == constr_type] return None if len(constr_types) == 0 else constr_types[0] @classmethod diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index d87c9f80f7..8ee1373185 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -31,7 +31,7 @@ import blenderbim.core.geometry from . import wall, slab, profile, mep from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.data import AuthoringData -from blenderbim.bim.helper import prop_with_search, col_with_margins +from blenderbim.bim.helper import prop_with_search, layout_with_margins from ifcopenshell.api.pset.data import Data as PsetData from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper @@ -201,7 +201,6 @@ class DisplayConstrTypes(bpy.types.Operator): bl_description = "Display all available Construction Types to add new instances" def execute(self, context): - bpy.ops.object.mode_set(mode="OBJECT") return {"FINISHED"} def invoke(self, context, event): @@ -230,7 +229,7 @@ class DisplayConstrTypes(bpy.types.Operator): def draw_header(self, props): layout = self.layout - inner_layout = col_with_margins(layout, margin_left=0.004) + inner_layout = layout_with_margins(layout, margin_left=0.004) inner_layout.row().separator(factor=0.75) split = inner_layout.split(align=True, factor=2./3) col1 = split.column(align=True) @@ -284,7 +283,7 @@ class DisplayConstrTypes(bpy.types.Operator): row = box.row() split = row.split(factor=0.5) col = split.column() - op = col.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") + op = col.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") op.constr_class = constr_class_browser op.constr_type_id = constr_type_id_browser col = split.column() @@ -315,7 +314,7 @@ class DisplayConstrTypes(bpy.types.Operator): col1.row().separator(factor=4.75) row = col1.row() row.enabled = enabled - op = row.operator("bim.select_type_instance", icon="RIGHTARROW_THIN") + op = row.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") op.constr_class = constr_class_browser op.constr_type_id = constr_type_id_browser op = row.operator("bim.add_type_instance", icon="ADD") @@ -332,8 +331,8 @@ class DisplayConstrTypes(bpy.types.Operator): col1.row().separator(factor=1) -class SelectTypeInstance(bpy.types.Operator): - bl_idname = "bim.select_type_instance" +class SelectConstructionType(bpy.types.Operator): + bl_idname = "bim.select_construction_type" bl_label = "Select" bl_options = {"REGISTER", "UNDO"} bl_description = "Pick Type Instance as selection for subsequent operations" @@ -374,15 +373,15 @@ class TypeInstanceHelp(bpy.types.Operator): row.label(text="BlenderBIM Help", icon="BLENDER") layout.row().separator(factor=0.5) - row = col_with_margins(layout.row()).row() + row = layout_with_margins(layout.row()).row() row.label(text="Overview:", icon="KEYTYPE_MOVING_HOLD_VEC") self.draw_lines(layout, self.message_summary) layout.row().separator() - row = col_with_margins(layout.row()).row() + row = layout_with_margins(layout.row()).row() row.label(text="Further support:", icon="KEYTYPE_MOVING_HOLD_VEC") layout.row().separator(factor=0.5) - row = col_with_margins(layout).row() + row = layout_with_margins(layout).row() op = row.operator("bim.open_upstream", text="Homepage", icon="HOME") op.page = "home" op = row.operator("bim.open_upstream", text="Docs", icon="DOCUMENTS") @@ -394,7 +393,7 @@ class TypeInstanceHelp(bpy.types.Operator): layout.row().separator() def draw_lines(self, layout, lines): - box = col_with_margins(layout).box() + box = layout_with_margins(layout).box() for line in lines: row = box.row() row.label(text=f" {line}") diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 788aac4d14..6dd14444c9 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -77,6 +77,20 @@ def update_constr_type(self, context): AuthoringData.load_constr_types() +def update_constr_type_by_name(self, context): + AuthoringData.load_constr_types() + constr_type_id = AuthoringData.constr_type_id_by_name(self.constr_class, self.constr_type) + if constr_type_id is not None: + self.constr_type_id = constr_type_id + + +def update_constr_type_browser_by_name(self, context): + AuthoringData.load_constr_types_browser() + constr_type_id_browser = AuthoringData.constr_type_id_by_name(self.constr_class_browser, self.constr_type_browser) + if constr_type_id_browser is not None: + self.constr_type_id_browser = constr_type_id_browser + + def update_constr_type_browser(self, context): AuthoringData.load_constr_types_browser() update_icon_id(self, context) @@ -91,9 +105,11 @@ class BIMModelProperties(PropertyGroup): constr_class_browser: bpy.props.EnumProperty( items=get_constr_class, name="Construction Class", update=update_constr_class_browser ) + constr_type: bpy.props.StringProperty(update=update_constr_type_by_name) constr_type_id: bpy.props.EnumProperty( items=get_constr_type, name="Construction Type", update=update_constr_type ) + constr_type_browser: bpy.props.StringProperty(update=update_constr_type_browser_by_name) constr_type_id_browser: bpy.props.EnumProperty( items=get_constr_type_browser, name="Construction Type", update=update_constr_type_browser ) From 6c260ef5c9bbf724b404ea33403eb444d2b77329 Mon Sep 17 00:00:00 2001 From: carlos Date: Sat, 23 Jul 2022 00:38:26 +0200 Subject: [PATCH 12/19] Margins function for UILayouts --- src/blenderbim/blenderbim/bim/helper.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index 06508b50fa..45661d64f1 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -118,6 +118,17 @@ def prop_with_search(layout, data, prop_name, **kwargs): op.prop_name = prop_name +def layout_with_margins(layout, margin_left=0.025, margin_right=None): + margin_right = margin_left if margin_right is None else margin_right + split = layout.split(factor=margin_left, align=True) + cols = [split.column() for _ in range(2)] + cols[0].label(text="") + subsplit = cols[-1].split(factor=(1. - margin_right), align=True) + subcol = subsplit.column() + subsplit.column().label(text="") + return subcol + + def get_enum_items(data, prop_name, context): # Retrieve items from a dynamic EnumProperty, which is otherwise not supported # Or throws an error in the console when the items callback returns an empty list From 64752e52c9d653a49bdd5d5f3a7fa7307462aafa Mon Sep 17 00:00:00 2001 From: carlos Date: Sat, 23 Jul 2022 00:39:17 +0200 Subject: [PATCH 13/19] Tuning feature tests --- src/blenderbim/test/bim/feature/model.feature | 34 +++++----- src/blenderbim/test/bim/test_feature.py | 63 +++++++++++-------- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index 7bb0a45e13..68d3e5f76b 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -8,9 +8,9 @@ Scenario: Add type instance - add from a mesh And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.relating_type" to "{cube}" + And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" When I press "bim.add_type_instance" Then the object "IfcWall/Wall" exists @@ -21,9 +21,9 @@ Scenario: Add type instance - add from an empty And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.relating_type" to "{empty}" + And I set "scene.BIMModelProperties.constr_type_id" to "{empty}" When I press "bim.add_type_instance" Then the object "IfcWall/Wall" exists @@ -34,9 +34,9 @@ Scenario: Add type instance - add a mesh where existing instances have changed c And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.relating_type" to "{cube}" + And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" And I press "bim.add_type_instance" And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall" is selected @@ -48,18 +48,22 @@ Scenario: Add type instance - add a mesh where existing instances have changed c Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" -Scenario: Assetize one object - Given I load the demo construction library - And I set "scene.BIMModelProperties.constr_class" to "IfcBeamType" - And I set "scene.BIMModelProperties.constr_class" to "DEMO1" - When I make an asset from the selected construction type +Scenario: Preview one type on the Construction Type Browser + Given an empty IFC project + And I load the demo construction library + When I display the Construction Type Browser + And I preview only one asset on the Construction Type Browser + And I set "scene.BIMModelProperties.constr_class_browser" to "IfcBeamType" + And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO1" Then the object "IfcBeam/Beam" does not exist And the construction type "IfcBeamType"/"DEMO1" has a preview -Scenario: Assetize one class - Given I load the demo construction library - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" - When I make assets from the selected construction class +Scenario: Preview one class on the Construction Type Browser + Given an empty IFC project + And I load the demo construction library + When I display the Construction Type Browser + And I preview all available assets on the Construction Type Browser + And I set "scene.BIMModelProperties.constr_class_browser" to "IfcWallType" Then objects starting with "IfcWall/" do not exist And all construction types for "IfcWallType" have a preview diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 07249bb456..b359819f3b 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -261,19 +261,6 @@ def the_object_name_exists(name) -> bpy.types.Object: assert False, f'The object "{name}" does not exist' return obj -@then(parsers.parse('the object "{name}" does not exist')) -def the_object_name_not_exists(name): - obj = bpy.data.objects.get(name) - if obj: - assert False, f'The object "{name}" exists' - assert True - -@then(parsers.parse('objects starting with "{name}" do not exist')) -def objects_not_exist_starting_with(name): - objs = [obj for obj in bpy.data.objects if obj.name.startswith(name)] - if len(objs) > 0: - assert False, f'{len(objs)} objects starting with "{name}" exist' - assert True @then(parsers.parse('the object "{name1}" and "{name2}" are different elements')) def the_object_name1_and_name2_are_different_elements(name1, name2): @@ -520,7 +507,16 @@ def the_collection_name1_is_in_the_collection_name2(name1, name2): @then(parsers.parse('the object "{name}" does not exist')) def the_object_name_does_not_exist(name): - assert bpy.data.objects.get(name) is None, "Object exists" + obj = bpy.data.objects.get(name) + assert obj is None or len(obj.users_collection) == 0, "Object exists" + + +@then(parsers.parse('objects starting with "{name}" do not exist')) +def objects_not_exist_starting_with(name): + objs = [obj for obj in bpy.data.objects if obj.name.startswith(name) and len(obj.users_collection) > 0] + if len(objs) > 0: + assert False, f'{len(objs)} objects starting with "{name}" exist' + assert True @then(parsers.parse('the object "{name}" is at "{location}"')) @@ -579,7 +575,7 @@ def the_object_name_has_no_modifiers(name): @then(parsers.parse('the construction type "{constr_class}"/"{constr_type}" has a preview')) def the_construction_type_has_a_preview(constr_class, constr_type): - if "preview_constr_types" not in AuthoringData: + if "preview_constr_types" not in AuthoringData.data: assert False, 'There are no previews loaded' preview_constr_types = AuthoringData.data["preview_constr_types"] if constr_class not in preview_constr_types: @@ -593,7 +589,7 @@ def the_construction_type_has_a_preview(constr_class, constr_type): if 'icon_id' not in preview_data: assert False, f'Construction type {constr_class}/{constr_type} has a preview, but no assigned icon_id' icon_id = preview_data["icon_id"] - if type(icon_id) is not int: + if not isinstance(icon_id, int): assert False, f'Construction type {constr_class}/{constr_type} has an invalid icon_id {icon_id}' if icon_id == 0: assert False, f'Construction type {constr_class}/{constr_type} has the default null value for icon_id' @@ -602,7 +598,7 @@ def the_construction_type_has_a_preview(constr_class, constr_type): @then(parsers.parse('all construction types for "{constr_class}" have a preview')) def all_construction_types_have_a_preview(constr_class): - if "preview_constr_types" not in AuthoringData: + if "preview_constr_types" not in AuthoringData.data: assert False, 'There are no previews loaded' preview_constr_types = AuthoringData.data["preview_constr_types"] if constr_class not in preview_constr_types: @@ -615,17 +611,32 @@ def all_construction_types_have_a_preview(constr_class): @given("I load the demo construction library") @when("I load the demo construction library") def i_add_a_construction_library(): - lib_path = '../../blenderbim/bim/data/libraries/IFC4 Demo Library.ifc' + lib_path = './blenderbim/bim/data/libraries/IFC4 Demo Library.ifc' bpy.ops.bim.select_library_file(filepath=lib_path, append_all=True) -@given("I make an asset from the selected construction type") -@when("I make an asset from the selected construction type") -def i_assetize_from_selected_constr_type(): - AuthoringData.assetize_constr_type_from_selection() +@given("I display the Construction Type Browser") +@when("I display the Construction Type Browser") +def i_display_the_construction_type_browser(): + bpy.ops.bim.display_constr_types() -@given("I make assets from the selected construction class") -@when("I make assets from the selected construction class") -def i_assetize_from_selected_constr_class(): - AuthoringData.assetize_constr_class() +@given("I preview only one asset on the Construction Type Browser") +@when("I preview only one asset on the Construction Type Browser") +def i_preview_one_construction_type(): + bpy.context.scene.BIMModelProperties.unfold_relating_type = False + + +@given("I preview all available assets on the Construction Type Browser") +@when("I preview all available assets on the Construction Type Browser") +def i_preview_all_construction_types(): + bpy.context.scene.BIMModelProperties.unfold_relating_type = True + + +@given("I select the active construction type") +@when("I select the active construction type") +def i_select_the_active_construction_type(): + props = bpy.context.scene.BIMModelProperties + bpy.ops.bim.select_construction_type( + constr_class=props.constr_class_browser, constr_type_id=props.constr_type_id_browser + ) From cee48d7424a0d708428c85028266ce928ff27dfa Mon Sep 17 00:00:00 2001 From: Carlos Villagrasa Date: Tue, 26 Jul 2022 01:21:51 +0200 Subject: [PATCH 14/19] Some more renaming/refactoring --- .../blenderbim/bim/module/model/__init__.py | 4 +- .../blenderbim/bim/module/model/data.py | 42 +++++++++---------- .../blenderbim/bim/module/model/product.py | 42 ++++++++----------- .../blenderbim/bim/module/model/prop.py | 7 ++-- .../blenderbim/bim/module/model/workspace.py | 4 +- .../blenderbim/bim/module/type/ui.py | 2 +- 6 files changed, 44 insertions(+), 57 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 6319a9c061..a023470ad3 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -21,10 +21,10 @@ from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, classes = ( product.AddEmptyType, - product.AddTypeInstance, + product.AddConstrType, product.DisplayConstrTypes, product.SelectConstructionType, - product.TypeInstanceHelp, + product.HelpConstrTypes, product.AlignProduct, product.DynamicallyVoidProduct, workspace.Hotkey, diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index da86dd6ac8..a9a93929ab 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -16,12 +16,14 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import functools import bpy import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore preview_icon_ids = {} +attempts = 0 def refresh(): @@ -37,6 +39,7 @@ class AuthoringData: cls.is_loaded = True if not hasattr(cls, "data"): cls.data = {} + cls.props = bpy.context.scene.BIMModelProperties cls.load_constr_classes() cls.load_constr_types() cls.load_constr_types_browser() @@ -77,8 +80,7 @@ class AuthoringData: return [] results = [] if constr_class is None: - props = bpy.context.scene.BIMModelProperties - constr_class = props.constr_class + constr_class = cls.props.constr_class if not constr_class and constr_classes: constr_class = constr_classes[0][0] if constr_class: @@ -95,8 +97,7 @@ class AuthoringData: @classmethod def constr_types_browser(cls): - props = bpy.context.scene.BIMModelProperties - return cls.constr_types(constr_class=props.constr_class_browser) + return cls.constr_types(constr_class=cls.props.constr_class_browser) @staticmethod def new_constr_type_info(constr_class): @@ -107,8 +108,7 @@ class AuthoringData: @classmethod def assetize_constr_class(cls, constr_class=None): if constr_class is None: - props = bpy.context.scene.BIMModelProperties - constr_class = props.constr_class + constr_class = cls.props.constr_class constr_type_info = cls.constr_type_info(constr_class) _ = cls.new_constr_type_info(constr_class) if constr_type_info is None else constr_type_info constr_class_occurrences = cls.constr_class_entities(constr_class) @@ -131,7 +131,7 @@ class AuthoringData: kwargs = {} if not from_selection: kwargs.update({'constr_class': constr_class, 'constr_type_id': constr_type_id}) - new_obj = cls.new_constr_type_instance(**kwargs) + new_obj = cls.new_constr_type(**kwargs) if new_obj is not None: to_be_deleted = True obj = new_obj @@ -147,9 +147,8 @@ class AuthoringData: @classmethod def assetize_constr_type_from_selection(cls): - props = bpy.context.scene.BIMModelProperties - constr_class_browser = props.constr_class_browser - constr_type_id_browser = props.constr_type_id_browser + constr_class_browser = cls.props.constr_class_browser + constr_type_id_browser = cls.props.constr_type_id_browser constr_class_occurrences = cls.constr_class_entities(constr_class=constr_class_browser) constr_class_occurrences = [ entity for entity in constr_class_occurrences if entity.id() == int(constr_type_id_browser) @@ -169,16 +168,15 @@ class AuthoringData: return None if len(constr_type_infos) == 0 else constr_type_infos[0] @classmethod - def new_constr_type_instance(cls, constr_class=None, constr_type_id=None): - props = bpy.context.scene.BIMModelProperties + def new_constr_type(cls, constr_class=None, constr_type_id=None): if constr_class is None: - bpy.ops.bim.add_type_instance( - constr_class=props.constr_class_browser, constr_type_id=int(props.constr_type_id_browser) + bpy.ops.bim.add_constr_type( + constr_class=cls.props.constr_class_browser, constr_type_id=int(cls.props.constr_type_id_browser) ) else: - props.constr_class = constr_class - props.constr_type_id = str(constr_type_id) - bpy.ops.bim.add_type_instance() + cls.props.constr_class = constr_class + cls.props.constr_type_id = str(constr_type_id) + bpy.ops.bim.add_constr_type() return bpy.context.selected_objects[-1] @staticmethod @@ -197,12 +195,10 @@ class AuthoringData: @classmethod def consolidate_constr_type(cls): - props = bpy.context.scene.BIMModelProperties - props.constr_class = props.constr_class_browser - props.constr_type_id = props.constr_type_id_browser + cls.props.constr_class = cls.props.constr_class_browser + cls.props.constr_type_id = cls.props.constr_type_id_browser @classmethod def setup_constr_type_browser(cls): - props = bpy.context.scene.BIMModelProperties - props.constr_class_browser = props.constr_class - props.constr_type_id_browser = props.constr_type_id + cls.props.constr_class_browser = cls.props.constr_class + cls.props.constr_type_id_browser = cls.props.constr_type_id diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 8ee1373185..4b0408e83d 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -31,7 +31,7 @@ import blenderbim.core.geometry from . import wall, slab, profile, mep from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.data import AuthoringData -from blenderbim.bim.helper import prop_with_search, layout_with_margins +from blenderbim.bim.helper import prop_with_search, layout_with_margins, close_operator_panel from ifcopenshell.api.pset.data import Data as PsetData from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper @@ -57,15 +57,8 @@ def add_empty_type_button(self, context): self.layout.operator(AddEmptyType.bl_idname, icon="FILE_3D") -def close_operator_panel(event): - x, y = event.mouse_x, event.mouse_y - bpy.context.window.cursor_warp(10, 10) - move_back = lambda: bpy.context.window.cursor_warp(x, y) - bpy.app.timers.register(move_back, first_interval=0.001) - - -class AddTypeInstance(bpy.types.Operator): - bl_idname = "bim.add_type_instance" +class AddConstrType(bpy.types.Operator): + bl_idname = "bim.add_constr_type" bl_label = "Add" bl_options = {"REGISTER", "UNDO"} bl_description = "Add Type Instance to the model" @@ -208,13 +201,13 @@ class DisplayConstrTypes(bpy.types.Operator): AuthoringData.load() AuthoringData.setup_constr_type_browser() props = context.scene.BIMModelProperties - if props.unfold_relating_type: + if props.unfold_constr_types: constr_class = props.constr_class_browser constr_type_info = AuthoringData.constr_type_info(constr_class) if constr_type_info is None or not constr_type_info.fully_loaded: AuthoringData.assetize_constr_class(constr_class) else: - prop.update_constr_type(props, context) + prop.update_constr_type_browser(props, context) min_width = 250 width_scaling = 5 ** -1 width = max([min_width, int(width_scaling * context.region.width)]) @@ -222,10 +215,11 @@ class DisplayConstrTypes(bpy.types.Operator): def draw(self, context): props = context.scene.BIMModelProperties - if props.unfold_relating_type: - self.draw_by_constr_class(props) + header_data = self.draw_header(props) + if props.unfold_constr_types: + self.draw_by_constr_class(props, header_data) else: - self.draw_by_constr_class_and_type(props) + self.draw_by_constr_class_and_type(props, header_data) def draw_header(self, props): layout = self.layout @@ -234,7 +228,7 @@ class DisplayConstrTypes(bpy.types.Operator): split = inner_layout.split(align=True, factor=2./3) col1 = split.column(align=True) row = col1.row() - row.prop(data=props, property="unfold_relating_type", text="Preview All Construction Types") + row.prop(data=props, property="unfold_constr_types", text="Preview All Construction Types") col1.row().separator(factor=1) row = col1.row() row.label(text="Select Construction Type:") @@ -250,12 +244,11 @@ class DisplayConstrTypes(bpy.types.Operator): col2 = split.column(align=True) subsplit = col2.split(factor=0.9) subcol = [subsplit.column() for _ in range(2)][-1] - subcol.operator("bim.type_instance_help", text="", icon="QUESTION") + subcol.operator("bim.help_constr_types", text="", icon="QUESTION") col2.row().separator(factor=1) return {"enabled": enabled, "layout": inner_layout, "col1": col1, "col2": col2} - def draw_by_constr_class(self, props): - header_data = self.draw_header(props) + def draw_by_constr_class(self, props, header_data): enabled, layout = [header_data[key] for key in ["enabled", "layout"]] constr_class_browser = props.constr_class_browser num_cols = 3 @@ -287,7 +280,7 @@ class DisplayConstrTypes(bpy.types.Operator): op.constr_class = constr_class_browser op.constr_type_id = constr_type_id_browser col = split.column() - op = col.operator("bim.add_type_instance", icon="ADD") + op = col.operator("bim.add_constr_type", icon="ADD") op.from_invoke = True op.constr_class = constr_class_browser if constr_type_id_browser.isnumeric(): @@ -299,8 +292,7 @@ class DisplayConstrTypes(bpy.types.Operator): for _ in range(num_cols - last_row_cols): flow.column() - def draw_by_constr_class_and_type(self, props): - header_data = self.draw_header(props) + def draw_by_constr_class_and_type(self, props, header_data): enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]] constr_class_browser = props.constr_class_browser constr_type_id_browser = props.constr_type_id_browser @@ -317,7 +309,7 @@ class DisplayConstrTypes(bpy.types.Operator): op = row.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") op.constr_class = constr_class_browser op.constr_type_id = constr_type_id_browser - op = row.operator("bim.add_type_instance", icon="ADD") + op = row.operator("bim.add_constr_type", icon="ADD") op.from_invoke = True op.constr_class = constr_class_browser if constr_type_id_browser.isnumeric(): @@ -353,8 +345,8 @@ class SelectConstructionType(bpy.types.Operator): return {"FINISHED"} -class TypeInstanceHelp(bpy.types.Operator): - bl_idname = "bim.type_instance_help" +class HelpConstrTypes(bpy.types.Operator): + bl_idname = "bim.help_constr_types" bl_label = "Construction Types Help" bl_options = {"REGISTER", "UNDO"} bl_description = "Click to read some contextual help" diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 6dd14444c9..122c850302 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -48,8 +48,7 @@ def update_icon_id(self, context): and constr_type_browser is not None): if not AuthoringData.assetize_constr_type_from_selection(): return - props = bpy.context.scene.BIMModelProperties - props.icon_id = AuthoringData.data["preview_constr_types"][constr_class_browser][constr_type_id_browser]["icon_id"] + self.icon_id = AuthoringData.data["preview_constr_types"][constr_class_browser][constr_type_id_browser]["icon_id"] def update_constr_class(self, context): @@ -62,7 +61,7 @@ def update_constr_class_browser(self, context): AuthoringData.load_constr_classes() AuthoringData.load_constr_types_browser() props = context.scene.BIMModelProperties - if props.unfold_relating_type: + if props.unfold_constr_types: constr_class_browser = props.constr_class_browser constr_type_info = AuthoringData.constr_type_info(constr_class_browser) if constr_type_info is None or not constr_type_info.fully_loaded: @@ -114,7 +113,7 @@ class BIMModelProperties(PropertyGroup): items=get_constr_type_browser, name="Construction Type", update=update_constr_type_browser ) icon_id: bpy.props.IntProperty() - unfold_relating_type: bpy.props.BoolProperty(update=update_unfold_constr_type) + unfold_constr_types: bpy.props.BoolProperty(update=update_unfold_constr_type) occurrence_name_style: bpy.props.EnumProperty( items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], name="Occurrence Name Style", diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 1531dad146..6a52dc8e47 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -66,7 +66,7 @@ class BimTool(WorkSpaceTool): constr_types_ids = AuthoringData.data["constr_types_ids"] if is_tool_header: - row.operator("bim.type_instance_help", text="", icon="QUESTION") + row.operator("bim.help_constr_types", text="", icon="QUESTION") if constr_classes and is_tool_header: row.label(text="", icon="BLANK1") @@ -184,7 +184,7 @@ class Hotkey(bpy.types.Operator): return {"FINISHED"} def hotkey_S_A(self): - bpy.ops.bim.add_type_instance() + bpy.ops.bim.add_constr_type() def hotkey_S_C(self): if self.has_ifc_class and self.props.ifc_class == "IfcWallType": diff --git a/src/blenderbim/blenderbim/bim/module/type/ui.py b/src/blenderbim/blenderbim/bim/module/type/ui.py index 95256e99c7..04178e7ebb 100644 --- a/src/blenderbim/blenderbim/bim/module/type/ui.py +++ b/src/blenderbim/blenderbim/bim/module/type/ui.py @@ -88,4 +88,4 @@ class BIM_PT_type(Panel): def add_object_button(self, context): - self.layout.operator("bim.add_type_instance", icon="PLUGIN") + self.layout.operator("bim.add_constr_type", icon="PLUGIN") From 99c47069cd629eb86d0364db590fb55fe3e4d11c Mon Sep 17 00:00:00 2001 From: Carlos Villagrasa Date: Tue, 26 Jul 2022 01:22:22 +0200 Subject: [PATCH 15/19] Tests polished --- .../test/bim/feature/geometry.feature | 26 +++---- src/blenderbim/test/bim/feature/model.feature | 42 ++++++++---- src/blenderbim/test/bim/test_feature.py | 67 +++++++++++++++---- 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index dae44cff62..af5afb882e 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -31,8 +31,8 @@ Scenario: Add representation - add a new representation to a typed instance And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I press "bim.add_type_instance" - And I press "bim.add_type_instance" + And I press "bim.add_constr_type" + And I press "bim.add_constr_type" Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" When the object "IfcWall/Wall" is selected @@ -144,11 +144,11 @@ Scenario: Remove representation - remove an instanced representation from an act And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.relating_type" to "{cube}" - And I press "bim.add_type_instance" - And I press "bim.add_type_instance" + And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" + And I press "bim.add_constr_type" + And I press "bim.add_constr_type" And the object "IfcWallType/Cube" is selected When the variable "representation" is "{ifc}.by_type('IfcWallType')[0].RepresentationMaps[1].MappedRepresentation.id()" And I press "bim.remove_representation(representation_id={representation})" @@ -163,11 +163,11 @@ Scenario: Remove representation - remove an instanced representation from an act And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.relating_type" to "{cube}" - And I press "bim.add_type_instance" - And I press "bim.add_type_instance" + And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" + And I press "bim.add_constr_type" + And I press "bim.add_constr_type" And the object "IfcWall/Wall" is selected When the variable "representation" is "{ifc}.by_type('IfcWall')[0].Representation.Representations[1].id()" And I press "bim.remove_representation(representation_id={representation})" @@ -337,10 +337,10 @@ Scenario: Override duplicate move - copying a type instance with a representatio And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.relating_type" to "{cube}" - And I press "bim.add_type_instance" + And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" + And I press "bim.add_constr_type" And the object "IfcWall/Wall" is selected When I press "object.duplicate_move" Then the object "IfcWall/Wall.001" exists diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index 68d3e5f76b..247f4fed45 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -11,7 +11,7 @@ Scenario: Add type instance - add from a mesh And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - When I press "bim.add_type_instance" + When I press "bim.add_constr_type" Then the object "IfcWall/Wall" exists Scenario: Add type instance - add from an empty @@ -24,7 +24,7 @@ Scenario: Add type instance - add from an empty And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{empty}" - When I press "bim.add_type_instance" + When I press "bim.add_constr_type" Then the object "IfcWall/Wall" exists Scenario: Add type instance - add a mesh where existing instances have changed context @@ -37,36 +37,50 @@ Scenario: Add type instance - add a mesh where existing instances have changed c And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - And I press "bim.add_type_instance" + And I press "bim.add_constr_type" And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall" is selected And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()" And I set "scene.BIMRootProperties.contexts" to "{context}" And I press "bim.add_representation" And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" - When I press "bim.add_type_instance" + When I press "bim.add_constr_type" Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" Scenario: Preview one type on the Construction Type Browser Given an empty IFC project And I load the demo construction library - When I display the Construction Type Browser - And I preview only one asset on the Construction Type Browser - And I set "scene.BIMModelProperties.constr_class_browser" to "IfcBeamType" - And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO1" - Then the object "IfcBeam/Beam" does not exist - And the construction type "IfcBeamType"/"DEMO1" has a preview + When I display the construction type browser + And I preview only one asset on the construction type browser + And I set "scene.BIMModelProperties.constr_class_browser" to "IfcColumnType" + And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO2" + And I select the browser construction type + Then "scene.BIMModelProperties.constr_class" is "IfcColumnType" + And construction type is DEMO2 + And objects starting with "IfcColumn/" do not exist + And the construction type "IfcColumnType"/"DEMO2" has a preview -Scenario: Preview one class on the Construction Type Browser +Scenario: Preview one class on the construction type browser Given an empty IFC project And I load the demo construction library - When I display the Construction Type Browser - And I preview all available assets on the Construction Type Browser + When I display the construction type browser + And I preview all available assets on the construction type browser And I set "scene.BIMModelProperties.constr_class_browser" to "IfcWallType" - Then objects starting with "IfcWall/" do not exist + Then "scene.BIMModelProperties.constr_class_browser" is "IfcWallType" + And objects starting with "IfcWall/" do not exist And all construction types for "IfcWallType" have a preview +Scenario: Add one type from the Construction Type Browser + Given an empty IFC project + And I load the demo construction library + When I display the construction type browser + And I preview only one asset on the construction type browser + And I set "scene.BIMModelProperties.constr_class_browser" to "IfcColumnType" + And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO2" + And I add the browser construction type + Then the object "IfcColumn/Column" exists + Scenario: Add grid Given an empty IFC project When I press "mesh.add_grid" diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index b359819f3b..4fc61fff59 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -99,16 +99,19 @@ def i_add_a_sun(): def i_add_a_material(): bpy.context.active_object.active_material = bpy.data.materials.new("Material") + @given("I add a new group to IfcSelector") @when("I add a new group to IfcSelector") def i_add_a_new_collection_item(): bpy.data.scenes["Scene"].IfcSelectorProperties.groups.add() - + + @given("I add a new query to IfcSelector") @when("I add a new query to IfcSelector") def i_add_a_new_collection_item(): bpy.data.scenes["Scene"].IfcSelectorProperties.groups[0].queries.add() + @given(parsers.parse('the material "{name}" colour is set to "{colour}"')) @when(parsers.parse('the material "{name}" colour is set to "{colour}"')) def the_material_name_colour_is_set_to_colour(name, colour): @@ -591,11 +594,18 @@ def the_construction_type_has_a_preview(constr_class, constr_type): icon_id = preview_data["icon_id"] if not isinstance(icon_id, int): assert False, f'Construction type {constr_class}/{constr_type} has an invalid icon_id {icon_id}' - if icon_id == 0: - assert False, f'Construction type {constr_class}/{constr_type} has the default null value for icon_id' + # Note: icon_id must be > 0 in UI mode, but asset_generate_preview() doesn't work headlessly -> skipping for now + # if icon_id == 0: + # assert False, f'Construction type {constr_class}/{constr_type} has the default null value for icon_id' assert True +@then("there is a Construction Type preview") +def there_is_a_construction_type_preview(): + props = bpy.context.scene.BIMModelProperties + assert props.icon_id > 0, f"There isn't a Construction Type preview" + + @then(parsers.parse('all construction types for "{constr_class}" have a preview')) def all_construction_types_have_a_preview(constr_class): if "preview_constr_types" not in AuthoringData.data: @@ -615,28 +625,57 @@ def i_add_a_construction_library(): bpy.ops.bim.select_library_file(filepath=lib_path, append_all=True) -@given("I display the Construction Type Browser") -@when("I display the Construction Type Browser") +@given("I display the construction type browser") +@when("I display the construction type browser") def i_display_the_construction_type_browser(): - bpy.ops.bim.display_constr_types() + bpy.ops.bim.display_constr_types('INVOKE_DEFAULT') -@given("I preview only one asset on the Construction Type Browser") -@when("I preview only one asset on the Construction Type Browser") +@given("I preview only one asset on the construction type browser") +@when("I preview only one asset on the construction type browser") def i_preview_one_construction_type(): - bpy.context.scene.BIMModelProperties.unfold_relating_type = False + bpy.context.scene.BIMModelProperties.unfold_constr_types = False -@given("I preview all available assets on the Construction Type Browser") -@when("I preview all available assets on the Construction Type Browser") +@given("I preview all available assets on the construction type browser") +@when("I preview all available assets on the construction type browser") def i_preview_all_construction_types(): - bpy.context.scene.BIMModelProperties.unfold_relating_type = True + bpy.context.scene.BIMModelProperties.unfold_constr_types = True -@given("I select the active construction type") -@when("I select the active construction type") +@given("I select the browser construction type") +@when("I select the browser construction type") def i_select_the_active_construction_type(): props = bpy.context.scene.BIMModelProperties bpy.ops.bim.select_construction_type( constr_class=props.constr_class_browser, constr_type_id=props.constr_type_id_browser ) + + +@given("I add the browser construction type") +@when("I add the browser construction type") +def i_add_the_active_construction_type(): + props = bpy.context.scene.BIMModelProperties + bpy.ops.bim.add_constr_type( + constr_class=props.constr_class_browser, constr_type_id=int(props.constr_type_id_browser) + ) + + +@then(parsers.parse("browser construction type is {constr_type_name}")) +def browser_construction_type(constr_type_name): + props = bpy.context.scene.BIMModelProperties + constr_type_browser = AuthoringData.constr_type_name_by_id(props.constr_class_browser, props.constr_type_id_browser) + assert constr_type_browser == constr_type_name, (f"Construction Type is a {constr_type_browser}, not " + + f"a {constr_type_name}") + + +@then(parsers.parse("construction type is {constr_type_name}")) +def construction_type(constr_type_name): + props = bpy.context.scene.BIMModelProperties + constr_type = AuthoringData.constr_type_name_by_id(props.constr_class, props.constr_type_id) + assert constr_type == constr_type_name, f"Construction Type is a {constr_type}, not a {constr_type_name}" + + +@when("I move the cursor to the bottom left corner") +def move_cursor_bottom_left(): + bpy.context.window.cursor_warp(10, 10) From 58150a296d43f892a2acb29f90cbab0833432518 Mon Sep 17 00:00:00 2001 From: Carlos Villagrasa Date: Tue, 26 Jul 2022 20:20:29 +0200 Subject: [PATCH 16/19] Revert "Merge remote-tracking branch 'origin/master' into v0.7.0" This reverts commit 14bca5d1999e260818d9a56db0fef5b5748bf93e, reversing changes made to 7321138d576b801054c213a8ea55cd5ffc1b3c03. --- win/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/readme.md b/win/readme.md index eb05129484..539ba9db1e 100644 --- a/win/readme.md +++ b/win/readme.md @@ -80,7 +80,7 @@ Let's say you have already installed 64-bit Python 3.5.1 to `C:\Python3`. Before building the dependencies, disable the script from installing Python: ``` > set IFCOS_INSTALL_PYTHON=FALSE -> build-deps.cmd +> buid-deps.cmd ``` After building the dependencies, append Python version and installation directory information to the BuildDepsCache file From 79453a6ae0caed1fee8ba1d47b2ca9dd6a935b89 Mon Sep 17 00:00:00 2001 From: Carlos Villagrasa Date: Wed, 27 Jul 2022 08:11:52 +0200 Subject: [PATCH 17/19] Renaming constr_class back to ifc_class --- .../blenderbim/bim/module/model/data.py | 98 +++++++++---------- .../blenderbim/bim/module/model/product.py | 56 +++++------ .../blenderbim/bim/module/model/prop.py | 48 ++++----- .../blenderbim/bim/module/model/workspace.py | 22 ++--- .../test/bim/feature/geometry.feature | 6 +- src/blenderbim/test/bim/feature/model.feature | 16 +-- src/blenderbim/test/bim/test_feature.py | 44 ++++----- 7 files changed, 145 insertions(+), 145 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index a9a93929ab..d5298bded5 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -40,14 +40,14 @@ class AuthoringData: if not hasattr(cls, "data"): cls.data = {} cls.props = bpy.context.scene.BIMModelProperties - cls.load_constr_classes() + cls.load_ifc_classes() cls.load_constr_types() cls.load_constr_types_browser() cls.load_preview_constr_types() @classmethod - def load_constr_classes(cls): - cls.data["constr_classes"] = cls.constr_classes() + def load_ifc_classes(cls): + cls.data["ifc_classes"] = cls.ifc_classes() @classmethod def load_constr_types(cls): @@ -62,7 +62,7 @@ class AuthoringData: cls.data["preview_constr_types"] = preview_icon_ids @classmethod - def constr_classes(cls): + def ifc_classes(cls): results = [] classes = { e.is_a() @@ -74,63 +74,63 @@ class AuthoringData: return results @classmethod - def constr_class_entities(cls, constr_class=None): - constr_classes = cls.data["constr_classes"] - if not constr_classes: + def constr_class_entities(cls, ifc_class=None): + ifc_classes = cls.data["ifc_classes"] + if not ifc_classes: return [] results = [] - if constr_class is None: - constr_class = cls.props.constr_class - if not constr_class and constr_classes: - constr_class = constr_classes[0][0] - if constr_class: - elements = sorted(tool.Ifc.get().by_type(constr_class), key=lambda s: s.Name) + if ifc_class is None: + ifc_class = cls.props.ifc_class + if not ifc_class and ifc_classes: + ifc_class = ifc_classes[0][0] + if ifc_class: + elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: s.Name) results.extend(elements) return results return [] @classmethod - def constr_types(cls, constr_class=None): + def constr_types(cls, ifc_class=None): return [ - (str(e.id()), e.Name, e.Description or "") for e in cls.constr_class_entities(constr_class=constr_class) + (str(e.id()), e.Name, e.Description or "") for e in cls.constr_class_entities(ifc_class=ifc_class) ] @classmethod def constr_types_browser(cls): - return cls.constr_types(constr_class=cls.props.constr_class_browser) + return cls.constr_types(ifc_class=cls.props.ifc_class_browser) @staticmethod - def new_constr_type_info(constr_class): + def new_constr_type_info(ifc_class): constr_type_info = bpy.context.scene.ConstrTypeInfo.add() - constr_type_info.name = constr_class + constr_type_info.name = ifc_class return constr_type_info @classmethod - def assetize_constr_class(cls, constr_class=None): - if constr_class is None: - constr_class = cls.props.constr_class - constr_type_info = cls.constr_type_info(constr_class) - _ = cls.new_constr_type_info(constr_class) if constr_type_info is None else constr_type_info - constr_class_occurrences = cls.constr_class_entities(constr_class) + def assetize_constr_class(cls, ifc_class=None): + if ifc_class is None: + ifc_class = cls.props.ifc_class + constr_type_info = cls.constr_type_info(ifc_class) + _ = cls.new_constr_type_info(ifc_class) if constr_type_info is None else constr_type_info + constr_class_occurrences = cls.constr_class_entities(ifc_class) preview_constr_types = cls.data["preview_constr_types"] for constr_class_entity in constr_class_occurrences: ### handle asset regeneration when library entity is updated ¿? - if (constr_class not in preview_constr_types - or str(constr_class_entity.id()) not in preview_constr_types[constr_class]): + if (ifc_class not in preview_constr_types + or str(constr_class_entity.id()) not in preview_constr_types[ifc_class]): obj = tool.Ifc.get_object(constr_class_entity) - cls.assetize_object(obj, constr_class, constr_class_entity) - constr_type_info = cls.constr_type_info(constr_class) + cls.assetize_object(obj, ifc_class, constr_class_entity) + constr_type_info = cls.constr_type_info(ifc_class) constr_type_info.fully_loaded = True @classmethod - def assetize_object(cls, obj, constr_class, constr_class_entity, from_selection=False): - constr_type_id = constr_class_entity.id() + def assetize_object(cls, obj, ifc_class, ifc_class_entity, from_selection=False): + constr_type_id = ifc_class_entity.id() to_be_deleted = False if obj.type == 'EMPTY': kwargs = {} if not from_selection: - kwargs.update({'constr_class': constr_class, 'constr_type_id': constr_type_id}) + kwargs.update({'ifc_class': ifc_class, 'constr_type_id': constr_type_id}) new_obj = cls.new_constr_type(**kwargs) if new_obj is not None: to_be_deleted = True @@ -138,18 +138,18 @@ class AuthoringData: obj.asset_mark() obj.asset_generate_preview() icon_id = obj.preview.icon_id - if constr_class not in cls.data["preview_constr_types"]: - cls.data["preview_constr_types"][constr_class] = {} - cls.data["preview_constr_types"][constr_class][str(constr_type_id)] = {"icon_id": icon_id, "object": obj} + if ifc_class not in cls.data["preview_constr_types"]: + cls.data["preview_constr_types"][ifc_class] = {} + cls.data["preview_constr_types"][ifc_class][str(constr_type_id)] = {"icon_id": icon_id, "object": obj} if to_be_deleted: for col in obj.users_collection: col.objects.unlink(obj) @classmethod def assetize_constr_type_from_selection(cls): - constr_class_browser = cls.props.constr_class_browser + ifc_class_browser = cls.props.ifc_class_browser constr_type_id_browser = cls.props.constr_type_id_browser - constr_class_occurrences = cls.constr_class_entities(constr_class=constr_class_browser) + constr_class_occurrences = cls.constr_class_entities(ifc_class=ifc_class_browser) constr_class_occurrences = [ entity for entity in constr_class_occurrences if entity.id() == int(constr_type_id_browser) ] @@ -159,46 +159,46 @@ class AuthoringData: obj = tool.Ifc.get_object(constr_class_entity) if obj is None: return False - cls.assetize_object(obj, constr_class_browser, constr_class_entity, from_selection=True) + cls.assetize_object(obj, ifc_class_browser, constr_class_entity, from_selection=True) return True @staticmethod - def constr_type_info(constr_class): - constr_type_infos = [element for element in bpy.context.scene.ConstrTypeInfo if element.name == constr_class] + def constr_type_info(ifc_class): + constr_type_infos = [element for element in bpy.context.scene.ConstrTypeInfo if element.name == ifc_class] return None if len(constr_type_infos) == 0 else constr_type_infos[0] @classmethod - def new_constr_type(cls, constr_class=None, constr_type_id=None): - if constr_class is None: + def new_constr_type(cls, ifc_class=None, constr_type_id=None): + if ifc_class is None: bpy.ops.bim.add_constr_type( - constr_class=cls.props.constr_class_browser, constr_type_id=int(cls.props.constr_type_id_browser) + ifc_class=cls.props.ifc_class_browser, constr_type_id=int(cls.props.constr_type_id_browser) ) else: - cls.props.constr_class = constr_class + cls.props.ifc_class = ifc_class cls.props.constr_type_id = str(constr_type_id) bpy.ops.bim.add_constr_type() return bpy.context.selected_objects[-1] @staticmethod - def constr_type_name_by_id(constr_class, constr_type_id): + def constr_type_name_by_id(ifc_class, constr_type_id): file = IfcStore.get_file() try: constr_class_entity = file.by_id(int(constr_type_id)) except (RuntimeError, ValueError): return None - return constr_class_entity.Name if constr_class_entity.is_a() == constr_class else None + return constr_class_entity.Name if constr_class_entity.is_a() == ifc_class else None @classmethod - def constr_type_id_by_name(cls, constr_class, constr_type): - constr_types = [ct[0] for ct in cls.constr_types(constr_class=constr_class) if ct[1] == constr_type] + def constr_type_id_by_name(cls, ifc_class, constr_type): + constr_types = [ct[0] for ct in cls.constr_types(ifc_class=ifc_class) if ct[1] == constr_type] return None if len(constr_types) == 0 else constr_types[0] @classmethod def consolidate_constr_type(cls): - cls.props.constr_class = cls.props.constr_class_browser + cls.props.ifc_class = cls.props.ifc_class_browser cls.props.constr_type_id = cls.props.constr_type_id_browser @classmethod def setup_constr_type_browser(cls): - cls.props.constr_class_browser = cls.props.constr_class + cls.props.ifc_class_browser = cls.props.ifc_class cls.props.constr_type_id_browser = cls.props.constr_type_id diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 4b0408e83d..5833e08731 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -62,7 +62,7 @@ class AddConstrType(bpy.types.Operator): bl_label = "Add" bl_options = {"REGISTER", "UNDO"} bl_description = "Add Type Instance to the model" - constr_class: bpy.props.StringProperty() + ifc_class: bpy.props.StringProperty() constr_type_id: bpy.props.IntProperty() from_invoke: bpy.props.BoolProperty(default=False) @@ -76,18 +76,18 @@ class AddConstrType(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMModelProperties - constr_class = self.constr_class or props.constr_class + ifc_class = self.ifc_class or props.ifc_class constr_type_id = self.constr_type_id or props.constr_type_id - if not constr_class or not constr_type_id: + if not ifc_class or not constr_type_id: return {"FINISHED"} if self.from_invoke: - props.constr_class = self.constr_class + props.ifc_class = self.ifc_class props.constr_type_id = str(self.constr_type_id) self.file = IfcStore.get_file() - instance_class = ifcopenshell.util.type.get_applicable_entities(constr_class, self.file.schema)[0] + instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema)[0] constr_type = self.file.by_id(int(constr_type_id)) material = ifcopenshell.util.element.get_material(constr_type) @@ -95,7 +95,7 @@ class AddConstrType(bpy.types.Operator): if profile.DumbProfileGenerator(constr_type).generate(): return {"FINISHED"} elif material and material.is_a("IfcMaterialLayerSet"): - if self.generate_layered_element(constr_class, constr_type): + if self.generate_layered_element(ifc_class, constr_type): return {"FINISHED"} if constr_type.is_a("IfcFlowSegmentType") and not constr_type.RepresentationMaps: if mep.MepGenerator(constr_type).generate(): @@ -202,10 +202,10 @@ class DisplayConstrTypes(bpy.types.Operator): AuthoringData.setup_constr_type_browser() props = context.scene.BIMModelProperties if props.unfold_constr_types: - constr_class = props.constr_class_browser - constr_type_info = AuthoringData.constr_type_info(constr_class) + ifc_class = props.ifc_class_browser + constr_type_info = AuthoringData.constr_type_info(ifc_class) if constr_type_info is None or not constr_type_info.fully_loaded: - AuthoringData.assetize_constr_class(constr_class) + AuthoringData.assetize_constr_class(ifc_class) else: prop.update_constr_type_browser(props, context) min_width = 250 @@ -217,9 +217,9 @@ class DisplayConstrTypes(bpy.types.Operator): props = context.scene.BIMModelProperties header_data = self.draw_header(props) if props.unfold_constr_types: - self.draw_by_constr_class(props, header_data) + self.draw_by_ifc_class(props, header_data) else: - self.draw_by_constr_class_and_type(props, header_data) + self.draw_by_ifc_class_and_type(props, header_data) def draw_header(self, props): layout = self.layout @@ -234,10 +234,10 @@ class DisplayConstrTypes(bpy.types.Operator): row.label(text="Select Construction Type:") col1.row().separator(factor=1.5) enabled = True - if AuthoringData.data["constr_classes"]: + if AuthoringData.data["ifc_classes"]: subsplit = col1.split(factor=1./3) subsplit.column().row().label(text="Construction Class:", icon="FILE_VOLUME") - prop_with_search(subsplit.column(), props, "constr_class_browser", text="") + prop_with_search(subsplit.column(), props, "ifc_class_browser", text="") col1.row().separator() else: enabled = False @@ -248,9 +248,9 @@ class DisplayConstrTypes(bpy.types.Operator): col2.row().separator(factor=1) return {"enabled": enabled, "layout": inner_layout, "col1": col1, "col2": col2} - def draw_by_constr_class(self, props, header_data): + def draw_by_ifc_class(self, props, header_data): enabled, layout = [header_data[key] for key in ["enabled", "layout"]] - constr_class_browser = props.constr_class_browser + ifc_class_browser = props.ifc_class_browser num_cols = 3 layout.row().separator(factor=0.25) layout.row().label(text="Construction Types:", icon="FILE_3D") @@ -267,22 +267,22 @@ class DisplayConstrTypes(bpy.types.Operator): row = box.row() if enabled: preview_constr_types = AuthoringData.data["preview_constr_types"] - if constr_class_browser in preview_constr_types: - preview_constr_class = preview_constr_types[constr_class_browser] - if constr_type_id_browser in preview_constr_class: - icon_id = preview_constr_class[constr_type_id_browser]["icon_id"] + if ifc_class_browser in preview_constr_types: + preview_ifc_class = preview_constr_types[ifc_class_browser] + if constr_type_id_browser in preview_ifc_class: + icon_id = preview_ifc_class[constr_type_id_browser]["icon_id"] row.template_icon(icon_value=icon_id, scale=6.) box.row().separator(factor=0.2) row = box.row() split = row.split(factor=0.5) col = split.column() op = col.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") - op.constr_class = constr_class_browser + op.ifc_class = ifc_class_browser op.constr_type_id = constr_type_id_browser col = split.column() op = col.operator("bim.add_constr_type", icon="ADD") op.from_invoke = True - op.constr_class = constr_class_browser + op.ifc_class = ifc_class_browser if constr_type_id_browser.isnumeric(): op.constr_type_id = int(constr_type_id_browser) factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 1.5 @@ -292,9 +292,9 @@ class DisplayConstrTypes(bpy.types.Operator): for _ in range(num_cols - last_row_cols): flow.column() - def draw_by_constr_class_and_type(self, props, header_data): + def draw_by_ifc_class_and_type(self, props, header_data): enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]] - constr_class_browser = props.constr_class_browser + ifc_class_browser = props.ifc_class_browser constr_type_id_browser = props.constr_type_id_browser if AuthoringData.data["constr_types_ids_browser"]: subsplit = col1.split(factor=1. / 3) @@ -307,11 +307,11 @@ class DisplayConstrTypes(bpy.types.Operator): row = col1.row() row.enabled = enabled op = row.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") - op.constr_class = constr_class_browser + op.ifc_class = ifc_class_browser op.constr_type_id = constr_type_id_browser op = row.operator("bim.add_constr_type", icon="ADD") op.from_invoke = True - op.constr_class = constr_class_browser + op.ifc_class = ifc_class_browser if constr_type_id_browser.isnumeric(): op.constr_type_id = int(constr_type_id_browser) col2.row().separator(factor=1.25) @@ -328,7 +328,7 @@ class SelectConstructionType(bpy.types.Operator): bl_label = "Select" bl_options = {"REGISTER", "UNDO"} bl_description = "Pick Type Instance as selection for subsequent operations" - constr_class: bpy.props.StringProperty() + ifc_class: bpy.props.StringProperty() constr_type_id: bpy.props.StringProperty() def invoke(self, context, event): @@ -337,8 +337,8 @@ class SelectConstructionType(bpy.types.Operator): def execute(self, context): props = context.scene.BIMModelProperties - if self.constr_class != "": - props.constr_class = self.constr_class + if self.ifc_class != "": + props.ifc_class = self.ifc_class AuthoringData.load_constr_types() if self.constr_type_id != "": props.constr_type_id = self.constr_type_id diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 122c850302..f65a3cfe1d 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -21,10 +21,10 @@ from blenderbim.bim.module.model.data import AuthoringData from bpy.types import PropertyGroup -def get_constr_class(self, context): +def get_ifc_class(self, context): if not AuthoringData.is_loaded: AuthoringData.load() - return AuthoringData.data["constr_classes"] + return AuthoringData.data["ifc_classes"] def get_constr_type(self, context): @@ -40,34 +40,34 @@ def get_constr_type_browser(self, context): def update_icon_id(self, context): - constr_class_browser = self.constr_class_browser + ifc_class_browser = self.ifc_class_browser constr_type_id_browser = self.constr_type_id_browser - constr_type_browser = AuthoringData.constr_type_name_by_id(constr_class_browser, constr_type_id_browser) - if ((constr_class_browser not in AuthoringData.data["preview_constr_types"] - or constr_type_id_browser not in AuthoringData.data["preview_constr_types"][constr_class_browser]) + constr_type_browser = AuthoringData.constr_type_name_by_id(ifc_class_browser, constr_type_id_browser) + if ((ifc_class_browser not in AuthoringData.data["preview_constr_types"] + or constr_type_id_browser not in AuthoringData.data["preview_constr_types"][ifc_class_browser]) and constr_type_browser is not None): if not AuthoringData.assetize_constr_type_from_selection(): return - self.icon_id = AuthoringData.data["preview_constr_types"][constr_class_browser][constr_type_id_browser]["icon_id"] + self.icon_id = AuthoringData.data["preview_constr_types"][ifc_class_browser][constr_type_id_browser]["icon_id"] -def update_constr_class(self, context): - AuthoringData.load_constr_classes() +def update_ifc_class(self, context): + AuthoringData.load_ifc_classes() AuthoringData.load_constr_types() self.constr_type_id = AuthoringData.data["constr_types_ids"][0][0] -def update_constr_class_browser(self, context): - AuthoringData.load_constr_classes() +def update_ifc_class_browser(self, context): + AuthoringData.load_ifc_classes() AuthoringData.load_constr_types_browser() props = context.scene.BIMModelProperties if props.unfold_constr_types: - constr_class_browser = props.constr_class_browser - constr_type_info = AuthoringData.constr_type_info(constr_class_browser) + ifc_class_browser = props.ifc_class_browser + constr_type_info = AuthoringData.constr_type_info(ifc_class_browser) if constr_type_info is None or not constr_type_info.fully_loaded: - curr_selection = props.constr_class, props.constr_type_id - AuthoringData.assetize_constr_class(constr_class_browser) - props.constr_class, props.constr_type_id = curr_selection + curr_selection = props.ifc_class, props.constr_type_id + AuthoringData.assetize_constr_class(ifc_class_browser) + props.ifc_class, props.constr_type_id = curr_selection else: self.constr_type_id_browser = AuthoringData.data["constr_types_ids_browser"][0][0] @@ -78,14 +78,14 @@ def update_constr_type(self, context): def update_constr_type_by_name(self, context): AuthoringData.load_constr_types() - constr_type_id = AuthoringData.constr_type_id_by_name(self.constr_class, self.constr_type) + constr_type_id = AuthoringData.constr_type_id_by_name(self.ifc_class, self.constr_type) if constr_type_id is not None: self.constr_type_id = constr_type_id def update_constr_type_browser_by_name(self, context): AuthoringData.load_constr_types_browser() - constr_type_id_browser = AuthoringData.constr_type_id_by_name(self.constr_class_browser, self.constr_type_browser) + constr_type_id_browser = AuthoringData.constr_type_id_by_name(self.ifc_class_browser, self.constr_type_browser) if constr_type_id_browser is not None: self.constr_type_id_browser = constr_type_id_browser @@ -96,13 +96,13 @@ def update_constr_type_browser(self, context): def update_unfold_constr_type(self, context): - update_constr_class_browser(self, context) + update_ifc_class_browser(self, context) class BIMModelProperties(PropertyGroup): - constr_class: bpy.props.EnumProperty(items=get_constr_class, name="Construction Class", update=update_constr_class) - constr_class_browser: bpy.props.EnumProperty( - items=get_constr_class, name="Construction Class", update=update_constr_class_browser + ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class) + ifc_class_browser: bpy.props.EnumProperty( + items=get_ifc_class, name="Construction Class", update=update_ifc_class_browser ) constr_type: bpy.props.StringProperty(update=update_constr_type_by_name) constr_type_id: bpy.props.EnumProperty( @@ -120,13 +120,13 @@ class BIMModelProperties(PropertyGroup): ) occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") getter_enum = { - "constr_class_browser": get_constr_class, + "ifc_class_browser": get_ifc_class, "constr_type_browser": get_constr_type_browser } def get_constr_type_info(self, context): - return AuthoringData.relating_types(constr_class=self.name) + return AuthoringData.relating_types(ifc_class=self.name) class ConstrTypeInfo(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 6a52dc8e47..8828d40e7a 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -62,40 +62,40 @@ class BimTool(WorkSpaceTool): row.label(text="No IFC Project", icon="ERROR") return - constr_classes = AuthoringData.data["constr_classes"] + ifc_classes = AuthoringData.data["ifc_classes"] constr_types_ids = AuthoringData.data["constr_types_ids"] if is_tool_header: row.operator("bim.help_constr_types", text="", icon="QUESTION") - if constr_classes and is_tool_header: + if ifc_classes and is_tool_header: row.label(text="", icon="BLANK1") row.operator("bim.display_constr_types", icon="COLLAPSEMENU") - constr_class = props.constr_class + ifc_class = props.ifc_class constr_type_id = props.constr_type_id - constr_type = AuthoringData.constr_type_name_by_id(constr_class, constr_type_id) + constr_type = AuthoringData.constr_type_name_by_id(ifc_class, constr_type_id) if is_tool_header: row.label(text="", icon="BLANK1") row = layout.row(align=True) row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_A") - if constr_classes: + if ifc_classes: row.label(text=f" Add") row.label(text="", icon="FILE_VOLUME") - row.label(text=constr_class) + row.label(text=ifc_class) row.label(text="", icon="FILE_3D") row.label(text=f"{constr_type} ") else: row.label(text=f" Add instance") else: - txt_constr_class = constr_class if constr_classes else "No Construction Class" + txt_ifc_class = ifc_class if ifc_classes else "No Construction Class" txt_constr_type = constr_type if constr_types_ids else "No Construction Type" row = layout.row(align=True) row.label(text="Selected Construction Type:") row = layout.row(align=True) - row.label(text=txt_constr_class, icon="FILE_VOLUME") + row.label(text=txt_ifc_class, icon="FILE_VOLUME") row = layout.row(align=True) row.label(text=txt_constr_type, icon="FILE_3D") row = layout.row(align=True) @@ -103,8 +103,8 @@ class BimTool(WorkSpaceTool): row.label(text="", icon="EVENT_A") row.label(text=f" Add Type Instance") - if AuthoringData.data["constr_classes"]: - if constr_class == "IfcWallType": + if AuthoringData.data["ifc_classes"]: + if ifc_class == "IfcWallType": row = layout.row() row.label(text="Join") row = layout.row(align=True) @@ -126,7 +126,7 @@ class BimTool(WorkSpaceTool): row.label(text="", icon="EVENT_SHIFT") row.label(text="Split", icon="EVENT_S") - if constr_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"): + if ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"): row = layout.row() row.label(text="Join") row = layout.row(align=True) diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index af5afb882e..4d79197486 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -144,7 +144,7 @@ Scenario: Remove representation - remove an instanced representation from an act And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" And I press "bim.add_constr_type" @@ -163,7 +163,7 @@ Scenario: Remove representation - remove an instanced representation from an act And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" And I press "bim.add_constr_type" @@ -337,7 +337,7 @@ Scenario: Override duplicate move - copying a type instance with a representatio And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" And I press "bim.add_constr_type" diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index 247f4fed45..ce0dc22616 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -8,7 +8,7 @@ Scenario: Add type instance - add from a mesh And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" When I press "bim.add_constr_type" @@ -21,7 +21,7 @@ Scenario: Add type instance - add from an empty And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{empty}" When I press "bim.add_constr_type" @@ -34,7 +34,7 @@ Scenario: Add type instance - add a mesh where existing instances have changed c And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMModelProperties.constr_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" And I press "bim.add_constr_type" @@ -53,10 +53,10 @@ Scenario: Preview one type on the Construction Type Browser And I load the demo construction library When I display the construction type browser And I preview only one asset on the construction type browser - And I set "scene.BIMModelProperties.constr_class_browser" to "IfcColumnType" + And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcColumnType" And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO2" And I select the browser construction type - Then "scene.BIMModelProperties.constr_class" is "IfcColumnType" + Then "scene.BIMModelProperties.ifc_class" is "IfcColumnType" And construction type is DEMO2 And objects starting with "IfcColumn/" do not exist And the construction type "IfcColumnType"/"DEMO2" has a preview @@ -66,8 +66,8 @@ Scenario: Preview one class on the construction type browser And I load the demo construction library When I display the construction type browser And I preview all available assets on the construction type browser - And I set "scene.BIMModelProperties.constr_class_browser" to "IfcWallType" - Then "scene.BIMModelProperties.constr_class_browser" is "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcWallType" + Then "scene.BIMModelProperties.ifc_class_browser" is "IfcWallType" And objects starting with "IfcWall/" do not exist And all construction types for "IfcWallType" have a preview @@ -76,7 +76,7 @@ Scenario: Add one type from the Construction Type Browser And I load the demo construction library When I display the construction type browser And I preview only one asset on the construction type browser - And I set "scene.BIMModelProperties.constr_class_browser" to "IfcColumnType" + And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcColumnType" And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO2" And I add the browser construction type Then the object "IfcColumn/Column" exists diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 65b2a29863..d5927207ed 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -575,27 +575,27 @@ def the_object_name_has_no_modifiers(name): assert len(the_object_name_exists(name).modifiers) == 0 -@then(parsers.parse('the construction type "{constr_class}"/"{constr_type}" has a preview')) -def the_construction_type_has_a_preview(constr_class, constr_type): +@then(parsers.parse('the construction type "{ifc_class}"/"{constr_type}" has a preview')) +def the_construction_type_has_a_preview(ifc_class, constr_type): if "preview_constr_types" not in AuthoringData.data: assert False, 'There are no previews loaded' preview_constr_types = AuthoringData.data["preview_constr_types"] - if constr_class not in preview_constr_types: - assert False, f'Construction class {constr_class} has no available previews' - constr_type_id = AuthoringData.constr_type_id_by_name(constr_class, constr_type) + if ifc_class not in preview_constr_types: + assert False, f'Construction class {ifc_class} has no available previews' + constr_type_id = AuthoringData.constr_type_id_by_name(ifc_class, constr_type) if constr_type_id is None: - assert False, f'No construction type {constr_class}/{constr_type} was found' - if constr_type_id not in preview_constr_types[constr_class]: - assert False, f'Construction type {constr_class}/{constr_type} has no available previews' - preview_data = preview_constr_types[constr_class][constr_type_id] + assert False, f'No construction type {ifc_class}/{constr_type} was found' + if constr_type_id not in preview_constr_types[ifc_class]: + assert False, f'Construction type {ifc_class}/{constr_type} has no available previews' + preview_data = preview_constr_types[ifc_class][constr_type_id] if 'icon_id' not in preview_data: - assert False, f'Construction type {constr_class}/{constr_type} has a preview, but no assigned icon_id' + assert False, f'Construction type {ifc_class}/{constr_type} has a preview, but no assigned icon_id' icon_id = preview_data["icon_id"] if not isinstance(icon_id, int): - assert False, f'Construction type {constr_class}/{constr_type} has an invalid icon_id {icon_id}' + assert False, f'Construction type {ifc_class}/{constr_type} has an invalid icon_id {icon_id}' # Note: icon_id must be > 0 in UI mode, but asset_generate_preview() doesn't work headlessly -> skipping for now # if icon_id == 0: - # assert False, f'Construction type {constr_class}/{constr_type} has the default null value for icon_id' + # assert False, f'Construction type {ifc_class}/{constr_type} has the default null value for icon_id' assert True @@ -605,16 +605,16 @@ def there_is_a_construction_type_preview(): assert props.icon_id > 0, f"There isn't a Construction Type preview" -@then(parsers.parse('all construction types for "{constr_class}" have a preview')) -def all_construction_types_have_a_preview(constr_class): +@then(parsers.parse('all construction types for "{ifc_class}" have a preview')) +def all_construction_types_have_a_preview(ifc_class): if "preview_constr_types" not in AuthoringData.data: assert False, 'There are no previews loaded' preview_constr_types = AuthoringData.data["preview_constr_types"] - if constr_class not in preview_constr_types: - assert False, f'Construction class {constr_class} has no available previews' - constr_class_occurrences = AuthoringData.constr_class_entities(constr_class) + if ifc_class not in preview_constr_types: + assert False, f'Construction class {ifc_class} has no available previews' + constr_class_occurrences = AuthoringData.constr_class_entities(ifc_class) for constr_class_entity in constr_class_occurrences: - the_construction_type_has_a_preview(constr_class, constr_class_entity.Name) + the_construction_type_has_a_preview(ifc_class, constr_class_entity.Name) @given("I load the demo construction library") @@ -647,7 +647,7 @@ def i_preview_all_construction_types(): def i_select_the_active_construction_type(): props = bpy.context.scene.BIMModelProperties bpy.ops.bim.select_construction_type( - constr_class=props.constr_class_browser, constr_type_id=props.constr_type_id_browser + ifc_class=props.ifc_class_browser, constr_type_id=props.constr_type_id_browser ) @@ -656,14 +656,14 @@ def i_select_the_active_construction_type(): def i_add_the_active_construction_type(): props = bpy.context.scene.BIMModelProperties bpy.ops.bim.add_constr_type( - constr_class=props.constr_class_browser, constr_type_id=int(props.constr_type_id_browser) + ifc_class=props.ifc_class_browser, constr_type_id=int(props.constr_type_id_browser) ) @then(parsers.parse("browser construction type is {constr_type_name}")) def browser_construction_type(constr_type_name): props = bpy.context.scene.BIMModelProperties - constr_type_browser = AuthoringData.constr_type_name_by_id(props.constr_class_browser, props.constr_type_id_browser) + constr_type_browser = AuthoringData.constr_type_name_by_id(props.ifc_class_browser, props.constr_type_id_browser) assert constr_type_browser == constr_type_name, (f"Construction Type is a {constr_type_browser}, not " + f"a {constr_type_name}") @@ -671,7 +671,7 @@ def browser_construction_type(constr_type_name): @then(parsers.parse("construction type is {constr_type_name}")) def construction_type(constr_type_name): props = bpy.context.scene.BIMModelProperties - constr_type = AuthoringData.constr_type_name_by_id(props.constr_class, props.constr_type_id) + constr_type = AuthoringData.constr_type_name_by_id(props.ifc_class, props.constr_type_id) assert constr_type == constr_type_name, f"Construction Type is a {constr_type}, not a {constr_type_name}" From 40854eb3d38af430270d840c4f6665d746d9f30e Mon Sep 17 00:00:00 2001 From: Carlos Villagrasa Date: Wed, 27 Jul 2022 08:18:34 +0200 Subject: [PATCH 18/19] Renaming constr_type back to relating_type --- .../blenderbim/bim/module/model/data.py | 82 ++++++++--------- .../blenderbim/bim/module/model/product.py | 88 +++++++++--------- .../blenderbim/bim/module/model/prop.py | 90 +++++++++---------- .../blenderbim/bim/module/model/workspace.py | 18 ++-- .../blenderbim/bim/module/type/ui.py | 2 +- .../test/bim/feature/geometry.feature | 20 ++--- src/blenderbim/test/bim/feature/model.feature | 18 ++-- src/blenderbim/test/bim/test_feature.py | 52 +++++------ 8 files changed, 185 insertions(+), 185 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index d5298bded5..b8d74ecf54 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -41,8 +41,8 @@ class AuthoringData: cls.data = {} cls.props = bpy.context.scene.BIMModelProperties cls.load_ifc_classes() - cls.load_constr_types() - cls.load_constr_types_browser() + cls.load_relating_types() + cls.load_relating_types_browser() cls.load_preview_constr_types() @classmethod @@ -50,12 +50,12 @@ class AuthoringData: cls.data["ifc_classes"] = cls.ifc_classes() @classmethod - def load_constr_types(cls): - cls.data["constr_types_ids"] = cls.constr_types() + def load_relating_types(cls): + cls.data["relating_types_ids"] = cls.relating_types() @classmethod - def load_constr_types_browser(cls): - cls.data["constr_types_ids_browser"] = cls.constr_types_browser() + def load_relating_types_browser(cls): + cls.data["relating_types_ids_browser"] = cls.relating_types_browser() @classmethod def load_preview_constr_types(cls): @@ -90,27 +90,27 @@ class AuthoringData: return [] @classmethod - def constr_types(cls, ifc_class=None): + def relating_types(cls, ifc_class=None): return [ (str(e.id()), e.Name, e.Description or "") for e in cls.constr_class_entities(ifc_class=ifc_class) ] @classmethod - def constr_types_browser(cls): - return cls.constr_types(ifc_class=cls.props.ifc_class_browser) + def relating_types_browser(cls): + return cls.relating_types(ifc_class=cls.props.ifc_class_browser) @staticmethod - def new_constr_type_info(ifc_class): - constr_type_info = bpy.context.scene.ConstrTypeInfo.add() - constr_type_info.name = ifc_class - return constr_type_info + def new_relating_type_info(ifc_class): + relating_type_info = bpy.context.scene.ConstrTypeInfo.add() + relating_type_info.name = ifc_class + return relating_type_info @classmethod def assetize_constr_class(cls, ifc_class=None): if ifc_class is None: ifc_class = cls.props.ifc_class - constr_type_info = cls.constr_type_info(ifc_class) - _ = cls.new_constr_type_info(ifc_class) if constr_type_info is None else constr_type_info + relating_type_info = cls.relating_type_info(ifc_class) + _ = cls.new_relating_type_info(ifc_class) if relating_type_info is None else relating_type_info constr_class_occurrences = cls.constr_class_entities(ifc_class) preview_constr_types = cls.data["preview_constr_types"] for constr_class_entity in constr_class_occurrences: @@ -120,18 +120,18 @@ class AuthoringData: or str(constr_class_entity.id()) not in preview_constr_types[ifc_class]): obj = tool.Ifc.get_object(constr_class_entity) cls.assetize_object(obj, ifc_class, constr_class_entity) - constr_type_info = cls.constr_type_info(ifc_class) - constr_type_info.fully_loaded = True + relating_type_info = cls.relating_type_info(ifc_class) + relating_type_info.fully_loaded = True @classmethod def assetize_object(cls, obj, ifc_class, ifc_class_entity, from_selection=False): - constr_type_id = ifc_class_entity.id() + relating_type_id = ifc_class_entity.id() to_be_deleted = False if obj.type == 'EMPTY': kwargs = {} if not from_selection: - kwargs.update({'ifc_class': ifc_class, 'constr_type_id': constr_type_id}) - new_obj = cls.new_constr_type(**kwargs) + kwargs.update({'ifc_class': ifc_class, 'relating_type_id': relating_type_id}) + new_obj = cls.new_relating_type(**kwargs) if new_obj is not None: to_be_deleted = True obj = new_obj @@ -140,18 +140,18 @@ class AuthoringData: icon_id = obj.preview.icon_id if ifc_class not in cls.data["preview_constr_types"]: cls.data["preview_constr_types"][ifc_class] = {} - cls.data["preview_constr_types"][ifc_class][str(constr_type_id)] = {"icon_id": icon_id, "object": obj} + cls.data["preview_constr_types"][ifc_class][str(relating_type_id)] = {"icon_id": icon_id, "object": obj} if to_be_deleted: for col in obj.users_collection: col.objects.unlink(obj) @classmethod - def assetize_constr_type_from_selection(cls): + def assetize_relating_type_from_selection(cls): ifc_class_browser = cls.props.ifc_class_browser - constr_type_id_browser = cls.props.constr_type_id_browser + relating_type_id_browser = cls.props.relating_type_id_browser constr_class_occurrences = cls.constr_class_entities(ifc_class=ifc_class_browser) constr_class_occurrences = [ - entity for entity in constr_class_occurrences if entity.id() == int(constr_type_id_browser) + entity for entity in constr_class_occurrences if entity.id() == int(relating_type_id_browser) ] if len(constr_class_occurrences) == 0: return False @@ -163,42 +163,42 @@ class AuthoringData: return True @staticmethod - def constr_type_info(ifc_class): - constr_type_infos = [element for element in bpy.context.scene.ConstrTypeInfo if element.name == ifc_class] - return None if len(constr_type_infos) == 0 else constr_type_infos[0] + def relating_type_info(ifc_class): + relating_type_infos = [element for element in bpy.context.scene.ConstrTypeInfo if element.name == ifc_class] + return None if len(relating_type_infos) == 0 else relating_type_infos[0] @classmethod - def new_constr_type(cls, ifc_class=None, constr_type_id=None): + def new_relating_type(cls, ifc_class=None, relating_type_id=None): if ifc_class is None: - bpy.ops.bim.add_constr_type( - ifc_class=cls.props.ifc_class_browser, constr_type_id=int(cls.props.constr_type_id_browser) + bpy.ops.bim.add_relating_type( + ifc_class=cls.props.ifc_class_browser, relating_type_id=int(cls.props.relating_type_id_browser) ) else: cls.props.ifc_class = ifc_class - cls.props.constr_type_id = str(constr_type_id) - bpy.ops.bim.add_constr_type() + cls.props.relating_type_id = str(relating_type_id) + bpy.ops.bim.add_relating_type() return bpy.context.selected_objects[-1] @staticmethod - def constr_type_name_by_id(ifc_class, constr_type_id): + def relating_type_name_by_id(ifc_class, relating_type_id): file = IfcStore.get_file() try: - constr_class_entity = file.by_id(int(constr_type_id)) + constr_class_entity = file.by_id(int(relating_type_id)) except (RuntimeError, ValueError): return None return constr_class_entity.Name if constr_class_entity.is_a() == ifc_class else None @classmethod - def constr_type_id_by_name(cls, ifc_class, constr_type): - constr_types = [ct[0] for ct in cls.constr_types(ifc_class=ifc_class) if ct[1] == constr_type] - return None if len(constr_types) == 0 else constr_types[0] + def relating_type_id_by_name(cls, ifc_class, relating_type): + relating_types = [ct[0] for ct in cls.relating_types(ifc_class=ifc_class) if ct[1] == relating_type] + return None if len(relating_types) == 0 else relating_types[0] @classmethod - def consolidate_constr_type(cls): + def consolidate_relating_type(cls): cls.props.ifc_class = cls.props.ifc_class_browser - cls.props.constr_type_id = cls.props.constr_type_id_browser + cls.props.relating_type_id = cls.props.relating_type_id_browser @classmethod - def setup_constr_type_browser(cls): + def setup_relating_type_browser(cls): cls.props.ifc_class_browser = cls.props.ifc_class - cls.props.constr_type_id_browser = cls.props.constr_type_id + cls.props.relating_type_id_browser = cls.props.relating_type_id diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 5833e08731..bc70dd2ebb 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -58,12 +58,12 @@ def add_empty_type_button(self, context): class AddConstrType(bpy.types.Operator): - bl_idname = "bim.add_constr_type" + bl_idname = "bim.add_relating_type" bl_label = "Add" bl_options = {"REGISTER", "UNDO"} bl_description = "Add Type Instance to the model" ifc_class: bpy.props.StringProperty() - constr_type_id: bpy.props.IntProperty() + relating_type_id: bpy.props.IntProperty() from_invoke: bpy.props.BoolProperty(default=False) def invoke(self, context, event): @@ -77,28 +77,28 @@ class AddConstrType(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMModelProperties ifc_class = self.ifc_class or props.ifc_class - constr_type_id = self.constr_type_id or props.constr_type_id + relating_type_id = self.relating_type_id or props.relating_type_id - if not ifc_class or not constr_type_id: + if not ifc_class or not relating_type_id: return {"FINISHED"} if self.from_invoke: props.ifc_class = self.ifc_class - props.constr_type_id = str(self.constr_type_id) + props.relating_type_id = str(self.relating_type_id) self.file = IfcStore.get_file() instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema)[0] - constr_type = self.file.by_id(int(constr_type_id)) - material = ifcopenshell.util.element.get_material(constr_type) + relating_type = self.file.by_id(int(relating_type_id)) + material = ifcopenshell.util.element.get_material(relating_type) if material and material.is_a("IfcMaterialProfileSet"): - if profile.DumbProfileGenerator(constr_type).generate(): + if profile.DumbProfileGenerator(relating_type).generate(): return {"FINISHED"} elif material and material.is_a("IfcMaterialLayerSet"): - if self.generate_layered_element(ifc_class, constr_type): + if self.generate_layered_element(ifc_class, relating_type): return {"FINISHED"} - if constr_type.is_a("IfcFlowSegmentType") and not constr_type.RepresentationMaps: - if mep.MepGenerator(constr_type).generate(): + if relating_type.is_a("IfcFlowSegmentType") and not relating_type.RepresentationMaps: + if mep.MepGenerator(relating_type).generate(): return {"FINISHED"} building_obj = None @@ -127,14 +127,14 @@ class AddConstrType(bpy.types.Operator): ] mesh = bpy.data.meshes.new(name="Instance") mesh.from_pydata(verts, edges, faces) - obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(constr_type, instance_class), mesh) + obj = bpy.data.objects.new(tool.Model.generate_occurrence_name(relating_type, instance_class), mesh) obj.location = context.scene.cursor.location collection = context.view_layer.active_layer_collection.collection collection.objects.link(obj) collection_obj = bpy.data.objects.get(collection.name) bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class) element = tool.Ifc.get_entity(obj) - blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=constr_type) + blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type) if building_obj: if instance_class in ["IfcWindow", "IfcDoor"]: @@ -149,7 +149,7 @@ class AddConstrType(bpy.types.Operator): obj.location[2] = collection_obj.location[2] - min([v[2] for v in obj.bound_box]) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - for port in ifcopenshell.util.system.get_ports(constr_type): + for port in ifcopenshell.util.system.get_ports(relating_type): mat = ifcopenshell.util.placement.get_local_placement(port.ObjectPlacement) mat[0][3] *= unit_scale mat[1][3] *= unit_scale @@ -188,7 +188,7 @@ class AddConstrType(bpy.types.Operator): class DisplayConstrTypes(bpy.types.Operator): - bl_idname = "bim.display_constr_types" + bl_idname = "bim.display_relating_types" bl_label = "Browse Construction Types" bl_options = {"REGISTER", "UNDO"} bl_description = "Display all available Construction Types to add new instances" @@ -199,15 +199,15 @@ class DisplayConstrTypes(bpy.types.Operator): def invoke(self, context, event): if not AuthoringData.is_loaded: AuthoringData.load() - AuthoringData.setup_constr_type_browser() + AuthoringData.setup_relating_type_browser() props = context.scene.BIMModelProperties - if props.unfold_constr_types: + if props.unfold_relating_types: ifc_class = props.ifc_class_browser - constr_type_info = AuthoringData.constr_type_info(ifc_class) - if constr_type_info is None or not constr_type_info.fully_loaded: + relating_type_info = AuthoringData.relating_type_info(ifc_class) + if relating_type_info is None or not relating_type_info.fully_loaded: AuthoringData.assetize_constr_class(ifc_class) else: - prop.update_constr_type_browser(props, context) + prop.update_relating_type_browser(props, context) min_width = 250 width_scaling = 5 ** -1 width = max([min_width, int(width_scaling * context.region.width)]) @@ -216,7 +216,7 @@ class DisplayConstrTypes(bpy.types.Operator): def draw(self, context): props = context.scene.BIMModelProperties header_data = self.draw_header(props) - if props.unfold_constr_types: + if props.unfold_relating_types: self.draw_by_ifc_class(props, header_data) else: self.draw_by_ifc_class_and_type(props, header_data) @@ -228,7 +228,7 @@ class DisplayConstrTypes(bpy.types.Operator): split = inner_layout.split(align=True, factor=2./3) col1 = split.column(align=True) row = col1.row() - row.prop(data=props, property="unfold_constr_types", text="Preview All Construction Types") + row.prop(data=props, property="unfold_relating_types", text="Preview All Construction Types") col1.row().separator(factor=1) row = col1.row() row.label(text="Select Construction Type:") @@ -244,7 +244,7 @@ class DisplayConstrTypes(bpy.types.Operator): col2 = split.column(align=True) subsplit = col2.split(factor=0.9) subcol = [subsplit.column() for _ in range(2)][-1] - subcol.operator("bim.help_constr_types", text="", icon="QUESTION") + subcol.operator("bim.help_relating_types", text="", icon="QUESTION") col2.row().separator(factor=1) return {"enabled": enabled, "layout": inner_layout, "col1": col1, "col2": col2} @@ -256,9 +256,9 @@ class DisplayConstrTypes(bpy.types.Operator): layout.row().label(text="Construction Types:", icon="FILE_3D") layout.row().separator(factor=0.25) flow = layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True) - constr_types_browser = AuthoringData.constr_types_browser() - num_types = len(constr_types_browser) - for idx, (constr_type_id_browser, name, desc) in enumerate(constr_types_browser): + relating_types_browser = AuthoringData.relating_types_browser() + num_types = len(relating_types_browser) + for idx, (relating_type_id_browser, name, desc) in enumerate(relating_types_browser): outer_col = flow.column() box = outer_col.box() row = box.row() @@ -269,8 +269,8 @@ class DisplayConstrTypes(bpy.types.Operator): preview_constr_types = AuthoringData.data["preview_constr_types"] if ifc_class_browser in preview_constr_types: preview_ifc_class = preview_constr_types[ifc_class_browser] - if constr_type_id_browser in preview_ifc_class: - icon_id = preview_ifc_class[constr_type_id_browser]["icon_id"] + if relating_type_id_browser in preview_ifc_class: + icon_id = preview_ifc_class[relating_type_id_browser]["icon_id"] row.template_icon(icon_value=icon_id, scale=6.) box.row().separator(factor=0.2) row = box.row() @@ -278,13 +278,13 @@ class DisplayConstrTypes(bpy.types.Operator): col = split.column() op = col.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") op.ifc_class = ifc_class_browser - op.constr_type_id = constr_type_id_browser + op.relating_type_id = relating_type_id_browser col = split.column() - op = col.operator("bim.add_constr_type", icon="ADD") + op = col.operator("bim.add_relating_type", icon="ADD") op.from_invoke = True op.ifc_class = ifc_class_browser - if constr_type_id_browser.isnumeric(): - op.constr_type_id = int(constr_type_id_browser) + if relating_type_id_browser.isnumeric(): + op.relating_type_id = int(relating_type_id_browser) factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 1.5 outer_col.row().separator(factor=factor) last_row_cols = num_types % num_cols @@ -295,11 +295,11 @@ class DisplayConstrTypes(bpy.types.Operator): def draw_by_ifc_class_and_type(self, props, header_data): enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]] ifc_class_browser = props.ifc_class_browser - constr_type_id_browser = props.constr_type_id_browser - if AuthoringData.data["constr_types_ids_browser"]: + relating_type_id_browser = props.relating_type_id_browser + if AuthoringData.data["relating_types_ids_browser"]: subsplit = col1.split(factor=1. / 3) subsplit.column().row().label(text="Construction Type:", icon="FILE_3D") - prop_with_search(subsplit.column(), props, "constr_type_id_browser", text="") + prop_with_search(subsplit.column(), props, "relating_type_id_browser", text="") col1.row().separator() else: enabled = False @@ -308,12 +308,12 @@ class DisplayConstrTypes(bpy.types.Operator): row.enabled = enabled op = row.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") op.ifc_class = ifc_class_browser - op.constr_type_id = constr_type_id_browser - op = row.operator("bim.add_constr_type", icon="ADD") + op.relating_type_id = relating_type_id_browser + op = row.operator("bim.add_relating_type", icon="ADD") op.from_invoke = True op.ifc_class = ifc_class_browser - if constr_type_id_browser.isnumeric(): - op.constr_type_id = int(constr_type_id_browser) + if relating_type_id_browser.isnumeric(): + op.relating_type_id = int(relating_type_id_browser) col2.row().separator(factor=1.25) split = col2.split(factor=0.025) col = [split.column() for _ in range(2)][-1] @@ -329,7 +329,7 @@ class SelectConstructionType(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} bl_description = "Pick Type Instance as selection for subsequent operations" ifc_class: bpy.props.StringProperty() - constr_type_id: bpy.props.StringProperty() + relating_type_id: bpy.props.StringProperty() def invoke(self, context, event): close_operator_panel(event) @@ -339,14 +339,14 @@ class SelectConstructionType(bpy.types.Operator): props = context.scene.BIMModelProperties if self.ifc_class != "": props.ifc_class = self.ifc_class - AuthoringData.load_constr_types() - if self.constr_type_id != "": - props.constr_type_id = self.constr_type_id + AuthoringData.load_relating_types() + if self.relating_type_id != "": + props.relating_type_id = self.relating_type_id return {"FINISHED"} class HelpConstrTypes(bpy.types.Operator): - bl_idname = "bim.help_constr_types" + bl_idname = "bim.help_relating_types" bl_label = "Construction Types Help" bl_options = {"REGISTER", "UNDO"} bl_description = "Click to read some contextual help" diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index f65a3cfe1d..9d6b7b3794 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -27,75 +27,75 @@ def get_ifc_class(self, context): return AuthoringData.data["ifc_classes"] -def get_constr_type(self, context): +def get_relating_type(self, context): if not AuthoringData.is_loaded: AuthoringData.load() - return AuthoringData.data["constr_types_ids"] + return AuthoringData.data["relating_types_ids"] -def get_constr_type_browser(self, context): +def get_relating_type_browser(self, context): if not AuthoringData.is_loaded: AuthoringData.load() - return AuthoringData.data["constr_types_ids_browser"] + return AuthoringData.data["relating_types_ids_browser"] def update_icon_id(self, context): ifc_class_browser = self.ifc_class_browser - constr_type_id_browser = self.constr_type_id_browser - constr_type_browser = AuthoringData.constr_type_name_by_id(ifc_class_browser, constr_type_id_browser) + relating_type_id_browser = self.relating_type_id_browser + relating_type_browser = AuthoringData.relating_type_name_by_id(ifc_class_browser, relating_type_id_browser) if ((ifc_class_browser not in AuthoringData.data["preview_constr_types"] - or constr_type_id_browser not in AuthoringData.data["preview_constr_types"][ifc_class_browser]) - and constr_type_browser is not None): - if not AuthoringData.assetize_constr_type_from_selection(): + or relating_type_id_browser not in AuthoringData.data["preview_constr_types"][ifc_class_browser]) + and relating_type_browser is not None): + if not AuthoringData.assetize_relating_type_from_selection(): return - self.icon_id = AuthoringData.data["preview_constr_types"][ifc_class_browser][constr_type_id_browser]["icon_id"] + self.icon_id = AuthoringData.data["preview_constr_types"][ifc_class_browser][relating_type_id_browser]["icon_id"] def update_ifc_class(self, context): AuthoringData.load_ifc_classes() - AuthoringData.load_constr_types() - self.constr_type_id = AuthoringData.data["constr_types_ids"][0][0] + AuthoringData.load_relating_types() + self.relating_type_id = AuthoringData.data["relating_types_ids"][0][0] def update_ifc_class_browser(self, context): AuthoringData.load_ifc_classes() - AuthoringData.load_constr_types_browser() + AuthoringData.load_relating_types_browser() props = context.scene.BIMModelProperties - if props.unfold_constr_types: + if props.unfold_relating_types: ifc_class_browser = props.ifc_class_browser - constr_type_info = AuthoringData.constr_type_info(ifc_class_browser) - if constr_type_info is None or not constr_type_info.fully_loaded: - curr_selection = props.ifc_class, props.constr_type_id + relating_type_info = AuthoringData.relating_type_info(ifc_class_browser) + if relating_type_info is None or not relating_type_info.fully_loaded: + curr_selection = props.ifc_class, props.relating_type_id AuthoringData.assetize_constr_class(ifc_class_browser) - props.ifc_class, props.constr_type_id = curr_selection + props.ifc_class, props.relating_type_id = curr_selection else: - self.constr_type_id_browser = AuthoringData.data["constr_types_ids_browser"][0][0] + self.relating_type_id_browser = AuthoringData.data["relating_types_ids_browser"][0][0] -def update_constr_type(self, context): - AuthoringData.load_constr_types() +def update_relating_type(self, context): + AuthoringData.load_relating_types() -def update_constr_type_by_name(self, context): - AuthoringData.load_constr_types() - constr_type_id = AuthoringData.constr_type_id_by_name(self.ifc_class, self.constr_type) - if constr_type_id is not None: - self.constr_type_id = constr_type_id +def update_relating_type_by_name(self, context): + AuthoringData.load_relating_types() + relating_type_id = AuthoringData.relating_type_id_by_name(self.ifc_class, self.relating_type) + if relating_type_id is not None: + self.relating_type_id = relating_type_id -def update_constr_type_browser_by_name(self, context): - AuthoringData.load_constr_types_browser() - constr_type_id_browser = AuthoringData.constr_type_id_by_name(self.ifc_class_browser, self.constr_type_browser) - if constr_type_id_browser is not None: - self.constr_type_id_browser = constr_type_id_browser +def update_relating_type_browser_by_name(self, context): + AuthoringData.load_relating_types_browser() + relating_type_id_browser = AuthoringData.relating_type_id_by_name(self.ifc_class_browser, self.relating_type_browser) + if relating_type_id_browser is not None: + self.relating_type_id_browser = relating_type_id_browser -def update_constr_type_browser(self, context): - AuthoringData.load_constr_types_browser() +def update_relating_type_browser(self, context): + AuthoringData.load_relating_types_browser() update_icon_id(self, context) -def update_unfold_constr_type(self, context): +def update_unfold_relating_type(self, context): update_ifc_class_browser(self, context) @@ -104,16 +104,16 @@ class BIMModelProperties(PropertyGroup): ifc_class_browser: bpy.props.EnumProperty( items=get_ifc_class, name="Construction Class", update=update_ifc_class_browser ) - constr_type: bpy.props.StringProperty(update=update_constr_type_by_name) - constr_type_id: bpy.props.EnumProperty( - items=get_constr_type, name="Construction Type", update=update_constr_type + relating_type: bpy.props.StringProperty(update=update_relating_type_by_name) + relating_type_id: bpy.props.EnumProperty( + items=get_relating_type, name="Construction Type", update=update_relating_type ) - constr_type_browser: bpy.props.StringProperty(update=update_constr_type_browser_by_name) - constr_type_id_browser: bpy.props.EnumProperty( - items=get_constr_type_browser, name="Construction Type", update=update_constr_type_browser + relating_type_browser: bpy.props.StringProperty(update=update_relating_type_browser_by_name) + relating_type_id_browser: bpy.props.EnumProperty( + items=get_relating_type_browser, name="Construction Type", update=update_relating_type_browser ) icon_id: bpy.props.IntProperty() - unfold_constr_types: bpy.props.BoolProperty(update=update_unfold_constr_type) + unfold_relating_types: bpy.props.BoolProperty(update=update_unfold_relating_type) occurrence_name_style: bpy.props.EnumProperty( items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], name="Occurrence Name Style", @@ -121,18 +121,18 @@ class BIMModelProperties(PropertyGroup): occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") getter_enum = { "ifc_class_browser": get_ifc_class, - "constr_type_browser": get_constr_type_browser + "relating_type_browser": get_relating_type_browser } -def get_constr_type_info(self, context): +def get_relating_type_info(self, context): return AuthoringData.relating_types(ifc_class=self.name) class ConstrTypeInfo(PropertyGroup): name: bpy.props.StringProperty(name="Construction class") - constr_type: bpy.props.EnumProperty( - name="Construction type", items=get_constr_type_info + relating_type: bpy.props.EnumProperty( + name="Construction type", items=get_relating_type_info ) fully_loaded: bpy.props.BoolProperty(default=False) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 8828d40e7a..d2f5d939cd 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -63,18 +63,18 @@ class BimTool(WorkSpaceTool): return ifc_classes = AuthoringData.data["ifc_classes"] - constr_types_ids = AuthoringData.data["constr_types_ids"] + relating_types_ids = AuthoringData.data["relating_types_ids"] if is_tool_header: - row.operator("bim.help_constr_types", text="", icon="QUESTION") + row.operator("bim.help_relating_types", text="", icon="QUESTION") if ifc_classes and is_tool_header: row.label(text="", icon="BLANK1") - row.operator("bim.display_constr_types", icon="COLLAPSEMENU") + row.operator("bim.display_relating_types", icon="COLLAPSEMENU") ifc_class = props.ifc_class - constr_type_id = props.constr_type_id - constr_type = AuthoringData.constr_type_name_by_id(ifc_class, constr_type_id) + relating_type_id = props.relating_type_id + relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id) if is_tool_header: row.label(text="", icon="BLANK1") @@ -86,18 +86,18 @@ class BimTool(WorkSpaceTool): row.label(text="", icon="FILE_VOLUME") row.label(text=ifc_class) row.label(text="", icon="FILE_3D") - row.label(text=f"{constr_type} ") + row.label(text=f"{relating_type} ") else: row.label(text=f" Add instance") else: txt_ifc_class = ifc_class if ifc_classes else "No Construction Class" - txt_constr_type = constr_type if constr_types_ids else "No Construction Type" + txt_relating_type = relating_type if relating_types_ids else "No Construction Type" row = layout.row(align=True) row.label(text="Selected Construction Type:") row = layout.row(align=True) row.label(text=txt_ifc_class, icon="FILE_VOLUME") row = layout.row(align=True) - row.label(text=txt_constr_type, icon="FILE_3D") + row.label(text=txt_relating_type, icon="FILE_3D") row = layout.row(align=True) row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_A") @@ -184,7 +184,7 @@ class Hotkey(bpy.types.Operator): return {"FINISHED"} def hotkey_S_A(self): - bpy.ops.bim.add_constr_type() + bpy.ops.bim.add_relating_type() def hotkey_S_C(self): if self.has_ifc_class and self.props.ifc_class == "IfcWallType": diff --git a/src/blenderbim/blenderbim/bim/module/type/ui.py b/src/blenderbim/blenderbim/bim/module/type/ui.py index 04178e7ebb..70e1006096 100644 --- a/src/blenderbim/blenderbim/bim/module/type/ui.py +++ b/src/blenderbim/blenderbim/bim/module/type/ui.py @@ -88,4 +88,4 @@ class BIM_PT_type(Panel): def add_object_button(self, context): - self.layout.operator("bim.add_constr_type", icon="PLUGIN") + self.layout.operator("bim.add_relating_type", icon="PLUGIN") diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index 4d79197486..aa42012036 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -31,8 +31,8 @@ Scenario: Add representation - add a new representation to a typed instance And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I press "bim.add_constr_type" - And I press "bim.add_constr_type" + And I press "bim.add_relating_type" + And I press "bim.add_relating_type" Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" When the object "IfcWall/Wall" is selected @@ -146,9 +146,9 @@ Scenario: Remove representation - remove an instanced representation from an act And I press "bim.assign_class" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - And I press "bim.add_constr_type" - And I press "bim.add_constr_type" + And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" + And I press "bim.add_relating_type" + And I press "bim.add_relating_type" And the object "IfcWallType/Cube" is selected When the variable "representation" is "{ifc}.by_type('IfcWallType')[0].RepresentationMaps[1].MappedRepresentation.id()" And I press "bim.remove_representation(representation_id={representation})" @@ -165,9 +165,9 @@ Scenario: Remove representation - remove an instanced representation from an act And I press "bim.assign_class" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - And I press "bim.add_constr_type" - And I press "bim.add_constr_type" + And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" + And I press "bim.add_relating_type" + And I press "bim.add_relating_type" And the object "IfcWall/Wall" is selected When the variable "representation" is "{ifc}.by_type('IfcWall')[0].Representation.Representations[1].id()" And I press "bim.remove_representation(representation_id={representation})" @@ -339,8 +339,8 @@ Scenario: Override duplicate move - copying a type instance with a representatio And I press "bim.assign_class" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - And I press "bim.add_constr_type" + And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" + And I press "bim.add_relating_type" And the object "IfcWall/Wall" is selected When I press "object.duplicate_move" Then the object "IfcWall/Wall.001" exists diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index ce0dc22616..7a44a94bd4 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -10,8 +10,8 @@ Scenario: Add type instance - add from a mesh And I press "bim.assign_class" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - When I press "bim.add_constr_type" + And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" + When I press "bim.add_relating_type" Then the object "IfcWall/Wall" exists Scenario: Add type instance - add from an empty @@ -23,8 +23,8 @@ Scenario: Add type instance - add from an empty And I press "bim.assign_class" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.constr_type_id" to "{empty}" - When I press "bim.add_constr_type" + And I set "scene.BIMModelProperties.relating_type_id" to "{empty}" + When I press "bim.add_relating_type" Then the object "IfcWall/Wall" exists Scenario: Add type instance - add a mesh where existing instances have changed context @@ -36,15 +36,15 @@ Scenario: Add type instance - add a mesh where existing instances have changed c And I press "bim.assign_class" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMModelProperties.constr_type_id" to "{cube}" - And I press "bim.add_constr_type" + And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" + And I press "bim.add_relating_type" And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall" is selected And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()" And I set "scene.BIMRootProperties.contexts" to "{context}" And I press "bim.add_representation" And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" - When I press "bim.add_constr_type" + When I press "bim.add_relating_type" Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" @@ -54,7 +54,7 @@ Scenario: Preview one type on the Construction Type Browser When I display the construction type browser And I preview only one asset on the construction type browser And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcColumnType" - And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO2" + And I set "scene.BIMModelProperties.relating_type_browser" to "DEMO2" And I select the browser construction type Then "scene.BIMModelProperties.ifc_class" is "IfcColumnType" And construction type is DEMO2 @@ -77,7 +77,7 @@ Scenario: Add one type from the Construction Type Browser When I display the construction type browser And I preview only one asset on the construction type browser And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcColumnType" - And I set "scene.BIMModelProperties.constr_type_browser" to "DEMO2" + And I set "scene.BIMModelProperties.relating_type_browser" to "DEMO2" And I add the browser construction type Then the object "IfcColumn/Column" exists diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index d5927207ed..67382b5f1a 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -575,27 +575,27 @@ def the_object_name_has_no_modifiers(name): assert len(the_object_name_exists(name).modifiers) == 0 -@then(parsers.parse('the construction type "{ifc_class}"/"{constr_type}" has a preview')) -def the_construction_type_has_a_preview(ifc_class, constr_type): +@then(parsers.parse('the construction type "{ifc_class}"/"{relating_type}" has a preview')) +def the_construction_type_has_a_preview(ifc_class, relating_type): if "preview_constr_types" not in AuthoringData.data: assert False, 'There are no previews loaded' preview_constr_types = AuthoringData.data["preview_constr_types"] if ifc_class not in preview_constr_types: assert False, f'Construction class {ifc_class} has no available previews' - constr_type_id = AuthoringData.constr_type_id_by_name(ifc_class, constr_type) - if constr_type_id is None: - assert False, f'No construction type {ifc_class}/{constr_type} was found' - if constr_type_id not in preview_constr_types[ifc_class]: - assert False, f'Construction type {ifc_class}/{constr_type} has no available previews' - preview_data = preview_constr_types[ifc_class][constr_type_id] + relating_type_id = AuthoringData.relating_type_id_by_name(ifc_class, relating_type) + if relating_type_id is None: + assert False, f'No construction type {ifc_class}/{relating_type} was found' + if relating_type_id not in preview_constr_types[ifc_class]: + assert False, f'Construction type {ifc_class}/{relating_type} has no available previews' + preview_data = preview_constr_types[ifc_class][relating_type_id] if 'icon_id' not in preview_data: - assert False, f'Construction type {ifc_class}/{constr_type} has a preview, but no assigned icon_id' + assert False, f'Construction type {ifc_class}/{relating_type} has a preview, but no assigned icon_id' icon_id = preview_data["icon_id"] if not isinstance(icon_id, int): - assert False, f'Construction type {ifc_class}/{constr_type} has an invalid icon_id {icon_id}' + assert False, f'Construction type {ifc_class}/{relating_type} has an invalid icon_id {icon_id}' # Note: icon_id must be > 0 in UI mode, but asset_generate_preview() doesn't work headlessly -> skipping for now # if icon_id == 0: - # assert False, f'Construction type {ifc_class}/{constr_type} has the default null value for icon_id' + # assert False, f'Construction type {ifc_class}/{relating_type} has the default null value for icon_id' assert True @@ -627,19 +627,19 @@ def i_add_a_construction_library(): @given("I display the construction type browser") @when("I display the construction type browser") def i_display_the_construction_type_browser(): - bpy.ops.bim.display_constr_types('INVOKE_DEFAULT') + bpy.ops.bim.display_relating_types('INVOKE_DEFAULT') @given("I preview only one asset on the construction type browser") @when("I preview only one asset on the construction type browser") def i_preview_one_construction_type(): - bpy.context.scene.BIMModelProperties.unfold_constr_types = False + bpy.context.scene.BIMModelProperties.unfold_relating_types = False @given("I preview all available assets on the construction type browser") @when("I preview all available assets on the construction type browser") def i_preview_all_construction_types(): - bpy.context.scene.BIMModelProperties.unfold_constr_types = True + bpy.context.scene.BIMModelProperties.unfold_relating_types = True @given("I select the browser construction type") @@ -647,7 +647,7 @@ def i_preview_all_construction_types(): def i_select_the_active_construction_type(): props = bpy.context.scene.BIMModelProperties bpy.ops.bim.select_construction_type( - ifc_class=props.ifc_class_browser, constr_type_id=props.constr_type_id_browser + ifc_class=props.ifc_class_browser, relating_type_id=props.relating_type_id_browser ) @@ -655,24 +655,24 @@ def i_select_the_active_construction_type(): @when("I add the browser construction type") def i_add_the_active_construction_type(): props = bpy.context.scene.BIMModelProperties - bpy.ops.bim.add_constr_type( - ifc_class=props.ifc_class_browser, constr_type_id=int(props.constr_type_id_browser) + bpy.ops.bim.add_relating_type( + ifc_class=props.ifc_class_browser, relating_type_id=int(props.relating_type_id_browser) ) -@then(parsers.parse("browser construction type is {constr_type_name}")) -def browser_construction_type(constr_type_name): +@then(parsers.parse("browser construction type is {relating_type_name}")) +def browser_construction_type(relating_type_name): props = bpy.context.scene.BIMModelProperties - constr_type_browser = AuthoringData.constr_type_name_by_id(props.ifc_class_browser, props.constr_type_id_browser) - assert constr_type_browser == constr_type_name, (f"Construction Type is a {constr_type_browser}, not " + - f"a {constr_type_name}") + relating_type_browser = AuthoringData.relating_type_name_by_id(props.ifc_class_browser, props.relating_type_id_browser) + assert relating_type_browser == relating_type_name, (f"Construction Type is a {relating_type_browser}, not " + + f"a {relating_type_name}") -@then(parsers.parse("construction type is {constr_type_name}")) -def construction_type(constr_type_name): +@then(parsers.parse("construction type is {relating_type_name}")) +def construction_type(relating_type_name): props = bpy.context.scene.BIMModelProperties - constr_type = AuthoringData.constr_type_name_by_id(props.ifc_class, props.constr_type_id) - assert constr_type == constr_type_name, f"Construction Type is a {constr_type}, not a {constr_type_name}" + relating_type = AuthoringData.relating_type_name_by_id(props.ifc_class, props.relating_type_id) + assert relating_type == relating_type_name, f"Construction Type is a {relating_type}, not a {relating_type_name}" @when("I move the cursor to the bottom left corner") From cd58b994dd85eb8b4151ad81c277a450bd76cec8 Mon Sep 17 00:00:00 2001 From: Carlos Villagrasa Date: Wed, 27 Jul 2022 08:24:50 +0200 Subject: [PATCH 19/19] Renaming AddConstrTypeInstance --- .../blenderbim/bim/module/model/__init__.py | 2 +- src/blenderbim/blenderbim/bim/module/model/data.py | 4 ++-- .../blenderbim/bim/module/model/product.py | 8 ++++---- .../blenderbim/bim/module/model/workspace.py | 2 +- src/blenderbim/blenderbim/bim/module/type/ui.py | 2 +- src/blenderbim/test/bim/feature/geometry.feature | 14 +++++++------- src/blenderbim/test/bim/feature/model.feature | 8 ++++---- src/blenderbim/test/bim/test_feature.py | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index a023470ad3..30c5ff8c12 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -21,7 +21,7 @@ from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, classes = ( product.AddEmptyType, - product.AddConstrType, + product.AddConstrTypeInstance, product.DisplayConstrTypes, product.SelectConstructionType, product.HelpConstrTypes, diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index b8d74ecf54..c5ba73182c 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -170,13 +170,13 @@ class AuthoringData: @classmethod def new_relating_type(cls, ifc_class=None, relating_type_id=None): if ifc_class is None: - bpy.ops.bim.add_relating_type( + bpy.ops.bim.add_constr_type_instance( ifc_class=cls.props.ifc_class_browser, relating_type_id=int(cls.props.relating_type_id_browser) ) else: cls.props.ifc_class = ifc_class cls.props.relating_type_id = str(relating_type_id) - bpy.ops.bim.add_relating_type() + bpy.ops.bim.add_constr_type_instance() return bpy.context.selected_objects[-1] @staticmethod diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index bc70dd2ebb..312caa0dcf 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -57,8 +57,8 @@ def add_empty_type_button(self, context): self.layout.operator(AddEmptyType.bl_idname, icon="FILE_3D") -class AddConstrType(bpy.types.Operator): - bl_idname = "bim.add_relating_type" +class AddConstrTypeInstance(bpy.types.Operator): + bl_idname = "bim.add_constr_type_instance" bl_label = "Add" bl_options = {"REGISTER", "UNDO"} bl_description = "Add Type Instance to the model" @@ -280,7 +280,7 @@ class DisplayConstrTypes(bpy.types.Operator): op.ifc_class = ifc_class_browser op.relating_type_id = relating_type_id_browser col = split.column() - op = col.operator("bim.add_relating_type", icon="ADD") + op = col.operator("bim.add_constr_type_instance", icon="ADD") op.from_invoke = True op.ifc_class = ifc_class_browser if relating_type_id_browser.isnumeric(): @@ -309,7 +309,7 @@ class DisplayConstrTypes(bpy.types.Operator): op = row.operator("bim.select_construction_type", icon="RIGHTARROW_THIN") op.ifc_class = ifc_class_browser op.relating_type_id = relating_type_id_browser - op = row.operator("bim.add_relating_type", icon="ADD") + op = row.operator("bim.add_constr_type_instance", icon="ADD") op.from_invoke = True op.ifc_class = ifc_class_browser if relating_type_id_browser.isnumeric(): diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index d2f5d939cd..d7be1ebe61 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -184,7 +184,7 @@ class Hotkey(bpy.types.Operator): return {"FINISHED"} def hotkey_S_A(self): - bpy.ops.bim.add_relating_type() + bpy.ops.bim.add_constr_type_instance() def hotkey_S_C(self): if self.has_ifc_class and self.props.ifc_class == "IfcWallType": diff --git a/src/blenderbim/blenderbim/bim/module/type/ui.py b/src/blenderbim/blenderbim/bim/module/type/ui.py index 70e1006096..58cebe2c14 100644 --- a/src/blenderbim/blenderbim/bim/module/type/ui.py +++ b/src/blenderbim/blenderbim/bim/module/type/ui.py @@ -88,4 +88,4 @@ class BIM_PT_type(Panel): def add_object_button(self, context): - self.layout.operator("bim.add_relating_type", icon="PLUGIN") + self.layout.operator("bim.add_constr_type_instance", icon="PLUGIN") diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index aa42012036..ee0cdedc39 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -31,8 +31,8 @@ Scenario: Add representation - add a new representation to a typed instance And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I press "bim.add_relating_type" - And I press "bim.add_relating_type" + And I press "bim.add_constr_type_instance" + And I press "bim.add_constr_type_instance" Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" When the object "IfcWall/Wall" is selected @@ -147,8 +147,8 @@ Scenario: Remove representation - remove an instanced representation from an act And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" - And I press "bim.add_relating_type" - And I press "bim.add_relating_type" + And I press "bim.add_constr_type_instance" + And I press "bim.add_constr_type_instance" And the object "IfcWallType/Cube" is selected When the variable "representation" is "{ifc}.by_type('IfcWallType')[0].RepresentationMaps[1].MappedRepresentation.id()" And I press "bim.remove_representation(representation_id={representation})" @@ -166,8 +166,8 @@ Scenario: Remove representation - remove an instanced representation from an act And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" - And I press "bim.add_relating_type" - And I press "bim.add_relating_type" + And I press "bim.add_constr_type_instance" + And I press "bim.add_constr_type_instance" And the object "IfcWall/Wall" is selected When the variable "representation" is "{ifc}.by_type('IfcWall')[0].Representation.Representations[1].id()" And I press "bim.remove_representation(representation_id={representation})" @@ -340,7 +340,7 @@ Scenario: Override duplicate move - copying a type instance with a representatio And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" - And I press "bim.add_relating_type" + And I press "bim.add_constr_type_instance" And the object "IfcWall/Wall" is selected When I press "object.duplicate_move" Then the object "IfcWall/Wall.001" exists diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index 7a44a94bd4..cd950f888d 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -11,7 +11,7 @@ Scenario: Add type instance - add from a mesh And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" - When I press "bim.add_relating_type" + When I press "bim.add_constr_type_instance" Then the object "IfcWall/Wall" exists Scenario: Add type instance - add from an empty @@ -24,7 +24,7 @@ Scenario: Add type instance - add from an empty And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.relating_type_id" to "{empty}" - When I press "bim.add_relating_type" + When I press "bim.add_constr_type_instance" Then the object "IfcWall/Wall" exists Scenario: Add type instance - add a mesh where existing instances have changed context @@ -37,14 +37,14 @@ Scenario: Add type instance - add a mesh where existing instances have changed c And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And I set "scene.BIMModelProperties.relating_type_id" to "{cube}" - And I press "bim.add_relating_type" + And I press "bim.add_constr_type_instance" And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Wall" is selected And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()" And I set "scene.BIMRootProperties.contexts" to "{context}" And I press "bim.add_representation" And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" - When I press "bim.add_relating_type" + When I press "bim.add_constr_type_instance" Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW" diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 67382b5f1a..d964e883b9 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -655,7 +655,7 @@ def i_select_the_active_construction_type(): @when("I add the browser construction type") def i_add_the_active_construction_type(): props = bpy.context.scene.BIMModelProperties - bpy.ops.bim.add_relating_type( + bpy.ops.bim.add_constr_type_instance( ifc_class=props.ifc_class_browser, relating_type_id=int(props.relating_type_id_browser) )