%!PS
% POSTSCRIPT BALL-BOUNCING SIMULATION
% by Golan Levin golan@media.mit.edu
% NMM/MAS834, Spring 1999
% MIT Media Laboratory
% flipbook.ps
% This PostScript program runs a simulation of a bouncing ball.
% All of the computation is carried out by the page output device,
% using forward-differencing in an Euler integration.
% The bouncing ball is subject to air resistance (drag) as well
% as energy loss from collisions with the ground.
% The printed output of the program is a small flipbook
% showing an animation of the ball. The ball animates with
% an oriented squash-and-stretch effect.
% This program was written with a C-like use of variables,
% in which some globally-defined terms are used to carry the
% state of the simulation. This is NOT a recommended style
% of PostScript programming, as it is an especially inefficient
% use of the stack. On the other hand, I found it to be much
% easier (and more familiar) than mucking about in the stack with
% PostScript's reverse-polish Forth-like syntax.
%---------------------------------------------------------------
% BALL-DRAWING CODE
% draws the ball
true setstrokeadjust % use good-quality lines
/inch {72 mul} bind def % define what an inch is
/ballRadius {0.1 inch} bind def % parameters for the ball
/ballGray 0.4 def % and also for its specular highlight
/hiliteGray ballGray def
/hiliteRadius ballRadius 0.5 mul def
/hilitePos ballRadius 0.25 mul def
/ratio 1 def % variables for squash and stretch
/angle 0 def % ratio is the aspect ratio, while
% angle is the ball's orientation.
% these get set in the simulation.
/makeBall { % procedure to draw the ball
gsave
angle rotate % orient the ball
1 ratio scale % squash & stretch
newpath % the basic circle of the ball
0 0 ballRadius
0 360 arc
closepath
gsave
ballGray setgray fill
grestore
gsave % draw the highlight gradient
clip % make it fit within the basic circle
/hiliteGray ballGray def % init some things here
/hiliteRadius ballRadius 1.1 mul def
6 { % make six concentric highlightings
% use an exponential brightening
/hiliteGray hiliteGray sqrt def
/hiliteRadius hiliteRadius 0.618 mul def
angle 45 add sin hilitePos mul % rotate the highlight into position
angle 45 add cos hilitePos mul
newpath
hiliteRadius
0 360 arc
closepath
gsave
hiliteGray setgray fill
grestore
} repeat
grestore
newpath % draw a cartoon outline around the ball
0 0 ballRadius
0 360 arc
closepath
0.0 setgray
0.48 setlinewidth
stroke
grestore
} def
%---------------------------------------------------------------
% PAGE-DRAWING CODE
% draws a grid of lines for the flipbook
% I've found 3 cells by 6, within an 8.5x11-inch page,
% yields a good size for flipbook pages.
% Watch out, there are a couple of hard-coded constants.
/gridW 2.5 inch def
/gridH 1.666666 inch def
/gridX 3 def
/gridY 6 def
/nGridCells gridX gridY mul def
/viewH 10 inch def
/viewW 7.5 inch def
/makeGrid {
% make a grid of lines which will represent flipbook pages
gsave % draw the 'ground'
6 { 0.5 inch 0.5 inch moveto
0 0.2 inch rlineto
viewW 0 rlineto
0 -0.2 inch rlineto
closepath
0 gridH translate
} repeat
0.85 setgray fill
grestore
gsave % draw the vertical grid lines
4 { 0.5 inch 0.5 inch moveto
0 viewH rlineto
gridW 0 translate
} repeat
0.24 setlinewidth
0.0 setgray stroke
grestore
gsave % draw the horizontal grid lines
7 { 0.5 inch 0.5 inch moveto
viewW 0 rlineto
0 gridH translate
} repeat
0.24 setlinewidth
0.0 setgray
stroke
grestore
} def
%---------------------------------------------------------------
% SIMULATION VARIABLES
/px 1.75 def % x position
/py 10.3 def % y position
/vx 0.018 def % x velocity
/vy 0.00 def % y velocity
/ax 0.00 def % x acceleration
/ay -0.01625 def % y acceleration (gravity)
/ground 9.0333 def % height of ground
/collisionDamp 0.8000 def % momentum retained after a ground collision
/airFrictionDamp 0.999 def % momentum retained after air friction, per time step
/nsteps 4 nGridCells mul def % number of iterations
/temp 0.0 def % temporary variable
/cell 0 def % init current cell ID
/stretchFactor 25 def % squash & stretch
/flipbook 1 def % do a flipbook (1), or not (0)
% if not, the program will just draw the whole
% path of the ball in the first flipbook page
%---------------------------------------------------------------
gsave % draw my title
/Courier findfont
0.1 inch scalefont
setfont
2.9 inch 9.5 inch moveto
90 rotate
(bounce by golan) show
grestore
%---------------------------------------------------------------
% MAIN ROUTINE
nsteps { % run the simulation nsteps times
flipbook 1 eq { % if we're making a flipbook
cell nGridCells mod 0 eq { % if we've filled the page
cell nGridCells ge { % make a new page if necessary
showpage
} if
gsave
makeGrid % draw the grid for the page
grestore
px inch py inch translate
} if
}
{ cell 0 eq {
gsave
makeGrid
grestore
px inch py inch translate
} if
} ifelse
/ratio vy vx div abs stretchFactor div 1 add def % set squash & stretch
/angle vy vx atan 90 add def % compute orientation
makeBall % draw the ball
/cell cell 1 add def % increment cell counter
flipbook 1 eq { % make a flipbook?
0 0 gridH sub translate % if so, move to next row
cell gridY mod 0 eq { % move to next column when necessary
gridW viewH translate
} if
} if
% -- SIMULATION --
/vy vy airFrictionDamp mul def % lose energy from air friction
/vx vx airFrictionDamp mul def
py vy add ground le % if ((py+vy) is less than ground) then
{ % -- collision case --
/vy vy collisionDamp mul def % lose energy from collision
/vx vx ax add def
/temp ground py sub 2 mul vy sub def % deal with reflection. hah.
/py py temp add def
/px px vx add def
vx inch temp inch translate % update current state
/vy 0 vy sub def % reverse direction
} % else
{ % -- normal case --
/vy vy ay add def % euler integrate gravity
/vx vx ax add def
/px px vx add def
/py py vy add def
vx inch vy inch translate % update current state
} ifelse
} repeat
showpage
%---------------------------------------------------------------
% (c) Golan Levin, MIT Media Laboratory 1999
% end of document