1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 2 3 /* 4 Sonic Visualiser 5 An audio file viewer and annotation editor. 6 Centre for Digital Music, Queen Mary, University of London. 7 This file copyright 2006 QMUL. 8 9 This program is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public License as 11 published by the Free Software Foundation; either version 2 of the 12 License, or (at your option) any later version. See the file 13 COPYING included with this distribution for more information. 14 */ 15 16 #ifndef SV_RANGE_MAPPER_H 17 #define SV_RANGE_MAPPER_H 18 19 #include <QString> 20 21 #include "Debug.h" 22 #include <map> 23 24 class RangeMapper 25 { 26 public: 27 virtual ~RangeMapper() { } 28 29 /** 30 * Return the position that maps to the given value, rounding to 31 * the nearest position and clamping to the minimum and maximum 32 * extents of the mapper's positional range. 33 */ 34 virtual int getPositionForValue(double value) const = 0; 35 36 /** 37 * Return the position that maps to the given value, rounding to 38 * the nearest position, without clamping. That is, whatever 39 * mapping function is in use will be projected even outside the 40 * minimum and maximum extents of the mapper's positional 41 * range. (The mapping outside that range is not guaranteed to be 42 * exact, except if the mapper is a linear one.) 43 */ 44 virtual int getPositionForValueUnclamped(double value) const = 0; 45 46 /** 47 * Return the value mapped from the given position, clamping to 48 * the minimum and maximum extents of the mapper's value range. 49 */ 50 virtual double getValueForPosition(int position) const = 0; 51 52 /** 53 * Return the value mapped from the given position, without 54 * clamping. That is, whatever mapping function is in use will be 55 * projected even outside the minimum and maximum extents of the 56 * mapper's value range. (The mapping outside that range is not 57 * guaranteed to be exact, except if the mapper is a linear one.) 58 */ 59 virtual double getValueForPositionUnclamped(int position) const = 0; 60 61 /** 62 * Get the unit of the mapper's value range. 63 */ 64 virtual QString getUnit() const { return ""; } 65 66 /** 67 * The mapper may optionally provide special labels for one or 68 * more individual positions (such as the minimum position, the 69 * default, or indeed all positions). These should be used in any 70 * display context in preference to just showing the numerical 71 * value for the position. If a position has such a label, return 72 * it here. 73 */ 74 virtual QString getLabel(int /* position */) const { return ""; } 75 }; 76 77 78 class LinearRangeMapper : public RangeMapper 79 { 80 public: 81 /** 82 * Map values in range minval->maxval linearly into integer range 83 * minpos->maxpos. minval and minpos must be less than maxval and 84 * maxpos respectively. If inverted is true, the range will be 85 * mapped "backwards" (minval to maxpos and maxval to minpos). 86 */ 87 LinearRangeMapper(int minpos, int maxpos, 88 double minval, double maxval, 89 QString unit = "", bool inverted = false, 90 std::map<int, QString> labels = {}); 91 92 int getPositionForValue(double value) const override; 93 int getPositionForValueUnclamped(double value) const override; 94 95 double getValueForPosition(int position) const override; 96 double getValueForPositionUnclamped(int position) const override; 97 98 QString getUnit() const override { return m_unit; } 99 QString getLabel(int position) const override; 100 101 protected: 102 int m_minpos; 103 int m_maxpos; 104 double m_minval; 105 double m_maxval; 106 QString m_unit; 107 bool m_inverted; 108 std::map<int, QString> m_labels; 109 }; 110 111 class LogRangeMapper : public RangeMapper 112 { 113 public: 114 /** 115 * Map values in range minval->maxval into integer range 116 * minpos->maxpos such that logs of the values are mapped 117 * linearly. minval must be greater than zero, and minval and 118 * minpos must be less than maxval and maxpos respectively. If 119 * inverted is true, the range will be mapped "backwards" (minval 120 * to maxpos and maxval to minpos). 121 */ 122 LogRangeMapper(int minpos, int maxpos, 123 double minval, double maxval, 124 QString m_unit = "", bool inverted = false); 125 126 static void convertRatioMinLog(double ratio, double minlog, 127 int minpos, int maxpos, 128 double &minval, double &maxval); 129 130 static void convertMinMax(int minpos, int maxpos, 131 double minval, double maxval, 132 double &minlog, double &ratio); 133 134 int getPositionForValue(double value) const override; 135 int getPositionForValueUnclamped(double value) const override; 136 137 double getValueForPosition(int position) const override; 138 double getValueForPositionUnclamped(int position) const override; 139 140 QString getUnit() const override { return m_unit; } 141 142 protected: 143 int m_minpos; 144 int m_maxpos; 145 double m_ratio; 146 double m_minlog; 147 double m_maxlog; 148 QString m_unit; 149 bool m_inverted; 150 }; 151 152 class InterpolatingRangeMapper : public RangeMapper 153 { 154 public: 155 typedef std::map<double, int> CoordMap; 156 157 /** 158 * Given a series of (value, position) coordinate mappings, 159 * construct a range mapper that maps arbitrary values, in the 160 * range between minimum and maximum of the provided values, onto 161 * coordinates using linear interpolation between the supplied 162 * points. 163 * 164 *!!! todo: Cubic -- more generally useful than linear interpolation 165 *!!! todo: inverted flag 166 * 167 * The set of provided mappings must contain at least two 168 * coordinates. 169 * 170 * It is expected that the values and positions in the coordinate 171 * mappings will both be monotonically increasing (i.e. no 172 * inflections in the mapping curve). Behaviour is undefined if 173 * this is not the case. 174 */ 175 InterpolatingRangeMapper(CoordMap pointMappings, 176 QString unit); 177 178 int getPositionForValue(double value) const override; 179 int getPositionForValueUnclamped(double value) const override; 180 181 double getValueForPosition(int position) const override; 182 double getValueForPositionUnclamped(int position) const override; 183 184 QString getUnit() const override { return m_unit; } 185 186 protected: 187 CoordMap m_mappings; 188 std::map<int, double> m_reverse; 189 QString m_unit; 190 191 template <typename T> 192 double interpolate(T *mapping, double v) const; 193 }; 194 195 class AutoRangeMapper : public RangeMapper 196 { 197 public: 198 enum MappingType { 199 Interpolating, 200 StraightLine, 201 Logarithmic, 202 }; 203 204 typedef std::map<double, int> CoordMap; 205 206 /** 207 * Given a series of (value, position) coordinate mappings, 208 * construct a range mapper that maps arbitrary values, in the 209 * range between minimum and maximum of the provided values, onto 210 * coordinates. 211 * 212 * The mapping used may be 213 * 214 * Interpolating -- an InterpolatingRangeMapper will be used 215 * 216 * StraightLine -- a LinearRangeMapper from the minimum to 217 * maximum value coordinates will be used, ignoring all other 218 * supplied coordinate mappings 219 * 220 * Logarithmic -- a LogRangeMapper from the minimum to 221 * maximum value coordinates will be used, ignoring all other 222 * supplied coordinate mappings 223 * 224 * The mapping will be chosen automatically by looking at the 225 * supplied coordinates. If the supplied coordinates fall on a 226 * straight line, a StraightLine mapping will be used; if they 227 * fall on a log curve, a Logarithmic mapping will be used; 228 * otherwise an Interpolating mapping will be used. 229 * 230 *!!! todo: inverted flag 231 * 232 * The set of provided mappings must contain at least two 233 * coordinates, or at least three if the points are not supposed 234 * to be in a straight line. 235 * 236 * It is expected that the values and positions in the coordinate 237 * mappings will both be monotonically increasing (i.e. no 238 * inflections in the mapping curve). Behaviour is undefined if 239 * this is not the case. 240 */ 241 AutoRangeMapper(CoordMap pointMappings, 242 QString unit); 243 244 ~AutoRangeMapper(); 245 246 /** 247 * Return the mapping type in use. 248 */ 249 MappingType getType() const { return m_type; } 250 251 int getPositionForValue(double value) const override; 252 int getPositionForValueUnclamped(double value) const override; 253 254 double getValueForPosition(int position) const override; 255 double getValueForPositionUnclamped(int position) const override; 256 257 QString getUnit() const override { return m_unit; } 258 259 protected: 260 MappingType m_type; 261 CoordMap m_mappings; 262 QString m_unit; 263 RangeMapper *m_mapper; 264 265 MappingType chooseMappingTypeFor(const CoordMap &); 266 }; 267 268 #endif 269