svcore  1.9
RangeMapper.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "RangeMapper.h"
00017 #include "system/System.h"
00018 
00019 #include <cassert>
00020 #include <cmath>
00021 
00022 #include <iostream>
00023 
00024 LinearRangeMapper::LinearRangeMapper(int minpos, int maxpos,
00025                                      float minval, float maxval,
00026                                      QString unit, bool inverted) :
00027     m_minpos(minpos),
00028     m_maxpos(maxpos),
00029     m_minval(minval),
00030     m_maxval(maxval),
00031     m_unit(unit),
00032     m_inverted(inverted)
00033 {
00034     assert(m_maxval != m_minval);
00035     assert(m_maxpos != m_minpos);
00036 }
00037 
00038 int
00039 LinearRangeMapper::getPositionForValue(float value) const
00040 {
00041     int position = getPositionForValueUnclamped(value);
00042     if (position < m_minpos) position = m_minpos;
00043     if (position > m_maxpos) position = m_maxpos;
00044     return position;
00045 }
00046 
00047 int
00048 LinearRangeMapper::getPositionForValueUnclamped(float value) const
00049 {
00050     int position = m_minpos +
00051         lrintf(((value - m_minval) / (m_maxval - m_minval))
00052                * (m_maxpos - m_minpos));
00053     if (m_inverted) return m_maxpos - (position - m_minpos);
00054     else return position;
00055 }
00056 
00057 float
00058 LinearRangeMapper::getValueForPosition(int position) const
00059 {
00060     if (position < m_minpos) position = m_minpos;
00061     if (position > m_maxpos) position = m_maxpos;
00062     float value = getValueForPositionUnclamped(position);
00063     return value;
00064 }
00065 
00066 float
00067 LinearRangeMapper::getValueForPositionUnclamped(int position) const
00068 {
00069     if (m_inverted) position = m_maxpos - (position - m_minpos);
00070     float value = m_minval +
00071         ((float(position - m_minpos) / float(m_maxpos - m_minpos))
00072          * (m_maxval - m_minval));
00073     return value;
00074 }
00075 
00076 LogRangeMapper::LogRangeMapper(int minpos, int maxpos,
00077                                float minval, float maxval,
00078                                QString unit, bool inverted) :
00079     m_minpos(minpos),
00080     m_maxpos(maxpos),
00081     m_unit(unit),
00082     m_inverted(inverted)
00083 {
00084     convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio);
00085 
00086 //    cerr << "LogRangeMapper: minpos " << minpos << ", maxpos "
00087 //              << maxpos << ", minval " << minval << ", maxval "
00088 //              << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio
00089 //              << ", unit " << unit << endl;
00090 
00091     assert(m_maxpos != m_minpos);
00092 
00093     m_maxlog = (m_maxpos - m_minpos) / m_ratio + m_minlog;
00094 
00095 //    cerr << "LogRangeMapper: maxlog = " << m_maxlog << endl;
00096 }
00097 
00098 void
00099 LogRangeMapper::convertMinMax(int minpos, int maxpos,
00100                               float minval, float maxval,
00101                               float &minlog, float &ratio)
00102 {
00103     static float thresh = powf(10, -10);
00104     if (minval < thresh) minval = thresh;
00105     minlog = log10f(minval);
00106     ratio = (maxpos - minpos) / (log10f(maxval) - minlog);
00107 }
00108 
00109 void
00110 LogRangeMapper::convertRatioMinLog(float ratio, float minlog,
00111                                    int minpos, int maxpos,
00112                                    float &minval, float &maxval)
00113 {
00114     minval = powf(10, minlog);
00115     maxval = powf(10, (maxpos - minpos) / ratio + minlog);
00116 }
00117 
00118 int
00119 LogRangeMapper::getPositionForValue(float value) const
00120 {
00121     int position = getPositionForValueUnclamped(value);
00122     if (position < m_minpos) position = m_minpos;
00123     if (position > m_maxpos) position = m_maxpos;
00124     return position;
00125 }
00126 
00127 int
00128 LogRangeMapper::getPositionForValueUnclamped(float value) const
00129 {
00130     static float thresh = powf(10, -10);
00131     if (value < thresh) value = thresh;
00132     int position = lrintf((log10(value) - m_minlog) * m_ratio) + m_minpos;
00133     if (m_inverted) return m_maxpos - (position - m_minpos);
00134     else return position;
00135 }
00136 
00137 float
00138 LogRangeMapper::getValueForPosition(int position) const
00139 {
00140     if (position < m_minpos) position = m_minpos;
00141     if (position > m_maxpos) position = m_maxpos;
00142     float value = getValueForPositionUnclamped(position);
00143     return value;
00144 }
00145 
00146 float
00147 LogRangeMapper::getValueForPositionUnclamped(int position) const
00148 {
00149     if (m_inverted) position = m_maxpos - (position - m_minpos);
00150     float value = powf(10, (position - m_minpos) / m_ratio + m_minlog);
00151     return value;
00152 }
00153 
00154 InterpolatingRangeMapper::InterpolatingRangeMapper(CoordMap pointMappings,
00155                                                    QString unit) :
00156     m_mappings(pointMappings),
00157     m_unit(unit)
00158 {
00159     for (CoordMap::const_iterator i = m_mappings.begin(); 
00160          i != m_mappings.end(); ++i) {
00161         m_reverse[i->second] = i->first;
00162     }
00163 }
00164 
00165 int
00166 InterpolatingRangeMapper::getPositionForValue(float value) const
00167 {
00168     int pos = getPositionForValueUnclamped(value);
00169     CoordMap::const_iterator i = m_mappings.begin();
00170     if (pos < i->second) pos = i->second;
00171     i = m_mappings.end(); --i;
00172     if (pos > i->second) pos = i->second;
00173     return pos;
00174 }
00175 
00176 int
00177 InterpolatingRangeMapper::getPositionForValueUnclamped(float value) const
00178 {
00179     float p = interpolate(&m_mappings, value);
00180     return lrintf(p);
00181 }
00182 
00183 float
00184 InterpolatingRangeMapper::getValueForPosition(int position) const
00185 {
00186     float val = getValueForPositionUnclamped(position);
00187     CoordMap::const_iterator i = m_mappings.begin();
00188     if (val < i->first) val = i->first;
00189     i = m_mappings.end(); --i;
00190     if (val > i->first) val = i->first;
00191     return val;
00192 }
00193 
00194 float
00195 InterpolatingRangeMapper::getValueForPositionUnclamped(int position) const
00196 {
00197     return interpolate(&m_reverse, position);
00198 }
00199 
00200 template <typename T>
00201 float
00202 InterpolatingRangeMapper::interpolate(T *mapping, float value) const
00203 {
00204     // lower_bound: first element which does not compare less than value
00205     typename T::const_iterator i = mapping->lower_bound(value);
00206 
00207     if (i == mapping->begin()) {
00208         // value is less than or equal to first element, so use the
00209         // gradient from first to second and extend it
00210         ++i;
00211     }
00212 
00213     if (i == mapping->end()) {
00214         // value is off the end, so use the gradient from penultimate
00215         // to ultimate and extend it
00216         --i;
00217     }
00218 
00219     typename T::const_iterator j = i;
00220     --j;
00221 
00222     float gradient = float(i->second - j->second) / float(i->first - j->first);
00223 
00224     return j->second + (value - j->first) * gradient;
00225 }
00226 
00227 AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings,
00228                                  QString unit) :
00229     m_mappings(pointMappings),
00230     m_unit(unit)
00231 {
00232     m_type = chooseMappingTypeFor(m_mappings);
00233 
00234     CoordMap::const_iterator first = m_mappings.begin();
00235     CoordMap::const_iterator last = m_mappings.end();
00236     --last;
00237 
00238     switch (m_type) {
00239     case StraightLine:
00240         m_mapper = new LinearRangeMapper(first->second, last->second,
00241                                          first->first, last->first,
00242                                          unit, false);
00243         break;
00244     case Logarithmic:
00245         m_mapper = new LogRangeMapper(first->second, last->second,
00246                                       first->first, last->first,
00247                                       unit, false);
00248         break;
00249     case Interpolating:
00250         m_mapper = new InterpolatingRangeMapper(m_mappings, unit);
00251         break;
00252     }
00253 }
00254 
00255 AutoRangeMapper::~AutoRangeMapper()
00256 {
00257     delete m_mapper;
00258 }
00259 
00260 AutoRangeMapper::MappingType
00261 AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings)
00262 {
00263     // how do we work out whether a linear/log mapping is "close enough"?
00264 
00265     CoordMap::const_iterator first = mappings.begin();
00266     CoordMap::const_iterator last = mappings.end();
00267     --last;
00268 
00269     LinearRangeMapper linm(first->second, last->second,
00270                            first->first, last->first,
00271                            "", false);
00272 
00273     bool inadequate = false;
00274 
00275     for (CoordMap::const_iterator i = mappings.begin();
00276          i != mappings.end(); ++i) {
00277         int candidate = linm.getPositionForValue(i->first);
00278         int diff = candidate - i->second;
00279         if (diff < 0) diff = -diff;
00280         if (diff > 1) {
00281 //            cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
00282 //                 << ", straight-line mapping inadequate" << endl;
00283             inadequate = true;
00284             break;
00285         }
00286     }
00287 
00288     if (!inadequate) {
00289         return StraightLine;
00290     }
00291 
00292     LogRangeMapper logm(first->second, last->second,
00293                         first->first, last->first,
00294                         "", false);
00295 
00296     inadequate = false;
00297 
00298     for (CoordMap::const_iterator i = mappings.begin();
00299          i != mappings.end(); ++i) {
00300         int candidate = logm.getPositionForValue(i->first);
00301         int diff = candidate - i->second;
00302         if (diff < 0) diff = -diff;
00303         if (diff > 1) {
00304 //            cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
00305 //                 << ", log mapping inadequate" << endl;
00306             inadequate = true;
00307             break;
00308         }
00309     }
00310 
00311     if (!inadequate) {
00312         return Logarithmic;
00313     }
00314 
00315     return Interpolating;
00316 }
00317 
00318 int
00319 AutoRangeMapper::getPositionForValue(float value) const
00320 {
00321     return m_mapper->getPositionForValue(value);
00322 }
00323 
00324 float
00325 AutoRangeMapper::getValueForPosition(int position) const
00326 {
00327     return m_mapper->getValueForPosition(position);
00328 }
00329 
00330 int
00331 AutoRangeMapper::getPositionForValueUnclamped(float value) const
00332 {
00333     return m_mapper->getPositionForValueUnclamped(value);
00334 }
00335 
00336 float
00337 AutoRangeMapper::getValueForPositionUnclamped(int position) const
00338 {
00339     return m_mapper->getValueForPositionUnclamped(position);
00340 }