annotate layer/ColourMapper.cpp @ 1012:2a85ab180d08 colourschemes

Experimentation with colour schemes and mappings
author Chris Cannam
date Wed, 02 Dec 2015 15:01:38 +0000
parents 6e2a034f7117
children ac69043f82b6
rev   line source
Chris@376 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@376 2
Chris@376 3 /*
Chris@376 4 Sonic Visualiser
Chris@376 5 An audio file viewer and annotation editor.
Chris@376 6 Centre for Digital Music, Queen Mary, University of London.
Chris@376 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@376 8
Chris@376 9 This program is free software; you can redistribute it and/or
Chris@376 10 modify it under the terms of the GNU General Public License as
Chris@376 11 published by the Free Software Foundation; either version 2 of the
Chris@376 12 License, or (at your option) any later version. See the file
Chris@376 13 COPYING included with this distribution for more information.
Chris@376 14 */
Chris@376 15
Chris@376 16 #include "ColourMapper.h"
Chris@376 17
Chris@376 18 #include <iostream>
Chris@376 19
Chris@376 20 #include <cmath>
Chris@376 21
Chris@682 22 #include "base/Debug.h"
Chris@682 23
Chris@1012 24 #include <vector>
Chris@1012 25
Chris@1012 26 using namespace std;
Chris@1012 27
Chris@1012 28 static vector<QString> ylGnBuS {
Chris@1012 29 // this is ylGnBu 9
Chris@1012 30 // "#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081"
Chris@1012 31
Chris@1012 32 "#ffffff", "#ffff00", "#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081","#042040"
Chris@1012 33
Chris@1012 34 // this is PuOr 11
Chris@1012 35 // "#7f3b08","#b35806","#e08214","#fdb863","#fee0b6","#f7f7f7","#d8daeb","#b2abd2","#8073ac","#542788","#2d004b"
Chris@1012 36 };
Chris@1012 37
Chris@1012 38
Chris@1012 39 static vector<QColor> convertStrings(const vector<QString> &strs)
Chris@1012 40 {
Chris@1012 41 vector<QColor> converted;
Chris@1012 42 for (const auto &s: strs) converted.push_back(QColor(s));
Chris@1012 43 reverse(converted.begin(), converted.end());
Chris@1012 44 return converted;
Chris@1012 45 }
Chris@1012 46
Chris@1012 47 static vector<QColor> ylGnBu = convertStrings(ylGnBuS);
Chris@1012 48
Chris@1012 49 static void
Chris@1012 50 mapDiscrete(double norm, vector<QColor> &colours, double &r, double &g, double &b)
Chris@1012 51 {
Chris@1012 52 int n = colours.size();
Chris@1012 53 double m = norm * (n-1);
Chris@1012 54 if (m >= n-1) { colours[n-1].getRgbF(&r, &g, &b, 0); return; }
Chris@1012 55 if (m <= 0) { colours[0].getRgbF(&r, &g, &b, 0); return; }
Chris@1012 56 int base(int(floor(m)));
Chris@1012 57 double prop0 = (base + 1.0) - m, prop1 = m - base;
Chris@1012 58 QColor c0(colours[base]), c1(colours[base+1]);
Chris@1012 59 r = c0.redF() * prop0 + c1.redF() * prop1;
Chris@1012 60 g = c0.greenF() * prop0 + c1.greenF() * prop1;
Chris@1012 61 b = c0.blueF() * prop0 + c1.blueF() * prop1;
Chris@1012 62 cerr << "mapDiscrete: norm " << norm << " -> " << r << "," << g << "," << b << endl;
Chris@1012 63 }
Chris@1012 64
Chris@902 65 ColourMapper::ColourMapper(int map, double min, double max) :
Chris@376 66 QObject(),
Chris@376 67 m_map(map),
Chris@376 68 m_min(min),
Chris@376 69 m_max(max)
Chris@376 70 {
Chris@376 71 if (m_min == m_max) {
Chris@682 72 cerr << "WARNING: ColourMapper: min == max (== " << m_min
Chris@682 73 << "), adjusting" << endl;
Chris@376 74 m_max = m_min + 1;
Chris@376 75 }
Chris@376 76 }
Chris@376 77
Chris@376 78 ColourMapper::~ColourMapper()
Chris@376 79 {
Chris@376 80 }
Chris@376 81
Chris@376 82 int
Chris@376 83 ColourMapper::getColourMapCount()
Chris@376 84 {
Chris@1012 85 return 13;
Chris@376 86 }
Chris@376 87
Chris@376 88 QString
Chris@376 89 ColourMapper::getColourMapName(int n)
Chris@376 90 {
Chris@376 91 if (n >= getColourMapCount()) return tr("<unknown>");
Chris@376 92 StandardMap map = (StandardMap)n;
Chris@376 93
Chris@376 94 switch (map) {
Chris@376 95 case DefaultColours: return tr("Default");
Chris@376 96 case WhiteOnBlack: return tr("White on Black");
Chris@376 97 case BlackOnWhite: return tr("Black on White");
Chris@376 98 case RedOnBlue: return tr("Red on Blue");
Chris@376 99 case YellowOnBlack: return tr("Yellow on Black");
Chris@376 100 case BlueOnBlack: return tr("Blue on Black");
Chris@376 101 case Sunset: return tr("Sunset");
Chris@376 102 case FruitSalad: return tr("Fruit Salad");
Chris@376 103 case Banded: return tr("Banded");
Chris@376 104 case Highlight: return tr("Highlight");
Chris@376 105 case Printer: return tr("Printer");
Chris@536 106 case HighGain: return tr("High Gain");
Chris@1012 107 case YlGnBu: return tr("YlGnBu");
Chris@376 108 }
Chris@376 109
Chris@376 110 return tr("<unknown>");
Chris@376 111 }
Chris@376 112
Chris@376 113 QColor
Chris@902 114 ColourMapper::map(double value) const
Chris@376 115 {
Chris@902 116 double norm = (value - m_min) / (m_max - m_min);
Chris@902 117 if (norm < 0.0) norm = 0.0;
Chris@902 118 if (norm > 1.0) norm = 1.0;
Chris@376 119
Chris@902 120 double h = 0.0, s = 0.0, v = 0.0, r = 0.0, g = 0.0, b = 0.0;
Chris@376 121 bool hsv = true;
Chris@376 122
Chris@911 123 double blue = 0.6666, pieslice = 0.3333;
Chris@376 124
Chris@376 125 if (m_map >= getColourMapCount()) return Qt::black;
Chris@376 126 StandardMap map = (StandardMap)m_map;
Chris@376 127
Chris@376 128 switch (map) {
Chris@376 129
Chris@376 130 case DefaultColours:
Chris@902 131 h = blue - norm * 2.0 * pieslice;
Chris@902 132 s = 0.5f + norm/2.0;
Chris@376 133 v = norm;
Chris@376 134 break;
Chris@376 135
Chris@376 136 case WhiteOnBlack:
Chris@376 137 r = g = b = norm;
Chris@376 138 hsv = false;
Chris@376 139 break;
Chris@376 140
Chris@376 141 case BlackOnWhite:
Chris@902 142 r = g = b = 1.0 - norm;
Chris@376 143 hsv = false;
Chris@376 144 break;
Chris@376 145
Chris@376 146 case RedOnBlue:
Chris@902 147 h = blue - pieslice/4.0 + norm * (pieslice + pieslice/4.0);
Chris@902 148 s = 1.0;
Chris@376 149 v = norm;
Chris@376 150 break;
Chris@376 151
Chris@376 152 case YellowOnBlack:
Chris@902 153 h = 0.15;
Chris@902 154 s = 1.0;
Chris@376 155 v = norm;
Chris@376 156 break;
Chris@376 157
Chris@376 158 case BlueOnBlack:
Chris@376 159 h = blue;
Chris@902 160 s = 1.0;
Chris@902 161 v = norm * 2.0;
Chris@902 162 if (v > 1.0) {
Chris@902 163 v = 1.0;
Chris@904 164 s = 1.0 - (sqrt(norm) - 0.707) * 3.413;
Chris@902 165 if (s < 0.0) s = 0.0;
Chris@902 166 if (s > 1.0) s = 1.0;
Chris@376 167 }
Chris@376 168 break;
Chris@376 169
Chris@376 170 case Sunset:
Chris@902 171 r = (norm - 0.24) * 2.38;
Chris@902 172 if (r > 1.0) r = 1.0;
Chris@902 173 if (r < 0.0) r = 0.0;
Chris@902 174 g = (norm - 0.64) * 2.777;
Chris@902 175 if (g > 1.0) g = 1.0;
Chris@902 176 if (g < 0.0) g = 0.0;
Chris@376 177 b = (3.6f * norm);
Chris@902 178 if (norm > 0.277) b = 2.0 - b;
Chris@902 179 if (b > 1.0) b = 1.0;
Chris@902 180 if (b < 0.0) b = 0.0;
Chris@376 181 hsv = false;
Chris@376 182 break;
Chris@376 183
Chris@376 184 case FruitSalad:
Chris@902 185 h = blue + (pieslice/6.0) - norm;
Chris@902 186 if (h < 0.0) h += 1.0;
Chris@902 187 s = 1.0;
Chris@902 188 v = 1.0;
Chris@376 189 break;
Chris@376 190
Chris@376 191 case Banded:
Chris@376 192 if (norm < 0.125) return Qt::darkGreen;
Chris@376 193 else if (norm < 0.25) return Qt::green;
Chris@376 194 else if (norm < 0.375) return Qt::darkBlue;
Chris@376 195 else if (norm < 0.5) return Qt::blue;
Chris@376 196 else if (norm < 0.625) return Qt::darkYellow;
Chris@376 197 else if (norm < 0.75) return Qt::yellow;
Chris@376 198 else if (norm < 0.875) return Qt::darkRed;
Chris@376 199 else return Qt::red;
Chris@376 200 break;
Chris@376 201
Chris@376 202 case Highlight:
Chris@376 203 if (norm > 0.99) return Qt::white;
Chris@376 204 else return Qt::darkBlue;
Chris@376 205
Chris@376 206 case Printer:
Chris@376 207 if (norm > 0.8) {
Chris@902 208 r = 1.0;
Chris@376 209 } else if (norm > 0.7) {
Chris@902 210 r = 0.9;
Chris@376 211 } else if (norm > 0.6) {
Chris@902 212 r = 0.8;
Chris@376 213 } else if (norm > 0.5) {
Chris@902 214 r = 0.7;
Chris@376 215 } else if (norm > 0.4) {
Chris@902 216 r = 0.6;
Chris@376 217 } else if (norm > 0.3) {
Chris@902 218 r = 0.5;
Chris@376 219 } else if (norm > 0.2) {
Chris@902 220 r = 0.4;
Chris@376 221 } else {
Chris@902 222 r = 0.0;
Chris@376 223 }
Chris@902 224 r = g = b = 1.0 - r;
Chris@376 225 hsv = false;
Chris@376 226 break;
Chris@536 227
Chris@536 228 case HighGain:
Chris@902 229 if (norm <= 1.0 / 256.0) {
Chris@902 230 norm = 0.0;
Chris@536 231 } else {
Chris@904 232 norm = 0.1f + (pow(((norm - 0.5) * 2.0), 3.0) + 1.0) / 2.081;
Chris@536 233 }
Chris@536 234 // now as for Sunset
Chris@902 235 r = (norm - 0.24) * 2.38;
Chris@902 236 if (r > 1.0) r = 1.0;
Chris@902 237 if (r < 0.0) r = 0.0;
Chris@902 238 g = (norm - 0.64) * 2.777;
Chris@902 239 if (g > 1.0) g = 1.0;
Chris@902 240 if (g < 0.0) g = 0.0;
Chris@536 241 b = (3.6f * norm);
Chris@902 242 if (norm > 0.277) b = 2.0 - b;
Chris@902 243 if (b > 1.0) b = 1.0;
Chris@902 244 if (b < 0.0) b = 0.0;
Chris@536 245 hsv = false;
Chris@536 246 /*
Chris@902 247 if (r > 1.0) r = 1.0;
Chris@902 248 r = g = b = 1.0 - r;
Chris@536 249 hsv = false;
Chris@536 250 */
Chris@536 251 break;
Chris@1012 252
Chris@1012 253 case YlGnBu:
Chris@1012 254 hsv = false;
Chris@1012 255 mapDiscrete(norm, ylGnBu, r, g, b);
Chris@376 256 }
Chris@376 257
Chris@376 258 if (hsv) {
Chris@376 259 return QColor::fromHsvF(h, s, v);
Chris@376 260 } else {
Chris@376 261 return QColor::fromRgbF(r, g, b);
Chris@376 262 }
Chris@376 263 }
Chris@376 264
Chris@376 265 QColor
Chris@376 266 ColourMapper::getContrastingColour() const
Chris@376 267 {
Chris@376 268 if (m_map >= getColourMapCount()) return Qt::white;
Chris@376 269 StandardMap map = (StandardMap)m_map;
Chris@376 270
Chris@376 271 switch (map) {
Chris@376 272
Chris@376 273 case DefaultColours:
Chris@376 274 return QColor(255, 150, 50);
Chris@376 275
Chris@376 276 case WhiteOnBlack:
Chris@376 277 return Qt::red;
Chris@376 278
Chris@376 279 case BlackOnWhite:
Chris@376 280 return Qt::darkGreen;
Chris@376 281
Chris@376 282 case RedOnBlue:
Chris@376 283 return Qt::green;
Chris@376 284
Chris@376 285 case YellowOnBlack:
Chris@376 286 return QColor::fromHsv(240, 255, 255);
Chris@376 287
Chris@376 288 case BlueOnBlack:
Chris@376 289 return Qt::red;
Chris@376 290
Chris@376 291 case Sunset:
Chris@376 292 return Qt::white;
Chris@376 293
Chris@376 294 case FruitSalad:
Chris@376 295 return Qt::white;
Chris@376 296
Chris@376 297 case Banded:
Chris@376 298 return Qt::cyan;
Chris@376 299
Chris@376 300 case Highlight:
Chris@376 301 return Qt::red;
Chris@376 302
Chris@376 303 case Printer:
Chris@376 304 return Qt::red;
Chris@536 305
Chris@536 306 case HighGain:
Chris@536 307 return Qt::red;
Chris@376 308 }
Chris@376 309
Chris@376 310 return Qt::white;
Chris@376 311 }
Chris@376 312
Chris@376 313 bool
Chris@376 314 ColourMapper::hasLightBackground() const
Chris@376 315 {
Chris@376 316 if (m_map >= getColourMapCount()) return false;
Chris@376 317 StandardMap map = (StandardMap)m_map;
Chris@376 318
Chris@376 319 switch (map) {
Chris@376 320
Chris@376 321 case BlackOnWhite:
Chris@376 322 case Printer:
Chris@536 323 case HighGain:
Chris@376 324 return true;
Chris@376 325
Chris@805 326 case DefaultColours:
Chris@805 327 case Sunset:
Chris@805 328 case WhiteOnBlack:
Chris@805 329 case RedOnBlue:
Chris@805 330 case YellowOnBlack:
Chris@805 331 case BlueOnBlack:
Chris@805 332 case FruitSalad:
Chris@805 333 case Banded:
Chris@805 334 case Highlight:
Chris@805 335
Chris@376 336 default:
Chris@376 337 return false;
Chris@376 338 }
Chris@376 339 }
Chris@376 340
Chris@376 341