#!/usr/bin/env python
"""
movie.py

Kelvin Chu, Department of Physics, University of Vermont
http://www.uvm.edu/~kchu/

A simple script to draw a pendulum oscillating and record it to a
movie. This works on MacOSX but should be easily extensible to
Linux/Unix. It does not work on Microsoft Windows, because the
commands module (shell command line) does nothing, and some system
tools are not available in the form used here.

Additional details (and the resulting animated gif and QuickTime
movie) can be found at:

   http://www.uvm.edu/~kchu/?Page=VPython/index.html

Strategy:
--------

1.  Use VPython to make your animation.

2.  At every step in the animation (i.e. every re-rendering of
    objects) take a screenshot (/usr/sbin/screencapture) and save the
    file.

3.  Use Imagemagick to crop out the image of the VPython animation and
    turn it into an animated gif file or movie file (using QuickTime).

    - I manually position the VPython window in the top left-hand
      corner of the screen, and then let the animation run.
      
    - For the default VPython window settings, one can crop with a
      setting of 430x365+0+0.

Requirements:
------------
0. An Apple Macintosh running MacOSX (currently Panther or Jaguar)
1. Fink (fink.sf.net)
2. Imagmagick (install via fink: 'fink install imagemagick')
3. VPython (www.vpython.org)
4. QuickTime Player

Movie comments are denoted by ** movie **

Some physics: The actual differential equation,

d(dtheta/dt)/dt = -(g/L)sin(theta) 

needs to be integrated.


"""

from visual import *                    # vpython module
from commands import *                  # shell/command line module; Unix only


def GetScreenShot(FrameNumber):         # Take a screenshot and write
                                        # it to a numbered file.
    tmp = getoutput('/usr/sbin/screencapture pendulum.%03d.pdf' % FrameNumber)
    print 'Frame: %d' % (FrameNumber)
    return

def GetRVector(Theta):                  # Calculate the position of
                                        # the mass based upon \theta
                                        # measured from the vertical.
    
    return vector(sin(Theta),-cos(Theta),0)

def CalculatePosition(Theta,ThetaDot,dt,l):
    
                                        # Integrate the differential
                                        # equation to get the new
                                        # \theta and \dot{\theta}
                                        # based upon previous position.
    g = 9.81

    NewThetaDot = -g/l*sin(Theta)*dt + ThetaDot
    NewTheta = NewThetaDot*dt + Theta 
    return NewTheta,NewThetaDot


# Some initial conditions and variable settings.
#

g = 9.81                                # Acceleration from gravity

Length = 1.0                            # Length of the pendulum bob.

Theta = pi/2                            # Initial angle, measured
                                        # relative to the vertical.

ThetaDot = 0                            # Initial velocity.

Deltat = .01                            # Time step (.01 seconds)

FrameNumber = 0                         # Counter for frames.




R = GetRVector(Theta)                   

# Draw the ground as something vaguely interesting.
box(pos = vector(-Length/2,-.5 - Length,-Length/2),height = 0.1, 
             length = Length,width = 1.0,color=color.white)
box(pos = vector( Length/2,-.5 - Length, Length/2),height = 0.1, 
             length = Length,width = 1.0,color=color.white)
box(pos = vector(-Length/2,-.5 - Length, Length/2),height = 0.1, 
             length = Length,width = 1.0,color=color.blue)
box(pos = vector( Length/2,-.5 - Length,-Length/2),height = 0.1, 
             length = Length,width = 1.0,color=color.blue)
# Anchor for the pendulum.
sphere(radius = 0.02,color=color.red,pos=vector(0,0,0))

# Pendulum itself
Dot = sphere(radius=0.07,color=color.yellow,pos=R)
Shaft = cylinder(radius = 0.02, color=color.white, pos=vector(0,0,0),
                 axis=R)

scene.autoscale = 0                     # Need to turn off autoscaling
                                        # to prevent insanity.

# **movie** By positioning the VPython window in the top left hand corner, we
# know its position on the screen and then can correctly crop it out
# of the screenshot afterwards.
#
print 'This program works only on MacOSX but could be extended to Unix/Linux.'
print 'This program does NOT work on Microsoft Windows.'
raw_input('Position VPython window at top left hand corner. Then hit enter in this window.')
#


while FrameNumber < 235:                # Period of oscillation is ~2.35s.
    Dot.pos = R
    Shaft.axis = R
    Theta,ThetaDot = CalculatePosition(Theta,ThetaDot,Deltat,Length)
    R = GetRVector(Theta)

# **movie** Use the screencapture utility to get a screenshot.  
    GetScreenShot(FrameNumber)
    FrameNumber = FrameNumber + 1
#

# **movie** Use ImageMagick to process the files, cropping out the
# unwanted portions of the screenshot and converting to an animated
# gif.  
print 'Converting files.'

# The arguments to convert are:
# -delay 01     : space a 1/100th of a second delay in between frames.
# -loop 0       : animated gif will repeat ad infinitum
# -crop AxB+C+D : crop the image with aspect AxB, offsets at C and D.
#                 You will have to experiment with these numbers if
#                 you change the VPython window size.
#
tmp = getoutput('/sw/bin/convert -delay 01 -loop 0 -crop 430x365+0+0 pendulum.*.pdf animated.pendulum.gif')
print 'File written to "animate.pendulum.gif". If you like it, you can delete the PDFs'
print 'Done' 
# 

