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. 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:
- Plotting a imshow() image in 3d in matplotlib
- Creating intersecting images in matplotlib with imshow or other function
... I have to basically use ax.plot_surface
to have the equivalent of ax.imshow
in 3D. 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:
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:
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)? 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(),
)
...