簡體   English   中英

從 3D plot 中提取可繪圖 Matplotlib 二維軸?

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

所以,這是我的問題 - 我使用基於 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.

據我所知:

...我必須基本上使用ax.plot_surface在 3D 中具有等效的ax.imshow 但是,這涉及重寫/破解庫,因此所有相應的調用都會被替換。

所以,我試着想出這個簡單的例子,看看在 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()

...因此,從語法上講,它可以被“哄騙”-不幸的是,結果不是 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作為輸入傳遞給庫,它將照常進行(但其 plot 的最終結果將成為 3D 圖的一部分)? 基本上,如以下偽代碼所示:

...
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(),
               )
...

不是這個問題的完全答案——而且很可能,從 3D 軸“提取”“可繪制”二維軸並不容易——但在下面的示例中,我已經嘗試過了,但我無法讓它做太多事情.

但是,我也嘗試在“虛擬”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()

上面的代碼產生:

在此處輸入圖像描述

...看起來不錯...

現在我只希望我知道如何控制 z 順序(下面的 plot_surface,網格線和正弦曲線) - 但我為此發布了一個單獨的 Q( 如何在 Matplotlib 中的 plot_surface() 上繪制 Axes3D 網格線?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM