Friday, November 25, 2016

Animation in PDF Presentations

I've been teaching an introductory class in physics and wanted to show my students how a standing wave gets constructed. The math & physics of it aren't too hard, really:

  • Start with a wave of a given frequency and wavelength, moving to to right.
  • Add a similar wave, with the same frequency, wavelength, and amplitude, except that it's moving to the left.
  • Add them together, and the combined waves will have a fixed points (nodes) every λ/2, where λ is the common wavelength.

The whole process looks something like this:

Animation of standing wave generated from two traveling waves

The problem is twofold. First, how to construct such an animation, and second, how to get it into a presentation.

To begin, we need to create a moving plot. It turns out that my favorite plotting program, gnuplot, is sort up to the task. Sort of. You'd expect that an animation algorithm would follow a set of rules like this:

  • Draw the picture at time t = 0
  • Increment the time a bit
  • Redraw the picture
  • Repeat after you've gone enough time steps to make you happy

Most codes implement this with some kind of a loop function. Gnuplot doesn't have a loop. However, a file can ask that the program reread the file. So an animation gnuplot script has features like this:

  #! /usr/bin/gnuplot     # Location of the source program YMMV
  dt = 0.01  # Length of a time step (choose yours to fit your needs)
  steps = 1000 # Number of steps you want
  i = 0 # this is a counter. It will be incremented one every time step
  load "realplot.gnu" # The actual plot file

realplot.gnu has the meat of the plotting program. For our traveling sine wave, it looks something like this:

  plot sin(x - i*dt) w l lt 1
  i = i + 1
  if (i < steps) reread

The code plots the curve at a given time, then increments the time by an amount i*dt, and asks to be reread. The program does this until the final time step is completed.

That's fine, this displays a very nice set of curves on the screen. However, it's nothing you can embed in a presentation. We need to save the output in some form. The best way to do this right now is using an animated GIF. Just like a movie, this is a collection of still images played at a pre-determined rate. Gnuplot has this capability. Adding these two lines changes the output from the screen to a file, in this case one named standing_waves.gif:

set term gif enhanced font 'arial' 16 animate delay 0.05 size 1000,750 nooptimize
set output "standing_waves.gif"

Most of this isn't hard to figure out:

  • set term gif tells gnuplot to make the output a gif file
  • enhanced font 'arial' 16 tells gnuplot to use its fancy plotting features, like printing Greek letters, and to use the Arial font with a font size of 16 (some platforms handle this better than others, more on that later)
  • animate delay 0.05 says to do animation, and play the frames 0.05 seconds apart (25 frames/second)
  • size 1000,750 is the size of the GIF, use what fits your presentation best
  • nooptimize is probably best. Optimization tries to save file space by only printing out the parts of a picture which are changing. This works on some platforms (my Linux box, my Mac at work), and fails on others (my Mac at home). So turning optimization off is probably best.
  • set output "standing_waves.gif" just tells gnuplot to output all these frames into a file.

