about summary refs log tree commit diff
path: root/generator/ppmpat.c
diff options
context:
space:
mode:
Diffstat (limited to 'generator/ppmpat.c')
-rw-r--r--generator/ppmpat.c1384
1 files changed, 746 insertions, 638 deletions
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 89585c3e..772fa51d 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -12,17 +12,244 @@
 
 #define _XOPEN_SOURCE  /* get M_PI in math.h */
 
+#include <assert.h>
 #include <math.h>
+#include <limits.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "ppm.h"
 #include "ppmdraw.h"
 
 
+typedef enum {
+    PAT_GINGHAM2,
+    PAT_GINGHAM3,
+    PAT_MADRAS,
+    PAT_TARTAN,
+    PAT_POLES,
+    PAT_SQUIG,
+    PAT_CAMO,
+    PAT_ANTICAMO
+} pattern;
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    pattern basePattern;
+    unsigned int width;
+    unsigned int height;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    unsigned int basePatternCount;
+    unsigned int gingham2;
+    unsigned int gingham3;
+    unsigned int madras;
+    unsigned int tartan;
+    unsigned int poles;
+    unsigned int squig;
+    unsigned int camo;
+    unsigned int anticamo;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "gingham2",  OPT_FLAG,   NULL, &gingham2,   0);
+    OPTENT3(0, "g2",        OPT_FLAG,   NULL, &gingham2,   0);
+    OPTENT3(0, "gingham3",  OPT_FLAG,   NULL, &gingham3,   0);
+    OPTENT3(0, "g3",        OPT_FLAG,   NULL, &gingham3,   0);
+    OPTENT3(0, "madras",    OPT_FLAG,   NULL, &madras,     0);
+    OPTENT3(0, "tartan",    OPT_FLAG,   NULL, &tartan,     0);
+    OPTENT3(0, "poles",     OPT_FLAG,   NULL, &poles,      0);
+    OPTENT3(0, "squig",     OPT_FLAG,   NULL, &squig,      0);
+    OPTENT3(0, "camo",      OPT_FLAG,   NULL, &camo,       0);
+    OPTENT3(0, "anticamo",  OPT_FLAG,   NULL, &anticamo,   0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    basePatternCount =
+        gingham2 +
+        gingham3 +
+        madras +
+        tartan +
+        poles +
+        squig +
+        camo +
+        anticamo;
+
+    if (basePatternCount < 1)
+        pm_error("You must specify a base pattern option such as -gingham2");
+    else if (basePatternCount > 1)
+        pm_error("You may not specify more than one base pattern option.  "
+                 "You specified %u", basePatternCount);
+    else {
+        if (gingham2)
+            cmdlineP->basePattern = PAT_GINGHAM2;
+        else if (gingham3)
+            cmdlineP->basePattern = PAT_GINGHAM3;
+        else if (madras)
+            cmdlineP->basePattern = PAT_MADRAS;
+        else if (tartan)
+            cmdlineP->basePattern = PAT_TARTAN;
+        else if (poles)
+            cmdlineP->basePattern = PAT_POLES;
+        else if (squig)
+            cmdlineP->basePattern = PAT_SQUIG;
+        else if (camo)
+            cmdlineP->basePattern = PAT_CAMO;
+        else if (anticamo)
+            cmdlineP->basePattern = PAT_ANTICAMO;
+        else
+            assert(false);  /* Every possibility is accounted for */
+    }
+    if (argc-1 != 2)
+        pm_error("You must specify 2 non-option arguments: width and height "
+                 "in pixels.  You specified %u", argc-1);
+    else {
+        cmdlineP->width  = atoi(argv[1]);
+        cmdlineP->height = atoi(argv[2]);
+
+        if (cmdlineP->width < 1)
+            pm_error("Width must be at least 1 pixel");
+        if (cmdlineP->height < 1)
+            pm_error("Height must be at least 1 pixel");
+    }
+}
+
+
+
+static void
+validateComputableDimensions(unsigned int const cols,
+                             unsigned int const rows) {
+
+    /*
+      Notes on width and height limits:
+
+      cols * 3, rows * 3 appear in madras, tartan
+      cols*rows appears in poles
+      cols+rows appears in squig
+
+      PPMD functions use signed integers for pixel positions
+      (because they allow you to specify points off the canvas).
+    */
+      
+    if (cols > INT_MAX/4 || rows > INT_MAX/4 || rows > INT_MAX/cols)
+        pm_error("Width and/or height are way too large: %u x %u",
+                 cols, rows);
+}
+
 
 
 static pixel
