Reorganize to make the widgets reusable.
authorMartin Lambers <marlam@marlam.de>
Thu, 4 Feb 2016 10:24:37 +0000 (11:24 +0100)
committerMartin Lambers <marlam@marlam.de>
Thu, 4 Feb 2016 10:24:37 +0000 (11:24 +0100)
CMakeLists.txt
colormapwidgets.cpp [new file with mode: 0644]
colormapwidgets.hpp [new file with mode: 0644]
gui.cpp
gui.hpp

index 0681a0d..b81d710 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2015
+# Copyright (C) 2015, 2016
 # Computer Graphics Group, University of Siegen
 # Written by Martin Lambers <martin.lambers@uni-siegen.de>
 #
@@ -21,7 +21,9 @@ install(TARGETS gencolormap RUNTIME DESTINATION bin)
 
 if(Qt5Widgets_FOUND)
         qt5_add_resources(GUI_RESOURCES gui.qrc)
-       add_executable(gencolormap-gui gui.cpp colormap.hpp colormap.cpp ${GUI_RESOURCES})
+       add_executable(gencolormap-gui gui.cpp
+               colormapwidgets.hpp colormapwidgets.cpp
+               colormap.hpp colormap.cpp ${GUI_RESOURCES})
        target_link_libraries(gencolormap-gui Qt5::Widgets)
        install(TARGETS gencolormap-gui RUNTIME DESTINATION bin)
 endif()
