#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Initial copyright and message :
# Josh Lifton 2004
#
# Permission is hereby granted to use and abuse this document
# so long as proper attribution is given.
#
# This Python script demonstrates how to use the numarray package
# to generate and handle large arrays of data and how to use the
# matplotlib package to generate plots from the data and then save
# those plots as images.  These images are then stitched together
# by Mencoder to create a movie of the plotted data.  This script
# is for demonstration purposes only and is not intended to be
# for general use.  In particular, you will likely need to modify
# the script to suit your own needs.
#
#
# Heavily modified by C. Joubert to trace rotating field

epilog = \
"""
Examples

GIF output. The only powered coil is coil 1
anim3 -g -1

GIF output. All coils are powered. The resulting field is also plotted.
anim3 -g --all -c -r
"""



# Background image filename
bkg_filename='background.png'


import argparse

parser = argparse.ArgumentParser(description='Create animation of rotating field', 
                                 epilog=epilog,
                                 formatter_class=argparse.RawDescriptionHelpFormatter)

parser.add_argument('-1', '--coil1', action='store_true',
                    help='plot coil 1 field')
parser.add_argument('-2', '--coil2', action='store_true',
                    help='plot coil 2 field')
parser.add_argument('-3', '--coil3', action='store_true',
                    help='plot coil 3 field')
parser.add_argument('-A', '--all', action='store_true',
                    help='plot all 3 fields')
parser.add_argument('-c', '--compose', action='store_true',
                    help='vectors are plotted "chained"')
parser.add_argument('-r', '--resulting', action='store_true',
                    help='draw resulting magnetic field in black')
parser.add_argument('-m', '--compass-angle', type=int, default= None,
                    help='draw a compass with given angle shift. Default : no compass drawn')
parser.add_argument('-p', '--preserve', action='store_true',
                    help='do not delete individual PartialOut*.png files')
parser.add_argument('--fps', type=int, default= 10,
                    help='frames per second')
parser.add_argument('-f', '--frames', type=int, default= 40,
                    help='total number or frames')
parser.add_argument('-o', '--output', default = 'output_file',
                    help='output file (without extension)')
parser.add_argument('-a', '--avi', action='store_true',
                    help='outputs .avi file')
parser.add_argument('-g', '--gif', action='store_true',
                    help='outputs .gif file')

args = parser.parse_args()


# Output filename (without extension)
out_filename = args.output

# We can get either a .avi movie or an animated GIF (or both)
# if neither is set to True, the GUI is launched and the figure plotted into
AVI_output = args.avi
GIF_output = args.gif

numberOfTimeSteps = args.frames   # Number of frames we want in the movie.
fps = args.fps                    # Frames per second

d=0.05                 # margin between figures
w1=0.25                # width of sinus. figures
h1=0.95*(1-4*d)/3      # height of sinus. figures
centerfig=(228,249)    # Coordinates of the center of the figure
VL=60                  # Vector length
VHL=10                 # Vector arrow dimensions

# Which of the current in the three coils is non zero and must be plotted along with the corresponding field ?
drawv = [args.coil1 , args.coil2 , args.coil3]
if args.all:
    drawv = [True, True, True]

# If set to true, vectors are plotted "chained"
compose = args.compose

# If set to True, also plots the resulting field
drawres = args.resulting

# angle de la boussole (en °) par rapport au cham. Si Non : on ne trace pas la boussole
if args.compass_angle is None:
    angle_boussole = None
else:
    angle_boussole =  -args.compass_angle

# Dessine-t-on les fleches ?
draw_arrows = True

import matplotlib
if AVI_output or GIF_output:
    matplotlib.use('Agg')
import matplotlib.pyplot as plt   # For plotting graphs.
import matplotlib.image as mpimg
import matplotlib.figure as mpfig
import matplotlib.patches as mpatches
import numpy as np
import math
import subprocess                 # For issuing commands to the OS.
import os
import sys                        # For determining the Python version.

#
# Print the version information for the machine, OS,
# Python interpreter, and matplotlib.  The version of
# Mencoder is printed when it is called.
#
print 'Executing on', os.uname()
print 'Python version', sys.version
print 'matplotlib version', matplotlib.__version__

not_found_msg = """
The mencoder command was not found;
mencoder is used by this script to make an avi file from a set of pngs.
It is typically not installed by default on linux distros because of
legal restrictions, but it is widely available.
"""

try:
    subprocess.check_call(['mencoder'])
except subprocess.CalledProcessError:
    print "mencoder command was found"
    pass # mencoder is found, but returns non-zero exit as expected
    # This is a quick and dirty check; it leaves some spurious output
    # for the user to puzzle over.
except OSError:
    print not_found_msg
    sys.exit("quitting\n")


def draw_triangle(ax, angle, color="red", size=20):
    points = [ (0, -size), (2*size, 0), (0, +size) ]
    p_tr = []
    angle = -angle  # pour s'adapter aux axes
    for p in points:
        x = p[0]*math.cos(angle) - p[1]*math.sin(angle) + centerfig[0]
        y = p[0]*math.sin(angle) + p[1]*math.cos(angle) + centerfig[1]
        p_tr.append( (x, y) )
    p = matplotlib.patches.Polygon(p_tr, closed=True, fill=True, color="k", fc=color)
    ax.add_patch(p)

alph = np.arange(0,2*np.pi,0.05)   # Values to be plotted on the x-axis.



