From b4f4d9b3c5f495943d26462cf7b052b57e53b608 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 28 Sep 2020 22:14:03 +1000 Subject: [PATCH] Improve prototype of native round-tripping by reading from original file and preserving styles --- .../blenderbim/bim/export_ifc.py | 64 +++++++++++-------- src/ifcblenderexport/blenderbim/bim/helper.py | 13 ++++ .../blenderbim/bim/import_ifc.py | 8 +-- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/ifcblenderexport/blenderbim/bim/export_ifc.py b/src/ifcblenderexport/blenderbim/bim/export_ifc.py index 5ab9cba773..94e94d8d87 100644 --- a/src/ifcblenderexport/blenderbim/bim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/export_ifc.py @@ -8,8 +8,9 @@ import zipfile import tempfile from pathlib import Path from mathutils import Vector, Matrix -from .helper import SIUnitHelper +from .helper import SIUnitHelper, get_representation_elements from . import schema +from . import ifc import ifcopenshell import addon_utils @@ -1027,7 +1028,7 @@ class IfcParser(): obj.data, obj, 'Model', 'Body', 'MODEL_VIEW') if 'Model/Box/MODEL_VIEW' in self.generated_subcontexts: if self.ifc_export_settings.should_roundtrip_native \ - and obj.data.BIMMeshProperties.ifc_definition: + and obj.data.BIMMeshProperties.ifc_definition_id: return self.representations['Model/Box/MODEL_VIEW/{}'.format(obj.data.name)] = self.get_representation( obj.data, obj, 'Model', 'Box', 'MODEL_VIEW') @@ -1068,7 +1069,7 @@ class IfcParser(): if 'Model/Box/MODEL_VIEW' in self.generated_subcontexts \ and context_prefix == 'Model/Body/MODEL_VIEW': if self.ifc_export_settings.should_roundtrip_native \ - and obj.data.BIMMeshProperties.ifc_definition: + and obj.data.BIMMeshProperties.ifc_definition_id: pass else: self.representations['Model/Box/MODEL_VIEW/{}'.format(mesh_name.split('/')[3])] = self.get_representation( @@ -1101,6 +1102,7 @@ class IfcParser(): 'context': context, 'subcontext': subcontext, 'target_view': target_view, + 'has_ifc_definition': False if not hasattr(mesh, 'BIMMeshProperties') else (mesh.BIMMeshProperties.ifc_definition or mesh.BIMMeshProperties.ifc_definition_id), 'ifc_definition': mesh.BIMMeshProperties.ifc_definition if hasattr(mesh, 'BIMMeshProperties') else None, 'ifc_definition_id': mesh.BIMMeshProperties.ifc_definition_id if hasattr(mesh, 'BIMMeshProperties') else None, 'is_parametric': mesh.BIMMeshProperties.is_parametric if hasattr(mesh, 'BIMMeshProperties') else False, @@ -1172,7 +1174,9 @@ class IfcParser(): parsed_data_names = [] for product in self.selected_products + self.type_products: obj = product['raw'] - if obj.data is None or obj.data.name in parsed_data_names: + if obj.data is None \ + or obj.data.name in parsed_data_names \ + or obj.data.BIMMeshProperties.ifc_definition_id: continue parsed_data_names.append(obj.data.name) for slot in obj.material_slots: @@ -2180,7 +2184,7 @@ class IfcExporter(): results = [] for representation_name in product['representations']: representation = self.ifc_parser.representations[representation_name] - if self.ifc_export_settings.should_roundtrip_native and representation['ifc_definition']: + if self.ifc_export_settings.should_roundtrip_native and representation['has_ifc_definition']: results.append(representation['ifc']) else: results.append(self.get_product_mapped_geometry(product, representation)) @@ -2233,7 +2237,7 @@ class IfcExporter(): self.file.createIfcDirection((forward.x, forward.y, forward.z))) def create_representation(self, representation): - if self.ifc_export_settings.should_roundtrip_native and representation['ifc_definition']: + if self.ifc_export_settings.should_roundtrip_native and representation['has_ifc_definition']: return self.create_representation_from_definition(representation) self.ifc_vertices = [] self.ifc_edges = [] @@ -2245,28 +2249,32 @@ class IfcExporter(): return self.create_variable_representation(representation) def create_representation_from_definition(self, representation): - # See bug #999 on why we don't garbage collect the temporary IFC file - representation['ifc_definition_file'] = ifcopenshell.file.from_string(representation['ifc_definition']) - entry = self.file.add(representation['ifc_definition_file'].by_id(representation['ifc_definition_id'])) - substitutions = [] - for element in representation['ifc_definition_file']: - added_element = self.file.add(element) - if added_element.is_a('IfcGeometricRepresentationContext'): - substitutions.append(added_element) - for element in substitutions: - if element.is_a() == 'IfcGeometricRepresentationContext': - new_element = [e for e in - self.file.by_type('IfcGeometricRepresentationContext') - if e.ContextType == element.ContextType][0] - elif element.is_a() == 'IfcGeometricRepresentationSubContext': - new_element = [e for e in - self.file.by_type('IfcGeometricRepresentationContext') - if e.ContextType == element.ContextType and - e.ContextIdentifier == element.ContextIdentifier][0] - for inverse in self.file.get_inverse(element): - ifcopenshell.util.element.replace_attribute(inverse, element, new_element) - self.file.remove(element) - return entry + if representation['ifc_definition']: + print('Authoring an IFC definition directly is not yet implemented') + return + elif representation['ifc_definition_id']: + entry = self.file.add(ifc.IfcStore.get_file().by_id(representation['ifc_definition_id'])) + substitutions = [] + for element in get_representation_elements( + ifc.IfcStore.get_file(), representation['ifc_definition_id']): + added_element = self.file.add(element) + if added_element.is_a('IfcGeometricRepresentationContext'): + substitutions.append(added_element) + for element in substitutions: + if element.is_a() == 'IfcGeometricRepresentationContext': + new_element = [e for e in + self.file.by_type('IfcGeometricRepresentationContext') + if e.ContextType == element.ContextType][0] + elif element.is_a() == 'IfcGeometricRepresentationSubContext': + new_element = [e for e in + self.file.by_type('IfcGeometricRepresentationContext') + if e.ContextType == element.ContextType and + e.ContextIdentifier == element.ContextIdentifier][0] + for inverse in self.file.get_inverse(element): + ifcopenshell.util.element.replace_attribute(inverse, element, new_element) + # TODO: Work out how and when to purge this + #self.file.remove(element) + return entry def create_model_representation(self, representation): if representation['subcontext'] == 'Annotation': diff --git a/src/ifcblenderexport/blenderbim/bim/helper.py b/src/ifcblenderexport/blenderbim/bim/helper.py index 3771f1a720..3de489d048 100644 --- a/src/ifcblenderexport/blenderbim/bim/helper.py +++ b/src/ifcblenderexport/blenderbim/bim/helper.py @@ -1,6 +1,19 @@ import math import bpy +# TODO: figure out where this should go +def get_representation_elements(ifc_file, step_id): + results = [] + for child in ifc_file.traverse(ifc_file.by_id(step_id)): + if hasattr(child, 'StyledByItem') and child.StyledByItem: + for styled_by_item in child.StyledByItem: + for style in styled_by_item.Styles: + for style_child in ifc_file.traverse(style): + results.append(style_child) + results.append(child) + return results + + # TODO: Deprecate this in favour of ifcopenshell.util.unit class SIUnitHelper: diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index 9037534bd8..05c9dc757d 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -1938,17 +1938,11 @@ class IfcImporter(): mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element)) if not self.ifc_import_settings.should_roundtrip_native: return - dummy = ifcopenshell.file(schema=self.file.schema) if element.is_a('IfcRepresentation'): representation = element else: representation = self.get_representation_of_context(element.Representation.Representations, shape.context) - mesh.BIMMeshProperties.ifc_definition_id = int(dummy.add(representation).id()) - for child in self.file.traverse(representation): - [dummy.add(inverse) for inverse in self.file.get_inverse(child)] - dummy.add(child) - mesh.BIMMeshProperties.ifc_definition = dummy.to_string() - + mesh.BIMMeshProperties.ifc_definition_id = int(representation.id()) def create_curve(self, geometry): curve = bpy.data.curves.new(geometry.id, type='CURVE')