diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index ec7012adfc..5d25f2efb9 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -19,6 +19,7 @@ import bpy import math import functools +import ifcopenshell import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound @@ -41,6 +42,8 @@ class AuthoringData: cls.load_ifc_classes() cls.load_relating_types() cls.load_relating_types_browser() + cls.data["type_class"] = cls.type_class() + cls.data["type_predefined_type"] = cls.type_predefined_type() cls.data["total_types"] = cls.total_types() cls.data["total_pages"] = cls.total_pages() cls.data["next_page"] = cls.next_page() @@ -51,6 +54,24 @@ class AuthoringData: cls.data["has_visible_openings"] = cls.has_visible_openings() cls.data["active_class"] = cls.active_class() + @classmethod + def type_class(cls): + declaration = tool.Ifc.schema().declaration_by_name("IfcElementType") + declarations = ifcopenshell.util.schema.get_subtypes(declaration) + names = [d.name() for d in declarations] + names.extend(("IfcDoorStyle", "IfcWindowStyle")) + return [(c, c, "") for c in sorted(names)] + + @classmethod + def type_predefined_type(cls): + results = [] + declaration = tool.Ifc().schema().declaration_by_name(cls.props.type_class) + for attribute in declaration.attributes(): + if attribute.name() == "PredefinedType": + results.extend([(e, e, "") for e in attribute.type_of_attribute().declared_type().enumeration_items()]) + break + return results + @classmethod def type_thumbnail(cls): if not cls.props.relating_type_id: @@ -72,13 +93,13 @@ class AuthoringData: @classmethod def total_types(cls): - ifc_class = cls.props.ifc_class - return len(tool.Ifc.get().by_type(ifc_class)) if ifc_class else 0 + type_class = cls.props.type_class + return len(tool.Ifc.get().by_type(type_class)) if type_class else 0 @classmethod def total_pages(cls): - ifc_class = cls.props.ifc_class - total_types = len(tool.Ifc.get().by_type(ifc_class)) if ifc_class else 0 + type_class = cls.props.type_class + total_types = len(tool.Ifc.get().by_type(type_class)) if type_class else 0 return math.ceil(total_types / cls.types_per_page) @classmethod @@ -93,20 +114,26 @@ class AuthoringData: @classmethod def paginated_relating_types(cls): - ifc_class = cls.props.ifc_class - if not ifc_class: + type_class = cls.props.type_class + if not type_class: return [] results = [] - elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda e: e.Name or "Unnamed") - elements = elements[(cls.props.type_page - 1) * cls.types_per_page:cls.props.type_page * cls.types_per_page] + elements = sorted(tool.Ifc.get().by_type(type_class), key=lambda e: e.Name or "Unnamed") + elements = elements[(cls.props.type_page - 1) * cls.types_per_page : cls.props.type_page * cls.types_per_page] for element in elements: - results.append({ - "id": element.id(), - "ifc_class": element.is_a(), - "name": element.Name or "Unnamed", - "description": element.Description or "No Description", - "icon_id": cls.type_thumbnails.get(element.id(), None) or 0, - }) + predefined_type = ifcopenshell.util.element.get_predefined_type(element) + if predefined_type == "NOTDEFINED": + predefined_type = None + results.append( + { + "id": element.id(), + "ifc_class": element.is_a(), + "name": element.Name or "Unnamed", + "description": element.Description or "No Description", + "predefined_type": predefined_type, + "icon_id": cls.type_thumbnails.get(element.id(), None) or 0, + } + ) return results @classmethod diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 6a71b4bf1a..9a15a5908e 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -29,6 +29,12 @@ def get_ifc_class(self, context): return AuthoringData.data["ifc_classes"] +def get_type_class(self, context): + if not AuthoringData.is_loaded: + AuthoringData.load() + return AuthoringData.data["type_class"] + + def get_relating_type(self, context): if not AuthoringData.is_loaded: AuthoringData.load() @@ -41,6 +47,12 @@ def get_relating_type_browser(self, context): return AuthoringData.data["relating_types_ids_browser"] +def get_type_predefined_type(self, context): + if not AuthoringData.is_loaded: + AuthoringData.load() + return AuthoringData.data["type_predefined_type"] + + def store_cursor_position(context, event, cursor=True, window=True): browser_state = context.scene.BIMModelProperties.constr_browser_state if cursor: @@ -62,8 +74,10 @@ def update_icon_id(self, context, browser=False): return def set_icon(update_interval_seconds=1e-4): - if (ifc_class not in self.constr_classes - or relating_type_id not in self.constr_classes[ifc_class].constr_types): + if ( + ifc_class not in self.constr_classes + or relating_type_id not in self.constr_classes[ifc_class].constr_types + ): return update_interval_seconds else: self.icon_id = self.constr_classes[ifc_class].constr_types[relating_type_id].icon_id @@ -76,6 +90,15 @@ def update_ifc_class(self, context): AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail() +def update_type_class(self, context): + AuthoringData.data["total_types"] = AuthoringData.total_types() + AuthoringData.data["total_pages"] = AuthoringData.total_pages() + AuthoringData.data["prev_page"] = AuthoringData.prev_page() + AuthoringData.data["next_page"] = AuthoringData.next_page() + AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types() + AuthoringData.data["type_predefined_type"] = AuthoringData.type_predefined_type() + + def update_ifc_class_browser(self, context): if context.region is not None and context.region.type != "TOOL_HEADER": AuthoringData.load_ifc_classes() @@ -206,3 +229,16 @@ class BIMModelProperties(PropertyGroup): z: bpy.props.FloatProperty(name="Z", default=0.5) rl: bpy.props.FloatProperty(name="RL", default=1) type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page) + type_template: bpy.props.EnumProperty( + items=( + ("1", "Custom Mesh", ""), + ("2", "Vertical Layers", ""), + ("2", "Horizontal Layers", ""), + ("3", "Extruded Profile", ""), + ("4", "Non-Geometric Type", ""), + ), + name="Type Template", + default="1", + ) + type_class: bpy.props.EnumProperty(items=get_type_class, name="IFC Class", update=update_type_class) + type_predefined_type: bpy.props.EnumProperty(items=get_type_predefined_type, name="Predefined Type", default=None) diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 6fa5aa670c..2d29723623 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -35,6 +35,8 @@ class LaunchTypeManager(bpy.types.Operator): def invoke(self, context, event): props = context.scene.BIMModelProperties props.type_page = 1 + if props.ifc_class: + props.type_class = props.ifc_class bpy.ops.bim.load_type_thumbnails(ifc_class=props.ifc_class) if not AuthoringData.is_loaded: AuthoringData.load() @@ -43,15 +45,24 @@ class LaunchTypeManager(bpy.types.Operator): def draw(self, context): props = context.scene.BIMModelProperties - row = self.layout.row(align=True) + row = self.layout.row() + row.prop(props, "type_class", text="") + + columns = self.layout.column_flow(columns=3) + row = columns.row() + row.alignment = "LEFT" + row.label(text=f"{AuthoringData.data['total_types']} Types", icon="FILE_VOLUME") + + row = columns.row(align=True) + row.alignment = "CENTER" + row.prop(props, "type_predefined_type", text="") + row.prop(props, "type_template", text="") + op = row.operator("bim.change_type_page", icon="ADD", text="") + + row = columns.row(align=True) row.alignment = "RIGHT" - #row.label(text=f"", icon="FILE_VOLUME") - - text = f"{AuthoringData.data['total_types']} types" if AuthoringData.data["total_pages"] > 1: - text += f" ({props.type_page}/{AuthoringData.data['total_pages']}) " - row.label(text=text) - + row.label(text=f"Page {props.type_page}/{AuthoringData.data['total_pages']} ") if AuthoringData.data["prev_page"]: op = row.operator("bim.change_type_page", icon="TRIA_LEFT", text="") op.page = AuthoringData.data["prev_page"] @@ -74,11 +85,16 @@ class LaunchTypeManager(bpy.types.Operator): row.label(text=relating_type["description"]) row = box.row() - row.template_icon(icon_value=relating_type["icon_id"], scale=4) + if relating_type["icon_id"]: + row.template_icon(icon_value=relating_type["icon_id"], scale=4) + else: + op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH") + op.ifc_class = props.type_class row = box.row(align=True) - op = row.operator("bim.add_constr_type_instance", icon="ADD") + text = f"Add {relating_type['predefined_type']}" if relating_type["predefined_type"] else "Add" + op = row.operator("bim.add_constr_type_instance", icon="ADD", text=text) op.from_invoke = True op.ifc_class = relating_type["ifc_class"] op.relating_type_id = relating_type["id"]