annotate base/RangeMapper.cpp @ 1176:a6929fb9cc77

Win32 build fix
author Chris Cannam
date Fri, 18 Mar 2016 15:30:28 +0000
parents cc27f35aa75c
children c7b9c902642f
rev   line source
Chris@189 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@189 2
Chris@189 3 /*
Chris@189 4 Sonic Visualiser
Chris@189 5 An audio file viewer and annotation editor.
Chris@189 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 QMUL.
Chris@189 8
Chris@189 9 This program is free software; you can redistribute it and/or
Chris@189 10 modify it under the terms of the GNU General Public License as
Chris@189 11 published by the Free Software Foundation; either version 2 of the
Chris@189 12 License, or (at your option) any later version. See the file
Chris@189 13 COPYING included with this distribution for more information.
Chris@189 14 */
Chris@189 15
Chris@189 16 #include "RangeMapper.h"
Chris@573 17 #include "system/System.h"
Chris@189 18
Chris@189 19 #include <cassert>
Chris@189 20 #include <cmath>
Chris@189 21
Chris@189 22 #include <iostream>
Chris@189 23
Chris@189 24 LinearRangeMapper::LinearRangeMapper(int minpos, int maxpos,
Chris@1038 25 double minval, double maxval,
Chris@464 26 QString unit, bool inverted) :
Chris@189 27 m_minpos(minpos),
Chris@189 28 m_maxpos(maxpos),
Chris@189 29 m_minval(minval),
Chris@189 30 m_maxval(maxval),
Chris@464 31 m_unit(unit),
Chris@464 32 m_inverted(inverted)
Chris@189 33 {
Chris@189 34 assert(m_maxval != m_minval);
Chris@189 35 assert(m_maxpos != m_minpos);
Chris@189 36 }
Chris@189 37
Chris@189 38 int
Chris@1038 39 LinearRangeMapper::getPositionForValue(double value) const
Chris@189 40 {
Chris@885 41 int position = getPositionForValueUnclamped(value);
Chris@885 42 if (position < m_minpos) position = m_minpos;
Chris@885 43 if (position > m_maxpos) position = m_maxpos;
Chris@885 44 return position;
Chris@885 45 }
Chris@885 46
Chris@885 47 int
Chris@1038 48 LinearRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 49 {
Chris@190 50 int position = m_minpos +
Chris@1038 51 int(lrint(((value - m_minval) / (m_maxval - m_minval))
Chris@1038 52 * (m_maxpos - m_minpos)));
Chris@879 53 if (m_inverted) return m_maxpos - (position - m_minpos);
Chris@464 54 else return position;
Chris@189 55 }
Chris@189 56
Chris@1038 57 double
Chris@189 58 LinearRangeMapper::getValueForPosition(int position) const
Chris@189 59 {
Chris@879 60 if (position < m_minpos) position = m_minpos;
Chris@879 61 if (position > m_maxpos) position = m_maxpos;
Chris@1038 62 double value = getValueForPositionUnclamped(position);
Chris@885 63 return value;
Chris@885 64 }
Chris@885 65
Chris@1038 66 double
Chris@885 67 LinearRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 68 {
Chris@885 69 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@1038 70 double value = m_minval +
Chris@1038 71 ((double(position - m_minpos) / double(m_maxpos - m_minpos))
Chris@190 72 * (m_maxval - m_minval));
Chris@189 73 return value;
Chris@189 74 }
Chris@189 75
Chris@189 76 LogRangeMapper::LogRangeMapper(int minpos, int maxpos,
Chris@1038 77 double minval, double maxval,
Chris@464 78 QString unit, bool inverted) :
Chris@189 79 m_minpos(minpos),
Chris@189 80 m_maxpos(maxpos),
Chris@464 81 m_unit(unit),
Chris@464 82 m_inverted(inverted)
Chris@189 83 {
Chris@356 84 convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio);
Chris@356 85
Chris@879 86 // cerr << "LogRangeMapper: minpos " << minpos << ", maxpos "
Chris@879 87 // << maxpos << ", minval " << minval << ", maxval "
Chris@879 88 // << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio
Chris@879 89 // << ", unit " << unit << endl;
Chris@356 90
Chris@189 91 assert(m_maxpos != m_minpos);
Chris@189 92
Chris@189 93 m_maxlog = (m_maxpos - m_minpos) / m_ratio + m_minlog;
Chris@879 94
Chris@879 95 // cerr << "LogRangeMapper: maxlog = " << m_maxlog << endl;
Chris@189 96 }
Chris@189 97
Chris@356 98 void
Chris@356 99 LogRangeMapper::convertMinMax(int minpos, int maxpos,
Chris@1038 100 double minval, double maxval,
Chris@1038 101 double &minlog, double &ratio)
Chris@356 102 {
Chris@1038 103 static double thresh = powf(10, -10);
Chris@356 104 if (minval < thresh) minval = thresh;
Chris@1038 105 minlog = log10(minval);
Chris@1038 106 ratio = (maxpos - minpos) / (log10(maxval) - minlog);
Chris@356 107 }
Chris@356 108
Chris@356 109 void
Chris@1038 110 LogRangeMapper::convertRatioMinLog(double ratio, double minlog,
Chris@356 111 int minpos, int maxpos,
Chris@1038 112 double &minval, double &maxval)
Chris@356 113 {
Chris@1038 114 minval = pow(10, minlog);
Chris@1038 115 maxval = pow(10, (maxpos - minpos) / ratio + minlog);
Chris@356 116 }
Chris@356 117
Chris@189 118 int
Chris@1038 119 LogRangeMapper::getPositionForValue(double value) const
Chris@189 120 {
Chris@885 121 int position = getPositionForValueUnclamped(value);
Chris@189 122 if (position < m_minpos) position = m_minpos;
Chris@189 123 if (position > m_maxpos) position = m_maxpos;
Chris@885 124 return position;
Chris@885 125 }
Chris@885 126
Chris@885 127 int
Chris@1038 128 LogRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 129 {
Chris@1038 130 static double thresh = pow(10, -10);
Chris@885 131 if (value < thresh) value = thresh;
Chris@1038 132 int position = int(lrint((log10(value) - m_minlog) * m_ratio)) + m_minpos;
Chris@879 133 if (m_inverted) return m_maxpos - (position - m_minpos);
Chris@464 134 else return position;
Chris@189 135 }
Chris@189 136
Chris@1038 137 double
Chris@189 138 LogRangeMapper::getValueForPosition(int position) const
Chris@189 139 {
Chris@879 140 if (position < m_minpos) position = m_minpos;
Chris@879 141 if (position > m_maxpos) position = m_maxpos;
Chris@1038 142 double value = getValueForPositionUnclamped(position);
Chris@885 143 return value;
Chris@885 144 }
Chris@885 145
Chris@1038 146 double
Chris@885 147 LogRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 148 {
Chris@885 149 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@1038 150 double value = pow(10, (position - m_minpos) / m_ratio + m_minlog);
Chris@189 151 return value;
Chris@189 152 }
Chris@189 153
Chris@880 154 InterpolatingRangeMapper::InterpolatingRangeMapper(CoordMap pointMappings,
Chris@880 155 QString unit) :
Chris@880 156 m_mappings(pointMappings),
Chris@880 157 m_unit(unit)
Chris@880 158 {
Chris@880 159 for (CoordMap::const_iterator i = m_mappings.begin();
Chris@880 160 i != m_mappings.end(); ++i) {
Chris@880 161 m_reverse[i->second] = i->first;
Chris@880 162 }
Chris@880 163 }
Chris@880 164
Chris@880 165 int
Chris@1038 166 InterpolatingRangeMapper::getPositionForValue(double value) const
Chris@880 167 {
Chris@885 168 int pos = getPositionForValueUnclamped(value);
Chris@885 169 CoordMap::const_iterator i = m_mappings.begin();
Chris@885 170 if (pos < i->second) pos = i->second;
Chris@885 171 i = m_mappings.end(); --i;
Chris@885 172 if (pos > i->second) pos = i->second;
Chris@885 173 return pos;
Chris@885 174 }
Chris@880 175
Chris@885 176 int
Chris@1038 177 InterpolatingRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 178 {
Chris@1038 179 double p = interpolate(&m_mappings, value);
Chris@1038 180 return int(lrint(p));
Chris@880 181 }
Chris@880 182
Chris@1038 183 double
Chris@880 184 InterpolatingRangeMapper::getValueForPosition(int position) const
Chris@880 185 {
Chris@1038 186 double val = getValueForPositionUnclamped(position);
Chris@885 187 CoordMap::const_iterator i = m_mappings.begin();
Chris@885 188 if (val < i->first) val = i->first;
Chris@885 189 i = m_mappings.end(); --i;
Chris@885 190 if (val > i->first) val = i->first;
Chris@885 191 return val;
Chris@885 192 }
Chris@885 193
Chris@1038 194 double
Chris@885 195 InterpolatingRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 196 {
Chris@885 197 return interpolate(&m_reverse, position);
Chris@885 198 }
Chris@885 199
Chris@885 200 template <typename T>
Chris@1038 201 double
Chris@1038 202 InterpolatingRangeMapper::interpolate(T *mapping, double value) const
Chris@885 203 {
Chris@885 204 // lower_bound: first element which does not compare less than value
Chris@1038 205 typename T::const_iterator i =
Chris@1038 206 mapping->lower_bound(typename T::key_type(value));
Chris@885 207
Chris@885 208 if (i == mapping->begin()) {
Chris@885 209 // value is less than or equal to first element, so use the
Chris@885 210 // gradient from first to second and extend it
Chris@885 211 ++i;
Chris@885 212 }
Chris@885 213
Chris@885 214 if (i == mapping->end()) {
Chris@885 215 // value is off the end, so use the gradient from penultimate
Chris@885 216 // to ultimate and extend it
Chris@885 217 --i;
Chris@885 218 }
Chris@885 219
Chris@885 220 typename T::const_iterator j = i;
Chris@880 221 --j;
Chris@880 222
Chris@1038 223 double gradient = double(i->second - j->second) / double(i->first - j->first);
Chris@880 224
Chris@885 225 return j->second + (value - j->first) * gradient;
Chris@880 226 }
Chris@880 227
Chris@880 228 AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings,
Chris@880 229 QString unit) :
Chris@880 230 m_mappings(pointMappings),
Chris@880 231 m_unit(unit)
Chris@880 232 {
Chris@880 233 m_type = chooseMappingTypeFor(m_mappings);
Chris@880 234
Chris@880 235 CoordMap::const_iterator first = m_mappings.begin();
Chris@880 236 CoordMap::const_iterator last = m_mappings.end();
Chris@880 237 --last;
Chris@880 238
Chris@880 239 switch (m_type) {
Chris@880 240 case StraightLine:
Chris@880 241 m_mapper = new LinearRangeMapper(first->second, last->second,
Chris@880 242 first->first, last->first,
Chris@880 243 unit, false);
Chris@880 244 break;
Chris@880 245 case Logarithmic:
Chris@880 246 m_mapper = new LogRangeMapper(first->second, last->second,
Chris@880 247 first->first, last->first,
Chris@880 248 unit, false);
Chris@880 249 break;
Chris@880 250 case Interpolating:
Chris@880 251 m_mapper = new InterpolatingRangeMapper(m_mappings, unit);
Chris@880 252 break;
Chris@880 253 }
Chris@880 254 }
Chris@880 255
Chris@880 256 AutoRangeMapper::~AutoRangeMapper()
Chris@880 257 {
Chris@880 258 delete m_mapper;
Chris@880 259 }
Chris@880 260
Chris@880 261 AutoRangeMapper::MappingType
Chris@880 262 AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings)
Chris@880 263 {
Chris@880 264 // how do we work out whether a linear/log mapping is "close enough"?
Chris@880 265
Chris@880 266 CoordMap::const_iterator first = mappings.begin();
Chris@880 267 CoordMap::const_iterator last = mappings.end();
Chris@880 268 --last;
Chris@880 269
Chris@880 270 LinearRangeMapper linm(first->second, last->second,
Chris@880 271 first->first, last->first,
Chris@880 272 "", false);
Chris@880 273
Chris@880 274 bool inadequate = false;
Chris@880 275
Chris@880 276 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 277 i != mappings.end(); ++i) {
Chris@880 278 int candidate = linm.getPositionForValue(i->first);
Chris@880 279 int diff = candidate - i->second;
Chris@880 280 if (diff < 0) diff = -diff;
Chris@880 281 if (diff > 1) {
Chris@885 282 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 283 // << ", straight-line mapping inadequate" << endl;
Chris@880 284 inadequate = true;
Chris@880 285 break;
Chris@880 286 }
Chris@880 287 }
Chris@880 288
Chris@880 289 if (!inadequate) {
Chris@880 290 return StraightLine;
Chris@880 291 }
Chris@880 292
Chris@880 293 LogRangeMapper logm(first->second, last->second,
Chris@880 294 first->first, last->first,
Chris@880 295 "", false);
Chris@880 296
Chris@880 297 inadequate = false;
Chris@880 298
Chris@880 299 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 300 i != mappings.end(); ++i) {
Chris@880 301 int candidate = logm.getPositionForValue(i->first);
Chris@880 302 int diff = candidate - i->second;
Chris@880 303 if (diff < 0) diff = -diff;
Chris@880 304 if (diff > 1) {
Chris@885 305 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 306 // << ", log mapping inadequate" << endl;
Chris@880 307 inadequate = true;
Chris@880 308 break;
Chris@880 309 }
Chris@880 310 }
Chris@880 311
Chris@880 312 if (!inadequate) {
Chris@880 313 return Logarithmic;
Chris@880 314 }
Chris@880 315
Chris@880 316 return Interpolating;
Chris@880 317 }
Chris@880 318
Chris@880 319 int
Chris@1038 320 AutoRangeMapper::getPositionForValue(double value) const
Chris@880 321 {
Chris@880 322 return m_mapper->getPositionForValue(value);
Chris@880 323 }
Chris@880 324
Chris@1038 325 double
Chris@880 326 AutoRangeMapper::getValueForPosition(int position) const
Chris@880 327 {
Chris@880 328 return m_mapper->getValueForPosition(position);
Chris@880 329 }
Chris@885 330
Chris@885 331 int
Chris@1038 332 AutoRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 333 {
Chris@885 334 return m_mapper->getPositionForValueUnclamped(value);
Chris@885 335 }
Chris@885 336
Chris@1038 337 double
Chris@885 338 AutoRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 339 {
Chris@885 340 return m_mapper->getValueForPositionUnclamped(position);
Chris@885 341 }