Index: /branches/eam_branch_20071222/Ohana/src/kapa2/Makefile
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/Makefile	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/Makefile	(revision 15996)
@@ -78,5 +78,6 @@
 $(SRC)/PSimage.$(ARCH).o                  $(SRC)/PSPixmap.$(ARCH).o           \
 $(SRC)/PSOverlay.$(ARCH).o                $(SRC)/SetChannel.$(ARCH).o         \
-$(SRC)/SetColorScale.$(ARCH).o
+$(SRC)/SetColorScale.$(ARCH).o            $(SRC)/ColorCube.$(ARCH).o          \
+$(SRC)/ColorHistogram.$(ARCH).o           $(SRC)/sort.$(ARCH).o
 
 OBJ  =  $(KAPA)
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/include/constants.h
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/include/constants.h	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/include/constants.h	(revision 15996)
@@ -12,5 +12,7 @@
 # define NCHANNELS 3
 # define NPIXELS_DYNAMIC 128
-# define NPIXELS_STATIC 1024
+
+// XXX for the moment, this is set to match the values in SetColorScale3D_CC
+# define NPIXELS_STATIC 3000
 
 # define PAD1  3
@@ -33,5 +35,6 @@
 typedef enum {
     KAPA_SCALE_1D,
-    KAPA_SCALE_3D
+    KAPA_SCALE_3D_RUFF,
+    KAPA_SCALE_3D_FULL
 } KapaColorScaleMode;
 
@@ -47,4 +50,17 @@
   LABELLR
 } KapaLabelMode;
+
+typedef enum {
+  KAPA_CHANNEL_RED,
+  KAPA_CHANNEL_GREEN,
+  KAPA_CHANNEL_BLUE,
+} KapaChannels;
+
+// use an enum to identify the 3 dimensions:
+typedef enum {
+  CC_X,
+  CC_Y,
+  CC_Z,
+} CCDimen;
 
 /* EVENT_MASK consists of:
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/include/prototypes.h
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/include/prototypes.h	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/include/prototypes.h	(revision 15996)
@@ -142,5 +142,5 @@
 void          SetColorScale       PROTO((Graphic *graphic, KapaImageWidget *image));
 void          SetColorScale1D     PROTO((Graphic *graphic, KapaImageWidget *image));
-void          SetColorScale3D     PROTO((Graphic *graphic, KapaImageWidget *image));
+int           SetColorScale3D     PROTO((Graphic *graphic, KapaImageWidget *image));
 void          Remap               PROTO((Graphic *graphic, KapaImageWidget *image));
 void          Remap8              PROTO((Graphic *graphic, KapaImageWidget *image, Matrix *matrix));
@@ -223,2 +223,22 @@
 void InvertButton (Graphic *graphic, Button *button);
 void bDrawOverlay (KapaImageWidget *image, int N);
+
+/* color cube tools */
+CCNode *CCNodeAlloc ();
+CCNode *CCFindChild (CCNode *node, float x, float y, float z);
+int CCSplitNode (CCNode *node);
+int CCSplitNodeIterate (CCNode *node, int current, int max);
+void CCNodeFree (CCNode *node);
+CCNode *CCFindBottom (CCNode *top, float x, float y, float z);
+int CCNodeExtractCounts (CCNode *node, float **values, int *nvalues, int *NVALUES);
+int CCNodeDivideLimit (CCNode *node, float minValue);
+int CCNodeInitCounts (CCNode *node, float value);
+
+/* 3D color histogram */
+void ColorHistogram (KapaImageWidget *image, CCNode *cube);
+void CCNodeSetColorPixels (KapaImageWidget *image, CCNode *cube);
+int CCNodeSetColorMap (CCNode *node, XColor *cmap, int Npixels, int *current);
+
+int SetColorScale3D_CC (Graphic *graphic, KapaImageWidget *image);
+
+void sort (float *value, int N);
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/include/structures.h
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/include/structures.h	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/include/structures.h	(revision 15996)
@@ -66,4 +66,18 @@
 } TextBox;
 
+/*** 3C color cube histogram tree thingy ***/
+typedef struct CCNode {
+  // for the moment, this structure is specific to the color-histogram analysis.  if
+  // this were a void *pointer and we defined some additional apis, we could use this
+  // structure to track any 3D space
+  int count;    
+  int pixel;
+  char bottom;
+  float min[3]; // min value for each of the 3 dimensions for this node
+  float mid[3]; // mid-point for each of the 3 dimensions for this node
+  float max[3]; // max value for each of the 3 dimensions for this node
+  struct CCNode *sub[2][2][2];
+} CCNode;
+
 /**************** general structures ****************/
 typedef struct {
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/src/ButtonFunctions.c
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 15996)
@@ -29,5 +29,5 @@
   char *name;
   name = HEAT;
