svcore
1.9
|
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 }