diff layer/ColourMapper.cpp @ 1362:d79e21855aef

Add mechanism for saving/loading colour maps by name/id rather than by numerical index, for future compatibility when adding to or changing the supported colour maps. Add two new colour maps (and one old one). Write out backward-compatible numerical indices for use when reloading in older versions. Also add a mechanism to invert the colour map, though I don't think it turns out useful enough to include in the UI.
author Chris Cannam
date Thu, 18 Oct 2018 13:21:56 +0100
parents 6e724c81f18f
children c8a6fd3f9dff
line wrap: on
line diff
--- a/layer/ColourMapper.cpp	Fri Oct 12 11:17:29 2018 +0100
+++ b/layer/ColourMapper.cpp	Thu Oct 18 13:21:56 2018 +0100
@@ -27,11 +27,14 @@
 
 using namespace std;
 
-static vector<QColor> convertStrings(const vector<QString> &strs)
+static vector<QColor> convertStrings(const vector<QString> &strs,
+                                     bool reversed)
 {
     vector<QColor> converted;
     for (const auto &s: strs) converted.push_back(QColor(s));
-    reverse(converted.begin(), converted.end());
+    if (reversed) {
+        reverse(converted.begin(), converted.end());
+    }
     return converted;
 }
 
@@ -39,13 +42,70 @@
         // Based on ColorBrewer ylGnBu
         "#ffffff", "#ffff00", "#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5",
         "#7bccc4", "#4eb3d3", "#2b8cbe", "#0868ac", "#084081", "#042040"
-        });
+    },
+    true);
 
 static vector<QColor> cherry = convertStrings({
         "#f7f7f7", "#fddbc7", "#f4a582", "#d6604d", "#b2182b", "#dd3497",
         "#ae017e", "#7a0177", "#49006a"
-        });
-    
+    },
+    true);
+
+static vector<QColor> magma = convertStrings({
+        "#FCFFB2", "#FCDF96", "#FBC17D", "#FBA368", "#FA8657", "#F66B4D",
+        "#ED504A", "#E03B50", "#C92D59", "#B02363", "#981D69", "#81176D",
+        "#6B116F", "#57096E", "#43006A", "#300060", "#1E0848", "#110B2D",
+        "#080616", "#000005"
+    },
+    true);
+
+static vector<QColor> cividis = convertStrings({
+        "#00204c", "#00204e", "#002150", "#002251", "#002353", "#002355",
+        "#002456", "#002558", "#00265a", "#00265b", "#00275d", "#00285f",
+        "#002861", "#002963", "#002a64", "#002a66", "#002b68", "#002c6a",
+        "#002d6c", "#002d6d", "#002e6e", "#002e6f", "#002f6f", "#002f6f",
+        "#00306f", "#00316f", "#00316f", "#00326e", "#00336e", "#00346e",
+        "#00346e", "#01356e", "#06366e", "#0a376d", "#0e376d", "#12386d",
+        "#15396d", "#17396d", "#1a3a6c", "#1c3b6c", "#1e3c6c", "#203c6c",
+        "#223d6c", "#243e6c", "#263e6c", "#273f6c", "#29406b", "#2b416b",
+        "#2c416b", "#2e426b", "#2f436b", "#31446b", "#32446b", "#33456b",
+        "#35466b", "#36466b", "#37476b", "#38486b", "#3a496b", "#3b496b",
+        "#3c4a6b", "#3d4b6b", "#3e4b6b", "#404c6b", "#414d6b", "#424e6b",
+        "#434e6b", "#444f6b", "#45506b", "#46506b", "#47516b", "#48526b",
+        "#49536b", "#4a536b", "#4b546b", "#4c556b", "#4d556b", "#4e566b",
+        "#4f576c", "#50586c", "#51586c", "#52596c", "#535a6c", "#545a6c",
+        "#555b6c", "#565c6c", "#575d6d", "#585d6d", "#595e6d", "#5a5f6d",
+        "#5b5f6d", "#5c606d", "#5d616e", "#5e626e", "#5f626e", "#5f636e",
+        "#60646e", "#61656f", "#62656f", "#63666f", "#64676f", "#65676f",
+        "#666870", "#676970", "#686a70", "#686a70", "#696b71", "#6a6c71",
+        "#6b6d71", "#6c6d72", "#6d6e72", "#6e6f72", "#6f6f72", "#6f7073",
+        "#707173", "#717273", "#727274", "#737374", "#747475", "#757575",
+        "#757575", "#767676", "#777776", "#787876", "#797877", "#7a7977",
+        "#7b7a77", "#7b7b78", "#7c7b78", "#7d7c78", "#7e7d78", "#7f7e78",
+        "#807e78", "#817f78", "#828078", "#838178", "#848178", "#858278",
+        "#868378", "#878478", "#888578", "#898578", "#8a8678", "#8b8778",
+        "#8c8878", "#8d8878", "#8e8978", "#8f8a78", "#908b78", "#918c78",
+        "#928c78", "#938d78", "#948e78", "#958f78", "#968f77", "#979077",
+        "#989177", "#999277", "#9a9377", "#9b9377", "#9c9477", "#9d9577",
+        "#9e9676", "#9f9776", "#a09876", "#a19876", "#a29976", "#a39a75",
+        "#a49b75", "#a59c75", "#a69c75", "#a79d75", "#a89e74", "#a99f74",
+        "#aaa074", "#aba174", "#aca173", "#ada273", "#aea373", "#afa473",
+        "#b0a572", "#b1a672", "#b2a672", "#b4a771", "#b5a871", "#b6a971",
+        "#b7aa70", "#b8ab70", "#b9ab70", "#baac6f", "#bbad6f", "#bcae6e",
+        "#bdaf6e", "#beb06e", "#bfb16d", "#c0b16d", "#c1b26c", "#c2b36c",
+        "#c3b46c", "#c5b56b", "#c6b66b", "#c7b76a", "#c8b86a", "#c9b869",
+        "#cab969", "#cbba68", "#ccbb68", "#cdbc67", "#cebd67", "#d0be66",
+        "#d1bf66", "#d2c065", "#d3c065", "#d4c164", "#d5c263", "#d6c363",
+        "#d7c462", "#d8c561", "#d9c661", "#dbc760", "#dcc860", "#ddc95f",
+        "#deca5e", "#dfcb5d", "#e0cb5d", "#e1cc5c", "#e3cd5b", "#e4ce5b",
+        "#e5cf5a", "#e6d059", "#e7d158", "#e8d257", "#e9d356", "#ebd456",
+        "#ecd555", "#edd654", "#eed753", "#efd852", "#f0d951", "#f1da50",
+        "#f3db4f", "#f4dc4e", "#f5dd4d", "#f6de4c", "#f7df4b", "#f9e049",
+        "#fae048", "#fbe147", "#fce246", "#fde345", "#ffe443", "#ffe542",
+        "#ffe642", "#ffe743", "#ffe844", "#ffe945"
+    },
+    false);
+
 static void
 mapDiscrete(double norm, vector<QColor> &colours, double &r, double &g, double &b)
 {
@@ -61,8 +121,9 @@
     b = c0.blueF() * prop0 + c1.blueF() * prop1;
 }
 
