Matplotlib中的三维参数化曲线不符合zorder。变通办法?

2024-09-24 00:35:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用Matplotlib设计一个三维插图。所有的工作都很顺利,除了(红色)参数曲线得到错误的zorder而(绿色)参数化曲面绘制完全正确。在

由以下代码生成的输出: Output generated by code below

我知道Matplotlib在精确计算对象的zorder方面能力有限,但是由于它可以对参数化曲面执行此操作,所以它似乎是Matplotlib中的一个bug。在

也就是说,有没有什么方法可以强制执行正确的z顺序,以使事情快速运转?看来,我所能说的就是,正确的透明蓝色飞机在其他一切之上。然而,将zorder参数放入PolyCollection似乎没有任何效果,而在绘制读取线的plot函数中放入一个显式的zorder参数将扰乱其相对于绿色曲面的顺序。在

有没有一种方法可以将正确的蓝色透明表面强加于所有物体之上?以下是我目前掌握的代码:

#!/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()

Tags: textfromimportself参数plotmatplotlibax
2条回答

经过反复试验,我找到了解决办法。如果使用plot_surface绘制右平面,并且我更改红色曲线上的zorder,matplotlib将获得对象的整体顺序。有趣的是,无论我是通过PolyCollection还是plot_surface绘制它们,它们的颜色都会略有变化,所以我需要使用相同的函数绘制两个平面。因此,zorder对mplot3d的处理相当不一致,但最终的结果看起来相当不错。I post it here for reference

最终代码如下:

#!/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()

Axes3D忽略zorder并按它认为的顺序绘制所有艺术家。然而,您可以将zorder=0设置为红线,zorder=-1为绿色表面(反之亦然),以将它们置于右侧蓝色面板后面。在

我的结果:

enter image description here

你必须知道:

The default drawing order for axes is patches, lines, text. This order is determined by the zorder attribute. The following defaults are set

Artist Z-order

Patch / PatchCollection 1

Line2D / LineCollection 2

Text 3

相关问题 更多 >