annotate base/RangeMapper.cpp @ 1061:c1e43c8d2527 tonioni

Thread-local debug was causing crash on exit with Qt 5.4.x. But we introduced that because QDebug itself was crashing when used from multiple threads. Replace with simpler fstream version
author Chris Cannam
date Tue, 31 Mar 2015 10:36:52 +0100
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 }