annotate base/RangeMapper.h @ 1459:3a128665fa6f horizontal-scale

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