mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Proof of concept of native representation roundtripping
This commit is contained in:
@@ -201,6 +201,8 @@ if bpy is not None:
|
||||
operator.CreateShapeFromStepId,
|
||||
operator.SelectHighPolygonMeshes,
|
||||
operator.RefreshDrawingList,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.UpdateIfcRepresentation,
|
||||
prop.StrProperty,
|
||||
prop.Variable,
|
||||
prop.Role,
|
||||
@@ -236,6 +238,7 @@ if bpy is not None:
|
||||
prop.MapConversion,
|
||||
prop.TargetCRS,
|
||||
prop.Attribute,
|
||||
prop.IfcParameter,
|
||||
prop.BoundaryCondition,
|
||||
prop.PsetQto,
|
||||
prop.GlobalId,
|
||||
|
||||
@@ -489,7 +489,7 @@ class IfcParser():
|
||||
|
||||
def get_applicable_qtos(self, ifc_class):
|
||||
results = []
|
||||
empty = ifcopenshell.file()
|
||||
empty = ifcopenshell.file(schema=self.ifc_export_settings.schema)
|
||||
element = empty.create_entity(ifc_class)
|
||||
for ifc_class, qto_names in schema.ifc.applicable_qtos.items():
|
||||
if element.is_a(ifc_class):
|
||||
@@ -1026,6 +1026,9 @@ class IfcParser():
|
||||
self.representations['Model/Body/MODEL_VIEW/{}'.format(obj.data.name)] = self.get_representation(
|
||||
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:
|
||||
return
|
||||
self.representations['Model/Box/MODEL_VIEW/{}'.format(obj.data.name)] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Box', 'MODEL_VIEW')
|
||||
|
||||
@@ -1063,11 +1066,13 @@ class IfcParser():
|
||||
self.representations[mesh_name] = self.get_representation(
|
||||
mesh, obj, context, subcontext, target_view)
|
||||
if 'Model/Box/MODEL_VIEW' in self.generated_subcontexts \
|
||||
and context == 'Model' \
|
||||
and subcontext == 'Body' \
|
||||
and target_view == 'MODEL_VIEW':
|
||||
self.representations['Model/Box/MODEL_VIEW/{}'.format(mesh_name.split('/')[3])] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Box', 'MODEL_VIEW')
|
||||
and context_prefix == 'Model/Body/MODEL_VIEW':
|
||||
if self.ifc_export_settings.should_roundtrip_native \
|
||||
and obj.data.BIMMeshProperties.ifc_definition:
|
||||
pass
|
||||
else:
|
||||
self.representations['Model/Box/MODEL_VIEW/{}'.format(mesh_name.split('/')[3])] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Box', 'MODEL_VIEW')
|
||||
elif context_prefix == 'Model/Body/MODEL_VIEW' \
|
||||
and obj.data \
|
||||
and not self.is_mesh_context_sensitive(obj.data.name):
|
||||
@@ -1096,6 +1101,8 @@ class IfcParser():
|
||||
'context': context,
|
||||
'subcontext': subcontext,
|
||||
'target_view': target_view,
|
||||
'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,
|
||||
'is_curve': isinstance(mesh, bpy.types.Curve),
|
||||
'is_point_cloud': self.is_point_cloud(obj),
|
||||
@@ -2172,11 +2179,15 @@ class IfcExporter():
|
||||
def get_product_shape_representations(self, product):
|
||||
results = []
|
||||
for representation_name in product['representations']:
|
||||
results.append(self.get_product_mapped_geometry(product, representation_name))
|
||||
representation = self.ifc_parser.representations[representation_name]
|
||||
if self.ifc_export_settings.should_roundtrip_native and representation['ifc_definition']:
|
||||
results.append(representation['ifc'])
|
||||
else:
|
||||
results.append(self.get_product_mapped_geometry(product, representation))
|
||||
return results
|
||||
|
||||
def get_product_mapped_geometry(self, product, representation_name):
|
||||
mapping_source = self.ifc_parser.representations[representation_name]['ifc']
|
||||
def get_product_mapped_geometry(self, product, representation):
|
||||
mapping_source = representation['ifc']
|
||||
shape_representation = mapping_source.MappedRepresentation
|
||||
if product['has_scale']:
|
||||
if not product['has_mirror']:
|
||||
@@ -2222,6 +2233,8 @@ 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']:
|
||||
return self.create_representation_from_definition(representation)
|
||||
self.ifc_vertices = []
|
||||
self.ifc_edges = []
|
||||
if representation['context'] == 'Model':
|
||||
@@ -2231,6 +2244,30 @@ class IfcExporter():
|
||||
elif representation['context'] == 'NotDefined':
|
||||
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
|
||||
|
||||
def create_model_representation(self, representation):
|
||||
if representation['subcontext'] == 'Annotation':
|
||||
return self.file.createIfcRepresentationMap(self.origin,
|
||||
@@ -3063,6 +3100,7 @@ class IfcExportSettings:
|
||||
settings.should_use_presentation_style_assignment = scene_bim.export_should_use_presentation_style_assignment
|
||||
settings.should_guess_quantities = scene_bim.export_should_guess_quantities
|
||||
settings.should_force_faceted_brep = scene_bim.export_should_force_faceted_brep
|
||||
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||
settings.context_tree = []
|
||||
for ifc_context in ['model', 'plan']:
|
||||
if getattr(scene_bim, 'has_{}_context'.format(ifc_context)):
|
||||
|
||||
@@ -876,6 +876,7 @@ class IfcImporter():
|
||||
cumulative_vertex_index += item['total_vertices']
|
||||
|
||||
def create_native_mesh(self, element, shape):
|
||||
# TODO This should be split off into its own module for run-time native mesh conversion
|
||||
data = self.native_elements[element.GlobalId]
|
||||
materials = []
|
||||
items = []
|
||||
@@ -1826,6 +1827,11 @@ class IfcImporter():
|
||||
results.append({ 'raw': representation, 'matrix': self.scale_matrix(matrix) })
|
||||
return results
|
||||
|
||||
def get_representation_of_context(self, representations, context):
|
||||
for representation in representations:
|
||||
if representation.RepresentationIdentifier == context:
|
||||
return representation
|
||||
|
||||
def scale_matrix(self, matrix):
|
||||
matrix[0][3] *= self.unit_scale
|
||||
matrix[1][3] *= self.unit_scale
|
||||
@@ -1912,10 +1918,29 @@ class IfcImporter():
|
||||
ios_materials.append(mat.name)
|
||||
mesh['ios_materials'] = ios_materials
|
||||
mesh['ios_material_ids'] = geometry.material_ids
|
||||
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
|
||||
self.store_representation_source(mesh, element, shape)
|
||||
return mesh
|
||||
except:
|
||||
self.ifc_import_settings.logger.error('Could not create mesh for {}'.format(element))
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
def store_representation_source(self, mesh, element, shape):
|
||||
# TODO Refactor to specialist class
|
||||
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()
|
||||
|
||||
|
||||
def create_curve(self, geometry):
|
||||
curve = bpy.data.curves.new(geometry.id, type='CURVE')
|
||||
@@ -2062,6 +2087,7 @@ class IfcImportSettings:
|
||||
settings.should_use_cpu_multiprocessing = scene_bim.import_should_use_cpu_multiprocessing
|
||||
settings.should_import_with_profiling = scene_bim.import_should_import_with_profiling
|
||||
settings.should_import_native = scene_bim.import_should_import_native
|
||||
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||
settings.should_use_legacy = scene_bim.import_should_use_legacy
|
||||
settings.should_import_aggregates = scene_bim.import_should_import_aggregates
|
||||
settings.should_merge_aggregates = scene_bim.import_should_merge_aggregates
|
||||
|
||||
@@ -4082,3 +4082,53 @@ class RefreshDrawingList(bpy.types.Operator):
|
||||
new.name = obj.name.split('/')[1]
|
||||
new.camera = obj
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class GetRepresentationIfcParameters(bpy.types.Operator):
|
||||
bl_idname = 'bim.get_representation_ifc_parameters'
|
||||
bl_label = 'Get Representation IFC Parameters'
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.active_object.data.BIMMeshProperties
|
||||
dummy = ifcopenshell.file.from_string(props.ifc_definition)
|
||||
for element in dummy:
|
||||
if not element.is_a('IfcRepresentationItem'):
|
||||
continue
|
||||
for i in range(0, len(element)):
|
||||
if element.attribute_type(i) == 'DOUBLE':
|
||||
new = props.ifc_parameters.add()
|
||||
new.name = '{}/{}'.format(element.is_a(), element.attribute_name(i))
|
||||
new.step_id = element.id()
|
||||
new.type = element.attribute_type(i)
|
||||
new.index = i
|
||||
if element[i]:
|
||||
new.value = element[i]
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class UpdateIfcRepresentation(bpy.types.Operator):
|
||||
bl_idname = 'bim.update_ifc_representation'
|
||||
bl_label = 'Update IFC Representation'
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.active_object.data.BIMMeshProperties
|
||||
parameter = props.ifc_parameters[self.index]
|
||||
dummy = ifcopenshell.file.from_string(props.ifc_definition)
|
||||
element = dummy.by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||
props.ifc_definition = dummy.to_string()
|
||||
self.recreate_ifc_representation()
|
||||
return {'FINISHED'}
|
||||
|
||||
def recreate_ifc_representation(self):
|
||||
props = bpy.context.active_object.data.BIMMeshProperties
|
||||
dummy = ifcopenshell.file.from_string(props.ifc_definition)
|
||||
logger = logging.getLogger('ImportIFC')
|
||||
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
|
||||
element = dummy.by_id(props.ifc_definition_id)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
|
||||
ifc_importer.file = dummy
|
||||
mesh = ifc_importer.create_mesh(element, shape)
|
||||
bpy.context.active_object.data.user_remap(mesh)
|
||||
|
||||
@@ -1207,6 +1207,7 @@ class BIMProperties(PropertyGroup):
|
||||
import_should_treat_styled_item_as_material: BoolProperty(name="Import Treating Styled Item as Material", default=False)
|
||||
import_should_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
|
||||
import_should_import_native: BoolProperty(name="Import Native Representations", default=False)
|
||||
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=False)
|
||||
import_should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=True)
|
||||
import_should_import_with_profiling: BoolProperty(name="Import with Profiling", default=True)
|
||||
import_should_import_aggregates: BoolProperty(name="Import Aggregates", default=True)
|
||||
@@ -1351,6 +1352,15 @@ class Attribute(PropertyGroup):
|
||||
int_value: IntProperty(name="Value")
|
||||
float_value: FloatProperty(name="Value")
|
||||
|
||||
|
||||
class IfcParameter(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
step_id: IntProperty(name="STEP ID")
|
||||
index: IntProperty(name="Index")
|
||||
value: FloatProperty(name="Value") # For now, only floats
|
||||
type: StringProperty(name="Type")
|
||||
|
||||
|
||||
class PsetQto(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
@@ -1426,4 +1436,7 @@ class BIMMeshProperties(PropertyGroup):
|
||||
is_parametric: BoolProperty(name='Is Parametric', default=False)
|
||||
presentation_layer: StringProperty(name="Presentation Layer")
|
||||
geometry_type: StringProperty(name="Geometry Type")
|
||||
ifc_definition: StringProperty(name="IFC Definition")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
|
||||
active_representation_item_index: IntProperty(name='Active Representation Item Index')
|
||||
|
||||
@@ -605,6 +605,18 @@ class BIM_PT_mesh(Panel):
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, 'geometry_type')
|
||||
row = layout.row()
|
||||
row.prop(props, 'ifc_definition')
|
||||
layout.label(text="IFC Parameters:")
|
||||
row = layout.row()
|
||||
row.operator('bim.get_representation_ifc_parameters')
|
||||
for index, ifc_parameter in enumerate(props.ifc_parameters):
|
||||
row = layout.row(align=True)
|
||||
row.prop(ifc_parameter, 'name', text='')
|
||||
row.prop(ifc_parameter, 'step_id')
|
||||
row.prop(ifc_parameter, 'index')
|
||||
row.prop(ifc_parameter, 'value', text='')
|
||||
row.operator('bim.update_ifc_representation', icon='FILE_REFRESH', text='').index = index
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, 'presentation_layer')
|
||||
@@ -1696,6 +1708,8 @@ class BIM_PT_mvd(Panel):
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_should_import_native')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_export_should_roundtrip_native')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_should_use_cpu_multiprocessing')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_should_import_with_profiling')
|
||||
|
||||
Reference in New Issue
Block a user