Implement Moreland diverging color maps.
authorMartin Lambers <marlam@marlam.de>
Mon, 8 Feb 2016 08:33:40 +0000 (09:33 +0100)
committerMartin Lambers <marlam@marlam.de>
Mon, 8 Feb 2016 08:33:40 +0000 (09:33 +0100)
cmdline.cpp
colormap.cpp
colormap.hpp
colormapwidgets.cpp
colormapwidgets.hpp
gui.cpp
gui.hpp

index ae83678..1f7f4fc 100644 (file)
@@ -37,7 +37,8 @@ enum type {
     brewer_sequential = 0,
     brewer_diverging = 1,
     brewer_qualitative = 2,
-    cubehelix = 3
+    cubehelix = 3,
+    morelanddiverging = 4
 };
 
 int main(int argc, char* argv[])
@@ -54,6 +55,10 @@ int main(int argc, char* argv[])
     float warmth = -1.0f;
     float rotations = NAN;
     float gamma = -1.0f;
+    bool have_color0 = false;
+    unsigned char color0[3];
+    bool have_color1 = false;
+    unsigned char color1[3];
     struct option options[] = {
         { "version",    no_argument,       0, 'v' },
         { "help",       no_argument,       0, 'H' },
@@ -67,11 +72,13 @@ int main(int argc, char* argv[])
         { "warmth",     required_argument, 0, 'w' },
         { "rotations",  required_argument, 0, 'r' },
         { "gamma",      required_argument, 0, 'g' },
+        { "color0",     required_argument, 0, 'A' },
+        { "color1",     required_argument, 0, 'O' },
         { 0, 0, 0, 0 }
     };
 
     for (;;) {
-        int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:r:g:", options, NULL);
+        int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:r:g:A:O:", options, NULL);
         if (c == -1)
             break;
         switch (c) {
@@ -86,6 +93,7 @@ int main(int argc, char* argv[])
                     : strcmp(optarg, "brewer-diverging") == 0 ? brewer_diverging
                     : strcmp(optarg, "brewer-qualitative") == 0 ? brewer_qualitative
                     : strcmp(optarg, "cubehelix") == 0 ? cubehelix
+                    : strcmp(optarg, "morelanddiverging") == 0 ? morelanddiverging
                     : -2);
             break;
         case 'n':
@@ -115,6 +123,14 @@ int main(int argc, char* argv[])
         case 'g':
             gamma = atof(optarg);
             break;
+        case 'A':
+            std::sscanf(optarg, "%hhu,%hhu,%hhu", color0 + 0, color0 + 1, color0 + 2);
+            have_color0 = true;
+            break;
+        case 'O':
+            std::sscanf(optarg, "%hhu,%hhu,%hhu", color1 + 0, color1 + 1, color1 + 2);
+            have_color1 = true;
+            break;
         default:
             return 1;
         }
@@ -148,6 +164,10 @@ int main(int argc, char* argv[])
                 "    [-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"
+                "  MorelandDiverging color maps:\n"
+                "    -t|--type=morelanddiverging   Generate a Moreland diverging color map\n"
+                "    [-A|--color0=sr,sg,sb         Set the first color as sRGB values in [0,255]\n"
+                "    [-O|--color1=sr,sg,sb         Set the last color as sRGB values in [0,255]\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]);
         return 0;
@@ -219,6 +239,20 @@ int main(int argc, char* argv[])
         if (type == cubehelix)
             gamma = ColorMap::CubeHelixDefaultGamma;
     }
+    if (!have_color0) {
+        if (type == morelanddiverging) {
+            color0[0] = ColorMap::MorelandDivergingDefaultR0;
+            color0[1] = ColorMap::MorelandDivergingDefaultG0;
+            color0[2] = ColorMap::MorelandDivergingDefaultB0;
+        }
+    }
+    if (!have_color1) {
+        if (type == morelanddiverging) {
+            color1[0] = ColorMap::MorelandDivergingDefaultR1;
+            color1[1] = ColorMap::MorelandDivergingDefaultG1;
+            color1[2] = ColorMap::MorelandDivergingDefaultB1;
+        }
+    }
 
     std::vector<unsigned char> colormap(3 * n);
     switch (type) {
@@ -234,6 +268,11 @@ int main(int argc, char* argv[])
     case cubehelix:
         ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
         break;
+    case morelanddiverging:
+        ColorMap::MorelandDiverging(n, &(colormap[0]),
+                color0[0], color0[1], color0[2],
+                color1[0], color1[1], color1[2]);
+        break;
     }
 
     for (int i = 0; i < n; i++) {
index a9e349b..4357ef0 100644 (file)
@@ -51,6 +51,22 @@ static float clamp(float x, float lo, float hi)
     return std::min(std::max(x, lo), hi);
 }
 
+static float hue_diff(float h0, float h1)
+{
+    float t = std::fabs(h1 - h0);
+    return (t < pi ? t : twopi - t);
+}
+
+static float uchar_to_float(unsigned char x)
+{
+    return x / 255.0f;
+}
+
+static unsigned char float_to_uchar(float x)
+{
+    return std::round(x * 255.0f);
+}
+
 /* XYZ and related color spaces helper functions and values */
 
 static float u_prime(float x, float y, float z)
@@ -128,6 +144,42 @@ static void xyz_to_luv(float x, float y, float z, float* l, float* u, float* v)
     *v = 13.0f * (*l) * (v_prime(x, y, z) - d65_v_prime);
 }
 
+/* Color space conversion: LAB <-> XYZ */
+
+static float lab_invf(float t)
+{
+    if (t > 6.0f / 29.0f)
+        return t * t * t;
+    else
+        return (3.0f * 6.0f * 6.0f) / (29.0f * 29.0f) * (t - 4.0f / 29.0f);
+}
+
+static void lab_to_xyz(float l, float a, float b, float* x, float* y, float* z)
+{
+    float t = (l + 16.0f) / 116.0f;
+    *x = d65_x * lab_invf(t + a / 500.0f);
+    *y = d65_y * lab_invf(t);
+    *z = d65_z * lab_invf(t - b / 200.0f);
+}
+
+static float lab_f(float t)
+{
+    if (t > (6.0f * 6.0f * 6.0f) / (29.0f * 29.0f * 29.0f))
+        return std::cbrt(t);
+    else
+        return (29.0f * 29.0f) / (3.0f * 6.0f * 6.0f) * t + 4.0f / 29.0f;
+}
+
+static void xyz_to_lab(float x, float y, float z, float* l, float* a, float* b)
+{
+    float fx = lab_f(x / d65_x);
+    float fy = lab_f(y / d65_y);
+    float fz = lab_f(z / d65_z);
+    *l = 116.0f * fy - 16.0f;
+    *a = 500.0f * (fx - fy);
+    *b = 200.0f * (fy - fz);
+}
+
 /* Color space conversion: RGB <-> XYZ */
 
 static void rgb_to_xyz(float r, float g, float b, float* x, float* y, float *z)
@@ -361,12 +413,12 @@ static void convert_colormap_entry(LUVColor color, unsigned char* srgb)
     xyz_to_rgb(x, y, z, &r, &g, &b);
     float sr, sg, sb;
     rgb_to_srgb(r, g, b, &sr, &sg, &sb);
-    srgb[0] = std::round(sr * 255.0f);
-    srgb[1] = std::round(sg * 255.0f);
-    srgb[2] = std::round(sb * 255.0f);
+    srgb[0] = float_to_uchar(sr);
+    srgb[1] = float_to_uchar(sg);
+    srgb[2] = float_to_uchar(sb);
 }
 
-/* Public functions: Brewer-like color maps */
+/* Brewer-like color maps */
 
 float BrewerSequentialDefaultContrastForSmallN(int n)
 {
@@ -444,12 +496,6 @@ void BrewerDiverging(int n, unsigned char* colormap, float hue, float divergence
     }
 }
 
-static float HueDiff(float h0, float h1)
-{
-    float t = std::fabs(h1 - h0);
-    return (t < pi ? t : twopi - t);
-}
-
 void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergence,
         float contrast, float saturation, float brightness)
 {
@@ -484,7 +530,7 @@ void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergen
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         float ch = std::fmod(twopi * (eps + t * r), twopi);
-        float alpha = HueDiff(ch, yh) / pi;
+        float alpha = hue_diff(ch, yh) / pi;
         float cl = (1.0f - alpha) * l0 + alpha * l1;
         float cs = std::min(Smax(cl, ch), saturation * rs);
         LUVColor c;
@@ -494,7 +540,7 @@ void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergen
     }
 }
 
-/* Public functions: CubeHelix */
+/* CubeHelix */
 
 int CubeHelix(int n, unsigned char* colormap, float hue,
         float rot, float saturation, float gamma)
@@ -534,11 +580,108 @@ int CubeHelix(int n, unsigned char* colormap, float hue,
         }
         if (clipped)
             clippings++;
-        colormap[3 * i + 0] = r * 255.0f;
-        colormap[3 * i + 1] = g * 255.0f;
-        colormap[3 * i + 2] = b * 255.0f;
+        colormap[3 * i + 0] = float_to_uchar(r);
+        colormap[3 * i + 1] = float_to_uchar(g);
+        colormap[3 * i + 2] = float_to_uchar(b);
     }
     return clippings;
 }
 
+/* MorelandDiverging */
+
+static void lab_to_msh(float l, float a, float b, float* m, float* s, float* h)
+{
+    *m = std::sqrt(l * l + a * a + b * b);
+    *s = (*m > 0.001f) ? std::acos(l / (*m)) : 0.0f;
+    *h = (*s > 0.001f) ? std::atan2(b, a) : 0.0f;
+}
+
+static void msh_to_lab(float m, float s, float h, float* l, float* a, float* b)
+{
+    *l = m * std::cos(s);
+    *a = m * std::sin(s) * std::cos(h);
+    *b = m * std::sin(s) * std::sin(h);
+}
+
+static void srgb_to_msh(unsigned char sr, unsigned char sg, unsigned char sb, float* m, float* s, float *h)
+{
+    float lr, lg, lb;
+    srgb_to_rgb(uchar_to_float(sr), uchar_to_float(sg), uchar_to_float(sb), &lr, &lg, &lb);
+    float x, y, z;
+    rgb_to_xyz(lr, lg, lb, &x, &y, &z);
+    float l, a, b;
+    xyz_to_lab(x, y, z, &l, &a, &b);
+    lab_to_msh(l, a, b, m, s, h);
+}
+
+static void msh_to_srgb(float m, float s, float h, unsigned char* sr, unsigned char* sg, unsigned char* sb)
+{
+    float l, a, b;
+    msh_to_lab(m, s, h, &l, &a, &b);
+    float x, y, z;
+    lab_to_xyz(l, a, b, &x, &y, &z);
+    float lr, lg, lb;
+    xyz_to_rgb(x, y, z, &lr, &lg, &lb);
+    float fsr, fsg, fsb;
+    rgb_to_srgb(lr, lg, lb, &fsr, &fsg, &fsb);
+    *sr = float_to_uchar(fsr);
+    *sg = float_to_uchar(fsg);
+    *sb = float_to_uchar(fsb);
+}
+
+static float adjust_hue(float m, float s, float h, float unsaturated_m)
+{
+    if (m >= unsaturated_m - 0.1f) {
+        return h;
+    } else {
+        float hue_spin = s * std::sqrt(unsaturated_m * unsaturated_m - m * m)
+            / (m * std::sin(s));
+        if (h > -pi / 3.0f)
+            return h + hue_spin;
+        else
+            return h - hue_spin;
+    }
+}
+
+void MorelandDiverging(int n, unsigned char* colormap,
+        unsigned char sr0, unsigned char sg0, unsigned char sb0,
+        unsigned char sr1, unsigned char sg1, unsigned char sb1)
+{
+    float om0, os0, oh0, om1, os1, oh1;
+    srgb_to_msh(sr0, sg0, sb0, &om0, &os0, &oh0);
+    srgb_to_msh(sr1, sg1, sb1, &om1, &os1, &oh1);
+    bool place_white = (os0 >= 0.05f && os1 >= 0.05f && hue_diff(oh0, oh1) > pi / 3.0f);
+    float mmid = std::max(std::max(om0, om1), 88.0f);
+
+    for (int i = 0; i < n; i++) {
+        float m0 = om0, s0 = os0, h0 = oh0;
+        float m1 = om1, s1 = os1, h1 = oh1;
+        float t = i / (n - 1.0f);
+        if (place_white) {
+            if (t < 0.5f) {
+                m1 = mmid;
+                s1 = 0.0f;
+                h1 = 0.0f;
+                t *= 2.0f;
+            } else {
+                m0 = mmid;
+                s0 = 0.0f;
+                h0 = 0.0f;
+                t = 2.0f * t - 1.0f;
+            }
+        }
+        if (s0 < 0.05f && s1 >= 0.05f) {
+            h0 = adjust_hue(m1, s1, h1, m0);
+        } else if (s0 >= 0.05f && s1 < 0.05f) {
+            h1 = adjust_hue(m0, s0, h0, m1);
+        }
+
+        float m = (1.0f - t) * m0 + t * m1;
+        float s = (1.0f - t) * s0 + t * s1;
+        float h = (1.0f - t) * h0 + t * h1;
+
+        msh_to_srgb(m, s, h, colormap + 3 * i + 0, colormap + 3 * i + 1, colormap + 3 * i + 2);
+    }
+}
+
 }
