
One good way of making an animation is by simulating a physical system and illustrating the variables in that system using a dynamically changing graphical display.

The classic such physical system is a simple gravity pendulum.



Task

Create a simple physical model of a pendulum and animate it.




 
import gui
$include "guih.icn"
 
# some constants to define the display and pendulum
$define HEIGHT 400
$define WIDTH 500
$define STRING_LENGTH 200
$define HOME_X 250
$define HOME_Y 21
$define SIZE 30
$define START_ANGLE 80
 
class WindowApp : Dialog ()
 
  # draw the pendulum on given context_window, at position (x,y)
  method draw_pendulum (x, y)
    # reference to current screen area to draw on
    cw := Clone(self.cwin)
 
    # clear screen
    WAttrib (cw, "bg=grey")
    EraseRectangle (cw, 0, 0, WIDTH, HEIGHT)
 
    # draw the display
    WAttrib (cw, "fg=dark gray")
    DrawLine (cw, 10, 20, WIDTH-20, 20)
    WAttrib (cw, "fg=black")
    DrawLine (cw, HOME_X, HOME_Y, x, y)
    FillCircle (cw, x, y, SIZE+2)
    WAttrib (cw, "fg=yellow")
    FillCircle (cw, x, y, SIZE)
 
    # free reference to screen area
    Uncouple (cw)
  end
 
  # find the average of given two arguments
  method avg (a, b)
    return (a + b) / 2
  end
 
  # this method gets called by the ticker
  # it computes the next position of the pendulum and
  # requests a redraw
  method tick ()
    static x, y
    static theta := START_ANGLE
    static d_theta := 0
    # update x,y of pendulum
    scaling := 3000.0 / (STRING_LENGTH * STRING_LENGTH)
    # -- first estimate
    first_dd_theta := -(sin (dtor (theta)) * scaling)
    mid_d_theta := d_theta + first_dd_theta
    mid_theta := theta + avg (d_theta, mid_d_theta)
    # -- second estimate
    mid_dd_theta := - (sin (dtor (mid_theta)) * scaling)
    mid_d_theta_2 := d_theta + avg (first_dd_theta, mid_dd_theta)
    mid_theta_2 := theta + avg (d_theta, mid_d_theta_2)
    # -- again first
    mid_dd_theta_2 := -(sin (dtor (mid_theta_2)) * scaling)
    last_d_theta := mid_d_theta_2 + mid_dd_theta_2
    last_theta := mid_theta_2 + avg (mid_d_theta_2, last_d_theta)
    # -- again second
    last_dd_theta := - (sin (dtor (last_theta)) * scaling)
    last_d_theta_2 := mid_d_theta_2 + avg (mid_dd_theta_2, last_dd_theta)
    last_theta_2 := mid_theta_2 + avg (mid_d_theta_2, last_d_theta_2)
    # -- update stored angles
    d_theta := last_d_theta_2
    theta := last_theta_2
    # -- update x, y
    pendulum_angle := dtor (theta)
    x := HOME_X + STRING_LENGTH * sin (pendulum_angle)
    y := HOME_Y + STRING_LENGTH * cos (pendulum_angle)
 
    # draw pendulum
    draw_pendulum (x, y)
  end
 
  # set up the window
  method component_setup ()
    # some cosmetic settings for the window
    attrib("size="||WIDTH||","||HEIGHT, "bg=light gray", "label=Pendulum")
    # make sure we respond to window close event
    connect (self, "dispose", CLOSE_BUTTON_EVENT)
    # start the ticker, to update the display periodically
    self.set_ticker (20)
  end
end
 
procedure main ()
  w := WindowApp ()
  w.show_modal ()
end
 