% % Date: Tue, 30 May 89 22:07:02 EDT % To: NeWS-makers@brillig.umd.edu % Subject: psPyro, a user extensible fireworks display screen saver % From: marshall@software.org (Eric Marshall) % Message-Id: % % psPyro is a user extensible fireworks display screen % saver. psPyro is fashioned after the MacIntosh screen saver % program "Pyro", written by Bill Steinberg and Steve Brecher. % Although psPyro actually contains no code which determines if % the screen saver should be invoked, it should be possible to % incorporate psPyro into a "true" screen saver program, e.g. Stan % Switzer's NeWS-based BlankScreen program or some system-level % screen saver. psPyro executes by randomly selecting a place % to fire each firework, randomly selects a direction in which % to fire the firework, randomly selects a width and height for % the firework trail to follow, and finally, randomly selects a % number of explosions to explode to conclude the firework firing. % This continues forever until either the mouse is moved or one % of the mouse buttons is pressed. % % User extensibility is achieved via definitions in the % /PSPyro dictionary, which is located in /systemdict. Although % only a small number of psPyro's internal definitions were intended % to be user configurable, all can be modified. The intended user % modifiable definitions are as follows: % % /unusable_screen_width - the percent of each side of the screen % which will not be used to fire a firework % from. The default is 10% of the screen % width. % % /unusable_screen_height - the percent of the top of the screen which % will not be used by the trail of a firework. % The default is 10% of the screen height. % % /minimum_trail_height - the minimum height of a firework trail, in % terms of a percentage of the screen height. % The default is 75% of the screen height. % % /minimum_explosion_angle - the minimum angle a firework trail will % travel before an explosion occurs. % Logically, fireworks travel in a % counterclockwise direction, starting at % angle 0. The default is 100. % % /maximum_explosion_angle - the maximum angle a firework trail will % travel before an explosion occurs. The % default is 150. % % /minimum_#_explosions - the minimum number of explosions per firework. % The default is 1. % % /maximum_#_explosions - the maximum number of explosions per firework. % The default is 5. % % /trail_length - the length (in degrees) of the animated firework trail. % The default is 3. % % /time_between_firings - the time (in seconds) between firework firings. % The default is 2. % % /multiple_explosion_radius - the radius (in units) from the center % of the original explosion to the center % of multiple explosions. The default % is 100. % % /delay_amount - the number of /pause's between firework trail updates. % The default is 50. I would have preferred to specify % a time in seconds to delay for, but /sleep wasn't % allowing for satisfactory animation. % % /explosion_kinds - the array which contains the names of the procedures % implementing the different explosions. % % A standard place to provide user specific definitions % for psPyro is in your user.ps file. An example user.ps fragment % follows which specifies the values for the minimum and maximum % explosion angles, and which adds a new explosion to the list % of available explosions (default is 5 different explosions): % % systemdict begin % /PSPyro 20 dict def % % PSPyro begin % /minimum_explosion_angle 45 def % /maximum_explosion_angle 90 def % % /T { translate 0 0 moveto 1 setgray (Explosion) show } def % % /explosion_kinds [ /T ] def % end % end % % A few words are in order concerning the definition of % new explosions. If /explosion_kinds is provided within /PSPyro, % then the contents of the provided array are ADDED to the internal % psPyro list of available explosions. If /replace_explosion_kinds % is provided, the provided array is used to REPLACE the internal % psPyro list. Also, each procedure named in the /explosion_kinds % and /replace_explosion_kinds arrays accept two parameters, the % x and y position of the center of the explosion. The supplied % explosion procedures simply /translate to the specified location % and draw about the origin, but more sophisticated procedures % could use the positional information as the basis for altering % the explosion shape, e.g. if the explosion is to occur very high % or very low. Go ahead and write some fancy explosions. Collect % them, trade them, sell them :-) % % Unfortunately, I don't have access to a color monitor, % so there is no support for color anything. Wouldn't it be nice % if someone with a color monitor ... % % psPyro was developed on a Sun 3/160 running NeWS 1.1 % under SunOS 3.5. % % % Eric Marshall % Software Productivity Consortium % SPC Building % 2214 Rock Hill Road % Herndon, VA 22070 % (703) 742-7153 % % CSNET: marshall@software.org % ARPANET: marshall%software.org@relay.cs.net % % ---------------------------------------------------------------------- % I'm just an X programmer gone straight. % % %% For Color systems use this /setacolor { % num -> - { 0 { 0 setgray } 1 { random random random setrgbcolor } 2 { 1 .1 0 setrgbcolor } % Red Rocket trail 3 { .86 .86 0 setrgbcolor } % Yellow ground flash /Default { random random random setrgbcolor } } case } def %% For Technicolor displays that dont fade from memory! %% Note: It would help to clear the display now and then. %% Just uncomment the next three lines. /setacolor { % num -> - pop random random random setrgbcolor } def %% For Black & White (Monocrome machines) uncomment the next 7 lines %/setacolor { % num -> - % { % 0 { 0 setgray } % 1 2 3 { 1 setgray } % /Default { random setgray } % } case %} def % % psPyro, a user extensible fireworks display screen saver. % % % Physical screen dimensions ------------------------------------------ % /screen_width 0 def /screen_height 0 def % % Global variables concerning current firework characteristics -------- % /trail_width 0 def /trail_height 0 def /trail_start_x 0 def /trail_going_left? true def /explosion_angle 0 def % % Heuristics for firework creation and display (user configurable) ---- % /unusable_screen_width 0.1 def % set to percent of physical screen width /unusable_screen_height 0.1 def % set to percent of physical screen height /minimum_trail_height 0.75 def % set to percent of physical screen height /minimum_explosion_angle 100 def /maximum_explosion_angle 150 def /minimum_#_explosions 1 def /maximum_#_explosions 5 def % the length of the firework trail /trail_length 3 def % time between firework firings (seconds) /time_between_firings 2 def % radius of multiple explosions from the center of the original explosion /multiple_explosion_radius 100 def % the number of /pause's between firework trail updates, and other stuff /delay_amount 50 def % % Firework explosion definitions (user configurable) ------------------ % % array to keep the names of the different explosions in /explosion_kinds [ /dots_explosion /circle_explosion /star_explosion /solid_circle_explosion /colored_circle_explosion ] def % % Dots explosion. % /draw_one_quarter { 30 0 moveto 2 -2 rlineto 2 2 rlineto -2 2 rlineto closepath fill 20 10 3 3 rectpath fill 10 20 3 3 rectpath fill } def /dots_explosion { % x y => - gsave translate gsave 5 { 1 setacolor 4 { draw_one_quarter 90 rotate } repeat 1.3 1.3 scale } repeat grestore gsave 5 { 0 setacolor 4 { draw_one_quarter 90 rotate } repeat 1.3 1.3 scale } repeat grestore grestore } def % % Circle explosion. % /draw_circle { % - => - 0 0 10 0 360 arc stroke } def /circle_explosion { % x y => - gsave translate gsave 6 { 1.4 dup scale 1 setacolor little_delay draw_circle } repeat grestore gsave 6 { 1.4 dup scale 0 setacolor little_delay draw_circle } repeat grestore grestore } def % % Star explosion. % /draw_star { % - => - gsave 8 { -30 0 moveto 60 0 rlineto stroke 22.5 rotate } repeat grestore } def /star_explosion { % x y => - gsave translate gsave 4 { 1.4 dup scale 1 setacolor little_delay little_delay draw_star } repeat grestore gsave 4 { 1.4 dup scale 0 setacolor little_delay draw_star } repeat grestore grestore } def % % Solid circle explosion. % /draw_dot { % - => - 0 0 10 0 360 arc fill } def /solid_circle_explosion { % x y => - gsave translate gsave 4 { 1.4 dup scale 1 setacolor little_delay draw_dot } repeat 0 setacolor little_delay draw_dot grestore grestore } def % % Colored circle explosion. % /draw_dot { % - => - 0 0 10 0 360 arc fill } def /colored_circle_explosion { % x y => - gsave translate gsave 4 { 1.4 dup scale random setacolor little_delay draw_dot } repeat 0 setacolor little_delay draw_dot grestore grestore } def % % Support utilities --------------------------------------------------- % % % A delay loop. % % The prefered method, /sleep, wasn't working smoothly enough % to give good animation. % /little_delay { 1 1 delay_amount { pause pop } for } def % % Apply the user preferences to the program. % /apply_user_preferences { % - => - systemdict /PSPyro known { PSPyro { 1 index dup % if it's the special EXPLOSION_KINDS or REPLACE_EXPLOSION_KINDS name /explosion_kinds eq { % concatenate user's array with existing EXPLOSION_KINDS array pop exch pop /explosion_kinds [ 3 2 roll aload pop explosion_kinds aload pop ] def } { /replace_explosion_kinds eq { % replace entire EXPLOSION_KINDS array with user's array exch pop /explosion_kinds exch def } { % do assignment into userdict def } ifelse } ifelse } forall } if } def % % Draw one instance of a firework trail from: % angle to angle % theta theta + trail_length - 1 % /draw_trail { % theta => - /theta exch def % erase the first degree from the previous trail 0 setacolor 0 0 1 theta 1 sub theta arc stroke 2 setacolor theta 1 theta trail_length add 1 sub { 0 0 1 4 -1 roll dup 1 add arc stroke } for } def % % Animate the entire firework trail up until the explosion. % /animate_trail { % - => - % draw the first trail instance 2 setacolor 0 1 trail_length 1 sub { 0 0 1 4 -1 roll dup 1 add arc stroke } for % draw the rest of the trail 1 1 explosion_angle { draw_trail little_delay } for % remove the last trail instance 0 setacolor explosion_angle 1 explosion_angle trail_length add 1 sub { 0 0 1 4 -1 roll dup 1 add arc stroke } for } def % % Determine the characterictics of the next firework. % /determine_firework_characteristics { % - => - % randomly pick the starting position to fire the firework from /trail_start_x screen_width random mul def % % pick the direction of the firework trail % % if the picked starting position is too close to a screen % edge, force the direction to be towards the opposite screen % side, otherwise randomly pick the direction % trail_start_x unusable_screen_width le { /trail_going_left? false def } { trail_start_x screen_width unusable_screen_width sub ge { /trail_going_left? true def } { % randomly pick the direction /trail_going_left? random round 0 eq def } ifelse } ifelse % randomly pick the width of the firework trail trail_going_left? { /trail_width trail_start_x random mul def } { /trail_width screen_width trail_start_x sub random mul def } ifelse % randomly pick the height of the firework trail /trail_height screen_height random mul def trail_height minimum_trail_height le { /trail_height minimum_trail_height def } if % randomly pick the ending angle of the firework explosion /explosion_angle maximum_explosion_angle minimum_explosion_angle sub random mul minimum_explosion_angle add round def } def % % Animate a firework explosion. % /animate_explosion { % explosion_x explosion_y #explosions => - /#explosions exch cvi def /explosion_y exch def /explosion_x exch def % randomly pick which explosion to use /which_explosion explosion_kinds explosion_kinds length random mul floor cvi get def % explode the first of the explosions explosion_x explosion_y which_explosion cvx exec % explode the remaining number of explosions (possible none), % randomly picking each location based upon the initial explosion % location #explosions 1 sub { % randomly pick another explosion location 360 random mul dup cos multiple_explosion_radius mul explosion_x add exch sin multiple_explosion_radius mul explosion_y add which_explosion cvx exec } repeat } def % % Continually make fireworks. % /make_fireworks { % - => - { { initmatrix sky setcanvas % create a new firework determine_firework_characteristics % translate to the center of the firework trail arc trail_start_x unusable_screen_width add trail_width 2 div trail_going_left? { sub } { add } ifelse 0 translate trail_going_left? not { -1 1 scale } if % create the firing of the firework 3 setacolor trail_width 2 div 0 20 0 180 arc fill little_delay 0 setacolor trail_width 2 div 0 20 0 180 arc fill % create the firework trail trail_width 2 div trail_height scale animate_trail % calculate the location of the firework explosion initmatrix trail_start_x unusable_screen_width add trail_width 2 div trail_going_left? { sub } { add } ifelse explosion_angle trail_length add cos trail_width 2 div mul trail_going_left? { add } { sub } ifelse explosion_angle trail_length add sin trail_height mul % calculate the number of explosions for the firework maximum_#_explosions random mul round dup 0 eq { pop minimum_#_explosions } if % create the explosion! animate_explosion % wait between firework firings time_between_firings 60 div sleep } loop } fork } def % % Main ---------------------------------------------------------- % % get the dimensions of the physical screen clippath pathbbox /screen_height exch def /screen_width exch def clear % apply user preferences apply_user_preferences % calculate the actual values of the firework creation and display heuristics /unusable_screen_width screen_width unusable_screen_width mul def /unusable_screen_height screen_height unusable_screen_height mul def /minimum_trail_height screen_height minimum_trail_height mul def % calculate the usable screen area /screen_height screen_height unusable_screen_height sub def /screen_width screen_width unusable_screen_width dup add sub def % create the black sky clippath pathbbox rectpath /sky framebuffer newcanvas def sky reshapecanvas sky /Mapped true put sky setcanvas 0 fillcanvas % remove the cursor /nouse /nouse_m sky setstandardcursor % start the fireworks display make_fireworks % wait for a mouse movement or click to end the fireworks display createevent dup begin % Note: I dont know why this does Not get us keyboard events -HD % Note: I have removed the /MouseDragged as it was to easy to jog % the mouse and louse a good Technicolor display! /Name 200 dict dup begin 0 1 127 { dup def } for /LeftMouseButton dup def /MiddleMouseButton dup def /RightMouseButton dup def end def /Action /DownTransition def /Canvas sky def end expressinterest awaitevent currentprocess killprocessgroup