python 3d绘图旋转_如何更改Matplotlib 3d旋转(mplot3d)的鼠标交互样式?

我绘制了3D图,并用于quiver绘制x,y和z轴。

在matplotlib的交互式绘图中,我可以拖动和旋转3D绘图,但是存在一个问题:

当我拖动绘图时,似乎Z轴限制在一个平面上。无论我如何拖动绘图,Z轴只能以有限的方式(在一个平面内)旋转,而X轴和Y轴可以自由旋转。

我的问题是:这是对matplotlib的限制吗?是否有什么方法可以配置x,y和z轴的旋转方式?

任何建议表示赞赏。

随附一个最小的可复制示例,以供参考:

from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt

import numpy as np

n_radii = 8

n_angles = 36

# Make radii and angles spaces (radius r=0 omitted to eliminate duplication).

radii = np.linspace(0.125, 1.0, n_radii)

angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)

# Repeat all angles for each radius.

angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)

# Convert polar (radii, angles) coords to cartesian (x, y) coords.

# (0, 0) is manually added at this stage, so there will be no duplicate

# points in the (x, y) plane.

x = np.append(0, (radii*np.cos(angles)).flatten())

y = np.append(0, (radii*np.sin(angles)).flatten())

# Compute z to make the pringle surface.

z = np.sin(-x*y)

fig = plt.figure()

ax = fig.gca(projection='3d')

ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)

steps = 100

theta = np.linspace(0, 2 * np.pi, steps)

r_max = 1.2

x = np.zeros_like(theta)

y = r_max * np.cos(theta)

z = r_max * np.sin(theta)

ax.plot(x, y, z, 'r')

ax.plot(y, x, z, 'g')

ax.plot(z, y, x, 'b')

scale = 1.08

ax.quiver((0,), (0), (0),

(0), (0), (r_max), color=('c'))

ax.text(0, 0, r_max * scale, 'Z Theta', weight='bold')

ax.quiver((0), (0), (0),

(0), (r_max), (0), color=('m'))

ax.text(0, r_max * scale, 0, 'Y', weight='bold')

ax.quiver((0), (0), (0),

(r_max), (0), (0), color=('y'))

ax.text(r_max * scale, 0, 0, 'X', weight='bold')

plt.show()

python 3d绘图旋转_如何更改Matplotlib 3d旋转(mplot3d)的鼠标交互样式?_第1张图片

解决方案

我的第一个建议是这个。

但是,如果根本不可能,我找到了一个可行的解决方案。

The method _on_move in Axes3Dresponsible for processing the mouse events and rotating the plot.

As you can see this function only thinks in azimuth and elevation. That`s why it behaves the way it does.

It is possible to re-bind the default _on_move as seen in the method mouse_init() which is called in the constructor of Axes3D.

Say our custom mouse interaction style is defined in

def _my_on_move(self, event):

print('my custom mouse style', event)

This does not work:

ax._on_move = _my_on_move

because _my_on_move is a function but we need it to be a bound method so the self is available.

The solution is to bind the function as method, this is described in detail here:

import types

ax._on_move = types.MethodType(_my_on_move, ax)

and re-run the mouse initialization:

ax.mouse_init()

This part in the original _on_move will set elev and azim which are then used by get_proj() to set the

transformation matrix used in figure.canvas.draw_idle():

self.elev = art3d._norm_angle(self.elev - (dy/h)*180)

self.azim = art3d._norm_angle(self.azim - (dx/w)*180)

self.get_proj()

self.figure.canvas.draw_idle()

Somehow we have to sneak in a modified transformation matrix. I am not sure if there is a better way,

but we could just pass in modified values for elev and azim.

Since we want something smarter we should switch to quaternions.

I recommend using transformations.py

but there is also a module called mathutils from Blender with works fine.

Now to the fun part:

You have to get the current view (the current transformation matrix) and

rotate it based on the mouse movement. Then extract the equivalent elev and azim

from the rotated matrix. Fun task, some math, but it should be possible.

But I will leave that for to someone else :)

Maybe there is some inspitation found in VTK`s interactors or the ones from Blender.

If you want to try the interactors from Mayavi / VTK:

pip install mayavi (or pip3 install mayavi depending on your version and your virtual environment).

Then run

from mayavi import mlab

from tvtk.api import tvtk

for i in [tvtk.InteractorStyleTerrain(),

tvtk.InteractorStyleJoystickActor(),

tvtk.InteractorStyleTrackballActor(),

tvtk.InteractorStyleTrackball()]:

mlab.test_surf()

fig = mlab.gcf()

fig.scene.interactor.interactor_style = i

mlab.show()

你可能感兴趣的:(python,3d绘图旋转)