-  SetColormap (name);
+  SetColormap ("ruffcolor");
   CreateColorbar (image, graphic);
   SetColorScale (graphic, image);
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/src/ColorCube.c
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/src/ColorCube.c	(revision 15996)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/src/ColorCube.c	(revision 15996)
@@ -0,0 +1,225 @@
+# include "Ximage.h"
+
+/* these functions are used to generate an appropriately-sampled color map for a given image.
+   we assume that only a limited number of colors can be allocated (start with, eg, 1024, grow to
+   64k or so).  the RGB image is constructed from the 32bit images, each with their own zero and
+   scale.  We want to allocate set of color cells which sample the actual colors available in the
+   image data.  We generate a 3D histogram of the actual data values.  The histogram is generated
+   using a k-d tree. */
+
+// the k-d tree is built of a bunch of nodes, each of which represents a cubic region in the 3D
+// color space.  each node may be occupied, or may point to a set of 8 children, subdividing the
+// cube into equal cubes.
+
+// NOTE : nothing in this file specifies a relationship between x,y,z and red,blue,green
+
+// set of operations needed:
+
+// allocate empty, bottom-level node
+CCNode *CCNodeAlloc () {
+
+  CCNode *node;
+  int ix, iy, iz;
+
+  ALLOCATE (node, CCNode, 1);
+  node->count = 0;
+  node->pixel = 0;
+  node->bottom = TRUE;
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	node->sub[ix][iy][iz] = NULL;
+      }
+    }
+  }
+  return node;
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+CCNode *CCFindChild (CCNode *node, float x, float y, float z) {
+
+  int ix, iy, iz;
+
+  assert (x >= node->min[CC_X]);
+  assert (y >= node->min[CC_Y]);
+  assert (z >= node->min[CC_Z]);
+
+  assert (x <= node->max[CC_X]);
+  assert (y <= node->max[CC_Y]);
+  assert (z <= node->max[CC_Z]);
+
+  if (node->bottom) return NULL; // already at the bottom
+
+  ix = (x < node->mid[CC_X]) ? 0 : 1;
+  iy = (y < node->mid[CC_Y]) ? 0 : 1;
+  iz = (z < node->mid[CC_Z]) ? 0 : 1;
+  
+  return node->sub[ix][iy][iz];
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+int CCSplitNode (CCNode *node) {
+
+  CCNode *child;
+  int ix, iy, iz;
+
+  // trying to split an already split node
+  if (!node->bottom) return FALSE;
+
+  node->bottom = FALSE;
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	child = CCNodeAlloc();
+	child->min[CC_X] = (ix == 0) ? node->min[CC_X] : node->mid[CC_X];
+	child->min[CC_Y] = (iy == 0) ? node->min[CC_Y] : node->mid[CC_Y];
+	child->min[CC_Z] = (iz == 0) ? node->min[CC_Z] : node->mid[CC_Z];
+	child->max[CC_X] = (ix == 0) ? node->mid[CC_X] : node->max[CC_X];
+	child->max[CC_Y] = (iy == 0) ? node->mid[CC_Y] : node->max[CC_Y];
+	child->max[CC_Z] = (iz == 0) ? node->mid[CC_Z] : node->max[CC_Z];
+
+	child->mid[CC_X] = 0.5*(child->min[CC_X] + child->max[CC_X]);
+	child->mid[CC_Y] = 0.5*(child->min[CC_Y] + child->max[CC_Y]);
+	child->mid[CC_Z] = 0.5*(child->min[CC_Z] + child->max[CC_Z]);
+
+	node->sub[ix][iy][iz] = child;
+      }
+    }
+  }
+  return TRUE;
+}
+
+int CCSplitNodeIterate (CCNode *node, int current, int max) {
+
+  int ix, iy, iz;
+
+  if (current == max) return;
+  current ++;
+
+  CCSplitNode (node);
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCSplitNodeIterate (node->sub[ix][iy][iz], current, max);
+      }
+    }
+  }
+  return;
+}
+
+// allocate empty, bottom-level node
+void CCNodeFree (CCNode *node) {
+
+  int ix, iy, iz;
+
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	if (node->sub[ix][iy][iz]) CCNodeFree (node->sub[ix][iy][iz]);
+      }
+    }
+  }
+  free (node);
+  return;
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+CCNode *CCFindBottom (CCNode *top, float x, float y, float z) {
+
+  CCNode *node;
+  CCNode *next;
+
+  assert (x >= top->min[CC_X]);
+  assert (y >= top->min[CC_Y]);
+  assert (z >= top->min[CC_Z]);
+
+  assert (x <= top->max[CC_X]);
+  assert (y <= top->max[CC_Y]);
+  assert (z <= top->max[CC_Z]);
+
+  node = top;
+  next = CCFindChild (node, x, y, z);
+  while (next != NULL) {
+    node = next;
+    next = CCFindChild (node, x, y, z);
+  }
+
+  return node;
+}
+
+// iterate over the CCNode tree to generate a single, 1d vector of all data values
+// XXX this function is specific to the color histogram concept
+int CCNodeExtractCounts (CCNode *node, float **values, int *nvalues, int *NVALUES) {
+
+  int ix, iy, iz;
+
+  if (*values == NULL) {
+    nvalues[0] = 0;
+    NVALUES[0] = 32;
+    ALLOCATE (*values, float, NVALUES[0]);
+  }
+
+  if (node->bottom) {
+    values[0][nvalues[0]] = node->count;
+    nvalues[0] ++;
+    if (nvalues[0] >= NVALUES[0]) {
+      NVALUES[0] += 32;
+      REALLOCATE (*values, float, NVALUES[0]);
+    }
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeExtractCounts (node->sub[ix][iy][iz], values, nvalues, NVALUES);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+int CCNodeDivideLimit (CCNode *node, float minValue) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    if (node->count > minValue) {
+      CCSplitNode (node);
+    }
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeDivideLimit (node->sub[ix][iy][iz], minValue);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+int CCNodeInitCounts (CCNode *node, float value) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    node->count = value;
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeInitCounts (node->sub[ix][iy][iz], value);
+      }
+    }
+  }
+  return TRUE;
+}
+
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/src/ColorHistogram.c
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/src/ColorHistogram.c	(revision 15996)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/src/ColorHistogram.c	(revision 15996)
@@ -0,0 +1,176 @@
+# include "Ximage.h"
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+void ColorHistogram (KapaImageWidget *image, CCNode *cube) {
+
+  int i, j, DX, DY, value, nPixels;
+  float *rData, *bData, *gData, rValue, gValue, bValue;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+  CCNode *node;
+
+  // Input images are scaled to 0.0 - 1.0 floating point range.  These values are accumulated in the
+  // 3d histogram.  Use the histogram to allocate colors or subdivide the top-populated histogram
+  // cells and re-evaluated.   
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = 1.0 / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = 1.0 / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = 1.0 / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  DX = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1];
+  // XXX check on equal size for all three channels
+
+  nPixels = DX*DY;
+
+  // loop over pixels, convert data values to range values (0.0 - 1.0), increment 3D histogram cell
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0.0) rValue = 0.0;
+    if (rValue > 1.0) rValue = 1.0;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0.0) bValue = 0.0;
+    if (bValue > 1.0) bValue = 1.0;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0.0) gValue = 0.0;
+    if (gValue > 1.0) gValue = 1.0;
+
+    // XXX at the moment, we are saturating before supplying to this function
+    // NOTE : x,y,z = red,green,blue
+    node = CCFindBottom (cube, rValue, gValue, bValue);
+    node->count ++;
+  }
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+void CCNodeSetColorPixels (KapaImageWidget *image, CCNode *cube) {
+
+  int i, j, DX, DY, value, nPixels;
+  float *rData, *bData, *gData, rValue, gValue, bValue;
+  unsigned short *oData;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+  CCNode *node;
+
+  // Input images are scaled to 0.0 - 1.0 floating point range.  These values are accumulated in the
+  // 3d histogram.  Use the histogram to allocate colors or subdivide the top-populated histogram
+  // cells and re-evaluated.   
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = 1.0 / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = 1.0 / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = 1.0 / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  DX = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1];
+  // XXX check on equal size for all three channels
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  // loop over pixels, convert data values to range values (0.0 - 1.0), increment 3D histogram cell
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  oData = image[0].pixmap;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++, oData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0.0) rValue = 0.0;
+    if (rValue > 1.0) rValue = 1.0;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0.0) bValue = 0.0;
+    if (bValue > 1.0) bValue = 1.0;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0.0) gValue = 0.0;
+    if (gValue > 1.0) gValue = 1.0;
+
+    // XXX at the moment, we are saturating before supplying to this function
+    // NOTE : x,y,z = red,green,blue 
+    node = CCFindBottom (cube, rValue, gValue, bValue);
+    *oData = node->pixel;
+  }
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+// NOTE : x,y,z = red,green,blue 
+int CCNodeSetColorMap (CCNode *node, XColor *cmap, int Npixels, int *current) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    cmap[current[0]].red   = node->mid[CC_X]*0xffff;
+    cmap[current[0]].green = node->mid[CC_Y]*0xffff;
+    cmap[current[0]].blue  = node->mid[CC_Z]*0xffff;
+    cmap[current[0]].flags = DoRed | DoGreen | DoBlue;
+    node->pixel = current[0];
+    current[0] ++;
+    assert (current[0] <= Npixels);
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeSetColorMap (node->sub[ix][iy][iz], cmap, Npixels, current);
+      }
+    }
+  }
+  return TRUE;
+}
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/src/SetColorScale.c
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/src/SetColorScale.c	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/src/SetColorScale.c	(revision 15996)
@@ -7,6 +7,17 @@
       SetColorScale1D (graphic, image);
       break;
