Well, I've continued to work on it, if only because I thought it would be good to get experience with coding something like this. And I think I've improved things somewhat. I still haven't worked on fixing the problem with *how* it displays when it comes to animations (that is, I haven't sorted out animations where frames aren't all well-aligned already), but I've tweaked things in terms of the interface itself.
So, here are the relevant changes, including all of the previous changes (where necessary to have it run by just issuing the appropriate cython compile command and then "sage -b"): ---- /devel/sagenb-main/sagenb/notebook/cell.py - between "elif F.endswith('.jmol'):" and "elif F.endswith('.jmol.zip'):" lines ---- elif F.endswith('.jmol'): # If F ends in -size500.jmol then we make the viewer applet with size 500. i = F.rfind('-size') if i != -1: if F.endswith('a.jmol'): size = F[i+5:-6] else: size = F[i+5:-5] else: size = 500 if self.worksheet().docbrowser(): jmol_name = os.path.join(self.directory(), F) jmol_file = open(jmol_name, 'r') jmol_script = jmol_file.read() jmol_file.close() jmol_script = jmol_script.replace('defaultdirectory "', 'defaultdirectory "' + self.url_to_self() + '/') jmol_file = open(jmol_name, 'w') jmol_file.write(jmol_script) jmol_file.close() if F.endswith('a.jmol'): script = '<div><script>jmol_applet_anim(%s, "%s? %d");</script></div>' % (size, url, time.time()) else: script = '<div><script>jmol_applet(%s, "%s?%d");</ script></div>' % (size, url, time.time()) images.append(script) elif F.endswith('.jmol.zip'): (This allows the code to recognise the difference between animated jmol applets and normal ones, so that the animated ones have animation control on the interface. ---- /devel/sagenb-main/sagenb/data/sage/js/jmol_lib.js - replacing function jmol_applet(size, url) ---- function jmol_applet(size, url) { var s; jmolSetDocument(cell_writer); jmolSetAppletCssClass('jmol_applet'); cell_writer.write('<table border=0 bgcolor="white"><tr><td>'); jmolApplet(size, "script " + url, jmol_count); s = '</td><td><input type="button" onclick="jmol_image(' + jmol_count + ');return false;" value="Get Image"><br>' + '<form name="MyForm'+jmol_count+'" action="#" onsubmit="jmolResize(100*document.MyForm'+jmol_count+'.jmolResizeText' + jmol_count + '.value,100*document.MyForm'+jmol_count +'.jmolResizeText' + jmol_count + '.value,' + jmol_count + ');return false;"><input type="text" size=2 value=5 name="jmolResizeText' + jmol_count + '"><input type="submit" value="Resize"></form>' + '</td></tr></table>'; cell_writer.write(s); jmol_count += 1; return s; } function jmol_applet_anim(size, url) { var s; jmolSetDocument(cell_writer); jmolSetAppletCssClass('jmol_applet'); cell_writer.write('<table border=0 bgcolor="white"><tr><td>'); jmolApplet(size, "script " + url, jmol_count); s = '</td><td><input type="button" onclick="jmol_image(' + jmol_count + ');return false;" value="Get Image"><br>' + 'Animation: <input type="button" onclick="jmolFrameControl(true,'+jmol_count+')" value="On"><input type="button" onclick="jmolFrameControl(false,'+jmol_count+')" value="Off"><br>' + '<form name="MyForm'+jmol_count+'" action="#" onsubmit="jmolResize(100*document.MyForm'+jmol_count+'.jmolResizeText' + jmol_count + '.value,100*document.MyForm'+jmol_count +'.jmolResizeText' + jmol_count + '.value,' + jmol_count + ');return false;"><input type="text" size=2 value=5 name="jmolResizeText' + jmol_count + '"><input type="submit" value="Resize"></form>' + '</td></tr></table>'; cell_writer.write(s); jmol_count += 1; return s; } function jmolFrameControl(onval,jmolcount) { if (onval) { jmolScript("frame play",jmolcount); } else { jmolScript("frame pause",jmolcount); } } (The first function is the original jmol_applet with some modifications. The second function is the animation-appropriate version, and the third function provides the command functionality for the animations) ---- /devel/sage-main/sage/plot/plot3d/base.pyx - export_jmol function, main comment removed ---- 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): 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,labels=False) 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() ---- /devel/sage-main/sage/plot/plot3d/base.pyx - _prepare_for_jmol function ---- def _prepare_for_jmol(self, frame, axes, frame_aspect_ratio, aspect_ratio, zoom, labels=True): from sage.plot.plot import EMBEDDED_MODE if EMBEDDED_MODE: s = 6 else: s = 3 box_min, box_max = self._rescale_for_frame_aspect_ratio_and_zoom(s, frame_aspect_ratio, zoom) a_min, a_max = self._box_for_aspect_ratio(aspect_ratio, box_min, box_max) return self._transform_to_bounding_box(box_min, box_max, a_min, a_max, frame=frame, axes=axes, thickness=1, labels = labels) # jmol labels are implemented (added "labels=True" in arguments and replaced "labels=True" in return command with "labels=labels") ---- /devel/sage-main/sage/plot/plot3d/base.pyx - show function, main comment removed ---- 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 if moreframes: f = open(filename + 'a.jmol', 'w') else: 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)) (note that for animated jmol animations, this puts an "a" on the end of the filename, prior to the extension. This will cause the function to fail if cell.py and jmol_lib.js are not amended as noted above) I *think* that's all of the code that I changed - if the code doesn't seem to work, let me know, so I can try to find any bits of change that I may have made. So, what does it do? Right now, it adjusts the display to have a "Get Image" button rather than a text link, it adds a resize option (put a "3" into the text box and hit the resize button, and it'll make the applet 300x300, for instance), and if it's an animation, it provides animation "on" and "off" buttons. As a test run, use the following code in the notebook: x,y=var('x y') ps1=[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)] ps1[0].show(moreframes=ps1[1:]) This should create an animated jmol of a rotating red cube above a blue plot that appears like a circular wave rippling out from the centre. To see the animation buttons disappear, and to confirm that the code still works without animation, just remove "moreframes=ps1[1:]" from the show command, and it will show the first frame only, with the resize and get image options, but no animation option. I'm still working on it, though. I'm certainly getting a better feel of how sage's code works, so I may be able to look into "interact" jmol plots that don't require reloading of the applet. But the next step is to fix it so that it uses an appropriate scaling to keep the entire plot stable, rather than having each frame generated with its own bounds. On Sep 30, 3:09 pm, Jonathan <gu...@uwosh.edu> wrote: > It would definitely be good to figure out an interactive version. I > keep intending to work on this but have had no time. It would make > interacts work much better. The problem is that by default the server > deletes everything in a cell output div when the cell is > recalculated. I was thinking about putting the jmol in a separate > "applet" div that would only be deleted if the recalculation did not > generate Jmol data. This requires reworking both the python of plot3D > and the notebook javascript. If you've got time to work on this now, > I'm willing to provide insight into Jmol, just not much coding time. > I personally think the interactive version may be higher priority. > > Jonathan -- 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