From c8c0c9a6219dfc7a596f13156f2f8b20e2f47836 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 27 May 2023 22:50:59 +1000 Subject: [PATCH] Fix #3169. Don't allow the user to create invalid material sets without items. --- .../blenderbim/bim/module/material/data.py | 15 +++++ .../bim/module/material/operator.py | 55 ++++++++++--------- .../blenderbim/bim/module/material/prop.py | 24 ++------ .../blenderbim/bim/module/material/ui.py | 8 +-- 4 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/material/data.py b/src/blenderbim/blenderbim/bim/module/material/data.py index 81be795f0d..9e1d4154b9 100644 --- a/src/blenderbim/blenderbim/bim/module/material/data.py +++ b/src/blenderbim/blenderbim/bim/module/material/data.py @@ -96,6 +96,7 @@ class ObjectMaterialData: cls.data["total_thickness"] = cls.total_thickness() cls.data["materials"] = cls.materials() cls.data["type_material"] = cls.type_material() + cls.data["material_type"] = cls.material_type() cls.is_loaded = True @classmethod @@ -244,3 +245,17 @@ class ObjectMaterialData: else: name_attr = "Name" return getattr(material, name_attr, "Unnamed") or "Unnamed" + + @classmethod + def material_type(cls): + material_types = [ + "IfcMaterial", + "IfcMaterialConstituentSet", + "IfcMaterialLayerSet", + "IfcMaterialProfileSet", + "IfcMaterialList", + ] + version = tool.Ifc.get_schema() + if version == "IFC2X3": + material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialList"] + return [(m, m, ifcopenshell.util.doc.get_entity_doc(version, m).get("description", "")) for m in material_types] diff --git a/src/blenderbim/blenderbim/bim/module/material/operator.py b/src/blenderbim/blenderbim/bim/module/material/operator.py index 2390537890..756b2d8e9b 100644 --- a/src/blenderbim/blenderbim/bim/module/material/operator.py +++ b/src/blenderbim/blenderbim/bim/module/material/operator.py @@ -189,23 +189,25 @@ class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator): material=material, ) assigned_material = ifcopenshell.util.element.get_material(element) - if assigned_material.is_a() in ("IfcMaterialLayerSet", "IfcMaterialLayerSetUsage"): - if assigned_material.is_a("IfcMaterialLayerSet"): - layer_set = assigned_material - else: - layer_set = assigned_material.ForLayerSet - - if not layer_set.MaterialLayers: + if assigned_material.is_a("IfcMaterialConstituentSet"): + if not assigned_material.MaterialConstituents: + ifcopenshell.api.run( + "material.add_constituent", + tool.Ifc.get(), + constituent_set=assigned_material, + material=material, + ) + elif assigned_material.is_a() == "IfcMaterialLayerSet": + if not assigned_material.MaterialLayers: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) layer = ifcopenshell.api.run( "material.add_layer", tool.Ifc.get(), - layer_set=layer_set, + layer_set=assigned_material, material=material, ) thickness = 0.1 # Arbitrary metric thickness for now layer.LayerThickness = thickness / unit_scale - elif assigned_material.is_a("IfcMaterialProfileSet"): if not assigned_material.MaterialProfiles: named_profiles = [p for p in tool.Ifc.get().by_type("IfcProfileDef") if p.ProfileName] @@ -279,12 +281,12 @@ class RemoveConstituent(bpy.types.Operator, tool.Ifc.Operator): constituent: bpy.props.IntProperty() def _execute(self, context): - for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.constituent)): - if inverse.is_a("IfcMaterialConstituentSet") and len(inverse.MaterialConstituents) == 1: - return - ifcopenshell.api.run( - "material.remove_constituent", tool.Ifc.get(), constituent=tool.Ifc.get().by_id(self.constituent) - ) + constituent = tool.Ifc.get().by_id(self.constituent) + for material_set in layer.ToMaterialConstituentSet: + if len(material_set.MaterialConstituents) == 1: + self.report({"ERROR"}, "At least one constituent must exist") + return {"ERROR"} + ifcopenshell.api.run("material.remove_constituent", tool.Ifc.get(), constituent=constituent) class AddProfile(bpy.types.Operator, tool.Ifc.Operator): @@ -314,10 +316,12 @@ class RemoveProfile(bpy.types.Operator, tool.Ifc.Operator): profile: bpy.props.IntProperty() def _execute(self, context): - for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.profile)): - if inverse.is_a("IfcMaterialProfileSet") and len(inverse.MaterialProfiles) == 1: - return - ifcopenshell.api.run("material.remove_profile", tool.Ifc.get(), profile=tool.Ifc.get().by_id(self.profile)) + profile = tool.Ifc.get().by_id(self.profile) + for material_set in profile.ToMaterialProfileSet: + if len(material_set.MaterialProfiles) == 1: + self.report({"ERROR"}, "At least one profile must exist") + return {"ERROR"} + ifcopenshell.api.run("material.remove_profile", tool.Ifc.get(), profile=profile) class AddLayer(bpy.types.Operator, tool.Ifc.Operator): @@ -340,7 +344,7 @@ class AddLayer(bpy.types.Operator, tool.Ifc.Operator): ) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - thickness = 0.1 # Arbitrary metric thickness for now + thickness = 0.1 # Arbitrary metric thickness for now layer.LayerThickness = thickness / unit_scale @@ -376,13 +380,12 @@ class RemoveLayer(bpy.types.Operator, tool.Ifc.Operator): layer: bpy.props.IntProperty() def _execute(self, context): - for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.layer)): - if inverse.is_a("IfcMaterialLayerSet") and len(inverse.MaterialLayers) == 1: - self.report( - {"ERROR"}, "Cannot remove material layer - IfcMaterialLayerSet should alawys have atleast 1 layer" - ) + layer = tool.Ifc.get().by_id(self.layer) + for material_set in layer.ToMaterialLayerSet: + if len(material_set.MaterialLayers) == 1: + self.report({"ERROR"}, "At least one layer must exist") return {"ERROR"} - ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=tool.Ifc.get().by_id(self.layer)) + ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=layer) class AddListItem(bpy.types.Operator, tool.Ifc.Operator): diff --git a/src/blenderbim/blenderbim/bim/module/material/prop.py b/src/blenderbim/blenderbim/bim/module/material/prop.py index 16c00f6978..f09075cdf0 100644 --- a/src/blenderbim/blenderbim/bim/module/material/prop.py +++ b/src/blenderbim/blenderbim/bim/module/material/prop.py @@ -85,24 +85,10 @@ def get_materials(self, context): return ObjectMaterialData.data["materials"] -def get_object_material_types(self, context): - global materialtypes_enum - if len(materialtypes_enum) == 0 and IfcStore.get_file(): - material_types = [ - "IfcMaterial", - "IfcMaterialConstituentSet", - "IfcMaterialLayerSet", - "IfcMaterialLayerSetUsage", - "IfcMaterialProfileSet", - "IfcMaterialProfileSetUsage", - "IfcMaterialList", - ] - version = tool.Ifc.get_schema() - if version == "IFC2X3": - material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", "IfcMaterialList"] - materialtypes_enum.clear() - materialtypes_enum = [(m, m, get_entity_doc(version, m).get("description", "")) for m in material_types] - return materialtypes_enum +def get_object_material_type(self, context): + if not ObjectMaterialData.is_loaded: + ObjectMaterialData.load() + return ObjectMaterialData.data["material_type"] def get_material_types(self, context): @@ -137,7 +123,7 @@ class BIMMaterialProperties(PropertyGroup): class BIMObjectMaterialProperties(PropertyGroup): - material_type: EnumProperty(items=get_object_material_types, name="Material Type") + material_type: EnumProperty(items=get_object_material_type, name="Material Type") material: EnumProperty(items=get_materials, name="Material") is_editing: BoolProperty(name="Is Editing", default=False) material_set_usage_attributes: CollectionProperty(name="Material Set Usage Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/material/ui.py b/src/blenderbim/blenderbim/bim/module/material/ui.py index be85ca2e06..da1490aae1 100644 --- a/src/blenderbim/blenderbim/bim/module/material/ui.py +++ b/src/blenderbim/blenderbim/bim/module/material/ui.py @@ -155,13 +155,7 @@ class BIM_PT_object_material(Panel): row = self.layout.row(align=True) prop_with_search(row, self.props, "material_type", text="") - if self.props.material_type in ( - "IfcMaterial", - "IfcMaterialList", - "IfcMaterialLayerSet", - "IfcMaterialLayerSetUsage", - ): - prop_with_search(row, self.props, "material", text="") + prop_with_search(row, self.props, "material", text="") row.operator("bim.assign_material", icon="ADD", text="") def draw_material_ui(self):