-    case KAPA_SCALE_3D:
-      SetColorScale3D (graphic, image);
+    case KAPA_SCALE_3D_RUFF:
+      // fall-back on 1D colors (ie, if images are mis-matched)
+      if (!SetColorScale3D (graphic, image)) {
+	graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+	SetColorScale1D (graphic, image);
+      }
+      break;
+    case KAPA_SCALE_3D_FULL:
+      // fall-back on 1D colors (ie, if images are mis-matched)
+      if (!SetColorScale3D_CC (graphic, image)) {
+	graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+	SetColorScale1D (graphic, image);
+      }
       break;
     default:
@@ -60,5 +71,6 @@
 
 // in 3D we use channels 0,1,2 to choose the pixel from the cube
-void SetColorScale3D (Graphic *graphic, KapaImageWidget *image) {
+// XXX this uses a crude, uniform-spacing cube
+int SetColorScale3D (Graphic *graphic, KapaImageWidget *image) {
   int i, j, DX, DY, value, nPixels, rValue, gValue, bValue;
   float *rData, *bData, *gData;
@@ -67,4 +79,12 @@
   float redStart, blueStart, greenStart;
 
+  DX = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[1];
+
+  if (DX != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1]) return FALSE;
+  if (DX != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[1]) return FALSE;
+
   // define the color transform parameters
   unsigned short maxRed = graphic[0].nRed - 1;
@@ -73,31 +93,27 @@
 
   // set start & slope for red (channel 0)
-  if (image[0].channel[0].range != 0.0) {
-    redSlope = graphic[0].nRed / image[0].channel[0].range;
-    redStart = graphic[0].nRed * image[0].channel[0].zero / image[0].channel[0].range;
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = graphic[0].nRed / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = graphic[0].nRed * image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
   } else {
     redSlope = 1.0;
-    redStart = image[0].channel[0].zero;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
   }
   // set start & slope for blue (channel 0)
-  if (image[0].channel[1].range != 0.0) {
-    blueSlope = graphic[0].nBlue / image[0].channel[1].range;
-    blueStart = graphic[0].nBlue * image[0].channel[1].zero / image[0].channel[1].range;
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = graphic[0].nBlue / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = graphic[0].nBlue * image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
   } else {
     blueSlope = 1.0;
-    blueStart = image[0].channel[1].zero;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
   }
   // set start & slope for green (channel 0)
-  if (image[0].channel[2].range != 0.0) {
-    greenSlope = graphic[0].nGreen / image[0].channel[2].range;
-    greenStart = graphic[0].nGreen * image[0].channel[2].zero / image[0].channel[2].range;
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = graphic[0].nGreen / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = graphic[0].nGreen * image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
   } else {
     greenSlope = 1.0;
-    greenStart = image[0].channel[2].zero;
-  }
-
-  DX = image[0].channel[0].matrix.Naxis[0];
-  DY = image[0].channel[0].matrix.Naxis[1];
-  // check on equal size for channel[1] and channel[2]
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
 
   nPixels = DX*DY;
@@ -108,7 +124,7 @@
 
   oData = image[0].pixmap;
-  rData = (float *) image[0].channel[0].matrix.buffer;
-  bData = (float *) image[0].channel[1].matrix.buffer;
-  gData = (float *) image[0].channel[2].matrix.buffer;
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
 
   // convert pixel data values to pixel index values (0 - Npixel)
@@ -128,3 +144,74 @@
     *oData = gValue + bValue * (maxGreen + 1) + rValue * (maxGreen + 1) * (maxBlue + 1);
   }
