# HG changeset patch # User Chris Cannam # Date 1391700676 0 # Node ID 12a6140b3ae00bcfbadbd2aad9ef5bf46a9f9381 # Parent 633a8fa622c916b8dca0caf0ab39a52203632e44 Add unclamped range mapper methods diff -r 633a8fa622c9 -r 12a6140b3ae0 base/RangeMapper.cpp --- a/base/RangeMapper.cpp Thu Feb 06 10:14:59 2014 +0000 +++ b/base/RangeMapper.cpp Thu Feb 06 15:31:16 2014 +0000 @@ -38,11 +38,18 @@ int LinearRangeMapper::getPositionForValue(float value) const { + int position = getPositionForValueUnclamped(value); + if (position < m_minpos) position = m_minpos; + if (position > m_maxpos) position = m_maxpos; + return position; +} + +int +LinearRangeMapper::getPositionForValueUnclamped(float value) const +{ int position = m_minpos + lrintf(((value - m_minval) / (m_maxval - m_minval)) * (m_maxpos - m_minpos)); - if (position < m_minpos) position = m_minpos; - if (position > m_maxpos) position = m_maxpos; if (m_inverted) return m_maxpos - (position - m_minpos); else return position; } @@ -50,9 +57,16 @@ float LinearRangeMapper::getValueForPosition(int position) const { - if (m_inverted) position = m_maxpos - (position - m_minpos); if (position < m_minpos) position = m_minpos; if (position > m_maxpos) position = m_maxpos; + float value = getValueForPositionUnclamped(position); + return value; +} + +float +LinearRangeMapper::getValueForPositionUnclamped(int position) const +{ + if (m_inverted) position = m_maxpos - (position - m_minpos); float value = m_minval + ((float(position - m_minpos) / float(m_maxpos - m_minpos)) * (m_maxval - m_minval)); @@ -104,11 +118,18 @@ int LogRangeMapper::getPositionForValue(float value) const { - int position = lrintf((log10(value) - m_minlog) * m_ratio) + m_minpos; + int position = getPositionForValueUnclamped(value); if (position < m_minpos) position = m_minpos; if (position > m_maxpos) position = m_maxpos; -// cerr << "LogRangeMapper::getPositionForValue: " << value << " -> " -// << position << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << endl; + return position; +} + +int +LogRangeMapper::getPositionForValueUnclamped(float value) const +{ + static float thresh = powf(10, -10); + if (value < thresh) value = thresh; + int position = lrintf((log10(value) - m_minlog) * m_ratio) + m_minpos; if (m_inverted) return m_maxpos - (position - m_minpos); else return position; } @@ -116,12 +137,17 @@ float LogRangeMapper::getValueForPosition(int position) const { - if (m_inverted) position = m_maxpos - (position - m_minpos); if (position < m_minpos) position = m_minpos; if (position > m_maxpos) position = m_maxpos; + float value = getValueForPositionUnclamped(position); + return value; +} + +float +LogRangeMapper::getValueForPositionUnclamped(int position) const +{ + if (m_inverted) position = m_maxpos - (position - m_minpos); float value = powf(10, (position - m_minpos) / m_ratio + m_minlog); -// cerr << "LogRangeMapper::getValueForPosition: " << position << " -> " -// << value << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << endl; return value; } @@ -139,31 +165,63 @@ int InterpolatingRangeMapper::getPositionForValue(float value) const { - CoordMap::const_iterator i = m_mappings.lower_bound(value); - CoordMap::const_iterator j = i; - --j; + int pos = getPositionForValueUnclamped(value); + CoordMap::const_iterator i = m_mappings.begin(); + if (pos < i->second) pos = i->second; + i = m_mappings.end(); --i; + if (pos > i->second) pos = i->second; + return pos; +} - 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); +int +InterpolatingRangeMapper::getPositionForValueUnclamped(float value) const +{ + float p = interpolate(&m_mappings, value); + return lrintf(p); } float InterpolatingRangeMapper::getValueForPosition(int position) const { - std::map::const_iterator i = m_reverse.lower_bound(position); - std::map::const_iterator j = i; + float val = getValueForPositionUnclamped(position); + CoordMap::const_iterator i = m_mappings.begin(); + if (val < i->first) val = i->first; + i = m_mappings.end(); --i; + if (val > i->first) val = i->first; + return val; +} + +float +InterpolatingRangeMapper::getValueForPositionUnclamped(int position) const +{ + return interpolate(&m_reverse, position); +} + +template +float +InterpolatingRangeMapper::interpolate(T *mapping, float value) const +{ + // lower_bound: first element which does not compare less than value + typename T::const_iterator i = mapping->lower_bound(value); + + if (i == mapping->begin()) { + // value is less than or equal to first element, so use the + // gradient from first to second and extend it + ++i; + } + + if (i == mapping->end()) { + // value is off the end, so use the gradient from penultimate + // to ultimate and extend it + --i; + } + + typename T::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; + float gradient = float(i->second - j->second) / float(i->first - j->first); - return ((float(position) - j->first) / (i->first - j->first)) * - (i->second - j->second) + j->second; + return j->second + (value - j->first) * gradient; } AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings, @@ -220,8 +278,8 @@ int diff = candidate - i->second; if (diff < 0) diff = -diff; if (diff > 1) { - cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff - << ", straight-line mapping inadequate" << endl; +// cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff +// << ", straight-line mapping inadequate" << endl; inadequate = true; break; } @@ -243,8 +301,8 @@ int diff = candidate - i->second; if (diff < 0) diff = -diff; if (diff > 1) { - cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff - << ", log mapping inadequate" << endl; +// cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff +// << ", log mapping inadequate" << endl; inadequate = true; break; } @@ -268,3 +326,15 @@ { return m_mapper->getValueForPosition(position); } + +int +AutoRangeMapper::getPositionForValueUnclamped(float value) const +{ + return m_mapper->getPositionForValueUnclamped(value); +} + +float +AutoRangeMapper::getValueForPositionUnclamped(int position) const +{ + return m_mapper->getValueForPositionUnclamped(position); +} diff -r 633a8fa622c9 -r 12a6140b3ae0 base/RangeMapper.h --- a/base/RangeMapper.h Thu Feb 06 10:14:59 2014 +0000 +++ b/base/RangeMapper.h Thu Feb 06 15:31:16 2014 +0000 @@ -25,8 +25,42 @@ { public: virtual ~RangeMapper() { } + + /** + * Return the position that maps to the given value, rounding to + * the nearest position and clamping to the minimum and maximum + * extents of the mapper's positional range. + */ virtual int getPositionForValue(float value) const = 0; + + /** + * Return the position that maps to the given value, rounding to + * the nearest position, without clamping. That is, whatever + * mapping function is in use will be projected even outside the + * minimum and maximum extents of the mapper's positional + * range. (The mapping outside that range is not guaranteed to be + * exact, except if the mapper is a linear one.) + */ + virtual int getPositionForValueUnclamped(float value) const = 0; + + /** + * Return the value mapped from the given position, clamping to + * the minimum and maximum extents of the mapper's value range. + */ virtual float getValueForPosition(int position) const = 0; + + /** + * Return the value mapped from the given positionq, without + * clamping. That is, whatever mapping function is in use will be + * projected even outside the minimum and maximum extents of the + * mapper's value range. (The mapping outside that range is not + * guaranteed to be exact, except if the mapper is a linear one.) + */ + virtual float getValueForPositionUnclamped(int position) const = 0; + + /** + * Get the unit of the mapper's value range. + */ virtual QString getUnit() const { return ""; } }; @@ -45,7 +79,10 @@ QString unit = "", bool inverted = false); virtual int getPositionForValue(float value) const; + virtual int getPositionForValueUnclamped(float value) const; + virtual float getValueForPosition(int position) const; + virtual float getValueForPositionUnclamped(int position) const; virtual QString getUnit() const { return m_unit; } @@ -64,9 +101,10 @@ /** * Map values in range minval->maxval into integer range * minpos->maxpos such that logs of the values are mapped - * linearly. minval and minpos must be less than maxval and maxpos - * respectively. If inverted is true, the range will be mapped - * "backwards" (minval to maxpos and maxval to minpos). + * linearly. minval must be greater than zero, and minval and + * minpos must be less than maxval and maxpos respectively. If + * inverted is true, the range will be mapped "backwards" (minval + * to maxpos and maxval to minpos). */ LogRangeMapper(int minpos, int maxpos, float minval, float maxval, @@ -81,7 +119,10 @@ float &ratio, float &minlog); virtual int getPositionForValue(float value) const; + virtual int getPositionForValueUnclamped(float value) const; + virtual float getValueForPosition(int position) const; + virtual float getValueForPositionUnclamped(int position) const; virtual QString getUnit() const { return m_unit; } @@ -122,7 +163,10 @@ QString unit); virtual int getPositionForValue(float value) const; + virtual int getPositionForValueUnclamped(float value) const; + virtual float getValueForPosition(int position) const; + virtual float getValueForPositionUnclamped(int position) const; virtual QString getUnit() const { return m_unit; } @@ -130,6 +174,9 @@ CoordMap m_mappings; std::map m_reverse; QString m_unit; + + template + float interpolate(T *mapping, float v) const; }; class AutoRangeMapper : public RangeMapper @@ -189,7 +236,10 @@ MappingType getType() const { return m_type; } virtual int getPositionForValue(float value) const; + virtual int getPositionForValueUnclamped(float value) const; + virtual float getValueForPosition(int position) const; + virtual float getValueForPositionUnclamped(int position) const; virtual QString getUnit() const { return m_unit; } diff -r 633a8fa622c9 -r 12a6140b3ae0 base/test/TestRangeMapper.h --- a/base/test/TestRangeMapper.h Thu Feb 06 10:14:59 2014 +0000 +++ b/base/test/TestRangeMapper.h Thu Feb 06 15:31:16 2014 +0000 @@ -42,6 +42,10 @@ QCOMPARE(rm.getPositionForValue(0.2), 1); QCOMPARE(rm.getPositionForValue(-12), 1); QCOMPARE(rm.getPositionForValue(6.1), 8); + QCOMPARE(rm.getPositionForValueUnclamped(3.0), 6); + QCOMPARE(rm.getPositionForValueUnclamped(0.2), 0); + QCOMPARE(rm.getPositionForValueUnclamped(-12), -24); + QCOMPARE(rm.getPositionForValueUnclamped(6.1), 12); } void linearDownForward() @@ -56,6 +60,10 @@ QCOMPARE(rm.getPositionForValue(0.2), 8); QCOMPARE(rm.getPositionForValue(-12), 8); QCOMPARE(rm.getPositionForValue(6.1), 1); + QCOMPARE(rm.getPositionForValueUnclamped(3.0), 3); + QCOMPARE(rm.getPositionForValueUnclamped(0.2), 9); + QCOMPARE(rm.getPositionForValueUnclamped(-12), 33); + QCOMPARE(rm.getPositionForValueUnclamped(6.1), -3); } void linearUpBackward() @@ -68,6 +76,10 @@ QCOMPARE(rm.getValueForPosition(7), 3.5); QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(1)); QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(8)); + QCOMPARE(rm.getValueForPositionUnclamped(6), 3.0); + QCOMPARE(rm.getValueForPositionUnclamped(0), 0.0); + QCOMPARE(rm.getValueForPositionUnclamped(-24), -12.0); + QCOMPARE(rm.getValueForPositionUnclamped(12), 6.0); } void linearDownBackward() @@ -80,6 +92,10 @@ QCOMPARE(rm.getValueForPosition(2), 3.5); QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(1)); QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(8)); + QCOMPARE(rm.getValueForPositionUnclamped(3), 3.0); + QCOMPARE(rm.getValueForPositionUnclamped(9), 0.0); + QCOMPARE(rm.getValueForPositionUnclamped(33), -12.0); + QCOMPARE(rm.getValueForPositionUnclamped(-3), 6.0); } void logUpForward() @@ -93,6 +109,9 @@ QCOMPARE(rm.getPositionForValue(1000.0), 5); QCOMPARE(rm.getPositionForValue(900.0), 5); QCOMPARE(rm.getPositionForValue(20000), 6); + QCOMPARE(rm.getPositionForValueUnclamped(1.0), 2); + QCOMPARE(rm.getPositionForValueUnclamped(1000000.0), 8); + QCOMPARE(rm.getPositionForValueUnclamped(1000.0), 5); } void logDownForward() @@ -106,6 +125,9 @@ QCOMPARE(rm.getPositionForValue(1000.0), 5); QCOMPARE(rm.getPositionForValue(900.0), 5); QCOMPARE(rm.getPositionForValue(20000), 4); + QCOMPARE(rm.getPositionForValueUnclamped(1.0), 8); + QCOMPARE(rm.getPositionForValueUnclamped(1000000.0), 2); + QCOMPARE(rm.getPositionForValueUnclamped(1000.0), 5); } void logUpBackward() @@ -118,6 +140,9 @@ QCOMPARE(rm.getValueForPosition(6), 10000.0); QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(3)); QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(7)); + QCOMPARE(rm.getValueForPositionUnclamped(2), 1.0); + QCOMPARE(rm.getValueForPositionUnclamped(8), 1000000.0); + QCOMPARE(rm.getValueForPositionUnclamped(5), 1000.0); } void logDownBackward() @@ -130,6 +155,9 @@ QCOMPARE(rm.getValueForPosition(4), 10000.0); QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(3)); QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(7)); + QCOMPARE(rm.getValueForPositionUnclamped(8), 1.0); + QCOMPARE(rm.getValueForPositionUnclamped(2), 1000000.0); + QCOMPARE(rm.getValueForPositionUnclamped(5), 1000.0); } void interpolatingForward() @@ -147,6 +175,9 @@ QCOMPARE(rm.getPositionForValue(3.0), 30); QCOMPARE(rm.getPositionForValue(2.5), 25); QCOMPARE(rm.getPositionForValue(4.5), 60); + QCOMPARE(rm.getPositionForValueUnclamped(0.0), 0); + QCOMPARE(rm.getPositionForValueUnclamped(2.5), 25); + QCOMPARE(rm.getPositionForValueUnclamped(6.0), 90); } void interpolatingBackward() @@ -174,10 +205,15 @@ AutoRangeMapper rm1(mappings, "x"); QCOMPARE(rm1.getUnit(), QString("x")); QCOMPARE(rm1.getType(), AutoRangeMapper::StraightLine); + QCOMPARE(rm1.getPositionForValue(0.1), 1); QCOMPARE(rm1.getPositionForValue(0.5), 1); QCOMPARE(rm1.getPositionForValue(4.0), 8); + QCOMPARE(rm1.getPositionForValue(4.5), 8); QCOMPARE(rm1.getPositionForValue(3.0), 6); QCOMPARE(rm1.getPositionForValue(3.1), 6); + QCOMPARE(rm1.getPositionForValueUnclamped(0.1), 0); + QCOMPARE(rm1.getPositionForValueUnclamped(3.1), 6); + QCOMPARE(rm1.getPositionForValueUnclamped(4.5), 9); mappings[3.0] = 6; mappings[3.5] = 7; AutoRangeMapper rm2(mappings, "x"); @@ -205,6 +241,9 @@ QCOMPARE(rm1.getPositionForValue(1000.0), 5); QCOMPARE(rm1.getPositionForValue(900.0), 5); QCOMPARE(rm1.getPositionForValue(20000), 6); + QCOMPARE(rm1.getPositionForValueUnclamped(1.0), 2); + QCOMPARE(rm1.getPositionForValueUnclamped(900.0), 5); + QCOMPARE(rm1.getPositionForValueUnclamped(1000000.0), 8); mappings[100] = 4; AutoRangeMapper rm2(mappings, "x"); QCOMPARE(rm2.getUnit(), QString("x")); @@ -234,6 +273,9 @@ QCOMPARE(rm.getPositionForValue(3.0), 30); QCOMPARE(rm.getPositionForValue(2.5), 25); QCOMPARE(rm.getPositionForValue(4.5), 60); + QCOMPARE(rm.getPositionForValueUnclamped(0.0), 0); + QCOMPARE(rm.getPositionForValueUnclamped(5.0), 70); + QCOMPARE(rm.getPositionForValueUnclamped(6.0), 90); } };