Как изменить стиль взаимодействия с мышью для вращения Matplotlib 3d (mplot3d)? - PullRequest
0 голосов
/ 06 января 2020

Я построил трехмерный график и использовал quiver для построения осей x, y и z.

На интерактивном графике matplotlib я могу перетаскивать и вращать 3D-график, но есть одна проблема:

кажется, что ось Z ограничена плоскостью, когда я перетаскиваю график. Независимо от того, как я перетаскиваю график, ось Z может вращаться только ограниченным образом (в плоскости), тогда как ось X и ось Y могут вращаться свободно.

Мой вопрос: является ли это ограничение matplotlib или есть какой-либо метод, который я могу настроить, как вращать оси x, y и z?

Любые предложения приветствуются.

Воспроизводимый пример mininum прилагается для справки:

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()

enter image description here

enter image description here

1 Ответ

1 голос
/ 07 января 2020

Моя первая рекомендация: это .

Но если это вообще невозможно, я нашел решение, которое могло бы работать.

Метод _on_move in Axes3D отвечает за обработку событий мыши и поворот графика.

Как вы можете видеть, эта функция думает только по азимуту и ​​высоте. Вот почему он ведет себя так, как делает.

Можно повторно связать значение по умолчанию _on_move, как показано в методе mouse_init(), который вызывается в конструкторе Axes3D.

Скажите наш пользовательский стиль взаимодействия с мышью определен в

def _my_on_move(self, event):
    print('my custom mouse style', event)

. Это не работает:

ax._on_move = _my_on_move

, потому что _my_on_move является функцией, но нам нужен ограниченный метод, поэтому self доступно. Решение состоит в том, чтобы связать функцию как метод, это подробно описано здесь :

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

и повторно запустить инициализацию мыши:

ax.mouse_init()

Это часть в исходном _on_move установит elev и azim, которые затем используются get_proj() для установки матрицы преобразования, используемой в 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()

Каким-то образом мы должны проникнуть в модифицированная матрица преобразования. Я не уверен, что есть лучший способ, но мы могли бы просто передать измененные значения для elev и azim.

Поскольку мы хотим что-то более умное, мы должны переключиться на кватернионы. Я рекомендую использовать transfors.py , но есть также модуль под названием mathutils от Blender, который отлично работает.

Теперь самое интересное:

Вы должны получить текущий вид (текущая матрица преобразования) и вращать его в зависимости от движения мыши. Затем извлеките эквивалентные elev и azim из повернутой матрицы. Забавная задача, немного математики, но она должна быть возможной.

Но я оставлю это кому-то другому:)

Может быть, есть какое-то вдохновение, найденное в интеракторах VTK или из Blender.

Если вы хотите попробовать интеракторы из Mayavi / VTK:

pip install mayavi (или pip3 install mayavi в зависимости от вашей версии и вашей виртуальной среды).

Затем запустите

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...