-}
+  return TRUE;
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+int SetColorScale3D_CC (Graphic *graphic, KapaImageWidget *image) {
+
+  int i, DX, DY, Nvalues, NVALUES, Npixels, Nmin;
+  float *values, *colors;
+  CCNode *cube;
+
+  DX = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[1];
+  if (DX != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1]) return FALSE;
+  if (DX != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[1]) return FALSE;
+
+  // create the top-level cube
+  cube = CCNodeAlloc();
+  cube->min[CC_X] = cube->min[CC_Y] = cube->min[CC_Z] = 0.0;
+  cube->max[CC_X] = cube->max[CC_Y] = cube->max[CC_Z] = 1.0;
+  cube->mid[CC_X] = cube->mid[CC_Y] = cube->mid[CC_Z] = 0.5;
+
+  // subdivide cube into first, uniform division (4x4x4)
+  CCSplitNodeIterate (cube, 0, 2);
+
+  // need to allocate at least 1184 pixels
+  // 64, 288, 512, 736, 960, 1184
+  for (i = 0; i < 8; i++) {
+    // generate histogram
+    ColorHistogram (image, cube);
+
+    // extract histogram values
+    values  = NULL;
+    Nvalues = 0;
+    NVALUES = 0;
+    CCNodeExtractCounts (cube, &values, &Nvalues, &NVALUES);
+  
+    // select top N cells
+    sort (values, Nvalues);
+
+    // split nodes with more than value[n]
+    Nmin = MAX (0, Nvalues - 50);
+    CCNodeDivideLimit (cube, values[Nmin]);
+    free (values);
+
+    CCNodeInitCounts (cube, 0.0);
+  }
+
+  // generate histogram
+  ColorHistogram (image, cube);
+  
+  // convert histogram 3D values to cmap colors
+  Npixels = 0;
+  CCNodeSetColorMap (cube, graphic[0].cmap, graphic[0].Npixels, &Npixels);
+
+  // store the colors
+  if (USE_XWINDOW) {
+    if (graphic[0].visualclass) {
+      XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, Npixels);
+    } else {
+      for (i = 0; i < Npixels; i++) {
+	if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
+	  fprintf (stderr, "error on %d\n", i);
+	}
+      }
+    }
+  }
+  CCNodeSetColorPixels (image, cube);
+  return TRUE;
+}
+
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/src/SetColormap.c
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/src/SetColormap.c	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/src/SetColormap.c	(revision 15996)
@@ -19,6 +19,13 @@
   graphic = GetGraphic();
 
