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

Reply via email to