[comp.lang.postscript] Escher's Fish.ps: Draws interlocking, endless fish

u-lchoqu%sunset.utah.edu@utah-cs.UUCP (Lee Choquette) (02/03/88)

This PostScript program comes from our freely distributed Macintosh
software library.  It draws interlocking stylized fish:  four large ones
in the center, growing continually smaller toward the edges.

----
Lee Choquette				u-lchoqu@ug.utah.edu
University of Utah Computer Center	utah-gr!utah-ug!u-lchoqu
MEB 3440				Choquette@UTAHCCA.Bitnet
Salt Lake City, UT  84112-1180		(801) 581-8504

	"All right, all right, we're not all programmed for perfection,
	 you know."

% "Square Limit" drawing by M. C. Escher
% Functions derived by Peter Henderson
% Paper in 1982 Conference on LISP and Functional Programming
% Coding by Michael Parsons, Sept. 1985
% Translation to PostScript for Sun CRT by Peter Bumbulis, July 1986
% Adaptation for Apple LaserWriter by Price Collins, Jan. 1988

% corner defines one corner of the picture at level n
/corner
  { dup 0 le
      {   pop        { }       { }        { } u quartet }
      { 1 sub dup corner exch side dup 90 rot u quartet }
    ifelse
  } def

% cycle combines 4 copies of image each rotated by 90 degrees
/cycle
  { dup 270 rot
    dup 180 rot
    dup 90 rot
    quartet
  } def

% fish gives a border to produce the final drawing
/fish
  { squarelimit exec
    newpath  
      0 0 moveto
      0 1 lineto
      1 1 lineto
      1 0 lineto
    closepath
    stroke
  } def

% flip inverts the image
/flip
  { .5 .5 translate
    1 -1 scale
    -.5 -.5 translate
  } def

% nonet arranges 9 images in equal sized squares to form 1 image
/nonet
  { 9 -1 roll 1 3 div 0  1  zoom
    9 -1 roll 1 3 div .5 1  zoom
    9 -1 roll 1 3 div 1  1  zoom
    9 -1 roll 1 3 div 0  .5 zoom
    9 -1 roll 1 3 div .5 .5 zoom
    9 -1 roll 1 3 div 1  .5 zoom
    9 -1 roll 1 3 div 0  0  zoom
    9 -1 roll 1 3 div .5 0  zoom
    9 -1 roll 1 3 div 1  0  zoom
    9 { exec } /repeat cvx 12 array astore cvx
  } def

% quadcorner forms 1 quarter of the image at level n.
/quadcorner
  { dup corner
    exch side
    dup dup
    90 rot
    dup u
    exch t
    90 rot
    dup 3 1 roll
    { q }
    90 rot
    nonet
  } def

% quartet combines 4 images, 1 in each quadrant, to produce 1 image
/quartet
  { 4 -1 roll 1 2 div 0 1 zoom
    4 -1 roll 1 2 div 1 1 zoom
    4 -1 roll 1 2 div 0 0 zoom
    4 -1 roll 1 2 div 1 0 zoom
    4 { exec } /repeat cvx 7 array astore cvx
  } def

% rot rotates image by the radians anti-clockwise about the image center
/rotcode
  { gsave
     .5 .5 translate
      rotate
      -.5 -.5 translate
      exec
    grestore
  } def
/rot { /rotcode cvx 3 array astore cvx } def

% side defines one side of the picture at level n
/side
  { dup 0 le
      {   pop  { } dup t 90 rot t quartet }
      { 1 sub side dup t 90 rot t quartet }
    ifelse
  } def

% squarelimit produces the fish drawing
/squarelimit { quadcorner cycle } def

% zoom scales an image up or down with one fixed point
/zoomcode
  { gsave
      2 copy neg exch neg exch 5 2 roll translate
      dup scale translate
      exec
    grestore
  } def
/zoom { /zoomcode cvx 5 array astore cvx } def

% p, q, r and s are the data needed to draw the fish
/p
  { gsave
      flip
      .0625 .0625 scale
      newpath
       3 12 moveto  0  8 lineto
                    0 13 lineto
                    3 12 lineto
       4 12 moveto  6 16 lineto
       7 10 moveto  4 11 lineto
                    4  6 lineto
                    7 10 lineto
       8  4 moveto 16  6 lineto
       8  8 moveto 10 12 lineto
                   13 11 lineto
                   16 12 lineto
       9 10 moveto 12  9 lineto
                   16 10 lineto
      10  0 moveto 12  2 lineto
                   16  3 lineto
      10 12 moveto 11 16 lineto
                   14 14 lineto
                   16 14 lineto
      12  0 moveto 13  1 lineto
                   16  2 lineto
      14  0 moveto 16  1 lineto
      16  4 moveto 12  4 lineto
                    8  0 lineto
                    6  1 lineto
                    0  0 lineto
                    4  3 lineto
                    8  8 lineto
                   12  7 lineto
                   16  8 lineto
      stroke
    grestore
  } def

/q
  { gsave
      flip
      .0625 .0625 scale
      newpath
       0  0 moveto  0  4 lineto
                    3  3 lineto
                    5  2 lineto
                    4  0 lineto
       0  6 moveto  7  5 lineto
       2  0 moveto  3  3 lineto
       2 16 moveto  4 11 lineto
                    4  9 lineto
                    6  9 lineto
       4  9 moveto  0  8 lineto
                    0 16 lineto
                    8 16 lineto
                   10 10 lineto
                   10  7 lineto
       4 16 moveto  6 11 lineto
                    6  9 lineto
                   12  6 lineto
                   16  0 lineto
                   15  6 lineto
                   16  8 lineto
                   13 12 lineto
                   12 16 lineto
                   16 16 lineto
       5  2 moveto  7  1 lineto
                    8  0 lineto
       6  0 moveto  7  1 lineto
       6 16 moveto  8 11 lineto
       8  1 moveto 11  1 lineto
                    9  3 lineto
                    8  1 lineto
       8 11 moveto  8  8 lineto
       9  4 moveto 12  4 lineto
                   10  6 lineto
                    9  4 lineto
      10 16 moveto 14  5 lineto
      13 16 moveto 16 10 lineto
      14 16 moveto 16 12 lineto
      15 16 moveto 16 14 lineto
      stroke
    grestore
  } def

/r
  { gsave
      flip
      .0625 .0625 scale
      newpath
       0  4 moveto  1  2 lineto
       0  8 moveto  2  4 lineto
       0 12 moveto  5  6 lineto
       0 16 moveto  8  8 lineto
       1 15 moveto  4 16 lineto
       2 14 moveto  8 16 lineto
       3 13 moveto  8 14 lineto
                   12 16 lineto
       5 11 moveto 12 13 lineto
                   16 16 lineto
       6  0 moveto 11  6 lineto
                   16 10 lineto
      11  0 moveto 12  4 lineto
                   16  8 lineto
      12  4 moveto 16  0 lineto
      15  1 moveto 16  2 lineto
      14  2 moveto 16  4 lineto
      13  3 moveto 16  6 lineto
      16 12 moveto 14 10 lineto
                    8  8 lineto
                    2  4 lineto
                    0  0 lineto
      stroke
    grestore
  } def

/s
  { gsave
      flip
      .0625 .0625 scale
      newpath
       0 16 moveto  4 14 lineto
                    8 14 lineto
                   16 16 lineto
                   10 12 lineto
                    8 10 lineto
                    7  8 lineto
                    7  3 lineto
                    8  0 lineto
       0  2 moveto  7  3 lineto
       0  4 moveto  7  6 lineto
       0  6 moveto  7  8 lineto
       0  8 moveto  8 10 lineto
       0 10 moveto  7 12 lineto
       0 12 moveto  2 15 lineto
      10  0 moveto 11  6 lineto
      12  0 moveto 13  3 lineto
      12 12 moveto 12  9 lineto
                   10 10 lineto
                   12 12 lineto
      14  5 moveto 16  4 lineto
      15  7 moveto 13  3 lineto
                   16  2 lineto
      15 11 moveto 15  8 lineto
                   13  9 lineto
                   15 11 lineto
      16  6 moveto 15  7 lineto
                   16  8 lineto
      stroke
    grestore
  } def

% t is one of the basic fish rearrangements
/t { { p } { q } { r } { s } quartet } def

% u is another of the basic fish rearrangements
/u { { q } 90 rot cycle } def

% display a level 2 fish
.001 setlinewidth
0 setgray
60 150 translate
500 500 scale
2 2 fish
showpage