diff --git a/colormapwidgets.cpp b/colormapwidgets.cpp
new file mode 100644 (file)
index 0000000..8778f0a
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * 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
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "gui.hpp"
+
+#include <QGridLayout>
+#include <QLabel>
+#include <QSlider>
+#include <QDoubleSpinBox>
+#include <QImage>
+#include <QtMath>
+
+#include "colormapwidgets.hpp"
+#include "colormap.hpp"
+
+
+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>, "
+        "Computer Graphics Forum 27(3), May 2008.");
+
+static QString cubehelix_reference = QString("Relevant paper: "
+        "D. A. Green, "
+        "<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.");
+
+/* ColorMapCombinedSliderSpinBox */
+
+ColorMapCombinedSliderSpinBox::ColorMapCombinedSliderSpinBox(float minval, float maxval, float step) :
+    _update_lock(false),
+    minval(minval), maxval(maxval), step(step)
+{
+    slider = new QSlider(Qt::Horizontal);
+    slider->setMinimum(0);
+    slider->setMaximum((maxval - minval) / step);
+    slider->setSingleStep(step);
+
+    spinbox = new QDoubleSpinBox();
+    spinbox->setRange(minval, maxval);
+    spinbox->setSingleStep(step);
+    spinbox->setDecimals(std::log10(1.0f / step));
+
+    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged()));
+    connect(spinbox, SIGNAL(valueChanged(double)), this, SLOT(spinboxChanged()));
+}
+
+float ColorMapCombinedSliderSpinBox::value() const
+{
+    return spinbox->value();
+}
+
+void ColorMapCombinedSliderSpinBox::setValue(float v)
+{
+    _update_lock = true;
+    spinbox->setValue(v);
+    slider->setValue((v - minval) / step);
+    _update_lock = false;
+}
+
+void ColorMapCombinedSliderSpinBox::sliderChanged()
+{
+    if (!_update_lock) {
+        _update_lock = true;
+        int i = slider->value();
+        float v = i * step + minval;
+        spinbox->setValue(v);
+        _update_lock = false;
+        emit valueChanged(value());
+    }
+}
+
+void ColorMapCombinedSliderSpinBox::spinboxChanged()
+{
+    if (!_update_lock) {
+        _update_lock = true;
+        float v = spinbox->value();
+        int i = (v - minval) / step;
+        slider->setValue(i);
+        _update_lock = false;
+        emit valueChanged(value());
+    }
+}
+
+/* ColorMapWidget */
+
+ColorMapWidget::ColorMapWidget() : QWidget()
+{
+}
+
+ColorMapWidget::~ColorMapWidget()
+{
+}
+
+QImage ColorMapWidget::colorMapImage(int width, int height)
+{
+    QVector<QColor> colormap = colorMap();
+    if (width <= 0)
+        width = colormap.size();
+    if (height <= 0)
+        height = colormap.size();
+    QImage img(width, height, QImage::Format_RGB32);
+    bool y_direction = (height > width);
+    if (y_direction) {
+        for (int y = 0; y < height; y++) {
+            float entry_height = height / static_cast<float>(colormap.size());
+            int i = y / entry_height;
+            QRgb rgb = colormap[i].rgb();
+            QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(height - 1 - y));
+            for (int x = 0; x < width; x++)
+                scanline[x] = rgb;
+        }
+    } else {
+        for (int y = 0; y < height; y++) {
+            QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(y));
+            for (int x = 0; x < width; x++) {
+                float entry_width = width / static_cast<float>(colormap.size());
+                int i = x / entry_width;
+                scanline[x] = colormap[i].rgb();
+            }
+        }
+    }
+    return img;
+}
+
+/* Helper functions for ColorMap*Widget */
+
+static QVector<QColor> toQColor(const QVector<unsigned char>& cm)
+{
+    QVector<QColor> colormap(cm.size() / 3);
+    for (int i = 0; i < colormap.size(); i++)
+        colormap[i] = QColor(cm[3 * i + 0], cm[3 * i + 1], cm[3 * i + 2]);
+    return colormap;
+}
+
+static void hideWidgetButPreserveSize(QWidget* widget)
+{
+    QSizePolicy sp = widget->sizePolicy();
+    sp.setRetainSizeWhenHidden(true);
+    widget->setSizePolicy(sp);
+    widget->hide();
+}
+
+/* ColorMapBrewerSequentialWidget */
+
+ColorMapBrewerSequentialWidget::ColorMapBrewerSequentialWidget() :
+    _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);
+    ColorMapCombinedSliderSpinBox* divergence_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
+    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->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();
+}
+
+QVector<QColor> ColorMapBrewerSequentialWidget::colorMap() const
+{
+    int n;
+    float h, c, s, b, w;
+    parameters(n, h, c, s, b, w);
+    QVector<unsigned char> colormap(3 * n);
+    ColorMap::BrewerSequential(n, colormap.data(), h, c, s, b, w);
+    return toQColor(colormap);
+}
+
+QString ColorMapBrewerSequentialWidget::reference() const
+{
+    return brewerlike_reference;
+}
+
+void ColorMapBrewerSequentialWidget::parameters(int& n, float& hue,
+        float& contrast, float& saturation, float& brightness, float& warmth) const
+{
+    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::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
+
+ColorMapBrewerDivergingWidget::ColorMapBrewerDivergingWidget() :
+    _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);
+    _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();
+}
+
+QVector<QColor> ColorMapBrewerDivergingWidget::colorMap() const
+{
+    int n;
+    float h, d, c, s, b, w;
+    parameters(n, h, d, c, s, b, w);
+    QVector<unsigned char> colormap(3 * n);
+    ColorMap::BrewerDiverging(n, colormap.data(), h, d, c, s, b, w);
+    return toQColor(colormap);
+}
+
+QString ColorMapBrewerDivergingWidget::reference() const
+{
+    return brewerlike_reference;
+}
+
+void ColorMapBrewerDivergingWidget::parameters(int& n, float& hue, float& divergence,
+        float& contrast, float& saturation, float& brightness, float& warmth) const
+{
+    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::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);
+    hideWidgetButPreserveSize(warmth_label);
+    hideWidgetButPreserveSize(warmth_changer->slider);
+    hideWidgetButPreserveSize(warmth_changer->spinbox);
+
+    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(_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();
+}
+
+QVector<QColor> ColorMapBrewerQualitativeWidget::colorMap() const
+{
+    int n;
+    float h, d, c, s, b;
+    parameters(n, h, d, c, s, b);
+    QVector<unsigned char> colormap(3 * n);
+    ColorMap::BrewerQualitative(n, colormap.data(), h, d, c, s, b);
+    return toQColor(colormap);
+}
+
+QString ColorMapBrewerQualitativeWidget::reference() const
+{
+    return brewerlike_reference;
+}
+
+void ColorMapBrewerQualitativeWidget::parameters(int& n, float& hue, float& divergence,
+        float& contrast, float& saturation, float& brightness) const
+{
+    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::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, 180, 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();
+}
+
+QVector<QColor> ColorMapCubeHelixWidget::colorMap() const
+{
+    int n;
+    float h, r, s, g;
+    parameters(n, h, r, s, g);
+    QVector<unsigned char> colormap(3 * n);
+    ColorMap::CubeHelix(n, colormap.data(), h, r, s, g);
+    return toQColor(colormap);
+}
+
+QString ColorMapCubeHelixWidget::reference() const
+{
+    return cubehelix_reference;
+}
+
+void ColorMapCubeHelixWidget::parameters(int& n, float& hue,
+        float& rotations, float& saturation, float& gamma) const
+{
+    n = _n_spinbox->value();
+    hue = qDegreesToRadians(_hue_changer->value());
+    rotations = _rotations_changer->value();
+    saturation = _saturation_changer->value();
+    gamma = _gamma_changer->value();
+}
+
+void ColorMapCubeHelixWidget::update()
+{
+    if (!_update_lock)
+        emit colorMapChanged();
+}
diff --git a/colormapwidgets.hpp b/colormapwidgets.hpp
new file mode 100644 (file)
index 0000000..b7fcbe4
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef COLORMAPWIDGETS_HPP
+#define COLORMAPWIDGETS_HPP
+
+#include <QVector>
+#include <QWidget>
+
+class QSpinBox;
+class QSlider;
+class QDoubleSpinBox;
+class QTabWidget;
+
+
+// Internal helper class for a slider/spinbox combination
+class ColorMapCombinedSliderSpinBox : public QObject
+{
+Q_OBJECT
+
+private:
+    bool _update_lock;
+
+public:
+    float minval, maxval, step;
+    QSlider* slider;
+    QDoubleSpinBox* spinbox;
+
+    ColorMapCombinedSliderSpinBox(float minval, float maxval, float step);
+    float value() const;
+    void setValue(float v);
+
+private slots:
+    void sliderChanged();
+    void spinboxChanged();
+
+signals:
+    void valueChanged(float);
+};
+
+/* The ColorMapWidget interface, implemented by all color map method widgets */
+class ColorMapWidget : public QWidget
+{
+Q_OBJECT
+
+public:
+    ColorMapWidget();
+    ~ColorMapWidget();
+
+    /* Reset all values to their method-specific defaults */
+    virtual void reset() = 0;
+
+    /* Get the color map corresponding to the current values as a vector of colors */
+    virtual QVector<QColor> colorMap() const = 0;
+
+    /* Get a color map as an image of the specified size. If width or height is zero,
+     * then it will be set to the number of colors in the color map. */
+    QImage colorMapImage(int width, int height);
+
+    /* Get a rich text string containing the relevant literature reference for this method */
+    virtual QString reference() const = 0;
+
+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();
+
+public:
+    ColorMapBrewerSequentialWidget();
+    ~ColorMapBrewerSequentialWidget();
+
+    void reset() override;
+    QVector<QColor> colorMap() const override;
+    QString reference() const override;
+    void parameters(int& n, float& hue,
+            float& contrast, float& saturation, float& brightness, float& warmth) const;
+};
+
+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();
+
+public:
+    ColorMapBrewerDivergingWidget();
+    ~ColorMapBrewerDivergingWidget();
+
+    void reset() override;
+    QVector<QColor> colorMap() const override;
+    QString reference() const override;
+    void parameters(int& n, float& hue, float& divergence,
+            float& contrast, float& saturation, float& brightness, float& warmth) const;
+};
+
+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();
+
+public:
+    ColorMapBrewerQualitativeWidget();
+    ~ColorMapBrewerQualitativeWidget();
+
+    void reset() override;
+    QVector<QColor> colorMap() const override;
+    QString reference() const override;
+    void parameters(int& n, float& hue, float& divergence,
+            float& contrast, float& saturation, float& brightness) const;
+};
+
+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();
+
+public:
+    ColorMapCubeHelixWidget();
+    ~ColorMapCubeHelixWidget();
+
+    void reset() override;
+    QVector<QColor> colorMap() const override;
+    QString reference() const override;
+    void parameters(int& n, float& hue, float& rotations,
+            float& saturation, float& gamma) const;
+};
+
+#endif
diff --git a/gui.cpp b/gui.cpp
index 5acdd03..a5b783a 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
 #include <QApplication>
 #include <QGuiApplication>
 #include <QGridLayout>
