From ae9fce8c456d76ff8add917932d2a1dfff23c046 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 31 Aug 2019 23:51:53 +1000 Subject: [PATCH] Support dynamic contexts and trees of spatial structures --- export.py | 196 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 45 deletions(-) diff --git a/export.py b/export.py index 35788ce7fc..b223b1bad2 100644 --- a/export.py +++ b/export.py @@ -1,19 +1,110 @@ import ifcopenshell import bpy -class IfcExporter(): + +class IfcParser(): def __init__(self): + self.spatial_structure_elements = [] + self.spatial_structure_elements_tree = [] + self.rel_contained_in_spatial_structure = {} + self.context = {} + self.products = [] + + def parse(self): + self.spatial_structure_elements = self.get_spatial_structure_elements() + + collection_name_filter = [] + product_index = 0 + for object in bpy.context.selected_objects: + product_data = { + 'ifc': None, + 'raw': object, + 'relating_structure': None + } + for collection in object.users_collection: + if self.is_a_spatial_structure_element(self.get_ifc_class(collection.name)): + reference = self.get_spatial_structure_element_reference(collection.name) + self.rel_contained_in_spatial_structure.setdefault(reference, []).append(product_index) + product_data['relating_structure'] = reference + collection_name_filter.append(collection.name) + self.products.append(product_data) + product_index += 1 + + self.context = self.get_context() + self.spatial_structure_elements_tree = self.get_spatial_structure_elements_tree( + self.context['raw'].children, collection_name_filter) + + def get_context(self): + for collection in bpy.data.collections: + if self.is_a_context(self.get_ifc_class(collection.name)): + return { + 'ifc': None, + 'class': self.get_ifc_class(collection.name), + 'raw': collection, + 'attributes': { 'Name': self.get_ifc_name(collection.name) } + } + + def get_spatial_structure_elements(self): + elements = [] + for collection in bpy.data.collections: + if self.is_a_spatial_structure_element(self.get_ifc_class(collection.name)): + elements.append({ + 'ifc': None, + 'class': self.get_ifc_class(collection.name), + 'raw': collection, + 'attributes': { 'Name': self.get_ifc_name(collection.name)} + }) + return elements + + def get_spatial_structure_elements_tree(self, collections, name_filter): + collection_tree = [] + + for collection in collections: + if not self.is_a_spatial_structure_element(self.get_ifc_class(collection.name)): + continue + children = self.get_spatial_structure_elements_tree( + collection.children, name_filter) + if collection.name in name_filter \ + or children: + collection_tree.append({ + 'reference': self.get_spatial_structure_element_reference(collection.name), + 'children': children + }) + + return collection_tree + + def get_spatial_structure_element_reference(self, name): + return [ e['attributes']['Name'] for e in self.spatial_structure_elements ].index(self.get_ifc_name(name)) + + def get_ifc_class(self, name): + return name.split('/')[0] + + def get_ifc_name(self, name): + return name.split('/')[1] + + def is_a_spatial_structure_element(self, class_name): + return class_name in ['IfcBuilding', 'IfcBuildingStorey', 'IfcSite'] # TODO: unhardcode + + def is_a_context(self, class_name): + return class_name in ['IfcProject', 'IfcProjectLibrary'] # TODO: unhardcode + +class IfcExporter(): + def __init__(self, ifc_parser, ifc_schema): self.template_file = '/home/dion/Projects/blender-bim-ifc/template.ifc' self.output_file = '/home/dion/Projects/blender-bim-ifc/output.ifc' + self.data_dir = '/home/dion/Projects/blender-bim-ifc/data/' + self.ifc_parser = ifc_parser + self.ifc_schema = ifc_schema def export(self): self.file = ifcopenshell.open(self.template_file) self.set_common_definitions() + self.parse_objects_into_ifc_data() + self.create_rep_context() self.create_context() - self.create_project() - self.create_building() - self.create_building_storey() + self.create_spatial_structure_elements(self.ifc_parser.spatial_structure_elements_tree) self.create_products() + self.relate_elements_to_spatial_structures() self.file.write(self.output_file) def set_common_definitions(self): @@ -21,51 +112,56 @@ class IfcExporter(): # Owner history doesn't actually work like this, but for now, it does :) self.owner_history = self.file.by_type("ifcownerhistory")[0] - def create_context(self): - self.ifc_context = self.file.createIfcGeometricRepresentationContext( + def parse_objects_into_ifc_data(self): + self.ifc_parser.parse() + + def create_rep_context(self): + self.ifc_rep_context = self.file.createIfcGeometricRepresentationContext( None, "Model", 3, 1.0E-05, self.origin, self.file.createIfcDirection((0., 1., 0.))) - self.ifc_subcontext = self.file.createIfcGeometricRepresentationSubContext( + self.ifc_rep_subcontext = self.file.createIfcGeometricRepresentationSubContext( "Body", "Model", None, None, None, None, - self.ifc_context, None, "MODEL_VIEW", None) + self.ifc_rep_context, None, "MODEL_VIEW", None) - def create_project(self): - self.project = self.file.createIfcProject( - ifcopenshell.guid.new(), - self.owner_history, - 'Name', None, - None, None, None, [self.ifc_context], - self.file.by_type("IfcUnitAssignment")[0]) + def create_context(self): + context = self.ifc_parser.context + attributes = context['attributes'] + attributes.update({ + 'GlobalId': ifcopenshell.guid.new(), + 'RepresentationContexts': [self.ifc_rep_context], + 'UnitsInContext': self.file.by_type("IfcUnitAssignment")[0] + }) + self.ifc_parser.context['ifc'] = self.file.create_entity(self.ifc_parser.context['class'], **attributes) - def create_building(self): - self.ifc_building_placement = self.file.createIfcLocalPlacement(None, self.origin) - self.ifc_building = self.file.createIfcBuilding( - ifcopenshell.guid.new(), - self.owner_history, 'My building', None, None, - self.ifc_building_placement, - None, None, 'ELEMENT', None, None, None) - self.file.createIfcRelAggregates( - ifcopenshell.guid.new(), - self.owner_history, None, None, self.project, [self.ifc_building]) - - def create_building_storey(self): - self.ifc_building_storey_placement = self.file.createIfcLocalPlacement(self.ifc_building_placement, self.origin) - self.ifc_building_storey = self.file.createIfcBuildingStorey( - ifcopenshell.guid.new(), - self.owner_history, 'Ground floor', None, None, - self.ifc_building_storey_placement, None, None, 'ELEMENT', None) - self.file.createIfcRelAggregates( - ifcopenshell.guid.new(), - self.owner_history, None, None, self.ifc_building, [self.ifc_building_storey]) + def create_spatial_structure_elements(self, element_tree, relating_object=None): + if relating_object == None: + relating_object = self.ifc_parser.context['ifc'] + placement_rel_to = None + else: + placement_rel_to = relating_object.ObjectPlacement + related_objects = [] + for node in element_tree: + element = self.ifc_parser.spatial_structure_elements[node['reference']] + element['attributes'].update({ + 'GlobalId': ifcopenshell.guid.new(), # TODO: unhardcode + 'OwnerHistory': self.owner_history, # TODO: unhardcode + 'ObjectPlacement': self.file.createIfcLocalPlacement(placement_rel_to, self.origin) + }) + element['ifc'] = self.file.create_entity(element['class'], **element['attributes']) + related_objects.append(element['ifc']) + self.create_spatial_structure_elements(node['children'], element['ifc']) + if related_objects: + self.file.createIfcRelAggregates( + ifcopenshell.guid.new(), + self.owner_history, None, None, relating_object, related_objects) def create_products(self): - ifc_products = [] - - for object in bpy.context.selected_objects: + for product in self.ifc_parser.products: + object = product['raw'] ifc_vertices = [] ifc_faces = [] for vertice in object.data.vertices: @@ -79,20 +175,30 @@ class IfcExporter(): representation = self.file.createIfcProductDefinitionShape(None, None, [self.file.createIfcShapeRepresentation( - self.ifc_subcontext, 'Body', 'Brep', + self.ifc_rep_subcontext, 'Body', 'Brep', [self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(ifc_faces))])]) - placement = self.file.createIfcLocalPlacement(self.ifc_building_storey_placement, + # placement = self.file.createIfcLocalPlacement(self.ifc_building_storey_placement, + placement_rel_to = self.ifc_parser.spatial_structure_elements[product['relating_structure']]['ifc'].ObjectPlacement + placement = self.file.createIfcLocalPlacement(placement_rel_to, self.file.createIfcAxis2Placement3D( self.file.createIfcCartesianPoint( (object.location.x, object.location.y, object.location.z)))) - ifc_products.append(self.file.createIfcBuildingElementProxy( + product['ifc'] = self.file.createIfcBuildingElementProxy( ifcopenshell.guid.new(), - self.owner_history, object.name, None, None, placement, representation, None, None)) + self.owner_history, object.name, None, None, placement, representation, None, None) - self.file.createIfcRelContainedInSpatialStructure( - ifcopenshell.guid.new(), self.owner_history, None, None, ifc_products, self.ifc_building_storey) + def relate_elements_to_spatial_structures(self): + for relating_structure, related_elements in self.ifc_parser.rel_contained_in_spatial_structure.items(): + self.file.createIfcRelContainedInSpatialStructure( + ifcopenshell.guid.new(), self.owner_history, None, None, + [ self.ifc_parser.products[e]['ifc'] for e in related_elements], + self.ifc_parser.spatial_structure_elements[relating_structure]['ifc']) -ifc_exporter = IfcExporter() + + +ifc_parser = IfcParser() +ifc_schema = IfcSchema() +ifc_exporter = IfcExporter(ifc_parser, ifc_schema) ifc_exporter.export()