Fix #3169. Don't allow the user to create invalid material sets without items.

This commit is contained in:
Dion Moult
2023-05-27 22:50:59 +10:00
parent 00c010e04d
commit c8c0c9a621
4 changed files with 50 additions and 52 deletions
@@ -96,6 +96,7 @@ class ObjectMaterialData:
cls.data["total_thickness"] = cls.total_thickness() cls.data["total_thickness"] = cls.total_thickness()
cls.data["materials"] = cls.materials() cls.data["materials"] = cls.materials()
cls.data["type_material"] = cls.type_material() cls.data["type_material"] = cls.type_material()
cls.data["material_type"] = cls.material_type()
cls.is_loaded = True cls.is_loaded = True
@classmethod @classmethod
@@ -244,3 +245,17 @@ class ObjectMaterialData:
else: else:
name_attr = "Name" name_attr = "Name"
return getattr(material, name_attr, "Unnamed") or "Unnamed" 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]
@@ -189,23 +189,25 @@ class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator):
material=material, material=material,
) )
assigned_material = ifcopenshell.util.element.get_material(element) assigned_material = ifcopenshell.util.element.get_material(element)
if assigned_material.is_a() in ("IfcMaterialLayerSet", "IfcMaterialLayerSetUsage"): if assigned_material.is_a("IfcMaterialConstituentSet"):
if assigned_material.is_a("IfcMaterialLayerSet"): if not assigned_material.MaterialConstituents:
layer_set = assigned_material ifcopenshell.api.run(
else: "material.add_constituent",
layer_set = assigned_material.ForLayerSet tool.Ifc.get(),
constituent_set=assigned_material,
if not layer_set.MaterialLayers: material=material,
)
elif assigned_material.is_a() == "IfcMaterialLayerSet":
if not assigned_material.MaterialLayers:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
layer = ifcopenshell.api.run( layer = ifcopenshell.api.run(
"material.add_layer", "material.add_layer",
tool.Ifc.get(), tool.Ifc.get(),
layer_set=layer_set, layer_set=assigned_material,
material=material, material=material,
) )
thickness = 0.1 # Arbitrary metric thickness for now thickness = 0.1 # Arbitrary metric thickness for now
layer.LayerThickness = thickness / unit_scale layer.LayerThickness = thickness / unit_scale
elif assigned_material.is_a("IfcMaterialProfileSet"): elif assigned_material.is_a("IfcMaterialProfileSet"):
if not assigned_material.MaterialProfiles: if not assigned_material.MaterialProfiles:
named_profiles = [p for p in tool.Ifc.get().by_type("IfcProfileDef") if p.ProfileName] 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() constituent: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.constituent)): constituent = tool.Ifc.get().by_id(self.constituent)
if inverse.is_a("IfcMaterialConstituentSet") and len(inverse.MaterialConstituents) == 1: for material_set in layer.ToMaterialConstituentSet:
return if len(material_set.MaterialConstituents) == 1:
ifcopenshell.api.run( self.report({"ERROR"}, "At least one constituent must exist")
"material.remove_constituent", tool.Ifc.get(), constituent=tool.Ifc.get().by_id(self.constituent) return {"ERROR"}
) ifcopenshell.api.run("material.remove_constituent", tool.Ifc.get(), constituent=constituent)
class AddProfile(bpy.types.Operator, tool.Ifc.Operator): class AddProfile(bpy.types.Operator, tool.Ifc.Operator):
@@ -314,10 +316,12 @@ class RemoveProfile(bpy.types.Operator, tool.Ifc.Operator):
profile: bpy.props.IntProperty() profile: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.profile)): profile = tool.Ifc.get().by_id(self.profile)
if inverse.is_a("IfcMaterialProfileSet") and len(inverse.MaterialProfiles) == 1: for material_set in profile.ToMaterialProfileSet:
return if len(material_set.MaterialProfiles) == 1:
ifcopenshell.api.run("material.remove_profile", tool.Ifc.get(), profile=tool.Ifc.get().by_id(self.profile)) 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): 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()) 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 layer.LayerThickness = thickness / unit_scale
@@ -376,13 +380,12 @@ class RemoveLayer(bpy.types.Operator, tool.Ifc.Operator):
layer: bpy.props.IntProperty() layer: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.layer)): layer = tool.Ifc.get().by_id(self.layer)
if inverse.is_a("IfcMaterialLayerSet") and len(inverse.MaterialLayers) == 1: for material_set in layer.ToMaterialLayerSet:
self.report( if len(material_set.MaterialLayers) == 1:
{"ERROR"}, "Cannot remove material layer - IfcMaterialLayerSet should alawys have atleast 1 layer" self.report({"ERROR"}, "At least one layer must exist")
)
return {"ERROR"} 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): class AddListItem(bpy.types.Operator, tool.Ifc.Operator):
@@ -85,24 +85,10 @@ def get_materials(self, context):
return ObjectMaterialData.data["materials"] return ObjectMaterialData.data["materials"]
def get_object_material_types(self, context): def get_object_material_type(self, context):
global materialtypes_enum if not ObjectMaterialData.is_loaded:
if len(materialtypes_enum) == 0 and IfcStore.get_file(): ObjectMaterialData.load()
material_types = [ return ObjectMaterialData.data["material_type"]
"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_material_types(self, context): def get_material_types(self, context):
@@ -137,7 +123,7 @@ class BIMMaterialProperties(PropertyGroup):
class BIMObjectMaterialProperties(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") material: EnumProperty(items=get_materials, name="Material")
is_editing: BoolProperty(name="Is Editing", default=False) is_editing: BoolProperty(name="Is Editing", default=False)
material_set_usage_attributes: CollectionProperty(name="Material Set Usage Attributes", type=Attribute) material_set_usage_attributes: CollectionProperty(name="Material Set Usage Attributes", type=Attribute)
@@ -155,13 +155,7 @@ class BIM_PT_object_material(Panel):
row = self.layout.row(align=True) row = self.layout.row(align=True)
prop_with_search(row, self.props, "material_type", text="") prop_with_search(row, self.props, "material_type", text="")
if self.props.material_type in ( prop_with_search(row, self.props, "material", text="")
"IfcMaterial",
"IfcMaterialList",
"IfcMaterialLayerSet",
"IfcMaterialLayerSetUsage",
):
prop_with_search(row, self.props, "material", text="")
row.operator("bim.assign_material", icon="ADD", text="") row.operator("bim.assign_material", icon="ADD", text="")
def draw_material_ui(self): def draw_material_ui(self):