简体   繁体   English

从 3D plot 中提取可绘图 Matplotlib 二维轴?

[英]Extract plottable Matplotlib 2D axes from 3D plot?

So, this is my problem - I use a matplotlib-based library that accepts 2D Axes ax as input, and uses ax.imshow , which makes the assumption that the ax passed to it is a 2D one.所以,这是我的问题 - 我使用基于 matplotlib 的库,它接受 2D Axes ax作为输入,并使用ax.imshow ,它假设传递给它的ax是 2D 的。 I'd like to use this library, but to plot its result on the xy plane at z=0 on a 3D matplotlib plot. I'd like to use this library, but to plot its result on the xy plane at z=0 on a 3D matplotlib plot.

As far as I can see from:据我所知:

... I have to basically use ax.plot_surface to have the equivalent of ax.imshow in 3D. ...我必须基本上使用ax.plot_surface在 3D 中具有等效的ax.imshow However, that involves rewriting/hacking the library, so all corresponding calls are replaced.但是,这涉及重写/破解库,因此所有相应的调用都会被替换。

So, I tried to come up with this simple example, to see what can be achieved by using imshow in a 3D context:所以,我试着想出这个简单的例子,看看在 3D 上下文中使用imshow可以实现什么:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

fig = plt.figure()
ax3d = fig.add_subplot(projection='3d')

x = np.linspace(0, 1, 100)
y = np.sin(x * 2 * np.pi) / 2 + 0.5
#ax3d.plot(x, y, zs=0, zdir='z', label='curve in (x, y)') # works

# syntax as for 2d plot:
ax3d.plot(x, y, label='curve in (x, y)') # works

# https://matplotlib.org/stable/gallery/images_contours_and_fields/image_demo.html
delta = 0.025
xa = ya = np.arange(0.0, 1.0, delta)
Xa, Ya = np.meshgrid(xa, ya)
Z1a = np.exp(-Xa**2 - Ya**2)
Z2a = np.exp(-(Xa - 1)**2 - (Ya - 1)**2)
Za = (Z1a - Z2a) * 2

# imshow causes NotImplementedError: Axes3D currently only supports the aspect argument 'auto'. You passed in 'equal'.
ax3d.set_aspect('auto') # does not help
im = ax3d.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
               origin='lower', extent=[0, 1, 0, 1],
               vmax=abs(Za).max(), vmin=-abs(Za).max(),
               aspect='auto' # makes imshow pass and draw - but the drawing is not connected to 3d rotation
               )

ax3d.set_xlim(0, 1)
ax3d.set_ylim(0, 1)
ax3d.set_zlim(0, 1)
ax3d.view_init(elev=20., azim=-35)
plt.show()

... so, syntactically, it can be "coaxed" - unfortunately, the result is not a "part" of the 3D plot, in the sense that it is not on the xy plane at z=0, and it does not rotate with the 3D view as the rest of the plot: ...因此,从语法上讲,它可以被“哄骗”-不幸的是,结果不是 3D plot 的“一部分”,因为它不在 z=0 的 xy 平面上,并且不旋转将 3D 视为 plot 的 rest 视图:

带有 imshow 的 3d 绘图

So, I was thinking - is there a way/a "hack" of sorts, so that I could "extract" 2D Axes matplotlib object for the xy plane at z=0 of the 3D plot, - and then use that Axes object to pass as input to the library, which will proceed as usual (but the ultimate results of its plot will be a part of the 3D plot)? So, I was thinking - is there a way/a "hack" of sorts, so that I could "extract" 2D Axes matplotlib object for the xy plane at z=0 of the 3D plot, - and then use that Axes object to作为输入传递给库,它将照常进行(但其 plot 的最终结果将成为 3D 图的一部分)? Basically, as in the following pseudocode:基本上,如以下伪代码所示:

...
ax2dxy = ax3d.get_2daxes('x', 'y', z=0) # PSEUDO

im = ax2dxy.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
               origin='lower', extent=[0, 1, 0, 1],
               vmax=abs(Za).max(), vmin=-abs(Za).max(),
               )
...

