marshall@software.org (Eric Marshall) (05/31/89)
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.
----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< -----
#! /usr/NeWS/bin/psh
%
% 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 setgray
4 {
draw_one_quarter
90 rotate
} repeat
1.3 1.3 scale
} repeat
grestore
gsave
5 {
0 setgray
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 setgray
little_delay
draw_circle
} repeat
grestore
gsave
6 {
1.4 dup scale
0 setgray
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 setgray
little_delay little_delay
draw_star
} repeat
grestore
gsave
4 {
1.4 dup scale
0 setgray
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 setgray
little_delay
draw_dot
} repeat
0 setgray
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 setgray
little_delay
draw_dot
} repeat
0 setgray
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 setgray
0 0 1 theta 1 sub theta arc stroke
1 setgray
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
1 setgray
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 setgray
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 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 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
1 setgray
trail_width 2 div 0 20 0 180 arc fill
little_delay
0 setgray
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
/Name [
/LeftMouseButton
/MiddleMouseButton
/RightMouseButton
/MouseDragged
] def
/Canvas sky def
end expressinterest
awaitevent
currentprocess killprocessgroupgra@mrmarx.east.sun.com (Gary R. Adams) (06/01/89)
Quick and dirty color :
% Add color
/newcolor {3 {random dup .1 le {.3 add } if } repeat rgbcolor setcolor} def
Then replace all occurences of "1 setgray" with "1 setgray newcolor".
-------------
Gary R. Adams sun!suneast!mrmarx!gra
Window Systems & Applications (508) 671-0416 "... it only takes a
Entry Systems Software sparc to get a fire going ..."