2016-06-03 1 views
1

Je suis en train de concevoir une illustration en trois dimensions en utilisant Matplotlib. Tout fonctionne bien, sauf que la courbe paramétrique (rouge) est erronée alors que la surface paramétrique (verte) est dessinée correctement.La courbe paramétrique 3D de Matplotlib ne respecte pas zorder. Solution de contournement?

sortie généré par le code ci-dessous: Output generated by code below

Je sais que Matplotlib a des capacités limitées pour calculer avec précision la zorder des objets, mais comme il peut le faire pour la surface paramétrique, il semble comme un bogue dans Matplotlib. Cela dit, y a-t-il un moyen de forcer la bonne commande de z juste pour que les choses fonctionnent rapidement? Il semble que tout ce que je peux dire c'est que le bon plan bleu transparent est au-dessus de tout le reste. Cependant, mettre un argument zorder dans PolyCollection ne semble pas avoir d'effet, et mettre un argument zorder explicite dans la fonction plot qui dessine la ligne de lecture gâchera son ordre par rapport à la surface verte.

Existe-t-il un moyen de forcer la bonne surface transparente bleue par-dessus tout? Voici le code que j'ai jusqu'à présent:

#!/bin/env python3 

from pylab import * 
from mpl_toolkits.mplot3d import * 

from matplotlib.collections import PolyCollection 
from matplotlib.colors import colorConverter 
from matplotlib.patches import FancyArrowPatch 

rc('text', usetex=True) 
rc('font', size=20) 

fig = figure(figsize=(11,6)) 
ax = fig.gca(projection='3d') 

ax.set_axis_off() 

def f(x,t): 
    return t/2 * 0.55*(sin(2*x)+0.4*x**2-0.65) 

c_plane = colorConverter.to_rgba('b', alpha=0.15) 

N = 50 
y = linspace(-1,1,N) 
t = linspace(0,2,N) 
yy, tt = meshgrid(y, t) 
zz = f(yy,tt) 

ax.plot(0*ones(y.shape), y, f(y,0), '-g', linewidth=3) 
ax.plot(2*ones(y.shape), y, f(y,2), '-g', linewidth=3) 

yt = 0.7*y 
zt = f(yt, t) + 0.2*t 

ax.plot(t, yt, zt, '-r', linewidth=3) 
ax.plot((0,2), (yt[0], yt[-1]), (zt[0], zt[-1]), 'or') 

ax.plot([2,2,2], [-1,yt[-1],yt[-1]], [zt[-1],zt[-1],-1], 'k--') 
ax.plot(2*ones(y.shape), yt, f(yt,2)+0.1*(y+1), 'g:', linewidth=2) 
ax.plot((2,2), 
(yt[0], yt[-1]), 
(f(yt[0], 2), f(yt[-1], 2) + 0.1*(y[-1]+1)), 'og') 
ax.plot((0,2,2), 
(-1,-1,zt[-1]), 
(0,yt[-1],-1), 'ok') 

ax.text(0, -1.1, 0, r'$p(0)=0$', ha='right', va='center') 
ax.text(2, -1.05, zt[-1], r'$p(T)$', ha='right', va='center') 
ax.text(0, -1.0, 1, r'$p$', ha='right', va='bottom') 
ax.text(0, 1, -1.1, r'$q$', ha='center', va='top') 
ax.text(0, -1, -1.1, r'$t=0$', ha='right', va='top') 
ax.text(2, -1, -1.1, r'$t=T$', ha='right', va='top') 
ax.text(2, yt[-1]-0.05, -1.05, r'$q(T)=q^*$', ha='left', va='top') 
ax.text(0, 0.5, 0.05, r'$\mathcal{M}(0)$', ha='center', va='bottom') 
ax.text(2, 0.1, -0.8, r'$\mathcal{M}(T)$', ha='center', va='bottom') 

arrowprops = dict(mutation_scale=20, 
linewidth=2, 
arrowstyle='-|>', 
color='k') 

