Наслоение графика контура и поверхности_графика в matplotlib

Я борюсь со слоями и zorder в python. Я делаю трехмерный график, используя matplotlib с тремя соответствующими элементами: surface_plot планеты, surface_plot колец вокруг этой планеты и contourf изображение, показывающее тень планеты, отбрасываемую на кольца.

Я хочу, чтобы графика отображала именно то, как этот сценарий будет выглядеть в реальной жизни, с кольцами, огибающими планету, и тенью, находящейся поперек колец в соответствующем месте. Если тень находится за планетой для данного POV, я хочу, чтобы тень была заблокирована планетой, и наоборот, если тень находится перед планетой для данного POV.

Чтобы было ясно, это ТОЛЬКО проблема наслоения. Планета, кольца и тень у меня отображаются правильно. Однако тень никогда не будет отображаться перед планетой. Это действует так, как будто планета «блокирует» тень, хотя предполагается, что планета находится под тенью с точки зрения многослойности.

Я перепробовал все, что только мог придумать с точки зрения zorder и изменения порядка отрисовки различных элементов сюжета. Кольца ДЕЙСТВИТЕЛЬНО правильно отображаются перед планетой, но тень не будет.

Мой фактический код очень длинный. вот соответствующие части:

Настройка сюжета:


def orthographic_proj(zfront, zback):
    a = (zfront+zback)/(zfront-zback)
    b = -2*(zfront*zback)/(zfront-zback)
    return np.array([[1,0,0,0],
                        [0,1,0,0],
                        [0,0,a,b],
                        [0,0,0,zback]])

def setup_saturn_plot(ax3, elev, azim, drawz, drawxy,view):
    #ax3.set_aspect('equal','box')
    ax3.view_init(elev=elev, azim=azim)
    if(view=="top" or view == "Top" or view == "TOP"):
        ax3.dist = 5.5
    if(view=="star" or view == "Star" or view == "STAR"):
        ax3.dist = 5.0 #4.5 is best value
    proj3d.persp_transformation = orthographic_proj

    # hide grid and background
    ax3.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
    ax3.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
    ax3.w_zaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
    ax3.grid(False)

    # hide z axis in orthographic top view, xy axes in star view
    if (drawz == False):
        ax3.w_zaxis.line.set_lw(0.)
        ax3.set_zticks([])

    if (drawz == True):
        ax3.set_zlabel('Z (1000 km)',fontsize=12)

    if (drawxy == False):
        ax3.w_xaxis.line.set_lw(0.)
        ax3.set_xticks([])
        ax3.w_yaxis.line.set_lw(0.)
        ax3.set_yticks([])

    if (drawxy == True):
        ax3.set_xlabel('X (1000 km)',fontsize=12)
        ax3.set_ylabel('Y (1000 km)',fontsize=12)

Планета:

def draw_saturn(ax3, elev, azim):
    # Saturn dimensions
    radius = 60268. / 1000.
    radius_pole = 54364. / 1000.

    # draw Saturn
    phi, theta = np.mgrid[0.0:np.pi:100j, 0.0:2.0*np.pi:100j]
    x = radius*np.sin(phi)*np.cos(theta)
    y = radius*np.sin(phi)*np.sin(theta)
    z = radius_pole*np.cos(phi)

    line3 = ax3.plot_surface(x, y, z, color="w", edgecolor='b', rstride = 8, cstride=5, shade=False, lw=0.25)
    #line3 = ax3.plot_wireframe(x, y, z, color="w", edgecolor='b', rstride = 5, cstride=5, lw=0.25)

    ax3.tick_params(labelsize=10)

кольца:

