%
% Date: Tue, 27 Jun 89 12:29:26 EDT
% From: alexis%yummy@gateway.mitre.org
% To: NeWS-makers@brillig.umd.edu
% Subject: Another rework of the psPyro
% 
% 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  ----------------------

%
%  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 10 def
/maximum_#_explosions 20 def

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


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

/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 cvi 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 setacolor
        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 setacolor
        big_delay
        draw_circle
      } repeat
    grestore
  grestore
} def


%
%  Star explosion.
%

/draw_star { % n-lines => n-lines
  gsave
    cvi 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 setacolor
        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 setacolor
      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 setacolor
  0 0 1 theta 1 sub theta newpath arc stroke

  1 setacolor
  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 setacolor
  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 setacolor
  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 cvi 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 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

/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 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 
      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
  /Action /DownTransition def
  %/Canvas sky def
end expressinterest
awaitevent pop
(Melting!\n) print flush
%!
%
% Date: Tue, 26 Jul 88 21:25:03 EDT
% To: NeWS-makers@brillig.umd.edu
% Subject: NeWS meltdown
% From: eagle!icdoc!Ist!jh@ucbvax.Berkeley.EDU  (Jeremy Huxtable)
% 
% I thought it was time one of these appeared as well....

% NeWS screen meltdown
%
% Jeremy Huxtable
%
% Mon Jul 25 17:36:06 BST 1988

% The procedure "melt" implements the ever-popular screen meltdown feature.

% Modified for X11/NeWS by Don Hopkins

/melt {
    10 dict begin
        /t currenttime def
	/c framebuffer newcanvas def
	framebuffer setcanvas clippath c reshapecanvas
	clippath pathbbox /height exch def /width exch def pop pop
	c /Transparent true put
        c /EventsConsumed /NoEvents put
	c /Mapped true put
	c setcanvas

	3000 {
	    /w random 200 mul 5 add def
	    /h random 200 mul 5 add def
	    /x width w sub random mul def
	    /y height h sub random mul def
	    newpath
%	    x y w h rectpath
	    x y w h ovalpath
%gsave random random random setrgbcolor stroke grestore
	    20 random mul 10 sub
	    -20 random mul
	    copyarea
	    pause
	} repeat

	framebuffer setcanvas
	c /Mapped false put
	/c null def
    end
} def

{melt} fork
10 60 div sleep
awaitevent pop
killprocessgroup