%!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