 #include <avr/power.h>
 #endif
 
+#define GRID_SIZE_X 5
+#define GRID_SIZE_Y 5
+
 // ---------------------------------------------------------------------------------------------------------------------
 //  types
 // ---------------------------------------------------------------------------------------------------------------------
 
-struct point_s
+struct point
 {
    int x,y;
 };
 
-struct rgb_s
+struct rgbTriple
 {
    int r,g,b;
 };


Adafruit_WS2801 strip = Adafruit_WS2801(25, dataPin, clockPin);

float MIN_LIGHTNESS = 0.004;

struct point_s org = (struct point_s) { 0, 0   }   ;
struct point_s gridSize = (struct point_s) {5, 5    };

// ---------------------------------------------------------------------------------------------------------------------
//  setup

   int dColor = 64;
   float d1 = 1.0 / (5-1);      // 0..4 ==> 0..1.0 in 5 steps
   int exponent = 4;            // Non-linear brightness increase as {x^exponent : x in [0.0..1.0]}
   struct rgbTriple rgb;
   int r,g,b;
   float rf, gf, bf;
   int h;
   float s, l;
   uint32_t c;
   int i;
   struct point pt;

   enum gridColorspace 
   {
      RGB = 1,
      HSL
   };

   enum axisColorspace
   {
      RED = 1,
      GREEN,
      BLUE,
      HUE,
      SAT,                      // Saturation
      LIGHT                     // Lightness
   };
   
   typedef struct
   {
      gridColorspace colorspace;  
      int xAxisSpace, yAxisSpace; 

      // Axis values may be non-linear (e.g., intensity)
      union
      {
         int axisIntValues[GRID_SIZE_X];
         float axisFloatValues[GRID_SIZE_X];
      } x;
      
      union
      {
         int axisIntValues[GRID_SIZE_Y];
         float axisFloatValues[GRID_SIZE_Y];
      } y;
   } runParamTuple;
   
   // TODO: put the following setup in setup() ----------------------------------------------------------------
   
   // Gonna cheat, since we know we have a square grid:  same values on each axis.

   int ledBrightness[GRID_SIZE_X]; // RGB intensity
   float dLedBrightness = 1.0 / (GRID_SIZE_X - 1);
   float lightness[GRID_SIZE_X]; // HSL
   float saturation[GRID_SIZE_X]; // HSL
   
   ledBrightness[0] = 0;
   ledBrightness[GRID_SIZE_X - 1] = 255;

   saturation[0] = lightness[0] = 0;
   saturation[GRID_SIZE_X - 1] = lightness[GRID_SIZE_X - 1] = 1;

   float v;

   clearStrip();
   
   for (i = 1; i < GRID_SIZE_X - 1; i++)
   {
      v = i * dLedBrightness;   // Linear
      saturation[i] = v;
      v = pow( v, exponent);    // Non-linear
      lightness[i] = v;
      ledBrightness[i] = v * 256; // Don't need to constrain; won't get close to 255.
   }

   // Debug:
   for (i = 0; i < GRID_SIZE_X; i++)
   {
      pt = {i, 4 } ;
      rgb = hsl2rgb( 0, saturation[i], 0.5); // red/pink
      c = Color( rgb.r, rgb.g, rgb.b);
      strip.setPixelColor( point2seq( pt, org, gridSize), c);

      pt = {i, 3 } ; 
      rgb = hsl2rgb( 120, 0.5, lightness[i]); // greenish
      c = Color( rgb.r, rgb.g, rgb.b);
      strip.setPixelColor( point2seq( pt, org, gridSize), c);

      pt = {i, 2 } ;
      c = Color( 0, 0, ledBrightness[i]); // blue
      strip.setPixelColor( point2seq( pt, org, gridSize), c);
   }
   strip.show();
   delay( 20000);

   runParamTuple runParams[6] = { { RGB , RED   , GREEN } ,  // 0
                                  { RGB , BLUE  , RED }   ,  // 1
                                  { RGB , GREEN , BLUE }  ,  // 2
                                  { HSL , HUE   , SAT }   ,  // 3
                                  { HSL , HUE   , LIGHT } ,  // 4
                                  { HSL , SAT   , LIGHT } }; // 5
   
   for (i = 0; i < 3; i++)
   {
      copyIntArray( runParams[i].x.axisIntValues, ledBrightness, GRID_SIZE_X);
      copyIntArray( runParams[i].y.axisIntValues, ledBrightness, GRID_SIZE_X);
   }
   
   float dHue = 360.0 / GRID_SIZE_X;
   for (i = 0; i < GRID_SIZE_X; i++)
   {
      runParams[3].x.axisIntValues[i] = (int) (i * dHue + 0.5);
      runParams[4].x.axisIntValues[i] = (int) (i * dHue + 0.5);
   }
   copyFloatArray( runParams[3].y.axisFloatValues, saturation, GRID_SIZE_X);
   copyFloatArray( runParams[4].y.axisFloatValues, lightness, GRID_SIZE_X);
   copyFloatArray( runParams[5].y.axisFloatValues, lightness, GRID_SIZE_X);

   copyFloatArray( runParams[5].x.axisFloatValues, saturation, GRID_SIZE_X);

   // (setup ends) ----------------------------------------------------------------
   
   int run;

   // Label the runs for x and y axes: rg, br, gb
   // 0, 1, 2

   for (run = 0; run < 6; run++)
   {
      runParamTuple rp = runParams[run];
      for (y = 0; y < GRID_SIZE_Y; y++)
         for (x = 0; x < GRID_SIZE_X; x++)
         {
            pt = {x,y };
            // Compute color based on colorspace
            switch (rp.colorspace)
            {
               case RGB:
                  r = (rp.xAxisSpace == RED
                       ? rp.x.axisIntValues[x]
                       : (rp.yAxisSpace == RED
                          ? rp.y.axisIntValues[y]
                          : 0));
                  g = (rp.xAxisSpace == GREEN
                       ? rp.x.axisIntValues[x]
                       : (rp.yAxisSpace == GREEN
                          ? rp.y.axisIntValues[y]
                          : 0));
                  b = (rp.xAxisSpace == BLUE
                       ? rp.x.axisIntValues[x]
                       : (rp.yAxisSpace == BLUE
                          ? rp.y.axisIntValues[y]
                          : 0));
                  c = Color( r, g, b);
                  break;
               case HSL:
                  h = (rp.xAxisSpace == HUE
                       ? rp.x.axisIntValues[x]
                       : (rp.yAxisSpace == HUE
                          ? rp.y.axisIntValues[y]
                          : 0));
                  s = (rp.xAxisSpace == SAT
                       ? rp.x.axisIntValues[x]
                       : (rp.yAxisSpace == SAT
                          ? rp.y.axisIntValues[y]
                          : 0.5));
                  l = (rp.xAxisSpace == LIGHT
                       ? rp.x.axisIntValues[x]
                       : (rp.yAxisSpace == LIGHT
                          ? rp.y.axisIntValues[y]
                          : 0.5));
                  rgb = hsl2rgb( h, s, l);
                  c = Color( rgb.r, rgb.g, rgb.b);
                  break;
               default:
                  c = 0;
                  break;
            }
            strip.setPixelColor( point2seq( pt, org, gridSize), c);
         }
      rgb = hsl2rgb( run * 60, 1.0, 0.05);
      c = Color( rgb.r, rgb.g, rgb.b);
      pt = {0,4 } ;
      strip.setPixelColor( point2seq( pt, org, gridSize), c);
      strip.show();
      delay( 2000);
   }
   
   for (run = 0; run < 3; run++)
   {
      for (y = 0; y < 5; y++)
