# Multiple 3D plots in one panel -> graphics_array and 3D?

Hello, I am new to Sage and trying to learn as quickly as I can (I am a former Maple user). I have been searching the reference docs, google group and the Net to find a way for putting multiple 3D plots in one panel, in a similar way as with graphics_array for 2D plots. Actually, I have not initially seen that graphics_array was only for 2D plots, and spent some time trying to make it work with my plots. When trying this, I was getting "raise TypeError, "every element of array must be a Graphics object" since the elements of my array are of type <class 'sage.plot.plot3d.base.graphics3dgroup'="">. So, may question is the following: is there any trick that would allow me a to build a panel of 3D plots in a similar way as with graphics_array? I would be very grateful if you could give me some hints on this problem.

Regards,

Murat

edit retag close merge delete

This would be a good idea to have without the tricks below, as well. I wonder if Tachyon and/or Jmol even support these things in theory?

( 2011-01-26 13:26:02 -0500 )edit

I don't understand Jmol at all, but we should be able to get Tachyon to do it, whether working with the Tachyon strings or simply by joining the output images into a grid manually.

( 2011-01-26 16:16:21 -0500 )edit

Sort by » oldest newest most voted

Okay, just to illustrate the non-Jmol approach I had in mind:


# matplotlib-based version
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
var("x y alpha")

Fp1_11 = 5*x+3*y
Fp1_12 = (2*(x+y)^(1/3)*cos(alpha)+(1+alpha/10)^((y)))*20
Fp1_21 = sin(x*y+alpha)
Fp1_22 = alpha*((x^2+y^2))^(1/2)
fns = [Fp1_11, Fp1_12, Fp1_21, Fp1_22]

@interact
def fplot(alpha=(1.0,5)):
fig = plt.figure()
for i, fn in enumerate(fns, 1):
xx = np.linspace(-10, 10, 50)
yy = xx
X, Y = np.meshgrid(xx, yy)
# the next line is ugly, but it works
Z = np.array(list(np.float(fn.subs(x=x_,y=y_,alpha=alpha)) for x_, y_ in zip(X.flatten(), Y.flatten())))
Z = Z.reshape(X.shape)
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
cset = ax.contour(X, Y, Z, zdir='z', offset=-100)
#cset = ax.contour(X, Y, Z, zdir='x', offset=-10)
#cset = ax.contour(X, Y, Z, zdir='y', offset=10)
ax.set_xlabel('X')
ax.set_xlim3d(-10, 10)
ax.set_ylabel('Y')
ax.set_ylim3d(-10, 10)
ax.set_zlabel('Z')
ax.set_zlim3d(-100, 100)
plt.savefig("fplot.png")


gives something like

where if you adjust alpha the plots change. No rotation, and it's not that speedy, but it's functional. Also note that I didn't bother checking that my limits were sensible, or give enough space to the ticks, etc. [Update: looks like I forgot an import. Also some minor edits for aesthetic reasons.]

more

Thanks a lot for this very elaborate solution, and the time you have dedicated to propose it! It effectively seems to solve my problem. I will delve into matplotlib docs to understand your tricks, they are very impressing.

( 2011-01-26 10:29:31 -0500 )edit

It was actually pretty quick, because I just went to http://matplotlib.sourceforge.net/gallery.html and found a 3d example which looked nice (http://matplotlib.sourceforge.net/examples/mplot3d/contour3d_demo3.html), put an @interact on it, and turned your Sage expressions into something numpy would like.

( 2011-01-26 16:18:59 -0500 )edit

I looked around too, and I couldn't see a way to convert a 3D graphics object to a plain graphics object. I did, however, notice the translate function, which could provide a workaround: you could translate your objects by different amounts so that .show() results in something like a graphics array. This is even better, in some ways, because it remains a 3D object, which you can render in various different ways. Here's an example:

sage: graphiclist = [dodecahedron(),sphere(color='red'),sphere(color='yellow')]
sage: h = sum(graphiclist[i].translate((2*i,0,0)) for i in range(len(graphiclist)))
sage: h.show()


And the result is:

Of course translating by multiples of (2,0,0) worked out because each of the 3D graphics I started with was in the cube [-1,1]^3, but you could write a function to determine the bounding boxes of your objects and translate them by the maximum box-width.

more

I have the same problem, but I have found a trivial workaround that works in 6.4.1. I generate two 3D plots and I want to be able to manipulate them interactively, side by side. However, as you experienced, I saw only the second plot. My workaround still doesn't let you have the plots side by side horizontally, but they are side by side vertically. In my notebook worksheet I generate both 3D graphics objects in cell_1, but then, I apply show() on one of them in cell_2 and the other one in cell_3. I have then two interactive Jmol 3D plots right on top of each other and I can interactively manipulate them separately.

Of course, I can also export the plots as Jmol scripts (using the right-click Jmol menu) and then use the standalone Jmol program to show them side by side in two different Jmol windows.

Hope this helps,

Istvan

more

Hi Niles, Nice trick but it does not seem very practical to me in the general case where I would like to plots many 3D plots by varying some configuration parameters (to check their consequences on the plots). In my actual case, I am trying to analyze the effect of some parameters on 3D plots that are already composed by 4 3D plots (I sum them, like in your example). Also, I would not necessarily have the same axes between cases). I need a trick for really generating an array in order to compare many graphics on the screen. With you trick, I can only arrange them if they are not very complicate (in which case, it is indeed quite easy to decode the translated values on the axes). Thank you for your help, I will try if I can get somewhere using it, but I keep hoping that a solution similar to graphics_array exists also for 3D :-)

more

How wedded are you to the Jmol 3d plots? It would be relatively straightforward to produce a grid of matplotlib 3d plots and toss an @interact on it so you could vary alpha, but you wouldn't get the nice Jmol rotations.

( 2011-01-26 06:15:04 -0500 )edit

I too would suggest using matplotlib directly -- that's how GraphicsArray objects are built anyway. If your interest is in comparing the result of different parameters, @DSM is right that using @interact might be another good way to go.

( 2011-01-26 06:45:04 -0500 )edit

Here's a link for @interact: http://wiki.sagemath.org/interact/

( 2011-01-26 06:49:49 -0500 )edit

Thanks to DSM ans NIles for these suggestions. I am quite new to Sage an Python (I am a Maple/Java guy). I will explore matplotlib, since it looks very promising.

( 2011-01-27 06:10:19 -0500 )edit

And thanks a lot Niles for the link! @interact is a very nice tool, also for teaching. I will explore it.

( 2011-01-28 09:57:03 -0500 )edit