Изменение соотношения сторон для 3D-графиков на Matplotlib - PullRequest
0 голосов
/ 24 февраля 2020

Я пытаюсь настроить соотношение сторон для 3D-графиков с помощью Matplotlib. После ответа на этот вопрос: Настройка соотношения сторон 3D-графика

Я применил решение как:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d import proj3d

def get_proj(self):
    """
    Create the projection matrix from the current viewing position.

    elev stores the elevation angle in the z plane
    azim stores the azimuth angle in the (x, y) plane

    dist is the distance of the eye viewing point from the object point.
    """
    # chosen for similarity with the initial view before gh-8896

    relev, razim = np.pi * self.elev/180, np.pi * self.azim/180

    #EDITED TO HAVE SCALED AXIS
    xmin, xmax = np.divide(self.get_xlim3d(), self.pbaspect[0])
    ymin, ymax = np.divide(self.get_ylim3d(), self.pbaspect[1])
    zmin, zmax = np.divide(self.get_zlim3d(), self.pbaspect[2])

    # transform to uniform world coordinates 0-1, 0-1, 0-1
    worldM = proj3d.world_transformation(xmin, xmax,
                                         ymin, ymax,
                                         zmin, zmax)

    # look into the middle of the new coordinates
    R = self.pbaspect / 2

    xp = R[0] + np.cos(razim) * np.cos(relev) * self.dist
    yp = R[1] + np.sin(razim) * np.cos(relev) * self.dist
    zp = R[2] + np.sin(relev) * self.dist
    E = np.array((xp, yp, zp))

    self.eye = E
    self.vvec = R - E
    self.vvec = self.vvec / np.linalg.norm(self.vvec)

    if abs(relev) > np.pi/2:
        # upside down
        V = np.array((0, 0, -1))
    else:
        V = np.array((0, 0, 1))
    zfront, zback = -self.dist, self.dist

    viewM = proj3d.view_transformation(E, R, V)
    projM = self._projection(zfront, zback)
    M0 = np.dot(viewM, worldM)
    M = np.dot(projM, M0)
    return M

Axes3D.get_proj = get_proj

Затем я создаю пример данных и построение графика:

y,z,x = np.meshgrid(range(10),-np.arange(5)[::-1],range(20))
d = np.sin(x)+np.sin(y)+np.sin(z)

iy,ix = 0,-1
iz = -1

fig,ax = plt.subplots(figsize=(5,5),subplot_kw={'projection':'3d'})
ax.pbaspect = np.array([1, 1, 1])#np.array([2.0, 1.0, 0.5])

ax.contourf(x[iz], y[iz], d[iz],zdir='z',offset=0)
ax.contourf(x[:,iy,:],d[:,iy,:],z[:,iy,:],zdir='y',offset=y.min())
ax.contourf(d[:,:,ix],y[:,:,ix],z[:,:,ix],zdir='x',offset=x.max())

color = '0.3'
ax.plot(x[0,iy,:],y[0,iy,:],y[0,iy,:]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,iy,0]*0+x.max(),y[:,iy,0],z[:,iy,0],color,linewidth=1,zorder=1e4)

ax.plot(x[0,:,ix],y[0,:,ix],y[0,:,ix]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,0,ix],y[:,0,ix]*0+y.min(),z[:,0,ix],color,linewidth=1,zorder=1e4)


ax.set(xlim=[x.min(),x.max()],ylim=[y.min(),y.max()],zlim=[z.min(),z.max()])

fig.tight_layout()

Если я установлю параметр pbaspect как (1,1,1), я получу:

enter image description here

Но если я установлю его для (2,1,0.5), то ось кажется правильной, но она как-то обрезает данные:

enter image description here

Четный если я позволю xlim, ylim и zlim автомати c. В этом аспекте тоже есть что-то странное. Что-то подсказывает мне, что эта ось не ортогональна.

enter image description here

Кто-нибудь знает, как исправить соотношение сторон для этого? Я также хотел бы знать, как избежать обрезки оси по границам рисунка.

Я долго искал в Интернете, но не смог найти лучшего решения для этого.

Обновление :

Я попытался использовать менее 1 значения для pbaspect, как предложено, и получилось лучше, но все еще обрезал данные:

enter image description here

1 Ответ

1 голос
/ 28 февраля 2020

Вы можете попытаться изменить figsize и настроить поля субплощадок, как показано ниже:

fig = plt.figure(figsize=(10,6))
fig.subplots_adjust(left=0.2, right=0.8, top=0.8, bottom=0.2)
ax = fig.gca(projection='3d')
ax.pbaspect = np.array([2, 1, 0.5])
ax.contourf(x[iz], y[iz], d[iz],zdir='z',offset=0)
ax.contourf(x[:,iy,:],d[:,iy,:],z[:,iy,:],zdir='y',offset=y.min())
ax.contourf(d[:,:,ix],y[:,:,ix],z[:,:,ix],zdir='x',offset=x.max())
ax.plot(x[0,iy,:],y[0,iy,:],y[0,iy,:]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,iy,0]*0+x.max(),y[:,iy,0],z[:,iy,0],color,linewidth=1,zorder=1e4)
ax.plot(x[0,:,ix],y[0,:,ix],y[0,:,ix]*0,color,linewidth=1,zorder=1e4)
ax.plot(x[:,0,ix],y[:,0,ix]*0+y.min(),z[:,0,ix],color,linewidth=1,zorder=1e4)
plt.show()

Вывод для (2,1,0.5):

enter image description here

Относительно исчезающей графики существует Проблема Matplotlib :

Проблема возникает из-за сокращения 3D-данных до 2D + скаляр z-порядка. Одно значение представляет 3-е измерение для всех частей трехмерных объектов в коллекции. Следовательно, когда ограничивающие рамки двух коллекций пересекаются, становится возможным возникновение этого артефакта. Кроме того, пересечение двух 3D-объектов (таких как полигоны или участки) не может быть правильно отображено в движке 2D-рендеринга matplotlib.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...