WIP port object material assignment with 3 new features. See message. See #1222.

* Full support for editing IfcMaterialList added
 * The reusability of material set relationships are now roundtripped
 * UI is sensitive to IFC schema version and adapts intelligently
This commit is contained in:
Dion Moult
2021-01-17 12:46:35 +11:00
parent fa4ba58cac
commit 25fb9be82e
21 changed files with 943 additions and 292 deletions
@@ -20,6 +20,7 @@ if bpy is not None:
"bimtester": None,
"debug": None,
"geometry": None,
"material": None,
"model": None,
"owner": None,
"project": None,
@@ -191,10 +192,6 @@ if bpy is not None:
operator.AddSectionsAnnotations,
prop.StrProperty,
prop.Attribute,
prop.MaterialLayer,
prop.MaterialConstituent,
prop.MaterialProfile,
prop.MaterialSet,
prop.Variable,
prop.Role,
prop.Address,
@@ -250,9 +247,7 @@ if bpy is not None:
ui.BIM_PT_diff,
ui.BIM_PT_patch,
ui.BIM_PT_mvd,
ui.BIM_PT_material,
ui.BIM_PT_presentation_layer_data,
ui.BIM_PT_object_material,
ui.BIM_PT_classification_references,
ui.BIM_PT_documents,
ui.BIM_PT_constraint_relations,
@@ -302,7 +297,7 @@ if bpy is not None:
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
bpy.types.Scene.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
@@ -326,7 +321,7 @@ if bpy is not None:
del bpy.types.Scene.MapConversion
del bpy.types.Scene.TargetCRS
del bpy.types.Object.BIMObjectProperties
del bpy.types.Collection.BIMObjectProperties
del bpy.types.Collection.BIMObjectProperties # Check if we need this
del bpy.types.Material.BIMMaterialProperties
del bpy.types.Mesh.BIMMeshProperties
del bpy.types.Camera.BIMCameraProperties
@@ -180,86 +180,31 @@ class MaterialCreator:
def create_single(self, material):
if material.Name not in self.materials:
self.create_new_single(material)
self.obj.BIMObjectProperties.material_type = "IfcMaterial"
self.obj.BIMObjectProperties.material = self.materials[material.Name]
def create_layer_set(self, layer_set):
props = self.obj.BIMObjectProperties
props.material_type = "IfcMaterialLayerSet"
props.material_set.name = layer_set.LayerSetName or ""
if hasattr(layer_set, "Description"): # IFC2X3 support
props.material_set.description = layer_set.Description or ""
for layer in layer_set.MaterialLayers:
new = props.material_set.material_layers.add()
if layer.Material:
if layer.Material.Name not in self.materials:
self.create_new_single(layer.Material)
new.material = self.materials[layer.Material.Name]
new.layer_thickness = layer.LayerThickness
new.is_ventilated = "TRUE" if layer.IsVentilated else "FALSE"
if not hasattr(layer, "Name"):
continue # IFC2X3 support
new.name = layer.Name or ""
new.description = layer.Description or ""
try:
new.category = layer.Category if layer.Category else "None"
except:
new.custom_category = layer.Category or ""
new.priority = layer.Priority or 0
def create_constituent_set(self, constituent_set):
props = self.obj.BIMObjectProperties
props.material_type = "IfcMaterialConstituentSet"
props.material_set.name = constituent_set.Name or ""
props.material_set.description = constituent_set.Description or ""
for constituent in constituent_set.MaterialConstituents:
new = props.material_set.material_constituents.add()
new.name = constituent.Name or ""
new.description = constituent.Description or ""
if constituent.Material.Name not in self.materials:
self.create_new_single(constituent.Material)
new.material = self.materials[constituent.Material.Name]
new.fraction = constituent.Fraction or 0.0
new.category = constituent.Category or ""
def create_profile_set(self, profile_set):
props = self.obj.BIMObjectProperties
props.material_type = "IfcMaterialProfileSet"
props.material_set.name = profile_set.Name or ""
props.material_set.description = profile_set.Description or ""
for profile in profile_set.MaterialProfiles:
new = props.material_set.material_profiles.add()
new.name = profile.Name or ""
new.description = profile.Description or ""
if profile.Material.Name not in self.materials:
self.create_new_single(profile.Material)
new.material = self.materials[profile.Material.Name]
try:
new.profile = profile.Profile.is_a()
for i, attribute in enumerate(profile.Profile):
newa = new.profile_attributes.add()
newa.name = profile.Profile.attribute_name(i)
newa.string_value = str(attribute)
except:
pass # TODO: currently, only parametric profile sets are supported
new.priority = profile.Priority or 0
new.category = profile.Category or ""
def create_material_list(self, material_list):
props = self.obj.BIMObjectProperties
props.material_type = "IfcMaterialConstituentSet" # Constituent sets are the recommended upgrade path
for material in material_list.Materials:
new = props.material_set.material_constituents.add()
if material.Name not in self.materials:
self.create_new_single(material)
new.material = self.materials[material.Name]
def create_new_single(self, material):
self.materials[material.Name] = obj = bpy.data.materials.new(material.Name)
obj.BIMMaterialProperties.ifc_definition_id = int(material.id())
self.ifc_importer.add_element_attributes(material, obj.BIMMaterialProperties)
for pset in getattr(material, "HasProperties", ()):
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
if not material.HasRepresentation or not material.HasRepresentation[0].Representations:
return
for representation in material.HasRepresentation[0].Representations:
@@ -0,0 +1,29 @@
import bpy
from . import ui, prop, operator
classes = (
operator.AssignMaterial,
operator.UnassignMaterial,
operator.AddConstituent,
operator.RemoveConstituent,
operator.AddLayer,
operator.RemoveLayer,
operator.AddListItem,
operator.RemoveListItem,
operator.EnableEditingAssignedMaterial,
operator.DisableEditingAssignedMaterial,
operator.EditAssignedMaterial,
operator.EnableEditingMaterialSetItem,
operator.DisableEditingMaterialSetItem,
operator.EditMaterialSetItem,
prop.BIMObjectMaterialProperties,
ui.BIM_PT_object_material,
)
def register():
bpy.types.Object.BIMObjectMaterialProperties = bpy.props.PointerProperty(type=prop.BIMObjectMaterialProperties)
def unregister():
del bpy.types.Object.BIMObjectMaterialProperties
@@ -0,0 +1,16 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"constituent_set": None, "material": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
constituents = list(self.settings["constituent_set"].MaterialConstituents)
constituent = self.file.create_entity("IfcMaterialConstituent", **{"Material": self.settings["material"]})
constituents.append(constituent)
self.settings["constituent_set"].MaterialConstituents = constituents
return constituent
@@ -0,0 +1,19 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"layer_set": None, "material": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
layers = list(self.settings["layer_set"].MaterialLayers)
layer = self.file.create_entity("IfcMaterialLayer", **{
"Material": self.settings["material"],
"LayerThickness": 0.
})
layers.append(layer)
self.settings["layer_set"].MaterialLayers = layers
return layer
@@ -0,0 +1,14 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"material_list": None, "material": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
materials = list(self.settings["material_list"].Materials)
materials.append(self.settings["material"])
self.settings["material_list"].Materials = materials
@@ -0,0 +1,58 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"product": None, "type": "IfcMaterial", "material": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
if self.settings["type"] == "IfcMaterial":
self.assign_ifc_material()
elif self.settings["type"] == "IfcMaterialConstituentSet":
material_set = self.file.create_entity(self.settings["type"])
material_set.MaterialConstituents = [self.settings["material"]]
self.create_material_association(material_set)
elif self.settings["type"] == "IfcMaterialLayerSet":
material_set = self.file.create_entity(self.settings["type"])
material_set.MaterialLayers = [self.settings["material"]]
self.create_material_association(material_set)
elif self.settings["type"] == "IfcMaterialProfileSet":
material_set = self.file.create_entity(self.settings["type"])
material_set.MaterialProfiles = [self.settings["material"]]
self.create_material_association(material_set)
elif self.settings["type"] == "IfcMaterialList":
material_set = self.file.create_entity(self.settings["type"])
material_set.Materials = [self.settings["material"]]
self.create_material_association(material_set)
def assign_ifc_material(self):
rel = self.get_rel_associates_material(self.settings["material"])
if not rel:
return self.create_material_association(self.settings["material"])
related_objects = list(rel.RelatedObjects)
related_objects.append(self.settings["product"])
rel.RelatedObjects = related_objects
def create_material_association(self, relating_material):
return self.file.create_entity(
"IfcRelAssociatesMaterial",
**{
"GlobalId": ifcopenshell.guid.new(),
"RelatedObjects": [self.settings["product"]],
"RelatingMaterial": relating_material,
}
)
def get_rel_associates_material(self, material):
if self.file.schema == "IFC2X3":
rel = [
r
for r in self.file.by_type("IfcRelAssociatesMaterial")
if r.RelatingMaterial == self.settings["material"]
]
return rel[0] if rel else None
if self.settings["material"].AssociatedTo:
return self.settings["material"].AssociatedTo[0]
@@ -0,0 +1,88 @@
import ifcopenshell
from blenderbim.bim.ifc import IfcStore
class Data:
is_loaded = False
materials = {}
constituent_sets = {}
constituents = {}
layer_sets = {}
layers = {}
profile_sets = {}
profiles = {}
lists = {}
products = {}
_file = None
@classmethod
def load(cls, product_id=None):
cls._file = IfcStore.get_file()
if not cls._file:
return
if product_id:
return cls.load_product_material(product_id)
cls.load_materials()
cls.load_constituents()
cls.load_layers()
cls.load_profiles()
cls.load_lists()
cls.is_loaded = True
@classmethod
def load_materials(cls):
cls.load_element("IfcMaterial", cls.materials)
@classmethod
def load_constituents(cls):
cls.load_element("IfcMaterialConstituent", cls.constituents)
cls.load_element("IfcMaterialConstituentSet", cls.constituent_sets)
@classmethod
def load_layers(cls):
cls.load_element("IfcMaterialLayer", cls.layers)
cls.load_element("IfcMaterialLayerSet", cls.layer_sets)
@classmethod
def load_profiles(cls):
cls.load_element("IfcMaterialProfile", cls.profiles)
cls.load_element("IfcMaterialProfileSet", cls.profile_sets)
@classmethod
def load_lists(cls):
cls.load_element("IfcMaterialList", cls.lists)
@classmethod
def load_product_material(cls, product_id):
cls.products[product_id] = {}
for association in cls._file.by_id(product_id).HasAssociations:
if association.is_a("IfcRelAssociatesMaterial"):
cls.load_association(association, product_id)
@classmethod
def load_element(cls, ifc_class, to_dict):
try:
elements = cls._file.by_type(ifc_class)
except:
return
for element in elements:
to_dict[element.id()] = cls.get_simple_info(element)
@classmethod
def get_simple_info(cls, element):
info = element.get_info()
for key, value in info.items():
if isinstance(value, ifcopenshell.entity_instance):
info[key] = value.id()
elif isinstance(value, tuple) and value and isinstance(value[0], ifcopenshell.entity_instance):
info[key] = [v.id() for v in value]
return info
@classmethod
def load_association(cls, association, product_id):
material_select = association.RelatingMaterial
if material_select.is_a("IfcMaterialLayerSetUsage"): # TODO: implement usages
material_select = material_select.ForLayerSet
elif material_select.is_a("IfcMaterialProfileSetUsage"): # TODO: implement usages
material_select = material_select.ForProfileSet
cls.products[product_id] = {"type": material_select.is_a(), "id": material_select.id()}
@@ -0,0 +1,13 @@
class Usecase():
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"element": None,
"attributes": {}
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["element"], name, value)
@@ -0,0 +1,15 @@
class Usecase():
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"constituent": None,
"attributes": {},
"material": None
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["constituent"], name, value)
self.settings["constituent"].Material = self.settings["material"]
@@ -0,0 +1,15 @@
class Usecase():
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"layer": None,
"attributes": {},
"material": None
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["layer"], name, value)
self.settings["layer"].Material = self.settings["material"]
@@ -0,0 +1,15 @@
class Usecase():
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"profile": None,
"attributes": {},
"material": None
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["profile"], name, value)
self.settings["profile"].Material = self.settings["material"]
@@ -0,0 +1,377 @@
import bpy
import blenderbim.bim.module.material.assign_material as assign_material
import blenderbim.bim.module.material.unassign_material as unassign_material
import blenderbim.bim.module.material.add_constituent as add_constituent
import blenderbim.bim.module.material.remove_constituent as remove_constituent
import blenderbim.bim.module.material.add_layer as add_layer
import blenderbim.bim.module.material.edit_layer as edit_layer
import blenderbim.bim.module.material.remove_layer as remove_layer
import blenderbim.bim.module.material.add_list_item as add_list_item
import blenderbim.bim.module.material.remove_list_item as remove_list_item
import blenderbim.bim.module.material.edit_assigned_material as edit_assigned_material
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.material.data import Data
class AssignMaterial(bpy.types.Operator):
bl_idname = "bim.assign_material"
bl_label = "Assign Material"
obj: bpy.props.StringProperty()
material_type: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
material_type = self.material_type or obj.BIMObjectMaterialProperties.material_type
self.file = IfcStore.get_file()
assign_material.Usecase(
self.file,
{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"type": material_type,
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
},
).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class UnassignMaterial(bpy.types.Operator):
bl_idname = "bim.unassign_material"
bl_label = "Unassign Material"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
unassign_material.Usecase(
self.file, {"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)}
).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class AddConstituent(bpy.types.Operator):
bl_idname = "bim.add_constituent"
bl_label = "Add Constituent"
obj: bpy.props.StringProperty()
constituent_set: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
add_constituent.Usecase(
self.file,
{
"constituent_set": self.file.by_id(self.constituent_set),
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
},
).execute()
Data.load_constituents()
return {"FINISHED"}
class RemoveConstituent(bpy.types.Operator):
bl_idname = "bim.remove_constituent"
bl_label = "Remove Constituent"
obj: bpy.props.StringProperty()
constituent: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
remove_constituent.Usecase(self.file, {"constituent": self.file.by_id(self.constituent)}).execute()
Data.load_constituents()
return {"FINISHED"}
class AddLayer(bpy.types.Operator):
bl_idname = "bim.add_layer"
bl_label = "Add Layer"
obj: bpy.props.StringProperty()
layer_set: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
add_layer.Usecase(
self.file,
{
"layer_set": self.file.by_id(self.layer_set),
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
},
).execute()
Data.load_layers()
return {"FINISHED"}
class RemoveLayer(bpy.types.Operator):
bl_idname = "bim.remove_layer"
bl_label = "Remove Layer"
obj: bpy.props.StringProperty()
layer: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
remove_layer.Usecase(self.file, {"layer": self.file.by_id(self.layer)}).execute()
Data.load_layers()
return {"FINISHED"}
class AddListItem(bpy.types.Operator):
bl_idname = "bim.add_list_item"
bl_label = "Add List Item"
obj: bpy.props.StringProperty()
list_item_set: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
add_list_item.Usecase(
self.file,
{
"material_list": self.file.by_id(self.list_item_set),
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
},
).execute()
Data.load_lists()
return {"FINISHED"}
class RemoveListItem(bpy.types.Operator):
bl_idname = "bim.remove_list_item"
bl_label = "Remove List Item"
obj: bpy.props.StringProperty()
list_item_set: bpy.props.IntProperty()
list_item: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
remove_list_item.Usecase(
self.file,
{
"material_list": self.file.by_id(self.list_item_set),
"material": self.file.by_id(self.list_item),
},
).execute()
Data.load_lists()
return {"FINISHED"}
class EnableEditingAssignedMaterial(bpy.types.Operator):
bl_idname = "bim.enable_editing_assigned_material"
bl_label = "Enable Editing Assigned Material"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectMaterialProperties
props.is_editing = True
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
if product_data["type"] == "IfcMaterial":
props.material = str(product_data["id"])
return {"FINISHED"}
if product_data["type"] == "IfcMaterialConstituentSet":
material_set_data = Data.constituent_sets[product_data["id"]]
elif product_data["type"] == "IfcMaterialLayerSet":
material_set_data = Data.layer_sets[product_data["id"]]
elif product_data["type"] == "IfcMaterialProfileSet":
material_set_data = Data.profile_sets[product_data["id"]]
elif product_data["type"] == "IfcMaterialList":
material_set_data = Data.lists[product_data["id"]]
else:
material_set_data = {}
while len(props.material_set_attributes) > 0:
props.material_set_attributes.remove(0)
for attribute in IfcStore.get_schema().declaration_by_name(product_data["type"]).all_attributes():
if "<string>" not in str(attribute.type_of_attribute):
continue
if attribute.name() in material_set_data:
new = props.material_set_attributes.add()
new.name = attribute.name()
new.is_null = material_set_data[attribute.name()] is None
new.string_value = "" if new.is_null else material_set_data[attribute.name()]
return {"FINISHED"}
class DisableEditingAssignedMaterial(bpy.types.Operator):
bl_idname = "bim.disable_editing_assigned_material"
bl_label = "Disable Editing Assigned Material"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectMaterialProperties
props.is_editing = False
return {"FINISHED"}
class EditAssignedMaterial(bpy.types.Operator):
bl_idname = "bim.edit_assigned_material"
bl_label = "Edit Assigned Material"
obj: bpy.props.StringProperty()
material_set: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectMaterialProperties
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
material_set_id = self.material_set or product_data["id"]
material_set = self.file.by_id(material_set_id)
if product_data["type"] == "IfcMaterial":
bpy.ops.bim.unassign_material(obj=obj.name)
bpy.ops.bim.assign_material(obj=obj.name, material_type="IfcMaterial")
Data.load(obj.BIMObjectProperties.ifc_definition_id)
bpy.ops.bim.disable_editing_assigned_material(obj=obj.name)
return {"FINISHED"}
attributes = {}
for attribute in props.material_set_attributes:
attributes[attribute.name] = None if attribute.is_null else attribute.string_value
edit_assigned_material.Usecase(
self.file,
{
"element": material_set,
"attributes": attributes,
},
).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
if material_set.is_a("IfcMaterialConstituentSet"):
Data.load_constituents()
elif material_set.is_a("IfcMaterialLayerSet"):
Data.load_layers()
elif material_set.is_a("IfcMaterialProfileSet"):
Data.load_profiles()
bpy.ops.bim.disable_editing_assigned_material(obj=obj.name)
return {"FINISHED"}
class EnableEditingMaterialSetItem(bpy.types.Operator):
bl_idname = "bim.enable_editing_material_set_item"
bl_label = "Enable Editing Material Set Item"
obj: bpy.props.StringProperty()
material_set_item: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectMaterialProperties
props.active_material_set_item_id = self.material_set_item
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
material_set_item = self.file.by_id(self.material_set_item)
if product_data["type"] == "IfcMaterialConstituentSet":
material_set_item_data = Data.constituents[self.material_set_item]
elif product_data["type"] == "IfcMaterialLayerSet":
material_set_item_data = Data.layers[self.material_set_item]
elif product_data["type"] == "IfcMaterialProfileSet":
material_set_item_data = Data.profiles[self.material_set_item]
else:
material_set_item_data = {}
props.material_set_item_material = str(material_set_item_data["Material"])
while len(props.material_set_item_attributes) > 0:
props.material_set_item_attributes.remove(0)
for attribute in IfcStore.get_schema().declaration_by_name(material_set_item.is_a()).all_attributes():
data_type = str(attribute.type_of_attribute)
if "<entity" in data_type:
continue
if attribute.name() in material_set_item_data:
new = props.material_set_item_attributes.add()
new.name = attribute.name()
new.is_null = material_set_item_data[attribute.name()] is None
if "<string>" in data_type:
new.string_value = "" if new.is_null else material_set_item_data[attribute.name()]
new.data_type = "string"
elif "<real>" in data_type:
new.float_value = 0.0 if new.is_null else material_set_item_data[attribute.name()]
new.data_type = "float"
elif "<integer>" in data_type:
new.int_value = 0 if new.is_null else material_set_item_data[attribute.name()]
new.data_type = "integer"
elif "<boolean>" in data_type or "<logical>" in data_type:
new.bool_value = False if new.is_null else material_set_item_data[attribute.name()]
new.data_type = "boolean"
return {"FINISHED"}
class DisableEditingMaterialSetItem(bpy.types.Operator):
bl_idname = "bim.disable_editing_material_set_item"
bl_label = "Disable Editing Material Set Item"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectMaterialProperties
props.active_material_set_item_id = 0
return {"FINISHED"}
class EditMaterialSetItem(bpy.types.Operator):
bl_idname = "bim.edit_material_set_item"
bl_label = "Edit Material Set Item"
obj: bpy.props.StringProperty()
material_set_item: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
props = obj.BIMObjectMaterialProperties
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
attributes = {}
for attribute in props.material_set_item_attributes:
if attribute.data_type == "string":
value = attribute.string_value
elif attribute.data_type == "float":
value = attribute.float_value
elif attribute.data_type == "integer":
value = attribute.int_value
elif attribute.data_type == "boolean":
value = attribute.bool_value
attributes[attribute.name] = None if attribute.is_null else value
if product_data["type"] == "IfcMaterialConstituentSet":
edit_constituent.Usecase(
self.file,
{
"constituent": self.file.by_id(self.material_set_item),
"attributes": attributes,
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
},
).execute()
Data.load_constituents()
elif product_data["type"] == "IfcMaterialLayerSet":
edit_layer.Usecase(
self.file,
{
"layer": self.file.by_id(self.material_set_item),
"attributes": attributes,
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
},
).execute()
Data.load_layers()
elif product_data["type"] == "IfcMaterialProfileSet":
edit_profile.Usecase(
self.file,
{
"profile": self.file.by_id(self.material_set_item),
"attributes": attributes,
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
},
).execute()
Data.load_profiles()
else:
pass
bpy.ops.bim.disable_editing_material_set_item(obj=obj.name)
return {"FINISHED"}
@@ -0,0 +1,54 @@
import bpy
import blenderbim.bim.schema # refactor
from blenderbim.bim.module.material.data import Data
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
materials_enum = []
materialtypes_enum = []
def getMaterials(self, context):
global materials_enum
if len(materials_enum) == 0 and IfcStore.get_file():
materials_enum.clear()
materials_enum = [(str(m_id), m["Name"], "") for m_id, m in Data.materials.items()]
return materials_enum
def getMaterialTypes(self, context):
global materialtypes_enum
if len(materialtypes_enum) == 0 and IfcStore.get_file():
material_types = [
"IfcMaterial",
"IfcMaterialConstituentSet",
"IfcMaterialLayerSet",
"IfcMaterialProfileSet",
"IfcMaterialList",
]
if IfcStore.get_file().schema == "IFC2X3":
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialList"]
materialtypes_enum.clear()
materialtypes_enum = [(m, m, "") for m in material_types]
return materialtypes_enum
class BIMObjectMaterialProperties(PropertyGroup):
material_type: EnumProperty(items=getMaterialTypes, name="Material Type")
material: EnumProperty(items=getMaterials, name="Material")
is_editing: BoolProperty(name="Is Editing", default=False)
material_set_attributes: CollectionProperty(name="Material Set Attributes", type=Attribute)
active_material_set_item_id: IntProperty(name="Active Material Set ID")
material_set_item_attributes: CollectionProperty(name="Material Set Item Attributes", type=Attribute)
material_set_item_material: EnumProperty(items=getMaterials, name="Material")
@@ -0,0 +1,12 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"constituent": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.file.remove(self.settings["constituent"])
@@ -0,0 +1,12 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"layer": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.file.remove(self.settings["layer"])
@@ -0,0 +1,13 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"material_list": None, "material": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
materials = [m for m in self.settings["material_list"].Materials if m != self.settings["material"]]
self.settings["material_list"].Materials = materials
@@ -0,0 +1,175 @@
from bpy.types import Panel
from blenderbim.bim.module.material.data import Data
from blenderbim.bim.ifc import IfcStore
class BIM_PT_object_material(Panel):
bl_label = "IFC Object Material"
bl_idname = "BIM_PT_object_material"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
self.file = IfcStore.get_file()
self.oprops = context.active_object.BIMObjectProperties
self.props = context.active_object.BIMObjectMaterialProperties
if not Data.is_loaded:
Data.load()
if self.oprops.ifc_definition_id not in Data.products:
Data.load(self.oprops.ifc_definition_id)
self.product_data = Data.products[self.oprops.ifc_definition_id]
if self.product_data:
if self.product_data["type"] == "IfcMaterialConstituentSet":
self.material_set_data = Data.constituent_sets[self.product_data["id"]]
self.set_items = self.material_set_data["MaterialConstituents"]
self.set_data = Data.constituents
self.set_item_name = "constituent"
elif self.product_data["type"] == "IfcMaterialLayerSet":
self.material_set_data = Data.layer_sets[self.product_data["id"]]
self.set_items = self.material_set_data["MaterialLayers"]
self.set_data = Data.layers
self.set_item_name = "layer"
elif self.product_data["type"] == "IfcMaterialProfileSet":
self.material_set_data = Data.profile_sets[self.product_data["id"]]
self.set_items = self.material_set_data["MaterialProfiles"]
self.set_data = Data.profiles
self.set_item_name = "profile"
elif self.product_data["type"] == "IfcMaterialList":
self.material_set_data = Data.lists[self.product_data["id"]]
self.set_items = self.material_set_data["Materials"]
self.set_item_name = "list_item"
return self.draw_material_ui()
row = self.layout.row(align=True)
row.prop(self.props, "material_type", text="")
if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList":
row.prop(self.props, "material", text="")
row.operator("bim.assign_material", icon="ADD", text="")
def draw_material_ui(self):
row = self.layout.row(align=True)
row.label(text=self.product_data["type"])
if self.props.is_editing:
row.operator("bim.edit_assigned_material", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_assigned_material", icon="X", text="")
else:
row.operator("bim.enable_editing_assigned_material", icon="GREASEPENCIL", text="")
row.operator("bim.unassign_material", icon="X", text="")
if self.product_data["type"] == "IfcMaterial":
self.draw_single_ui()
else:
self.draw_set_ui()
def draw_single_ui(self):
if self.props.is_editing:
return self.draw_editable_single_ui()
return self.draw_read_only_single_ui()
def draw_editable_single_ui(self):
row = self.layout.row(align=True)
row.prop(self.props, "material", text="")
def draw_read_only_single_ui(self):
material = Data.materials[self.product_data["id"]]
row = self.layout.row(align=True)
row.label(text="Name")
row.label(text=material["Name"])
def draw_set_ui(self):
if self.props.is_editing:
return self.draw_editable_set_ui()
self.draw_read_only_set_ui()
def draw_editable_set_ui(self):
for attribute in self.props.material_set_attributes:
row = self.layout.row(align=True)
row.prop(attribute, "string_value", text=attribute.name)
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
row = self.layout.row(align=True)
row.prop(self.props, "material", text="")
op = row.operator(f"bim.add_{self.set_item_name}", icon="ADD", text="")
setattr(op, f"{self.set_item_name}_set", self.product_data["id"])
total_items = len(self.set_items)
for index, set_item_id in enumerate(self.set_items):
if self.props.active_material_set_item_id == set_item_id:
self.draw_editable_set_item_ui(set_item_id)
else:
self.draw_read_only_set_item_ui(set_item_id, is_first=index == 0, is_last=index == total_items - 1)
def draw_editable_set_item_ui(self, set_item_id):
item = self.set_data[set_item_id]
material = Data.materials[item["Material"]]
box = self.layout.box()
row = box.row(align=True)
row.prop(self.props, "material_set_item_material", icon="MATERIAL")
op = row.operator("bim.edit_material_set_item", icon="CHECKMARK", text="")
op.material_set_item = set_item_id
row.operator("bim.disable_editing_material_set_item", icon="X", text="")
for attribute in self.props.material_set_item_attributes:
row = box.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_read_only_set_item_ui(self, set_item_id, is_first=False, is_last=False):
if self.product_data["type"] == "IfcMaterialList":
item = Data.materials[set_item_id]
row = self.layout.row(align=True)
row.label(text="IfcMaterial", icon="ALIGN_CENTER")
row.label(text=item["Name"], icon="MATERIAL")
else:
item = self.set_data[set_item_id]
row = self.layout.row(align=True)
row.label(text=item.get("Name", "Unnamed"), icon="ALIGN_CENTER")
row.label(text=Data.materials[item["Material"]]["Name"], icon="MATERIAL")
if not is_first:
row.operator("bim.edit_attributes", icon="TRIA_UP", text="")
if not is_last:
row.operator("bim.edit_attributes", icon="TRIA_DOWN", text="")
if not self.props.active_material_set_item_id and self.product_data["type"] != "IfcMaterialList":
op = row.operator("bim.enable_editing_material_set_item", icon="GREASEPENCIL", text="")
op.material_set_item = set_item_id
op = row.operator(f"bim.remove_{self.set_item_name}", icon="X", text="")
if self.product_data["type"] == "IfcMaterialList":
setattr(op, "list_item_set", self.product_data["id"])
setattr(op, self.set_item_name, item["id"])
def draw_read_only_set_ui(self):
name_attr = "LayerSetName" if self.product_data["type"] == "IfcMaterialLayerSet" else "Name"
row = self.layout.row(align=True)
row.label(text=name_attr)
row.label(text=self.material_set_data.get(name_attr, "Unnamed"))
if hasattr(self.material_set_data, "Description") and self.material_set_data["Description"]:
row = self.layout.row(align=True)
row.label(text="Description")
row.label(text=str(self.material_set_data["Description"]))
for item_id in self.set_items:
if self.product_data["type"] == "IfcMaterialList":
row = self.layout.row(align=True)
row.label(text="IfcMaterial", icon="ALIGN_CENTER")
row.label(text=Data.materials[item_id]["Name"], icon="MATERIAL")
else:
item = self.set_data[item_id]
row = self.layout.row(align=True)
row.label(text=item.get("Name", "Unnamed"), icon="ALIGN_CENTER")
row.label(text=Data.materials[item["Material"]]["Name"], icon="MATERIAL")
@@ -0,0 +1,14 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"product": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for association in self.settings["product"].HasAssociations:
if association.is_a("IfcRelAssociatesMaterial"):
self.file.remove(association)
+1 -91
View File
@@ -40,7 +40,6 @@ propertysettemplates_enum = []
classification_enum = []
attributes_enum = []
materialattributes_enum = []
materialtypes_enum = []
contexts_enum = []
subcontexts_enum = []
target_views_enum = []
@@ -285,13 +284,6 @@ def getAttributeEnumValues(self, context):
return [(e, e, "") for e in json.loads(self.enum_items)]
def getProfileDef(self, context):
global profiledef_enum
if len(profiledef_enum) < 1:
profiledef_enum.extend([(e, e, "") for e in getattr(schema.ifc, "IfcParameterizedProfileDef")])
return profiledef_enum
def getPersons(self, context):
from blenderbim.bim.module.owner.data import Data
if not Data.is_loaded:
@@ -424,27 +416,6 @@ def getApplicableMaterialAttributes(self, context):
return materialattributes_enum
def refreshProfileAttributes(self, context):
if not context.active_object:
return
props = context.active_object.BIMObjectProperties
profile = props.material_set.material_profiles[props.material_set.active_material_profile_index]
while len(profile.profile_attributes) > 0:
profile.profile_attributes.remove(0)
for attribute in schema.ifc.IfcParameterizedProfileDef[profile.profile]["attributes"]:
profile_attribute = profile.profile_attributes.add()
profile_attribute.name = attribute["name"]
def getMaterialTypes(self, context):
global materialtypes_enum
materialtypes_enum.clear()
materialtypes_enum = [
(m, m, "") for m in ["None", "IfcMaterial", "IfcMaterialConstituentSet", "IfcMaterialLayerSet", "IfcMaterialProfileSet"]
]
return materialtypes_enum
def getContexts(self, context):
from blenderbim.bim.module.context.data import Data
if not Data.is_loaded:
@@ -511,64 +482,6 @@ class Attribute(PropertyGroup):
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value")
class MaterialLayer(PropertyGroup):
name: StringProperty(name="Name")
material: PointerProperty(name="Material", type=bpy.types.Material)
layer_thickness: FloatProperty(name="Layer Thickness")
is_ventilated: EnumProperty(
items=[
("TRUE", "True", "Is an air gap and provides air exchange from the layer to outside air"),
("FALSE", "False", "Is a solid material layer"),
("UNKNOWN", "Unknown", "Is an air gap but does not provide air exchange or not known"),
],
name="Is Ventilated",
default="FALSE",
)
description: StringProperty(name="Description")
category: EnumProperty(
items=[
("None", "None", ""),
("LoadBearing", "Load Bearing", ""),
("Insulation", "Insulation", ""),
("Finish", "Finish", ""),
("Custom", "Custom", ""),
],
name="Category",
default="None",
)
custom_category: StringProperty(name="Custom Category")
priority: IntProperty(name="Priority")
class MaterialConstituent(PropertyGroup):
name: StringProperty(name="Name")
description: StringProperty(name="Description")
material: PointerProperty(name="Material", type=bpy.types.Material)
fraction: FloatProperty(name="Fraction")
category: StringProperty(name="Category")
class MaterialProfile(PropertyGroup):
name: StringProperty(name="Name")
description: StringProperty(name="Description")
material: PointerProperty(name="Material", type=bpy.types.Material)
profile: EnumProperty(items=getProfileDef, name="Parameterized Profile Def", update=refreshProfileAttributes)
profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute)
priority: IntProperty(name="Priority")
category: StringProperty(name="Category")
class MaterialSet(PropertyGroup):
name: StringProperty(name="Name")
description: StringProperty(name="Description")
active_material_layer_index: IntProperty(name="Active Material Layer Index")
material_layers: CollectionProperty(name="Material Layers", type=MaterialLayer)
active_material_constituent_index: IntProperty(name="Active Material Constituent Index")
material_constituents: CollectionProperty(name="Material Constituents", type=MaterialConstituent)
active_material_profile_index: IntProperty(name="Active Material Profile Index")
material_profiles: CollectionProperty(name="Material Profiles", type=MaterialProfile)
class Drawing(PropertyGroup):
name: StringProperty(name="Name", update=updateDrawingName)
camera: PointerProperty(name="Camera", type=bpy.types.Object)
@@ -1227,7 +1140,7 @@ class BIMProperties(PropertyGroup):
import_should_allow_non_element_aggregates: BoolProperty(name="Import Non-Element Aggregates", default=False)
import_should_offset_model: BoolProperty(name="Import and Offset Model", default=False)
import_model_offset_coordinates: StringProperty(name="Model Offset Coordinates", default="0,0,0")
person: PointerProperty(type=Person)
active_person_id: IntProperty(name="Active Person Id")
organisation: PointerProperty(type=Organisation)
@@ -1426,9 +1339,6 @@ class BIMObjectProperties(PropertyGroup):
constraints: CollectionProperty(name="Constraints", type=Constraint)
active_constraint_index: IntProperty(name="Active Constraint Index")
classifications: CollectionProperty(name="Classifications", type=ClassificationReference)
material_type: EnumProperty(items=getMaterialTypes, name="Material Type")
material: PointerProperty(name="Material", type=bpy.types.Material)
material_set: PointerProperty(name="Material Set", type=MaterialSet)
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
-138
View File
@@ -5,144 +5,6 @@ from bpy.types import Panel
from bpy.props import StringProperty
class BIM_PT_object_material(Panel):
bl_label = "IFC Object Material"
bl_idname = "BIM_PT_object_material"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
def draw(self, context):
if context.active_object is None:
return
layout = self.layout
props = context.active_object.BIMObjectProperties
row = layout.row()
row.prop(props, "material_type")
if props.material_type == "None" and props.relating_type:
props = props.relating_type.BIMObjectProperties
if props.material_type == "None":
pass
elif props.material_type == "IfcMaterial" and props.material:
layout.label(text="Inherited Material:")
elif props.material_set:
layout.label(text="Inherited Material Set:")
if props.material_type == "None":
pass
elif props.material_type == "IfcMaterial":
row = layout.row()
row.prop(props, "material")
else:
set_props = props.material_set
row = layout.row()
row.prop(set_props, "name", text="Name")
row = layout.row()
row.prop(set_props, "description", text="Description")
row = layout.row()
if props.material_type == "IfcMaterialLayerSet":
row.template_list(
"MATERIAL_UL_matslots", "", set_props, "material_layers", set_props, "active_material_layer_index"
)
col = row.column(align=True)
col.operator("bim.add_material_layer", icon="ADD", text="")
col.operator(
"bim.remove_material_layer", icon="REMOVE", text=""
).index = set_props.active_material_layer_index
col.operator("bim.move_material_layer", icon="TRIA_UP", text="").direction = "UP"
col.operator("bim.move_material_layer", icon="TRIA_DOWN", text="").direction = "DOWN"
if set_props.active_material_layer_index < len(set_props.material_layers):
material = set_props.material_layers[set_props.active_material_layer_index]
row = layout.row()
row.prop(material, "material")
row = layout.row()
row.prop(material, "name")
row = layout.row()
row.prop(material, "description")
row = layout.row()
row.prop(material, "category")
if material.category == "Custom":
row = layout.row()
row.prop(material, "custom_category")
row = layout.row()
row.prop(material, "layer_thickness")
row = layout.row()
row.prop(material, "is_ventilated")
row = layout.row()
row.prop(material, "priority")
elif props.material_type == "IfcMaterialConstituentSet":
row.template_list(
"MATERIAL_UL_matslots",
"",
set_props,
"material_constituents",
set_props,
"active_material_constituent_index",
)
col = row.column(align=True)
col.operator("bim.add_material_constituent", icon="ADD", text="")
col.operator(
"bim.remove_material_constituent", icon="REMOVE", text=""
).index = set_props.active_material_constituent_index
col.operator("bim.move_material_constituent", icon="TRIA_UP", text="").direction = "UP"
col.operator("bim.move_material_constituent", icon="TRIA_DOWN", text="").direction = "DOWN"
if set_props.active_material_constituent_index < len(set_props.material_constituents):
material = set_props.material_constituents[set_props.active_material_constituent_index]
row = layout.row()
row.prop(material, "material")
row = layout.row()
row.prop(material, "name")
row = layout.row()
row.prop(material, "description")
row = layout.row()
row.prop(material, "fraction")
row = layout.row()
row.prop(material, "category")
elif props.material_type == "IfcMaterialProfileSet":
row.template_list(
"MATERIAL_UL_matslots",
"",
set_props,
"material_profiles",
set_props,
"active_material_profile_index",
)
col = row.column(align=True)
col.operator("bim.add_material_profile", icon="ADD", text="")
col.operator(
"bim.remove_material_profile", icon="REMOVE", text=""
).index = set_props.active_material_profile_index
col.operator("bim.move_material_profile", icon="TRIA_UP", text="").direction = "UP"
col.operator("bim.move_material_profile", icon="TRIA_DOWN", text="").direction = "DOWN"
if set_props.active_material_profile_index < len(set_props.material_profiles):
material = set_props.material_profiles[set_props.active_material_profile_index]
row = layout.row()
row.prop(material, "material")
row = layout.row()
row.prop(material, "name")
row = layout.row()
row.prop(material, "description")
row = layout.row()
row.prop(material, "priority")
row = layout.row()
row.prop(material, "category")
row = layout.row()
row.prop(material, "profile")
for index, attribute in enumerate(material.profile_attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
class BIM_PT_object_structural(Panel):
bl_label = "IFC Structural Relationships"
bl_idname = "BIM_PT_object_structural"