def draw_rings(ax3, elev, azim, draw_mode):
    # Saturn dimensions
    radius = 60268. / 1000.

    # Saturn rings
    dringmin = 1.110 * radius 
    dringmax = 1.236 * radius 
    cringmin = 1.239 * radius 
    titanringlet = 1.292 * radius 
    maxwellgap = 1.452 * radius 
    cringmax = 1.526 * radius 
    bringmin = 1.526 * radius 
    bringmax = 1.950 * radius 
    aringmin = 2.030 * radius 
    enckegap = 2.214 * radius 
    keelergap = 2.265 * radius 
    aringmax = 2.270 * radius 
    fringmin = 2.320 * radius 
    gringmin = 2.754 * radius 
    gringmax = 2.874 * radius 
    eringmin = 2.987 * radius 
    eringmax = 7.964 * radius 

    if (draw_mode == 'back'):
        offset = -azim*np.pi/180. - 0.5*np.pi
    if (draw_mode == 'front'):
        offset = -azim*np.pi/180. + 0.5*np.pi

    rad, theta = np.mgrid[dringmin:dringmax:4j, 0.0-offset:1.0*np.pi-offset:100j]
    x = rad * np.cos(theta)
    y = rad * np.sin(theta)
    z = 0. * rad
    line1 = ax3.plot_surface(x, y, z, color="w", edgecolor='b', rstride = 8, cstride=25, shade=False, lw=0.25,alpha=0.)

    rad, theta = np.mgrid[cringmin:cringmax:4j, 0.0-offset:1.0*np.pi-offset:100j]
    x = rad * np.cos(theta)
    y = rad * np.sin(theta)
    z = 0. * rad
    line2 = ax3.plot_surface(x, y, z, color="w", edgecolor='b', rstride = 8, cstride=25, shade=False, lw=0.25,alpha=0.)

    rad, theta = np.mgrid[bringmin:bringmax:4j, 0.0-offset:1.0*np.pi-offset:100j]
    x = rad * np.cos(theta)
    y = rad * np.sin(theta)
    z = 0. * rad
    line3 = ax3.plot_surface(x, y, z, color="w", edgecolor='b', rstride = 8, cstride=25, shade=False, lw=0.25,alpha=0.)

    rad, theta = np.mgrid[aringmin:aringmax:4j, 0.0-offset:1.0*np.pi-offset:100j]
    x = rad * np.cos(theta)
    y = rad * np.sin(theta)
    z = 0. * rad
    line4 = ax3.plot_surface(x, y, z, color="w", edgecolor='b', rstride = 8, cstride=25, shade=False, lw=0.25,alpha=0.)

    rad, theta = np.mgrid[fringmin:1.005*fringmin:2j, 0.0-offset:1.0*np.pi-offset:100j]
    x = rad * np.cos(theta)
    y = rad * np.sin(theta)
    z = 0. * rad
    line7 = ax3.plot_surface(x, y, z, color="w", edgecolor='b', rstride = 8, cstride=25, shade=False, lw=0.1,alpha=0.)

Тень:

def draw_shadowboundary(ax3, sundir):
    sqrt = np.sqrt

    #azimuthal angle between x direction and direction of sun
    alpha = np.arctan2(sundir[1],sundir[0])
    #adjustments to keep -pi/2 < alpha < pi/2
    alphaadj = 0.*np.pi/180.
    if (alpha<0.):
        alpha += 2.*np.pi
    if ((alpha >= np.pi/2.) & (alpha <= np.pi)):
        alpha += np.pi
        alphaadj = np.pi
    if ((alpha > np.pi) & (alpha <= 3.*np.pi/2.)):
        alpha -= np.pi
        alphaadj = np.pi
    if (alpha>3.*np.pi/2.):
        alpha-=2*np.pi 

    #azimuthal angle between x direction and northern summer -- found using VIMS_2005_14_OMICET and VIMS_2017_053_ALPORI to define eq. of plane of Sun's annual path in chosen coordinate system: -0.193318*x + 0.1963755*y + 0.5471502*z = 0
    beta = 44.5505*np.pi/180.
    #Saturn's obliquity -- from NASA fact sheet
    psi = 26.73*np.pi/180.
    #Saturn's oblateness -- from NASA fact sheet
    obl = 0.09796
    #helpful definitions for optimization
    cpsic = np.cos(psi*np.cos(alpha+beta))
    spsic = np.sin(psi*np.cos(alpha+beta))
    calpha = np.cos(alpha)
    salpha = np.sin(alpha)
    #Saturn's projected shorter planetary axis as seen by the sun & ring inner edge
    req = 60268. / 1000.    
    b = req*sqrt((1.-obl)*(1.-obl)*cpsic*cpsic + spsic*spsic)
    ringstart = 1.239 * req
    ringend = 2.270 * req
    #shadow boundary of Saturn's rings -- can approximate using a=inf and cancelling terms
    a = 9.582*1.496*10.**5
    shadowline = lambda x,y : (1/a)*sqrt((req*salpha*(-a+x*calpha*cpsic+y*salpha)*(y*calpha-x*cpsic*salpha)/sqrt((y*calpha-x*cpsic*salpha)**2 + (x*spsic)**2) + calpha*(a*cpsic*(x*calpha*cpsic+y*salpha) + b*x*(a-x*calpha*cpsic-y*salpha)*spsic*spsic/sqrt((y*calpha-x*cpsic*salpha)**2 + (x*spsic)**2)))**2 + (req*calpha*(a-x*calpha*cpsic-y*salpha)*(y*calpha-x*cpsic*salpha)/sqrt((y*calpha-x*cpsic*salpha)**2 + (x*spsic)**2) + salpha*(a*cpsic*(x*calpha*cpsic+y*salpha)+b*x*(a-x*calpha*cpsic-y*salpha)*spsic*spsic/sqrt((y*calpha-x*cpsic*salpha)**2 + (x*spsic)**2)))**2)                                                                                                      
    #azimuthal radius & antisolar angle for inequalities
    radius = lambda x,y : np.sqrt(x**2+y**2)
    anti = lambda x,y : abs(np.arctan2(y,x)-(alpha-alphaadj))

    #properties of shadow
    samples=1200
    d = np.linspace(-3*req,3*req,samples)
    x,y = np.meshgrid(d,d)
    #z = ((radius(x,y)<=shadowline(x,y)) & (ringstart<=radius(x,y)) & (np.pi/2<=anti(x,y)) & (anti(x,y)<=3.*np.pi/2)).astype(int)
    z = ((radius(x,y)<=shadowline(x,y)) & (ringstart<=radius(x,y)) & (radius(x,y)<=ringend) & (np.pi/2<=anti(x,y)) & (anti(x,y)<=3.*np.pi/2)).astype(int)
    cmap = matplotlib.colors.ListedColormap(["k","k"])
    #add shadow to plot
    ax3.contourf(x,y,z, [0.5,1.50001], cmap=cmap,alpha=0.5)

