Chris@189: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@189: Chris@189: /* Chris@189: Sonic Visualiser Chris@189: An audio file viewer and annotation editor. Chris@189: Centre for Digital Music, Queen Mary, University of London. Chris@202: This file copyright 2006 QMUL. Chris@189: Chris@189: This program is free software; you can redistribute it and/or Chris@189: modify it under the terms of the GNU General Public License as Chris@189: published by the Free Software Foundation; either version 2 of the Chris@189: License, or (at your option) any later version. See the file Chris@189: COPYING included with this distribution for more information. Chris@189: */ Chris@189: Chris@189: #ifndef _RANGE_MAPPER_H_ Chris@189: #define _RANGE_MAPPER_H_ Chris@189: Chris@189: #include Chris@189: Chris@686: #include "Debug.h" Chris@880: #include Chris@189: Chris@189: class RangeMapper Chris@189: { Chris@189: public: Chris@189: virtual ~RangeMapper() { } Chris@885: Chris@885: /** Chris@885: * Return the position that maps to the given value, rounding to Chris@885: * the nearest position and clamping to the minimum and maximum Chris@885: * extents of the mapper's positional range. Chris@885: */ Chris@1038: virtual int getPositionForValue(double value) const = 0; Chris@885: Chris@885: /** Chris@885: * Return the position that maps to the given value, rounding to Chris@885: * the nearest position, without clamping. That is, whatever Chris@885: * mapping function is in use will be projected even outside the Chris@885: * minimum and maximum extents of the mapper's positional Chris@885: * range. (The mapping outside that range is not guaranteed to be Chris@885: * exact, except if the mapper is a linear one.) Chris@885: */ Chris@1038: virtual int getPositionForValueUnclamped(double value) const = 0; Chris@885: Chris@885: /** Chris@885: * Return the value mapped from the given position, clamping to Chris@885: * the minimum and maximum extents of the mapper's value range. Chris@885: */ Chris@1038: virtual double getValueForPosition(int position) const = 0; Chris@885: Chris@885: /** Chris@885: * Return the value mapped from the given positionq, without Chris@885: * clamping. That is, whatever mapping function is in use will be Chris@885: * projected even outside the minimum and maximum extents of the Chris@885: * mapper's value range. (The mapping outside that range is not Chris@885: * guaranteed to be exact, except if the mapper is a linear one.) Chris@885: */ Chris@1038: virtual double getValueForPositionUnclamped(int position) const = 0; Chris@885: Chris@885: /** Chris@885: * Get the unit of the mapper's value range. Chris@885: */ Chris@189: virtual QString getUnit() const { return ""; } Chris@189: }; Chris@189: Chris@189: Chris@189: class LinearRangeMapper : public RangeMapper Chris@189: { Chris@189: public: Chris@879: /** Chris@879: * Map values in range minval->maxval linearly into integer range Chris@879: * minpos->maxpos. minval and minpos must be less than maxval and Chris@879: * maxpos respectively. If inverted is true, the range will be Chris@879: * mapped "backwards" (minval to maxpos and maxval to minpos). Chris@879: */ Chris@189: LinearRangeMapper(int minpos, int maxpos, Chris@1038: double minval, double maxval, Chris@464: QString unit = "", bool inverted = false); Chris@189: Chris@1038: virtual int getPositionForValue(double value) const; Chris@1038: virtual int getPositionForValueUnclamped(double value) const; Chris@885: Chris@1038: virtual double getValueForPosition(int position) const; Chris@1038: virtual double getValueForPositionUnclamped(int position) const; Chris@189: Chris@189: virtual QString getUnit() const { return m_unit; } Chris@189: Chris@189: protected: Chris@189: int m_minpos; Chris@189: int m_maxpos; Chris@1038: double m_minval; Chris@1038: double m_maxval; Chris@189: QString m_unit; Chris@464: bool m_inverted; Chris@189: }; Chris@189: Chris@189: class LogRangeMapper : public RangeMapper Chris@189: { Chris@189: public: Chris@880: /** Chris@880: * Map values in range minval->maxval into integer range Chris@880: * minpos->maxpos such that logs of the values are mapped Chris@885: * linearly. minval must be greater than zero, and minval and Chris@885: * minpos must be less than maxval and maxpos respectively. If Chris@885: * inverted is true, the range will be mapped "backwards" (minval Chris@885: * to maxpos and maxval to minpos). Chris@880: */ Chris@189: LogRangeMapper(int minpos, int maxpos, Chris@1038: double minval, double maxval, Chris@464: QString m_unit = "", bool inverted = false); Chris@189: Chris@1038: static void convertRatioMinLog(double ratio, double minlog, Chris@356: int minpos, int maxpos, Chris@1038: double &minval, double &maxval); Chris@356: Chris@356: static void convertMinMax(int minpos, int maxpos, Chris@1038: double minval, double maxval, Chris@1038: double &ratio, double &minlog); Chris@356: Chris@1038: virtual int getPositionForValue(double value) const; Chris@1038: virtual int getPositionForValueUnclamped(double value) const; Chris@885: Chris@1038: virtual double getValueForPosition(int position) const; Chris@1038: virtual double getValueForPositionUnclamped(int position) const; Chris@189: Chris@189: virtual QString getUnit() const { return m_unit; } Chris@189: Chris@189: protected: Chris@189: int m_minpos; Chris@189: int m_maxpos; Chris@1038: double m_ratio; Chris@1038: double m_minlog; Chris@1038: double m_maxlog; Chris@189: QString m_unit; Chris@464: bool m_inverted; Chris@189: }; Chris@189: Chris@880: class InterpolatingRangeMapper : public RangeMapper Chris@880: { Chris@880: public: Chris@1038: typedef std::map CoordMap; Chris@880: Chris@880: /** Chris@880: * Given a series of (value, position) coordinate mappings, Chris@880: * construct a range mapper that maps arbitrary values, in the Chris@880: * range between minimum and maximum of the provided values, onto Chris@880: * coordinates using linear interpolation between the supplied Chris@880: * points. Chris@880: * Chris@880: *!!! todo: Cubic -- more generally useful than linear interpolation Chris@880: *!!! todo: inverted flag Chris@880: * Chris@880: * The set of provided mappings must contain at least two Chris@880: * coordinates. Chris@880: * Chris@880: * It is expected that the values and positions in the coordinate Chris@880: * mappings will both be monotonically increasing (i.e. no Chris@880: * inflections in the mapping curve). Behaviour is undefined if Chris@880: * this is not the case. Chris@880: */ Chris@880: InterpolatingRangeMapper(CoordMap pointMappings, Chris@880: QString unit); Chris@880: Chris@1038: virtual int getPositionForValue(double value) const; Chris@1038: virtual int getPositionForValueUnclamped(double value) const; Chris@885: Chris@1038: virtual double getValueForPosition(int position) const; Chris@1038: virtual double getValueForPositionUnclamped(int position) const; Chris@880: Chris@880: virtual QString getUnit() const { return m_unit; } Chris@880: Chris@880: protected: Chris@880: CoordMap m_mappings; Chris@1038: std::map m_reverse; Chris@880: QString m_unit; Chris@885: Chris@885: template Chris@1038: double interpolate(T *mapping, double v) const; Chris@880: }; Chris@880: Chris@880: class AutoRangeMapper : public RangeMapper Chris@880: { Chris@880: public: Chris@880: enum MappingType { Chris@880: Interpolating, Chris@880: StraightLine, Chris@880: Logarithmic, Chris@880: }; Chris@880: Chris@1038: typedef std::map CoordMap; Chris@880: Chris@880: /** Chris@880: * Given a series of (value, position) coordinate mappings, Chris@880: * construct a range mapper that maps arbitrary values, in the Chris@880: * range between minimum and maximum of the provided values, onto Chris@880: * coordinates. Chris@880: * Chris@880: * The mapping used may be Chris@880: * Chris@880: * Interpolating -- an InterpolatingRangeMapper will be used Chris@880: * Chris@880: * StraightLine -- a LinearRangeMapper from the minimum to Chris@880: * maximum value coordinates will be used, ignoring all other Chris@880: * supplied coordinate mappings Chris@880: * Chris@880: * Logarithmic -- a LogRangeMapper from the minimum to Chris@880: * maximum value coordinates will be used, ignoring all other Chris@880: * supplied coordinate mappings Chris@880: * Chris@880: * The mapping will be chosen automatically by looking at the Chris@880: * supplied coordinates. If the supplied coordinates fall on a Chris@880: * straight line, a StraightLine mapping will be used; if they Chris@880: * fall on a log curve, a Logarithmic mapping will be used; Chris@880: * otherwise an Interpolating mapping will be used. Chris@880: * Chris@880: *!!! todo: inverted flag Chris@880: * Chris@880: * The set of provided mappings must contain at least two Chris@880: * coordinates, or at least three if the points are not supposed Chris@880: * to be in a straight line. Chris@880: * Chris@880: * It is expected that the values and positions in the coordinate Chris@880: * mappings will both be monotonically increasing (i.e. no Chris@880: * inflections in the mapping curve). Behaviour is undefined if Chris@880: * this is not the case. Chris@880: */ Chris@880: AutoRangeMapper(CoordMap pointMappings, Chris@880: QString unit); Chris@880: Chris@880: ~AutoRangeMapper(); Chris@880: Chris@880: /** Chris@880: * Return the mapping type in use. Chris@880: */ Chris@880: MappingType getType() const { return m_type; } Chris@880: Chris@1038: virtual int getPositionForValue(double value) const; Chris@1038: virtual int getPositionForValueUnclamped(double value) const; Chris@885: Chris@1038: virtual double getValueForPosition(int position) const; Chris@1038: virtual double getValueForPositionUnclamped(int position) const; Chris@880: Chris@880: virtual QString getUnit() const { return m_unit; } Chris@880: Chris@880: protected: Chris@880: MappingType m_type; Chris@880: CoordMap m_mappings; Chris@880: QString m_unit; Chris@880: RangeMapper *m_mapper; Chris@880: Chris@880: MappingType chooseMappingTypeFor(const CoordMap &); Chris@880: }; Chris@189: Chris@189: #endif