[net.sources] Source for M.C.Escher "Square Limit" picture.

msp@ukc.UUCP (07/14/86)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#-----cut here-----cut here-----cut here-----cut here-----
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	fish.m
#	graphics.m
#	line.m
# This archive created: Fri Jul 11 10:26:31 1986
cat << \SHAR_EOF > README
SQUARE LIMIT PICTURE

The 3 following files are all you need to generate the M.C.Escher "Square Limit"
picture (apart from a Miranda interpreter!).

To actually create a picture file, compile the code using

    mira fish.m

and type

    pri (fish k) &> ofile

where k is in the range 0-2 and ofile is an output file. The output format is
line specifications, 1 line per physical file line.

I would appreciate it if the general graphics routines were not incorporated
in any published work, as I will need to include them in my thesis! The specific
"Square Limit" functions were originally developed by Peter Henderson and have 
already been published by him.

Have fun!
--Mike Parsons.
------------------------------------------------------------------------------
Mike Parsons    UUCP: ..!seismo!mcvax!ukc!msp             JANET: msp@uk.ac.ukc
                MAIL: Computing Lab, University of Kent, Canterbury, Kent, UK.
SHAR_EOF
cat << \SHAR_EOF > fish.m
%insert "graphics.m" || All the graphics primitives.
%list
||
|| The following are all the functions required to produce the famous 
|| "Square Limit" drawing by M. C. Escher. These functions were derived by 
|| Peter Henderson. See his paper in 1982 Conf. on LISP and Func. Prog.
|| (C) Miranda coding by Michael Parsons, September 1985
||
%nolist

|| quartet combines 4 images, one image in each quadrant, to produce one image.
quartet :: image -> image -> image -> image -> image
quartet a b c d = col [row [a,b], row [c,d]]

|| nonet arranges 9 images in equal sized squares to form 1 image.
nonet :: image -> image -> image -> image -> image -> image -> image -> image -> image -> image
nonet a b c d e f g h i = col [row [a,b,c], row [d,e,f], row [g,h,i]]

|| cycle combines 4 smaller copies of an image, each rotated by a different
|| multiple of 90 degrees.
cycle :: image -> image
cycle i = quartet i (rot threepibytwo i) (rot pibytwo i) (rot pi i)

|| t is one of the basic fish rearrangements.
t :: image
t = quartet p q r s

|| u is another of the basic fish rearrangements.
u :: image
u = cycle (rot pibytwo q)

|| side defines one side of the picture at level n.
side :: num -> image
side 0 = quartet [] [] rt t
         where                  || This where due to compiler bug?
         rt = rot pibytwo t
side n = quartet sn sn rt t
         where
         rt = rot pibytwo t
         sn = side (n-1)

|| corner defines one corner of the picture at level n.
corner :: num -> image
corner 0 = quartet [] [] [] u
corner n = quartet (corner (n-1)) sn (rot pibytwo sn) u
           where
           sn = side (n-1)

|| quadcorner forms one quarter of the image at level n;
quadcorner :: num -> image
quadcorner n = nonet (corner n) sn sn rsn u rt rsn rt (rot pibytwo q)
               where
               sn = side n
               rsn = rot pibytwo sn
               rt = rot pibytwo t

|| squarelimit produces the fish drawing.
squarelimit :: num -> image
squarelimit n = cycle (quadcorner n)

|| fish gives a border to produce the final drawing.
fish :: num -> image
fish n = (squarelimit n) ++ border

|| All the data needed to draw the fish.
p :: image
p = [Ln (Pt 0 0.8125) (Pt 0 0.5),
    Ln (Pt 0 0.8125) (Pt 0.1875 0.75),
    Ln (Pt 0.75 0) (Pt 0.8125 6.25e-2),
    Ln (Pt 0.1875 0.75) (Pt 0 0.5),
    Ln (Pt 0.375 1) (Pt 0.25 0.75),
    Ln (Pt 0.25 0.6875) (Pt 0.4375 0.625),
    Ln (Pt 0.25 0.6875) (Pt 0.25 0.375),
    Ln (Pt 0.4375 0.625) (Pt 0.25 0.375),
    Ln (Pt 0.6875 1) (Pt 0.625 0.75),
    Ln (Pt 0.6875 1) (Pt 0.875 0.875),
    Ln (Pt 0.875 0.875) (Pt 1 0.875),
    Ln (Pt 0.625 0.75) (Pt 0.8125 0.6875),
    Ln (Pt 0.8125 0.6875) (Pt 1 0.75),
    Ln (Pt 0.625 0.75) (Pt 0.5 0.5),
    Ln (Pt 0.5625 0.625) (Pt 0.75 0.5625),
    Ln (Pt 0.75 0.5625) (Pt 1 0.625),
    Ln (Pt 0.5 0.5) (Pt 0.75 0.4375),
    Ln (Pt 0.75 0.4375) (Pt 1 0.5),
    Ln (Pt 0.5 0.5) (Pt 0.25 0.1875),
    Ln (Pt 0.25 0.1875) (Pt 0 0),
    Ln (Pt 0.5 0.25) (Pt 1 0.375),
    Ln (Pt 1 0.25) (Pt 0.75 0.25),
    Ln (Pt 0.75 0.25) (Pt 0.5 0),
    Ln (Pt 0.5 0) (Pt 0.375 6.25e-2),
    Ln (Pt 0.375 6.25e-2) (Pt 0 0),
    Ln (Pt 0.625 0) (Pt 0.75 0.125),
    Ln (Pt 0.75 0.125) (Pt 1 0.1875),
    Ln (Pt 1 0.125) (Pt 0.8125 6.25e-2),
    Ln (Pt 1 6.25e-2) (Pt 0.875 0)]