-ColourMapper::ColourMapper(int map, double min, double max) :
+ColourMapper::ColourMapper(int map, bool inverted, double min, double max) :
     m_map(map),
+    m_inverted(inverted),
     m_min(min),
     m_max(max)
 {
@@ -80,14 +141,16 @@
 int
 ColourMapper::getColourMapCount()
 {
-    return 12;
+    return 15;
 }
 
 QString
-ColourMapper::getColourMapName(int n)
+ColourMapper::getColourMapLabel(int n)
 {
+    // When adding a map, be sure to also update getColourMapCount()
+    
     if (n >= getColourMapCount()) return QObject::tr("<unknown>");
-    StandardMap map = (StandardMap)n;
+    ColourMap map = (ColourMap)n;
 
     switch (map) {
     case Green:            return QObject::tr("Green");
@@ -102,17 +165,112 @@
     case Highlight:        return QObject::tr("Highlight");
     case Printer:          return QObject::tr("Printer");
     case HighGain:         return QObject::tr("High Gain");
+    case BlueOnBlack:      return QObject::tr("Blue on Black");
+    case Cividis:          return QObject::tr("Cividis");
+    case Magma:            return QObject::tr("Magma");
     }
 
     return QObject::tr("<unknown>");
 }
 
+QString
+ColourMapper::getColourMapId(int n)
+{
+    if (n >= getColourMapCount()) return "<unknown>";
+    ColourMap map = (ColourMap)n;
+
+    switch (map) {
+    case Green:            return "Green";
+    case WhiteOnBlack:     return "White on Black";
+    case BlackOnWhite:     return "Black on White";
+    case Cherry:           return "Cherry";
+    case Wasp:             return "Wasp";
+    case Ice:              return "Ice";
+    case Sunset:           return "Sunset";
+    case FruitSalad:       return "Fruit Salad";
+    case Banded:           return "Banded";
+    case Highlight:        return "Highlight";
+    case Printer:          return "Printer";
+    case HighGain:         return "High Gain";
+    case BlueOnBlack:      return "Blue on Black";
+    case Cividis:          return "Cividis";
+    case Magma:            return "Magma";
+    }
+
+    return "<unknown>";
+}
+
+int
+ColourMapper::getColourMapById(QString id)
+{
+    ColourMap map = (ColourMap)getColourMapCount();
+
+    if      (id == "Green")            { map = Green; }
+    else if (id == "White on Black")   { map = WhiteOnBlack; }
+    else if (id == "Black on White")   { map = BlackOnWhite; }
+    else if (id == "Cherry")           { map = Cherry; }
+    else if (id == "Wasp")             { map = Wasp; }
+    else if (id == "Ice")              { map = Ice; }
+    else if (id == "Sunset")           { map = Sunset; }
+    else if (id == "Fruit Salad")      { map = FruitSalad; }
+    else if (id == "Banded")           { map = Banded; }
+    else if (id == "Highlight")        { map = Highlight; }
+    else if (id == "Printer")          { map = Printer; }
+    else if (id == "High Gain")        { map = HighGain; }
+    else if (id == "Blue on Black")    { map = BlueOnBlack; }
+    else if (id == "Cividis")          { map = Cividis; }
+    else if (id == "Magma")            { map = Magma; }
+
+    if (map == (ColourMap)getColourMapCount()) {
+        return -1;
+    } else {
+        return int(map);
+    }
+}
+
+int
+ColourMapper::getBackwardCompatibilityColourMap(int n)
+{
+    /* Returned value should be an index into the series
+     * (Default/Green, Sunset, WhiteOnBlack, BlackOnWhite, RedOnBlue,
+     * YellowOnBlack, BlueOnBlack, FruitSalad, Banded, Highlight,
+     * Printer, HighGain). Minimum 0, maximum 11.
+     */
+        
+    if (n >= getColourMapCount()) return 0;
+    ColourMap map = (ColourMap)n;
+
+    switch (map) {
+    case Green:            return 0;
+    case WhiteOnBlack:     return 2;
+    case BlackOnWhite:     return 3;
+    case Cherry:           return 4;
+    case Wasp:             return 5;
+    case Ice:              return 6;
+    case Sunset:           return 1;
+    case FruitSalad:       return 7;
+    case Banded:           return 8;
+    case Highlight:        return 9;
+    case Printer:          return 10;
+    case HighGain:         return 11;
+    case BlueOnBlack:      return 6;
+    case Cividis:          return 6;
+    case Magma:            return 1;
+    }
+
+    return 0;
+}
+
 QColor
 ColourMapper::map(double value) const
 {
     double norm = (value - m_min) / (m_max - m_min);
     if (norm < 0.0) norm = 0.0;
     if (norm > 1.0) norm = 1.0;
+
+    if (m_inverted) {
+        norm = 1.0 - norm;
+    }
     
     double h = 0.0, s = 0.0, v = 0.0, r = 0.0, g = 0.0, b = 0.0;
     bool hsv = true;
@@ -120,7 +278,7 @@
     double blue = 0.6666, pieslice = 0.3333;
 
     if (m_map >= getColourMapCount()) return Qt::black;
-    StandardMap map = (StandardMap)m_map;
+    ColourMap map = (ColourMap)m_map;
 
     switch (map) {
 
@@ -150,6 +308,18 @@
         s = 1.0;
         v = norm;
         break;
+        
+    case BlueOnBlack:
+        h = blue;
+        s = 1.0;
+        v = norm * 2.0;
+        if (v > 1.0) {
+            v = 1.0;
+            s = 1.0 - (sqrt(norm) - 0.707) * 3.413;
+            if (s < 0.0) s = 0.0;
+            if (s > 1.0) s = 1.0;
+        }
+        break;
 
     case Sunset:
         r = (norm - 0.24) * 2.38;
@@ -237,6 +407,17 @@
     case Ice:
         hsv = false;
         mapDiscrete(norm, ice, r, g, b);
+        break;
+
+    case Cividis:
+        hsv = false;
+        mapDiscrete(norm, cividis, r, g, b);
+        break;
+
+    case Magma:
+        hsv = false;
+        mapDiscrete(norm, magma, r, g, b);
+        break;
     }
 
     if (hsv) {
@@ -250,7 +431,7 @@
 ColourMapper::getContrastingColour() const
 {
     if (m_map >= getColourMapCount()) return Qt::white;
-    StandardMap map = (StandardMap)m_map;
+    ColourMap map = (ColourMap)m_map;
 
     switch (map) {
 
@@ -289,6 +470,15 @@
 
     case HighGain:
         return Qt::red;
+
+    case BlueOnBlack:
+        return Qt::red;
+
+    case Cividis:
+        return Qt::white;
+
+    case Magma:
+        return Qt::white;
     }
 
     return Qt::white;
@@ -298,7 +488,7 @@
 ColourMapper::hasLightBackground() const
 {
     if (m_map >= getColourMapCount()) return false;
-    StandardMap map = (StandardMap)m_map;
+    ColourMap map = (ColourMap)m_map;
 
     switch (map) {
 
@@ -316,6 +506,9 @@
     case FruitSalad:
     case Banded:
     case Highlight:
+    case BlueOnBlack:
+    case Cividis:
+    case Magma:
         
     default:
         return false;