import numpy as np
from numpy import *
from scipy import interpolate
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
from matplotlib import animation

# PENDULUM 2
# g = 9.81
# A = 0.01
# w = 2.*pi*5
# l = 0.15

# PENDULUM 3
# g = 9.81
# A = 0.02
# w = 2.*pi*10
# l = 0.15

g = 9.81
A = 0.1
w = 2.*pi*10
l = 0.15

# ODE:
# l*ddtheta + (g + ddz)*sin(theta) = 0
# 
# x1 = theta
# x2 = dtheta
# 
# dx1 = x2 		( = dtheta)
# dx2 = -(g+A*sin(w*t))*sin(x1)/l 	( = ddtheta)
# 
# f(x1,x2) = [dx1, dx2]
def f(x1,x2):
	global g, A, w, l
	return [x2, -((g-A*pow(w,2)*sin(w*t))*sin(x1))/l]
	# return [x2,-(g*sin(x1))/l]

# RK4:
# k1 = h*f(x,y(x))
# k2 = h*f(x+h/2,y(x)+k1/2)
# k3 = h*f(x+h/2,y(x)+k2/2)
# k4 = h*f(x+h,y(x)+k3)
# y(x + h) = y(x) + k1/6 + k2/3 + k3/3 + k4/6

h = 0.005
hlast = h
desired_error = 1e-10
[x1, x2] = [pi-0.0001, 0]
# t = arange(0,10*pi,h)
t = 0
tmax = 10.*pi
tt = []
xx1 = []
xx2 = []
zz = []

while (t < tmax):
	dError = 1
	while (dError > 0):
		# full step:
		k1 = h*array(f(x1,x2))
		k2 = h*array(f(x1+k1[0]/2.,x2+k1[1]/2.))
		k3 = h*array(f(x1+k2[0]/2.,x2+k2[1]/2.))
		k4 = h*array(f(x1+k3[0],x2+k3[1]))
		[x1f, x2f] = [x1, x2] + k1/6. + k2/3. + k3/3. + k4/6.

		# two half steps:
		k1 = h/2.*array(f(x1,x2))
		k2 = h/2.*array(f(x1+k1[0]/2.,x2+k1[1]/2.))
		k3 = h/2.*array(f(x1+k2[0]/2.,x2+k2[1]/2.))
		k4 = h/2.*array(f(x1+k3[0],x2+k3[1]))
		[x1h, x2h] = [x1, x2] + k1/6. + k2/3. + k3/3. + k4/6.

		k1 = h/2.*array(f(x1h,x2h))
		k2 = h/2.*array(f(x1h+k1[0]/2.,x2h+k1[1]/2.))
		k3 = h/2.*array(f(x1h+k2[0]/2.,x2h+k2[1]/2.))
		k4 = h/2.*array(f(x1h+k3[0],x2h+k3[1]))
		[x1h, x2h] = [x1h, x2h] + k1/6. + k2/3. + k3/3. + k4/6.

		# update step size
		error_estimate = x1f-x1h
		dError = (error_estimate-desired_error)
		
		if (dError > 0):
			h = h*0.76
			hlast = h
			print("Error too large... Redo'ing step")
		elif (dError < 0):
			hlast = h
			h = h*1.02
			print("Expanding step size")

	print("t %.3f \t eE %.3e \t dE %.3e \t h %.3e" %(t,error_estimate,dError,h))
	t += hlast
	tt.append(t)
	[x1, x2] = [x1f, x2f]

	xx1.append(x1)
	xx2.append(x2)
	z = A*sin(w*t)
	zz.append(z)
	
# plt.figure()
# plt.plot(tt,xx1)
# plt.plot(tt,zz)

# Resample the datapoints in linear time
k = linspace(0,tmax,3000)
theta = interp(k,tt,xx1)
height = interp(k,tt,zz)
xs = []
ys = []

# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = fig.add_subplot(111,xlim=(-0.5, 0.5), ylim=(-0.5, 0.5))
frame = plt.gca()
frame.axes.get_xaxis().set_visible(False)
frame.axes.get_yaxis().set_visible(False)
ax.set_frame_on(False)
point, = ax.plot([], [],'b.',markersize=50.0)
line, = ax.plot([],[],lw=2)
platform, = ax.plot([],[],'k',lw=5)
trace, = ax.plot([],[],color='0.3',lw=1)
 
# initialization function: plot the background of each frame
def init():
    point.set_data([], [])
    platform.set_data([],[])
    line.set_data([],[])
    return line, point, platform,
 
# animation function.  This is called sequentially
def animate(i):
	global theta, height, l, xs, ys
	x = l*cos(theta[i]-pi/2.)
	y = l*sin(theta[i]-pi/2.)
	xs.append(x)
	ys.append(y+height[i])
	platform.set_data([-0.1, 0.1], [height[i], height[i]])
	line.set_data([0,x], [height[i],height[i]+y])
	point.set_data([x], [height[i]+y])
	trace.set_data([xs],[ys])
	return line, point, platform, trace
 
# call the animator.  blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=500, interval=20, blit=True)
 
# this is how you save your animation to file:
# anim.save('pendulum4.gif', writer='imagemagick', fps=30)
 
plt.show()
