Adding a preview popup for IFC Type additions

This commit is contained in:
carlos
2022-06-29 17:43:28 +02:00
parent 319f63c861
commit 7321138d57
5 changed files with 207 additions and 35 deletions
@@ -17,11 +17,13 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy 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 = ( classes = (
product.AddEmptyType, product.AddEmptyType,
product.AddTypeInstance, product.AddTypeInstance,
product.DisplayIFCTypes,
product.AddIFCTypeInstance,
product.AlignProduct, product.AlignProduct,
product.DynamicallyVoidProduct, product.DynamicallyVoidProduct,
workspace.Hotkey, workspace.Hotkey,
@@ -32,6 +34,7 @@ classes = (
opening.AddElementOpening, opening.AddElementOpening,
profile.ExtendProfile, profile.ExtendProfile,
prop.BIMModelProperties, prop.BIMModelProperties,
prop.IfcTypeInfo,
ui.BIM_PT_authoring, ui.BIM_PT_authoring,
ui.BIM_PT_authoring_architectural, ui.BIM_PT_authoring_architectural,
grid.BIM_OT_add_object, grid.BIM_OT_add_object,
@@ -51,6 +54,7 @@ def register():
if not bpy.app.background: if not bpy.app.background:
bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True) 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.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(grid.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.append(stair.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) bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
@@ -68,6 +72,7 @@ def unregister():
if not bpy.app.background: if not bpy.app.background:
bpy.utils.unregister_tool(workspace.BimTool) bpy.utils.unregister_tool(workspace.BimTool)
del bpy.types.Scene.BIMModelProperties del bpy.types.Scene.BIMModelProperties
del bpy.types.Scene.IfcTypeInfo
bpy.app.handlers.load_post.remove(handler.load_post) 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(grid.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button) bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
@@ -18,6 +18,10 @@
import bpy import bpy
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
preview_icon_ids = {}
def refresh(): def refresh():
@@ -31,10 +35,23 @@ class AuthoringData:
@classmethod @classmethod
def load(cls): def load(cls):
cls.is_loaded = True cls.is_loaded = True
cls.data = { if not hasattr(cls, "data"):
"ifc_classes": cls.ifc_classes(), cls.data = {}
"relating_types": cls.relating_types(), 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 @classmethod
def ifc_classes(cls): def ifc_classes(cls):
@@ -49,16 +66,71 @@ class AuthoringData:
return results return results
@classmethod @classmethod
def relating_types(cls): def ifc_class_entities(cls, ifc_class=None):
ifc_classes = cls.ifc_classes() ifc_classes = cls.data["ifc_classes"]
if not ifc_classes: if not ifc_classes:
return [] return []
results = [] 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: if not ifc_class and ifc_classes:
ifc_class = ifc_classes[0][0] ifc_class = ifc_classes[0][0]
if ifc_class: if ifc_class:
elements = [(str(e.id()), e.Name, e.Description or "") for e in tool.Ifc.get().by_type(ifc_class)] elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: s.Name)
results.extend(sorted(elements, key=lambda s: s[1])) results.extend(elements)
return results return results
return [] 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
@@ -29,6 +29,7 @@ import blenderbim.core.type
import blenderbim.core.geometry import blenderbim.core.geometry
from . import wall, slab, profile, mep from . import wall, slab, profile, mep
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.data import AuthoringData
from ifcopenshell.api.pset.data import Data as PsetData from ifcopenshell.api.pset.data import Data as PsetData
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from bpy_extras.object_utils import AddObjectHelper from bpy_extras.object_utils import AddObjectHelper
@@ -152,7 +153,8 @@ class AddTypeInstance(bpy.types.Operator):
context.view_layer.objects.active = obj context.view_layer.objects.active = obj
return {"FINISHED"} return {"FINISHED"}
def generate_layered_element(self, ifc_class, relating_type): @staticmethod
def generate_layered_element(ifc_class, relating_type):
layer_set_direction = None layer_set_direction = None
parametric = ifcopenshell.util.element.get_psets(relating_type).get("EPset_Parametric") 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? :) 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): class AlignProduct(bpy.types.Operator):
bl_idname = "bim.align_product" bl_idname = "bim.align_product"
bl_label = "Align Product" bl_label = "Align Product"
@@ -21,17 +21,8 @@ import ifcopenshell.util.type
from blenderbim.bim.module.model.data import AuthoringData from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
def get_ifc_class(self, context): def get_ifc_class(self, context):
@@ -46,15 +37,50 @@ def get_relating_type(self, context):
return AuthoringData.data["relating_types"] 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): 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): class BIMModelProperties(PropertyGroup):
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class) 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( occurrence_name_style: bpy.props.EnumProperty(
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
name="Occurrence Name Style", name="Occurrence Name Style",
) )
occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") 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
)
@@ -37,7 +37,6 @@ class BimTool(WorkSpaceTool):
bl_keymap = ( bl_keymap = (
# ("bim.wall_tool_op", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}), # ("bim.wall_tool_op", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}),
# ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"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.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": "T", "value": "PRESS", "shift": True}, {"properties": [("join_type", "L")]}),
("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}), ("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}),
@@ -61,20 +60,14 @@ class BimTool(WorkSpaceTool):
return return
props = context.scene.BIMModelProperties props = context.scene.BIMModelProperties
if AuthoringData.data["ifc_classes"]: if AuthoringData.data["ifc_classes"]:
row.prop(props, "ifc_class", text="") row.operator("bim.display_ifc_types", icon="COLLAPSEMENU")
else: else:
row.label(text="No IFC Class") row.label(text="No IFC Class")
if AuthoringData.data["relating_types"]: if not AuthoringData.data["relating_types"]:
row.prop(props, "relating_type", text="")
else:
row.label(text="No Relating Type") row.label(text="No Relating Type")
row.label(text="", icon="BLANK1") 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 AuthoringData.data["ifc_classes"]:
if props.ifc_class == "IfcWallType": if props.ifc_class == "IfcWallType":
row = layout.row() row = layout.row()
@@ -155,9 +148,6 @@ class Hotkey(bpy.types.Operator):
getattr(self, f"hotkey_{self.hotkey}")() getattr(self, f"hotkey_{self.hotkey}")()
return {"FINISHED"} return {"FINISHED"}
def hotkey_S_A(self):
bpy.ops.bim.add_type_instance()
def hotkey_S_C(self): def hotkey_S_C(self):
if self.has_ifc_class and self.props.ifc_class == "IfcWallType": if self.has_ifc_class and self.props.ifc_class == "IfcWallType":
bpy.ops.bim.align_wall(align_type="CENTERLINE") bpy.ops.bim.align_wall(align_type="CENTERLINE")