Ask Your Question

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

asked 2011-01-25 09:58:40 -0500

myildi gravatar image

updated 2011-01-25 21:26:42 -0500

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.



edit retag flag offensive 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?

kcrisman gravatar imagekcrisman ( 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.

DSM gravatar imageDSM ( 2011-01-26 16:16:21 -0500 )edit

4 answers

Sort by ยป oldest newest most voted

answered 2011-01-26 06:50:02 -0500

DSM gravatar image

updated 2011-01-26 16:03:52 -0500

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]

def fplot(alpha=(1.0,5)):
    fig = plt.figure()
    fig.subplots_adjust(wspace=0.05, hspace=0.05,left=0.05, right=0.95)
    for i, fn in enumerate(fns, 1):
        ax = fig.add_subplot(2,2,i,projection='3d')
        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_xlim3d(-10, 10)
        ax.set_ylim3d(-10, 10)
        ax.set_zlim3d(-100, 100)

gives something like

image description

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.]

edit flag offensive delete link 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.

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

It was actually pretty quick, because I just went to and found a 3d example which looked nice (, put an @interact on it, and turned your Sage expressions into something numpy would like.

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

answered 2011-01-26 01:16:15 -0500

niles gravatar image

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)))

And the result is:

image description

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.

edit flag offensive delete link more

answered 2015-02-22 21:42:42 -0500

ikol gravatar image

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,


edit flag offensive delete link more

answered 2011-01-26 04:46:14 -0500

myildi gravatar image

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 :-)

edit flag offensive delete link 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.

DSM gravatar imageDSM ( 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.

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

Here's a link for @interact:

niles gravatar imageniles ( 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.

myildi gravatar imagemyildi ( 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.

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

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools


Asked: 2011-01-25 09:58:40 -0500

Seen: 2,162 times

Last updated: Feb 22 '15