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