fig=plt.figure()
fig.set_facecolor('w')
img=mpimg.imread(bkg_filename) # on dessine l'image du fond (bobines)

for i in range(numberOfTimeSteps) :
    # Axes des courbes du courant :
    axp = [\
        fig.add_axes([d, 0*((1-4*d)/3+d)+d, w1, h1]),\
        fig.add_axes([d, 1*((1-4*d)/3+d)+d, w1, h1]),\
        fig.add_axes([d, 2*((1-4*d)/3+d)+d, w1, h1])]
    styles = ['r','b','g']
    # axe de la fenêtre principale avec les bobines et les champs
    axi = fig.add_axes([w1+2*d, d, 1-3*d-w1, 1-2*d])
    axi.set_axis_off() # on n'y ajoute pas les axes

    x1=centerfig[0]
    y1=centerfig[1]

    for j in range(0,3):  # j : indice de la phase
      ax=axp[j]
      ax.axhline(0,color='k')
      ax.axvline(0,color='k')
      ax.text(2*np.pi,0.05,'t',color='k')
      ax.text(0.1,1,'i%d'%(j+1),color=styles[j])
      fig.sca(ax)
      # y = f(alph) : sinusoïde décalée de j*2*pi/3 (j : indice de la phase)
      y=np.cos(alph-j*2*np.pi/3)
      xp=i*2*np.pi/numberOfTimeSteps  # (xp, yp) : point courant sur la courbe (indiqué avec un cercle)
      yp=np.cos(xp-j*2*np.pi/3)
      if drawv[j]:
        plt.plot(alph,y,styles[j]+'-',xp,yp,styles[j]+'o')
        fig.sca(axi)
        dx=VL*yp*np.cos(j*2*np.pi/3)
        dy=VL*yp*np.sin(-j*2*np.pi/3)
        #print "%d %d %d %d %d\n" % (j,x1,y1,dx,dy)
        # Trace du champ créé par la phase
        if draw_arrows:
            plt.arrow(x1,y1,dx,dy,color=styles[j],head_width=VHL,head_length=VHL)
        if compose:
          x1=x1+dx
          y1=y1+dy
        
      ax.set_xlim(-1.1,2*np.pi+0.1)
      ax.set_ylim(-1.1,1.1)
      ax.set_axis_off()
    
    # dessin de la boussole
    if angle_boussole is not None:
        a = angle_boussole * np.pi /180 + i*2*np.pi/numberOfTimeSteps
        draw_triangle(axi, a)
        draw_triangle(axi, a + np.pi, color="white")
    
    # Dessin du champ résultant
    if drawres:
      x0=centerfig[0]
      y0=centerfig[1]
      plt.arrow(x0,y0,x1-x0,y1-y0,color='k',head_width=VHL,head_length=VHL, linewidth=2)
    
    
    fig.sca(axi)
    imgplot = plt.imshow(img)

    #
    # The file name indicates how the image will be saved and the
    # order it will appear in the movie.  If you actually wanted each
    # graph to be displayed on the screen, you would include commands
    # such as show() and draw() here.  See the matplotlib
    # documentation for details.  In this case, we are saving the
    # images directly to a file without displaying them.
    #
    if AVI_output or GIF_output:
      filename = str('PartialOut%03d' % i) + '.png'
      plt.savefig(filename, dpi=100)
    
    plt.show()


    #
    # Let the user know what's happening.
    #
    print 'Wrote file', filename

    #
    # Clear the figure to make way for the next image.
    #
    plt.clf()

#
# Now that we have graphed images of the dataset, we will stitch them
# together using Mencoder to create a movie.  Each image will become
# a single frame in the movie.
#
# We want to use Python to make what would normally be a command line
# call to Mencoder.  Specifically, the command line call we want to
# emulate is (without the initial '#'):
# mencoder mf://*.png -mf type=png:w=800:h=600:fps=25 -ovc lavc -lavcopts vcodec=mpeg4 -oac copy -o output.avi
# See the MPlayer and Mencoder documentation for details.
#

if AVI_output:
  command = ('mencoder',
	    'mf://PartialOut*.png',
	    '-mf',
	    'type=png:w=800:h=600:fps=%d'% (fps),
	    '-ovc',
	    'lavc',
	    '-lavcopts',
	    'vcodec=mpeg4',
	    '-oac',
	    'copy',
	    '-o',
	    '%s.avi'%(out_filename))

  print "\n\nabout to execute:\n%s\n\n" % ' '.join(command)
  subprocess.check_call(command)

  print "\n\n The movie was written to 'output.avi'"
  
if GIF_output:
  # Ref : http://personal.cscs.ch/~mvalle/postprocessing/ImageTools.html
  # convert -delay 50 -dispose Background +page Img*.png -loop 0 animation.gif
  command = ('convert',
	    '-delay',
	    '%d'%(100/fps),
	    '-dispose',
	    'Background',
	    '+page',
	    'PartialOut*.png',
	    '-loop',
	    '0',
	    '%s.gif'%(out_filename))

  print "\n\nabout to execute:\n%s\n\n" % ' '.join(command)
  subprocess.check_call(command)

  print "\n\n The movie was written to '%s'" % out_filename


if (AVI_output or GIF_output) and not  args.preserve:
  print "Deleting tempory .png files..."
  for i in range(numberOfTimeSteps) :
    filename = str('PartialOut%03d' % i) + '.png'
    #print "Deleting %s" % filename
    os.unlink(filename)