+#include <QTabWidget>
 #include <QLabel>
-#include <QRadioButton>
-#include <QSlider>
-#include <QDoubleSpinBox>
 #include <QMenu>
 #include <QMenuBar>
-#include <QImage>
-#include <QPixmap>
 #include <QFileDialog>
 #include <QClipboard>
 #include <QTextStream>
 #include <QMessageBox>
-#include <QtMath>
 
-#include "colormap.hpp"
-
-
-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>, "
-        "Computer Graphics Forum 27(3), May 2008.");
-
-static QString cubehelix_reference = QString("Relevant paper: "
-        "D. A. Green, "
-        "<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.");
-
-
-ColorMapCombinedSliderSpinBox::ColorMapCombinedSliderSpinBox(float minval, float maxval, float step) :
-    _update_lock(false),
-    minval(minval), maxval(maxval), step(step)
-{
-    slider = new QSlider(Qt::Horizontal);
-    slider->setMinimum(0);
-    slider->setMaximum((maxval - minval) / step);
-    slider->setSingleStep(step);
-
-    spinbox = new QDoubleSpinBox();
-    spinbox->setRange(minval, maxval);
-    spinbox->setSingleStep(step);
-    spinbox->setDecimals(std::log10(1.0f / step));
-
-    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged()));
-    connect(spinbox, SIGNAL(valueChanged(double)), this, SLOT(spinboxChanged()));
-}
-
-float ColorMapCombinedSliderSpinBox::value() const
-{
-    return spinbox->value();
-}
-
-void ColorMapCombinedSliderSpinBox::setValue(float v)
-{
-    _update_lock = true;
-    spinbox->setValue(v);
-    slider->setValue((v - minval) / step);
-    _update_lock = false;
-}
-
-void ColorMapCombinedSliderSpinBox::sliderChanged()
-{
-    if (!_update_lock) {
-        _update_lock = true;
-        int i = slider->value();
-        float v = i * step + minval;
-        spinbox->setValue(v);
-        _update_lock = false;
-        emit valueChanged(value());
-    }
-}
-
-void ColorMapCombinedSliderSpinBox::spinboxChanged()
-{
-    if (!_update_lock) {
-        _update_lock = true;
-        float v = spinbox->value();
-        int i = (v - minval) / step;
-        slider->setValue(i);
-        _update_lock = false;
-        emit valueChanged(value());
-    }
-}
-
-static void hideWidgetButPreserveSize(QWidget* widget)
-{
-    QSizePolicy sp = widget->sizePolicy();
-    sp.setRetainSizeWhenHidden(true);
-    widget->setSizePolicy(sp);
-    widget->hide();
-}
-
-ColorMapBrewerSequentialWidget::ColorMapBrewerSequentialWidget() :
-    _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);
-    ColorMapCombinedSliderSpinBox* divergence_changer = new ColorMapCombinedSliderSpinBox(0, 360, 1);
-    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);
-    QLabel* reference_label = new QLabel(brewerlike_reference);
-    reference_label->setWordWrap(true);
-    reference_label->setOpenExternalLinks(true);
-    layout->addWidget(reference_label, 9, 0, 1, 4);
-
-    layout->setColumnStretch(1, 1);
-    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;
-
-    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);
-    _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);
-    QLabel* reference_label = new QLabel(brewerlike_reference);
-    reference_label->setWordWrap(true);
-    reference_label->setOpenExternalLinks(true);
-    layout->addWidget(reference_label, 9, 0, 1, 4);
-
-    layout->setColumnStretch(1, 1);
-    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);
-    hideWidgetButPreserveSize(warmth_label);
-    hideWidgetButPreserveSize(warmth_changer->slider);
-    hideWidgetButPreserveSize(warmth_changer->spinbox);
-
-    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);
-    QLabel* reference_label = new QLabel(brewerlike_reference);
-    reference_label->setWordWrap(true);
-    reference_label->setOpenExternalLinks(true);
-    layout->addWidget(reference_label, 9, 0, 1, 4);
-
-    layout->setColumnStretch(1, 1);
-    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, 180, 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->addItem(new QSpacerItem(0, 0), 6, 0, 1, 4);
-    QLabel* reference_label = new QLabel(cubehelix_reference);
-    reference_label->setWordWrap(true);
-    reference_label->setOpenExternalLinks(true);
-    layout->addWidget(reference_label, 7, 0, 1, 4);
-
-    layout->setColumnStretch(1, 1);
-    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();
-}
+#include "colormapwidgets.hpp"
 
 
 GUI::GUI()
@@ -577,12 +63,18 @@ GUI::GUI()
     _tab_widget->addTab(_cubehelix_widget,  "CubeHelix");
     connect(_tab_widget, SIGNAL(currentChanged(int)), this, SLOT(update()));
     layout->addWidget(_tab_widget, 0, 0);
+    layout->addItem(new QSpacerItem(0, 0), 1, 0);
+    _reference_label = new QLabel(_brewerseq_widget->reference());
+    _reference_label->setWordWrap(true);
+    _reference_label->setOpenExternalLinks(true);
+    layout->addWidget(_reference_label, 2, 0);
 
     _colormap_label = new QLabel();
     _colormap_label->setScaledContents(true);
-    layout->addWidget(_colormap_label, 0, 1);
+    layout->addWidget(_colormap_label, 0, 1, 3, 1);
 
     layout->setColumnStretch(0, 1);
+    layout->setRowStretch(1, 1);
     widget->setLayout(layout);
     setCentralWidget(widget);
 
@@ -625,20 +117,8 @@ GUI::~GUI()
 void GUI::update()
 {
     ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
-    const QVector<unsigned char>& colormap = *currentWidget->colorMap();
-
-    int img_width = 32;
-    int img_height = _colormap_label->height();
-    QImage img(img_width, img_height, QImage::Format_RGB32);
-    for (int y = 0; y < img_height; y++) {
-        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(img_height - 1 - y));
-        for (int x = 0; x < img_width; x++)
-            scanline[x] = rgb;
-    }
-    _colormap_label->setPixmap(QPixmap::fromImage(img));
+    _reference_label->setText(currentWidget->reference());
+    _colormap_label->setPixmap(QPixmap::fromImage(currentWidget->colorMapImage(32, _colormap_label->height())));
 }
 
 void GUI::file_export_png()
@@ -647,13 +127,7 @@ void GUI::file_export_png()
     if (!name.isEmpty()) {
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
         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));
-        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");
+        currentWidget->colorMapImage(0, 1).save(name, "png");
         QApplication::restoreOverrideCursor();
     }
 }
@@ -666,12 +140,12 @@ void GUI::file_export_csv()
         QFile file(name);
         if (file.open(QIODevice::ReadWrite)) {
             ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
-            const QVector<unsigned char>& colormap = *currentWidget->colorMap();
+            QVector<QColor> colormap = currentWidget->colorMap();
             QTextStream stream(&file);
-            for (int i = 0; i < colormap.size() / 3; i++) {
-                stream << colormap[3 * i + 0] << ", "
-                       << colormap[3 * i + 1] << ", "
-                       << colormap[3 * i + 2] << endl;
+            for (int i = 0; i < colormap.size(); i++) {
+                stream << colormap[i].red()   << ", "
+                       << colormap[i].green() << ", "
+                       << colormap[i].blue()  << endl;
             }
         }
         QApplication::restoreOverrideCursor();
@@ -687,25 +161,19 @@ void GUI::edit_reset()
 void GUI::edit_copy_as_img()
 {
     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));
-    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);
+    QApplication::clipboard()->setImage(currentWidget->colorMapImage(0, 1));
 }
 
 void GUI::edit_copy_as_txt()
 {
     ColorMapWidget* currentWidget = reinterpret_cast<ColorMapWidget*>(_tab_widget->currentWidget());
-    const QVector<unsigned char>& colormap = *currentWidget->colorMap();
+    QVector<QColor> colormap = currentWidget->colorMap();
     QString string;
     QTextStream stream(&string);
     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[i].red()   << ", "
+               << colormap[i].green() << ", "
+               << colormap[i].blue()  << endl;
     }
     QApplication::clipboard()->setText(string);
 }
@@ -721,11 +189,6 @@ void GUI::help_about()
                 "   This is free software under the terms of the "
                     "<a href=\"https://www.debian.org/legal/licenses/mit\">MIT/Expat License</a>. "
                 "   There is NO WARRANTY, to the extent permitted by law."
-                "</p>"
-                "<p>This program implements the color map generation techniques described in<br>"
-                "   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."
                 "</p>");
 }
 
diff --git a/gui.hpp b/gui.hpp
index 633c399..056b208 100644 (file)
--- a/gui.hpp
+++ b/gui.hpp
 #ifndef GUI_HPP
 #define GUI_HPP
 
-#include <QVector>
 #include <QMainWindow>
 
+class ColorMapBrewerSequentialWidget;
+class ColorMapBrewerDivergingWidget;
+class ColorMapBrewerQualitativeWidget;
+class ColorMapCubeHelixWidget;
 class QTabWidget;
 class QLabel;
-class QRadioButton;
-class QSpinBox;
-class QSlider;
-class QDoubleSpinBox;
-class QTabWidget;
-
-
-class ColorMapCombinedSliderSpinBox : public QObject
-{
-Q_OBJECT
-private:
-    bool _update_lock;
-
-public:
-    float minval, maxval, step;
-    QSlider* slider;
-    QDoubleSpinBox* spinbox;
-
-    ColorMapCombinedSliderSpinBox(float minval, float maxval, float step);
-    float value() const;
-    void setValue(float v);
-
-private slots:
-    void sliderChanged();
-    void spinboxChanged();
-
-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
 {
@@ -200,6 +48,7 @@ private:
     ColorMapBrewerQualitativeWidget* _brewerqual_widget;
     ColorMapCubeHelixWidget* _cubehelix_widget;
     QTabWidget* _tab_widget;
+    QLabel* _reference_label;
     QLabel* _colormap_label;
 
 private slots: