Blog — nicoco.fr

Get the transform matrix from paraview's transform filter

2022-06-07 subscribe to my blog's atom feed

Paraview is a great tool for visualising 3D datasets. It also offers a few features to transform the data through filters, among which the Transform filter, which allows to interactively apply rotations and translations.

If you want to apply the same transformation outside of paraview, it becomes be a little tricky. While paraview gives you a nice GUI showing the rotation angles and the translation vector, it is unclear in which order the transformations are applied. Do we apply translation first? What is the order of the rotations?

Screenshot of paraview's transform filter

It took me some time to figure it out, but the answer is:

  1. Translation
  2. Z Rotation
  3. X Rotation
  4. Y Rotation

I am sure there is a valid reason for this Z, X, Y order but boy did it take me some precious time to figure this out. Here's a little dirty python snippet that I am happy to share:

import numpy as np


def paraview_transform(points, translation, xrot, yrot, zrot):
    """
    points: array of shape (n_points, 3)
    translation: array of shape (3,) to copy from the paraview GUI
    xrot, yrot, zrot: floats to copy from the paraview GUI
    """
    new_points = transform_points(
        points,
        get_transform_matrix(
            translation=translation,
            rotation_x=xrot,
            rotation_y=yrot,
            rotation_z=zrot
        ),
    )

    return new_points


def get_transform_matrix(
    translation=(0.0, 0.0, 0.0),
    rotation_x=0.0,
    rotation_y=0.0,
    rotation_z=0.0,
    radians=False,
    z_first=True,
):
    t = np.eye(4)
    t[:3, 3] = translation

    if not radians:
        rotation_x = np.deg2rad(rotation_x)
        rotation_y = np.deg2rad(rotation_y)
        rotation_z = np.deg2rad(rotation_z)

    x = np.eye(4)
    x[1, 1] = x[2, 2] = np.cos(rotation_x)
    x[2, 1] = np.sin(rotation_x)
    x[1, 2] = -np.sin(rotation_x)

    y = np.eye(4)
    y[0, 0] = y[2, 2] = np.cos(rotation_y)
    y[2, 0] = -np.sin(rotation_y)
    y[0, 2] = np.sin(rotation_y)

    z = np.eye(4)
    z[0, 0] = z[1, 1] = np.cos(rotation_z)
    z[1, 0] = np.sin(rotation_z)
    z[0, 1] = -np.sin(rotation_z)

    if z_first:
        res = t @ z @ x @ y
    else:
        res = t @ x @ y @ z

    return res.T


def transform_points(points, transformation_matrix):
    source = np.ones((len(points), 4))
    source[:, :3] = points
    new_points = source @ transformation_matrix
    return new_points[:, :3]