Implement import and export of relative placements of spatial hierarchy. See #852.

This commit is contained in:
Dion Moult
2020-05-16 21:36:34 +10:00
parent 3235d2495a
commit e44eaf0ccd
2 changed files with 71 additions and 26 deletions
@@ -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