Объединить графику:

import matplotlib
import numpy

from math import *

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D # <--- This is important for 3d plotting 

from mpl_toolkits.mplot3d import proj3d

def plot_results(phi, theta, sundir=[0.5, 0.5]):
    #plot_names.append("occultation_track_" + starname)
    fig2 = plt.figure(figsize=(9,9))
    ax3 = fig2.add_subplot(111, projection='3d')
    setup_saturn_plot(ax3, phi, theta, False, False, "star")
    draw_saturn(ax3, phi, theta)
    draw_rings(ax3, phi, theta, 'back')
    draw_rings(ax3, phi, theta, 'front')
    draw_shadowboundary(ax3,sundir)
    ax3.set_xlim([-200, 200]) 
    ax3.set_ylim([-200, 200])
    ax3.set_zlim([-200, 200])


plot_results(phi=40, theta=50, sundir = (30,60))

Код создает такое изображение:

введите описание изображения здесь

Предполагается, что серая тень находится на кольцах перед планетой. Однако он не будет отображаться перед планетой, поэтому на самом деле появляется только небольшая полоска тени справа от планеты. Тень отображается правильно во всех сценариях, за исключением случаев, когда она должна идти перед планетой.

Любые исправления для этого?


person ahle6481    schedule 30.08.2018    source источник
comment
Может оказаться, что на самом деле это невозможно, см. Мой 3D-график выглядит неправильно в некоторых углы обзора. Если вы перепробовали все варианты zorder, единственный обходной путь, который я могу придумать, - это создать другие оси с точно такими же параметрами сверху и нанести тень на эти оси наложения.   -  person ImportanceOfBeingErnest    schedule 30.08.2018
comment
Вопрос не полный. Не могли бы вы дать ссылку на файл данных? И внутри plot_results() используется много переменных, где они установлены? Можете ли вы предоставить минимальный полный пример кода, демонстрирующий проблему?   -  person DrM    schedule 07.06.2019


Ответы (1)


В данный момент я работаю над тем, чтобы разобраться с этим кодом, но в то же время, по крайней мере, до сих пор, это кажется известной проблемой с matplotlib3d.

Как уже давно заметил @TheImportanceOfBeingErnest, эта проблема появляется в часто задаваемых вопросах по mpl3d.

Мой 3D-график выглядит неправильно при определенных углах обзора

Это, пожалуй, самая распространенная проблема с mplot3d. Проблема в том, что под некоторыми углами обзора 3D-объект может оказаться впереди другого объекта, хотя физически он находится позади него. Это может привести к тому, что графики не будут выглядеть «физически правильными».

К сожалению, несмотря на то, что ведется некоторая работа по уменьшению появления этого артефакта, в настоящее время это неразрешимая проблема, и ее нельзя полностью решить, пока matplotlib не будет поддерживать рендеринг 3D-графики в своей основе.

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

Эта проблема, скорее всего, не будет решена до тех пор, пока поддержка OpenGL не будет добавлена ​​ко всем бэкендам (исправления приветствуются). До тех пор, если вам нужны сложные 3D-сцены, мы рекомендуем использовать MayaVi.

person Andrew Micallef    schedule 01.06.2020