Mercurial > hg > svcore
diff base/RangeMapper.cpp @ 880:b4787b595db3
Implement and test the interpolating and auto range mappers
author | Chris Cannam |
---|---|
date | Fri, 31 Jan 2014 17:09:02 +0000 |
parents | eb6b6a88faed |
children | 12a6140b3ae0 |
line wrap: on
line diff
--- a/base/RangeMapper.cpp Fri Jan 31 13:39:37 2014 +0000 +++ b/base/RangeMapper.cpp Fri Jan 31 17:09:02 2014 +0000 @@ -125,3 +125,146 @@ return value; } +InterpolatingRangeMapper::InterpolatingRangeMapper(CoordMap pointMappings, + QString unit) : + m_mappings(pointMappings), + m_unit(unit) +{ + for (CoordMap::const_iterator i = m_mappings.begin(); + i != m_mappings.end(); ++i) { + m_reverse[i->second] = i->first; + } +} + +int +InterpolatingRangeMapper::getPositionForValue(float value) const +{ + CoordMap::const_iterator i = m_mappings.lower_bound(value); + CoordMap::const_iterator j = i; + --j; + + if (i == m_mappings.end()) return j->second; + if (i == m_mappings.begin()) return i->second; + if (i->first == value) return i->second; + + return lrintf(((value - j->first) / (i->first - j->first)) * + float(i->second - j->second) + j->second); +} + +float +InterpolatingRangeMapper::getValueForPosition(int position) const +{ + std::map<int, float>::const_iterator i = m_reverse.lower_bound(position); + std::map<int, float>::const_iterator j = i; + --j; + + if (i == m_reverse.end()) return j->second; + if (i == m_reverse.begin()) return i->second; + if (i->first == position) return i->second; + + return ((float(position) - j->first) / (i->first - j->first)) * + (i->second - j->second) + j->second; +} + +AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings, + QString unit) : + m_mappings(pointMappings), + m_unit(unit) +{ + m_type = chooseMappingTypeFor(m_mappings); + + CoordMap::const_iterator first = m_mappings.begin(); + CoordMap::const_iterator last = m_mappings.end(); + --last; + + switch (m_type) { + case StraightLine: + m_mapper = new LinearRangeMapper(first->second, last->second, + first->first, last->first, + unit, false); + break; + case Logarithmic: + m_mapper = new LogRangeMapper(first->second, last->second, + first->first, last->first, + unit, false); + break; + case Interpolating: + m_mapper = new InterpolatingRangeMapper(m_mappings, unit); + break; + } +} + +AutoRangeMapper::~AutoRangeMapper() +{ + delete m_mapper; +} + +AutoRangeMapper::MappingType +AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings) +{ + // how do we work out whether a linear/log mapping is "close enough"? + + CoordMap::const_iterator first = mappings.begin(); + CoordMap::const_iterator last = mappings.end(); + --last; + + LinearRangeMapper linm(first->second, last->second, + first->first, last->first, + "", false); + + bool inadequate = false; + + for (CoordMap::const_iterator i = mappings.begin(); + i != mappings.end(); ++i) { + int candidate = linm.getPositionForValue(i->first); + int diff = candidate - i->second; + if (diff < 0) diff = -diff; + if (diff > 1) { + cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff + << ", straight-line mapping inadequate" << endl; + inadequate = true; + break; + } + } + + if (!inadequate) { + return StraightLine; + } + + LogRangeMapper logm(first->second, last->second, + first->first, last->first, + "", false); + + inadequate = false; + + for (CoordMap::const_iterator i = mappings.begin(); + i != mappings.end(); ++i) { + int candidate = logm.getPositionForValue(i->first); + int diff = candidate - i->second; + if (diff < 0) diff = -diff; + if (diff > 1) { + cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff + << ", log mapping inadequate" << endl; + inadequate = true; + break; + } + } + + if (!inadequate) { + return Logarithmic; + } + + return Interpolating; +} + +int +AutoRangeMapper::getPositionForValue(float value) const +{ + return m_mapper->getPositionForValue(value); +} + +float +AutoRangeMapper::getValueForPosition(int position) const +{ + return m_mapper->getValueForPosition(position); +}