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.c582
1 files changed, 472 insertions, 110 deletions
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index fe1a1d27..908c200f 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -10,15 +10,21 @@
 ** implied warranty.
 */
 
-#define _XOPEN_SOURCE  /* get M_PI in math.h */
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+                           /* get M_PI in math.h */
+#define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
+#define SPIROGRAPHS 0   /* Spirograph to be added soon */
 
 #include <assert.h>
 #include <math.h>
 #include <limits.h>
+#include <string.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
+#include "nstring.h"
 #include "ppm.h"
 #include "ppmdraw.h"
 
@@ -28,28 +34,138 @@ typedef enum {
     PAT_GINGHAM3,
     PAT_MADRAS,
     PAT_TARTAN,
+    PAT_ARGYLE1,
+    PAT_ARGYLE2,
     PAT_POLES,
     PAT_SQUIG,
     PAT_CAMO,
-    PAT_ANTICAMO
-} pattern;
+    PAT_ANTICAMO,
+    PAT_SPIRO1,
+    PAT_SPIRO2,
+    PAT_SPIRO3
+} Pattern;
 
-struct cmdlineInfo {
+typedef struct {
+/*----------------------------------------------------------------------------
+   An ordered list of colors with a cursor.
+-----------------------------------------------------------------------------*/
+    unsigned int count;
+    unsigned int index;
+        /* Current position in the list */
+    pixel *      color;
+        /* Malloced array 'count' in size. */
+} ColorTable;
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    pattern basePattern;
+    Pattern      basePattern;
     unsigned int width;
     unsigned int height;
+    unsigned int colorSpec;
+    ColorTable   colorTable;
     unsigned int randomseed;
     unsigned int randomseedSpec;
 };
 
 
+static void
+validateColorCount(Pattern      const basePattern,
+                   unsigned int const colorCount) {
+
+    if (colorCount == 0)
+        pm_error("-color: no colors specified");
+
+    switch (basePattern) {
+    case PAT_GINGHAM2:
+    case PAT_ARGYLE1:
+    case PAT_SPIRO1:
+        if (colorCount != 2)
+            pm_error("Wrong number of colors: %u. "
+                     "2 colors are required for the specified pattern.",
+                     colorCount);
+        break;
+    case PAT_GINGHAM3:
+    case PAT_MADRAS:
+    case PAT_TARTAN:
+    case PAT_ARGYLE2:
+        if (colorCount != 3)
+            pm_error("Wrong number of colors: %u. "
+                     "3 colors are required for the specified pattern.",
+                     colorCount);
+        break;
+    case PAT_POLES:
+        if (colorCount < 2)
+            pm_error("Too few colors: %u. "
+                     "At least 2 colors are required "
+                     "for the specified pattern.",
+                     colorCount);
+        break;
+    case PAT_SQUIG:
+    case PAT_CAMO:
+    case PAT_ANTICAMO:
+        if (colorCount < 3)
+            pm_error("Wrong number of colors: %u. "
+                     "At least 3 colors are required "
+                     "for the specified pattern.",
+                     colorCount);
+        break;
+
+    case PAT_SPIRO2:
+    case PAT_SPIRO3:
+    default:
+        pm_error("INTERNAL ERROR.");
+    }
+}
+
+
+
+static void
+parseColorOpt(const char ** const colorText,
+              ColorTable  * const colorTableP,
+              Pattern       const basePattern) {
+/*----------------------------------------------------------------------------
+    String-list argument to -color is a comma-separated array of
+    color names or values, e.g.:
+    "-color=red,white,blue"
+    "-color=rgb:ff/ff/ff,rgb:00/00/00,rgb:80/80/ff"
+
+    Input:
+      Color name/value string-list: colorText[]
+
+    Output values:
+      Color array: colorTableP->color[]
+      Number of colors found: colorTableP->colors
+----------------------------------------------------------------------------*/
+    unsigned int colorCount;
+    unsigned int i;
+    pixel * inColor;
+
+    for (colorCount = 0; colorText[colorCount] != NULL; ++colorCount)
+        ;
+
+    MALLOCARRAY(inColor, colorCount);
+
+    if (!inColor)
+        pm_error("Failed to allocate table space for %u colors "
+                 "specified by -color", colorCount);
+
+    for (i = 0; i < colorCount; ++i)
+        inColor[i] = ppm_parsecolor(colorText[i], PPM_MAXMAXVAL);
+
+    validateColorCount(basePattern, colorCount);
+
+    colorTableP->count = colorCount;
+    colorTableP->index = 0;  /* initial value */
+    colorTableP->color = inColor;
+}
+
+
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 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.
@@ -60,15 +176,21 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
+    const char ** colorText;
     unsigned int basePatternCount;
     unsigned int gingham2;
     unsigned int gingham3;
     unsigned int madras;
     unsigned int tartan;
