From 10b5910654e70ec954f9ef459098f53fc79e687b Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 8 Jul 2024 13:43:42 +0500 Subject: [PATCH] support assigning existing non-ifcmaterials materials Example - https://imgur.com/a/KN8mQPL Also added some description to material operators (https://imgur.com/a/eIVNEdU). Need to somehow emphasize that this UI is only for IfcMaterials and cannot be used to assign existing layer sets, profile sets, etc... --- .../bim/module/material/__init__.py | 1 + .../bim/module/material/operator.py | 39 +++++++++++++++++++ .../blenderbim/bim/module/material/prop.py | 2 +- .../blenderbim/bim/module/material/ui.py | 4 ++ src/blenderbim/blenderbim/core/material.py | 24 ++++++++++-- src/blenderbim/blenderbim/core/tool.py | 2 +- src/blenderbim/blenderbim/tool/material.py | 17 ++++++-- 7 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/material/__init__.py b/src/blenderbim/blenderbim/bim/module/material/__init__.py index 437003800c..bbf9a2327d 100644 --- a/src/blenderbim/blenderbim/bim/module/material/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/material/__init__.py @@ -28,6 +28,7 @@ classes = ( operator.AddMaterialSet, operator.AddProfile, operator.AssignMaterial, + operator.AssignMaterialToSelected, operator.AssignParameterizedProfile, operator.ContractMaterialCategory, operator.DisableEditingAssignedMaterial, diff --git a/src/blenderbim/blenderbim/bim/module/material/operator.py b/src/blenderbim/blenderbim/bim/module/material/operator.py index 28cfef9dfa..9e7f3ec8ac 100644 --- a/src/blenderbim/blenderbim/bim/module/material/operator.py +++ b/src/blenderbim/blenderbim/bim/module/material/operator.py @@ -117,6 +117,7 @@ class AssignParameterizedProfile(bpy.types.Operator, tool.Ifc.Operator): class AddMaterial(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_material" bl_label = "Add Material" + bl_description = "Create a new IfcMaterial" bl_options = {"REGISTER", "UNDO"} name: bpy.props.StringProperty(default="Default") category: bpy.props.StringProperty(default="") @@ -185,6 +186,32 @@ class RemoveMaterialSet(bpy.types.Operator, tool.Ifc.Operator): core.remove_material_set(tool.Ifc, tool.Material, material=tool.Ifc.get().by_id(self.material)) +class AssignMaterialToSelected(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.assign_material_to_selected" + bl_label = "Assign Material To Selected" + bl_description = "Assign currently selected material in Materials UI to the selected objects" + bl_options = {"REGISTER", "UNDO"} + material: bpy.props.IntProperty(name="Material IFC ID") + + @classmethod + def poll(cls, context): + if not context.selected_objects: + cls.poll_message_set("No objects selected") + return False + return True + + def _execute(self, context): + material = tool.Ifc.get().by_id(self.material) + objects = tool.Blender.get_selected_objects() + core.assign_material( + tool.Ifc, + tool.Material, + material_type=tool.Material.get_active_material_type(), + objects=objects, + material=material, + ) + + class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_material" bl_label = "Assign Material" @@ -192,6 +219,18 @@ class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator): obj: bpy.props.StringProperty() material_type: bpy.props.StringProperty() + @classmethod + def description(cls, context, properties): + if not (material_type := properties.material_type): + if not (obj := context.active_object): + return "" + material_type = obj.BIMObjectMaterialProperties.material_type + + description = "Assign current IfcMaterial to the selected objects" + if material_type != "IfcMaterial": + description += f" as part of a new {material_type}" + return description + def _execute(self, context): objects = [bpy.data.objects.get(self.obj)] if self.obj else tool.Blender.get_selected_objects() core.assign_material(tool.Ifc, tool.Material, material_type=self.material_type, objects=objects) diff --git a/src/blenderbim/blenderbim/bim/module/material/prop.py b/src/blenderbim/blenderbim/bim/module/material/prop.py index d8737ad21e..192f7e7085 100644 --- a/src/blenderbim/blenderbim/bim/module/material/prop.py +++ b/src/blenderbim/blenderbim/bim/module/material/prop.py @@ -143,7 +143,7 @@ class BIMMaterialProperties(PropertyGroup): class BIMObjectMaterialProperties(PropertyGroup): material_type: EnumProperty(items=get_object_material_type, name="Material Type") - material: EnumProperty(items=get_materials, name="Material") + material: EnumProperty(items=get_materials, name="Material", description="Currently selected IfcMaterial") is_editing: BoolProperty(name="Is Editing", default=False) material_set_usage_attributes: CollectionProperty(name="Material Set Usage Attributes", type=Attribute) material_set_attributes: CollectionProperty(name="Material Set Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/material/ui.py b/src/blenderbim/blenderbim/bim/module/material/ui.py index 28bc1571e3..07577a28cd 100644 --- a/src/blenderbim/blenderbim/bim/module/material/ui.py +++ b/src/blenderbim/blenderbim/bim/module/material/ui.py @@ -67,6 +67,8 @@ class BIM_PT_materials(Panel): op.material = material.ifc_definition_id op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF") op.material = material.ifc_definition_id + op = row.operator("bim.assign_material_to_selected", text="", icon="BRUSH_DATA") + op.material = material.ifc_definition_id op = row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL") op.material = material.ifc_definition_id op = row.operator("bim.enable_editing_material_style", text="", icon="SHADING_RENDERED") @@ -81,6 +83,8 @@ class BIM_PT_materials(Panel): if material.ifc_definition_id: op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF") op.material = material.ifc_definition_id + op = row.operator("bim.assign_material_to_selected", text="", icon="BRUSH_DATA") + op.material = material.ifc_definition_id row.operator("bim.remove_material_set", text="", icon="X").material = material.ifc_definition_id self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index") diff --git a/src/blenderbim/blenderbim/core/material.py b/src/blenderbim/blenderbim/core/material.py index 119dfe77fd..75c9e8b97e 100644 --- a/src/blenderbim/blenderbim/core/material.py +++ b/src/blenderbim/blenderbim/core/material.py @@ -99,17 +99,33 @@ def disable_editing_material(material_tool: tool.Material) -> None: def assign_material( - ifc: tool.Ifc, material_tool: tool.Material, material_type: Union[str, None], objects: list[bpy.types.Object] + ifc: tool.Ifc, + material_tool: tool.Material, + material_type: Union[str, None], + objects: list[bpy.types.Object], + material: Optional[ifcopenshell.entity_instance] = None, ) -> None: - material_type = material_type or material_tool.get_active_object_material() - material = material_tool.get_active_material() + """Assign material to the provided objects. + + If `material_type` or `material` are not provided, the active ones from + Object Materials UI is used. + """ + material_type = material_type or material_tool.get_object_ui_material_type() + material = material or material_tool.get_object_ui_active_material() for obj in objects: element = ifc.get_entity(obj) if not element: continue + ifc.run("material.assign_material", products=[element], type=material_type, material=material) assigned_material = material_tool.get_material(element) - if material_tool.is_a_material_set(assigned_material): + assert assigned_material # Type checker. + + if material_tool.is_a_material_set(material): + # Ensure set is a valid IFC. + default_material = material_tool.get_default_material() + material_tool.add_material_to_set(material_set=material, material=default_material) + elif material_tool.is_a_material_set(assigned_material): material_tool.add_material_to_set(material_set=assigned_material, material=material) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index e3a6d57673..1b31080047 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -493,7 +493,7 @@ class Material: def enable_editing_materials(cls): pass def get_active_material(cls): pass def get_active_material_type(cls): pass - def get_active_object_material(cls, obj): pass + def get_active_object_material_type(cls, obj): pass def get_elements_by_material(cls, material): pass def get_material(cls, element, should_inherit): pass def get_material_attributes(cls): pass diff --git a/src/blenderbim/blenderbim/tool/material.py b/src/blenderbim/blenderbim/tool/material.py index 9a0864a252..85b2a5144f 100644 --- a/src/blenderbim/blenderbim/tool/material.py +++ b/src/blenderbim/blenderbim/tool/material.py @@ -19,6 +19,7 @@ from __future__ import annotations import bpy import ifcopenshell +import ifcopenshell.api.material import blenderbim.core.tool import blenderbim.core.material import blenderbim.tool as tool @@ -165,19 +166,27 @@ class Material(blenderbim.core.tool.Material): props.active_material_id = 0 props.editing_material_type = "" + @classmethod + def get_default_material(cls) -> ifcopenshell.entity_instance: + """Return first found IfcMaterial in IFC file or create a new default material.""" + ifc_file = tool.Ifc.get() + material = next(iter(ifc_file.by_type("IfcMaterial")), None) + if material: + return material + material = ifcopenshell.api.material.add_material(ifc_file, name="Default") + return material + @classmethod def get_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: return ifcopenshell.util.element.get_type(element) @classmethod - def get_active_object_material(cls) -> Union[str, None]: + def get_object_ui_material_type(cls) -> str: active_obj = bpy.context.active_object - if not active_obj: - return return active_obj.BIMObjectMaterialProperties.material_type @classmethod - def get_active_material(cls) -> ifcopenshell.entity_instance: + def get_object_ui_active_material(cls) -> ifcopenshell.entity_instance: return tool.Ifc.get().by_id(int(bpy.context.active_object.BIMObjectMaterialProperties.material)) @classmethod