From 2dbcd6712e1872057ee7b172655aac84572b7286 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 14 Jan 2020 15:38:22 +1100 Subject: [PATCH] Create UI to add/remove material attributes --- src/ifcblenderexport/blenderbim/__init__.py | 2 + src/ifcblenderexport/blenderbim/export_ifc.py | 53 ++- src/ifcblenderexport/blenderbim/operator.py | 23 ++ src/ifcblenderexport/blenderbim/prop.py | 19 +- src/ifcblenderexport/blenderbim/schema.py | 3 +- .../schema/IfcMaterialDefinition_IFC4.json | 366 ++++++++++++++++++ src/ifcblenderexport/blenderbim/ui.py | 14 + src/ifcblenderexport/getIfcElements.py | 6 +- 8 files changed, 451 insertions(+), 35 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/schema/IfcMaterialDefinition_IFC4.json diff --git a/src/ifcblenderexport/blenderbim/__init__.py b/src/ifcblenderexport/blenderbim/__init__.py index 9416a8e5db..e04338ca5b 100644 --- a/src/ifcblenderexport/blenderbim/__init__.py +++ b/src/ifcblenderexport/blenderbim/__init__.py @@ -68,6 +68,8 @@ if bpy is not None: operator.GenerateGlobalId, operator.AddAttribute, operator.RemoveAttribute, + operator.AddMaterialAttribute, + operator.RemoveMaterialAttribute, operator.QuickProjectSetup, operator.SelectGlobalId, operator.CreateAggregate, diff --git a/src/ifcblenderexport/blenderbim/export_ifc.py b/src/ifcblenderexport/blenderbim/export_ifc.py index 6bda64fd5e..f76f158611 100644 --- a/src/ifcblenderexport/blenderbim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/export_ifc.py @@ -772,26 +772,18 @@ class IfcParser(): continue results[slot.material.name] = { 'ifc': None, - 'layer_ifc': None, - 'constituent_ifc': None, + 'part_ifc': None, 'raw': slot.material, 'material_type': obj.BIMObjectProperties.material_type, - 'attributes': {'Name': slot.material.name}, - # TODO: turn into non-custom attributes - 'layer_attributes': { - key[3:]: slot.material[key] - for key in slot.material.keys() - if key[0:3] == 'Ifc' - }, - # TODO: turn into non-custom attributes - 'constituent_attributes': { - key[3:]: slot.material[key] - for key in slot.material.keys() - if key[0:3] == 'Ifc' - } + 'attributes': self.get_material_attributes(slot.material) } return results + def get_material_attributes(self, material): + attributes = {'Name': material.name} + attributes.update({a.name: a.string_value for a in material.BIMMaterialProperties.attributes}) + return attributes + def get_styled_items(self): results = [] if not self.ifc_export_settings.has_representations: @@ -1451,14 +1443,19 @@ class IfcExporter(): self.create_material_psets(material) self.file.createIfcMaterialDefinitionRepresentation( material['raw'].name, None, [styled_representation], material['ifc']) - if material['material_type'] == 'IfcMaterialLayerSet': - material['layer_attributes']['Material'] = material['ifc'] - material['layer_ifc'] = self.file.create_entity('IfcMaterialLayer', - **material['layer_attributes']) - elif material['material_type'] == 'IfcMaterialConstituentSet': - material['constituent_attributes']['Material'] = material['ifc'] - material['constituent_ifc'] = self.file.create_entity('IfcMaterialConstituent', - **material['constituent_attributes']) + if material['material_type'] != 'IfcMaterial': + material_type = material['material_type'][0:-3] + self.cast_attributes(material_type, material['attributes']) + material['attributes']['Material'] = material['ifc'] + material['part_ifc'] = self.file.create_entity(material_type, + **material['attributes']) + + def cast_attributes(self, ifc_class, attributes): + for key, value in attributes.items(): + var_type = self.get_product_attribute_type(ifc_class, key) + if var_type is None: + continue + attributes[key] = self.cast_to_base_type(var_type, value) def create_surface_style_rendering(self, styled_item): surface_colour = self.create_colour_rgb(styled_item['raw'].diffuse_color) @@ -1527,11 +1524,7 @@ class IfcExporter(): 'Representation': self.get_product_shape(product) }) - for key, value in product['attributes'].items(): - var_type = self.get_product_attribute_type(product['class'], key) - if var_type is None: - continue - product['attributes'][key] = self.cast_to_base_type(var_type, value) + self.cast_attributes(product['class'], product['attributes']) try: product['ifc'] = self.file.create_entity(product['class'], **product['attributes']) @@ -2070,7 +2063,7 @@ class IfcExporter(): return for product_index, related_materials in self.ifc_parser.rel_associates_material_layer_set.items(): material_layer_set = self.file.create_entity('IfcMaterialLayerSet', **{ - 'MaterialLayers': [self.ifc_parser.materials[m]['layer_ifc'] for m in related_materials] + 'MaterialLayers': [self.ifc_parser.materials[m]['part_ifc'] for m in related_materials] }) self.file.createIfcRelAssociatesMaterial( ifcopenshell.guid.new(), self.owner_history, None, None, @@ -2082,7 +2075,7 @@ class IfcExporter(): return for product_index, related_materials in self.ifc_parser.rel_associates_material_constituent_set.items(): material_constituent_set = self.file.create_entity('IfcMaterialConstituentSet', **{ - 'MaterialConstituents': [self.ifc_parser.materials[m]['constituent_ifc'] for m in related_materials] + 'MaterialConstituents': [self.ifc_parser.materials[m]['part_ifc'] for m in related_materials] }) self.file.createIfcRelAssociatesMaterial( ifcopenshell.guid.new(), self.owner_history, None, None, diff --git a/src/ifcblenderexport/blenderbim/operator.py b/src/ifcblenderexport/blenderbim/operator.py index 68b96c181c..07f98e9117 100644 --- a/src/ifcblenderexport/blenderbim/operator.py +++ b/src/ifcblenderexport/blenderbim/operator.py @@ -386,6 +386,18 @@ class AddAttribute(bpy.types.Operator): attribute.name = bpy.context.active_object.BIMObjectProperties.applicable_attributes return {'FINISHED'} + +class AddMaterialAttribute(bpy.types.Operator): + bl_idname = 'bim.add_material_attribute' + bl_label = 'Add Material Attribute' + + def execute(self, context): + if bpy.context.active_object.active_material.BIMMaterialProperties.applicable_attributes: + attribute = bpy.context.active_object.active_material.BIMMaterialProperties.attributes.add() + attribute.name = bpy.context.active_object.active_material.BIMMaterialProperties.applicable_attributes + return {'FINISHED'} + + class RemoveAttribute(bpy.types.Operator): bl_idname = 'bim.remove_attribute' bl_label = 'Remove Attribute' @@ -395,6 +407,17 @@ class RemoveAttribute(bpy.types.Operator): bpy.context.active_object.BIMObjectProperties.attributes.remove(self.attribute_index) return {'FINISHED'} + +class RemoveMaterialAttribute(bpy.types.Operator): + bl_idname = 'bim.remove_material_attribute' + bl_label = 'Remove Material Attribute' + attribute_index: bpy.props.IntProperty() + + def execute(self, context): + bpy.context.active_object.active_material.BIMMaterialProperties.attributes.remove(self.attribute_index) + return {'FINISHED'} + + class AddSweptSolid(bpy.types.Operator): bl_idname = 'bim.add_swept_solid' bl_label = 'Add Swept Solid' diff --git a/src/ifcblenderexport/blenderbim/prop.py b/src/ifcblenderexport/blenderbim/prop.py index ed166d26c7..8f1a9a29cb 100644 --- a/src/ifcblenderexport/blenderbim/prop.py +++ b/src/ifcblenderexport/blenderbim/prop.py @@ -30,6 +30,7 @@ psetfiles_enum = [] classification_enum = [] reference_enum = [] attributes_enum = [] +materialattributes_enum = [] documents_enum = [] materialtypes_enum = [] contexts_enum = [] @@ -197,13 +198,27 @@ def getApplicableAttributes(self, context): global attributes_enum attributes_enum.clear() if '/' in context.active_object.name \ - and context.active_object.name.split('/')[0] in ifc_schema.elements: + and context.active_object.name.split('/')[0] in ifc_schema.elements: attributes_enum.extend([(a['name'], a['name'], '') for a in ifc_schema.elements[context.active_object.name.split('/')[0]]['attributes'] if self.attributes.find(a['name']) == -1]) return attributes_enum +def getApplicableMaterialAttributes(self, context): + global materialattributes_enum + materialattributes_enum.clear() + if '/' in context.active_object.name \ + and context.active_object.name.split('/')[0] in ifc_schema.elements: + material_type = context.active_object.BIMObjectProperties.material_type + if material_type[-3:] == 'Set': + material_type = material_type[0:-3] + materialattributes_enum.extend([(a['name'], a['name'], '') for a in + ifc_schema.IfcMaterialDefinition[material_type]['attributes'] + if self.attributes.find(a['name']) == -1]) + return materialattributes_enum + + def getApplicableDocuments(self, context): global documents_enum documents_enum.clear() @@ -384,6 +399,8 @@ class BIMMaterialProperties(PropertyGroup): name: StringProperty(name="Name") available_material_psets: EnumProperty(items=getAvailableMaterialPsets, name="Material Pset") psets: CollectionProperty(name="Psets", type=Pset) + attributes: CollectionProperty(name="Attributes", type=Attribute) + applicable_attributes: EnumProperty(items=getApplicableMaterialAttributes, name="Attribute Names") class SweptSolid(PropertyGroup): diff --git a/src/ifcblenderexport/blenderbim/schema.py b/src/ifcblenderexport/blenderbim/schema.py index d29a084c2f..e148798514 100644 --- a/src/ifcblenderexport/blenderbim/schema.py +++ b/src/ifcblenderexport/blenderbim/schema.py @@ -7,9 +7,8 @@ cwd = os.path.dirname(os.path.realpath(__file__)) class IfcSchema(): def __init__(self): self.schema_dir = os.path.join(cwd, 'schema') # TODO: make configurable - self.products = ['IfcElement', 'IfcSpatialStructureElement', 'IfcStructural'] + self.products = ['IfcElement', 'IfcSpatialStructureElement', 'IfcStructural', 'IfcMaterialDefinition'] self.elements = {} - print(os.path.join(self.schema_dir, 'IFC4_ADD2.ifc')) self.property_file = ifcopenshell.open(os.path.join(self.schema_dir, 'IFC4_ADD2.ifc')) self.psets = {} self.qtos = {} diff --git a/src/ifcblenderexport/blenderbim/schema/IfcMaterialDefinition_IFC4.json b/src/ifcblenderexport/blenderbim/schema/IfcMaterialDefinition_IFC4.json new file mode 100644 index 0000000000..abda32338a --- /dev/null +++ b/src/ifcblenderexport/blenderbim/schema/IfcMaterialDefinition_IFC4.json @@ -0,0 +1,366 @@ +{ + "IfcMaterial": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Category", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterialDefinitionRepresentation", + "name": "HasRepresentation" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialConstituent": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcNormalisedRatioMeasure", + "name": "Fraction", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Category", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterial", + "name": "Material" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialConstituentSet": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterialConstituent", + "name": "MaterialConstituents" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialLayer": { + "attributes": [ + { + "enum_values": [], + "type": "IfcNonNegativeLengthMeasure", + "name": "LayerThickness", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLogical", + "name": "IsVentilated", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Category", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcInteger", + "name": "Priority", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterial", + "name": "Material" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialLayerSet": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "LayerSetName", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterialLayer", + "name": "MaterialLayers" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialLayerWithOffsets": { + "attributes": [ + { + "enum_values": [], + "type": "IfcNonNegativeLengthMeasure", + "name": "LayerThickness", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLogical", + "name": "IsVentilated", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Category", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcInteger", + "name": "Priority", + "is_enum": false + }, + { + "enum_values": [ + "AXIS1", + "AXIS2", + "AXIS3" + ], + "type": "IfcLayerSetDirectionEnum", + "name": "OffsetDirection", + "is_enum": true + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterial", + "name": "Material" + } + ], + "parent": "IfcMaterialLayer" + }, + "IfcMaterialProfile": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcInteger", + "name": "Priority", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Category", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterial", + "name": "Material" + }, + { + "type": "IfcProfileDef", + "name": "Profile" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialProfileSet": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterialProfile", + "name": "MaterialProfiles" + }, + { + "type": "IfcCompositeProfileDef", + "name": "CompositeProfile" + } + ], + "parent": "IfcMaterialDefinition" + }, + "IfcMaterialProfileWithOffsets": { + "attributes": [ + { + "enum_values": [], + "type": "IfcLabel", + "name": "Name", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcText", + "name": "Description", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcInteger", + "name": "Priority", + "is_enum": false + }, + { + "enum_values": [], + "type": "IfcLabel", + "name": "Category", + "is_enum": false + } + ], + "is_abstract": false, + "complex_attributes": [ + { + "type": "IfcMaterialProperties", + "name": "HasProperties" + }, + { + "type": "IfcMaterial", + "name": "Material" + }, + { + "type": "IfcProfileDef", + "name": "Profile" + } + ], + "parent": "IfcMaterialProfile" + } +} \ No newline at end of file diff --git a/src/ifcblenderexport/blenderbim/ui.py b/src/ifcblenderexport/blenderbim/ui.py index 2db12f7eec..487dc5587e 100644 --- a/src/ifcblenderexport/blenderbim/ui.py +++ b/src/ifcblenderexport/blenderbim/ui.py @@ -164,6 +164,20 @@ class BIM_PT_material(Panel): row = layout.row() row.operator('bim.fetch_external_material') + layout.label(text="Attributes:") + row = layout.row(align=True) + row.prop(props, 'applicable_attributes', text='') + row.operator('bim.add_material_attribute') + + for index, attribute in enumerate(props.attributes): + row = layout.row(align=True) + row.prop(attribute, 'name', text='') + row.prop(attribute, 'string_value', text='') + row.operator('bim.remove_material_attribute', icon='X', text='').attribute_index = index + + row = layout.row() + row.prop(props, 'attributes') + layout.label(text="Property Sets:") row = layout.row(align=True) row.prop(props, 'available_material_psets', text='') diff --git a/src/ifcblenderexport/getIfcElements.py b/src/ifcblenderexport/getIfcElements.py index 0b5528b63c..1b6018ebee 100644 --- a/src/ifcblenderexport/getIfcElements.py +++ b/src/ifcblenderexport/getIfcElements.py @@ -14,12 +14,14 @@ class IFC4Extractor: #self.filters = ['IfcBuildingElement'] #self.filters = ['IfcElement'] #self.filters = ['IfcSpatialStructureElement'] - self.filters = ['IfcStructuralActivity', 'IfcStructuralItem'] + #self.filters = ['IfcStructuralActivity', 'IfcStructuralItem'] + self.filters = ['IfcMaterialDefinition'] self.filtered_elements = {} def extract(self): for element in self.root.findall("xs:element", self.ns): - if self.is_descendant_from_class(element, "IfcRoot"): + #if self.is_descendant_from_class(element, "IfcRoot"): + if self.is_descendant_from_class(element, "IfcMaterialDefinition"): print('Processing {}'.format(element.attrib['name'])) data = { 'is_abstract': self.is_abstract(element),