# For arrows, see 
# https://stackoverflow.com/questions/29188612/arrows-in-matplotlib-using-mplot3d 
class Arrow3D(FancyArrowPatch): 
    def __init__(self, xs, ys, zs, *args, **kwargs): 
     FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs) 
     self._verts3d = xs, ys, zs 

    def draw(self, renderer): 
     xs3d, ys3d, zs3d = self._verts3d 
     xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M) 
     self.set_positions((xs[0],ys[0]),(xs[1],ys[1])) 
     FancyArrowPatch.draw(self, renderer) 

a = Arrow3D([0,2], [-1,-1], [-1,-1], **arrowprops) 
ax.add_artist(a) 
a = Arrow3D([0,0], [-1,-1], [-1,1], **arrowprops) 
ax.add_artist(a) 
a = Arrow3D([0,0], [-1,1], [-1,-1], **arrowprops) 
ax.add_artist(a) 

# For surface illumination, see 
# http://physicalmodelingwithpython.blogspot.de/2015/08/illuminating-surface-plots.html 

# Get lighting object for shading surface plots. 
from matplotlib.colors import LightSource 

# Get colormaps to use with lighting object. 
from matplotlib import cm 

# Create an instance of a LightSource and use it to illuminate the surface. 
light = LightSource(70, -120) 
white = np.ones((zz.shape[0], zz.shape[1], 3)) 
illuminated_surface = light.shade_rgb(white*(0,1,0), zz) 

ax.plot_surface(tt, yy, zz, 
cstride=1, rstride=1, 
alpha=0.3, facecolors=illuminated_surface, 
linewidth=0) 

verts = [array([(-1,-1), (-1,1), (1,1), (1,-1), (-1,-1)])] 

poly = PolyCollection(verts, facecolors=c_plane) 
ax.add_collection3d(poly, zs=[0], zdir='x') 
poly = PolyCollection(verts, facecolors=c_plane) 
ax.add_collection3d(poly, zs=[2], zdir='x') 

ax.set_xlim3d(0, 2) 
ax.view_init(elev=18, azim=-54) 

show() 

Répondre

2

Axes3D ne tient pas compte zorder et attire tous les artistes dans l'ordre qu'il pense qu'ils devraient être. Cependant, vous pouvez définir zorder=0 pour la ligne rouge et zorder=-1 pour la surface verte (ou vice-versa) pour les placer derrière le panneau bleu droit.

Mon résultat:

enter image description here

Vous devez savoir:

L'ordre de dessin par défaut pour les axes est patches, les lignes, le texte. Cet ordre est déterminé par l'attribut zorder. Les valeurs par défaut suivantes sont définies

Artiste Z ordre

Patch/PatchCollection 1

Line2D/LineCollection 2

Text 3

+0

Je l'avais essayé, mais il est pas tout à fait correct. La courbe rouge est maintenant en dessous de la surface verte (très visible dans la partie la plus à droite où elle apparaît par derrière sur la photo que vous avez affichée).En fait, plot_surface semble ignorer complètement l'attribut zorder. Si ce n'était pas le cas, je suppose que j'aurais des problèmes avec le plan bleu de gauche, mais je présume que cela pourrait aussi être corrigé par des spécifications explicites. – mjo

1

Après un peu plus tâtonnement, je trouvé une solution. Si le bon plan est dessiné en utilisant plot_surfaceet Je change le zorder sur la courbe rouge, matplotlib obtient l'ordre global des objets à droite. Assez drôle, la couleur des plans change légèrement si je les dessine via PolyCollection ou plot_surface, donc j'ai besoin de dessiner les deux plans en utilisant la même fonction. Donc, la gestion de Zorder de mplot3d est plutôt incohérente, mais le résultat final semble plutôt bon. I post it here for reference

avec code final ici:

#!/bin/env python3 

from pylab import * 
from mpl_toolkits.mplot3d import * 

from matplotlib.collections import PolyCollection 
from matplotlib.colors import colorConverter 
from matplotlib.patches import FancyArrowPatch 

rc('text', usetex=True) 
rc('font', size=20) 

fig = figure(figsize=(11,6)) 
ax = fig.gca(projection='3d') 

ax.set_axis_off() 

def f(x,t): 
    return t/2 * 0.55*(sin(2*x)+0.4*x**2-0.65) 

c_plane = colorConverter.to_rgba('b', alpha=0.15) 

N = 50 
y = linspace(-1,1,N) 
t = linspace(0,2,N) 
yy, tt = meshgrid(y, t) 
zz = f(yy,tt) 

