OK, I actually have managed to hack up a modification to the 3d plotting base.pyx file, so that I can make it generate an animation in jmol... however, it's somewhat clunky, and produces a lot of redundant script content in the resulting file. However, using it, I have managed to get an animation running in the notebook.
In order to get it to work, since I'm no expert in coding complex programs like Sage by any means, I basically just added a bit of extra code to the "show" and "export_jmol" functions for the Graphics3d(SageObject) class. The resulting command to make it produce an animation in jmol ends up being: P[0].show(moreframes=P[1:]) Here, P is the list of "frames" (so, P[0] may be given from "P[0] = sphere((1,0,1)) + plot3d(x^2+y^2,(x,-1,1),(y,-1,1))", for instance). The "moreframes" option lets you put in extra frames - if there are no extra frames (moreframes=[] is the default), it just performs the same operations it always has. Unfortunately, my understanding of the code has reached its limit, at this point, so I cannot identify the locations of the other changes I need to make. Notably, the code really requires a way to input the x, y, and z limits of the plot box, so that the frames can be matched up even when limits of the plots don't line up, and it also needs fine-tuning to get rid of the excess fluff in the resulting jmol script file. Here's a sample set of notebook instructions to be tested with the code: x,y=var('x y') P=[plot3d(cos(2*pi*sqrt((x^2+y^2)/2)+t),(x,-1,1), (y,-1,1),color='blue',adaptive=True,aspect_ratio=(1,1,1)) +cube((0,0,1.5),color='red').rotate((0,0,1),t/4) for t in srange(0,2*pi,pi/12)] P[0].show(moreframes=P[1:]) Here's the modified pieces of code themselves, with the function descriptions removed to save space - export_jmol: def export_jmol(self, filename='jmol_shape.jmol', force_reload=False, zoom=100, spin=False, background=(1,1,1), stereo=False, mesh=False, dots=False, perspective_depth = True, orientation = (-764,-346,-545,76.39), moreframes = [1, 1, 1, 1, 1, []], **ignored_kwds): # orientation chosen to look same as tachyon render_params = self.default_render_params() render_params.mesh = mesh render_params.dots = dots render_params.output_file = filename render_params.force_reload = render_params.randomize_counter = force_reload render_params.output_archive = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED, True) # Render the data all = flatten_list([self.jmol_repr(render_params), ""]) numextraframes=len(moreframes[5]) if numextraframes: all_otherframes = [] frame = moreframes[0] axes = moreframes[1] frame_aspect_ratio = moreframes[2] aspect_ratio = moreframes[3] zoom_other = moreframes[4] moreframes=moreframes[5] for framenum in xrange(numextraframes): MF=moreframes[framenum]._prepare_for_jmol(frame, axes, frame_aspect_ratio, aspect_ratio, zoom_other) all_otherframes.append(flatten_list([MF.jmol_repr(render_params), ""])) f = StringIO() if render_params.atom_list: # Load the atom model f.write('data "model list"\n') for framenum in xrange(numextraframes+1): f.write('%s\nempty\n' % (len(render_params.atom_list) + 1)) for atom in render_params.atom_list: f.write('Xx %s %s %s\n' % atom) f.write('Xx 5.5 5.5 5.5\n') # so the zoom fits the box f.write('end "model list"; show data\n') f.write('select *\n') f.write('wireframe off; spacefill off\n') f.write('set labelOffset 0 0\n') # Set the scene background color f.write('background [%s,%s,%s]\n'%tuple([int(a*255) for a in background])) if spin: f.write('spin ON\n') else: f.write('spin OFF\n') if stereo: if stereo is True: stereo = "redblue" f.write('stereo %s\n' % stereo) if orientation: f.write('moveto 0 %s %s %s %s\n'%tuple(orientation)) f.write('centerAt absolute {0 0 0}\n') f.write('zoom %s\n'%zoom) f.write('frank OFF\n') # jmol logo if perspective_depth: f.write('set perspectivedepth ON\n') else: f.write('set perspectivedepth OFF\n') # Put the rest of the object in f.write("\n".join(all)) if numextraframes: for framenum in xrange(numextraframes): f.write('\nframe 1.%s\n'%(framenum+2)) f.write("\n".join(all_otherframes[framenum])) # Make sure the lighting is correct f.write("isosurface fullylit; pmesh o* fullylit; set antialiasdisplay on;\n") if numextraframes: f.write("frame 1.1;animation mode loop 0 0;animation fps 10;animation on\n") render_params.output_archive.writestr('SCRIPT', f.getvalue()) render_params.output_archive.close() And show: def show(self, moreframes=[], **kwds): opts = self._process_viewing_options(kwds) viewer = opts['viewer'] verbosity = opts['verbosity'] figsize = opts['figsize'] aspect_ratio = opts['aspect_ratio'] frame_aspect_ratio = opts['frame_aspect_ratio'] zoom = opts['zoom'] frame = opts['frame'] axes = opts['axes'] import sage.misc.misc if 'filename' in kwds: filename = kwds['filename'] del kwds['filename'] else: filename = sage.misc.misc.tmp_filename() from sage.plot.plot import EMBEDDED_MODE, DOCTEST_MODE ext = None # Tachyon resolution options if DOCTEST_MODE: opts = '-res 10 10' filename = sage.misc.misc.SAGE_TMP + "/tmp" elif EMBEDDED_MODE: opts = '-res %s %s'%(figsize[0]*100, figsize[1]*100) filename = sage.misc.misc.graphics_filename()[:-4] else: opts = '-res %s %s'%(figsize[0]*100, figsize[1]*100) if DOCTEST_MODE or viewer=='tachyon' or (viewer=='java3d' and EMBEDDED_MODE): T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom) tachyon_rt(T.tachyon(), filename+".png", verbosity, True, opts) ext = "png" import sage.misc.viewer viewer_app = sage.misc.viewer.browser() if DOCTEST_MODE or viewer=='java3d': f = open(filename+".obj", "w") f.write("mtllib %s.mtl\n" % filename) f.write(self.obj()) f.close() f = open(filename+".mtl", "w") f.write(self.mtl_str()) f.close() ext = "obj" viewer_app = os.path.join(sage.misc.misc.SAGE_LOCAL, "bin/ sage3d") if DOCTEST_MODE or viewer=='jmol': # Temporary hack: encode the desired applet size in the end of the filename: # (This will be removed once we have dynamic resizing of applets in the browser.) base, ext = os.path.splitext(filename) fg = figsize[0] #if fg >= 2: # fg = 2 filename = '%s-size%s%s'%(base, fg*100, ext) ext = "jmol" archive_name = "%s.%s.zip" % (filename, ext) if EMBEDDED_MODE: # jmol doesn't seem to correctly parse the ?params part of a URL archive_name = "%s-%s.%s.zip" % (filename, randint(0, 1 << 30), ext) T = self._prepare_for_jmol(frame, axes, frame_aspect_ratio, aspect_ratio, zoom) T.export_jmol(archive_name, force_reload=EMBEDDED_MODE, zoom=zoom*100, moreframes=[frame, axes, frame_aspect_ratio, aspect_ratio, zoom, moreframes], **kwds) viewer_app = os.path.join(sage.misc.misc.SAGE_LOCAL, "bin/ jmol") # We need a script to load the file f = open(filename + '.jmol', 'w') f.write('set defaultdirectory "%s"\n' % archive_name) f.write('script SCRIPT\n') f.close() if viewer == 'canvas3d': T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom) data = flatten_list(T.json_repr(T.default_render_params())) f = open(filename + '.canvas3d', 'w') f.write('[%s]' % ','.join(data)) f.close() ext = 'canvas3d' if ext is None: raise ValueError, "Unknown 3d plot type: %s" % viewer if not DOCTEST_MODE and not EMBEDDED_MODE: if verbosity: pipes = "2>&1" else: pipes = "2>/dev/null 1>/dev/null &" os.system('%s "%s.%s" %s' % (viewer_app, filename, ext, pipes)) Hopefully, someone more competent at coding, etc, can improve on what I've done, and make a function suitable for inclusion in the real package. Right now, it would undoubtedly be too glitchy and too fiddly to be official. -- To post to this group, send an email to sage-devel@googlegroups.com To unsubscribe from this group, send an email to sage-devel+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sage-devel URL: http://www.sagemath.org