diff --git a/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py b/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py index c399a3f574..0f8afd40d6 100644 --- a/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py +++ b/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py @@ -17,6 +17,9 @@ # along with IfcPatch. If not, see . import math +import numpy as np +import ifcopenshell +import ifcopenshell.util.placement class Patcher: def __init__(self, src, file, logger, args=None): @@ -36,49 +39,17 @@ class Patcher: absolute_placements.append(absolute_placement) absolute_placements = set(absolute_placements) + angle = float(self.args[3]) + transformation = self.z_rotation_matrix(math.radians(angle)) if angle else np.eye(4) + transformation[0][3] += float(self.args[0]) + transformation[1][3] += float(self.args[1]) + transformation[2][3] += float(self.args[2]) + for placement in absolute_placements: - offset_location = ( - placement.RelativePlacement.Location.Coordinates[0] + float(self.args[0]), - placement.RelativePlacement.Location.Coordinates[1] + float(self.args[1]), - placement.RelativePlacement.Location.Coordinates[2] + float(self.args[2]) + placement.RelativePlacement = self.get_relative_placement( + transformation @ ifcopenshell.util.placement.get_local_placement(placement) ) - relative_placement = self.file.createIfcAxis2Placement3D( - self.file.createIfcCartesianPoint(offset_location)) - - if placement.RelativePlacement.Axis: - relative_placement.Axis = placement.RelativePlacement.Axis - if placement.RelativePlacement.RefDirection: - relative_placement.RefDirection = placement.RelativePlacement.RefDirection - - angle = float(self.args[3]) - if not angle: - placement.RelativePlacement = relative_placement - continue - - rotation_matrix = self.z_rotation_matrix(math.radians(angle)) - - if len(self.args) == 5: - # Move then rotate, if you want - relative_placement.Location.Coordinates = self.multiply_by_matrix(offset_location, rotation_matrix) - else: - # Rotate then move, like Solibri - pass - - if placement.RelativePlacement.Axis: - z_axis = placement.RelativePlacement.Axis.DirectionRatios - relative_placement.Axis = self.file.createIfcDirection( - self.multiply_by_matrix(z_axis, rotation_matrix)) - - if placement.RelativePlacement.RefDirection: - x_axis = placement.RelativePlacement.RefDirection.DirectionRatios - else: - x_axis = (1., 0., 0.) - relative_placement.RefDirection = self.file.createIfcDirection( - self.multiply_by_matrix(x_axis, rotation_matrix)) - - placement.RelativePlacement = relative_placement - def get_absolute_placement(self, object_placement): if object_placement.PlacementRelTo: return self.get_absolute_placement(object_placement.PlacementRelTo) @@ -86,14 +57,26 @@ class Patcher: def z_rotation_matrix(self, angle): return [ - [math.cos(angle), -math.sin(angle), 0.], - [math.sin(angle), math.cos(angle), 0.], - [0., 0., 1.] + [math.cos(angle), -math.sin(angle), 0., 0.], + [math.sin(angle), math.cos(angle), 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.], ] - def multiply_by_matrix(self, v, m): - return [ - v[0]*m[0][0] + v[1]*m[0][1] + v[2]*m[0][2], - v[0]*m[1][0] + v[1]*m[1][1] + v[2]*m[1][2], - v[0]*m[2][0] + v[1]*m[2][1] + v[2]*m[2][2] - ] + def get_relative_placement(self, m): + x = np.array((m[0][0], m[1][0], m[2][0])) + z = np.array((m[0][2], m[1][2], m[2][2])) + o = np.array((m[0][3], m[1][3], m[2][3])) + object_matrix = ifcopenshell.util.placement.a2p(o, z, x) + return self.create_ifc_axis_2_placement_3d( + object_matrix[:, 3][0:3], + object_matrix[:, 2][0:3], + object_matrix[:, 0][0:3], + ) + + def create_ifc_axis_2_placement_3d(self, point, up, forward): + return self.file.createIfcAxis2Placement3D( + self.file.createIfcCartesianPoint(point.tolist()), + self.file.createIfcDirection(up.tolist()), + self.file.createIfcDirection(forward.tolist()), + )