mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
Opening elements now create a boolean relationship. See #865.
This commit is contained in:
@@ -281,6 +281,7 @@ class IfcImporter():
|
||||
self.create_products_legacy()
|
||||
else:
|
||||
self.create_products()
|
||||
self.relate_openings()
|
||||
self.place_objects_in_spatial_tree()
|
||||
if self.ifc_import_settings.should_merge_aggregates:
|
||||
self.merge_aggregates()
|
||||
@@ -337,14 +338,7 @@ class IfcImporter():
|
||||
|
||||
def parse_native_swept_disk_solid(self):
|
||||
for element in self.file.by_type('IfcSweptDiskSolid'):
|
||||
dummy_geometry = self.get_dummy_geometry()
|
||||
inverse_elements = self.file.get_inverse(element)
|
||||
for inverse_element in inverse_elements:
|
||||
if inverse_element.is_a('IfcShapeRepresentation'):
|
||||
inverse_element.RepresentationType = 'Curve'
|
||||
for product in self.get_products_from_shape_representation(inverse_element):
|
||||
self.native_elements.setdefault(product.GlobalId, {})[dummy_geometry.id()] = element
|
||||
self.replace_attribute(inverse_element, element, dummy_geometry)
|
||||
self.swap_out_with_dummy_geometry(element)
|
||||
|
||||
def parse_native_extruded_area_solid(self):
|
||||
for element in self.file.by_type('IfcExtrudedAreaSolid'):
|
||||
@@ -353,14 +347,17 @@ class IfcImporter():
|
||||
'IfcRectangleProfileDef'
|
||||
]:
|
||||
continue
|
||||
dummy_geometry = self.get_dummy_geometry()
|
||||
inverse_elements = self.file.get_inverse(element)
|
||||
for inverse_element in inverse_elements:
|
||||
if inverse_element.is_a('IfcShapeRepresentation'):
|
||||
inverse_element.RepresentationType = 'Curve'
|
||||
for product in self.get_products_from_shape_representation(inverse_element):
|
||||
self.native_elements.setdefault(product.GlobalId, {})[dummy_geometry.id()] = element
|
||||
self.replace_attribute(inverse_element, element, dummy_geometry)
|
||||
self.swap_out_with_dummy_geometry(element)
|
||||
|
||||
def swap_out_with_dummy_geometry(self, element):
|
||||
dummy_geometry = self.get_dummy_geometry()
|
||||
inverse_elements = self.file.get_inverse(element)
|
||||
for inverse_element in inverse_elements:
|
||||
if inverse_element.is_a('IfcShapeRepresentation'):
|
||||
inverse_element.RepresentationType = 'Curve'
|
||||
for product in self.get_products_from_shape_representation(inverse_element):
|
||||
self.native_elements.setdefault(product.GlobalId, {})[dummy_geometry.id()] = element
|
||||
self.replace_attribute(inverse_element, element, dummy_geometry)
|
||||
|
||||
def get_dummy_geometry(self):
|
||||
point = self.file.createIfcCartesianPoint((0., 0., 0.))
|
||||
@@ -645,6 +642,7 @@ class IfcImporter():
|
||||
self.add_element_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.add_defines_by_type_relation(element, obj)
|
||||
self.add_opening_relation(element, obj)
|
||||
self.add_product_definitions(element, obj)
|
||||
self.add_product_representation_contexts(element, obj)
|
||||
self.added_data[element.GlobalId] = obj
|
||||
@@ -718,6 +716,8 @@ class IfcImporter():
|
||||
if materials[i]:
|
||||
material_ids += [i] * total_polygons
|
||||
else:
|
||||
# Magic string NULLMAT represents no material, unless this has a better approach
|
||||
materials[i] = 'NULLMAT'
|
||||
# Magic number 999999 represents no material, until this has a better approach
|
||||
material_ids += [999999] * total_polygons
|
||||
if merged_curve:
|
||||
@@ -783,7 +783,7 @@ class IfcImporter():
|
||||
bm = self.bmesh_from_rectangle(item.SweptArea.XDim, item.SweptArea.YDim)
|
||||
if item.SweptArea.Position:
|
||||
bmesh.ops.transform(bm, matrix=self.get_axis2placement(item.SweptArea.Position), verts=bm.verts)
|
||||
bmesh.ops.transform(bm, matrix=mathutils.Matrix() * self.unit_scale, verts=bm.verts)
|
||||
bmesh.ops.transform(bm, matrix=mathutils.Matrix() * self.unit_scale, verts=bm.verts)
|
||||
else:
|
||||
# TODO: what if we can't handle it?
|
||||
return
|
||||
@@ -794,7 +794,8 @@ class IfcImporter():
|
||||
if isinstance(geom, bmesh.types.BMVert):
|
||||
geom.co += offset
|
||||
if item.Position:
|
||||
bmesh.ops.transform(bm, matrix=self.get_axis2placement(item.Position), verts=bm.verts)
|
||||
bmesh.ops.transform(
|
||||
bm, matrix=self.scale_matrix(self.get_axis2placement(item.Position)), verts=bm.verts)
|
||||
return bm
|
||||
#mesh['ios_material_ids'] = [0] * len(bm.faces)
|
||||
|
||||
@@ -999,21 +1000,24 @@ class IfcImporter():
|
||||
new_prop.string_value = str(value)
|
||||
|
||||
def add_defines_by_type_relation(self, element, obj):
|
||||
related_type = None
|
||||
if self.file.schema == 'IFC2X3':
|
||||
if not hasattr(element, 'IsDefinedBy') or not element.IsDefinedBy:
|
||||
return
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.is_a('IfcRelDefinesByType'):
|
||||
related_type = relationship.RelatingType
|
||||
break
|
||||
else:
|
||||
if not hasattr(element, 'IsTypedBy') or not element.IsTypedBy:
|
||||
return
|
||||
related_type = element.IsTypedBy[0].RelatingType
|
||||
related_type = self.get_type(element)
|
||||
if related_type:
|
||||
obj.BIMObjectProperties.relating_type = self.type_products[related_type.GlobalId]
|
||||
|
||||
# TODO: migrate into util function
|
||||
def get_type(self, element):
|
||||
if hasattr(element, 'IsTypedBy') and element.IsTypedBy:
|
||||
return element.IsTypedBy[0].RelatingType
|
||||
elif hasattr(element, 'IsDefinedBy') and element.IsDefinedBy: # IFC2X3
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.is_a('IfcRelDefinesByType'):
|
||||
return relationship.RelatingType
|
||||
|
||||
def add_opening_relation(self, element, obj):
|
||||
if not element.is_a('IfcOpeningElement'):
|
||||
return
|
||||
self.openings[element.GlobalId] = obj
|
||||
|
||||
def load_existing_rooted_elements(self):
|
||||
for obj in bpy.data.objects:
|
||||
if hasattr(obj, 'BIMObjectProperties') and obj.BIMObjectProperties.attributes.get('GlobalId'):
|
||||
@@ -1249,8 +1253,9 @@ class IfcImporter():
|
||||
self.aggregates[element.GlobalId] = obj
|
||||
|
||||
def create_openings_collection(self):
|
||||
collection = bpy.data.collections.new('IfcOpeningElements')
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
self.opening_collection = bpy.data.collections.new('IfcOpeningElements')
|
||||
self.project['blender'].children.link(self.opening_collection)
|
||||
bpy.context.view_layer.layer_collection.children[self.project['blender'].name].children[self.opening_collection.name].hide_viewport = True
|
||||
|
||||
def get_name(self, element):
|
||||
return '{}/{}'.format(element.is_a(), element.Name)
|
||||
@@ -1325,6 +1330,16 @@ class IfcImporter():
|
||||
and getattr(association.RelatingDocument, value):
|
||||
setattr(reference, key, getattr(association.RelatingDocument, value))
|
||||
|
||||
def relate_openings(self):
|
||||
for global_id, opening in self.openings.items():
|
||||
building_element_global_id = self.file.by_guid(global_id).VoidsElements[0].RelatingBuildingElement.GlobalId
|
||||
if building_element_global_id not in self.added_data:
|
||||
continue
|
||||
building_element = self.added_data[building_element_global_id]
|
||||
modifier = building_element.modifiers.new('IfcOpeningElement', 'BOOLEAN')
|
||||
modifier.operation = 'DIFFERENCE'
|
||||
modifier.object = opening
|
||||
|
||||
def place_objects_in_spatial_tree(self):
|
||||
for global_id, obj in self.added_data.items():
|
||||
self.place_object_in_spatial_tree(self.file.by_guid(global_id), obj)
|
||||
@@ -1360,7 +1375,7 @@ class IfcImporter():
|
||||
self.ifc_import_settings.logger.error('An element could not be placed in the spatial tree {}'.format(element))
|
||||
elif hasattr(element, 'HasFillings') \
|
||||
and element.HasFillings:
|
||||
bpy.data.collections.get('IfcOpeningElements').objects.link(obj)
|
||||
self.opening_collection.objects.link(obj)
|
||||
else:
|
||||
self.ifc_import_settings.logger.warning('Warning: this object is outside the spatial hierarchy')
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
@@ -1467,12 +1482,15 @@ class IfcImporter():
|
||||
results.extend(self.get_body_representations([item.MappingSource.MappedRepresentation],
|
||||
transform @ matrix))
|
||||
elif representation.RepresentationIdentifier == 'Body':
|
||||
matrix[0][3] *= self.unit_scale
|
||||
matrix[1][3] *= self.unit_scale
|
||||
matrix[2][3] *= self.unit_scale
|
||||
results.append({ 'raw': representation, 'matrix': matrix })
|
||||
results.append({ 'raw': representation, 'matrix': self.scale_matrix(matrix) })
|
||||
return results
|
||||
|
||||
def scale_matrix(self, matrix):
|
||||
matrix[0][3] *= self.unit_scale
|
||||
matrix[1][3] *= self.unit_scale
|
||||
matrix[2][3] *= self.unit_scale
|
||||
return matrix
|
||||
|
||||
def get_representation_id(self, element):
|
||||
if not element.Representation:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user