+  // the "fullcolor" colormap is uniquely defined for each image;
+  // defer to the 'SetColorScale' step
+  if (!strcasecmp (name, "fullcolor")) {
+    graphic[0].ColorScaleMode = KAPA_SCALE_3D_FULL;
+    return TRUE;
+  }
+
   // very simple color model: evenly spaced cube 
-  if (!strcasecmp (name, "fullcolor")) {
+  if (!strcasecmp (name, "ruffcolor")) {
       graphic[0].nRed  = pow (graphic[0].Npixels, 0.333);
       graphic[0].nBlue = pow (graphic[0].Npixels, 0.333);
@@ -43,5 +50,5 @@
 
       // all other modes are 1D: set the flag:
-      graphic[0].ColorScaleMode = KAPA_SCALE_3D;
+      graphic[0].ColorScaleMode = KAPA_SCALE_3D_RUFF;
       goto store_colors;
   }
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/src/sort.c
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/src/sort.c	(revision 15996)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/src/sort.c	(revision 15996)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+
+void sort (float *value, int N) {
+
+  int l,j,ir,i;
+  float temp;
+  
+  if (N < 2) return;
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      temp = value[--l];
+    }
+    else {
+      temp = value[ir];
+      value[ir] = value[0];
+      if (--ir == 0) {
+	value[0] = temp;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && value[j] < value[j+1]) ++j;
+      if (temp < value[j]) {
+	value[i]=value[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    value[i] = temp;
+  }
+}
+
Index: /branches/eam_branch_20071222/Ohana/src/kapa2/test/input
===================================================================
--- /branches/eam_branch_20071222/Ohana/src/kapa2/test/input	(revision 15995)
+++ /branches/eam_branch_20071222/Ohana/src/kapa2/test/input	(revision 15996)
@@ -23,6 +23,6 @@
   tv R 0 256
   tvchannel 1
+  tv G 0 256
+  tvchannel 2
   tv B 0 256
-  tvchannel 2
-  tv G 0 256
 end
