annotate base/RangeMapper.h @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents ad5f892c0c4d
children
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@1581 16 #ifndef SV_RANGE_MAPPER_H
Chris@1581 17 #define SV_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@1580 92 int getPositionForValue(double value) const override;
Chris@1580 93 int getPositionForValueUnclamped(double value) const override;
Chris@885 94
Chris@1580 95 double getValueForPosition(int position) const override;
Chris@1580 96 double getValueForPositionUnclamped(int position) const override;
Chris@189 97
Chris@1580 98 QString getUnit() const override { return m_unit; }
Chris@1580 99 QString getLabel(int position) const override;
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@1580 134 int getPositionForValue(double value) const override;
Chris@1580 135 int getPositionForValueUnclamped(double value) const override;
Chris@885 136
Chris@1580 137 double getValueForPosition(int position) const override;
Chris@1580 138 double getValueForPositionUnclamped(int position) const override;
Chris@189 139
Chris@1580 140 QString getUnit() const override { 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@1580 178 int getPositionForValue(double value) const override;
Chris@1580 179 int getPositionForValueUnclamped(double value) const override;
Chris@885 180
Chris@1580 181 double getValueForPosition(int position) const override;
Chris@1580 182 double getValueForPositionUnclamped(int position) const override;
Chris@880 183
Chris@1580 184 QString getUnit() const override { 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@1580 251 int getPositionForValue(double value) const override;
Chris@1580 252 int getPositionForValueUnclamped(double value) const override;
Chris@885 253
Chris@1580 254 double getValueForPosition(int position) const override;
Chris@1580 255 double getValueForPositionUnclamped(int position) const override;
Chris@880 256
Chris@1580 257 QString getUnit() const override { 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