
Hi Jonathan, I just made an animation in Chimera that does more complicated motions than rock and roll. It is the myosin thick filament analysis animation on the Chimera animations page: http://www.cgl.ucsf.edu/chimera/animations/animations.html I created this with a Python script because it does many transitions for which there is no Chimera command. I include my Python script below (700 lines). It is messy but maybe you can mine it. The part most relevant to your question is the move_model() function: move_model(model_name, from_matrix, to_matrix, relative_to, fraction) It moves a model a certain fraction (0-1) of the way from one position to another position, with the positions given relative to a second model using 3 by 4 matrices. It uses positions relative to another model instead of absolute positions because I was using this to fit a crystal structure into a density map. If I later changed my viewpoint in Chimera I wanted it to still work. To get these matrices I used the write_relative_transforms() function called via a keyboard shortcut "wt". That wrote the matrices to the Chimera reply log which I then copied into my script. I think using the "matrixset <filename>" command for doing a smooth transition is unwieldy. Every matrix would have to be in a separate file. Two alternatives are to use a Python script to produce an animation as I did, or to add a command to Chimera that does the smooth interpolation -- a Chimera command version of the above move_model() function. Using Python requires more expertise. I think introducing the new Chimera command is the way to go. There should also be a command to print the current matrices like my "wt" shortcut. These two commands would be more usable versions of matrixget/matrixset for the purpose of making smooth transitions. If you would like I can write some Python code for these two commands that you can drop into your existing Chimera. Tom ---- Script that plays through myosin thick filament animation. This will not run for you since you do not have the data files. # ----------------------------------------------------------------------------- # Script that runs through animation of myosin thick filament density map. # # ----------------------------------------------------------------------------- # def play(): from os.path import join, dirname, realpath import myosinmovie dir = join(dirname(realpath(myosinmovie.__path__[0])), 'data') map_name = 'myosin2.mrc' map_color = (.7, .7, .7, 1) # gray map_color_t = map_color[:3] + (.7,) # transparent head_color = (1,.7,0,1) # orange subfil_color = (.980, .502, .447, 1) # salmon map2 = map_name + ' <1>' pdb1i84 = '1i84-notail.pdb' tail = 'tail.cmm' tailname = 'myosin tail' subfil = map(lambda s: 'subfilament%d.mrc' % s, range(13)) sfpaths = map(lambda sf: join(dir,sf), subfil) sfeven = subfil[::2] sfodd = subfil[1::2] sfocolor = (.7,.7,.3,1) # yellow sf = 'subfilament9.mrc' fc = 'subfilament0.mrc' fc_color = (.8,.6,.4,1) sfo = filter(lambda s: s != fc, subfil) sf11= sfo[sfo.index(sf)+1:] + sfo[:sfo.index(sf)] sf11r = list(sf11) sf11r.reverse() show_caption = '2dlabel change caption text "%s" color black xpos %f ypos .95 visibility show' show_caption2 = '2dlabel change caption text "%s" color black xpos %f ypos .97 size %d visibility show' caption_fade = '2dlabel change caption visibility hide frames 5' caption_fade_delay = 15 # Position of 1i84 close to eye. mt1 = ((-0.099649198789025978, 0.74111595074467951, 0.66394064850144952, -1008.6712224936828), (0.5863861489863087, -0.49534104948956315, 0.64092786565072368, -966.59941472485207), (0.80387892213797729, 0.45319354832389019, -0.38521978960054565, -124.9728590025926)) # Position of 1i84 to right of filament mt2 = ((0.073345968301112466, 0.62005697667643067, 0.78112080666748807, -140.8575902922845), (0.55558955434916146, -0.6758394649115651, 0.48431525349317678, -344.80539061238528), (0.82821531984869479, 0.39845998963878676, -0.3940672793127869, -124.7725634028488)) # Position of 1i84 hand fit mt3 = ((-0.011137186324278462, 0.57090152267568484, 0.8209430031904611, -117.86331645089319), (0.64007270897455237, -0.62668401091081916, 0.4444930569702038, -121.04296346830751), (0.76823361701162263, 0.53041361396114306, -0.35843898758075837, -132.97686247292341)) # Position of 1i84 computationally optimized mt4 = ((0.067350631154440885, 0.35912771983826075, 0.93085507643610677, -94.436118828488432), (0.78371645512883981, -0.59642399619826492, 0.1733981969894075, -121.53565912095156), (0.61745640367833565, 0.71784796273491214, -0.32162383610975182, -142.36032454320915)) # Position of 1i84 backwards fit by hand mt5 = ((0.040717288582604753, -0.68893892247763189, -0.72367483271552346, -41.989737858895808), (0.015968304931079015, 0.72463165183091871, -0.68895136432292714, -177.57106879703232), (0.9990430999952451, 0.016496371120766563, 0.040506222876700852, -112.28404385096682)) # Position of 1i84 backwards fit optimized mt6 = ((0.075798794154165489, -0.41125005975496942, -0.90836552728310249, -50.078309013435728), (0.26520245923950292, 0.88648910828917349, -0.37921592331808268, -190.88698833314771), (0.96120871740641978, -0.21215666201229763, 0.17625933264853111, -90.233284436572831)) # Position of 1i84 forward and right of filament for float in. mt7 = ((0.067350631154428922, 0.3591277198382945, 0.93085507643609822, -58.351913517245023), (0.78371645512883925, -0.59642399619825837, 0.17339819698943759, -483.01076846693888), (0.61745640367833865, 0.71784796273490414, -0.3216238361097708, -244.42186962044607)) # Position of subfilament tilted out. sfmt1 = ((0.97415635283527546, -0.025843647164727198, -0.22439141278557448, -103.49134676516807), (-0.025843647164727226, 0.97415635283527724, -0.22439141278557453, -107.25466846571966), (0.22439141278557431, 0.2243914127855747, 0.94831270567054426, -10.644281176985976)) # Position of subfilament displaced out. sfmt2 = ((0.9999957248565281, -4.2751434744161616e-06, -0.0029240811200199287, -214.50933693143944), (-4.275143474582695e-06, 0.9999957248565301, -0.0029240811200196941, -218.27265863199113), (0.0029240811200198034, 0.0029240811200199092, 0.99999144971304987, 11.974816324109245)) # Positioning of tail forward and right of filament for float in. tmt1 = ((1.0000000000000038, 0.0, -1.262386893235071e-32, 36.084205311245618), (0.0, 1.0000000000000038, -1.4293409452891395e-31, -361.47510934598643), (-1.262386893235071e-32, -1.4293409452891395e-31, 1.0, -102.06154507723714)) from PDBmatrices import identity_matrix identity = identity_matrix() from chimera.statusline import show_status_line from Accelerators.standard_accelerators import sphere_representation from Accelerators.standard_accelerators import select_all, clear_selection p = Player() sequence = [ (show_status_line, {'show': False}), # Needed to set small window widths. (set_window, {'size': (440,800), 'color':(1,1,1)}), 2, # Need window size update before setting label background. (label_background, {'position': (0,.92), 'size': (1.0,.11), 'color': 'white'}), '2dlabel create caption text "Myosin thick filament, EM map" color black xpos .1 ypos .95', 'open %s' % join(dir,map_name), 'scale 2', (set_center_of_rotation, {'center': (0,0,0)}), 'turn x -90', (set_map, {'map_name': map_name,'threshold': 0.2, 'style': 'surface'}), 'roll y 0.5 90', 75, (duplicate_map, {'map_name': map_name}), (set_map, {'map_name': map2, 'threshold': 0.15, 'style': 'surface', 'bounds':((0,0,41), (99,99,81),(1,1,1))}), (set_map, {'map_name': map2, 'color': (.2,.2,.6,.0)}, {'color': (.2,.2,.6,.5)}, 30), 30, caption_fade, caption_fade_delay, show_caption % ('Symmetry: 90 degree rotation', .1), (move_only, {'model_name': map2}), 'turn y 3 30', 50, caption_fade, caption_fade_delay, show_caption % ('Symmetry: 43.5 nm translation', .1), 'move y 7.5 58', 80, caption_fade, caption_fade_delay, show_caption % ('Four strand helix', .3), 'turn y -1 30', 'move y -5 29', 45, 'turn y -1 30', 'move y -5 29', 45, 'turn y -1 30', 'move y -5 29', 60, (set_map, {'map_name': map2, 'color': (.2,.2,.6,.5)}, {'color': (.2,.2,.6,0)}, 20), 20, (unshow_map, {'map_name': map2}), (move_only, {'model_name': 'all'}), caption_fade, caption_fade_delay, show_caption % ("Myosin heads, 'J' shapes on surface", .02), (cylinder_color, {'model_name': map_name, 'colormap':((120, map_color),(121, map_color))}, {'colormap':((120, map_color),(121, head_color))}, 30), 60, caption_fade, caption_fade_delay, show_caption % ('Myosin tails make filament body', .1), (cylinder_color, {'model_name': map_name, 'colormap':((120, map_color),(121, head_color))}, {'colormap':((110, subfil_color),(111, map_color))}, 30), 60, caption_fade, caption_fade_delay, show_caption % ('Fit myosin model, hand placement', .05), 'open %s' % join(dir,pdb1i84), 'rainbow chain', (sphere_representation, {}), (place_model, {'model_name': pdb1i84, 'matrix': mt1, 'relative_to':map_name}), (set_near_far, {'near': 577.7, 'far':-568.161}, {'near': 1700, 'far':-1700}, 10), 30, (move_model, {'model_name': pdb1i84, 'relative_to':map_name, 'from_matrix': mt1, 'to_matrix': mt2, 'fraction':0}, {'fraction':1}, 30), 30, (move_model, {'model_name': pdb1i84, 'relative_to':map_name, 'from_matrix': mt2, 'to_matrix': mt3, 'fraction':0}, {'fraction':1}, 30), 20, 'scale 1.02 25', 30, caption_fade, caption_fade_delay, show_caption % ('Computational fit optimization', .1), (select_all, {}), (cylinder_color, {'model_name': map_name, 'colormap':((110, subfil_color),(111, map_color))}, {'colormap':((110, subfil_color),(111, map_color_t))}, 30), (move_model, {'model_name': pdb1i84, 'relative_to':map_name, 'from_matrix': mt3, 'to_matrix': mt4, 'fraction':0}, {'fraction':1}, 30), 30, (clear_selection, {}), 30, caption_fade, caption_fade_delay, show_caption % ('Flip and fit - wrong orientation', .1), (move_model, {'model_name': pdb1i84, 'relative_to':map_name, 'from_matrix': mt4, 'to_matrix': mt5, 'fraction':0}, {'fraction':1}, 15), 15, (move_model, {'model_name': pdb1i84, 'relative_to':map_name, 'from_matrix': mt5, 'to_matrix': mt6, 'fraction':0}, {'fraction':1}, 15), 45, caption_fade, caption_fade_delay, show_caption % ('Fitting with symmetric copies', .1), (symmetric_copies, {'molecule_name': pdb1i84}), (move_only, {'model_name': pdb1i84}), # Needed for sym copies to work 'rainbow chain', 15, (move_model, {'model_name': pdb1i84, 'relative_to':map_name, 'from_matrix': mt6, 'to_matrix': mt4, 'fraction':0}, {'fraction':1}, 30), 60, (symmetric_copies, {'molecule_name': pdb1i84, 'show': False}), (move_only, {'model_name': 'all'}), (cylinder_color, {'model_name': map_name, 'colormap':((110, subfil_color),(111, map_color_t))}, {'colormap':((110, map_color_t),(111, map_color_t))}, 30), 30, caption_fade, caption_fade_delay, show_caption % ('Myosin tail, hand traced', .2), 'open %s' % join(dir,tail), (show_atom_sequence, {'molecule_name': tailname, 'count': 1}, {'count': 10}, 60), 30, 'scale .975 30', 60, (cylinder_color, {'model_name': map_name, 'colormap':((110, map_color_t),(111, map_color_t))}, {'colormap':((110, map_color),(111, map_color))}, 15), 15, (unshow_model, {'model_name': (pdb1i84, tailname)}), caption_fade, caption_fade_delay, show_caption % ('Filament has 12 subfilaments', .1), (open_models, {'paths': sfpaths}), (set_map, {'map_name': subfil, 'threshold': 0.2, 'style': 'surface', 'color': map_color}), (set_map, {'map_name': fc, 'color': fc_color}), (unshow_map, {'map_name': map_name}), (set_map, {'map_name': sfodd, 'color': map_color}, {'color':sfocolor}, 60), 60, (set_center_of_rotation, {'center': (0, 0, 0)}), 'roll y 3 15', 30, (move_model, {'model_name': sf, 'relative_to': map_name, 'from_matrix': identity, 'to_matrix': sfmt1, 'fraction':0}, {'fraction':1}, 15), 15, (move_model, {'model_name': sf, 'relative_to': map_name, 'from_matrix': sfmt1, 'to_matrix': sfmt2, 'fraction':0}, {'fraction':1}, 15), 25, (set_center_of_rotation, {'center': (275, 10, 257)}), (move_only, {'model_name': sf}), 'roll y 4 90', 90, (move_only, {'model_name': 'all'}), (set_center_of_rotation, {'center': (0, 0, 0)}), (explode, {'model_names': sfo, 'factor': 1.03}, {}, 60), 45, (set_map, {'map_name': sfo, 'opacity': 1.0}, {'opacity':0}, 15), 45, caption_fade, caption_fade_delay, show_caption % ('Filament core, contents unknown', .05), 'roll y -0.5 90', 'scale 1.01 60', 45, (place_model, {'model_name': subfil, 'matrix': identity, 'relative_to':map_name}), (set_map, {'map_name': sf11, 'opacity': 1.0}), (place_model, {'model_name': sf11, 'matrix': identity, 'relative_to':map_name}), (show_model_sequence, {'model_names': sf11r, 'count': 1}, {'count':11}, 45), 45, caption_fade, caption_fade_delay, show_caption2 % (' Animation created with UCSF Chimera.\\nData from John Woodhead and Roger Craig', .05, 18), (set_map, {'map_name': sf, 'style': 'mesh', 'color': sfocolor, 'opacity': 0.0, 'square_mesh': True}, {'opacity':0.4}, 30), (show_model, {'model_name': (pdb1i84, tailname)}), (move_model, {'model_name': pdb1i84, 'relative_to': map_name, 'from_matrix': mt7, 'to_matrix': mt4, 'fraction':0}, {'fraction':1}, 30), (move_model, {'model_name': tailname, 'relative_to': map_name, 'from_matrix': tmt1, 'to_matrix': identity, 'fraction':0}, {'fraction':1}, 30), ] p.play_sequence(sequence) # ----------------------------------------------------------------------------- # class Player: def __init__(self): self.sequence = [] self.delay = 0 self.transitions = [] self.new_frame_handler = None # --------------------------------------------------------------------------- # def play_sequence(self, sequence): self.sequence = sequence self.delay = 0 from chimera import triggers h = triggers.addHandler('new frame', self.new_frame, None) self.new_frame_handler = h # --------------------------------------------------------------------------- # def new_frame(self, trigger_name, call_data, trigger_data): t = 0 while t < len(self.transitions): command, kw_begin, kw_end, frames, cur = self.transitions[t] cur += 1 if cur > frames: del self.transitions[t] continue self.transitions[t] = (command, kw_begin, kw_end, frames, cur) kw = self.interpolate_parameters(kw_begin, kw_end, frames, cur) command(**kw) t += 1 while self.sequence: if self.delay > 0: self.delay -= 1 break s = self.sequence[0] del self.sequence[0] if type(s) is int: self.frame_delay(s) elif type(s) is type(''): run_command(s) elif len(s) == 2: command, kw = s command(**kw) elif len(s) == 4: command, kw_begin, kw_end, frames = s command(**kw_begin) if frames > 1: self.transitions.append((command, kw_begin, kw_end, frames, 1)) if (len(self.sequence) == 0 and len(self.transitions) == 0 and self.new_frame_handler): from chimera import triggers triggers.deleteHandler('new frame', self.new_frame_handler) self.new_frame_handler = None # --------------------------------------------------------------------------- # def frame_delay(self, frames): self.delay = frames # --------------------------------------------------------------------------- # def interpolate_parameters(self, kw_begin, kw_end, frames, cur): kw = {} kw.update(kw_begin) f = float(cur) / frames for key, value in kw_end.items(): kw[key] = self.interpolate_parameter(kw[key], value, f) return kw # --------------------------------------------------------------------------- # def interpolate_parameter(self, v0, v1, f): if type(v0) in (int, float): v = (1-f) * v0 + f * v1 return v elif type(v0) in (tuple, list): v = map(lambda e0, e1: self.interpolate_parameter(e0, e1, f), v0, v1) return v raise TypeError, 'Bad transition parameter of type ' + str(type(v0)) # ----------------------------------------------------------------------------- # def set_window(size = None, color = None): if size != None: set_window_size(*size) if color != None: from Accelerators.standard_accelerators import set_background set_background(color) # ----------------------------------------------------------------------------- # def set_window_size(width, height): from chimera import viewer viewer.windowSize = (width, height) # ----------------------------------------------------------------------------- # def run_command(command): from chimera import runCommand runCommand(command) # ----------------------------------------------------------------------------- # def move_only(model_name): from chimera import openModels mlist = openModels.list() if model_name == 'all': for m in mlist: m.openState.active = True else: for m in mlist: m.openState.active = False for m in filter(lambda m: m.name == model_name, mlist): m.openState.active = True # ----------------------------------------------------------------------------- # def set_map(map_name, style = None, threshold = None, color = None, opacity = None, square_mesh = None, bounds = None): if type(map_name) in (list, tuple): for mname in map_name: set_map(mname, style, threshold, color, opacity, square_mesh, bounds) return dr = find_map(map_name) if style != None: set_map_style(map_name, style) if threshold != None: set_map_threshold(map_name, threshold) if color != None: set_map_color(map_name, color) if opacity != None: set_map_opacity(map_name, opacity) if square_mesh != None: set_map_rendering_option(map_name, 'square_mesh', square_mesh) if bounds != None: set_map_bounds(map_name, bounds) show_map(map_name) # ----------------------------------------------------------------------------- # def set_map_threshold(map_name, threshold): dr = find_map(map_name) dr.components[0].surface_levels = [threshold] show_map(map_name) # ----------------------------------------------------------------------------- # def set_map_bounds(map_name, bounds): dr = find_map(map_name) dr.new_region(bounds) show_map(map_name) # ----------------------------------------------------------------------------- # def set_map_style(map_name, style): dr = find_map(map_name) dr.representation = style show_map(map_name) # ----------------------------------------------------------------------------- # def set_map_color(map_name, rgba): dr = find_map(map_name) for c in dr.components: c.surface_colors = [rgba] * len(c.surface_colors) show_map(map_name) # ----------------------------------------------------------------------------- # def set_map_opacity(map_name, opacity): dr = find_map(map_name) for c in dr.components: c.surface_colors = map(lambda rgba: tuple(rgba[:3]) + (opacity,), c.surface_colors) show_map(map_name) # ----------------------------------------------------------------------------- # def set_map_rendering_option(map_name, option_name, value): dr = find_map(map_name) setattr(dr.rendering_options, option_name, value) show_map(map_name) # ----------------------------------------------------------------------------- # def find_map(map_name): dr = volume_dialog().find_data_by_name(map_name) return dr # ----------------------------------------------------------------------------- # def show_map(map_name): dr = find_map(map_name) volume_dialog().show_data_regions([dr]) # ----------------------------------------------------------------------------- # def unshow_map(map_name): dr = find_map(map_name) dr.unshow() # ----------------------------------------------------------------------------- # def volume_dialog(): import VolumeViewer d = VolumeViewer.volume_dialog() return d # ----------------------------------------------------------------------------- # def set_center_of_rotation(center): from chimera import openModels, Point if center == None: openModels.cofrMethod = openModels.CenterOfModels else: openModels.cofr = Point(*center) openModels.cofrMethod = openModels.Fixed # ----------------------------------------------------------------------------- # def duplicate_map(map_name): dr = find_map(map_name) d = volume_dialog() dr2 = d.duplicate_region(dr) d.show_data_regions([dr2]) # ----------------------------------------------------------------------------- # def cylinder_color(model_name, colormap): model = find_model_by_name(model_name) from SurfaceColor import Color_Map, Cylinder_Color, color_surface values = map(lambda vc: vc[0], colormap) colors = map(lambda vc: vc[1], colormap) color_source = Cylinder_Color() color_source.colormap = Color_Map(values, colors) color_surface(model, color_source, caps_only = False, auto_update = False) # ----------------------------------------------------------------------------- # def find_model_by_name(model_name): from chimera import openModels m = filter(lambda m: m.name == model_name, openModels.list())[0] return m # ----------------------------------------------------------------------------- # def show_model(model_name, show = True): if type(model_name) in (tuple, list): for mname in model_name: show_model(mname, show) return model = find_model_by_name(model_name) model.display = show # ----------------------------------------------------------------------------- # def unshow_model(model_name): show_model(model_name, show = False) # ----------------------------------------------------------------------------- # def place_model(model_name, matrix, relative_to): if type(model_name) in (tuple, list): for mname in model_name: place_model(mname, matrix, relative_to) return model = find_model_by_name(model_name) rmodel = find_model_by_name(relative_to) from PDBmatrices import chimera_xform xf = chimera_xform(matrix) rxf = rmodel.openState.xform xf.premultiply(rxf) model.openState.xform = xf # ----------------------------------------------------------------------------- # def move_model(model_name, from_matrix, to_matrix, relative_to, fraction): model = find_model_by_name(model_name) rmodel = find_model_by_name(relative_to) from PDBmatrices import chimera_xform fxf = chimera_xform(from_matrix) txf = chimera_xform(to_matrix) rxf = rmodel.openState.xform fxf.premultiply(rxf) txf.premultiply(rxf) # Find model center have_box, bbox = model.bbox() c = bbox.center() cf = fxf.apply(c) ct = txf.apply(c) # Determine fractional translation from chimera import Xform, Vector, Point td = Xform.translation((ct-cf)*fraction) # Determine fractional rotation r = fxf.inverse() r.premultiply(txf) axis, angle = r.getRotation() rd = Xform.rotation(axis, fraction*angle) cv = Xform.translation(cf - Point()) cvi = Xform.translation(Point() - cf) xf = Xform() xf.multiply(fxf) # Apply start transform xf.premultiply(cvi) # Shift to put model center at origin xf.premultiply(rd) # Apply fractional rotation xf.premultiply(cv) # Shift center back to from position xf.premultiply(td) # Apply fractional translation model.openState.xform = xf # ----------------------------------------------------------------------------- # def set_near_far(near, far): from chimera import viewer c = viewer.camera c.nearFar = (near, far) # ----------------------------------------------------------------------------- # def symmetric_copies(molecule_name, show = True): m = find_model_by_name(molecule_name) if show: run_command('sym #%d' % m.id) else: run_command('~sym #%d' % m.id) # ----------------------------------------------------------------------------- # def show_atom_sequence(molecule_name, count): m = find_model_by_name(molecule_name) count = int(count) alist = m.atoms for a in alist[:count]: a.display = True for a in alist[count:]: a.display = False # ----------------------------------------------------------------------------- # def show_model_sequence(model_names, count): mlist = map(find_model_by_name, model_names) count = int(count) for m in mlist[:count]: m.display = True for m in mlist[count:]: m.display = False # ----------------------------------------------------------------------------- # def open_models(paths): for path in paths: run_command('open %s' % path) # ----------------------------------------------------------------------------- # def explode(model_names, factor): mlist = map(find_model_by_name, model_names) centers = map(lambda m: m.openState.xform.apply(m.bbox()[1].center()), mlist) n = len(model_names) from chimera import Point, Xform c = Point(centers, [1.0/n]*n) for k in range(len(mlist)): delta = (centers[k] - c) * (factor - 1.0) xf = Xform.translation(delta) mlist[k].openState.globalXform(xf) # ----------------------------------------------------------------------------- # Make a solid background for drawing labels. # This hack uses several labels containing strings of ells. # def label_background(position, size, color): import chimera v = chimera.viewer wsize = v.windowSize fsize = int(size[1] * wsize[1]) # pixels lcount = int(5 * float(wsize[0]) / fsize) ells = 'l' * lcount xstep = (.07 * fsize) / wsize[0] for k in range(4): run_command('2dlabel create bg%d text "%s" color %s xpos %f ypos %f size %d' % (k, ells, color, position[0] + k*xstep, position[1], fsize)) # ----------------------------------------------------------------------------- # def write_relative_transforms(): from chimera import openModels mlist = openModels.list() m0 = mlist[0] print 'Matrices relative to', m0.name xf_ref = m0.openState.xform xf_ref_inv = xf_ref.inverse() for m in mlist[1:]: xf = m.openState.xform xf.premultiply(xf_ref_inv) from PDBmatrices import xform_matrix, is_identity_matrix mt = xform_matrix(xf) if not is_identity_matrix(mt): print m.name, repr(mt) # ----------------------------------------------------------------------------- # from Accelerators import add_accelerator add_accelerator('pm', 'Play myosin thick filament movie', play) add_accelerator('wt', 'Write relative transformation matrices', write_relative_transforms)