#!/usr/bin/python# -*- coding: utf8 -*-import osimport inspectfrom math import *import matplotlib as mplimport matplotlib.pyplot as pltfrom matplotlib import animation# settingsmpl.rcParams['path.snap'] = Falsefname = 'driven-pendulums-resonance-animation'size = 220, 136nframes = 100susp = 16f0 = 3.0pendulums = [{'x0':29, 'y0':117.5, 'f':2.0, 'A':4, 'D':0.05, 'l':89, 'phi0':0.},{'x0':110, 'y0':117.5, 'f':3.0, 'A':4, 'D':0.05, 'l':89, 'phi0':0.},{'x0':191, 'y0':117.5, 'f':4.0, 'A':4, 'D':0.05, 'l':89, 'phi0':0.}]for p in pendulums: D = p['D'] eta = p['f'] / f0 # use harmonic approximation p['Arel'] = 1.0 / sqrt((1. - eta**2)**2 + (2.*eta*D)**2) p['Phaserel'] = atan2(2 * eta * D, 1.0 - eta**2)def animate(nframe, saveframes=False): print nframe, nframes t = float(nframe) / nframes plt.clf() fig.gca().set_position((0, 0, 1, 1)) plt.xlim(0, size[0]) plt.ylim(0, size[1]) plt.axis('off') for p in pendulums: dxSusp = p['A'] * sin(p['phi0'] + 2*pi*p['f']*t) dsPend = p['A'] * sin(p['phi0'] + 2*pi*p['f']*t - p['Phaserel']) * p['Arel'] PhiPend = (dsPend - dxSusp) / p['l'] dxPend = dxSusp + p['l'] * sin(PhiPend) dyPend = -p['l'] * cos(PhiPend) xSusp = p['x0'] + dxSusp xPend = p['x0'] + dxPend yPend = p['y0'] + dyPend # draw pendulum s = 0.72 plt.plot([p['x0'] - susp, p['x0'] + susp], [p['y0'], p['y0']], '-k', lw=3) plt.plot([xSusp, xPend], [p['y0'], yPend], '-k', lw=1.5) plt.plot([xSusp], [p['y0']], '.k', markersize=12) plt.plot([xPend], [yPend], 'o', color='#aaaaaa', markersize=14, markeredgewidth=1.5) if saveframes: # export frame dig = int(ceil(log10(nframes))) fsavename = ('frame{:0' + str(dig) + '}.svg').format(nframe) fig.savefig(fsavename) with open(fsavename) as f: content = f.read() content = content.replace('pt"', 'px"').replace('pt"', 'px"') with open(fsavename, 'w') as f: f.write(content)fig = plt.figure(figsize=(size[0]/72., size[1]/72.))#anim = animation.FuncAnimation(fig, animate, frames=nframes)#anim.save(fname + '.gif', writer='imagemagick', fps=25)# use imagemagick on svgs rather than builtin imagemagick support# this avoids snapping and offers some custom settingsos.chdir(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))for i in range(nframes): animate(i, True)os.system('convert -loop 0 -delay 4 frame*.svg +dither -posterize 16 ' + fname + '.gif')for i in os.listdir('.'): if i.startswith('frame') and i.endswith('.svg'): os.remove(i)