diff --git a/src/bonsai/bonsai/bim/module/geometry/data.py b/src/bonsai/bonsai/bim/module/geometry/data.py index 5dc52e74a4..74bf2ef5ea 100644 --- a/src/bonsai/bonsai/bim/module/geometry/data.py +++ b/src/bonsai/bonsai/bim/module/geometry/data.py @@ -109,11 +109,12 @@ class RepresentationsData: @classmethod def representations(cls): results = [] - element = tool.Ifc.get_entity(bpy.context.active_object) + obj = tool.Geometry.get_active_or_representation_obj() + element = tool.Ifc.get_entity(obj) active_representation_id = None - if bpy.context.active_object.data and hasattr(bpy.context.active_object.data, "BIMMeshProperties"): - active_representation_id = bpy.context.active_object.data.BIMMeshProperties.ifc_definition_id + if obj.data and hasattr(obj.data, "BIMMeshProperties"): + active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id for representation in tool.Geometry.get_representations_iter(element): representation_type = representation.RepresentationType @@ -159,7 +160,7 @@ class RepresentationsData: # Ignore objects without representations, e.g. IfcRelSpaceBoundary. if not cls.data["representations"]: return [] - obj = bpy.context.active_object + obj = tool.Geometry.get_active_or_representation_obj() if not obj.data: return [] element = tool.Ifc.get_entity(obj) @@ -192,7 +193,7 @@ class RepresentationItemsData: @classmethod def total_items(cls) -> int: result = 0 - obj = bpy.context.active_object + obj = tool.Geometry.get_active_or_representation_obj() assert obj element = tool.Geometry.get_active_representation(obj) if element: diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 1bb1c79fdf..20df7d3006 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -2314,7 +2314,7 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - obj = context.active_object + obj = tool.Geometry.get_active_or_representation_obj() props = obj.BIMGeometryProperties props.is_editing = True @@ -2326,8 +2326,8 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator): item.tags += "," item.tags += tag - if bpy.context.active_object.data and hasattr(bpy.context.active_object.data, "BIMMeshProperties"): - active_representation_id = bpy.context.active_object.data.BIMMeshProperties.ifc_definition_id + if obj.data and hasattr(obj.data, "BIMMeshProperties"): + active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id element = tool.Ifc.get().by_id(active_representation_id) # IfcShapeRepresentation or IfcTopologyRepresentation. if not element.is_a("IfcShapeModel"): @@ -2376,7 +2376,7 @@ class DisableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - obj = context.active_object + obj = tool.Geometry.get_active_or_representation_obj() obj.BIMGeometryProperties.is_editing = False @@ -2388,7 +2388,7 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator): @classmethod def poll(cls, context): - if context.active_object is None or len(context.active_object.BIMGeometryProperties.items) <= 1: + if not (obj := tool.Geometry.get_active_or_representation_obj()) or len(obj.BIMGeometryProperties.items) <= 1: cls.poll_message_set( "Active object need to have more than 1 representation items to keep representation valid" ) @@ -2396,7 +2396,7 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator): return True def _execute(self, context): - obj = context.active_object + obj = tool.Geometry.get_active_or_representation_obj() ifc_file = tool.Ifc.get() representation_item = ifc_file.by_id(self.representation_item_id) @@ -2409,7 +2409,7 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator): def poll_editing_representation_item_style(cls, context): - if not (obj := getattr(context, "active_object", None)): + if not (obj := tool.Geometry.get_active_or_representation_obj()): return False props = obj.BIMGeometryProperties if not props.is_editing: @@ -2446,7 +2446,8 @@ class EnableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator return poll_editing_representation_item_style(cls, context) def _execute(self, context): - props = context.active_object.BIMGeometryProperties + obj = tool.Geometry.get_active_or_representation_obj() + props = obj.BIMGeometryProperties props.is_editing_item_style = True ifc_file = tool.Ifc.get() @@ -2464,7 +2465,7 @@ class EditRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - obj = context.active_object + obj = tool.Geometry.get_active_or_representation_obj() props = obj.BIMGeometryProperties props.is_editing_item_style = False ifc_file = tool.Ifc.get() @@ -2486,7 +2487,8 @@ class DisableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operato bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - props = context.active_object.BIMGeometryProperties + obj = tool.Geometry.get_active_or_representation_obj() + props = obj.BIMGeometryProperties props.is_editing_item_style = False @@ -2501,7 +2503,7 @@ class UnassignRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator): return poll_editing_representation_item_style(cls, context) def _execute(self, context): - active_obj = context.active_object + active_obj = tool.Geometry.get_active_or_representation_obj() active_props = active_obj.BIMGeometryProperties active_props.is_editing_item_style = False @@ -2568,7 +2570,8 @@ class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Op bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - props = context.active_object.BIMGeometryProperties + obj = tool.Geometry.get_active_or_representation_obj() + props = obj.BIMGeometryProperties props.is_editing_item_shape_aspect = True # set dropdown to currently active shape aspect @@ -2583,7 +2586,7 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - obj = context.active_object + obj = tool.Geometry.get_active_or_representation_obj() element = tool.Ifc.get_entity(obj) props = obj.BIMGeometryProperties props.is_editing_item_shape_aspect = False @@ -2639,7 +2642,8 @@ class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.O bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - props = context.active_object.BIMGeometryProperties + obj = tool.Geometry.get_active_or_representation_obj() + props = obj.BIMGeometryProperties props.is_editing_item_shape_aspect = False @@ -2649,7 +2653,7 @@ class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, tool.Ifc.Opera bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - obj = context.active_object + obj = tool.Geometry.get_active_or_representation_obj() element = tool.Ifc.get_entity(obj) props = obj.BIMGeometryProperties ifc_file = tool.Ifc.get() diff --git a/src/bonsai/bonsai/bim/module/geometry/prop.py b/src/bonsai/bonsai/bim/module/geometry/prop.py index e151bf63d0..033034391a 100644 --- a/src/bonsai/bonsai/bim/module/geometry/prop.py +++ b/src/bonsai/bonsai/bim/module/geometry/prop.py @@ -136,8 +136,8 @@ class ShapeAspect(PropertyGroup): name: StringProperty( name="Name", description=( - "Note that IfcMaterialConstituent is applied based on shape aspects using the same name as material constituent.\n" - "In dropdown suggestions you can see names of existing material constituents." + "If applicable, shape aspect names should correlate with names of material constituents.\n" + "Click to see autocompletion for constituent names." ), **({} if bpy.app.version < (3, 3, 0) else {"search": get_material_constituents}), ) diff --git a/src/bonsai/bonsai/bim/module/geometry/ui.py b/src/bonsai/bonsai/bim/module/geometry/ui.py index c7ab3ddfe6..7d5f1d0ca6 100644 --- a/src/bonsai/bonsai/bim/module/geometry/ui.py +++ b/src/bonsai/bonsai/bim/module/geometry/ui.py @@ -119,25 +119,18 @@ class BIM_PT_representations(Panel): @classmethod def poll(cls, context): - if not context.active_object: - return False - if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id): - return False - return IfcStore.get_file() + return tool.Ifc.get() and (obj := tool.Blender.get_active_object()) and tool.Ifc.get_entity(obj) def draw(self, context): if not RepresentationsData.is_loaded: RepresentationsData.load() - layout = self.layout - props = context.active_object.BIMObjectProperties - - row = layout.row(align=True) + row = self.layout.row(align=True) prop_with_search(row, context.active_object.BIMGeometryProperties, "contexts", text="") row.operator("bim.add_representation", icon="ADD", text="") if not RepresentationsData.data["representations"]: - layout.label(text="No Representations Found") + self.layout.label(text="No Representations Found") return for representation in RepresentationsData.data["representations"]: @@ -156,18 +149,16 @@ class BIM_PT_representations(Panel): op.ifc_definition_id = representation["id"] op.disable_opening_subtractions = False row.operator("bim.remove_representation", icon="X", text="").representation_id = representation["id"] - if representation["is_active"]: - active_representation = representation - layout.separator() + self.layout.separator() if not LayersData.is_loaded: LayersData.load() if LayersData.data["active_layers"]: - layout.label(text="Representation Presentation Layers:") + self.layout.label(text="Representation Presentation Layers:") for layer_name in LayersData.data["active_layers"].values(): - layout.label(text=layer_name, icon="STICKY_UVS_LOC") + self.layout.label(text=layer_name, icon="STICKY_UVS_LOC") else: - layout.label(text="Representation Has No Presentation Layers", icon="STICKY_UVS_LOC") + self.layout.label(text="Representation Has No Presentation Layers", icon="STICKY_UVS_LOC") class BIM_PT_representation_items(Panel): @@ -181,18 +172,14 @@ class BIM_PT_representation_items(Panel): @classmethod def poll(cls, context): - if not context.active_object: - return False - if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id): - return False - return IfcStore.get_file() + return tool.Ifc.get() and tool.Geometry.get_active_or_representation_obj() def draw(self, context): if not RepresentationItemsData.is_loaded: RepresentationItemsData.load() - props = context.active_object.BIMGeometryProperties - layout = self.layout + obj = context.scene.BIMGeometryProperties.representation_obj or tool.Blender.get_active_object() + props = obj.BIMGeometryProperties row = self.layout.row(align=True) row.label(text=f"{RepresentationItemsData.data['total_items']} Items Found") @@ -240,13 +227,13 @@ class BIM_PT_representation_items(Panel): text = "Has UV mapping" else: text = "Has no UV mapping" - layout.label(text=text, icon="UV") + self.layout.label(text=text, icon="UV") if "Colour" in active_item.tags: text = "Has colour mapping" else: text = "Has no colour mapping" - layout.label(text=text, icon="COLOR") + self.layout.label(text=text, icon="COLOR") row = self.layout.row(align=True) if props.is_editing_item_shape_aspect: diff --git a/src/bonsai/bonsai/bim/module/material/data.py b/src/bonsai/bonsai/bim/module/material/data.py index 0605f5d699..95e68468c6 100644 --- a/src/bonsai/bonsai/bim/module/material/data.py +++ b/src/bonsai/bonsai/bim/module/material/data.py @@ -175,7 +175,7 @@ class ObjectMaterialData: @classmethod def material_class(cls) -> Union[str, None]: - element = tool.Ifc.get_entity(bpy.context.active_object) + element = tool.Ifc.get_entity(tool.Geometry.get_active_or_representation_obj()) cls.material = ifcopenshell.util.element.get_material(element) if cls.material: return cls.material.is_a() @@ -338,7 +338,7 @@ class ObjectMaterialData: @classmethod def type_material(cls): - element = tool.Ifc.get_entity(bpy.context.active_object) + element = tool.Ifc.get_entity(tool.Geometry.get_active_or_representation_obj()) element_type = ifcopenshell.util.element.get_type(element) if element_type and element_type != element: material = ifcopenshell.util.element.get_material(element_type) diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index 96324df008..7a85701d08 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -895,8 +895,7 @@ class BIM_PT_tab_representations(Panel): return ( tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get() - and (obj := context.active_object) - and tool.Ifc.get_entity(obj) + and tool.Geometry.get_active_or_representation_obj() ) def draw(self, context): @@ -1234,6 +1233,7 @@ class BIM_PT_decorators_overlay(Panel): row = col.row(align=True) row.prop(model_props, "show_slab_direction", text="Slab Direction") + class BIM_PT_snappping(Panel): bl_space_type = "VIEW_3D" bl_region_type = "HEADER" @@ -1252,4 +1252,3 @@ class BIM_PT_snappping(Panel): col.prop(prop, "edge", toggle=True, icon="SNAP_EDGE") col.prop(prop, "edge_center", toggle=True, icon="SNAP_MIDPOINT") col.prop(prop, "face", toggle=True, icon="SNAP_FACE") - diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 069ada8f13..f1e5b61638 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -978,6 +978,14 @@ class Geometry(bonsai.core.tool.Geometry): and tool.Ifc.get().by_id(ifc_id).is_a("IfcRepresentationItem") ) + @classmethod + def get_active_or_representation_obj(cls) -> bpy.types.Object | None: + if obj := tool.Blender.get_active_object(): + if tool.Ifc.get_entity(obj): + return obj + elif tool.Geometry.is_representation_item(obj): + return bpy.context.scene.BIMGeometryProperties.representation_obj + @classmethod def is_boolean_operand(cls, obj: bpy.types.Object) -> bool: return bool(