Not quite an answer to the question - and likely, it is not easily possible to "extract" "plottable" 2D axes from 3D ones - but in the below example I've attempted it, and I couldn't get it to do much.不是这个问题的完全答案——而且很可能,从 3D 轴“提取”“可绘制”二维轴并不容易——但在下面的示例中,我已经尝试过了,但我无法让它做太多事情.

However, I also tried plotting 2D imshow on "virtual" 2D axes - and reusing that data for 3D plot surface - and it seems to work:但是,我也尝试在“虚拟”2D 轴上绘制 2D imshow - 并将该数据重用于 3D plot 表面 - 它似乎有效:

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import inspect

fig = plt.figure()
ax3d = fig.add_subplot(projection='3d')
#~ ax3d = fig.add_subplot()

x = np.linspace(0, 1, 100)
y = np.sin(x * 2 * np.pi) / 2 + 0.5
#ax3d.plot(x, y, zs=0, zdir='z', label='curve in (x, y)') # works

# syntax as for 2d plot:
ax3d.plot(x, y, label='curve in (x, y)') # works

# https://matplotlib.org/stable/gallery/images_contours_and_fields/image_demo.html
delta = 0.025
xa = ya = np.arange(0.0, 1.0, delta)
Xa, Ya = np.meshgrid(xa, ya)
Z1a = np.exp(-Xa**2 - Ya**2)
Z2a = np.exp(-(Xa - 1)**2 - (Ya - 1)**2)
Za = (Z1a - Z2a) * 2

#print(inspect.getsourcefile(ax3d.plot3D)) # /mingw64/lib/python3.8/site-packages/mpl_toolkits/mplot3d/axes3d.py
#print(inspect.getsource(ax3d.plot3D)) # def plot! plot3D = plot
#print(inspect.getsourcefile(matplotlib.axes.Axes)) # /mingw64/lib/python3.8/site-packages/matplotlib/axes/_axes.py
# imshow in /mingw64/lib/python3.8/site-packages/matplotlib/axes/_axes.py
print( ax3d.xaxis, ax3d.yaxis , ax3d._position ) # ax3d._position is rect, Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=0.88)
newax = matplotlib.axes.Axes(fig, (0,0,1,1) )
print(newax._position) # Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0)
newax.xaxis = ax3d.xaxis
newax.yaxis = ax3d.yaxis

# imshow causes NotImplementedError: Axes3D currently only supports the aspect argument 'auto'. You passed in 'equal'.
ax3d.set_aspect('auto') # does not help
#im = ax3d.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
#               origin='lower', extent=[0, 1, 0, 1],
#               vmax=abs(Za).max(), vmin=-abs(Za).max(),
#               aspect='auto' # makes imshow pass and draw - but the drawing is not connected to 3d rotation
#               )
imB = newax.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
               origin='lower', extent=[0, 1, 0, 1],
               vmax=abs(Za).max(), vmin=-abs(Za).max(),
               ) # passes, but does not show anything
#imB.axes = ax3d # ValueError: Can not reset the axes.  You are probably trying to re-use an artist in more than one Axes which is not supported
#~ print(newax.images) # [<matplotlib.image.AxesImage object at 0x000002505f06f670>]
#~ print(imB._A) # is there
fig.canvas.draw() # call to create fig.canvas.renderer
im, l, b, trans = imB.make_image(fig.canvas.renderer, unsampled=True)
#print(im)
ax3d.set_axisbelow(False)
ax3d.plot_surface(Xa, Ya, np.zeros(Xa.shape), rstride=1, cstride=1, facecolors=np.divide(im, 255.0), shade=False, zorder=-100)
# ax3d.grid(True, which='major')

ax3d.set_xlim(0, 1)
ax3d.set_ylim(0, 1)
ax3d.set_zlim(0, 1)
ax3d.view_init(elev=20., azim=-35)
plt.show()

The code above produces:上面的代码产生:

在此处输入图像描述

... which looks decent... ...看起来不错...

Now I just wish I knew how I could control the z-order (plot_surface below, gridlines and sinusoid on top of it) - but I posted a separate Q for that ( How to draw Axes3D grid lines over plot_surface() in Matplotlib? )现在我只希望我知道如何控制 z 顺序(下面的 plot_surface,网格线和正弦曲线) - 但我为此发布了一个单独的 Q( 如何在 Matplotlib 中的 plot_surface() 上绘制 Axes3D 网格线?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM