Change black body color map to use linear lightness and saturation.
authorMartin Lambers <marlam@marlam.de>
Thu, 11 Feb 2016 12:38:10 +0000 (13:38 +0100)
committerMartin Lambers <marlam@marlam.de>
Thu, 11 Feb 2016 12:38:10 +0000 (13:38 +0100)
Only the hue is now taken from the black body color. This makes the map
actually useful.

cmdline.cpp
colormap.cpp
colormap.hpp
colormapwidgets.cpp
colormapwidgets.hpp
gui.cpp

index 1eaf0ed..76cee1b 100644 (file)
@@ -203,11 +203,12 @@ int main(int argc, char* argv[])
                 "    -t|--type=uniformrainbow          Generate a uniform rainbow color map\n"
                 "    [-h|--hue=H]                      Set start hue in [0,360] degrees\n"
                 "    [-r|--rotations=R]                Set number of rotations, in (-infty,infty)\n"
-                "    [-s|--saturation=S]               Set saturation, in [0,1]\n"
+                "    [-s|--saturation=S]               Set saturation, in [0,5]\n"
                 "  Black Body color maps:\n"
                 "    -t|--type=blackbody               Generate a Black Body color map\n"
                 "    [-T|--temperature=T]              Start temperature of the map in Kelvin\n"
                 "    [-R|--range=R]                    Range of temperatures of the map in Kelvin\n"
+                "    [-s|--saturation=S]               Set saturation, in [0,5]\n"
                 "  CubeHelix color maps:\n"
                 "    -t|--type=cubehelix               Generate a CubeHelix color map\n"
                 "    [-h|--hue=H]                      Set start hue in [0,180] degrees\n"
@@ -285,6 +286,8 @@ int main(int argc, char* argv[])
             saturation = ColorMap::IsoluminantQualitativeDefaultSaturation;
         else if (type == unirainbow)
             saturation = ColorMap::UniformRainbowDefaultSaturation;
+        else if (type == blackbody)
+            saturation = ColorMap::BlackBodyDefaultSaturation;
         else if (type == cubehelix)
             saturation = ColorMap::CubeHelixDefaultSaturation;
     }
@@ -371,7 +374,7 @@ int main(int argc, char* argv[])
         ColorMap::UniformRainbow(n, &(colormap[0]), hue, rotations, saturation);
         break;
     case blackbody:
-        ColorMap::BlackBody(n, &(colormap[0]), temperature, range);
+        ColorMap::BlackBody(n, &(colormap[0]), temperature, range, saturation);
         break;
     case cubehelix:
         ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
index ecb89e7..06c8856 100644 (file)
@@ -743,7 +743,7 @@ static triplet color_matching_function(int lambda /* in nanometers */)
     return xyz;
 }
 
-void BlackBody(int n, unsigned char* colormap, float temperature, float range)
+void BlackBody(int n, unsigned char* colormap, float temperature, float range, float saturation)
 {
     for (int i = 0; i < n; i++) {
         float fract = i / (n - 1.0f);
@@ -760,9 +760,10 @@ void BlackBody(int n, unsigned char* colormap, float temperature, float range)
             //xyz = xyz + s * radiosity * color_matching_function_approx(l);
             xyz = xyz + s * radiosity * color_matching_function(lambda);
         }
-        triplet luv = xyz_to_luv(adjust_y(xyz, 10.0f));
-        luv.l = 0.2f + fract * 99.8f;
-        luv_to_colormap(luv, colormap + 3 * i);
+        triplet lch = luv_to_lch(xyz_to_luv(adjust_y(xyz, 10.0f)));
+        lch.l = fract * 100.0f;
+        lch.c = lch_chroma(lch.l, (1.0f - fract) * saturation);
+        lch_to_colormap(lch, colormap + 3 * i);
     }
 }
 
index 081a38e..cd67cdb 100644 (file)
@@ -159,23 +159,25 @@ void UniformRainbow(int n, unsigned char* colormap,
         float saturation = UniformRainbowDefaultSaturation);
 
 /*
- * Black Body color maps, based on the chromaticity (hue and saturation) of a black body
- * at increading temperatures. The luminance is linearly increasing.
+ * Black Body color maps, based on the hue of a black body at increading
+ * temperatures, but with linearly increasing lightness and chromaticity.
  *
  * Parameters are the temperature at the lower end of the map, and the range of
  * temperatures in the map. (The temperature at the higher end of the map is the
- * sum of the two).
+ * sum of the two). Furthermore, the saturation can be adjusted, but high
+ * saturations will lead to color clipping.
  */
 
-// The defaults are chosen so that we start at a red that is representable in
-// sRGB and arrive at a chromaticity that is very close to the D65 white point
-// (6500 K) so that we get a good white at the end of the color map.
-const float BlackBodyDefaultTemperature = 500.0f;
-const float BlackBodyDefaultRange = 6000.0;
+// The defaults are chosen so that we start at red and arrive the D65 white point
+// (6500 K), thus excluding the blue colors that occur at higher temperatures.
+const float BlackBodyDefaultTemperature = 250.0f;
+const float BlackBodyDefaultRange = 6250.0f;
+const float BlackBodyDefaultSaturation = 2.5f;
 
 void BlackBody(int n, unsigned char* colormap,
         float temperature = BlackBodyDefaultTemperature,
-        float range = BlackBodyDefaultRange);
+        float range = BlackBodyDefaultRange,
+        float saturation = BlackBodyDefaultSaturation);
 
 /*
  * CubeHelix color maps, as described in
index f5f36f8..e260033 100644 (file)
@@ -922,26 +922,33 @@ ColorMapBlackBodyWidget::ColorMapBlackBodyWidget() :
     _n_spinbox->setSingleStep(1);
     layout->addWidget(_n_spinbox, 1, 1, 1, 3);
 
-    QLabel* temperature_label = new QLabel("Temperature:");
+    QLabel* temperature_label = new QLabel("Temperature (K):");
     layout->addWidget(temperature_label, 2, 0);
-    _temperature_changer = new ColorMapCombinedSliderSpinBox(250.0f, 15000.0f, 100.0f);
+    _temperature_changer = new ColorMapCombinedSliderSpinBox(250.0f, 20000.0f, 100.0f);
     layout->addWidget(_temperature_changer->slider, 2, 1, 1, 2);
     layout->addWidget(_temperature_changer->spinbox, 2, 3);
 
-    QLabel* range_label = new QLabel("Range:");
+    QLabel* range_label = new QLabel("Range (K):");
     layout->addWidget(range_label, 3, 0);
-    _range_changer = new ColorMapCombinedSliderSpinBox(1000.0f, 15000.0f, 100.0f);
+    _range_changer = new ColorMapCombinedSliderSpinBox(0.0f, 20000.0f, 100.0f);
     layout->addWidget(_range_changer->slider, 3, 1, 1, 2);
     layout->addWidget(_range_changer->spinbox, 3, 3);
 
+    QLabel* saturation_label = new QLabel("Saturation:");
+    layout->addWidget(saturation_label, 4, 0);
+    _saturation_changer = new ColorMapCombinedSliderSpinBox(0.0f, 5.0f, 0.1f);
+    layout->addWidget(_saturation_changer->slider, 4, 1, 1, 2);
+    layout->addWidget(_saturation_changer->spinbox, 4, 3);
+
     layout->setColumnStretch(1, 1);
-    layout->addItem(new QSpacerItem(0, 0), 4, 0, 1, 4);
-    layout->setRowStretch(4, 1);
+    layout->addItem(new QSpacerItem(0, 0), 5, 0, 1, 4);
+    layout->setRowStretch(5, 1);
     setLayout(layout);
 
     connect(_n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(update()));
     connect(_temperature_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
     connect(_range_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
+    connect(_saturation_changer, SIGNAL(valueChanged(float)), this, SLOT(update()));
     reset();
 }
 
@@ -955,6 +962,7 @@ void ColorMapBlackBodyWidget::reset()
     _n_spinbox->setValue(256);
     _temperature_changer->setValue(ColorMap::BlackBodyDefaultTemperature);
     _range_changer->setValue(ColorMap::BlackBodyDefaultRange);
+    _saturation_changer->setValue(ColorMap::BlackBodyDefaultSaturation);
     _update_lock = false;
     update();
 }
@@ -962,10 +970,10 @@ void ColorMapBlackBodyWidget::reset()
 QVector<QColor> ColorMapBlackBodyWidget::colorMap() const
 {
     int n;
-    float t, r;
-    parameters(n, t, r);
+    float t, r, s;
+    parameters(n, t, r, s);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::BlackBody(n, colormap.data(), t, r);
+    ColorMap::BlackBody(n, colormap.data(), t, r, s);
     return toQColor(colormap);
 }
 
@@ -974,11 +982,12 @@ QString ColorMapBlackBodyWidget::reference() const
     return blackbody_reference;
 }
 
-void ColorMapBlackBodyWidget::parameters(int& n, float& temperature, float& range) const
+void ColorMapBlackBodyWidget::parameters(int& n, float& temperature, float& range, float& saturation) const
 {
     n = _n_spinbox->value();
     temperature = _temperature_changer->value();
     range = _range_changer->value();
+    saturation = _saturation_changer->value();
 }
 
 void ColorMapBlackBodyWidget::update()
index c7a8490..aa52422 100644 (file)
@@ -257,6 +257,7 @@ private:
     QSpinBox* _n_spinbox;
     ColorMapCombinedSliderSpinBox* _temperature_changer;
     ColorMapCombinedSliderSpinBox* _range_changer;
+    ColorMapCombinedSliderSpinBox* _saturation_changer;
 private slots:
     void update();
 
@@ -267,7 +268,7 @@ public:
     void reset() override;
     QVector<QColor> colorMap() const override;
     QString reference() const override;
-    void parameters(int& n, float& temperature, float& range) const;
+    void parameters(int& n, float& temperature, float& range, float& saturation) const;
 };
 
 class ColorMapCubeHelixWidget : public ColorMapWidget
diff --git a/gui.cpp b/gui.cpp
index a4b2f59..74065bd 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -74,7 +74,7 @@ GUI::GUI()
     _category_seq_widget = new QTabWidget();
     _category_seq_widget->addTab(_brewerseq_widget, "Brewer-like");
     _category_seq_widget->addTab(_isolumseq_widget, "Isoluminant");
-    //_category_seq_widget->addTab(_blackbody_widget, "Black Body");
+    _category_seq_widget->addTab(_blackbody_widget, "Black Body");
     _category_seq_widget->addTab(_unirainbow_widget, "Uniform Rainbow");
     _category_seq_widget->addTab(_cubehelix_widget, "CubeHelix");
     //_category_seq_widget->addTab(_mcnames_widget, "McNames");