ax.plot(0*ones(y.shape), y, f(y,0), '-g', linewidth=3) 
ax.plot(2*ones(y.shape), y, f(y,2), '-g', linewidth=3) 

yt = 0.7*y 
zt = f(yt, t) + 0.2*t 

ax.plot(t, yt, zt, '-r', linewidth=3, zorder = 1) 

ax.plot([2,2,2], [-1,yt[-1],yt[-1]], [zt[-1],zt[-1],-1], 'k--') 
ax.plot(2*ones(y.shape), yt, f(yt,2)+0.1*(y+1), 'g:', linewidth=2) 
ax.plot((2,2), 
     (yt[0], yt[-1]), 
     (f(yt[0], 2), f(yt[-1], 2) + 0.1*(y[-1]+1)), 'og') 
ax.plot((0,2,2), 
     (-1,-1,zt[-1]), 
     (0,yt[-1],-1), 'ok') 

ax.text(0, -1.1, 0, r'$p(0)=0$', ha='right', va='center') 
ax.text(2, -1.05, zt[-1], r'$p(T)$', ha='right', va='center') 
ax.text(0, -1.0, 1, r'$p$', ha='right', va='bottom') 
ax.text(0, 1, -1.1, r'$q$', ha='center', va='top') 
ax.text(0, -1, -1.1, r'$t=0$', ha='right', va='top') 
ax.text(2, -1, -1.1, r'$t=T$', ha='right', va='top') 
ax.text(2, yt[-1]-0.05, -1.05, r'$q(T)=q^*$', ha='left', va='top') 
ax.text(0, 0.5, 0.05, r'$\mathcal{M}(0)$', ha='center', va='bottom') 
ax.text(2, 0.1, -0.8, r'$\mathcal{M}(T)$', ha='center', va='bottom') 

arrowprops = dict(mutation_scale=20, 
        linewidth=2, 
        arrowstyle='-|>', 
        color='k') 

# For arrows, see 
# https://stackoverflow.com/questions/29188612/arrows-in-matplotlib-using-mplot3d 
class Arrow3D(FancyArrowPatch): 
    def __init__(self, xs, ys, zs, *args, **kwargs): 
     FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs) 
     self._verts3d = xs, ys, zs 

    def draw(self, renderer): 
     xs3d, ys3d, zs3d = self._verts3d 
     xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M) 
     self.set_positions((xs[0],ys[0]),(xs[1],ys[1])) 
     FancyArrowPatch.draw(self, renderer) 

a = Arrow3D([0,2], [-1,-1], [-1,-1], **arrowprops) 
ax.add_artist(a) 
a = Arrow3D([0,0], [-1,-1], [-1,1], **arrowprops) 
ax.add_artist(a) 
a = Arrow3D([0,0], [-1,1], [-1,-1], **arrowprops) 
ax.add_artist(a) 

# For surface illumination, see 
# http://physicalmodelingwithpython.blogspot.de/2015/08/illuminating-surface-plots.html 

# Get lighting object for shading surface plots. 
from matplotlib.colors import LightSource 

# Get colormaps to use with lighting object. 
from matplotlib import cm 

# Create an instance of a LightSource and use it to illuminate the surface. 
light = LightSource(70, -120) 
white = ones((zz.shape[0], zz.shape[1], 3)) 
illuminated_surface = light.shade_rgb(white*(0,1,0), zz) 

ax.plot_surface(tt, yy, zz, 
       cstride=1, rstride=1, 
       alpha=0.3, facecolors=illuminated_surface, 
       linewidth=0, 
       zorder=10) 

verts = [array([(-1,-1), (-1,1), (1,1), (1,-1), (-1,-1)])] 

ax.plot_surface(((0,0),(0,0)), ((-1,-1),(1,1)), ((-1,1),(-1,1)), 
       color=c_plane) 

ax.plot_surface(((2,2),(2,2)), ((-1,-1),(1,1)), ((-1,1),(-1,1)), 
       color=c_plane) 

ax.plot((0,2), (yt[0], yt[-1]), (zt[0], zt[-1]), 'or') 

ax.set_xlim3d(0, 2) 
ax.view_init(elev=18, azim=-54) 

show()