q :: image
q = [Ln (Pt 0 0) (Pt 0 0.25),
    Ln (Pt 0 0.5) (Pt 0 1),
    Ln (Pt 0 1) (Pt 0.5 1),
    Ln (Pt 0.75 1) (Pt 1 1),
    Ln (Pt 0.125 1) (Pt 0.25 0.6875),
    Ln (Pt 0.25 1) (Pt 0.375 0.6875),
    Ln (Pt 0.375 1) (Pt 0.5 0.6875),
    Ln (Pt 0.5 1) (Pt 0.625 0.625),
    Ln (Pt 0.25 0.6875) (Pt 0.25 0.5625),
    Ln (Pt 0.375 0.6875) (Pt 0.375 0.5625),
    Ln (Pt 0.5 0.6875) (Pt 0.5 0.5),
    Ln (Pt 0.625 0.625) (Pt 0.625 0.4375),
    Ln (Pt 0.625 1) (Pt 0.875 0.3125),
    Ln (Pt 0.75 1) (Pt 0.8125 0.75),
    Ln (Pt 0.8125 1) (Pt 1 0.625),
    Ln (Pt 0.875 1) (Pt 1 0.75),
    Ln (Pt 0.9375 1) (Pt 1 0.875),
    Ln (Pt 0 0.25) (Pt 0.1875 0.1875),
    Ln (Pt 0 0.375) (Pt 0.4375 0.3125),
    Ln (Pt 0 0.5) (Pt 0.25 0.5625),
    Ln (Pt 0.125 0) (Pt 0.1875 0.1875),
    Ln (Pt 0.1875 0.1875) (Pt 0.3125 0.125),
    Ln (Pt 0.3125 0.125) (Pt 0.25 0),
    Ln (Pt 0.3125 0.125) (Pt 0.4375 6.25e-2),
    Ln (Pt 0.4375 6.25e-2) (Pt 0.5 0),
    Ln (Pt 0.5 6.25e-2) (Pt 0.6875 6.25e-2),
    Ln (Pt 0.6875 6.25e-2) (Pt 0.5625 0.1875),
    Ln (Pt 0.5625 0.1875) (Pt 0.5 6.25e-2),
    Ln (Pt 0.5625 0.25) (Pt 0.75 0.25),
    Ln (Pt 0.75 0.25) (Pt 0.625 0.375),
    Ln (Pt 0.625 0.375) (Pt 0.5625 0.25),
    Ln (Pt 1 0) (Pt 0.75 0.375),
    Ln (Pt 0.375 0) (Pt 0.4375 6.25e-2),
    Ln (Pt 0.75 0.375) (Pt 0.375 0.5625),
    Ln (Pt 0.375 0.5625) (Pt 0.25 0.5625),
    Ln (Pt 1 0) (Pt 0.9375 0.375),
    Ln (Pt 0.9375 0.375) (Pt 1 0.5),
    Ln (Pt 1 0.5) (Pt 0.8125 0.75)]

