"""
 ball_arc.py

 An animation of a ball trajectory,
 using Zelle's graphics package
 and some basic physics.

 Jim Mahoney | Fall 2010 | GPL
"""

# A numeric approximation to the Newton's
# equations of motion for a falling ball are
#
#   x(t + dt) = x(t) + Vx * dt
#   y(t + dt) = y(t) + Vy(t) * dt
#   Vx = constant
#   Vy(t + dt) = Vy(t) + g * dt
#   g = -9.8 m/sec**2 = downward (negative) acceleration from gravity
#
# which we can implement in python within a loop,
# updating the variables and re-positioning the ball
# each time throught the loop.

from graphics import GraphWin, Point, Line, Circle, Text
from time import sleep

# -- define a bunch of constants and variables

window_right = 20
window_top = 8
window_margin = 2
window_coords = (-window_margin,
                 -window_margin,
                 window_right + window_margin,
                 window_top + window_margin)
window_pixel_height = 400
window_pixel_width = window_pixel_height * \
                     (window_right + 2*window_margin) /  \
                     (window_top + 2*window_margin)
window_title = "ball arc"
window_background_color = "#e0e0e0"   # light grey

ball_radius = 0.5                     # in meters
ball_color = 'red'

dt = 0.02          # simluation tick time, in seconds
time = 0.0         # elapsed time
wait_time = 0.003  # wall clock wait time while ball moves by dt
stop_time = 2.0    # simulation stop time, in seconds

x = 0              # initial x ball position, in meters
y = 0              # ditto for y
Vx = 10            # initial x velocity, in meters/sec
Vy = 9             # ditto for y

g = -9.8           # meters/sec**2, downwards

# -- create the window --

window = GraphWin(window_title, window_pixel_width, window_pixel_height)
window.setBackground(window_background_color)
window.setCoords(window_coords[0], window_coords[1],
                 window_coords[2], window_coords[3])

# -- create and draw a lines for some plot edges

ground = Line(Point(0,0), Point(window_right, 0))
ground.setWidth(3)
ground.setOutline('#303000')
ground.draw(window)

wall = Line(Point(0,0), Point(0, window_top))
wall.setWidth(3)
wall.setOutline('#303000')
wall.draw(window)

# -- create and draw the ball

ball = Circle(Point(x,y), ball_radius)
ball.setOutline('black')
ball.setFill(ball_color)
ball.draw(window)

# -- create timer at top left of window

format = " t = %5.2f\n x = %5.2f\n y = %5.2f"
timer_position = Point(window_margin, window_top)
timer = Text(timer_position, (format % (time, x, y)))
timer.setFace('courier')  # fixed width, for number alignment
timer.draw(window)

# -- print text telling the user to click; wait for click to start

message_position = Point(window_right/2, -window_margin/2)
message = Text(message_position, "Click mouse to start.")
message.draw(window)
window.getMouse()

# -- loop over the time variable, updating position and velocity

for tick in range(int(stop_time/dt)):
    sleep(wait_time)
    dx = Vx * dt
    dy = Vy * dt
    x = x + dx
    y = y + dy
    time = time + dt
    Vy = Vy + g * dt       # g is negative, so Vy decreases over time.
    ball.move(dx, dy)
    timer.setText(format % (time, x, y))

# --wait for user click before quitting

message.setText("Done.  Click mouse to quit.")
window.getMouse()