annotate layer/ColourScale.cpp @ 1253:822edd9bb665

Restrict colour 3d plot log scale range to 10 orders of magnitude
author Chris Cannam
date Tue, 28 Feb 2017 16:34:59 +0000
parents c53ed1a6fcbd
children 6e724c81f18f
rev   line source
Chris@1068 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1068 2
Chris@1068 3 /*
Chris@1068 4 Sonic Visualiser
Chris@1068 5 An audio file viewer and annotation editor.
Chris@1068 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1068 7 This file copyright 2006-2016 Chris Cannam and QMUL.
Chris@1068 8
Chris@1068 9 This program is free software; you can redistribute it and/or
Chris@1068 10 modify it under the terms of the GNU General Public License as
Chris@1068 11 published by the Free Software Foundation; either version 2 of the
Chris@1068 12 License, or (at your option) any later version. See the file
Chris@1068 13 COPYING included with this distribution for more information.
Chris@1068 14 */
Chris@1068 15
Chris@1068 16 #include "ColourScale.h"
Chris@1068 17
Chris@1068 18 #include "base/AudioLevel.h"
Chris@1068 19 #include "base/LogRange.h"
Chris@1068 20
Chris@1068 21 #include <cmath>
Chris@1129 22 #include <iostream>
Chris@1129 23
Chris@1129 24 using namespace std;
Chris@1068 25
Chris@1068 26 int ColourScale::m_maxPixel = 255;
Chris@1068 27
Chris@1070 28 ColourScale::ColourScale(Parameters parameters) :
Chris@1070 29 m_params(parameters),
Chris@1070 30 m_mapper(m_params.colourMap, 1.f, double(m_maxPixel))
Chris@1068 31 {
Chris@1070 32 if (m_params.minValue >= m_params.maxValue) {
Chris@1129 33 cerr << "ERROR: ColourScale::ColourScale: minValue = "
Chris@1129 34 << m_params.minValue << ", maxValue = " << m_params.maxValue << endl;
Chris@1068 35 throw std::logic_error("maxValue must be greater than minValue");
Chris@1068 36 }
Chris@1068 37
Chris@1070 38 m_mappedMin = m_params.minValue;
Chris@1070 39 m_mappedMax = m_params.maxValue;
Chris@1068 40
Chris@1127 41 if (m_mappedMin < m_params.threshold) {
Chris@1127 42 m_mappedMin = m_params.threshold;
Chris@1127 43 }
Chris@1127 44
Chris@1137 45 if (m_params.scaleType == ColourScaleType::Log) {
Chris@1068 46
Chris@1253 47 // When used in e.g. spectrogram, we have a range with a min
Chris@1253 48 // value of zero. The LogRange converts that to a threshold
Chris@1253 49 // value of -10, so for a range of e.g. (0,1) we end up with
Chris@1253 50 // (-10,0) as the mapped range.
Chris@1253 51 //
Chris@1253 52 // But in other contexts we could end up with a mapped range
Chris@1253 53 // much larger than that if we have a small non-zero minimum
Chris@1253 54 // value (less than 1e-10), or a particularly large
Chris@1253 55 // maximum. That's unlikely to give us good results, so let's
Chris@1253 56 // insist that the mapped log range has no more than 10
Chris@1253 57 // difference between min and max, to match the behaviour when
Chris@1253 58 // min == 0 at the input.
Chris@1253 59 //
Chris@1253 60 double threshold = -10.0;
Chris@1253 61 LogRange::mapRange(m_mappedMin, m_mappedMax, threshold);
Chris@1253 62 if (m_mappedMin < m_mappedMax + threshold) {
Chris@1253 63 m_mappedMin = m_mappedMax + threshold;
Chris@1253 64 }
Chris@1068 65
Chris@1137 66 } else if (m_params.scaleType == ColourScaleType::PlusMinusOne) {
Chris@1068 67
Chris@1068 68 m_mappedMin = -1.0;
Chris@1068 69 m_mappedMax = 1.0;
Chris@1068 70
Chris@1137 71 } else if (m_params.scaleType == ColourScaleType::Absolute) {
Chris@1068 72
Chris@1068 73 m_mappedMin = fabs(m_mappedMin);
Chris@1068 74 m_mappedMax = fabs(m_mappedMax);
Chris@1068 75 if (m_mappedMin >= m_mappedMax) {
Chris@1068 76 std::swap(m_mappedMin, m_mappedMax);
Chris@1068 77 }
Chris@1068 78 }
Chris@1068 79
Chris@1068 80 if (m_mappedMin >= m_mappedMax) {
Chris@1129 81 cerr << "ERROR: ColourScale::ColourScale: minValue = " << m_params.minValue
Chris@1129 82 << ", maxValue = " << m_params.maxValue
Chris@1129 83 << ", threshold = " << m_params.threshold
Chris@1137 84 << ", scale = " << int(m_params.scaleType)
Chris@1129 85 << " resulting in mapped minValue = " << m_mappedMin
Chris@1129 86 << ", mapped maxValue = " << m_mappedMax << endl;
Chris@1068 87 throw std::logic_error("maxValue must be greater than minValue [after mapping]");
Chris@1068 88 }
Chris@1068 89 }
Chris@1068 90
Chris@1071 91 ColourScale::~ColourScale()
Chris@1071 92 {
Chris@1071 93 }
Chris@1071 94
Chris@1105 95 ColourScaleType
Chris@1079 96 ColourScale::getScale() const
Chris@1079 97 {
Chris@1137 98 return m_params.scaleType;
Chris@1079 99 }
Chris@1079 100
Chris@1068 101 int
Chris@1079 102 ColourScale::getPixel(double value) const
Chris@1068 103 {
Chris@1068 104 double maxPixF = m_maxPixel;
Chris@1068 105
Chris@1137 106 if (m_params.scaleType == ColourScaleType::Phase) {
Chris@1068 107 double half = (maxPixF - 1.f) / 2.f;
Chris@1138 108 int pixel = 1 + int((value * half) / M_PI + half);
Chris@1143 109 // cerr << "phase = " << value << " pixel = " << pixel << endl;
Chris@1138 110 return pixel;
Chris@1068 111 }
Chris@1068 112
Chris@1070 113 value *= m_params.gain;
Chris@1137 114
Chris@1070 115 if (value < m_params.threshold) return 0;
Chris@1068 116
Chris@1068 117 double mapped = value;
Chris@1068 118
Chris@1137 119 if (m_params.scaleType == ColourScaleType::Log) {
Chris@1068 120 mapped = LogRange::map(value);
Chris@1137 121 } else if (m_params.scaleType == ColourScaleType::PlusMinusOne) {
Chris@1068 122 if (mapped < -1.f) mapped = -1.f;
Chris@1068 123 if (mapped > 1.f) mapped = 1.f;
Chris@1137 124 } else if (m_params.scaleType == ColourScaleType::Absolute) {
Chris@1068 125 if (mapped < 0.f) mapped = -mapped;
Chris@1068 126 }
Chris@1137 127
Chris@1137 128 mapped *= m_params.multiple;
Chris@1137 129
Chris@1068 130 if (mapped < m_mappedMin) {
Chris@1068 131 mapped = m_mappedMin;
Chris@1068 132 }
Chris@1068 133 if (mapped > m_mappedMax) {
Chris@1068 134 mapped = m_mappedMax;
Chris@1068 135 }
Chris@1068 136
Chris@1068 137 double proportion = (mapped - m_mappedMin) / (m_mappedMax - m_mappedMin);
Chris@1068 138
Chris@1068 139 int pixel = 0;
Chris@1068 140
Chris@1137 141 if (m_params.scaleType == ColourScaleType::Meter) {
Chris@1068 142 pixel = AudioLevel::multiplier_to_preview(proportion, m_maxPixel-1) + 1;
Chris@1068 143 } else {
Chris@1068 144 pixel = int(proportion * maxPixF) + 1;
Chris@1068 145 }
Chris@1068 146
Chris@1070 147 if (pixel < 0) {
Chris@1070 148 pixel = 0;
Chris@1070 149 }
Chris@1070 150 if (pixel > m_maxPixel) {
Chris@1070 151 pixel = m_maxPixel;
Chris@1070 152 }
Chris@1068 153 return pixel;
Chris@1068 154 }
Chris@1068 155
Chris@1068 156 QColor
Chris@1079 157 ColourScale::getColourForPixel(int pixel, int rotation) const
Chris@1068 158 {
Chris@1068 159 if (pixel < 0) {
Chris@1068 160 pixel = 0;
Chris@1068 161 }
Chris@1068 162 if (pixel > m_maxPixel) {
Chris@1068 163 pixel = m_maxPixel;
Chris@1068 164 }
Chris@1068 165 if (pixel == 0) {
Chris@1068 166 if (m_mapper.hasLightBackground()) {
Chris@1068 167 return Qt::white;
Chris@1068 168 } else {
Chris@1068 169 return Qt::black;
Chris@1068 170 }
Chris@1068 171 } else {
Chris@1068 172 int target = int(pixel) + rotation;
Chris@1068 173 while (target < 1) target += m_maxPixel;
Chris@1068 174 while (target > m_maxPixel) target -= m_maxPixel;
Chris@1068 175 return m_mapper.map(double(target));
Chris@1068 176 }
Chris@1068 177 }