diff --git a/src/blenderbim/blenderbim/bim/module/classification/ui.py b/src/blenderbim/blenderbim/bim/module/classification/ui.py index 032a7cd022..efcdc6c42e 100644 --- a/src/blenderbim/blenderbim/bim/module/classification/ui.py +++ b/src/blenderbim/blenderbim/bim/module/classification/ui.py @@ -41,7 +41,7 @@ class BIM_PT_classifications(Panel): row = self.layout.row(align=True) row.prop(self.props.classification_attributes.get("Name"), "string_value", text="", icon="ASSET_MANAGER") row.operator("bim.edit_classification", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_classification", text="", icon="X") + row.operator("bim.disable_editing_classification", text="", icon="CANCEL") for attribute in self.props.classification_attributes: if attribute.name == "Name": diff --git a/src/blenderbim/blenderbim/bim/module/material/__init__.py b/src/blenderbim/blenderbim/bim/module/material/__init__.py index 83ef17c184..76d4dca153 100644 --- a/src/blenderbim/blenderbim/bim/module/material/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/material/__init__.py @@ -2,6 +2,7 @@ import bpy from . import ui, prop, operator classes = ( + operator.CopyMaterial, operator.AddMaterial, operator.RemoveMaterial, operator.AssignMaterial, diff --git a/src/blenderbim/blenderbim/bim/module/material/operator.py b/src/blenderbim/blenderbim/bim/module/material/operator.py index 506bb2cd5e..9bc102c2ce 100644 --- a/src/blenderbim/blenderbim/bim/module/material/operator.py +++ b/src/blenderbim/blenderbim/bim/module/material/operator.py @@ -754,3 +754,45 @@ class EditMaterialSetItem(bpy.types.Operator): bpy.ops.bim.disable_editing_material_set_item(obj=obj.name) return {"FINISHED"} + + +class CopyMaterial(bpy.types.Operator): + bl_idname = "bim.copy_material" + bl_label = "Copy Material" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + material = ifcopenshell.util.element.get_material( + self.file.by_id(context.active_object.BIMObjectProperties.ifc_definition_id) + ) + for obj in context.selected_objects: + if obj == context.active_object: + continue + if not obj.BIMObjectProperties.ifc_definition_id: + continue + ifcopenshell.api.run( + "material.copy_material", + self.file, + **{ + "material": material, + "element": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), + }, + ) + Data.load(self.file, obj.BIMObjectProperties.ifc_definition_id) + self.set_default_material(obj, material) + return {"FINISHED"} + + def set_default_material(self, obj, material): + object_material_ids = [ + om.BIMObjectProperties.ifc_definition_id + for om in obj.data.materials + if om is not None and om.BIMObjectProperties.ifc_definition_id + ] + + if material.id() in object_material_ids: + return + obj.data.materials.append(IfcStore.get_element(material.id())) diff --git a/src/blenderbim/blenderbim/bim/module/material/ui.py b/src/blenderbim/blenderbim/bim/module/material/ui.py index d0e2b1ade4..1a803aa836 100644 --- a/src/blenderbim/blenderbim/bim/module/material/ui.py +++ b/src/blenderbim/blenderbim/bim/module/material/ui.py @@ -118,6 +118,8 @@ class BIM_PT_object_material(Panel): op.material_set_usage = self.product_data["id"] row.operator("bim.disable_editing_assigned_material", icon="CANCEL", text="") else: + if self.product_data["type"] == "IfcMaterial": + row.operator("bim.copy_material", icon="COPYDOWN", text="") row.operator("bim.enable_editing_assigned_material", icon="GREASEPENCIL", text="") row.operator("bim.unassign_material", icon="X", text="") @@ -200,7 +202,9 @@ class BIM_PT_object_material(Panel): row.prop(self.props, "profile_classes", text="") if self.props.profile_classes == "IfcParameterizedProfileDef": row.prop(self.props, "parameterized_profile_classes", text="") - op = row.operator("bim.assign_parameterized_profile", icon="GREASEPENCIL" if item["Profile"] else "ADD", text="") + op = row.operator( + "bim.assign_parameterized_profile", icon="GREASEPENCIL" if item["Profile"] else "ADD", text="" + ) op.ifc_class = self.props.parameterized_profile_classes op.material_profile = item["id"] else: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py index 50927d3aa9..7c9e17aece 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py @@ -1,6 +1,3 @@ -import ifcopenshell - - class Usecase: def __init__(self, file, **settings): self.file = file diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py new file mode 100644 index 0000000000..977d4964a0 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py @@ -0,0 +1,30 @@ +import ifcopenshell +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"material": None, "element": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + ifcopenshell.api.run("material.unassign_material", self.file, product=self.settings["element"]) + if self.settings["material"].is_a("IfcMaterial"): + ifcopenshell.api.run( + "material.assign_material", + self.file, + product=self.settings["element"], + type="IfcMaterial", + material=self.settings["material"], + ) + # No other material type can be copied right now. + # 1. Material lists and constituents may have shape aspects and I + # haven't implemented it yet. + # 2. Material layer and profile sets implicitly define parametric + # geometry and we have no way of guaranteeing that this constraint is + # satisfied. + # 3. Material set usages follow an unofficial constraint that all + # instances must have a usage of their type's material set. We cannot + # guarantee that constraint.