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