Implement CubeHelix color maps, and reorganize command line and GUI accordingly.
authorMartin Lambers <marlam@marlam.de>
Tue, 2 Feb 2016 15:00:58 +0000 (16:00 +0100)
committerMartin Lambers <marlam@marlam.de>
Tue, 2 Feb 2016 15:00:58 +0000 (16:00 +0100)
cmdline.cpp
colormap.cpp
colormap.hpp
gui.cpp
gui.hpp

index e6d96db..ae83678 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2015 Computer Graphics Group, University of Siegen
+ * Copyright (C) 2015, 2016 Computer Graphics Group, University of Siegen
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -33,6 +33,12 @@ extern int optind;
 
 #include "colormap.hpp"
 
 
 #include "colormap.hpp"
 
+enum type {
+    brewer_sequential = 0,
+    brewer_diverging = 1,
+    brewer_qualitative = 2,
+    cubehelix = 3
+};
 
 int main(int argc, char* argv[])
 {
 
 int main(int argc, char* argv[])
 {
@@ -46,6 +52,8 @@ int main(int argc, char* argv[])
     float saturation = -1.0f;
     float brightness = -1.0f;
     float warmth = -1.0f;
     float saturation = -1.0f;
     float brightness = -1.0f;
     float warmth = -1.0f;
+    float rotations = NAN;
+    float gamma = -1.0f;
     struct option options[] = {
         { "version",    no_argument,       0, 'v' },
         { "help",       no_argument,       0, 'H' },
     struct option options[] = {
         { "version",    no_argument,       0, 'v' },
         { "help",       no_argument,       0, 'H' },
@@ -57,12 +65,13 @@ int main(int argc, char* argv[])
         { "saturation", required_argument, 0, 's' },
         { "brightness", required_argument, 0, 'b' },
         { "warmth",     required_argument, 0, 'w' },
         { "saturation", required_argument, 0, 's' },
         { "brightness", required_argument, 0, 'b' },
         { "warmth",     required_argument, 0, 'w' },
+        { "rotations",  required_argument, 0, 'r' },
+        { "gamma",      required_argument, 0, 'g' },
         { 0, 0, 0, 0 }
     };
         { 0, 0, 0, 0 }
     };
-    int retval = 0;
 
     for (;;) {
 
     for (;;) {
-        int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:", options, NULL);
+        int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:r:g:", options, NULL);
         if (c == -1)
             break;
         switch (c) {
         if (c == -1)
             break;
         switch (c) {
@@ -73,9 +82,10 @@ int main(int argc, char* argv[])
             print_help = true;
             break;
         case 't':
             print_help = true;
             break;
         case 't':
-            type = (strcmp(optarg, "sequential") == 0 ? 0
-                    : strcmp(optarg, "diverging") == 0 ? 1
-                    : strcmp(optarg, "qualitative") == 0 ? 2
+            type = (strcmp(optarg, "brewer-sequential") == 0 ? brewer_sequential
+                    : strcmp(optarg, "brewer-diverging") == 0 ? brewer_diverging
+                    : strcmp(optarg, "brewer-qualitative") == 0 ? brewer_qualitative
+                    : strcmp(optarg, "cubehelix") == 0 ? cubehelix
                     : -2);
             break;
         case 'n':
                     : -2);
             break;
         case 'n':
@@ -99,103 +109,136 @@ int main(int argc, char* argv[])
         case 'w':
             warmth = atof(optarg);
             break;
         case 'w':
             warmth = atof(optarg);
             break;
-        default:
-            retval = 1;
+        case 'r':
+            rotations = atof(optarg);
+            break;
+        case 'g':
+            gamma = atof(optarg);
             break;
             break;
+        default:
+            return 1;
         }
     }
         }
     }
-    if (retval != 0)
-        return retval;
 
     if (print_version) {
 
     if (print_version) {
-        printf("gencolormap version 0.1\n"
-                "Copyright (C) 2015 Computer Graphics Group, University of Siegen.\n"
+        printf("gencolormap version 0.2\n"
+                "Copyright (C) 2016 Computer Graphics Group, University of Siegen.\n"
                 "Written by Martin Lambers <martin.lambers@uni-siegen.de>.\n"
                 "This is free software under the terms of the MIT/Expat License.\n"
                 "There is NO WARRANTY, to the extent permitted by law.\n");
                 "Written by Martin Lambers <martin.lambers@uni-siegen.de>.\n"
                 "This is free software under the terms of the MIT/Expat License.\n"
                 "There is NO WARRANTY, to the extent permitted by law.\n");
-        return retval;
+        return 0;
     }
 
     }
 
-    if (!print_help) {
-        if (type < 0) {
-            fprintf(stderr, "Invalid or missing option -t|--type.\n");
-            print_help = true;
-            retval = 1;
-        }
-        if (n < 2) {
-            fprintf(stderr, "Invalid or missing option -n|--n.\n");
-            print_help = true;
-            retval = 1;
-        }
-        if (hue < 0.0f) {
-            fprintf(stderr, "Invalid or missing option -h|--hue.\n");
-            print_help = true;
-            retval = 1;
-        }
-    }
     if (print_help) {
         printf("Usage: %s\n"
     if (print_help) {
         printf("Usage: %s\n"
-                "  -t|--type=sequential   Generate a sequential color map\n"
-                "  -t|--type=diverging    Generate a diverging color map\n"
-                "  -t|--type=qualitative  Generate a qualitative color map\n"
-                "  -n|--n=N               Set number of colors in the map\n"
-                "  -h|--hue=H             Set hue in [0,360] degrees\n"
-                "  [-d|--divergence=D]    Set divergence for div. and qual. maps\n"
-                "  [-c|--contrast=C]      Set contrast in [0,1]\n"
-                "  [-s|--saturation=S]    Set saturation in [0,1]\n"
-                "  [-b|--brightness=B]    Set brightness in [0,1]\n"
-                "  [-w|--warmth=W]        Set color warmth in [0,1] for seq. and div. maps\n"
+                "  Common options, required for all types:\n"
+                "    -n|--n=N                      Set number of colors in the map\n"
+                "  Brewer-like color maps:\n"
+                "    -t|--type=brewer-sequential   Generate a sequential color map\n"
+                "    -t|--type=brewer-diverging    Generate a diverging color map\n"
+                "    -t|--type=brewer-qualitative  Generate a qualitative color map\n"
+                "    [-h|--hue=H]                  Set default hue in [0,360] degrees\n"
+                "    [-c|--contrast=C]             Set contrast in [0,1]\n"
+                "    [-s|--saturation=S]           Set saturation in [0,1]\n"
+                "    [-b|--brightness=B]           Set brightness in [0,1]\n"
+                "    [-w|--warmth=W]               Set warmth in [0,1] for seq. and div. maps\n"
+                "    [-d|--divergence=D]           Set divergence for div. and qual. maps\n"
+                "  CubeHelix color maps:\n"
+                "    -t|--type=cubehelix           Generate a CubeHelix color map\n"
+                "    [-r|--rotations=R]            Set number of rotations, in (-infty,infty)\n"
+                "    [-s|--saturation=S]           Set saturation, in [0,1]\n"
+                "    [-g|--gamma=G]                Set gamma correction, in (0,infty)\n"
                 "Generates a color map and prints it to standard output as sRGB triplets.\n"
                 "Report bugs to <martin.lambers@uni-siegen.de>.\n", argv[0]);
                 "Generates a color map and prints it to standard output as sRGB triplets.\n"
                 "Report bugs to <martin.lambers@uni-siegen.de>.\n", argv[0]);
-        return retval;
+        return 0;
     }
 
     }
 
+    if (type < 0) {
+        fprintf(stderr, "Invalid or missing option -t|--type.\n");
+        return 1;
+    }
+    if (n < 2) {
+        fprintf(stderr, "Invalid or missing option -n|--n.\n");
+        return 1;
+    }
+    if (hue < 0.0f) {
+        if (type == brewer_sequential)
+            hue = ColorMap::BrewerSequentialDefaultHue;
+        else if (type == brewer_diverging)
+            hue = ColorMap::BrewerDivergingDefaultHue;
+        else if (type == brewer_qualitative)
+            hue = ColorMap::BrewerQualitativeDefaultHue;
+        else if (type == cubehelix)
+            hue = ColorMap::CubeHelixDefaultHue;
+    }
     if (divergence < 0.0f) {
     if (divergence < 0.0f) {
-        if (type <= 1)
-            divergence = 2.0f / 3.0f * static_cast<float>(M_PI);
-        else
-            divergence = ColorMap::DefaultQualitativeDivergence;
+        if (type == brewer_diverging)
+            divergence = ColorMap::BrewerDivergingDefaultDivergence;
+        else if (type == brewer_qualitative)
+            divergence = ColorMap::BrewerQualitativeDefaultDivergence;
     }
     if (contrast < 0.0f) {
     }
     if (contrast < 0.0f) {
-        if (type <= 1)
-            if (n > 0 && n < 9)
-                contrast = ColorMap::DefaultContrastForSmallN(n);
-            else
-                contrast = ColorMap::DefaultContrast;
-        else
-            contrast = ColorMap::DefaultQualitativeContrast;
+        if (type == brewer_sequential)
+            contrast = (n <= 9 ? ColorMap::BrewerSequentialDefaultContrastForSmallN(n)
+                    : ColorMap::BrewerSequentialDefaultContrast);
+        else if (type == brewer_diverging)
+            contrast = (n <= 9 ? ColorMap::BrewerDivergingDefaultContrastForSmallN(n)
+                    : ColorMap::BrewerDivergingDefaultContrast);
+        else if (type == brewer_qualitative)
+            contrast = ColorMap::BrewerQualitativeDefaultContrast;
     }
     if (saturation < 0.0f) {
     }
     if (saturation < 0.0f) {
-        if (type <= 1)
-            saturation = ColorMap::DefaultSaturation;
-        else
-            saturation = ColorMap::DefaultQualitativeSaturation;
+        if (type == brewer_sequential)
+            saturation = ColorMap::BrewerSequentialDefaultSaturation;
+        else if (type == brewer_diverging)
+            saturation = ColorMap::BrewerDivergingDefaultSaturation;
+        else if (type == brewer_qualitative)
+            saturation = ColorMap::BrewerQualitativeDefaultSaturation;
+        else if (type == cubehelix)
+            saturation = ColorMap::CubeHelixDefaultSaturation;
     }
     if (brightness < 0.0f) {
     }
     if (brightness < 0.0f) {
-        if (type <= 1)
-            brightness = ColorMap::DefaultBrightness;
-        else
-            brightness = ColorMap::DefaultQualitativeBrightness;
+        if (type == brewer_sequential)
+            brightness = ColorMap::BrewerSequentialDefaultBrightness;
+        else if (type == brewer_diverging)
+            brightness = ColorMap::BrewerDivergingDefaultBrightness;
+        else if (type == brewer_qualitative)
+            brightness = ColorMap::BrewerQualitativeDefaultBrightness;
     }
     if (warmth < 0.0f) {
     }
     if (warmth < 0.0f) {
-        warmth = ColorMap::DefaultWarmth;
+        if (type == brewer_sequential)
+            brightness = ColorMap::BrewerSequentialDefaultWarmth;
+        else if (type == brewer_diverging)
+            brightness = ColorMap::BrewerDivergingDefaultWarmth;
+    }
+    if (std::isnan(rotations)) {
+        if (type == cubehelix)
+            rotations = ColorMap::CubeHelixDefaultRotations;
+    }
+    if (gamma < 0.0f) {
+        if (type == cubehelix)
+            gamma = ColorMap::CubeHelixDefaultGamma;
     }
 
     std::vector<unsigned char> colormap(3 * n);
     }
 
     std::vector<unsigned char> colormap(3 * n);
-    if (type == 0) {
-        ColorMap::Sequential(n, &(colormap[0]), hue,
-                contrast, saturation, brightness, warmth);
-    } else if (type == 1) {
-        ColorMap::Diverging(n, &(colormap[0]), hue, divergence,
-                contrast, saturation, brightness, warmth);
-    } else {
-        ColorMap::Qualitative(n, &(colormap[0]), hue, divergence,
-                contrast, saturation, brightness);
+    switch (type) {
+    case brewer_sequential:
+        ColorMap::BrewerSequential(n, &(colormap[0]), hue, contrast, saturation, brightness, warmth);
+        break;
+    case brewer_diverging:
+        ColorMap::BrewerDiverging(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness, warmth);
+        break;
+    case brewer_qualitative:
+        ColorMap::BrewerQualitative(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness);
+        break;
+    case cubehelix:
+        ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
+        break;
     }
 
     for (int i = 0; i < n; i++) {
         printf("%d, %d, %d\n", colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
     }
 
     }
 
     for (int i = 0; i < n; i++) {
         printf("%d, %d, %d\n", colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
     }
 
-    return retval;
+    return 0;
 }
 }
index 9a45fd4..0335c20 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2015 Computer Graphics Group, University of Siegen
+ * Copyright (C) 2015, 2016 Computer Graphics Group, University of Siegen
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -366,14 +366,14 @@ static void convert_colormap_entry(LUVColor color, unsigned char* srgb)
     srgb[2] = std::round(sb * 255.0f);
 }
 
     srgb[2] = std::round(sb * 255.0f);
 }
 
-/* The public functions */
+/* Public functions: Brewer-like color maps */
 
 
-float DefaultContrastForSmallN(int n)
+float BrewerSequentialDefaultContrastForSmallN(int n)
 {
     return std::min(0.88f, 0.34f + 0.06f * n);
 }
 
 {
     return std::min(0.88f, 0.34f + 0.06f * n);
 }
 
-void Sequential(int n, unsigned char* colormap, float hue,
+void BrewerSequential(int n, unsigned char* colormap, float hue,
         float contrast, float saturation, float brightness, float warmth)
 {
     LUVColor pb, p0, p1, p2, q0, q1, q2;
         float contrast, float saturation, float brightness, float warmth)
 {
     LUVColor pb, p0, p1, p2, q0, q1, q2;
@@ -390,10 +390,15 @@ void Sequential(int n, unsigned char* colormap, float hue,
     }
 }
 
     }
 }
 
-void Diverging(int n, unsigned char* colormap, float hue0, float divergence,
+float BrewerDivergingDefaultContrastForSmallN(int n)
+{
+    return std::min(0.88f, 0.34f + 0.06f * n);
+}
+
+void BrewerDiverging(int n, unsigned char* colormap, float hue, float divergence,
         float contrast, float saturation, float brightness, float warmth)
 {
         float contrast, float saturation, float brightness, float warmth)
 {
-    float hue1 = hue0 + divergence;
+    float hue1 = hue + divergence;
     if (hue1 >= twopi)
         hue1 -= twopi;
 
     if (hue1 >= twopi)
         hue1 -= twopi;
 
@@ -404,7 +409,7 @@ void Diverging(int n, unsigned char* colormap, float hue0, float divergence,
     float pbc, pbh, pbs;
     luv_to_lch(pb.u, pb.v, &pbc, &pbh);
     pbs = lch_saturation(pb.l, pbc);
     float pbc, pbh, pbs;
     luv_to_lch(pb.u, pb.v, &pbc, &pbh);
     pbs = lch_saturation(pb.l, pbc);
-    get_color_points(hue0, saturation, warmth, pb, pbh, pbs, &p00, &p01, &p02, &q00, &q01, &q02);
+    get_color_points(hue saturation, warmth, pb, pbh, pbs, &p00, &p01, &p02, &q00, &q01, &q02);
     get_color_points(hue1, saturation, warmth, pb, pbh, pbs, &p10, &p11, &p12, &q10, &q11, &q12);
 
     for (int i = 0; i < n; i++) {
     get_color_points(hue1, saturation, warmth, pb, pbh, pbs, &p10, &p11, &p12, &q10, &q11, &q12);
 
     for (int i = 0; i < n; i++) {
@@ -445,7 +450,7 @@ static float HueDiff(float h0, float h1)
     return (t < pi ? t : twopi - t);
 }
 
     return (t < pi ? t : twopi - t);
 }
 
-void Qualitative(int n, unsigned char* colormap, float hue0, float divergence,
+void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergence,
         float contrast, float saturation, float brightness)
 {
     // Get all information about yellow
         float contrast, float saturation, float brightness)
 {
     // Get all information about yellow
@@ -470,7 +475,7 @@ void Qualitative(int n, unsigned char* colormap, float hue0, float divergence,
     }
 
     // Derive parameters of the method
     }
 
     // Derive parameters of the method
-    float eps = hue0 / twopi;
+    float eps = hue / twopi;
     float r = divergence / twopi;
     float l0 = brightness * yl;
     float l1 = (1.0f - contrast) * l0;
     float r = divergence / twopi;
     float l0 = brightness * yl;
     float l1 = (1.0f - contrast) * l0;
@@ -489,4 +494,51 @@ void Qualitative(int n, unsigned char* colormap, float hue0, float divergence,
     }
 }
 
     }
 }
 
+/* Public functions: CubeHelix */
+
+int CubeHelix(int n, unsigned char* colormap, float hue,
+        float rot, float saturation, float gamma)
+{
+    int clippings = 0;
+    for (int i = 0; i < n; i++) {
+        float fract = i / (n - 1.0f);
+        float angle = twopi * (hue / 3.0f + 1.0f + rot * fract);
+        fract = std::pow(fract, gamma);
+        float amp = saturation * fract * (1.0f - fract) / 2.0f;
+        float s = std::sin(angle);
+        float c = std::cos(angle);
+        float r = fract + amp * (-0.14861f * c + 1.78277f * s);
+        float g = fract + amp * (-0.29227f * c - 0.90649f * s);
+        float b = fract + amp * (1.97294f * c);
+        bool clipped = false;
+        if (r < 0.0f) {
+            r = 0.0f;
+            clipped = true;
+        } else if (r > 1.0f) {
+            r = 1.0f;
+            clipped = true;
+        }
+        if (g < 0.0f) {
+            g = 0.0f;
+            clipped = true;
+        } else if (g > 1.0f) {
+            g = 1.0f;
+            clipped = true;
+        }
+        if (b < 0.0f) {
+            b = 0.0f;
+            clipped = true;
+        } else if (b > 1.0f) {
+            b = 1.0f;
+            clipped = true;
+        }
+        if (clipped)
+            clippings++;
+        colormap[3 * i + 0] = r * 255.0f;
+        colormap[3 * i + 1] = g * 255.0f;
+        colormap[3 * i + 2] = b * 255.0f;
+    }
+    return clippings;
+}
+
 }
 }
index 688f78f..adaa6bb 100644 (file)
 
 namespace ColorMap {
 
 
 namespace ColorMap {
 
-const float DefaultContrast = 0.88f;
-float DefaultContrastForSmallN(int n); // only for discrete color maps, i.e. n <= 9
-const float DefaultSaturation = 0.6f;
-const float DefaultBrightness = 0.75f;
-const float DefaultWarmth = 0.15f;
+/*
+ * Brewer-like color maps, as described in
+ * M. Wijffelaars, R. Vliegen, J.J. van Wijk, E.-J. van der Linden. Generating
+ * color palettes using intuitive parameters. In Computer Graphics Forum, vol. 27,
+ * no. 3, pp. 743-750, 2008.
+ */
 
 // Create a sequential color map with n colors of the given hue in [0,2*PI].
 
 // Create a sequential color map with n colors of the given hue in [0,2*PI].
-void Sequential(int n, unsigned char* srgb_colormap, float hue,
-        float contrast = DefaultContrast,
-        float saturation = DefaultSaturation,
-        float brightness = DefaultBrightness,
-        float warmth = DefaultWarmth);
-
-// Create a diverging color map with n colors. Half of them will have hue0 (in
-// [0,2*PI]), the other half will have a hue that has the distance given by
-// divergence (in [0,2*PI]) to hue0, and they will meet in the middle at a
-// neutral color.
-void Diverging(int n, unsigned char* srgb_colormap, float hue0, float divergence,
-        float contrast = DefaultContrast,
-        float saturation = DefaultSaturation,
-        float brightness = DefaultBrightness,
-        float warmth = DefaultWarmth);
-
-const float DefaultQualitativeDivergence = 6.2831853071795864769; // 2*PI
-const float DefaultQualitativeContrast = 0.5f;
-const float DefaultQualitativeSaturation = 0.5f;
-const float DefaultQualitativeBrightness = 1.0f;
+
+const float BrewerSequentialDefaultHue = 0.0;
+const float BrewerSequentialDefaultContrast = 0.88f;
+float BrewerSequentialDefaultContrastForSmallN(int n); // only for discrete color maps, i.e. n <= 9
+const float BrewerSequentialDefaultSaturation = 0.6f;
+const float BrewerSequentialDefaultBrightness = 0.75f;
+const float BrewerSequentialDefaultWarmth = 0.15f;
+
+void BrewerSequential(int n, unsigned char* srgb_colormap,
+        float hue = BrewerSequentialDefaultHue,
+        float contrast = BrewerSequentialDefaultContrast,
+        float saturation = BrewerSequentialDefaultSaturation,
+        float brightness = BrewerSequentialDefaultBrightness,
+        float warmth = BrewerSequentialDefaultWarmth);
+
+// Create a diverging color map with n colors. Half of them will have the given
+// hue (in [0,2*PI]), the other half will have a hue that has the distance given
+// by divergence (in [0,2*PI]) to that hue, and they will meet in the middle at
+// a neutral color.
+
+const float BrewerDivergingDefaultHue = 0.0;
+const float BrewerDivergingDefaultDivergence = 4.18879020479f; // 2/3 * 2PI
+const float BrewerDivergingDefaultContrast = 0.88f;
+float BrewerDivergingDefaultContrastForSmallN(int n); // only for discrete color maps, i.e. n <= 9
+const float BrewerDivergingDefaultSaturation = 0.6f;
+const float BrewerDivergingDefaultBrightness = 0.75f;
+const float BrewerDivergingDefaultWarmth = 0.15f;
+
+void BrewerDiverging(int n, unsigned char* srgb_colormap,
+        float hue = BrewerDivergingDefaultHue,
+        float divergence = BrewerDivergingDefaultDivergence,
+        float contrast = BrewerDivergingDefaultContrast,
+        float saturation = BrewerDivergingDefaultSaturation,
+        float brightness = BrewerDivergingDefaultBrightness,
+        float warmth = BrewerDivergingDefaultWarmth);
 
 // Create a qualitative color map with n colors. The colors will have the same
 
 // Create a qualitative color map with n colors. The colors will have the same
-// saturation; lightness and hue will differ. The parameter hue0 sets the hue of
+// saturation; lightness and hue will differ. The parameter hue sets the hue of
 // the first color, and the parameter divergence defines the hue range starting
 // the first color, and the parameter divergence defines the hue range starting
-// from hue0 that can be used for the colors.
-void Qualitative(int n, unsigned char* colormap, float hue0,
-        float divergence = DefaultQualitativeDivergence,
-        float contrast = DefaultQualitativeContrast,
-        float saturation = DefaultQualitativeSaturation,
-        float brightness = DefaultQualitativeBrightness);
+// from that hue that can be used for the colors.
+
+const float BrewerQualitativeDefaultHue = 0.0f;
+const float BrewerQualitativeDefaultDivergence = 4.18879020479f; // 2/3 * 2PI
+const float BrewerQualitativeDefaultContrast = 0.5f;
+const float BrewerQualitativeDefaultSaturation = 0.5f;
+const float BrewerQualitativeDefaultBrightness = 1.0f;
+
+void BrewerQualitative(int n, unsigned char* colormap,
+        float hue = BrewerQualitativeDefaultHue,
+        float divergence = BrewerQualitativeDefaultDivergence,
+        float contrast = BrewerQualitativeDefaultContrast,
+        float saturation = BrewerQualitativeDefaultSaturation,
+        float brightness = BrewerQualitativeDefaultBrightness);
+
+/*
+ * CubeHelix color maps, as described in
+ * Green, D. A., 2011, A colour scheme for the display of astronomical intensity
+ * images, Bulletin of the Astronomical Society of India, 39, 289.
+ */
+
+// Create a CubeHelix colormap with n colors. The parameter hue (in [0,2*PI])
+// sets the hue of the first color. The parameter rot sets the number of
+// rotations. It can be negative for backwards rotation. The saturation parameter
+// determines the saturation of the colors; higher values may lead to clipping
+// of colors in the sRGB space. The gamma parameter sets optional gamma correction.
+// The return value is the number of colors that had to be clipped.
+
+const float CubeHelixDefaultHue = 5.23598775598f; // 5/6 * 2PI
+const float CubeHelixDefaultRotations = -1.5f;
+const float CubeHelixDefaultSaturation = 1.2f;
+const float CubeHelixDefaultGamma = 1.0f;
 
 
+int CubeHelix(int n, unsigned char* colormap,
+        float hue = CubeHelixDefaultHue,
+        float rotations = CubeHelixDefaultRotations,
+        float saturation = CubeHelixDefaultSaturation,
+        float gamma = CubeHelixDefaultGamma);
 }
 
 #endif
 }
 
 #endif
diff --git a/gui.cpp b/gui.cpp
index 77f55ac..7da9750 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2015 Computer Graphics Group, University of Siegen
+ * Copyright (C) 2015, 2016 Computer Graphics Group, University of Siegen
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * SOFTWARE.
  */
 
  * SOFTWARE.
  */
 
-#include <cmath>
-
 #include "gui.hpp"
 
 #include <QApplication>
 #include "gui.hpp"
 
 #include <QApplication>
+#include <QGuiApplication>
 #include <QGridLayout>
 #include <QLabel>
 #include <QRadioButton>
 #include <QGridLayout>
 #include <QLabel>
 #include <QRadioButton>
 #include <QClipboard>
 #include <QTextStream>
 #include <QMessageBox>
 #include <QClipboard>
 #include <QTextStream>
 #include <QMessageBox>
+#include <QtMath>
 
 #include "colormap.hpp"
 
 
 
 #include "colormap.hpp"
 
 
-CombinedSliderSpinBox::CombinedSliderSpinBox(float minval, float maxval, float step) :
+ColorMapCombinedSliderSpinBox::ColorMapCombinedSliderSpinBox(float minval, float maxval, float step) :
     _update_lock(false),
     minval(minval), maxval(maxval), step(step)
 {
     _update_lock(false),
     minval(minval), maxval(maxval), step(step)
 {
@@ -57,16 +57,16 @@ CombinedSliderSpinBox::CombinedSliderSpinBox(float minval, float maxval, float s
     spinbox->setSingleStep(step);
     spinbox->setDecimals(std::log10(1.0f / step));
 
     spinbox->setSingleStep(step);
     spinbox->setDecimals(std::log10(1.0f / step));
 
-    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slider_changed()));
-    connect(spinbox, SIGNAL(valueChanged(double)), this, SLOT(spinbox_changed()));
+    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged()));
+    connect(spinbox, SIGNAL(valueChanged(double)), this, SLOT(spinboxChanged()));
 }
 
 }
 
-float CombinedSliderSpinBox::value() const
+float ColorMapCombinedSliderSpinBox::value() const
 {
     return spinbox->value();
 }
 
 {
     return spinbox->value();
 }
 
-void CombinedSliderSpinBox::setValue(float v)
+void ColorMapCombinedSliderSpinBox::setValue(float v)
 {
     _update_lock = true;
     spinbox->setValue(v);
 {
     _update_lock = true;
     spinbox->setValue(v);
@@ -74,7 +74,7 @@ void CombinedSliderSpinBox::setValue(float v)
     _update_lock = false;
 }
 
     _update_lock = false;
 }
 
-void CombinedSliderSpinBox::slider_changed()
+void ColorMapCombinedSliderSpinBox::sliderChanged()
 {
     if (!_update_lock) {
         _update_lock = true;
 {
     if (!_update_lock) {
         _update_lock = true;
@@ -86,7 +86,7 @@ void CombinedSliderSpinBox::slider_changed()
     }
 }
 
     }
 }
 
-void CombinedSliderSpinBox::spinbox_changed()
+void ColorMapCombinedSliderSpinBox::spinboxChanged()
 {
     if (!_update_lock) {
         _update_lock = true;
 {
     if (!_update_lock) {
         _update_lock = true;
@@ -98,80 +98,462 @@ void CombinedSliderSpinBox::spinbox_changed()
     }
 }
 
     }
 }
 
-GUI::GUI()
+static void hideWidgetButPreserveSize(QWidget* widget)
 {
 {
-    setWindowTitle("Generate Color Map");
-    setWindowIcon(QIcon(":cg-logo.png"));
-    QWidget *widget = new QWidget;
-    QGridLayout *layout = new QGridLayout;
+    QSizePolicy sp = widget->sizePolicy();
+    sp.setRetainSizeWhenHidden(true);
+    widget->setSizePolicy(sp);
+    widget->hide();
+}
 
 
-    QLabel* type_label = new QLabel("Type:");
-    layout->addWidget(type_label, 0, 0);
-    type_seq_btn = new QRadioButton("Sequential");
-    layout->addWidget(type_seq_btn, 0, 1);
-    type_div_btn = new QRadioButton("Diverging");
-    layout->addWidget(type_div_btn, 0, 2);
-    QRadioButton* type_qual_btn = new QRadioButton("Qualitative");
-    layout->addWidget(type_qual_btn, 0, 3);
+ColorMapBrewerSequentialWidget::ColorMapBrewerSequentialWidget() :
+    _update_lock(false)
+{
+    QGridLayout *layout = new QGridLayout;
 
     QLabel* n_label = new QLabel("Colors:");
     layout->addWidget(n_label, 1, 0);
 
     QLabel* n_label = new QLabel("Colors:");
     layout->addWidget(n_label, 1, 0);
-    n_spinbox = new QSpinBox();
-    n_spinbox->setRange(2, 1024);
-    n_spinbox->setSingleStep(1);
-    layout->addWidget(n_spinbox, 1, 1, 1, 3);
+    _n_spinbox = new QSpinBox();
+    _n_spinbox->setRange(2, 1024);
+    _n_spinbox->setSingleStep(1);
+    layout->addWidget(_n_spinbox, 1, 1, 1, 3);
 
     QLabel* hue_label = new QLabel("Hue:");
     layout->addWidget(hue_label, 2, 0);
 
     QLabel* hue_label = new QLabel("Hue:");
     layout->addWidget(hue_label, 2, 0);
-    hue_changer = new CombinedSliderSpinBox(0, 360, 1);
-    layout->addWidget(hue_changer->slider, 2, 1, 1, 2);
-    layout->addWidget(hue_changer->spinbox, 2, 3);
+    _hue_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    layout->addWidget(_hue_changer->slider, 2, 1, 1, 2);
+    layout->addWidget(_hue_changer->spinbox, 2, 3);
 
 
-    divergence_label = new QLabel("Divergence:");
+    QLabel* divergence_label = new QLabel("Divergence:");
     layout->addWidget(divergence_label, 3, 0);
     layout->addWidget(divergence_label, 3, 0);
-    divergence_changer = new CombinedSliderSpinBox(0, 360, 1);
+    ColorMapCombinedSliderSpinBox* divergence_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
     layout->addWidget(divergence_changer->slider, 3, 1, 1, 2);
     layout->addWidget(divergence_changer->spinbox, 3, 3);
     layout->addWidget(divergence_changer->slider, 3, 1, 1, 2);
     layout->addWidget(divergence_changer->spinbox, 3, 3);
+    hideWidgetButPreserveSize(divergence_label);
+    hideWidgetButPreserveSize(divergence_changer->slider);
+    hideWidgetButPreserveSize(divergence_changer->spinbox);
+
+    QLabel* warmth_label = new QLabel("Warmth:");
+    layout->addWidget(warmth_label, 4, 0);
+    _warmth_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_warmth_changer->slider, 4, 1, 1, 2);
+    layout->addWidget(_warmth_changer->spinbox, 4, 3);
+
+    QLabel* contrast_label = new QLabel("Contrast:");
+    layout->addWidget(contrast_label, 5, 0);
+    _contrast_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_contrast_changer->slider, 5, 1, 1, 2);
+    layout->addWidget(_contrast_changer->spinbox, 5, 3);
+
+    QLabel* saturation_label = new QLabel("Saturation:");
+    layout->addWidget(saturation_label, 6, 0);
+    _saturation_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_saturation_changer->slider, 6, 1, 1, 2);
+    layout->addWidget(_saturation_changer->spinbox, 6, 3);
+
+    QLabel* brightness_label = new QLabel("Brightness:");
+    layout->addWidget(brightness_label, 7, 0);
+    _brightness_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_brightness_changer->slider, 7, 1, 1, 2);
+    layout->addWidget(_brightness_changer->spinbox, 7, 3);
+
+    layout->addItem(new QSpacerItem(0, 0), 8, 0, 1, 4);
+
+    layout->setColumnStretch(1, 1);
+    layout->addItem(new QSpacerItem(0, 0), 8, 0, 1, 4);
+    layout->setRowStretch(8, 1);
+    setLayout(layout);
+
+    connect(_n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
+    connect(_hue_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_warmth_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_contrast_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_saturation_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_brightness_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    reset();
+}
+
+ColorMapBrewerSequentialWidget::~ColorMapBrewerSequentialWidget()
+{
+}
+
+void ColorMapBrewerSequentialWidget::reset()
+{
+    _update_lock = true;
+    _n_spinbox->setValue(256);
+    _hue_changer->setValue(qRadiansToDegrees(ColorMap::BrewerSequentialDefaultHue));
+    _warmth_changer->setValue(ColorMap::BrewerSequentialDefaultWarmth);
+    _contrast_changer->setValue(ColorMap::BrewerSequentialDefaultContrast);
+    _saturation_changer->setValue(ColorMap::BrewerSequentialDefaultSaturation);
+    _brightness_changer->setValue(ColorMap::BrewerSequentialDefaultBrightness);
+    _update_lock = false;
+    update();
+}
+
+void ColorMapBrewerSequentialWidget::parameters(int& n, float& hue,
+        float& contrast, float& saturation, float& brightness, float& warmth)
+{
+    n = _n_spinbox->value();
+    hue = qDegreesToRadians(_hue_changer->value());
+    contrast = _contrast_changer->value();
+    saturation = _saturation_changer->value();
+    brightness = _brightness_changer->value();
+    warmth = _warmth_changer->value();
+}
+
+void ColorMapBrewerSequentialWidget::recomputeColorMap()
+{
+    int n;
+    float h, c, s, b, w;
+    parameters(n, h, c, s, b, w);
+    _colormap.resize(3 * n);
+    ColorMap::BrewerSequential(n, _colormap.data(), h, c, s, b, w);
+}
+
+void ColorMapBrewerSequentialWidget::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
+
+ColorMapBrewerDivergingWidget::ColorMapBrewerDivergingWidget() :
+    _update_lock(false)
+{
+    QGridLayout *layout = new QGridLayout;
 
 
-    warmth_label = new QLabel("Warmth:");
+    QLabel* n_label = new QLabel("Colors:");
+    layout->addWidget(n_label, 1, 0);
+    _n_spinbox = new QSpinBox();
+    _n_spinbox->setRange(2, 1024);
+    _n_spinbox->setSingleStep(1);
+    layout->addWidget(_n_spinbox, 1, 1, 1, 3);
+
+    QLabel* hue_label = new QLabel("Hue:");
+    layout->addWidget(hue_label, 2, 0);
+    _hue_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    layout->addWidget(_hue_changer->slider, 2, 1, 1, 2);
+    layout->addWidget(_hue_changer->spinbox, 2, 3);
+
+    QLabel* divergence_label = new QLabel("Divergence:");
+    layout->addWidget(divergence_label, 3, 0);
+    _divergence_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    layout->addWidget(_divergence_changer->slider, 3, 1, 1, 2);
+    layout->addWidget(_divergence_changer->spinbox, 3, 3);
+
+    QLabel* warmth_label = new QLabel("Warmth:");
     layout->addWidget(warmth_label, 4, 0);
     layout->addWidget(warmth_label, 4, 0);
-    warmth_changer = new CombinedSliderSpinBox(0, 1, 0.01f);
+    _warmth_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_warmth_changer->slider, 4, 1, 1, 2);
+    layout->addWidget(_warmth_changer->spinbox, 4, 3);
+
+    QLabel* contrast_label = new QLabel("Contrast:");
+    layout->addWidget(contrast_label, 5, 0);
+    _contrast_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_contrast_changer->slider, 5, 1, 1, 2);
+    layout->addWidget(_contrast_changer->spinbox, 5, 3);
+
+    QLabel* saturation_label = new QLabel("Saturation:");
+    layout->addWidget(saturation_label, 6, 0);
+    _saturation_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_saturation_changer->slider, 6, 1, 1, 2);
+    layout->addWidget(_saturation_changer->spinbox, 6, 3);
+
+    QLabel* brightness_label = new QLabel("Brightness:");
+    layout->addWidget(brightness_label, 7, 0);
+    _brightness_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_brightness_changer->slider, 7, 1, 1, 2);
+    layout->addWidget(_brightness_changer->spinbox, 7, 3);
+
+    layout->setColumnStretch(1, 1);
+    layout->addItem(new QSpacerItem(0, 0), 8, 0, 1, 4);
+    layout->setRowStretch(8, 1);
+    setLayout(layout);
+
+    connect(_n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
+    connect(_hue_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_divergence_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_warmth_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_contrast_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_saturation_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_brightness_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    reset();
+}
+
+ColorMapBrewerDivergingWidget::~ColorMapBrewerDivergingWidget()
+{
+}
+
+void ColorMapBrewerDivergingWidget::reset()
+{
+    _update_lock = true;
+    _n_spinbox->setValue(256);
+    _hue_changer->setValue(qRadiansToDegrees(ColorMap::BrewerDivergingDefaultHue));
+    _divergence_changer->setValue(qRadiansToDegrees(ColorMap::BrewerDivergingDefaultDivergence));
+    _warmth_changer->setValue(ColorMap::BrewerDivergingDefaultWarmth);
+    _contrast_changer->setValue(ColorMap::BrewerDivergingDefaultContrast);
+    _saturation_changer->setValue(ColorMap::BrewerDivergingDefaultSaturation);
+    _brightness_changer->setValue(ColorMap::BrewerDivergingDefaultBrightness);
+    _update_lock = false;
+    update();
+}
+
+void ColorMapBrewerDivergingWidget::parameters(int& n, float& hue, float& divergence,
+        float& contrast, float& saturation, float& brightness, float& warmth)
+{
+    n = _n_spinbox->value();
+    hue = qDegreesToRadians(_hue_changer->value());
+    divergence = qDegreesToRadians(_divergence_changer->value());
+    contrast = _contrast_changer->value();
+    saturation = _saturation_changer->value();
+    brightness = _brightness_changer->value();
+    warmth = _warmth_changer->value();
+}
+
+void ColorMapBrewerDivergingWidget::recomputeColorMap()
+{
+    int n;
+    float h, d, c, s, b, w;
+    parameters(n, h, d, c, s, b, w);
+    _colormap.resize(3 * n);
+    ColorMap::BrewerDiverging(n, _colormap.data(), h, d, c, s, b, w);
+}
+
+void ColorMapBrewerDivergingWidget::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
+
+ColorMapBrewerQualitativeWidget::ColorMapBrewerQualitativeWidget() :
+    _update_lock(false)
+{
+    QGridLayout *layout = new QGridLayout;
+
+    QLabel* n_label = new QLabel("Colors:");
+    layout->addWidget(n_label, 1, 0);
+    _n_spinbox = new QSpinBox();
+    _n_spinbox->setRange(2, 1024);
+    _n_spinbox->setSingleStep(1);
+    layout->addWidget(_n_spinbox, 1, 1, 1, 3);
+
+    QLabel* hue_label = new QLabel("Hue:");
+    layout->addWidget(hue_label, 2, 0);
+    _hue_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    layout->addWidget(_hue_changer->slider, 2, 1, 1, 2);
+    layout->addWidget(_hue_changer->spinbox, 2, 3);
+
+    QLabel* divergence_label = new QLabel("Divergence:");
+    layout->addWidget(divergence_label, 3, 0);
+    _divergence_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    layout->addWidget(_divergence_changer->slider, 3, 1, 1, 2);
+    layout->addWidget(_divergence_changer->spinbox, 3, 3);
+
+    QLabel* warmth_label = new QLabel("Warmth:");
+    layout->addWidget(warmth_label, 4, 0);
+    ColorMapCombinedSliderSpinBox* warmth_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
     layout->addWidget(warmth_changer->slider, 4, 1, 1, 2);
     layout->addWidget(warmth_changer->spinbox, 4, 3);
     layout->addWidget(warmth_changer->slider, 4, 1, 1, 2);
     layout->addWidget(warmth_changer->spinbox, 4, 3);
+    hideWidgetButPreserveSize(warmth_label);
+    hideWidgetButPreserveSize(warmth_changer->slider);
+    hideWidgetButPreserveSize(warmth_changer->spinbox);
 
     QLabel* contrast_label = new QLabel("Contrast:");
     layout->addWidget(contrast_label, 5, 0);
 
     QLabel* contrast_label = new QLabel("Contrast:");
     layout->addWidget(contrast_label, 5, 0);
-    contrast_changer = new CombinedSliderSpinBox(0, 1, 0.01f);
-    layout->addWidget(contrast_changer->slider, 5, 1, 1, 2);
-    layout->addWidget(contrast_changer->spinbox, 5, 3);
+    _contrast_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_contrast_changer->slider, 5, 1, 1, 2);
+    layout->addWidget(_contrast_changer->spinbox, 5, 3);
 
     QLabel* saturation_label = new QLabel("Saturation:");
     layout->addWidget(saturation_label, 6, 0);
 
     QLabel* saturation_label = new QLabel("Saturation:");
     layout->addWidget(saturation_label, 6, 0);
-    saturation_changer = new CombinedSliderSpinBox(0, 1, 0.01f);
-    layout->addWidget(saturation_changer->slider, 6, 1, 1, 2);
-    layout->addWidget(saturation_changer->spinbox, 6, 3);
+    _saturation_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_saturation_changer->slider, 6, 1, 1, 2);
+    layout->addWidget(_saturation_changer->spinbox, 6, 3);
 
     QLabel* brightness_label = new QLabel("Brightness:");
     layout->addWidget(brightness_label, 7, 0);
 
     QLabel* brightness_label = new QLabel("Brightness:");
     layout->addWidget(brightness_label, 7, 0);
-    brightness_changer = new CombinedSliderSpinBox(0, 1, 0.01f);
-    layout->addWidget(brightness_changer->slider, 7, 1, 1, 2);
-    layout->addWidget(brightness_changer->spinbox, 7, 3);
-
-    connect(type_seq_btn, SIGNAL(toggled(bool)), this, SLOT(update()));
-    connect(n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
-    connect(type_div_btn, SIGNAL(toggled(bool)), this, SLOT(update()));
-    connect(hue_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
-    connect(divergence_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
-    connect(warmth_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
-    connect(contrast_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
-    connect(saturation_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
-    connect(brightness_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
-
-    colormap_label = new QLabel();
-    colormap_label->setScaledContents(true);
-    layout->addWidget(colormap_label, 0, 4, 8, 1);
-
-    layout->setColumnStretch(4, 1);
+    _brightness_changer = new ColorMapCombinedSliderSpinBox(0, 1, 0.01f);
+    layout->addWidget(_brightness_changer->slider, 7, 1, 1, 2);
+    layout->addWidget(_brightness_changer->spinbox, 7, 3);
+
+    layout->setColumnStretch(1, 1);
+    layout->addItem(new QSpacerItem(0, 0), 8, 0, 1, 4);
+    layout->setRowStretch(8, 1);
+    setLayout(layout);
+
+    connect(_n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
+    connect(_hue_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_divergence_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_contrast_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_saturation_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_brightness_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    reset();
+}
+
+ColorMapBrewerQualitativeWidget::~ColorMapBrewerQualitativeWidget()
+{
+}
+
+void ColorMapBrewerQualitativeWidget::reset()
+{
+    _update_lock = true;
+    _n_spinbox->setValue(256);
+    _hue_changer->setValue(qRadiansToDegrees(ColorMap::BrewerQualitativeDefaultHue));
+    _divergence_changer->setValue(qRadiansToDegrees(ColorMap::BrewerQualitativeDefaultDivergence));
+    _contrast_changer->setValue(ColorMap::BrewerQualitativeDefaultContrast);
+    _saturation_changer->setValue(ColorMap::BrewerQualitativeDefaultSaturation);
+    _brightness_changer->setValue(ColorMap::BrewerQualitativeDefaultBrightness);
+    _update_lock = false;
+    update();
+}
+
+void ColorMapBrewerQualitativeWidget::parameters(int& n, float& hue, float& divergence,
+        float& contrast, float& saturation, float& brightness)
+{
+    n = _n_spinbox->value();
+    hue = qDegreesToRadians(_hue_changer->value());
+    divergence = qDegreesToRadians(_divergence_changer->value());
+    contrast = _contrast_changer->value();
+    saturation = _saturation_changer->value();
+    brightness = _brightness_changer->value();
+}
+
+void ColorMapBrewerQualitativeWidget::recomputeColorMap()
+{
+    int n;
+    float h, d, c, s, b;
+    parameters(n, h, d, c, s, b);
+    _colormap.resize(3 * n);
+    ColorMap::BrewerQualitative(n, _colormap.data(), h, d, c, s, b);
+}
+
+void ColorMapBrewerQualitativeWidget::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
+
+ColorMapCubeHelixWidget::ColorMapCubeHelixWidget() :
+    _update_lock(false)
+{
+    QGridLayout *layout = new QGridLayout;
+
+    QLabel* n_label = new QLabel("Colors:");
+    layout->addWidget(n_label, 1, 0);
+    _n_spinbox = new QSpinBox();
+    _n_spinbox->setRange(2, 1024);
+    _n_spinbox->setSingleStep(1);
+    layout->addWidget(_n_spinbox, 1, 1, 1, 3);
+
+    QLabel* hue_label = new QLabel("Hue:");
+    layout->addWidget(hue_label, 2, 0);
+    _hue_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    layout->addWidget(_hue_changer->slider, 2, 1, 1, 2);
+    layout->addWidget(_hue_changer->spinbox, 2, 3);
+
+    QLabel* rotations_label = new QLabel("Rotations:");
+    layout->addWidget(rotations_label, 3, 0);
+    _rotations_changer = new ColorMapCombinedSliderSpinBox(-5.0f, +5.0f, 0.1f);
+    layout->addWidget(_rotations_changer->slider, 3, 1, 1, 2);
+    layout->addWidget(_rotations_changer->spinbox, 3, 3);
+
+    QLabel* saturation_label = new QLabel("Saturation:");
+    layout->addWidget(saturation_label, 4, 0);
+    _saturation_changer = new ColorMapCombinedSliderSpinBox(0.0f, 2.0f, 0.1f);
+    layout->addWidget(_saturation_changer->slider, 4, 1, 1, 2);
+    layout->addWidget(_saturation_changer->spinbox, 4, 3);
+
+    QLabel* gamma_label = new QLabel("Gamma:");
+    layout->addWidget(gamma_label, 5, 0);
+    _gamma_changer = new ColorMapCombinedSliderSpinBox(0.3f, 3.0f, 0.1f);
+    layout->addWidget(_gamma_changer->slider, 5, 1, 1, 2);
+    layout->addWidget(_gamma_changer->spinbox, 5, 3);
+
+    layout->setColumnStretch(1, 1);
+    layout->addItem(new QSpacerItem(0, 0), 6, 0, 1, 4);
+    layout->setRowStretch(6, 1);
+    setLayout(layout);
+
+    connect(_n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
+    connect(_hue_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_rotations_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_saturation_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_gamma_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    reset();
+}
+
+ColorMapCubeHelixWidget::~ColorMapCubeHelixWidget()
+{
+}
+
+void ColorMapCubeHelixWidget::reset()
+{
+    _update_lock = true;
+    _n_spinbox->setValue(256);
+    _hue_changer->setValue(qRadiansToDegrees(ColorMap::CubeHelixDefaultHue));
+    _rotations_changer->setValue(ColorMap::CubeHelixDefaultRotations);
+    _saturation_changer->setValue(ColorMap::CubeHelixDefaultSaturation);
+    _gamma_changer->setValue(ColorMap::CubeHelixDefaultGamma);
+    _update_lock = false;
+    update();
+}
+
+void ColorMapCubeHelixWidget::parameters(int& n, float& hue,
+        float& rotations, float& saturation, float& gamma)
+{
+    n = _n_spinbox->value();
+    hue = qDegreesToRadians(_hue_changer->value());
+    rotations = _rotations_changer->value();
+    saturation = _saturation_changer->value();
+    gamma = _gamma_changer->value();
+}
+
+void ColorMapCubeHelixWidget::recomputeColorMap()
+{
+    int n;
+    float h, r, s, g;
+    parameters(n, h, r, s, g);
+    _colormap.resize(3 * n);
+    ColorMap::CubeHelix(n, _colormap.data(), h, r, s, g);
+}
+
+void ColorMapCubeHelixWidget::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
+
+
+GUI::GUI()
+{
+    setWindowTitle("Generate Color Map");
+    setWindowIcon(QIcon(":cg-logo.png"));
+
+    _brewerseq_widget = new ColorMapBrewerSequentialWidget;
+    _brewerdiv_widget = new ColorMapBrewerDivergingWidget;
+    _brewerqual_widget = new ColorMapBrewerQualitativeWidget;
+    _cubehelix_widget = new ColorMapCubeHelixWidget;
+    connect(_brewerseq_widget, SIGNAL(colorMapChanged()), this, SLOT(update()));
+    connect(_brewerdiv_widget, SIGNAL(colorMapChanged()), this, SLOT(update()));
+    connect(_brewerqual_widget, SIGNAL(colorMapChanged()), this, SLOT(update()));
+    connect(_cubehelix_widget, SIGNAL(colorMapChanged()), this, SLOT(update()));
+
+    QWidget *widget = new QWidget;
+    widget->setMinimumWidth(384 * qApp->devicePixelRatio());
+    QGridLayout *layout = new QGridLayout;
+
+    _tab_widget = new QTabWidget();
+    _tab_widget->addTab(_brewerseq_widget,  "Brewer-like Sequential");
+    _tab_widget->addTab(_brewerdiv_widget,  "Brewer-like Diverging");
+    _tab_widget->addTab(_brewerqual_widget, "Brewer-like Qualitative");
+    _tab_widget->addTab(_cubehelix_widget,  "CubeHelix");
+    connect(_tab_widget, SIGNAL(currentChanged(int)), this, SLOT(update()));
+    layout->addWidget(_tab_widget, 0, 0);
+
+    _colormap_label = new QLabel();
+    _colormap_label->setScaledContents(true);
+    layout->addWidget(_colormap_label, 0, 1);
+
+    layout->setColumnStretch(0, 1);
     widget->setLayout(layout);
     setCentralWidget(widget);
 
     widget->setLayout(layout);
     setCentralWidget(widget);
 
@@ -204,87 +586,30 @@ GUI::GUI()
     help_menu->addAction(help_about_act);
 
     show();
     help_menu->addAction(help_about_act);
 
     show();
-    edit_reset();
+    update();
 }
 
 GUI::~GUI()
 {
 }
 
 }
 
 GUI::~GUI()
 {
 }
 
-void GUI::get_params(int& type, int& n, float& hue, float& divergence,
-        float& contrast, float& saturation, float& brightness,
-        float& warmth)
-{
-    type = type_seq_btn->isChecked() ? 0 : type_div_btn->isChecked() ? 1 : 2;
-    n = n_spinbox->value();
-    hue = hue_changer->value() / 180.0f * static_cast<float>(M_PI);
-    divergence = divergence_changer->value() / 180.0f * static_cast<float>(M_PI);
-    contrast = contrast_changer->value();
-    saturation = saturation_changer->value();
-    brightness = brightness_changer->value();
-    warmth = warmth_changer->value();
-}
-
-std::vector<unsigned char> GUI::get_map(int type, int n, float hue, float divergence,
-        float contrast, float saturation, float brightness,
-        float warmth)
-{
-    std::vector<unsigned char> colormap(3 * n);
-    if (type == 0) {
-        ColorMap::Sequential(n, &(colormap[0]), hue,
-                contrast, saturation, brightness, warmth);
-    } else if (type == 1) {
-        ColorMap::Diverging(n, &(colormap[0]), hue, divergence,
-                contrast, saturation, brightness, warmth);
-    } else {
-        ColorMap::Qualitative(n, &(colormap[0]), hue, divergence,
-                contrast, saturation, brightness);
-    }
-    return colormap;
-}
-
 void GUI::update()
 {
 void GUI::update()
 {
-    if (update_lock)
-        return;
+    ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
+    const QVector<unsigned char>& colormap = *currentWidget->colorMap();
 
 
-    int type, n;
-    float hue, divergence, contrast, saturation, brightness, warmth;
-    get_params(type, n, hue, divergence, contrast, saturation, brightness, warmth);
-
-    divergence_label->setEnabled(type >= 1);
-    divergence_changer->slider->setEnabled(type >= 1);
-    divergence_changer->spinbox->setEnabled(type >= 1);
-    warmth_label->setEnabled(type <= 1);
-    warmth_changer->slider->setEnabled(type <= 1);
-    warmth_changer->spinbox->setEnabled(type <= 1);
-
-    const int img_width = 32;
-    const int img_height = colormap_label->height();
-    if (img_height < n)
-        n = img_height;
+    int img_width = 32;
+    int img_height = _colormap_label->height();
     QImage img(img_width, img_height, QImage::Format_RGB32);
     QImage img(img_width, img_height, QImage::Format_RGB32);
-
-    std::vector<unsigned char> colormap(3 * n);
-    if (type == 0) {
-        ColorMap::Sequential(n, &(colormap[0]), hue,
-                contrast, saturation, brightness, warmth);
-    } else if (type == 1) {
-        ColorMap::Diverging(n, &(colormap[0]), hue, divergence,
-                contrast, saturation, brightness, warmth);
-    } else {
-        ColorMap::Qualitative(n, &(colormap[0]), hue, divergence,
-                contrast, saturation, brightness);
-    }
     for (int y = 0; y < img_height; y++) {
     for (int y = 0; y < img_height; y++) {
-        float entry_height = img_height / static_cast<float>(n);
+        float entry_height = img_height / static_cast<float>(colormap.size() / 3);
         int i = y / entry_height;
         QRgb rgb = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
         QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(y));
         for (int x = 0; x < img_width; x++)
             scanline[x] = rgb;
     }
         int i = y / entry_height;
         QRgb rgb = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
         QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(y));
         for (int x = 0; x < img_width; x++)
             scanline[x] = rgb;
     }
-    colormap_label->setPixmap(QPixmap::fromImage(img));
+    _colormap_label->setPixmap(QPixmap::fromImage(img));
 }
 
 void GUI::file_export_png()
 }
 
 void GUI::file_export_png()
@@ -292,14 +617,11 @@ void GUI::file_export_png()
     QString name = QFileDialog::getSaveFileName();
     if (!name.isEmpty()) {
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
     QString name = QFileDialog::getSaveFileName();
     if (!name.isEmpty()) {
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-        int type, n;
-        float hue, divergence, contrast, saturation, brightness, warmth;
-        get_params(type, n, hue, divergence, contrast, saturation, brightness, warmth);
-        std::vector<unsigned char> colormap = get_map(type, n, hue, divergence,
-                contrast, saturation, brightness, warmth);
-        QImage img(n, 1, QImage::Format_RGB32);
+        ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
+        const QVector<unsigned char>& colormap = *currentWidget->colorMap();
+        QImage img(colormap.size() / 3, 1, QImage::Format_RGB32);
         QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(0));
         QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(0));
-        for (int i = 0; i < n; i++) {
+        for (int i = 0; i < colormap.size() / 3; i++) {
             scanline[i] = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
         }
         img.save(name, "png");
             scanline[i] = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
         }
         img.save(name, "png");
@@ -314,13 +636,10 @@ void GUI::file_export_csv()
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
         QFile file(name);
         if (file.open(QIODevice::ReadWrite)) {
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
         QFile file(name);
         if (file.open(QIODevice::ReadWrite)) {
-            int type, n;
-            float hue, divergence, contrast, saturation, brightness, warmth;
-            get_params(type, n, hue, divergence, contrast, saturation, brightness, warmth);
-            std::vector<unsigned char> colormap = get_map(type, n, hue, divergence,
-                    contrast, saturation, brightness, warmth);
+            ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
+            const QVector<unsigned char>& colormap = *currentWidget->colorMap();
             QTextStream stream(&file);
             QTextStream stream(&file);
-            for (int i = 0; i < n; i++) {
+            for (int i = 0; i < colormap.size() / 3; i++) {
                 stream << colormap[3 * i + 0] << ", "
                        << colormap[3 * i + 1] << ", "
                        << colormap[3 * i + 2] << endl;
                 stream << colormap[3 * i + 0] << ", "
                        << colormap[3 * i + 1] << ", "
                        << colormap[3 * i + 2] << endl;
@@ -332,29 +651,17 @@ void GUI::file_export_csv()
 
 void GUI::edit_reset()
 {
 
 void GUI::edit_reset()
 {
-    update_lock = true;
-    type_seq_btn->setChecked(true);
-    n_spinbox->setValue(301);
-    hue_changer->setValue(0);
-    divergence_changer->setValue(240);
-    warmth_changer->setValue(ColorMap::DefaultWarmth);
-    contrast_changer->setValue(ColorMap::DefaultContrast);
-    saturation_changer->setValue(ColorMap::DefaultSaturation);
-    brightness_changer->setValue(ColorMap::DefaultBrightness);
-    update_lock = false;
-    update();
+    ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
+    currentWidget->reset();
 }
 
 void GUI::edit_copy_as_img()
 {
 }
 
 void GUI::edit_copy_as_img()
 {
-    int type, n;
-    float hue, divergence, contrast, saturation, brightness, warmth;
-    get_params(type, n, hue, divergence, contrast, saturation, brightness, warmth);
-    std::vector<unsigned char> colormap = get_map(type, n, hue, divergence,
-            contrast, saturation, brightness, warmth);
-    QImage img(n, 1, QImage::Format_RGB32);
+    ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
+    const QVector<unsigned char>& colormap = *currentWidget->colorMap();
+    QImage img(colormap.size() / 3, 1, QImage::Format_RGB32);
     QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(0));
     QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(0));
-    for (int i = 0; i < n; i++) {
+    for (int i = 0; i < colormap.size() / 3; i++) {
         scanline[i] = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
     }
     QApplication::clipboard()->setImage(img);
         scanline[i] = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
     }
     QApplication::clipboard()->setImage(img);
@@ -362,14 +669,11 @@ void GUI::edit_copy_as_img()
 
 void GUI::edit_copy_as_txt()
 {
 
 void GUI::edit_copy_as_txt()
 {
-    int type, n;
-    float hue, divergence, contrast, saturation, brightness, warmth;
-    get_params(type, n, hue, divergence, contrast, saturation, brightness, warmth);
-    std::vector<unsigned char> colormap = get_map(type, n, hue, divergence,
-            contrast, saturation, brightness, warmth);
+    ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
+    const QVector<unsigned char>& colormap = *currentWidget->colorMap();
     QString string;
     QTextStream stream(&string);
     QString string;
     QTextStream stream(&string);
-    for (int i = 0; i < n; i++) {
+    for (int i = 0; i < colormap.size() / 3; i++) {
         stream << colormap[3 * i + 0] << ", "
                << colormap[3 * i + 1] << ", "
                << colormap[3 * i + 2] << endl;
         stream << colormap[3 * i + 0] << ", "
                << colormap[3 * i + 1] << ", "
                << colormap[3 * i + 2] << endl;
@@ -380,8 +684,8 @@ void GUI::edit_copy_as_txt()
 void GUI::help_about()
 {
     QMessageBox::about(this, "About",
 void GUI::help_about()
 {
     QMessageBox::about(this, "About",
-                "<p>gencolormap version 0.1</p>"
-                "<p>Copyright (C) 2015<br>"
+                "<p>gencolormap version 0.2</p>"
+                "<p>Copyright (C) 2016<br>"
                 "   <a href=\"http://www.cg.informatik.uni-siegen.de/\">"
                 "   Computer Graphics Group, University of Siegen</a>.<br>"
                 "   Written by <a href=\"http://www.cg.informatik.uni-siegen.de/lambers-martin\">Martin Lambers</a>.<br>"
                 "   <a href=\"http://www.cg.informatik.uni-siegen.de/\">"
                 "   Computer Graphics Group, University of Siegen</a>.<br>"
                 "   Written by <a href=\"http://www.cg.informatik.uni-siegen.de/lambers-martin\">Martin Lambers</a>.<br>"
diff --git a/gui.hpp b/gui.hpp
index 8f136d9..633c399 100644 (file)
--- a/gui.hpp
+++ b/gui.hpp
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2015 Computer Graphics Group, University of Siegen
+ * Copyright (C) 2015, 2016 Computer Graphics Group, University of Siegen
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
 #ifndef GUI_HPP
 #define GUI_HPP
 
 #ifndef GUI_HPP
 #define GUI_HPP
 
-#include <vector>
-
+#include <QVector>
 #include <QMainWindow>
 
 #include <QMainWindow>
 
+class QTabWidget;
 class QLabel;
 class QRadioButton;
 class QSpinBox;
 class QSlider;
 class QDoubleSpinBox;
 class QLabel;
 class QRadioButton;
 class QSpinBox;
 class QSlider;
 class QDoubleSpinBox;
+class QTabWidget;
 
 
 
 
-class CombinedSliderSpinBox : public QObject
+class ColorMapCombinedSliderSpinBox : public QObject
 {
 Q_OBJECT
 private:
     bool _update_lock;
 {
 Q_OBJECT
 private:
     bool _update_lock;
+
 public:
     float minval, maxval, step;
     QSlider* slider;
     QDoubleSpinBox* spinbox;
 
 public:
     float minval, maxval, step;
     QSlider* slider;
     QDoubleSpinBox* spinbox;
 
-    CombinedSliderSpinBox(float minval, float maxval, float step);
+    ColorMapCombinedSliderSpinBox(float minval, float maxval, float step);
     float value() const;
     void setValue(float v);
 
 private slots:
     float value() const;
     void setValue(float v);
 
 private slots:
-    void slider_changed();
-    void spinbox_changed();
+    void sliderChanged();
+    void spinboxChanged();
 
 signals:
     void valueChanged(float);
 };
 
 
 signals:
     void valueChanged(float);
 };
 
+class ColorMapWidget : public QWidget
+{
+Q_OBJECT
+protected:
+    QVector<unsigned char> _colormap;
+    virtual void recomputeColorMap() = 0;
+
+public:
+    ColorMapWidget() {}
+    ~ColorMapWidget() {}
+
+    virtual void reset() = 0;
+
+    const QVector<unsigned char>* colorMap()
+    {
+        recomputeColorMap();
+        return &_colormap;
+    }
+
+signals:
+    void colorMapChanged();
+};
+
+class ColorMapBrewerSequentialWidget : public ColorMapWidget
+{
+Q_OBJECT
+private:
+    bool _update_lock;
+    QSpinBox* _n_spinbox;
+    ColorMapCombinedSliderSpinBox* _hue_changer;
+    ColorMapCombinedSliderSpinBox* _warmth_changer;
+    ColorMapCombinedSliderSpinBox* _contrast_changer;
+    ColorMapCombinedSliderSpinBox* _saturation_changer;
+    ColorMapCombinedSliderSpinBox* _brightness_changer;
+private slots:
+    void update();
+
+protected:
+    void recomputeColorMap() override;
+
+public:
+    ColorMapBrewerSequentialWidget();
+    ~ColorMapBrewerSequentialWidget();
+
+    void reset() override;
+    void parameters(int& n, float& hue,
+            float& contrast, float& saturation, float& brightness, float& warmth);
+};
+
+class ColorMapBrewerDivergingWidget : public ColorMapWidget
+{
+Q_OBJECT
+private:
+    bool _update_lock;
+    QSpinBox* _n_spinbox;
+    ColorMapCombinedSliderSpinBox* _hue_changer;
+    ColorMapCombinedSliderSpinBox* _divergence_changer;
+    ColorMapCombinedSliderSpinBox* _warmth_changer;
+    ColorMapCombinedSliderSpinBox* _contrast_changer;
+    ColorMapCombinedSliderSpinBox* _saturation_changer;
+    ColorMapCombinedSliderSpinBox* _brightness_changer;
+private slots:
+    void update();
+
+protected:
+    void recomputeColorMap() override;
+
+public:
+    ColorMapBrewerDivergingWidget();
+    ~ColorMapBrewerDivergingWidget();
+
+    void reset() override;
+    void parameters(int& n, float& hue, float& divergence,
+            float& contrast, float& saturation, float& brightness, float& warmth);
+};
+
+class ColorMapBrewerQualitativeWidget : public ColorMapWidget
+{
+Q_OBJECT
+private:
+    bool _update_lock;
+    QSpinBox* _n_spinbox;
+    ColorMapCombinedSliderSpinBox* _hue_changer;
+    ColorMapCombinedSliderSpinBox* _divergence_changer;
+    ColorMapCombinedSliderSpinBox* _contrast_changer;
+    ColorMapCombinedSliderSpinBox* _saturation_changer;
+    ColorMapCombinedSliderSpinBox* _brightness_changer;
+private slots:
+    void update();
+
+protected:
+    void recomputeColorMap() override;
+
+public:
+    ColorMapBrewerQualitativeWidget();
+    ~ColorMapBrewerQualitativeWidget();
+
+    void reset() override;
+    void parameters(int& n, float& hue, float& divergence,
+            float& contrast, float& saturation, float& brightness);
+};
+
+class ColorMapCubeHelixWidget : public ColorMapWidget
+{
+Q_OBJECT
+private:
+    bool _update_lock;
+    QSpinBox* _n_spinbox;
+    ColorMapCombinedSliderSpinBox* _hue_changer;
+    ColorMapCombinedSliderSpinBox* _rotations_changer;
+    ColorMapCombinedSliderSpinBox* _saturation_changer;
+    ColorMapCombinedSliderSpinBox* _gamma_changer;
+private slots:
+    void update();
+
+protected:
+    void recomputeColorMap() override;
+
+public:
+    ColorMapCubeHelixWidget();
+    ~ColorMapCubeHelixWidget();
+
+    void reset() override;
+    void parameters(int& n, float& hue, float& rotations,
+            float& saturation, float& gamma);
+};
+
 class GUI : public QMainWindow
 {
 Q_OBJECT
 class GUI : public QMainWindow
 {
 Q_OBJECT
@@ -66,27 +195,12 @@ public:
     ~GUI();
 
 private:
     ~GUI();
 
 private:
-    bool update_lock;
-    QRadioButton* type_seq_btn;
-    QRadioButton* type_div_btn;
-    QSpinBox* n_spinbox;
-    CombinedSliderSpinBox* hue_changer;
-    QLabel* divergence_label;
-    CombinedSliderSpinBox* divergence_changer;
-    QLabel* warmth_label;
-    CombinedSliderSpinBox* warmth_changer;
-    CombinedSliderSpinBox* contrast_changer;
-    CombinedSliderSpinBox* saturation_changer;
-    CombinedSliderSpinBox* brightness_changer;
-    QLabel* colormap_label;
-
-    void get_params(int& type, int& n, float& hue, float& divergence,
-            float& contrast, float& saturation, float& brightness,
-            float& warmth);
-    std::vector<unsigned char> get_map(int type, int n, float hue, float divergence,
-            float contrast, float saturation, float brightness,
-            float warmth);
-
+    ColorMapBrewerSequentialWidget* _brewerseq_widget;
+    ColorMapBrewerDivergingWidget* _brewerdiv_widget;
+    ColorMapBrewerQualitativeWidget* _brewerqual_widget;
+    ColorMapCubeHelixWidget* _cubehelix_widget;
+    QTabWidget* _tab_widget;
+    QLabel* _colormap_label;
 
 private slots:
     void update();
 
 private slots:
     void update();