mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Implement import and export of relative placements of spatial hierarchy. See #852.
This commit is contained in:
@@ -1608,13 +1608,18 @@ class IfcExporter():
|
||||
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']]
|
||||
|
||||
placement = self.file.createIfcLocalPlacement(
|
||||
placement_rel_to, self.get_relative_placement(element, placement_rel_to))
|
||||
|
||||
self.cast_attributes(element['class'], element['attributes'])
|
||||
element['attributes'].update({
|
||||
'OwnerHistory': self.owner_history, # TODO: unhardcode
|
||||
'ObjectPlacement': self.file.createIfcLocalPlacement(placement_rel_to, self.origin),
|
||||
'ObjectPlacement': placement,
|
||||
'Representation': self.get_product_shape(element)
|
||||
})
|
||||
element['ifc'] = self.file.create_entity(element['class'], **element['attributes'])
|
||||
@@ -1625,6 +1630,44 @@ class IfcExporter():
|
||||
ifcopenshell.guid.new(),
|
||||
self.owner_history, None, None, relating_object, related_objects)
|
||||
|
||||
def get_relative_placement(self, element, placement_rel_to):
|
||||
if placement_rel_to:
|
||||
relating_object_matrix = self.get_local_placement(placement_rel_to)
|
||||
else:
|
||||
relating_object_matrix = Matrix()
|
||||
z = Vector(element['up_axis'])
|
||||
x = Vector(element['forward_axis'])
|
||||
o = Vector(element['location'])
|
||||
object_matrix = self.a2p(o, z, x)
|
||||
relative_placement_matrix = relating_object_matrix.inverted() @ object_matrix
|
||||
return self.create_ifc_axis_2_placement_3d(
|
||||
relative_placement_matrix.translation,
|
||||
self.get_axis(relative_placement_matrix, 2),
|
||||
self.get_axis(relative_placement_matrix, 0))
|
||||
|
||||
def get_axis(self, matrix, axis):
|
||||
return matrix.col[axis].to_3d().normalized()
|
||||
|
||||
def get_local_placement(self, plc):
|
||||
if plc.PlacementRelTo is None:
|
||||
parent = Matrix()
|
||||
else:
|
||||
parent = self.get_local_placement(plc.PlacementRelTo)
|
||||
return parent @ self.get_axis2placement(plc.RelativePlacement)
|
||||
|
||||
def a2p(self, o, z, x):
|
||||
y = z.cross(x)
|
||||
r = Matrix((x, y, z, o))
|
||||
r.resize_4x4()
|
||||
r.transpose()
|
||||
return r
|
||||
|
||||
def get_axis2placement(self, plc):
|
||||
z = Vector(plc.Axis.DirectionRatios if plc.Axis else (0,0,1))
|
||||
x = Vector(plc.RefDirection.DirectionRatios if plc.RefDirection else (1,0,0))
|
||||
o = plc.Location.Coordinates
|
||||
return self.a2p(o,z,x)
|
||||
|
||||
def create_groups(self):
|
||||
for group in self.ifc_parser.groups:
|
||||
group['ifc'] = self.file.create_entity(group['class'], **group['attributes'])
|
||||
@@ -1778,12 +1821,11 @@ class IfcExporter():
|
||||
placement_rel_to = None
|
||||
|
||||
if product['has_scale']:
|
||||
placement = self.file.createIfcLocalPlacement(placement_rel_to, self.origin)
|
||||
# Omission of the relative placement here is not as per implementer agreements
|
||||
placement = self.file.createIfcLocalPlacement(None, self.origin)
|
||||
else:
|
||||
placement = self.file.createIfcLocalPlacement(placement_rel_to,
|
||||
self.create_ifc_axis_2_placement_3d(product['location'],
|
||||
product['up_axis'],
|
||||
product['forward_axis']))
|
||||
self.get_relative_placement(product, placement_rel_to))
|
||||
|
||||
self.cast_attributes(product['class'], product['attributes'])
|
||||
|
||||
|
||||
@@ -520,6 +520,8 @@ class IfcImporter():
|
||||
mat.transpose()
|
||||
obj.matrix_world = mat
|
||||
self.material_creator.create(element, obj, mesh)
|
||||
elif hasattr(element, 'ObjectPlacement'):
|
||||
obj.matrix_world = self.get_element_matrix(element)
|
||||
|
||||
self.add_element_attributes(element, obj)
|
||||
self.add_element_classifications(element, obj)
|
||||
@@ -1133,31 +1135,32 @@ class IfcImporter():
|
||||
return element.Identification
|
||||
return self.get_referenced_source_name(element.ReferencedSource)
|
||||
|
||||
def get_element_matrix(self, element, mesh_name):
|
||||
def get_element_matrix(self, element, mesh_name=None):
|
||||
element_matrix = self.get_local_placement(element.ObjectPlacement)
|
||||
|
||||
# Blender supports reusing a mesh with a different transformation
|
||||
# applied at the object level. In contrast, IFC supports reusing a mesh
|
||||
# with a different transformation applied at the mesh level _as well as_
|
||||
# the object level. For this reason, if the end-goal is to re-use mesh
|
||||
# data, we must combine IFC's mesh-level transformation into Blender's
|
||||
# object level transformation.
|
||||
if mesh_name:
|
||||
# Blender supports reusing a mesh with a different transformation
|
||||
# applied at the object level. In contrast, IFC supports reusing a mesh
|
||||
# with a different transformation applied at the mesh level _as well as_
|
||||
# the object level. For this reason, if the end-goal is to re-use mesh
|
||||
# data, we must combine IFC's mesh-level transformation into Blender's
|
||||
# object level transformation.
|
||||
|
||||
# The first step to do this is to _undo_ the mesh-level transformation
|
||||
# from whatever shared mesh we are using, as it is not necessarily the
|
||||
# same as the current mesh.
|
||||
shared_shape_transformation = self.get_representation_cartesian_transformation(
|
||||
self.file.by_id(self.mesh_shapes[mesh_name].product.id()))
|
||||
if shared_shape_transformation:
|
||||
shared_transform = self.get_cartesiantransformationoperator(shared_shape_transformation)
|
||||
shared_transform.invert()
|
||||
element_matrix = element_matrix @ shared_transform
|
||||
# The first step to do this is to _undo_ the mesh-level transformation
|
||||
# from whatever shared mesh we are using, as it is not necessarily the
|
||||
# same as the current mesh.
|
||||
shared_shape_transformation = self.get_representation_cartesian_transformation(
|
||||
self.file.by_id(self.mesh_shapes[mesh_name].product.id()))
|
||||
if shared_shape_transformation:
|
||||
shared_transform = self.get_cartesiantransformationoperator(shared_shape_transformation)
|
||||
shared_transform.invert()
|
||||
element_matrix = element_matrix @ shared_transform
|
||||
|
||||
# The next step is to apply the current element's mesh level
|
||||
# transformation to our current element's object transformation
|
||||
transformation = self.get_representation_cartesian_transformation(element)
|
||||
if transformation:
|
||||
element_matrix = self.get_cartesiantransformationoperator(transformation) @ element_matrix
|
||||
# The next step is to apply the current element's mesh level
|
||||
# transformation to our current element's object transformation
|
||||
transformation = self.get_representation_cartesian_transformation(element)
|
||||
if transformation:
|
||||
element_matrix = self.get_cartesiantransformationoperator(transformation) @ element_matrix
|
||||
|
||||
element_matrix[0][3] *= self.unit_scale
|
||||
element_matrix[1][3] *= self.unit_scale
|
||||
|
||||
Reference in New Issue
Block a user