[comp.windows.news] Another rework of the psPyro

alexis%yummy@GATEWAY.MITRE.ORG (06/28/89)

Yet another remake/tweek of the psPyro program, making it more 
worthy of a workstation (read as "it sucks us cpu cycles").  This 
version has lots of colors, more interesting explosions, more than 
one firecracker in the air at once (I have it set to 3, but it's 
just a variable), and general tweeking (they fly faster, explode 
slower, etc).  This just assumes you're running on a color machine.

alexis wieland
alexis%yummy@gateway.mitre.org

---------------------------  cut here  ----------------------
#! /usr/NeWS/bin/psh

%
%  psPyro, a user extensible fireworks display screen saver.
%

%
% Physical screen dimensions ------------------------------------------
%
/screen_width  0 def
/screen_height 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 2 def
/maximum_#_explosions 6 def

/trail_length 5 def		% the length of the firework trail
/n-in-air-at-once 3 def		% number of fireworks at any time
/multiple_explosion_radius 100 def % radius of multiple explosions from original

% array of colors
/colortable [
	1 1 1 rgbcolor
	1 0 0 rgbcolor
	0 1 0 rgbcolor
	0 0 1 rgbcolor
	1 1 0 rgbcolor
	0 1 1 rgbcolor
	1 .5 0 rgbcolor
	1 0 .5 rgbcolor
	.8 .5 .2 rgbcolor
	.92 .68 .92 rgbcolor
	.86 .44 .86 rgbcolor
] def

% randomly set color
/randcolor {
  colortable random colortable length 1 sub mul floor get setcolor
} def

%
% Firework explosion definitions
%
% array to keep the names of the different explosions in
/explosion_kinds [
	/dots_explosion		/circle_explosion
	/star_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
    360 random mul rotate
    8 random mul cvi 2 add
    .5 random add dup scale
    gsave
      5 {
	 randcolor
        dup {
          draw_one_quarter
          360 1 index div rotate
        } repeat
        1.3 1.3 scale
	big_delay
      } repeat
    grestore
    gsave
      5 {
        0 setgray
        dup {
          draw_one_quarter
          360 1 index div rotate
        } repeat
        1.3 1.3 scale
	big_delay
      } repeat
      pop
    grestore
  grestore
} def  % dots_explosion


%
%  Circle explosion.
%

/draw_circle { % - => -
  0 0 10 0 360 arc stroke
} def

/circle_explosion { % x y => -
  gsave
    translate
    .5 random add dup scale
    gsave
      6 {
        1.4 dup scale
	 randcolor
        big_delay
        draw_circle
      } repeat
    grestore
    gsave
      6 {
        1.4 dup scale
        0 setgray
        big_delay
        draw_circle
      } repeat
    grestore
  grestore
} def


%
%  Star explosion.
%

/draw_star { % n-lines => n-lines
  gsave
    dup {
      -30 0 moveto
      60 0 rlineto stroke
      360 1 index div rotate
    } repeat
  grestore
} def

/star_explosion { % x y => -
  gsave
    translate
    .5 random add dup scale
    360 random mul rotate
    10 random mul 4 add
    gsave
      4 {
        1.4 dup scale
         randcolor
        big_delay
        draw_star
      } repeat
    grestore
    gsave
      4 {
        1.4 dup scale
        0 setgray
        big_delay
        draw_star
      } repeat
    grestore
    pop
  grestore
} def


%
%  Colored circle explosion.
%

/draw_dot { % - => -
  0 0 10 0 360 arc fill
} def

/colored_circle_explosion { % x y => -
  gsave
    translate
    .5 random add dup scale
    gsave
      10 {
        1.2 dup scale
	randcolor
        big_delay
        draw_dot
      } repeat
      0 setgray
      big_delay big_delay
      draw_dot
    grestore
  grestore
} def


%
% Support utilities ---------------------------------------------------
%

%
%  Delay loops.
%
/big_delay {
  1 2048 div sleep
} 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 newpath arc stroke

  1 setgray
  theta 1 theta trail_length add 1 sub {
    0 0 1
    4 -1 roll
    dup 1 add newpath 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 newpath arc stroke
  } for
  % draw the rest of the trail
  1 1 explosion_angle {
    draw_trail
    pause
  } 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 newpath 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 store

  %
  % 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 store
  } {
    trail_start_x screen_width unusable_screen_width sub ge {
      /trail_going_left? true store
    } {
      % randomly pick the direction
      /trail_going_left? random round 0 eq store
    } ifelse
  } ifelse

  % randomly pick the width of the firework trail
  trail_going_left? {
    /trail_width trail_start_x random mul store
  } {
    /trail_width screen_width trail_start_x sub random mul store
  } ifelse

  % randomly pick the height of the firework trail
  /trail_height screen_height random mul store

  trail_height minimum_trail_height le {
    /trail_height minimum_trail_height store
  } 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 store
} def


%
%  Animate a firework explosion.
%
/animate_explosion { % explosion_x explosion_y #explosions => -
  /#explosions exch store
  /explosion_y exch store
  /explosion_x exch store

  % 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

/do-it     { gsave
	/trail_width          0 def
	/trail_height         0 def
	/trail_start_x        0 def
	/trail_going_left? true def
	/explosion_angle      0 def
	/#explosions null store
	/explosion_y null store
	/explosion_x null store
	/which_explosion null store

      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
      randcolor
      trail_width 2 div 0 20 0 180 arc fill

      3 { big_delay } repeat
      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 
      minimum_#_explosions max

      % create the explosion!
      animate_explosion

      grestore
} def


%
%  Continually make fireworks.
%
/make_fireworks { % - => -
 {
	n-in-air-at-once {
	  { 32 dict begin
	      { do-it } loop
	    end
	   } fork
	} repeat
 } fork
} def


%
% Main ----------------------------------------------------------
%

% get the dimensions of the physical screen
clippath pathbbox
/screen_height exch def
/screen_width exch def

clear

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