-random_anticamo_color(pixval const maxval) {
+randomColor(pixval const maxval) {
+
+    pixel p;
+
+    PPM_ASSIGN(p,
+               rand() % (maxval + 1),
+               rand() % (maxval + 1),
+               rand() % (maxval + 1)
+        );
+    
+    return p;
+}
+
+
+
+#define DARK_THRESH 0.25
+
+static pixel
+randomBrightColor(pixval const maxval) {
+
+    pixel p;
+
+    do {
+        p = randomColor(maxval);
+    } while (PPM_LUMIN(p) <= maxval * DARK_THRESH);
+
+    return p;
+}
+
+
+
+static pixel
+randomDarkColor(pixval const maxval) {
+
+    pixel p;
+
+    do {
+        p = randomColor(maxval);
+    } while (PPM_LUMIN(p) > maxval * DARK_THRESH);
+
+    return p;
+}
+
+
+
+static pixel
+averageTwoColors(pixel const p1,
+                 pixel const p2) {
+
+    pixel p;
+
+    PPM_ASSIGN(p,
+               (PPM_GETR(p1) + PPM_GETR(p2)) / 2,
+               (PPM_GETG(p1) + PPM_GETG(p2)) / 2,
+               (PPM_GETB(p1) + PPM_GETB(p2)) / 2);
+
+    return p;
+}
+
+
+
+static ppmd_drawproc average_drawproc;
+
+static void
+average_drawproc(pixel **     const pixels, 
+                 int          const cols, 
+                 int          const rows, 
+                 pixval       const maxval, 
+                 int          const col, 
+                 int          const row, 
+                 const void * const clientdata) {
+
+    if (col >= 0 && col < cols && row >= 0 && row < rows)
+        pixels[row][col] =
+            averageTwoColors(pixels[row][col], *((const pixel*) clientdata));
+}
+
+
+
+/*----------------------------------------------------------------------------
+   Camouflage stuff
+-----------------------------------------------------------------------------*/
+
+
+
+static pixel
+randomAnticamoColor(pixval const maxval) {
 
     int v1, v2, v3;
     pixel p;
@@ -70,17 +297,14 @@ random_anticamo_color(pixval const maxval) {
 
 
 
-/* Camouflage stuff. */
-
 static pixel
-random_camo_color(pixval const maxval) {
+randomCamoColor(pixval const maxval) {
 
-    int v1, v2, v3;
-    pixel p;
+    int const v1 = (maxval + 1 ) / 8;
+    int const v2 = (maxval + 1 ) / 4;
+    int const v3 = (maxval + 1 ) / 2;
 
-    v1 = (maxval + 1 ) / 8;
-    v2 = (maxval + 1 ) / 4;
-    v3 = (maxval + 1 ) / 2;
+    pixel p;
 
     switch (rand() % 10) {
     case 0:
@@ -122,6 +346,46 @@ rnduni(void) {
 
 
 
+static void
+clearBackground(pixel **     const pixels,
+                unsigned int const cols,
+                unsigned int const rows,
+                pixval       const maxval,
+                bool         const antiflag) {
+
+    pixel color;
+
+    if (antiflag)
+        color = randomAnticamoColor(maxval);
+    else
+        color = randomCamoColor(maxval);
+
+    ppmd_filledrectangle(
+        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
+        &color);
+}
+
+
+static void
+camoFill(pixel **         const pixels,
+         unsigned int     const cols,
+         unsigned int     const rows,
+         pixval           const maxval,
+         struct fillobj * const fh,
+         bool             const antiflag) {
+         
+    pixel color;
+
+    if (antiflag)
+        color = randomAnticamoColor(maxval);
+    else
+        color = randomCamoColor(maxval);
+
+    ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
+}        
+
+
+
 #define BLOBRAD 50
 
 #define MIN_POINTS 7
@@ -136,63 +400,68 @@ rnduni(void) {
 
 
 static void
+computeXsYs(int *        const xs,
+            int *        const ys,
+            unsigned int const cols,
+            unsigned int const rows,
+            unsigned int const pointCt) {
+
+    unsigned int const cx = rand() % cols;
+    unsigned int const cy = rand() % rows;
+    double const a = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
+        MIN_ELLIPSE_FACTOR;
+    double const b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
+        MIN_ELLIPSE_FACTOR;
+    double const theta = rnduni() * 2.0 * M_PI;
+    
+    unsigned int p;
+        
+    for (p = 0; p < pointCt; ++p) {
+        double const c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) +
+            MIN_POINT_FACTOR;
+        double const tx   = a * sin(p * 2.0 * M_PI / pointCt);
+        double const ty   = b * cos(p * 2.0 * M_PI / pointCt);
+        double const tang = atan2(ty, tx) + theta;
+        xs[p] = MAX(0, MIN(cols-1, cx + BLOBRAD * c * sin(tang)));
+        ys[p] = MAX(0, MIN(rows-1, cy + BLOBRAD * c * cos(tang)));
+    }
+}
+
+
+
+static void
 camo(pixel **     const pixels,
      unsigned int const cols,
      unsigned int const rows,
      pixval       const maxval,
      bool         const antiflag) {
 
-    pixel color;
-    int n, i, cx, cy;
-    struct fillobj * fh;
+    unsigned int const n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5;
 
-    /* Clear background. */
-    if (antiflag)
-        color = random_anticamo_color( maxval );
-    else
-        color = random_camo_color( maxval );
+    unsigned int i;
 
-    ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
-        &color);
+    clearBackground(pixels, cols, rows, maxval, antiflag);
 
-    n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5;
     for (i = 0; i < n; ++i) {
-        int points, p, xs[MAX_POINTS], ys[MAX_POINTS], x0, y0;
-        float a, b, c, theta, tang, tx, ty;
-        
-        cx = rand() % cols;
-        cy = rand() % rows;
-        
-        points = rand() % ( MAX_POINTS - MIN_POINTS + 1 ) + MIN_POINTS;
-        a = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
-            MIN_ELLIPSE_FACTOR;
-        b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
-            MIN_ELLIPSE_FACTOR;
-        theta = rnduni() * 2.0 * M_PI;
-        for (p = 0; p < points; ++p) {
-            tx = a * sin(p * 2.0 * M_PI / points);
-            ty = b * cos(p * 2.0 * M_PI / points);
-            tang = atan2(ty, tx) + theta;
-            c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) +
-                MIN_POINT_FACTOR;
-            xs[p] = cx + BLOBRAD * c * sin(tang);
-            ys[p] = cy + BLOBRAD * c * cos(tang);
-        }
-        x0 = (xs[0] + xs[points - 1]) / 2;
-        y0 = (ys[0] + ys[points - 1]) / 2;
+        unsigned int const pointCt =
+            rand() % (MAX_POINTS - MIN_POINTS + 1) + MIN_POINTS;
+
+        int xs[MAX_POINTS], ys[MAX_POINTS];
+        int x0, y0;
+        struct fillobj * fh;
+
+        computeXsYs(xs, ys, cols, rows, pointCt);
+
+        x0 = (xs[0] + xs[pointCt - 1]) / 2;
+        y0 = (ys[0] + ys[pointCt - 1]) / 2;
 
         fh = ppmd_fill_create();
 
         ppmd_polyspline(
-            pixels, cols, rows, maxval, x0, y0, points, xs, ys, x0, y0,
-            ppmd_fill_drawproc, fh );
+            pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0,
+            ppmd_fill_drawproc, fh);
         
-        if (antiflag)
-            color = random_anticamo_color(maxval);
-        else
-            color = random_camo_color(maxval);
-        ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
+        camoFill(pixels, cols, rows, maxval, fh, antiflag);
         
         ppmd_fill_destroy(fh);
     }
@@ -200,454 +469,398 @@ camo(pixel **     const pixels,
 
 
 
-static pixel
-random_color(pixval const maxval) {
-
-    pixel p;
-
-    PPM_ASSIGN(p,
-               rand() % (maxval + 1),
-               rand() % (maxval + 1),
-               rand() % (maxval + 1)
-        );
-    
-    return p;
-}
-
-
-
-#define DARK_THRESH 0.25
-
-#if __STDC__
-static pixel
-random_bright_color( pixval maxval )
-#else /*__STDC__*/
-static pixel
-random_bright_color( maxval )
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    pixel p;
-
-    do
-    {
-    p = random_color( maxval );
-    }
-    while ( PPM_LUMIN( p ) <= maxval * DARK_THRESH );
-
-    return p;
-    }
-
-#if __STDC__
-static pixel
-random_dark_color( pixval maxval )
-#else /*__STDC__*/
-static pixel
-random_dark_color( maxval )
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    pixel p;
-
-    do
-    {
-    p = random_color( maxval );
-    }
-    while ( PPM_LUMIN( p ) > maxval * DARK_THRESH );
-
-    return p;
-    }
-
-static pixel
-average_two_colors( p1, p2 )
-pixel p1, p2;
-    {
-    pixel p;
-
-    PPM_ASSIGN(
-    p, ( (int) PPM_GETR(p1) + (int) PPM_GETR(p2) ) / 2,
-    ( (int) PPM_GETG(p1) + (int) PPM_GETG(p2) ) / 2,
-    ( (int) PPM_GETB(p1) + (int) PPM_GETB(p2) ) / 2 );
+/*----------------------------------------------------------------------------
+   Gingham stuff
+-----------------------------------------------------------------------------*/
 
-    return p;
-    }
 
-/* Gingham stuff. */
 
 static void
-average_drawproc(pixel** const pixels, 
-                 int const cols, 
-                 int const rows, 
-                 pixval const maxval, 
-                 int const col, 
-                 int const row, 
-                 const void* const clientdata )
-{
-    if ( col >= 0 && col < cols && row >= 0 && row < rows )
-        pixels[row][col] =
-            average_two_colors( pixels[row][col], *( (pixel*) clientdata ) );
-}
+gingham2(pixel **     const pixels,
+         unsigned int const cols,
+         unsigned int const rows,
+         pixval       const maxval) {
 
-static void
-gingham2( pixel** pixels, int cols, int rows, pixval maxval )
-{
-    pixel const backcolor = random_dark_color( maxval );
-    pixel const forecolor = random_bright_color( maxval );
-    int const colso2 = cols / 2;
-    int const rowso2 = rows / 2;
+    pixel const backcolor = randomDarkColor(maxval);
+    pixel const forecolor = randomBrightColor(maxval);
+    unsigned int const colso2 = cols / 2;
+    unsigned int const rowso2 = rows / 2;
 
     /* Warp. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, colso2, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, colso2, 0, cols - colso2, rows,
-        PPMD_NULLDRAWPROC, &forecolor );
+        PPMD_NULLDRAWPROC, &forecolor);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rowso2, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rowso2, cols, rows - rowso2,
-        average_drawproc, &forecolor );
-    }
+        average_drawproc, &forecolor);
+}
+
+
 
-#if __STDC__
-static void
-gingham3( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
 static void
-gingham3( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int colso4, rowso4;
-    pixel backcolor, fore1color, fore2color;
-
-    colso4 = cols / 4;
-    rowso4 = rows / 4;
-    backcolor = random_dark_color( maxval );
-    fore1color = random_bright_color( maxval );
-    fore2color = random_bright_color( maxval );
+gingham3(pixel **     const pixels,
+         unsigned int const cols,
+         unsigned int const rows,
+         pixval       const maxval) {
+
+    unsigned int const colso4 = cols / 4;
+    unsigned int const rowso4 = rows / 4;
+
+    pixel const backcolor  = randomDarkColor(maxval);
+    pixel const fore1color = randomBrightColor(maxval);
+    pixel const fore2color = randomBrightColor(maxval);
 
     /* Warp. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, colso4, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, colso4, 0, colso4, rows, PPMD_NULLDRAWPROC,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 2 * colso4, 0, colso4, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 3 * colso4, 0, cols - colso4, rows,
-        PPMD_NULLDRAWPROC, &fore1color );
+        PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rowso4, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rowso4, cols, rowso4, average_drawproc,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 2 * rowso4, cols, rowso4,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 3 * rowso4, cols, rows - rowso4,
-        average_drawproc, &fore1color );
-    }
+        average_drawproc, &fore1color);
+}
+
+
 
-#if __STDC__
-static void
-madras( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
 static void
-madras( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int cols2, rows2, cols3, rows3, cols12, rows12, cols6a, rows6a, cols6b,
-    rows6b;
-    pixel backcolor, fore1color, fore2color;
-
-    cols2 = cols * 2 / 44;
-    rows2 = rows * 2 / 44;
-    cols3 = cols * 3 / 44;
-    rows3 = rows * 3 / 44;
-    cols12 = cols - 10 * cols2 - 4 * cols3;
-    rows12 = rows - 10 * rows2 - 4 * rows3;
-    cols6a = cols12 / 2;
-    rows6a = rows12 / 2;
-    cols6b = cols12 - cols6a;
-    rows6b = rows12 - rows6a;
-    backcolor = random_dark_color( maxval );
-    fore1color = random_bright_color( maxval );
-    fore2color = random_bright_color( maxval );
+madras(pixel **     const pixels,
+       unsigned int const cols,
+       unsigned int const rows,
+       pixval       const maxval) {
+
+    unsigned int const cols2  = cols * 2 / 44;
+    unsigned int const rows2  = rows * 2 / 44;
+    unsigned int const cols3  = cols * 3 / 44;
+    unsigned int const rows3  = rows * 3 / 44;
+    unsigned int const cols12 = cols - 10 * cols2 - 4 * cols3;
+    unsigned int const rows12 = rows - 10 * rows2 - 4 * rows3;
+    unsigned int const cols6a = cols12 / 2;
+    unsigned int const rows6a = rows12 / 2;
+    unsigned int const cols6b = cols12 - cols6a;
+    unsigned int const rows6b = rows12 - rows6a;
+    pixel const backcolor  = randomDarkColor(maxval);
+    pixel const fore1color = randomBrightColor(maxval);
+    pixel const fore2color = randomBrightColor(maxval);
 
     /* Warp. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols2, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols2, 0, cols3, rows, PPMD_NULLDRAWPROC,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols2 + cols3, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 2 * cols2 + cols3, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 3 * cols2 + cols3, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 4 * cols2 + cols3, 0, cols6a, rows,
-        PPMD_NULLDRAWPROC, &fore1color );
+        PPMD_NULLDRAWPROC, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 4 * cols2 + cols3 + cols6a, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 5 * cols2 + cols3 + cols6a, 0, cols3, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 5 * cols2 + 2 * cols3 + cols6a, 0, cols2,
-        rows, PPMD_NULLDRAWPROC, &backcolor );
+        rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 6 * cols2 + 2 * cols3 + cols6a, 0, cols3,
-        rows, PPMD_NULLDRAWPROC, &fore2color );
+        rows, PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 6 * cols2 + 3 * cols3 + cols6a, 0, cols2,
-        rows, PPMD_NULLDRAWPROC, &backcolor );
+        rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a, 0, cols6b,
-        rows, PPMD_NULLDRAWPROC, &fore1color );
+        rows, PPMD_NULLDRAWPROC, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
-        cols2, rows, PPMD_NULLDRAWPROC, &backcolor );
+        cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 8 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
-        cols2, rows, PPMD_NULLDRAWPROC, &fore2color );
+        cols2, rows, PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
-        cols2, rows, PPMD_NULLDRAWPROC, &backcolor );
+        cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 
-        0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color );
+        0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows2, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows2, cols, rows3, average_drawproc,
-        &fore2color );
+        &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows2 + rows3, cols, rows2,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 2 * rows2 + rows3, cols, rows2,
-        average_drawproc, &fore1color );
+        average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 3 * rows2 + rows3, cols, rows2,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 4 * rows2 + rows3, cols, rows6a,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 4 * rows2 + rows3 + rows6a, cols, rows2,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 5 * rows2 + rows3 + rows6a, cols, rows3,
-        average_drawproc, &fore1color );
+        average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 5 * rows2 + 2 * rows3 + rows6a, cols,
-        rows2, average_drawproc, &backcolor );
+        rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 6 * rows2 + 2 * rows3 + rows6a, cols,
-        rows3, average_drawproc, &fore1color );
+        rows3, average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 6 * rows2 + 3 * rows3 + rows6a, cols,
-        rows2, average_drawproc, &backcolor );
+        rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a, cols,
-        rows6b, average_drawproc, &fore2color );
+        rows6b, average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows2, average_drawproc, &backcolor );
+        cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 8 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows2, average_drawproc, &fore1color );
+        cols, rows2, average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows2, average_drawproc, &backcolor );
+        cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 
         10 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows3, average_drawproc, &fore2color );
-    }
+        cols, rows3, average_drawproc, &fore2color);
+}
+
+
 
-#if __STDC__
-static void
-tartan( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
 static void
-tartan( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int cols1, rows1, cols3, rows3, cols10, rows10, cols5a, rows5a, cols5b,
-    rows5b;
-    pixel backcolor, fore1color, fore2color;
-
-    cols1 = cols / 22;
-    rows1 = rows / 22;
-    cols3 = cols * 3 / 22;
-    rows3 = rows * 3 / 22;
-    cols10 = cols - 3 * cols1 - 3 * cols3;
-    rows10 = rows - 3 * rows1 - 3 * rows3;
-    cols5a = cols10 / 2;
-    rows5a = rows10 / 2;
-    cols5b = cols10 - cols5a;
-    rows5b = rows10 - rows5a;
-    backcolor = random_dark_color( maxval );
-    fore1color = random_bright_color( maxval );
-    fore2color = random_bright_color( maxval );
+tartan(pixel **     const pixels,
+       unsigned int const cols,
+       unsigned int const rows,
+       pixval       const maxval) {
+
+    unsigned int const cols1  = cols / 22;
+    unsigned int const rows1  = rows / 22;
+    unsigned int const cols3  = cols * 3 / 22;
+    unsigned int const rows3  = rows * 3 / 22;
+    unsigned int const cols10 = cols - 3 * cols1 - 3 * cols3;
+    unsigned int const rows10 = rows - 3 * rows1 - 3 * rows3;
+    unsigned int const cols5a = cols10 / 2;
+    unsigned int const rows5a = rows10 / 2;
+    unsigned int const cols5b = cols10 - cols5a;
+    unsigned int const rows5b = rows10 - rows5a;
+    pixel const backcolor  = randomDarkColor(maxval);
+    pixel const fore1color = randomBrightColor(maxval);
+    pixel const fore2color = randomBrightColor(maxval);
 
     /* Warp. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols5a, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols5a, 0, cols1, rows, PPMD_NULLDRAWPROC,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols5a + cols1, 0, cols5b, rows,
         PPMD_NULLDRAWPROC, &backcolor );
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + cols1, 0, cols3, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + cols1 + cols3, 0, cols1, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + 2 * cols1 + cols3, 0, cols3, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + 2 * cols1 + 2 * cols3, 0, cols1,
-        rows, PPMD_NULLDRAWPROC, (char*) &backcolor );
+        rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + 3 * cols1 + 2 * cols3, 0, cols3,
-        rows, PPMD_NULLDRAWPROC, &fore2color );
+        rows, PPMD_NULLDRAWPROC, &fore2color);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows5a, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows5a, cols, rows1, average_drawproc,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows5a + rows1, cols, rows5b,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + rows1, cols, rows3,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + rows1 + rows3, cols, rows1,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + rows3, cols, rows3,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + 2 * rows3, cols,
-        rows1, average_drawproc, &backcolor );
+        rows1, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + 3 * rows1 + 2 * rows3, cols,
-        rows3, average_drawproc, &fore2color );
-    }
+        rows3, average_drawproc, &fore2color);
+}
+
+
+
+/*----------------------------------------------------------------------------
+   Poles stuff
+-----------------------------------------------------------------------------*/
+
 
-/* Poles stuff. */
 
 #define MAXPOLES 500
 
-#if __STDC__
-static void
-poles( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
+
+
 static void
-poles( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int poles, i, xs[MAXPOLES], ys[MAXPOLES], col, row;
-    pixel colors[MAXPOLES];
+placeAndColorPolesRandomly(int *        const xs,
+                           int *        const ys,
+                           pixel *      const colors,
+                           unsigned int const cols,
+                           unsigned int const rows,
+                           pixval       const maxval,
+                           unsigned int const poleCt) {
 
-    poles = cols * rows / 30000;
+    unsigned int i;
 
-    /* Place and color poles randomly. */
-    for ( i = 0; i < poles; ++i )
-    {
-    xs[i] = rand() % cols;
-    ys[i] = rand() % rows;
-    colors[i] = random_bright_color( maxval );
+    for (i = 0; i < poleCt; ++i) {
+        xs[i] = rand() % cols;
+        ys[i] = rand() % rows;
+        colors[i] = randomBrightColor(maxval);
     }
+}
+
+
+
+static void
+assignInterpolatedColor(pixel * const resultP,
+                        pixel   const color1,
+                        double  const dist1,
+                        pixel   const color2,
+                        double  const dist2) {
+    
+    if (dist1 == 0) 
+        /* pixel is a pole */
+        *resultP = color1;
+    else {
+        double const sum = dist1 + dist2;
+
+        pixval const r = (PPM_GETR(color1)*dist2 + PPM_GETR(color2)*dist1)/sum;
+        pixval const g = (PPM_GETG(color1)*dist2 + PPM_GETG(color2)*dist1)/sum;
+        pixval const b = (PPM_GETB(color1)*dist2 + PPM_GETB(color2)*dist1)/sum;
+        
+        PPM_ASSIGN(*resultP, r, g, b);
+    }
+}
 
-    /* Now interpolate points. */
-    for ( row = 0; row < rows; ++row )
-    for ( col = 0; col < cols; ++col )
-        {
-        register long dist1, dist2, newdist, r, g, b;
-        pixel color1, color2;
 
-        /* Find two closest poles. */
-        dist1 = dist2 = 2000000000;
-        for ( i = 0; i < poles; ++i )
-        {
-        newdist = ( col - xs[i] ) * ( col - xs[i] ) +
-              ( row - ys[i] ) * ( row - ys[i] );
-        if ( newdist < dist1 )
-            {
-            dist2 = dist1;
-            color2 = color1;
-            dist1 = newdist;
-            color1 = colors[i];
-            }
-        else if ( newdist < dist2 )
-            {
-            dist2 = newdist;
-            color2 = colors[i];
-            }
-        }
 
-        /* And assign interpolated color. */
-        newdist = dist1 + dist2;
-        r = PPM_GETR(color1)*dist2/newdist + PPM_GETR(color2)*dist1/newdist;
-        g = PPM_GETG(color1)*dist2/newdist + PPM_GETG(color2)*dist1/newdist;
-        b = PPM_GETB(color1)*dist2/newdist + PPM_GETB(color2)*dist1/newdist;
-        PPM_ASSIGN( pixels[row][col], r, g, b );
+static void
+poles(pixel **     const pixels,
+      unsigned int const cols,
+      unsigned int const rows,
+      pixval       const maxval) {
+
+    unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000));
+    
+    int xs[MAXPOLES], ys[MAXPOLES];
+    pixel colors[MAXPOLES];
+    unsigned int row;
+
+    placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval, poleCt);
+
+    /* Interpolate points */
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            double dist1, dist2;
+            pixel color1, color2;
+            unsigned int i;
+
+            /* Find two closest poles. */
+            dist1 = dist2 = (SQR((double)cols) + SQR((double)rows));
+            for (i = 0; i < poleCt; ++i) {
+                double const newdist =
+                    (double)((int)col - xs[i]) * ((int)col - xs[i]) +
+                    (double)((int)row - ys[i]) * ((int)row - ys[i]);
+                if (newdist < dist1) {
+                    dist2  = dist1;
+                    color2 = color1;
+                    dist1  = newdist;
+                    color1 = colors[i];
+                } else if (newdist < dist2) {
+                    dist2  = newdist;
+                    color2 = colors[i];
+                }
+            }
+            assignInterpolatedColor(&pixels[row][col],
+                                    color1, dist1, color2, dist2);
         }
     }
+}
+
+
 
-/* Squig stuff. */
+/*----------------------------------------------------------------------------
+   Squig stuff
+-----------------------------------------------------------------------------*/
 
 #define SQUIGS 5
 #define SQ_POINTS 7
 #define SQ_MAXCIRCLE_POINTS 5000
 
-static int sq_radius, sq_circlecount;
+static int sq_circlecount;
 static pixel sq_colors[SQ_MAXCIRCLE_POINTS];
-static int sq_xoffs[SQ_MAXCIRCLE_POINTS], sq_yoffs[SQ_MAXCIRCLE_POINTS];
+static ppmd_point sq_offs[SQ_MAXCIRCLE_POINTS];
+
+
 
 static void
 validateSquigAspect(unsigned int const cols,
@@ -662,52 +875,61 @@ validateSquigAspect(unsigned int const cols,
 
 
 
-static void
-sq_measurecircle_drawproc(pixel** const pixels, 
-                          int const cols, 
-                          int const rows, 
-                          pixval const maxval, 
-                          int const col, 
-                          int const row, 
-                          const void* const clientdata)
-{
-    sq_xoffs[sq_circlecount] = col;
-    sq_yoffs[sq_circlecount] = row;
-    ++sq_circlecount;
+static ppmd_point
+vectorSum(ppmd_point const a,
+          ppmd_point const b) {
+
+    return ppmd_makePoint(a.x + b.x, a.y + b.y);
 }
 
+
+
+static ppmd_drawprocp sqMeasureCircleDrawproc;
+
 static void
-sq_rainbowcircle_drawproc(pixel** const pixels, 
-                          int const cols, 
-                          int const rows, 
-                          pixval const maxval, 
-                          int const col, 
-                          int const row, 
-                          const void* const clientdata )
-{
-    int i;
+sqMeasureCircleDrawproc(pixel**      const pixels, 
+                        unsigned int const cols, 
+                        unsigned int const rows, 
+                        pixval       const maxval, 
+                        ppmd_point   const p,
+                        const void * const clientdata) {
+
+    sq_offs[sq_circlecount++] = p;
+}
 
-    for ( i = 0; i < sq_circlecount; ++i )
-    ppmd_point_drawproc(
-        pixels, cols, rows, maxval, col + sq_xoffs[i], row + sq_yoffs[i],
-        &(sq_colors[i]) );
-    }
 
 
+static ppmd_drawprocp sqRainbowCircleDrawproc;
 
 static void
-sq_assign_colors(int     const circlecount,
-                 pixval  const maxval,
-                 pixel * const colors) {
+sqRainbowCircleDrawproc(pixel **     const pixels, 
+                        unsigned int const cols, 
+                        unsigned int const rows, 
+                        pixval       const maxval, 
+                        ppmd_point   const p,
+                        const void * const clientdata) {
 
-    pixel rc1, rc2, rc3;
-    float cco3;
     unsigned int i;
 
-    rc1 = random_bright_color(maxval);
-    rc2 = random_bright_color(maxval);
-    rc3 = random_bright_color(maxval);
-    cco3 = (circlecount - 1) / 3.0;
+    for (i = 0; i < sq_circlecount; ++i)
+        ppmd_point_drawprocp(
+            pixels, cols, rows, maxval, vectorSum(p, sq_offs[i]),
+            &sq_colors[i]);
+}
+
+
+
+static void
+sqAssignColors(unsigned int const circlecount,
+               pixval       const maxval,
+               pixel *      const colors) {
+
+    pixel const rc1 = randomBrightColor(maxval);
+    pixel const rc2 = randomBrightColor(maxval);
+    pixel const rc3 = randomBrightColor(maxval);
+    float const cco3 = (circlecount - 1) / 3.0;
+
+    unsigned int i;
 
     for (i = 0; i < circlecount ; ++i) {
         if (i < cco3) {
@@ -745,338 +967,224 @@ sq_assign_colors(int     const circlecount,
 }
 
 
-#if __STDC__
-static void
-squig( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
+
 static void
-squig( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
+clearImageToBlack(pixel **     const pixels,
+                  unsigned int const cols,
+                  unsigned int const rows,
+                  pixval       const maxval) {
+
     pixel color;
-    int i, j, xc[SQ_POINTS], yc[SQ_POINTS], x0, y0, x1, y1, x2, y2, x3, y3;
 
-    validateSquigAspect(cols, rows);
-    
-    /* Clear image to black. */
-    PPM_ASSIGN( color, 0, 0, 0 );
+    PPM_ASSIGN(color, 0, 0, 0);
+
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
-        &color );
+        &color);
+}
 
-    /* Draw the squigs. */
-    (void) ppmd_setlinetype( PPMD_LINETYPE_NODIAGS );
-    (void) ppmd_setlineclip( 0 );
-    for ( i = SQUIGS; i > 0; --i )
-    {
-    /* Measure circle. */
-    sq_radius = ( cols + rows ) / 2 / ( 25 + i * 2 );
-    sq_circlecount = 0;
-    ppmd_circle(
-        pixels, cols, rows, maxval, 0, 0, sq_radius,
-        sq_measurecircle_drawproc, NULL );
-    sq_assign_colors( sq_circlecount, maxval, sq_colors );
-
-    /* Choose wrap-around point. */
-    switch ( rand() % 4 )
-        {
-        case 0:
-        x1 = rand() % cols;
-        y1 = 0;
-        if ( x1 < cols / 2 )
-        xc[0] = rand() % ( x1 * 2 + 1);
+
+
+static void
+chooseWrapAroundPoint(unsigned int const cols,
+                      unsigned int const rows,
+                      ppmd_point * const pFirstP,
+                      ppmd_point * const pLastP,
+                      ppmd_point * const p0P,
+                      ppmd_point * const p1P,
+                      ppmd_point * const p2P,
+                      ppmd_point * const p3P) {
+                      
+    switch (rand() % 4) {
+    case 0:
+        p1P->x = rand() % cols;
+        p1P->y = 0;
+        if (p1P->x < cols / 2)
+            pFirstP->x = rand() % (p1P->x * 2 + 1);
         else
-        xc[0] = cols - 1 - rand() % ( ( cols - x1 ) * 2 );
-        yc[0] = rand() % rows;
-        x2 = x1;
-        y2 = rows - 1;
-        xc[SQ_POINTS - 1] = 2 * x2 - xc[0];
-        yc[SQ_POINTS - 1] = y2 - yc[0];
-        x0 = xc[SQ_POINTS - 1];
-        y0 = yc[SQ_POINTS - 1] - rows;
-        x3 = xc[0];
-        y3 = yc[0] + rows;
+            pFirstP->x = cols - 1 - rand() % ((cols - p1P->x) * 2);
+        pFirstP->y = rand() % rows;
+        p2P->x = p1P->x;
+        p2P->y = rows - 1;
+        pLastP->x = 2 * p2P->x - pFirstP->x;
+        pLastP->y = p2P->y - pFirstP->y;
+        p0P->x = pLastP->x;
+        p0P->y = pLastP->y - rows;
+        p3P->x = pFirstP->x;
+        p3P->y = pFirstP->y + rows;
         break;
 
-        case 1:
-        x2 = rand() % cols;
-        y2 = 0;
-        if ( x2 < cols / 2 )
-        xc[SQ_POINTS - 1] = rand() % ( x2 * 2 + 1);
+    case 1:
+        p2P->x = rand() % cols;
+        p2P->y = 0;
+        if (p2P->x < cols / 2)
+            pLastP->x = rand() % (p2P->x * 2 + 1);
         else
-        xc[SQ_POINTS - 1] = cols - 1 - rand() % ( ( cols - x2 ) * 2 );
-        yc[SQ_POINTS - 1] = rand() % rows;
-        x1 = x2;
-        y1 = rows - 1;
-        xc[0] = 2 * x1 - xc[SQ_POINTS - 1];
-        yc[0] = y1 - yc[SQ_POINTS - 1];
-        x0 = xc[SQ_POINTS - 1];
-        y0 = yc[SQ_POINTS - 1] + rows;
-        x3 = xc[0];
-        y3 = yc[0] - rows;
+            pLastP->x = cols - 1 - rand() % ((cols - p2P->x) * 2);
+        pLastP->y = rand() % rows;
+        p1P->x = p2P->x;
+        p1P->y = rows - 1;
+        pFirstP->x = 2 * p1P->x - pLastP->x;
+        pFirstP->y = p1P->y - pLastP->y;
+        p0P->x = pLastP->x;
+        p0P->y = pLastP->y + rows;
+        p3P->x = pFirstP->x;
+        p3P->y = pFirstP->y - rows;
         break;
 
-        case 2:
-        x1 = 0;
-        y1 = rand() % rows;
-        xc[0] = rand() % cols;
-        if ( y1 < rows / 2 )
-        yc[0] = rand() % ( y1 * 2 + 1);
+    case 2:
+        p1P->x = 0;
+        p1P->y = rand() % rows;
+        pFirstP->x = rand() % cols;
+        if (p1P->y < rows / 2)
+            pFirstP->y = rand() % (p1P->y * 2 + 1);
         else
-        yc[0] = rows - 1 - rand() % ( ( rows - y1 ) * 2 );
-        x2 = cols - 1;
-        y2 = y1;
-        xc[SQ_POINTS - 1] = x2 - xc[0];
-        yc[SQ_POINTS - 1] = 2 * y2 - yc[0];
-        x0 = xc[SQ_POINTS - 1] - cols;
-        y0 = yc[SQ_POINTS - 1];
-        x3 = xc[0] + cols;
-        y3 = yc[0];
+            pFirstP->y = rows - 1 - rand() % ((rows - p1P->y) * 2);
+        p2P->x = cols - 1;
+        p2P->y = p1P->y;
+        pLastP->x = p2P->x - pFirstP->x;
+        pLastP->y = 2 * p2P->y - pFirstP->y;
+        p0P->x = pLastP->x - cols;
+        p0P->y = pLastP->y;
+        p3P->x = pFirstP->x + cols;
+        p3P->y = pFirstP->y;
         break;
 
-        case 3:
-        x2 = 0;
-        y2 = rand() % rows;
-        xc[SQ_POINTS - 1] = rand() % cols;
-        if ( y2 < rows / 2 )
-        yc[SQ_POINTS - 1] = rand() % ( y2 * 2 + 1);
+    case 3:
+        p2P->x = 0;
+        p2P->y = rand() % rows;
+        pLastP->x = rand() % cols;
+        if (p2P->y < rows / 2)
+            pLastP->y = rand() % (p2P->y * 2 + 1);
         else
-        yc[SQ_POINTS - 1] = rows - 1 - rand() % ( ( rows - y2 ) * 2 );
-        x1 = cols - 1;
-        y1 = y2;
-        xc[0] = x1 - xc[SQ_POINTS - 1];
-        yc[0] = 2 * y1 - yc[SQ_POINTS - 1];
-        x0 = xc[SQ_POINTS - 1] + cols;
-        y0 = yc[SQ_POINTS - 1];
-        x3 = xc[0] - cols;
-        y3 = yc[0];
+            pLastP->y = rows - 1 - rand() % ((rows - p2P->y) * 2);
+        p1P->x = cols - 1;
+        p1P->y = p2P->y;
+        pFirstP->x = p1P->x - pLastP->x;
+        pFirstP->y = 2 * p1P->y - pLastP->y;
+        p0P->x = pLastP->x + cols;
+        p0P->y = pLastP->y;
+        p3P->x = pFirstP->x - cols;
+        p3P->y = pFirstP->y;
         break;
-        }
-
-    for ( j = 1; j < SQ_POINTS - 1; ++j )
-        {
-        xc[j] = ( rand() % ( cols - 2 * sq_radius ) ) + sq_radius;
-        yc[j] = ( rand() % ( rows - 2 * sq_radius ) ) + sq_radius;
-        }
-
-    ppmd_line(
-        pixels, cols, rows, maxval, x0, y0, x1, y1,
-        sq_rainbowcircle_drawproc, NULL );
-    ppmd_polyspline(
-        pixels, cols, rows, maxval, x1, y1, SQ_POINTS, xc, yc, x2, y2,
-        sq_rainbowcircle_drawproc, NULL );
-    ppmd_line(
-        pixels, cols, rows, maxval, x2, y2, x3, y3,
-        sq_rainbowcircle_drawproc, NULL );
     }
-    }
-
+}
 
 
-/* Test pattern.  Just a place to put ppmdraw exercises. */
 
 static void
-test(pixel **     const pixels,
-     unsigned int const cols,
-     unsigned int const rows,
-     pixval       const maxval) {
+squig(pixel **     const pixels,
+      unsigned int const cols,
+      unsigned int const rows,
+      pixval       const maxval) {
 
-    pixel color;
-    struct fillobj * fh;
+    int i;
 
-    /* Clear image to black. */
-    PPM_ASSIGN( color, 0, 0, 0 );
-    ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
-        &color);
+    validateSquigAspect(cols, rows);
+    
+    clearImageToBlack(pixels, cols, rows, maxval);
 
-    fh = ppmd_fill_create();
-
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/8, rows/8, cols/2, rows/4, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/2, rows/4, cols-cols/8, rows/8, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols-cols/8, rows/8, cols/2, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_spline3(pixels, cols, rows, maxval, 
-                 cols/2, rows/2, cols/2-cols/16, rows/2-rows/10, 
-                 cols/2-cols/8, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_spline3(pixels, cols, rows, maxval, 
-                 cols/2-cols/8, rows/2, cols/4+cols/16, rows/2+rows/10, 
-                 cols/4, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/4, rows/2, cols/8, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/8, rows/2, cols/8, rows/8, ppmd_fill_drawproc, fh);
-
-    PPM_ASSIGN(color, maxval, maxval, maxval);
-    ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
+    /* Draw the squigs. */
+    ppmd_setlinetype(PPMD_LINETYPE_NODIAGS);
+    ppmd_setlineclip(0);
+    for (i = SQUIGS; i > 0; --i) {
+        unsigned int const radius = (cols + rows) / 2 / (25 + i * 2);
+
+        ppmd_point c[SQ_POINTS];
+        ppmd_point p0, p1, p2, p3;
+        sq_circlecount = 0;
+        ppmd_circlep(pixels, cols, rows, maxval,
+                     ppmd_makePoint(0, 0), radius,
+                     sqMeasureCircleDrawproc, NULL);
+        sqAssignColors(sq_circlecount, maxval, sq_colors);
+
+        chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1],
+                              &p0, &p1, &p2, &p3);
 
-    ppmd_fill_destroy(fh);
+        {
+            /* Do the middle points */
+            unsigned int j;
+
+            for (j = 1; j < SQ_POINTS - 1; ++j) {
+              /* validateSquigAspect() assures that
+                 cols - 2 * radius, rows -2 * radius are positive
+              */
+                c[j].x = (rand() % (cols - 2 * radius)) + radius;
+                c[j].y = (rand() % (rows - 2 * radius)) + radius;
+            }
+        }
 
+        ppmd_linep(
+            pixels, cols, rows, maxval, p0, p1,
+            sqRainbowCircleDrawproc, NULL);
+        ppmd_polysplinep(
+            pixels, cols, rows, maxval, p1, SQ_POINTS, c, p2,
+            sqRainbowCircleDrawproc, NULL);
+        ppmd_linep(
+            pixels, cols, rows, maxval, p2, p3,
+            sqRainbowCircleDrawproc, NULL);
+    }
 }
 
 
 
 int
-main(int argc, char ** argv) {
-
-    pixel** pixels;
-    int argn, pattern, cols, rows;
-#define PAT_NONE 0
-#define PAT_GINGHAM2 1
-#define PAT_GINGHAM3 2
-#define PAT_MADRAS 3
-#define PAT_TARTAN 4
-#define PAT_POLES 5
-#define PAT_SQUIG 6
-#define PAT_CAMO 7
-#define PAT_ANTICAMO 8
-#define PAT_TEST 9
-    const char* const usage = "-gingham2|-g2|-gingham3|-g3|-madras|-tartan|-poles|-squig|-camo|-anticamo <width> <height>";
-
-
-    ppm_init(&argc, argv);
-
-    argn = 1;
-    pattern = PAT_NONE;
-
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-gingham2", 9 ) ||
-             pm_keymatch( argv[argn], "-g2", 3 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_GINGHAM2;
-        }
-        else if ( pm_keymatch( argv[argn], "-gingham3", 9 ) ||
-                  pm_keymatch( argv[argn], "-g3", 3 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_GINGHAM3;
-        }
-        else if ( pm_keymatch( argv[argn], "-madras", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_MADRAS;
-        }
-        else if ( pm_keymatch( argv[argn], "-tartan", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_TARTAN;
-        }
-        else if ( pm_keymatch( argv[argn], "-poles", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_POLES;
-        }
-        else if ( pm_keymatch( argv[argn], "-squig", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_SQUIG;
-        }
-        else if ( pm_keymatch( argv[argn], "-camo", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_CAMO;
-        }
-        else if ( pm_keymatch( argv[argn], "-anticamo", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_ANTICAMO;
-        }
-        else if ( pm_keymatch( argv[argn], "-test", 3 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_TEST;
-        }
-        else
-            pm_usage( usage );
-        ++argn;
-    }
-    if ( pattern == PAT_NONE )
-        pm_error( "a base pattern must be specified" );
-
-    if ( argn == argc )
-        pm_usage( usage);
-    if ( sscanf( argv[argn], "%d", &cols ) != 1 )
-        pm_usage( usage );
-    ++argn;
-    if ( argn == argc )
-        pm_usage( usage);
-    if ( sscanf( argv[argn], "%d", &rows ) != 1 )
-        pm_usage( usage );
-    ++argn;
-
-    if ( argn != argc )
-        pm_usage( usage);
-
-    if (cols < 1)
-        pm_error("width must be at least 1");
-    if (rows < 1)
-        pm_error("height must be at least 1");
-
-    srand( (int) ( time( 0 ) ^ getpid( ) ) );
-    pixels = ppm_allocarray( cols, rows );
-
-    switch ( pattern )
-    {
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    pixel ** pixels;
+
+    pm_proginit(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+
+    validateComputableDimensions(cmdline.width, cmdline.height);
+    
+    srand(pm_randseed());
+    pixels = ppm_allocarray(cmdline.width, cmdline.height);
+
+    switch (cmdline.basePattern) {
     case PAT_GINGHAM2:
-        gingham2( pixels, cols, rows, PPM_MAXMAXVAL );
+        gingham2(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_GINGHAM3:
-        gingham3( pixels, cols, rows, PPM_MAXMAXVAL );
+        gingham3(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_MADRAS:
-        madras( pixels, cols, rows, PPM_MAXMAXVAL );
+        madras(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_TARTAN:
-        tartan( pixels, cols, rows, PPM_MAXMAXVAL );
+        tartan(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_POLES:
-        poles( pixels, cols, rows, PPM_MAXMAXVAL );
+        poles(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_SQUIG:
-        squig( pixels, cols, rows, PPM_MAXMAXVAL );
+        squig(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_CAMO:
-        camo( pixels, cols, rows, PPM_MAXMAXVAL, 0 );
+        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0);
         break;
 
     case PAT_ANTICAMO:
-        camo( pixels, cols, rows, PPM_MAXMAXVAL, 1 );
-        break;
-
-    case PAT_TEST:
-        test( pixels, cols, rows, PPM_MAXMAXVAL );
+        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 1);
         break;
 
     default:
-        pm_error( "can't happen!" );
+        pm_error("can't happen!");
     }
 
-    /* All done, write it out. */
-    ppm_writeppm( stdout, pixels, cols, rows, PPM_MAXMAXVAL, 0 );
-    pm_close( stdout );
+    ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height,
+                 PPM_MAXMAXVAL, 0);
+
+    ppm_freearray(pixels, cmdline.height);
 
-    exit( 0 );
+    return 0;
 }