diff --git a/src/bonsai/bonsai/bim/module/geometry/data.py b/src/bonsai/bonsai/bim/module/geometry/data.py index 97948f75c7..50df9df315 100644 --- a/src/bonsai/bonsai/bim/module/geometry/data.py +++ b/src/bonsai/bonsai/bim/module/geometry/data.py @@ -90,6 +90,8 @@ class ViewportData: modes.append(item_mode) elif tool.Geometry.is_representation_item(obj): modes.append(edit_mode) + else: # A regular Blender object + modes.append(edit_mode) return modes diff --git a/src/bonsai/bonsai/bim/module/geometry/decorator.py b/src/bonsai/bonsai/bim/module/geometry/decorator.py index bd787eb9e5..866a53ec92 100644 --- a/src/bonsai/bonsai/bim/module/geometry/decorator.py +++ b/src/bonsai/bonsai/bim/module/geometry/decorator.py @@ -115,17 +115,13 @@ class ItemDecorator: continue edges = selected_edges verts = selected_verts - offset = len(selected_verts) - selected_verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices]) - selected_tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles]) + tris = selected_tris else: - offset = len(unselected_verts) - unselected_verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices]) - unselected_tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles]) edges = unselected_edges verts = unselected_verts - i = len(verts) + tris = unselected_tris + i = len(verts) matrix_world = obj.matrix_world bbox_verts = [matrix_world @ Vector(co) for co in obj.bound_box] bbox_edges = [ @@ -145,6 +141,16 @@ class ItemDecorator: edges.extend(bbox_edges) verts.extend(bbox_verts) + if (total_tris := len(obj.data.loop_triangles)) == 0: + continue + + if total_tris > 1000: # For performance + continue + + offset = len(verts) + verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices]) + tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles]) + if unselected_verts: self.draw_batch("LINES", unselected_verts, transparent_color(unselected_elements_color), unselected_edges) self.draw_batch("TRIS", unselected_verts, transparent_color(special_elements_color), unselected_tris) diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 6d545926fd..664ecd96ee 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -1631,6 +1631,8 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator): bpy.ops.bim.import_representation_items() elif tool.Geometry.is_representation_item(obj): self.enable_editing_representation_item(context, obj) + else: # A regular Blender object + self.enable_edit_mode(context) def handle_multiple_selected_objects(self, context): obj = context.active_object @@ -2300,9 +2302,7 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator): context.scene.BIMGeometryProperties.mode = "ITEM" context.scene.BIMGeometryProperties.is_changing_mode = False - tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get()) - tool.Loader.settings.context_settings = tool.Loader.create_settings() - tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True) + tool.Loader.load_settings() for item_id in set(obj.data["ios_item_ids"]): item = tool.Ifc.get().by_id(item_id) diff --git a/src/bonsai/bonsai/bim/module/model/__init__.py b/src/bonsai/bonsai/bim/module/model/__init__.py index cb18de504c..6951ef2c67 100644 --- a/src/bonsai/bonsai/bim/module/model/__init__.py +++ b/src/bonsai/bonsai/bim/module/model/__init__.py @@ -139,7 +139,7 @@ classes = ( ui.BIM_PT_roof, ui.BIM_MT_type_manager_menu, ui.LaunchTypeManager, - ui.BIM_MT_model, + ui.BIM_MT_elements, grid.BIM_OT_add_object, stair.BIM_OT_add_stair, stair.AddStair, @@ -216,8 +216,7 @@ def register(): bpy.types.Object.BIMRailingProperties = bpy.props.PointerProperty(type=prop.BIMRailingProperties) bpy.types.Object.BIMRoofProperties = bpy.props.PointerProperty(type=prop.BIMRoofProperties) - bpy.types.VIEW3D_MT_mesh_add.append(ui.add_mesh_object_menu) - bpy.types.VIEW3D_MT_add.append(ui.add_menu) + bpy.types.VIEW3D_MT_add.prepend(ui.add_menu) bpy.app.handlers.load_post.append(handler.load_post) workspace.load_custom_icons() @@ -248,7 +247,6 @@ def unregister(): del bpy.types.Object.BIMRoofProperties bpy.app.handlers.load_post.remove(handler.load_post) - bpy.types.VIEW3D_MT_mesh_add.remove(ui.add_mesh_object_menu) bpy.types.VIEW3D_MT_add.remove(ui.add_menu) workspace.unload_custom_icons() diff --git a/src/bonsai/bonsai/bim/module/model/ui.py b/src/bonsai/bonsai/bim/module/model/ui.py index 272f139ccc..39297a9ff5 100644 --- a/src/bonsai/bonsai/bim/module/model/ui.py +++ b/src/bonsai/bonsai/bim/module/model/ui.py @@ -116,7 +116,8 @@ class LaunchTypeManager(bpy.types.Operator): row.operator("bim.disable_add_type", icon="CANCEL", text="") else: row = self.layout.row() - row.operator("bim.enable_add_type", icon="ADD", text="Create New Type") + # row.operator("bim.enable_add_type", icon="ADD", text="Create New Type") + row.operator("bim.launch_add_element", icon="ADD", text="Create New Type") flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True) @@ -656,26 +657,16 @@ class BIM_PT_roof(bpy.types.Panel): row.operator("bim.add_roof", icon="ADD", text="") -class BIM_MT_model(Menu): - bl_idname = "BIM_MT_model" - bl_label = "Objects" +class BIM_MT_elements(Menu): + bl_idname = "BIM_MT_elements" + bl_label = "IFC Elements" def draw(self, context): - layout = self.layout - layout.operator("bim.add_empty_type", text="Empty Type", icon="EMPTY_AXIS") - layout.operator("bim.add_potential_half_space_solid", text="Half Space Proxy", icon="ORIENTATION_NORMAL") - layout.operator("bim.add_potential_opening", text="Opening Proxy", icon="CUBE") + # TODO consolidate in Item mode UI then remove + self.layout.operator("bim.add_potential_half_space_solid", text="Half Space", icon="ORIENTATION_NORMAL") + self.layout.operator("bim.add_potential_opening", text="Opening", icon="CUBE") def add_menu(self, context): - self.layout.menu("BIM_MT_model", icon="FILE_3D") - - -def add_mesh_object_menu(self, context): - if context.mode == "OBJECT": - self.layout.separator() - self.layout.operator("mesh.add_stair", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Stair (Untyped)") - self.layout.operator("mesh.add_window", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Window (Untyped)") - self.layout.operator("mesh.add_door", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Door (Untyped)") - self.layout.operator("mesh.add_railing", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Railing (Untyped)") - self.layout.operator("mesh.add_roof", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Roof (Untyped)") + self.layout.operator("bim.launch_add_element", icon_value=bonsai.bim.icons["IFC"].icon_id, text="IFC Element") + self.layout.separator() diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 7fe4506530..aae95903ad 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -131,6 +131,7 @@ class CreateProject(bpy.types.Operator): ) bonsai.bim.schema.reload(tool.Ifc.get().schema_identifier) tool.Blender.register_toolbar() + tool.Root.reload_item_decorator() def rollback(self, data): IfcStore.file = None diff --git a/src/bonsai/bonsai/bim/module/root/__init__.py b/src/bonsai/bonsai/bim/module/root/__init__.py index cf8f4b44ad..f2da6d3355 100644 --- a/src/bonsai/bonsai/bim/module/root/__init__.py +++ b/src/bonsai/bonsai/bim/module/root/__init__.py @@ -20,10 +20,12 @@ import bpy from . import ui, prop, operator classes = ( + operator.AddElement, operator.AssignClass, operator.CopyClass, operator.DisableReassignClass, operator.EnableReassignClass, + operator.LaunchAddElement, operator.ReassignClass, operator.UnlinkObject, prop.BIMRootProperties, diff --git a/src/bonsai/bonsai/bim/module/root/data.py b/src/bonsai/bonsai/bim/module/root/data.py index 3a11031e83..a14d3da18f 100644 --- a/src/bonsai/bonsai/bim/module/root/data.py +++ b/src/bonsai/bonsai/bim/module/root/data.py @@ -42,6 +42,7 @@ class IfcClassData: cls.data["ifc_products"] = cls.ifc_products() cls.data["ifc_classes"] = cls.ifc_classes() cls.data["ifc_classes_suggestions"] = cls.ifc_classes_suggestions() # Call AFTER cls.ifc_classes() + cls.data["representation_template"] = cls.representation_template() cls.data["contexts"] = cls.contexts() cls.data["has_entity"] = cls.has_entity() @@ -58,9 +59,7 @@ class IfcClassData: "IfcElement", "IfcSpatialElement", "IfcSpatialElementType", - "IfcGroup", "IfcStructuralItem", - "IfcContext", "IfcAnnotation", "IfcRelSpaceBoundary", ] @@ -70,7 +69,6 @@ class IfcClassData: "IfcElementType", "IfcElement", "IfcSpatialStructureElement", - "IfcGroup", "IfcStructuralItem", "IfcAnnotation", "IfcRelSpaceBoundary", @@ -130,6 +128,95 @@ class IfcClassData: suggestions[ifc_class].append(suggestion_dict) return suggestions + @classmethod + def representation_template(cls): + ifc_class = bpy.context.scene.BIMRootProperties.ifc_class + templates = [ + ("EMPTY", "No Geometry", "Start with an empty object"), + None, + ] + if ( + hasattr(bpy.context, "selected_objects") + and len(bpy.context.selected_objects) > 0 + and (obj := bpy.context.active_object) + and obj.type == "MESH" + ): + templates.append( + ( + "OBJ", + "Tessellation From Active Selection", + "Use the actively selected object as a template to create a new tessellation", + ) + ) + templates.extend( + [ + ( + "MESH", + "Custom Tessellation", + "Create a basic tessellated or faceted cube", + ), + ( + "EXTRUSION", + "Custom Extruded Solid", + "An extrusion from an arbitrary profile", + ), + ] + ) + if ifc_class.endswith("Type") or ifc_class.endswith("Style"): + templates.extend( + [ + None, + ( + "LAYERSET_AXIS2", + "Vertical Layers", + "For objects similar to walls, will automatically add IfcMaterialLayerSet", + ), + ( + "LAYERSET_AXIS3", + "Horizontal Layers", + "For objects similar to slabs, will automatically add IfcMaterialLayerSet", + ), + ( + "PROFILESET", + "Extruded Profile", + "Create profile type object, automatically defines IfcMaterialProfileSet with the first profile from library", + ), + ] + ) + if ifc_class in ("IfcWindowType", "IfcWindowStyle", "IfcWindow"): + templates.extend([None, ("WINDOW", "Window", "Parametric window")]) + elif ifc_class in ("IfcDoorType", "IfcDoorStyle", "IfcDoor"): + templates.extend([None, ("DOOR", "Door", "Parametric door")]) + elif ifc_class in ("IfcStairType", "IfcStairFlightType", "IfcStair", "IfcStairFlight"): + templates.extend([None, ("STAIR", "Stair", "Parametric stair")]) + elif ifc_class in ("IfcRailingType", "IfcRailing"): + templates.extend([None, ("RAILING", "Railing", "Parametric railing")]) + elif ifc_class in ("IfcRoofType", "IfcRoof"): + templates.extend([None, ("ROOF", "Roof", "Parametric roof with a constant pitch")]) + elif ifc_class and "Segment" in ifc_class: + templates.extend( + ( + None, + ( + "FLOW_SEGMENT_RECTANGULAR", + "Rectangular Distribution Segment", + "Works similarly to Profile, has distribution ports", + ), + ( + "FLOW_SEGMENT_CIRCULAR", + "Circular Distribution Segment", + "Works similarly to Profile, has distribution ports", + ), + ( + "FLOW_SEGMENT_CIRCULAR_HOLLOW", + "Circular Hollow Distribution Segment", + "Works similarly to Profile, has distribution ports", + ), + ) + ) + + return templates + @classmethod def contexts(cls): results = [] diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py index 18aa7e5654..cedbdab474 100644 --- a/src/bonsai/bonsai/bim/module/root/operator.py +++ b/src/bonsai/bonsai/bim/module/root/operator.py @@ -17,6 +17,7 @@ # along with Bonsai. If not, see . import bpy +import bmesh import ifcopenshell import ifcopenshell.api import ifcopenshell.util.schema @@ -24,9 +25,12 @@ import ifcopenshell.util.element import ifcopenshell.util.type import bonsai.bim.handler import bonsai.core.root as core +import bonsai.core.geometry import bonsai.tool as tool +import bonsai.bim.module.root.prop as root_prop from bonsai.bim.ifc import IfcStore -from bonsai.bim.helper import get_enum_items +from bonsai.bim.helper import get_enum_items, prop_with_search +from mathutils import Vector class EnableReassignClass(bpy.types.Operator): @@ -302,3 +306,241 @@ class CopyClass(bpy.types.Operator, tool.Ifc.Operator): for obj in objects: core.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj) bonsai.bim.handler.refresh_ui_data() + + +class AddElement(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.add_element" + bl_label = "Add Element" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Add an IFC physical product, construction type, and more" + + def invoke(self, context, event): + return IfcStore.execute_ifc_operator(self, context, is_invoke=True) + + def _invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + def _execute(self, context): + props = context.scene.BIMRootProperties + predefined_type = ( + props.userdefined_type if props.ifc_predefined_type == "USERDEFINED" else props.ifc_predefined_type + ) + representation_template = props.representation_template + + ifc_context = None + if get_enum_items(props, "contexts", context): + ifc_context = int(props.contexts or "0") or None + if ifc_context: + ifc_context = tool.Ifc.get().by_id(ifc_context) + + if representation_template in ( + "EMPTY", + "LAYERSET_AXIS2", + "LAYERSET_AXIS3", + "PROFILESET", + ) or representation_template.startswith("FLOW_SEGMENT_"): + mesh = None + else: + mesh = bpy.data.meshes.new("Mesh") + + obj = bpy.data.objects.new(props.ifc_class[3:], mesh) + obj.location = bpy.context.scene.cursor.location + element = core.assign_class( + tool.Ifc, + tool.Collector, + tool.Root, + obj=obj, + ifc_class=props.ifc_class, + predefined_type=predefined_type, + should_add_representation=False, + ) + + if representation_template == "EMTPY" or not ifc_context: + pass + elif ( + representation_template == "OBJ" + and (template_obj := context.active_object) + and template_obj.type == "MESH" + and len(template_obj.data.vertices) + ): + obj.matrix_world = template_obj.matrix_world + builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + bm = bmesh.new() + bmesh.ops.create_cube(bm, size=0.5) + verts = [v.co / unit_scale for v in template_obj.data.vertices] + faces = [p.vertices[:] for p in template_obj.data.polygons] + item = builder.mesh(verts, faces) + bm.free() + representation = builder.get_representation(ifc_context, [item]) + ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation) + bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) + if not tool.Ifc.get_entity(template_obj): + bpy.data.objects.remove(template_obj) + elif representation_template == "MESH": + builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + bm = bmesh.new() + bmesh.ops.create_cube(bm, size=0.5) + verts = [v.co / unit_scale for v in bm.verts] + faces = [[v.index for v in p.verts] for p in bm.faces] + item = builder.mesh(verts, faces) + bm.free() + representation = builder.get_representation(ifc_context, [item]) + ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation) + bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) + elif representation_template == "EXTRUSION": + builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + curve = builder.rectangle(size=Vector((0.5, 0.5)) / unit_scale) + item = builder.extrude(curve, magnitude=0.5 / unit_scale) + representation = builder.get_representation(ifc_context, [item]) + ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation) + bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) + elif representation_template in ("LAYERSET_AXIS2", "LAYERSET_AXIS3"): + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + materials = tool.Ifc.get().by_type("IfcMaterial") + if materials: + material = materials[0] # Arbitrarily pick a material + else: + material = ifcopenshell.api.run("material.add_material", tool.Ifc.get(), name="Unknown") + rel = ifcopenshell.api.run( + "material.assign_material", tool.Ifc.get(), products=[element], type="IfcMaterialLayerSet" + ) + layer_set = rel.RelatingMaterial + layer = ifcopenshell.api.run("material.add_layer", tool.Ifc.get(), layer_set=layer_set, material=material) + thickness = 0.1 # Arbitrary metric thickness for now + layer.LayerThickness = thickness / unit_scale + pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Parametric") + if representation_template == "LAYERSET_AXIS2": + axis = "AXIS2" + elif representation_template == "LAYERSET_AXIS3": + axis = "AXIS3" + ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"LayerSetDirection": axis}) + elif representation_template == "PROFILESET" or representation_template.startswith("FLOW_SEGMENT_"): + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + materials = tool.Ifc.get().by_type("IfcMaterial") + if materials: + material = materials[0] # Arbitrarily pick a material + else: + material = ifcopenshell.api.run("material.add_material", tool.Ifc.get(), name="Unknown") + if representation_template == "PROFILESET": + named_profiles = [p for p in tool.Ifc.get().by_type("IfcProfileDef") if p.ProfileName] + if named_profiles: + profile = named_profiles[0] + else: + size = 0.5 / unit_scale + profile = tool.Ifc.get().create_entity( + "IfcRectangleProfileDef", ProfileName="New Profile", ProfileType="AREA", XDim=size, YDim=size + ) + else: + # NOTE: defaults dims are in meters / mm + # for now default names are hardcoded to mm + if representation_template == "FLOW_SEGMENT_RECTANGULAR": + default_x_dim = 0.4 + default_y_dim = 0.2 + profile_name = f"{props.ifc_class}-{default_x_dim*1000}x{default_y_dim*1000}" + profile = tool.Ifc.get().create_entity( + "IfcRectangleProfileDef", + ProfileName=profile_name, + ProfileType="AREA", + XDim=default_x_dim / unit_scale, + YDim=default_y_dim / unit_scale, + ) + elif representation_template == "FLOW_SEGMENT_CIRCULAR": + default_diameter = 0.1 + profile_name = f"{props.ifc_class}-{default_diameter*1000}" + profile = tool.Ifc.get().create_entity( + "IfcCircleProfileDef", + ProfileName=profile_name, + ProfileType="AREA", + Radius=(default_diameter / 2) / unit_scale, + ) + elif representation_template == "FLOW_SEGMENT_CIRCULAR_HOLLOW": + default_diameter = 0.15 + default_thickness = 0.005 + profile_name = f"{props.ifc_class}-{default_diameter*1000}x{default_thickness*1000}" + profile = tool.Ifc.get().create_entity( + "IfcCircleHollowProfileDef", + ProfileName=profile_name, + ProfileType="AREA", + Radius=(default_diameter / 2) / unit_scale, + WallThickness=default_thickness, + ) + + rel = ifcopenshell.api.run( + "material.assign_material", tool.Ifc.get(), products=[element], type="IfcMaterialProfileSet" + ) + profile_set = rel.RelatingMaterial + material_profile = ifcopenshell.api.run( + "material.add_profile", tool.Ifc.get(), profile_set=profile_set, material=material + ) + ifcopenshell.api.run( + "material.assign_profile", tool.Ifc.get(), material_profile=material_profile, profile=profile + ) + elif representation_template == "WINDOW": + with context.temp_override(active_object=obj): + bpy.ops.bim.add_window() + elif representation_template == "DOOR": + with context.temp_override(active_object=obj): + bpy.ops.bim.add_door() + elif representation_template == "STAIR": + with context.temp_override(active_object=obj): + bpy.ops.bim.add_stair() + elif representation_template == "RAILING": + with context.temp_override(active_object=obj): + bpy.ops.bim.add_railing() + elif representation_template == "ROOF": + with context.temp_override(active_object=obj): + bpy.ops.bim.add_roof() + + def draw(self, context): + props = context.scene.BIMRootProperties + self.layout.use_property_split = True + self.layout.use_property_decorate = False + prop_with_search(self.layout, props, "ifc_product", text="Definition") + prop_with_search(self.layout, props, "ifc_class", should_click_ok_to_validate=True) + ifc_predefined_types = root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context) + if ifc_predefined_types: + prop_with_search(self.layout, props, "ifc_predefined_type") + if props.ifc_predefined_type == "USERDEFINED": + row = self.layout.row() + row.prop(props, "ifc_userdefined_type") + prop_with_search(self.layout, props, "representation_template", text="Representation") + if props.representation_template != "EMPTY": + prop_with_search(self.layout, props, "contexts") + + +class LaunchAddElement(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.launch_add_element" + bl_label = "Add Element" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Add an IFC physical product, construction type, and more" + + def execute(self, context): + bpy.ops.bim.add_element("INVOKE_DEFAULT") + return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/root/prop.py b/src/bonsai/bonsai/bim/module/root/prop.py index 0986d80bb5..9c49ae0f75 100644 --- a/src/bonsai/bonsai/bim/module/root/prop.py +++ b/src/bonsai/bonsai/bim/module/root/prop.py @@ -41,6 +41,12 @@ def get_ifc_predefined_types(self, context): return IfcClassData.data["ifc_predefined_types"] +def get_representation_template(self, context): + if not IfcClassData.is_loaded: + IfcClassData.load() + return IfcClassData.data["representation_template"] + + def refresh_classes(self, context): enum = get_ifc_classes(self, context) context.scene.BIMRootProperties.ifc_class = enum[0][0] @@ -110,11 +116,12 @@ def is_object_class_applicable(self, obj): class BIMRootProperties(PropertyGroup): - contexts: EnumProperty(items=get_contexts, name="Contexts") + contexts: EnumProperty(items=get_contexts, name="Contexts", options=set()) ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes) ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refresh_predefined_types) ifc_predefined_type: EnumProperty(items=get_ifc_predefined_types, name="Predefined Type", default=None) ifc_userdefined_type: StringProperty(name="Userdefined Type") + representation_template: bpy.props.EnumProperty(items=get_representation_template, name="Representation Template", default=0) relating_class_object: PointerProperty( type=bpy.types.Object, name="Copy Class", diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index 461bed1d4d..406c37ad8d 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -19,9 +19,10 @@ from __future__ import annotations import os import re -import math import bpy +import math import bmesh +import logging import ifcopenshell.geom import ifcopenshell.util.element import ifcopenshell.util.geolocation @@ -56,6 +57,14 @@ class Loader(bonsai.core.tool.Loader): def set_unit_scale(cls, unit_scale: float) -> None: cls.unit_scale = unit_scale + @classmethod + def load_settings(cls) -> None: + logger = logging.getLogger("ImportIFC") + cls.settings = bonsai.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger) + cls.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get()) + cls.settings.context_settings = cls.create_settings() + cls.settings.gross_context_settings = cls.create_settings(is_gross=True) + @classmethod def set_settings(cls, settings: bonsai.bim.import_ifc.IfcImportSettings) -> None: cls.settings = settings