+    unsigned int argyle1;
+    unsigned int argyle2;
     unsigned int poles;
     unsigned int squig;
     unsigned int camo;
     unsigned int anticamo;
+    unsigned int spiro1;
+    unsigned int spiro2;
+    unsigned int spiro3;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -85,6 +207,10 @@ parseCommandLine(int argc, const char ** argv,
             &madras,     0);
     OPTENT3(0, "tartan",        OPT_FLAG,   NULL,
             &tartan,     0);
+    OPTENT3(0, "argyle1",       OPT_FLAG,   NULL,
+            &argyle1,     0);
+    OPTENT3(0, "argyle2",       OPT_FLAG,   NULL,
+            &argyle2,     0);
     OPTENT3(0, "poles",         OPT_FLAG,   NULL,
             &poles,      0);
     OPTENT3(0, "squig",         OPT_FLAG,   NULL,
@@ -93,7 +219,19 @@ parseCommandLine(int argc, const char ** argv,
             &camo,       0);
     OPTENT3(0, "anticamo",      OPT_FLAG,   NULL,
             &anticamo,   0);
-    OPTENT3(0, "randomseed",    OPT_UINT,   &cmdlineP->randomseed,
+#if SPIROGRAPHS != 0
+    OPTENT3(0, "spiro1",        OPT_FLAG,   NULL,
+            &spiro1,     0);
+    OPTENT3(0, "spiro2",        OPT_FLAG,   NULL,
+            &spiro1,     0);
+    OPTENT3(0, "spiro3",        OPT_FLAG,   NULL,
+            &spiro1,     0);
+#else
+    spiro1 = spiro2 = spiro3 = 0;
+#endif
+    OPTENT3(0, "color",         OPT_STRINGLIST, &colorText,
+            &cmdlineP->colorSpec,           0);
+    OPTENT3(0, "randomseed",    OPT_UINT,       &cmdlineP->randomseed,
             &cmdlineP->randomseedSpec,      0);
 
     opt.opt_table = option_def;
@@ -102,16 +240,14 @@ parseCommandLine(int argc, const char ** argv,
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
 
     basePatternCount =
-        gingham2 +
-        gingham3 +
-        madras +
-        tartan +
+        gingham2 + gingham3 + madras + tartan + argyle1 + argyle2 +
         poles +
         squig +
-        camo +
-        anticamo;
+        camo + anticamo +
+        spiro1 + spiro2 + spiro3;
 
     if (basePatternCount < 1)
         pm_error("You must specify a base pattern option such as -gingham2");
@@ -127,6 +263,10 @@ parseCommandLine(int argc, const char ** argv,
             cmdlineP->basePattern = PAT_MADRAS;
         else if (tartan)
             cmdlineP->basePattern = PAT_TARTAN;
+        else if (argyle1)
+            cmdlineP->basePattern = PAT_ARGYLE1;
+        else if (argyle2)
+            cmdlineP->basePattern = PAT_ARGYLE2;
         else if (poles)
             cmdlineP->basePattern = PAT_POLES;
         else if (squig)
@@ -135,9 +275,22 @@ parseCommandLine(int argc, const char ** argv,
             cmdlineP->basePattern = PAT_CAMO;
         else if (anticamo)
             cmdlineP->basePattern = PAT_ANTICAMO;
+        else if (spiro1)
+            cmdlineP->basePattern = PAT_SPIRO1;
+        else if (spiro2)
+            cmdlineP->basePattern = PAT_SPIRO2;
+        else if (spiro3)
+            cmdlineP->basePattern = PAT_SPIRO3;
         else
             assert(false);  /* Every possibility is accounted for */
     }
+
+    if (cmdlineP->colorSpec) {
+        parseColorOpt(colorText, &cmdlineP->colorTable, cmdlineP->basePattern);
+        free(colorText);
+    } else
+        cmdlineP->colorTable.count = 0;
+
     if (argc-1 != 2)
         pm_error("You must specify 2 non-option arguments: width and height "
                  "in pixels.  You specified %u", argc-1);
@@ -150,7 +303,15 @@ parseCommandLine(int argc, const char ** argv,
         if (cmdlineP->height < 1)
             pm_error("Height must be at least 1 pixel");
     }
-    free(option_def);
+}
+
+
+
+static void
+freeCmdline(struct CmdlineInfo const cmdline) {
+
+    if (cmdline.colorSpec)
+        free(cmdline.colorTable.color);
 }
 
 
@@ -169,7 +330,7 @@ validateComputableDimensions(unsigned int const cols,
       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);
@@ -187,7 +348,7 @@ randomColor(pixval const maxval) {
                rand() % (maxval + 1),
                rand() % (maxval + 1)
         );
-    
+
     return p;
 }
 
@@ -242,12 +403,12 @@ averageTwoColors(pixel const p1,
 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, 
+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)
@@ -257,6 +418,29 @@ average_drawproc(pixel **     const pixels,
 
 
 
+static void
+nextColor(ColorTable * const colorTableP) {
+/*----------------------------------------------------------------------------
+  Increment index, return it to 0 if we have used all the colors
+-----------------------------------------------------------------------------*/
+    colorTableP->index = (colorTableP->index + 1) % colorTableP->count;
+}
+
+
+
+static void
+nextColorBg(ColorTable * const colorTableP) {
+/*----------------------------------------------------------------------------
+  Increment index, return it to 1 if we have used all the colors (color[0] is
+  the background color, it's outside the cycle)
+-----------------------------------------------------------------------------*/
+    colorTableP->index = colorTableP->index % (colorTableP->count - 1) + 1;
+        /* Works when index == 0, but no callers rely on this. */
+
+}
+
+
+
 /*----------------------------------------------------------------------------
    Camouflage stuff
 -----------------------------------------------------------------------------*/
@@ -282,7 +466,7 @@ randomAnticamoColor(pixval const maxval) {
     case 3:
         PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v2);
         break;
-        
+
     case 4:
     case 5:
         PPM_ASSIGN(p, rand() % v2, rand() % v2, rand() % v1 + v3);
@@ -306,7 +490,7 @@ randomAnticamoColor(pixval const maxval) {
         PPM_ASSIGN(p, rand() % v1 + v3, rand() % v1 + v3, rand() % v2);
         break;
     }
-    
+
     return p;
 }
 
@@ -335,7 +519,7 @@ randomCamoColor(pixval const maxval) {
         /* dark green */
         PPM_ASSIGN(p, rand() % v2, rand() % v2 + 3 * v1, rand() % v2);
         break;
-    
+
     case 6:
     case 7:
         /* brown */
@@ -362,15 +546,18 @@ rnduni(void) {
 
 
 static void
-clearBackground(pixel **     const pixels,
-                unsigned int const cols,
-                unsigned int const rows,
-                pixval       const maxval,
-                bool         const antiflag) {
+clearBackgroundCamo(pixel **     const pixels,
+                    unsigned int const cols,
+                    unsigned int const rows,
+                    pixval       const maxval,
+                    ColorTable * const colorTableP,
+                    bool         const antiflag) {
 
     pixel color;
 
-    if (antiflag)
+    if (colorTableP->count > 0) {
+        color = colorTableP->color[0];
+    } else if (antiflag)
         color = randomAnticamoColor(maxval);
     else
         color = randomCamoColor(maxval);
@@ -381,23 +568,29 @@ clearBackground(pixel **     const pixels,
 }
 
 
+
 static void
 camoFill(pixel **         const pixels,
          unsigned int     const cols,
          unsigned int     const rows,
          pixval           const maxval,
          struct fillobj * const fh,
+         ColorTable     * const colorTableP,
          bool             const antiflag) {
-         
+
     pixel color;
 
-    if (antiflag)
+    if (colorTableP->count > 0) {
+        assert(colorTableP->index < colorTableP->count);
+        color = colorTableP->color[colorTableP->index];
+        nextColorBg(colorTableP);
+    } else if (antiflag)
         color = randomAnticamoColor(maxval);
     else
         color = randomCamoColor(maxval);
 
     ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
-}        
+}
 
 
 
@@ -428,9 +621,9 @@ computeXsYs(int *        const xs,
     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;
@@ -448,14 +641,20 @@ static void
 camo(pixel **     const pixels,
      unsigned int const cols,
      unsigned int const rows,
+     ColorTable * const colorTableP,
      pixval       const maxval,
      bool         const antiflag) {
 
-    unsigned int const n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5;
+    unsigned int const n = (rows * cols) / SQR(BLOBRAD) * 5;
 
     unsigned int i;
 
-    clearBackground(pixels, cols, rows, maxval, antiflag);
+    clearBackgroundCamo(pixels, cols, rows, maxval, colorTableP, antiflag);
+
+    if (colorTableP->count > 0) {
+        assert(colorTableP->count > 1);
+        colorTableP->index = 1;  /* Foreground colors start at 1 */
+    }
 
     for (i = 0; i < n; ++i) {
         unsigned int const pointCt =
@@ -475,9 +674,9 @@ camo(pixel **     const pixels,
         ppmd_polyspline(
             pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0,
             ppmd_fill_drawproc, fh);
-        
-        camoFill(pixels, cols, rows, maxval, fh, antiflag);
-        
+
+        camoFill(pixels, cols, rows, maxval, fh, colorTableP, antiflag);
+
         ppmd_fill_destroy(fh);
     }
 }
@@ -485,19 +684,21 @@ camo(pixel **     const pixels,
 
 
 /*----------------------------------------------------------------------------
-   Gingham stuff
+   Plaid patterns
 -----------------------------------------------------------------------------*/
 
-
-
 static void
 gingham2(pixel **     const pixels,
          unsigned int const cols,
          unsigned int const rows,
+         ColorTable   const colorTable,
          pixval       const maxval) {
 
-    pixel const backcolor = randomDarkColor(maxval);
-    pixel const forecolor = randomBrightColor(maxval);
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const forecolor = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
     unsigned int const colso2 = cols / 2;
     unsigned int const rowso2 = rows / 2;
 
@@ -524,15 +725,19 @@ static void
 gingham3(pixel **     const pixels,
          unsigned int const cols,
          unsigned int const rows,
+         ColorTable   const colorTable,
          pixval       const maxval) {
 
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const fore1color = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
+    pixel const fore2color = colorSpec ?
+                            colorTable.color[2] : randomBrightColor(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,
@@ -568,8 +773,16 @@ static void
 madras(pixel **     const pixels,
        unsigned int const cols,
        unsigned int const rows,
+       ColorTable   const colorTable,
        pixval       const maxval) {
 
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const fore1color = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
+    pixel const fore2color = colorSpec ?
+                            colorTable.color[2] : randomBrightColor(maxval);
     unsigned int const cols2  = cols * 2 / 44;
     unsigned int const rows2  = rows * 2 / 44;
     unsigned int const cols3  = cols * 3 / 44;
@@ -580,9 +793,6 @@ madras(pixel **     const pixels,
     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(
@@ -631,7 +841,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
         cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 
+        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b,
         0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
@@ -681,7 +891,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 
+        pixels, cols, rows, maxval, 0,
         10 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows3, average_drawproc, &fore2color);
 }
@@ -692,8 +902,16 @@ static void
 tartan(pixel **     const pixels,
        unsigned int const cols,
        unsigned int const rows,
+       ColorTable   const colorTable,
        pixval       const maxval) {
 
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const fore1color = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
+    pixel const fore2color = colorSpec ?
+                            colorTable.color[2] : randomBrightColor(maxval);
     unsigned int const cols1  = cols / 22;
     unsigned int const rows1  = rows / 22;
     unsigned int const cols3  = cols * 3 / 22;
@@ -704,9 +922,6 @@ tartan(pixel **     const pixels,
     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(
@@ -763,6 +978,71 @@ tartan(pixel **     const pixels,
 
 
 
+static void
+drawAndFillDiamond(pixel **     const pixels,
+                   unsigned int const cols,
+                   unsigned int const rows,
+                   pixval       const maxval,
+                   pixel        const forecolor) {
+
+    unsigned int const colso2 = cols / 2;
+    unsigned int const rowso2 = rows / 2;
+
+    ppmd_pathbuilder * const pathBuilderP = ppmd_pathbuilder_create();
+
+    ppmd_pathbuilder_setBegPoint(pathBuilderP,
+                 ppmd_makePoint (colso2, 0));
+
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(cols-1, rowso2)));
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(colso2, rows-1)));
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(0,      rowso2)));
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(colso2, 0)));
+
+    ppmd_fill_path(pixels, cols, rows, maxval,
+                   ppmd_pathbuilder_pathP(pathBuilderP), forecolor);
+}
+
+
+
+static void
+argyle(pixel **     const pixels,
+       unsigned int const cols,
+       unsigned int const rows,
+       ColorTable   const colorTable,
+       pixval       const maxval,
+       bool         const stripes) {
+
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+        colorTable.color[0] : randomDarkColor(maxval);
+    pixel const forecolor = colorSpec ?
+        colorTable.color[1] : randomBrightColor(maxval);
+
+    /* Fill canvas with background to start */
+    ppmd_filledrectangle(
+        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
+        &backcolor);
+
+    drawAndFillDiamond(pixels, cols, rows, maxval, forecolor);
+
+    if (stripes) {
+         /* Connect corners with thin stripes */
+         pixel const stripecolor =
+             colorSpec ? colorTable.color[2] : randomBrightColor(maxval);
+
+         ppmd_line(pixels, cols, rows, maxval, 0, 0, cols-1, rows-1,
+              PPMD_NULLDRAWPROC, (char *) &stripecolor);
+         ppmd_line(pixels, cols, rows, maxval, cols-1, 0, 0, rows-1,
+              PPMD_NULLDRAWPROC, (char *) &stripecolor);
+    }
+}
+
+
+
 /*----------------------------------------------------------------------------
    Poles stuff
 -----------------------------------------------------------------------------*/
@@ -780,14 +1060,21 @@ placeAndColorPolesRandomly(int *        const xs,
                            unsigned int const cols,
                            unsigned int const rows,
                            pixval       const maxval,
+                           ColorTable * const colorTableP,
                            unsigned int const poleCt) {
 
     unsigned int i;
 
     for (i = 0; i < poleCt; ++i) {
+
         xs[i] = rand() % cols;
         ys[i] = rand() % rows;
-        colors[i] = randomBrightColor(maxval);
+
+        if (colorTableP->count > 0) {
+            colors[i] = colorTableP->color[colorTableP->index];
+            nextColor(colorTableP);
+        } else
+            colors[i] = randomBrightColor(maxval);
     }
 }
 
@@ -799,8 +1086,8 @@ assignInterpolatedColor(pixel * const resultP,
                         double  const dist1,
                         pixel   const color2,
                         double  const dist2) {
-    
-    if (dist1 == 0) 
+
+    if (dist1 == 0)
         /* pixel is a pole */
         *resultP = color1;
     else {
@@ -809,7 +1096,7 @@ assignInterpolatedColor(pixel * const resultP,
         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);
     }
 }
@@ -820,15 +1107,17 @@ static void
 poles(pixel **     const pixels,
       unsigned int const cols,
       unsigned int const rows,
+      ColorTable * const colorTableP,
       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);
+    placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval,
+                               colorTableP, poleCt);
 
     /* Interpolate points */
 
@@ -871,11 +1160,15 @@ poles(pixel **     const pixels,
 #define SQ_POINTS 7
 #define SQ_MAXCIRCLE_POINTS 5000
 
-static int sq_circlecount;
-static pixel sq_colors[SQ_MAXCIRCLE_POINTS];
-static ppmd_point sq_offs[SQ_MAXCIRCLE_POINTS];
-
+struct Squig {
+    unsigned int circleCt;
+    pixel        color[SQ_MAXCIRCLE_POINTS];
+    ppmd_point   off[SQ_MAXCIRCLE_POINTS];
+};
 
+typedef struct {
+    struct Squig * squigP;
+} SqClientData;
 
 static void
 validateSquigAspect(unsigned int const cols,
@@ -884,7 +1177,7 @@ validateSquigAspect(unsigned int const cols,
     if (cols / rows >= 25 || rows / cols >= 25)
         pm_error("Image too narrow.  Aspect ratio: %u/%u=%f "
                  "is outside accepted range: 0.04 - 25.0",
-                 cols, rows, (float)cols/rows ); 
+                 cols, rows, (float)cols/rows );
 
 }
 
@@ -902,14 +1195,18 @@ vectorSum(ppmd_point const a,
 static ppmd_drawprocp sqMeasureCircleDrawproc;
 
 static void
-sqMeasureCircleDrawproc(pixel**      const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+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;
+    const SqClientData * const sqClientDataP = clientdata;
+
+    struct Squig * const squigP = sqClientDataP->squigP;
+
+    squigP->off[squigP->circleCt++] = p;
 }
 
 
@@ -917,19 +1214,46 @@ sqMeasureCircleDrawproc(pixel**      const pixels,
 static ppmd_drawprocp sqRainbowCircleDrawproc;
 
 static void
-sqRainbowCircleDrawproc(pixel **     const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqRainbowCircleDrawproc(pixel **     const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
+    const SqClientData * const sqClientDataP = clientdata;
+
+    struct Squig * const squigP = sqClientDataP->squigP;
+
     unsigned int i;
 
-    for (i = 0; i < sq_circlecount; ++i)
+    for (i = 0; i < squigP->circleCt; ++i)
         ppmd_point_drawprocp(
-            pixels, cols, rows, maxval, vectorSum(p, sq_offs[i]),
-            &sq_colors[i]);
+            pixels, cols, rows, maxval, vectorSum(p, squigP->off[i]),
+            &squigP->color[i]);
+}
+
+
+
+static void
+chooseSqPoleColors(ColorTable * const colorTableP,
+                   pixval       const maxval,
+                   pixel *      const color1P,
+                   pixel *      const color2P,
+                   pixel *      const color3P) {
+
+    if (colorTableP->count > 0) {
+        *color1P = colorTableP->color[colorTableP->index];
+        nextColor(colorTableP);
+        *color2P = colorTableP->color[colorTableP->index];
+        nextColor(colorTableP);
+        *color3P = colorTableP->color[colorTableP->index];
+        nextColor(colorTableP);
+    } else {
+        *color1P = randomBrightColor(maxval);
+        *color2P = randomBrightColor(maxval);
+        *color3P = randomBrightColor(maxval);
+    }
 }
 
 
@@ -937,16 +1261,19 @@ sqRainbowCircleDrawproc(pixel **     const pixels,
 static void
 sqAssignColors(unsigned int const circlecount,
                pixval       const maxval,
+               ColorTable * const colorTableP,
                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;
 
+    pixel rc1;
+    pixel rc2;
+    pixel rc3;
     unsigned int i;
 
-    for (i = 0; i < circlecount ; ++i) {
+    chooseSqPoleColors(colorTableP, maxval, &rc1, &rc2, &rc3);
+
+    for (i = 0; i < circlecount; ++i) {
         if (i < cco3) {
             float const frac = (float)i/cco3;
             PPM_ASSIGN(colors[i],
@@ -984,14 +1311,19 @@ sqAssignColors(unsigned int const circlecount,
 
 
 static void
-clearImageToBlack(pixel **     const pixels,
-                  unsigned int const cols,
-                  unsigned int const rows,
-                  pixval       const maxval) {
+clearBackgroundSquig(pixel **     const pixels,
+                     unsigned int const cols,
+                     unsigned int const rows,
+                     ColorTable * const colorTableP,
+                     pixval       const maxval) {
 
     pixel color;
 
-    PPM_ASSIGN(color, 0, 0, 0);
+    if (colorTableP->count > 0) {
+        color = colorTableP->color[0];
+        colorTableP->index = 1;
+    } else
+        PPM_ASSIGN(color, 0, 0, 0);
 
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
@@ -1009,7 +1341,7 @@ chooseWrapAroundPoint(unsigned int const cols,
                       ppmd_point * const p1P,
                       ppmd_point * const p2P,
                       ppmd_point * const p3P) {
-                      
+
     switch (rand() % 4) {
     case 0:
         p1P->x = rand() % cols;
@@ -1091,27 +1423,37 @@ static void
 squig(pixel **     const pixels,
       unsigned int const cols,
       unsigned int const rows,
+      ColorTable * const colorTableP,
       pixval       const maxval) {
 
     int i;
 
     validateSquigAspect(cols, rows);
-    
-    clearImageToBlack(pixels, cols, rows, maxval);
+
+    clearBackgroundSquig(pixels, cols, rows, colorTableP, maxval);
 
     /* 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);
 
+        struct Squig squig;
+
+        SqClientData sqClientData;
+
         ppmd_point c[SQ_POINTS];
         ppmd_point p0, p1, p2, p3;
-        sq_circlecount = 0;
+
+        squig.circleCt = 0;
+
+        sqClientData.squigP = &squig;
+
         ppmd_circlep(pixels, cols, rows, maxval,
                      ppmd_makePoint(0, 0), radius,
-                     sqMeasureCircleDrawproc, NULL);
-        sqAssignColors(sq_circlecount, maxval, sq_colors);
+                     sqMeasureCircleDrawproc, &sqClientData);
+        sqAssignColors(squig.circleCt, maxval, colorTableP, squig.color);
 
         chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1],
                               &p0, &p1, &p2, &p3);
@@ -1131,13 +1473,13 @@ squig(pixel **     const pixels,
 
         ppmd_linep(
             pixels, cols, rows, maxval, p0, p1,
-            sqRainbowCircleDrawproc, NULL);
+            sqRainbowCircleDrawproc, &sqClientData);
         ppmd_polysplinep(
             pixels, cols, rows, maxval, p1, SQ_POINTS, c, p2,
-            sqRainbowCircleDrawproc, NULL);
+            sqRainbowCircleDrawproc, &sqClientData);
         ppmd_linep(
             pixels, cols, rows, maxval, p2, p3,
-            sqRainbowCircleDrawproc, NULL);
+            sqRainbowCircleDrawproc, &sqClientData);
     }
 }
 
@@ -1146,50 +1488,68 @@ squig(pixel **     const pixels,
 int
 main(int argc, const char ** argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     pixel ** pixels;
 
     pm_proginit(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
 
     validateComputableDimensions(cmdline.width, cmdline.height);
-    
+
     srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
     pixels = ppm_allocarray(cmdline.width, cmdline.height);
 
     switch (cmdline.basePattern) {
     case PAT_GINGHAM2:
-        gingham2(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        gingham2(pixels, cmdline.width, cmdline.height,
+                 cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_GINGHAM3:
-        gingham3(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        gingham3(pixels, cmdline.width, cmdline.height,
+                 cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_MADRAS:
-        madras(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        madras(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_TARTAN:
-        tartan(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        tartan(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL);
+        break;
+
+    case PAT_ARGYLE1:
+        argyle(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL, FALSE);
+        break;
+
+    case PAT_ARGYLE2:
+        argyle(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL, TRUE);
         break;
 
     case PAT_POLES:
-        poles(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        poles(pixels, cmdline.width, cmdline.height,
+              &cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_SQUIG:
-        squig(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        squig(pixels, cmdline.width, cmdline.height,
+              &cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_CAMO:
-        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0);
+        camo(pixels, cmdline.width, cmdline.height,
+             &cmdline.colorTable, PPM_MAXMAXVAL, 0);
         break;
 
     case PAT_ANTICAMO:
-        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 1);
+        camo(pixels, cmdline.width, cmdline.height,
+             &cmdline.colorTable, PPM_MAXMAXVAL, 1);
         break;
 
     default:
@@ -1201,6 +1561,8 @@ main(int argc, const char ** argv) {
 
     ppm_freearray(pixels, cmdline.height);
 
+    freeCmdline(cmdline);
+
     return 0;
 }