annotate base/RangeMapper.cpp @ 985:f073d924a7c3

Fix #1058 clicking row in Layer Edit dialog when colour 3d plot layer active jumps to wrong frame (was using sample rate where resolution intended)
author Chris Cannam
date Tue, 16 Sep 2014 10:29:19 +0100
parents 12a6140b3ae0
children cc27f35aa75c
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@189 25 float minval, float 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@189 39 LinearRangeMapper::getPositionForValue(float 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@885 48 LinearRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 49 {
Chris@190 50 int position = m_minpos +
Chris@190 51 lrintf(((value - m_minval) / (m_maxval - m_minval))
Chris@190 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@189 57 float
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@885 62 float value = getValueForPositionUnclamped(position);
Chris@885 63 return value;
Chris@885 64 }
Chris@885 65
Chris@885 66 float
Chris@885 67 LinearRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 68 {
Chris@885 69 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@190 70 float value = m_minval +
Chris@190 71 ((float(position - m_minpos) / float(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@356 77 float minval, float 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@356 100 float minval, float maxval,
Chris@356 101 float &minlog, float &ratio)
Chris@356 102 {
Chris@356 103 static float thresh = powf(10, -10);
Chris@356 104 if (minval < thresh) minval = thresh;
Chris@356 105 minlog = log10f(minval);
Chris@356 106 ratio = (maxpos - minpos) / (log10f(maxval) - minlog);
Chris@356 107 }
Chris@356 108
Chris@356 109 void
Chris@356 110 LogRangeMapper::convertRatioMinLog(float ratio, float minlog,
Chris@356 111 int minpos, int maxpos,
Chris@356 112 float &minval, float &maxval)
Chris@356 113 {
Chris@356 114 minval = powf(10, minlog);
Chris@356 115 maxval = powf(10, (maxpos - minpos) / ratio + minlog);
Chris@356 116 }
Chris@356 117
Chris@189 118 int
Chris@189 119 LogRangeMapper::getPositionForValue(float 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@885 128 LogRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 129 {
Chris@885 130 static float thresh = powf(10, -10);
Chris@885 131 if (value < thresh) value = thresh;
Chris@885 132 int position = lrintf((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@189 137 float
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@885 142 float value = getValueForPositionUnclamped(position);
Chris@885 143 return value;
Chris@885 144 }
Chris@885 145
Chris@885 146 float
Chris@885 147 LogRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 148 {
Chris@885 149 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@189 150 float value = powf(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@880 166 InterpolatingRangeMapper::getPositionForValue(float 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@885 177 InterpolatingRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 178 {
Chris@885 179 float p = interpolate(&m_mappings, value);
Chris@885 180 return lrintf(p);
Chris@880 181 }
Chris@880 182
Chris@880 183 float
Chris@880 184 InterpolatingRangeMapper::getValueForPosition(int position) const
Chris@880 185 {
Chris@885 186 float 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@885 194 float
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@885 201 float
Chris@885 202 InterpolatingRangeMapper::interpolate(T *mapping, float value) const
Chris@885 203 {
Chris@885 204 // lower_bound: first element which does not compare less than value
Chris@885 205 typename T::const_iterator i = mapping->lower_bound(value);
Chris@885 206
Chris@885 207 if (i == mapping->begin()) {
Chris@885 208 // value is less than or equal to first element, so use the
Chris@885 209 // gradient from first to second and extend it
Chris@885 210 ++i;
Chris@885 211 }
Chris@885 212
Chris@885 213 if (i == mapping->end()) {
Chris@885 214 // value is off the end, so use the gradient from penultimate
Chris@885 215 // to ultimate and extend it
Chris@885 216 --i;
Chris@885 217 }
Chris@885 218
Chris@885 219 typename T::const_iterator j = i;
Chris@880 220 --j;
Chris@880 221
Chris@885 222 float gradient = float(i->second - j->second) / float(i->first - j->first);
Chris@880 223
Chris@885 224 return j->second + (value - j->first) * gradient;
Chris@880 225 }
Chris@880 226
Chris@880 227 AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings,
Chris@880 228 QString unit) :
Chris@880 229 m_mappings(pointMappings),
Chris@880 230 m_unit(unit)
Chris@880 231 {
Chris@880 232 m_type = chooseMappingTypeFor(m_mappings);
Chris@880 233
Chris@880 234 CoordMap::const_iterator first = m_mappings.begin();
Chris@880 235 CoordMap::const_iterator last = m_mappings.end();
Chris@880 236 --last;
Chris@880 237
Chris@880 238 switch (m_type) {
Chris@880 239 case StraightLine:
Chris@880 240 m_mapper = new LinearRangeMapper(first->second, last->second,
Chris@880 241 first->first, last->first,
Chris@880 242 unit, false);
Chris@880 243 break;
Chris@880 244 case Logarithmic:
Chris@880 245 m_mapper = new LogRangeMapper(first->second, last->second,
Chris@880 246 first->first, last->first,
Chris@880 247 unit, false);
Chris@880 248 break;
Chris@880 249 case Interpolating:
Chris@880 250 m_mapper = new InterpolatingRangeMapper(m_mappings, unit);
Chris@880 251 break;
Chris@880 252 }
Chris@880 253 }
Chris@880 254
Chris@880 255 AutoRangeMapper::~AutoRangeMapper()
Chris@880 256 {
Chris@880 257 delete m_mapper;
Chris@880 258 }
Chris@880 259
Chris@880 260 AutoRangeMapper::MappingType
Chris@880 261 AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings)
Chris@880 262 {
Chris@880 263 // how do we work out whether a linear/log mapping is "close enough"?
Chris@880 264
Chris@880 265 CoordMap::const_iterator first = mappings.begin();
Chris@880 266 CoordMap::const_iterator last = mappings.end();
Chris@880 267 --last;
Chris@880 268
Chris@880 269 LinearRangeMapper linm(first->second, last->second,
Chris@880 270 first->first, last->first,
Chris@880 271 "", false);
Chris@880 272
Chris@880 273 bool inadequate = false;
Chris@880 274
Chris@880 275 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 276 i != mappings.end(); ++i) {
Chris@880 277 int candidate = linm.getPositionForValue(i->first);
Chris@880 278 int diff = candidate - i->second;
Chris@880 279 if (diff < 0) diff = -diff;
Chris@880 280 if (diff > 1) {
Chris@885 281 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 282 // << ", straight-line mapping inadequate" << endl;
Chris@880 283 inadequate = true;
Chris@880 284 break;
Chris@880 285 }
Chris@880 286 }
Chris@880 287
Chris@880 288 if (!inadequate) {
Chris@880 289 return StraightLine;
Chris@880 290 }
Chris@880 291
Chris@880 292 LogRangeMapper logm(first->second, last->second,
Chris@880 293 first->first, last->first,
Chris@880 294 "", false);
Chris@880 295
Chris@880 296 inadequate = false;
Chris@880 297
Chris@880 298 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 299 i != mappings.end(); ++i) {
Chris@880 300 int candidate = logm.getPositionForValue(i->first);
Chris@880 301 int diff = candidate - i->second;
Chris@880 302 if (diff < 0) diff = -diff;
Chris@880 303 if (diff > 1) {
Chris@885 304 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 305 // << ", log mapping inadequate" << endl;
Chris@880 306 inadequate = true;
Chris@880 307 break;
Chris@880 308 }
Chris@880 309 }
Chris@880 310
Chris@880 311 if (!inadequate) {
Chris@880 312 return Logarithmic;
Chris@880 313 }
Chris@880 314
Chris@880 315 return Interpolating;
Chris@880 316 }
Chris@880 317
Chris@880 318 int
Chris@880 319 AutoRangeMapper::getPositionForValue(float value) const
Chris@880 320 {
Chris@880 321 return m_mapper->getPositionForValue(value);
Chris@880 322 }
Chris@880 323
Chris@880 324 float
Chris@880 325 AutoRangeMapper::getValueForPosition(int position) const
Chris@880 326 {
Chris@880 327 return m_mapper->getValueForPosition(position);
Chris@880 328 }
Chris@885 329
Chris@885 330 int
Chris@885 331 AutoRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 332 {
Chris@885 333 return m_mapper->getPositionForValueUnclamped(value);
Chris@885 334 }
Chris@885 335
Chris@885 336 float
Chris@885 337 AutoRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 338 {
Chris@885 339 return m_mapper->getValueForPositionUnclamped(position);
Chris@885 340 }