index c9be7fa..f6448aa 100644 (file)
 
 /* Generate color maps for scientific visualization purposes.
  *
- * This implements the methods described in
- * "Generating Color Palettes using Intuitive Parameters" by
- * Martijn Wijffelaars, Roel Vliegen, Jarke J. van Wijk and Erik-Jan van der
- * Linden, Eurographics/IEEE-VGTC Symposium on Visualization 2008
- *
  * Usage:
+ * - Decide which type of color map you need and how many colors the map should
+ *   contain.
+ * - Allocate memory for you color map (3 * unsigned char for each color entry).
+ * - Call the function that generates your color map.
  *
- * Decide which type of color map you need and how many colors the map should
- * contain:
- * - For a continuous range of values (e.g. temperature, size, ...):
- *   Sequential map with a single hue, at least 200 colors
- * - For a continuous of values around a neutral middle (e.g. deviation from an
- *   ideal value):
- *   Diverging map, composed of two sequential maps with a neutral color in the
- *   middle, at least 200 colors
- *
- * Allocate memory for your color map, and call the function that generates your
- * map. All colors are represented as unsigned char sRGB triplets, with each
- * value in [0,255].
+ * All colors are represented as unsigned char sRGB triplets, with each value in
+ * [0,255].
  */
 
 namespace ColorMap {
@@ -134,6 +123,30 @@ int CubeHelix(int n, unsigned char* colormap,
         float rotations = CubeHelixDefaultRotations,
         float saturation = CubeHelixDefaultSaturation,
         float gamma = CubeHelixDefaultGamma);
+
+/*
+ * MorelandDiverging color maps, as described in
+ * K. Moreland, Diverging Color Maps for Scientific Visualization, Proc. Int.
+ * Symp. Visual Computing, December 2009, DOI 10.1007/978-3-642-10520-3_9.
+ */
+
+// Create a MorelandDiverging colormap with n colors. Specify the two endpoints
+// of the colormap as sRGB colors; all intermediate colors will be generated.
+const unsigned char MorelandDivergingDefaultR0 = 59;
+const unsigned char MorelandDivergingDefaultG0 = 76;
+const unsigned char MorelandDivergingDefaultB0 = 192;
+const unsigned char MorelandDivergingDefaultR1 = 180;
+const unsigned char MorelandDivergingDefaultG1 = 4;
+const unsigned char MorelandDivergingDefaultB1 = 38;
+
+void MorelandDiverging(int n, unsigned char* colormap,
+        unsigned char sr0 = MorelandDivergingDefaultR0,
+        unsigned char sg0 = MorelandDivergingDefaultG0,
+        unsigned char sb0 = MorelandDivergingDefaultB0,
+        unsigned char sr1 = MorelandDivergingDefaultR1,
+        unsigned char sg1 = MorelandDivergingDefaultG1,
+        unsigned char sb1 = MorelandDivergingDefaultB1);
+
 }
 
 #endif
index 8778f0a..ee7a42e 100644 (file)
@@ -27,6 +27,8 @@
 #include <QLabel>
 #include <QSlider>
 #include <QDoubleSpinBox>
+#include <QPushButton>
+#include <QColorDialog>
 #include <QImage>
 #include <QtMath>
 
@@ -34,6 +36,8 @@
 #include "colormap.hpp"
 
 
+/* References */
+
 static QString brewerlike_reference = QString("Relevant paper: "
         "M. Wijffelaars, R. Vliegen, J. J. van Wijk, E-J. van der Linden, "
         "<a href=\"http://dx.doi.org/10.1111/j.1467-8659.2008.01203.x\">Generating Color Palettes using Intuitive Parameters</a>, "
@@ -44,6 +48,11 @@ static QString cubehelix_reference = QString("Relevant paper: "
         "<a href=\"http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/\">A colour scheme for the display of astronomical intensity</a>, "
         "Bulletin of the Astronomical Society of India 39(2), June 2011.");
 
+static QString moreland_reference = QString("Relevant paper: "
+        "K. Moreland, "
+        "<a href=\"http://www.kennethmoreland.com/color-maps/\">Diverging Color Maps for Scientific Visualization</a>, "
+        "Proc. Int. Symp. Visual Computing, December 2009."); // DOI 10.1007/978-3-642-10520-3_9.
+
 /* ColorMapCombinedSliderSpinBox */
 
 ColorMapCombinedSliderSpinBox::ColorMapCombinedSliderSpinBox(float minval, float maxval, float step) :
@@ -599,3 +608,122 @@ void ColorMapCubeHelixWidget::update()
     if (!_update_lock)
         emit colorMapChanged();
 }
+
+ColorMapMorelandDivergingWidget::ColorMapMorelandDivergingWidget() :
+    _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* color0_label = new QLabel("First color:");
+    layout->addWidget(color0_label, 2, 0);
+    _color0_button = new QPushButton;
+    layout->addWidget(_color0_button, 2, 1, 1, 3);
+
+    QLabel* color1_label = new QLabel("Last color:");
+    layout->addWidget(color1_label, 3, 0);
+    _color1_button = new QPushButton;
+    layout->addWidget(_color1_button, 3, 1, 1, 3);
+
+    layout->setColumnStretch(1, 1);
+    layout->addItem(new QSpacerItem(0, 0), 4, 0, 1, 4);
+    layout->setRowStretch(4, 1);
+    setLayout(layout);
+
+    connect(_n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
+    connect(_color0_button, SIGNAL(clicked(bool)), this, SLOT(chooseColor0()));
+    connect(_color1_button, SIGNAL(clicked(bool)), this, SLOT(chooseColor1()));
+    reset();
+}
+
+ColorMapMorelandDivergingWidget::~ColorMapMorelandDivergingWidget()
+{
+}
+
+static void setButtonColor(QPushButton* button, const QColor& color)
+{
+    QPixmap pixmap(button->iconSize());
+    pixmap.fill(color);
+    button->setIcon(QIcon(pixmap));
+}
+
+static QColor getButtonColor(QPushButton* button)
+{
+    return button->icon().pixmap(1, 1).toImage().pixel(0, 0);
+}
+
+void ColorMapMorelandDivergingWidget::chooseColor0()
+{
+    QColor c = QColorDialog::getColor(getButtonColor(_color0_button), this);
+    if (c.isValid()) {
+        setButtonColor(_color0_button, c);
+        update();
+    }
+}
+
+void ColorMapMorelandDivergingWidget::chooseColor1()
+{
+    QColor c = QColorDialog::getColor(getButtonColor(_color1_button), this);
+    if (c.isValid()) {
+        setButtonColor(_color1_button, c);
+        update();
+    }
+}
+
+void ColorMapMorelandDivergingWidget::reset()
+{
+    _update_lock = true;
+    _n_spinbox->setValue(256);
+    setButtonColor(_color0_button, QColor(
+                ColorMap::MorelandDivergingDefaultR0,
+                ColorMap::MorelandDivergingDefaultG0,
+                ColorMap::MorelandDivergingDefaultB0));
+    setButtonColor(_color1_button, QColor(
+                ColorMap::MorelandDivergingDefaultR1,
+                ColorMap::MorelandDivergingDefaultG1,
+                ColorMap::MorelandDivergingDefaultB1));
+    _update_lock = false;
+    update();
+}
+
+QVector<QColor> ColorMapMorelandDivergingWidget::colorMap() const
+{
+    int n;
+    unsigned char r0, g0, b0, r1, g1, b1;
+    parameters(n, r0, g0, b0, r1, g1, b1);
+    QVector<unsigned char> colormap(3 * n);
+    ColorMap::MorelandDiverging(n, colormap.data(), r0, g0, b0, r1, g1, b1);
+    return toQColor(colormap);
+}
+
+QString ColorMapMorelandDivergingWidget::reference() const
+{
+    return moreland_reference;
+}
+
+void ColorMapMorelandDivergingWidget::parameters(int& n,
+        unsigned char& r0, unsigned char& g0, unsigned char& b0,
+        unsigned char& r1, unsigned char& g1, unsigned char& b1) const
+{
+    n = _n_spinbox->value();
+    QColor c0 = getButtonColor(_color0_button);
+    r0 = c0.red();
+    g0 = c0.green();
+    b0 = c0.blue();
+    QColor c1 = getButtonColor(_color1_button);
+    r1 = c1.red();
+    g1 = c1.green();
+    b1 = c1.blue();
+}
+
+void ColorMapMorelandDivergingWidget::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
index b7fcbe4..4c71243 100644 (file)
@@ -30,7 +30,7 @@
 class QSpinBox;
 class QSlider;
 class QDoubleSpinBox;
-class QTabWidget;
+class QPushButton;
 
 
 // Internal helper class for a slider/spinbox combination
@@ -184,4 +184,29 @@ public:
             float& saturation, float& gamma) const;
 };
 
+class ColorMapMorelandDivergingWidget : public ColorMapWidget
+{
+Q_OBJECT
+private:
+    bool _update_lock;
+    QSpinBox* _n_spinbox;
+    QPushButton* _color0_button;
+    QPushButton* _color1_button;
+private slots:
+    void chooseColor0();
+    void chooseColor1();
+    void update();
+
+public:
+    ColorMapMorelandDivergingWidget();
+    ~ColorMapMorelandDivergingWidget();
+
+    void reset() override;
+    QVector<QColor> colorMap() const override;
+    QString reference() const override;
+    void parameters(int& n,
+            unsigned char& r0, unsigned char& g0, unsigned char& b0,
+            unsigned char& r1, unsigned char& g1, unsigned char& b1) const;
+};
+
 #endif
diff --git a/gui.cpp b/gui.cpp
index a5b783a..1571cf4 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -47,20 +47,23 @@ GUI::GUI()
     _brewerdiv_widget = new ColorMapBrewerDivergingWidget;
     _brewerqual_widget = new ColorMapBrewerQualitativeWidget;
     _cubehelix_widget = new ColorMapCubeHelixWidget;
+    _morelanddiv_widget = new ColorMapMorelandDivergingWidget;
     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()));
+    connect(_morelanddiv_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");
+    _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");
+    _tab_widget->addTab(_morelanddiv_widget, "Moreland Diverging");
     connect(_tab_widget, SIGNAL(currentChanged(int)), this, SLOT(update()));
     layout->addWidget(_tab_widget, 0, 0);
     layout->addItem(new QSpacerItem(0, 0), 1, 0);
diff --git a/gui.hpp b/gui.hpp
index 056b208..59bb5d9 100644 (file)
--- a/gui.hpp
+++ b/gui.hpp
@@ -30,6 +30,7 @@ class ColorMapBrewerSequentialWidget;
 class ColorMapBrewerDivergingWidget;
 class ColorMapBrewerQualitativeWidget;
 class ColorMapCubeHelixWidget;
+class ColorMapMorelandDivergingWidget;
 class QTabWidget;
 class QLabel;
 
@@ -47,6 +48,7 @@ private:
     ColorMapBrewerDivergingWidget* _brewerdiv_widget;
     ColorMapBrewerQualitativeWidget* _brewerqual_widget;
     ColorMapCubeHelixWidget* _cubehelix_widget;
+    ColorMapMorelandDivergingWidget* _morelanddiv_widget;
     QTabWidget* _tab_widget;
     QLabel* _reference_label;
     QLabel* _colormap_label;