Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mayavi: rotate around y axis

Tags:

python

3d

mayavi

I'm plotting a 3D mesh using mayavi's triangular_mesh method. The data describes a human silhouette laying face-down in 3D space (so the cmap can be used to denote distance from the camera).

Here's the code used to generate the plot (the faces and vertices come from an external object, and there are far too many to show here):

from mayavi import mlab

import math
import numpy as np
import sys
import os


fig = mlab.figure(fgcolor=(0, 0, 0), bgcolor=(1, 1, 1), size=(1920, 980))

a = np.array(this_mesh.vertices - refC.conj().transpose())  # this_mesh is an object created from external data files 

this_mesh.faces = this_mesh.faces.astype(int) -1  # mesh data is generated by matlab, which is 1-indexed

m = mlab.triangular_mesh(x, y, z, this_mesh.faces, opacity=0.75)

mlab.axes(extent=[-1100, 1100, -1100, 1100, -1100, 1100])

Without moving the camera, the silhouette lays face-down. In order to view the model face-on, I'm altering the azimuth and elevation of the camera to look at the graph top-down. This shows the silhouette as intended.

mlab.view(azimuth=0, elevation=180)
mlab.show()

My next task is to create a series of images where the camera pans around the plot, starting with the silhouette facing to the right, and finishing with it facing to the left.

The complication is that in order to get the colormap for the depth information, I'm already moving the view's azimuth and elevation (as shown in the code above). Mayavi has more options for moving the camera than matplotlib, but it doesn't seem to have a way to rotate around the Y axis, so I'm guessing I'll need to perform some complex calculations on the azimuth and elevation to achieve the same result - but I'm at a loss as to where to start (I'm new to working in 3D space, and my brain doesn't think that way yet).

Can anyone point me in the right direction?

like image 592
MassivePenguin Avatar asked Aug 05 '16 13:08

MassivePenguin


2 Answers

It turns out there's a bit of a workaround for this.

You can rotate the actors on their axes independently of the camera. (This throws the visualization out of step with the data labeling, but as I'm actually hiding the axes of the figure it doesn't matter in this case.)

All you need to do is:

m.actor.actor.rotate_y(desired_angle)

...and you're good to go.

like image 77
MassivePenguin Avatar answered Oct 20 '22 21:10

MassivePenguin


You need some math here. Ok, here is how to do it in terms of code, its not the best code but I wanted to make it self-explanatory. I use the Rodrigues formula for rotation in 3d to achieve this, az_new and el_new are your new viewing angles. Change the value of theta to get a different viewing angle in your frame of reference, i have used 45 degrees in the code below:

import numpy as np
import math

def rotation_matrix(axis, theta):
    """
    Return the rotation matrix associated with counterclockwise rotation about
    the given axis by theta radians.
    """
    axis = np.asarray(axis)
    theta = np.asarray(theta)
    axis = axis/math.sqrt(np.dot(axis, axis))
    a = math.cos(theta/2.0)
    b, c, d = -axis*math.sin(theta/2.0)
    aa, bb, cc, dd = a*a, b*b, c*c, d*d
    bc, ad, ac, ab, bd, cd = b*c, a*d, a*c, a*b, b*d, c*d
    return np.array([[aa+bb-cc-dd, 2*(bc+ad), 2*(bd-ac)],
                     [2*(bc-ad), aa+cc-bb-dd, 2*(cd+ab)],
                     [2*(bd+ac), 2*(cd-ab), aa+dd-bb-cc]])


az = 90
el = -75

x = np.cos(np.deg2rad(el))*np.cos(np.deg2rad(az))
y = np.cos(np.deg2rad(el))*np.sin(np.deg2rad(az))
z = np.sin(np.deg2rad(el))

# So your viewing vector in x,y coordinates on unit sphere
v = [x,y,z]

# Since you want to rotate about the y axis from this viewing angle, we just increase the
# elevation angle by 90 degrees to obtain our axis of rotation

az2 = az
el2 = el+90

x = np.cos(np.deg2rad(el2))*np.cos(np.deg2rad(az2))
y = np.cos(np.deg2rad(el2))*np.sin(np.deg2rad(az2))
z = np.sin(np.deg2rad(el2))

axis = [x,y,z]

# Now to rotate about the y axis from this viewing angle we use the rodrigues formula
# We compute our new viewing vector, lets say we rotate by 45 degrees
theta = 45
newv = np.dot(rotation_matrix(axis,np.deg2rad(theta)), v)

#Get azimuth and elevation for new viewing vector
az_new = np.rad2deg(np.arctan(newv[1]/newv[0]))
el_new = np.rad2deg(np.arcsin(newv[2]))
like image 28
Gaurav Dhama Avatar answered Oct 20 '22 20:10

Gaurav Dhama