r :: image
r = [Ln (Pt 0.5 0.5) (Pt 0.125 0.25),
    Ln (Pt 0.125 0.25) (Pt 0 0),
    Ln (Pt 0 0.5) (Pt 0.125 0.25),
    Ln (Pt 0 0.25) (Pt 6.25e-2 0.125),
    Ln (Pt 0 1) (Pt 0.5 0.5),
    Ln (Pt 0 0.75) (Pt 0.3125 0.375),
    Ln (Pt 6.25e-2 0.9375) (Pt 0.25 1),
    Ln (Pt 0.125 0.875) (Pt 0.5 1),
    Ln (Pt 0.1875 0.8125) (Pt 0.5 0.875),
    Ln (Pt 0.3125 0.6875) (Pt 0.75 0.8125),
    Ln (Pt 0.5 0.875) (Pt 0.75 1),
    Ln (Pt 0.75 0.8125) (Pt 1 1),
    Ln (Pt 0.5 0.5) (Pt 0.875 0.625),
    Ln (Pt 1 0.625) (Pt 0.6875 0.375),
    Ln (Pt 0.6875 0.375) (Pt 0.375 0),
    Ln (Pt 1 0.5) (Pt 0.75 0.25),
    Ln (Pt 1 0.75) (Pt 0.875 0.625),
    Ln (Pt 0.75 0.25) (Pt 0.6875 0),
    Ln (Pt 0.75 0.25) (Pt 1 0),
    Ln (Pt 1 0.375) (Pt 0.8125 0.1875),
    Ln (Pt 1 0.25) (Pt 0.875 0.125),
    Ln (Pt 1 0.125) (Pt 0.9375 6.25e-2)]

s :: image
s = [Ln (Pt 0 1) (Pt 0.25 0.875),
    Ln (Pt 0.125 0.9375) (Pt 0 0.75),
    Ln (Pt 0.25 0.875) (Pt 0.5 0.875),
    Ln (Pt 0.5 0.875) (Pt 1 1),
    Ln (Pt 1 1) (Pt 0.625 0.75),
    Ln (Pt 0.625 0.75) (Pt 0.5 0.625),
    Ln (Pt 0 0.625) (Pt 0.4375 0.75),
    Ln (Pt 0 0.5) (Pt 0.5 0.625),
    Ln (Pt 0 0.375) (Pt 0.4375 0.5),
    Ln (Pt 0 0.25) (Pt 0.4375 0.375),
    Ln (Pt 0 0.125) (Pt 0.4375 0.1875),
    Ln (Pt 0.5 0.625) (Pt 0.4375 0.5),
    Ln (Pt 0.4375 0.5) (Pt 0.4375 0.1875),
    Ln (Pt 0.4375 0.1875) (Pt 0.5 0),
    Ln (Pt 0.625 0) (Pt 0.6875 0.375),
    Ln (Pt 0.75 0) (Pt 0.8125 0.1875),
    Ln (Pt 0.8125 0.1875) (Pt 0.9375 0.4375),
    Ln (Pt 0.9375 0.4375) (Pt 1 0.5),
    Ln (Pt 1 0.125) (Pt 0.8125 0.1875),
    Ln (Pt 1 0.25) (Pt 0.875 0.3125),
    Ln (Pt 1 0.375) (Pt 0.9375 0.4375),
    Ln (Pt 0.75 0.5625) (Pt 0.75 0.75),
    Ln (Pt 0.75 0.75) (Pt 0.625 0.625),
    Ln (Pt 0.625 0.625) (Pt 0.75 0.5625),
    Ln (Pt 0.8125 0.5625) (Pt 0.9375 0.5),
    Ln (Pt 0.9375 0.5) (Pt 0.9375 0.6875),
    Ln (Pt 0.9375 0.6875) (Pt 0.8125 0.5625)]
SHAR_EOF
cat << \SHAR_EOF > graphics.m
%list
||
|| graphics.m: Copyright Michael S. Parsons, September 1985
||
|| A collection of functions performing the primitive vector operations.
|| All these functions work on line end points. Each point has coordinates
|| which are real values 0 <= x,y <= 1.
||
%nolist

||
|| The algebraic data types.
||

%insert "line.m" || The point and line algebraic data types.

|| polar is the same in a polar coordinate format. Angle then length.
polar ::= Po num num

||
|| A couple of type synonyms for brevity.
||

|| string is the normal string type.
string == [char]

|| An image is a one-level list of lines.
image == [line]

||
|| Assorted constants.
||

twopi = 2 * pi
pibytwo = pi / 2
threepibytwo = 3 * pi / 2

|| imsize is the multiplier for writing out the coordinates as integers.
imsize :: num
imsize = 1024

|| The top left corner of the picture.
topleft :: point
topleft = Pt 0 0

|| The top right of the picture.
topright :: point
topright = Pt 1 0

|| The middle of the picture.
middle = Pt 0.5 0.5

|| border is the square round the perimeter of the picture.
border :: image
border = square topleft 1

||
|| Some useful functions.
||

|| sqr is the normal square function.
sqr :: num -> num
sqr x = x * x

|| ctop converts the cartesian coords to polar coords.
ctop :: point -> polar
ctop (Pt x y) = Po (a x y) l
                where
                a 0 0 = 0
                a 0 y = pi/2, y >= 0
                a 0 y = -pi/2
                a x y = arctan(y / x), x >= 0 & y >= 0
                a x y = pi + arctan(y / x),  x < 0
                a x y = 2 * pi + arctan(y / x)
                l = sqrt(sqr x + sqr y)