For my project, I then stacked a bunch of plots together. At the risk of boring you, here's the whole thing in detail.

  • The main calling program sets up everything that is going to be fixed throughout the run, sets the time steps, etc. I'm doing this for a fixed number of periods, with a fixed number of time steps per period. For those who need to know, I set the wavelength to be 2π in arbitrary units, and the period to also be 2π, again in arbitrary units.
          #! /usr/bin/gnuplot
          # Settings which remain the same for all graphs
          set samples 2000
          unset ytics
          set xrange [0:7*pi]
          set yrange [-2.6:2.6]
          set xtics ("0" 0,"{/Symbol l}/2" pi,"{/Symbol l}" 2*pi, \
          "3{/Symbol l}/2" 3*pi, "2{/Symbol l}" 4*pi, \
          "5{/Symbol l}/2" 5*pi, "3{/Symbol l}" 6*pi, \
          "7{/Symbol l}/2" 7*pi)
          set arrow 1 from pi,graph 0 to pi,graph 1 nohead lt -1
          set arrow 2 from 2*pi,graph 0 to 2*pi,graph 1 nohead lt -1
          set arrow 3 from 3*pi,graph 0 to 3*pi,graph 1 nohead lt -1
          set arrow 4 from 4*pi,graph 0 to 4*pi,graph 1 nohead lt -1
          set arrow 5 from 5*pi,graph 0 to 5*pi,graph 1 nohead lt -1
          set arrow 6 from 6*pi,graph 0 to 6*pi,graph 1 nohead lt -1
          set key opaque
          set key reverse Left
          set xzeroaxis
          # Length of time used to plot each image, in periods
          periods = 1.00
          # Number of time steps for each period
          stepsperperiod = 50
          # Length of a time step
          dt = 2*pi/stepsperperiod
          # Total number of setps
          maxstep = periods*stepsperperiod
          set term gif enhanced font 'arial' 16 animate delay 0.05 size 1000,750 nooptimize
          set output "standing_waves.gif"
          # Remember to reset the time before running each plot
          i = 0
          load "rightsine.gnu"
          i= 0
          load "leftsine.gnu"
          i = 0
          load "bothsine.gnu"
          i = 0 load "constructive.gnu"
          i = 0 load "standing.gnu"
        
  • The remaining files have everything that changes in from one graphic to the next:
    A rightward traveling wave:
          set title "Wave Traveling to Right"
          plot sin(x-i*dt) t "sin ( k x - {/Symbol w} t)" w l lt rgb "red" lw 8
          i = i + 1
          if (i < maxstep) reread
        

    A leftward traveling wave:
          set title "Wave Traveling to Left"
          plot sin(x+i*dt) t "sin ( k x + {/Symbol w} t)" w l lt rgb "green" lw 8
          i = i + 1
          if (i < maxstep) reread
        

    Both waves together:
          set title "Both Waves"
          plot sin(x-i*dt) t "sin ( k x - {/Symbol w} t)" w l lt rgb "red" lw 8 , \
          sin(x+i*dt) t "sin ( k x + {/Symbol w} t)" w l lt rgb "green" lw 8
          i = i + 1
          if (i < maxstep) reread
        

    Both waves, along with the combined waveform:
          set title "Constructive Interference"
          plot sin(x-i*dt) t "sin ( k x - {/Symbol w} t)" w l lt rgb "red" lw 8 , \
          sin(x+i*dt) t "sin ( k x + {/Symbol w} t)" w l lt rgb "green" lw 8 , \
          sin(x-i*dt)+sin(x+i*dt) t "Combined Waves" w l lt rgb "blue" lw 8
          i = i + 1
          if (i < maxstep) reread
        

    Just the final wave:
          set title "Standing Wave"
          plot sin(x-i*dt)+sin(x+i*dt) t "Combined Waves" w l lt rgb "blue" lw 8
          i = i + 1
          if (i < maxstep) reread
        

The result is just what you see in the picture above.

Now we want to get this into a presentation. You can obviously embed this into a web page, as we're doing now. You can also put it into a Powerpoint presentation.

I, however, like to to my presentations in Beamer, a class in LaTeX which can produce fairly nice PDF files. The problem is that pdflatex, which takes LaTeX source and produces a PDF file, doesn't know nor care anything about GIFs, animated or not, so first we have to convert the GIF into something LaTeX can handle. The answer is the PNG format, which LaTeX loves. We simply use the convert command from ImageMagick:

  convert standing_waves.gif sw.png

and Whoa! we get a bunch of PNG files, conveniently labeled sw-0.png, sw-1.png, sw-2.png, ... , all the way up to, in this case, sw-249.png. To put them all back together we need to use the LaTeX package animate. A very simple Beamer file might look like this:

  \documentclass{beamer}

  % Allow animation
  \usepackage{animate}
  \begin{document}
  \section{Standing Waves}
  \begin{frame}
  \begin{center}
    \animategraphics[loop,controls,height=7cm]{25}{png/sw-}{0}{249} 
  \end{center}
  \end{frame}
  \end{document}

Most of this is pretty self-explanatory. I put the PNG files into a sub-directory, so they are png/sw-0.png, png/sw-1.png, ... . The {25} is the number of frames per second, since we lost that information from the GIF file. Keep running pdflatex on this until the errors stop, and voila, you've got a PDF presentation with animation. You'll have to use Adobe Reader to play it out, but hey, it works.

I've made all of this work on my LMDE box, and on two Macs, both using Macports to get the LaTeX and ImageMagick software. One of my Macs has a problem with the Greek fonts going into the GIF, and I don't know why. Also, gnuplot on the Mac is slightly different that my current Linux gnuplot, so I've written everything in a form that works on both machines. Finally, I've found that setting the font size works better on one of my Macs than it does on the other Mac or Linux box. I don't know why, but if I figure it out I'll let you know.