|| pri prints out the image as line endpoints: 4 numbers to a line.
pri :: image -> string
pri = concat.(map prl)
      where
      prl (Ln p1 p2) = concat [prp p1, " ", prp p2, "\n"]
                       where
                       prp (Pt x y) = concat [ip x, " ", ip y]
                                      where
                                      ip p = show(entier(p * imsize))

|| imap maps the given function onto each point of the image.
imap :: (point -> point) -> image -> image
imap f i = map imap' i
           where
           imap' (Ln p1 p2) = Ln (f p1) (f p2)

|| join produces a list of lines connecting the points given.
join :: [point] -> image
join pts = join' (hd pts) pts
           where
           join' h [x]     = [Ln h x]
           join' h (a:b:x) = (Ln a b):join' h (b:x)

|| square produces a square given top left hand corner and size.
square :: point -> num -> image
square (Pt x y) s = join [Pt x y, Pt (x+s) y, Pt (x+s) (y+s), Pt x (y+s)]

||
|| Start of graphics primitives.
||

|| tr translates an image by amount t.
tr :: point -> image -> image
tr t = imap (trp t)
       where
       trp (Pt x y) (Pt p q) = Pt (p+x) (q+y)

|| rot rotates an image by th radians anti-clockwise, about the image centre.
rot :: num -> image -> image
rot th i = tr middle (imap rotp (tr (negpoint middle) i))
           where
           rotp p = Pt (coord cos) (coord sin)
                    where
                    coord f = l * (f (a + th))
                              where
                              (Po a l) = ctop p

|| ref90 reflects everything about the line x = 0.5
ref90 :: image -> image
ref90 = imap ref90p
        where
        ref90p (Pt x y) = Pt (1-x) y

|| negpoint changes the sign of the point coordinates.
negpoint :: point -> point
negpoint (Pt x y) = Pt (-x) (-y)

|| scale scales an image up or down in x or y directions with one fixed point.
scale :: point -> point -> image -> image
scale s t i = tr t (imap (scalep s) (tr (negpoint t) i))
              where
              scalep (Pt sx sy) (Pt x y) = Pt (x * sx) (y * sy)

|| beside squashes one image against the other in the ratio r1:r2
beside :: num -> num -> image -> image -> image
beside r1 r2 i1 i2 = scale (sfac r1) topleft i1 ++ scale (sfac r2) topright i2
                     where
                     sfac r = Pt (r / (r1 + r2)) 1

|| above squashes one image above the other in the ratio r1:r2
above :: num -> num -> image -> image -> image
above r1 r2 i1 i2 = rot threepibytwo (beside r1 r2 (rot pibytwo i1) (rot pibytwo i2))

|| seq produces a sequence of images from a list of images.
seq :: (num -> num -> image -> image -> image) -> [image] -> image
seq f [i] = i
seq f (a:x) = f 1 (#x) a (seq f x)

|| row produces a row of (different) images.
row :: [image] -> image
row = seq beside

|| likewise col
col :: [image] -> image
col = seq above

|| sameseq produces a sequence of images all the same.
sameseq :: (num -> num -> image -> image -> image) -> num -> image -> image
sameseq f 0 i = []
sameseq f n i = f 1 (n-1) i (sameseq f (n-1) i)

|| samerow uses sameseq to give a row of identical images.
samerow :: num -> image -> image
samerow = sameseq beside

|| likewise samecol.
samecol :: num -> image -> image
samecol = sameseq above

|| samegrid produces a grid of images, all the same.
samegrid :: num -> num -> image -> image
samegrid x y = (samecol y).(samerow x)

|| mirror90 combines an image with its reflection about the line x = 0.5
mirror90 :: image -> image
mirror90 i = i ++ ref90 i

|| squashes produces a sequence of squashed images, getting more squashed.
squashes :: num -> image -> image
squashes 0 i = []
squashes n i = above 1 (n-1) (samerow n i) (squashes (n-1) i)

|| star merges a sequence of rotations of the same object.
star :: num -> image -> image
star n i = star' 0
           where
           incth = twopi/n
           star' th = [], th >= twopi
           star' th = rot th i ++ star' (th+incth)
SHAR_EOF
cat << \SHAR_EOF > line.m
%list
||
|| Line and point as algebraic types.
||
%nolist

|| point is the coordinate structure. x then y.
point ::= Pt num num

|| line is a specification of a line by its end points.
line ::= Ln point point

SHAR_EOF
#	End of shell archive
exit 0