RangeMapper.cpp
Go to the documentation of this file.
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 #include "RangeMapper.h"
17 #include "system/System.h"
18 
19 #include <cassert>
20 #include <cmath>
21 
22 #include <iostream>
23 #include <stdexcept>
24 
25 LinearRangeMapper::LinearRangeMapper(int minpos, int maxpos,
26  double minval, double maxval,
27  QString unit, bool inverted,
28  std::map<int, QString> labels) :
29  m_minpos(minpos),
30  m_maxpos(maxpos),
31  m_minval(minval),
32  m_maxval(maxval),
33  m_unit(unit),
34  m_inverted(inverted),
35  m_labels(labels)
36 {
37  if (m_maxval == m_minval) {
38  throw std::logic_error("LinearRangeMapper: maxval must differ from minval");
39  }
40  if (m_maxpos == m_minpos) {
41  throw std::logic_error("LinearRangeMapper: maxpos must differ from minpos");
42  }
43 }
44 
45 int
47 {
48  int position = getPositionForValueUnclamped(value);
49  if (position < m_minpos) position = m_minpos;
50  if (position > m_maxpos) position = m_maxpos;
51  return position;
52 }
53 
54 int
56 {
57  int position = m_minpos +
58  int(lrint(((value - m_minval) / (m_maxval - m_minval))
59  * (m_maxpos - m_minpos)));
60  if (m_inverted) return m_maxpos - (position - m_minpos);
61  else return position;
62 }
63 
64 double
66 {
67  if (position < m_minpos) position = m_minpos;
68  if (position > m_maxpos) position = m_maxpos;
69  double value = getValueForPositionUnclamped(position);
70  return value;
71 }
72 
73 double
75 {
76  if (m_inverted) position = m_maxpos - (position - m_minpos);
77  double value = m_minval +
78  ((double(position - m_minpos) / double(m_maxpos - m_minpos))
79  * (m_maxval - m_minval));
80 // cerr << "getValueForPositionUnclamped(" << position << "): minval " << m_minval << ", maxval " << m_maxval << ", value " << value << endl;
81  return value;
82 }
83 
84 QString
85 LinearRangeMapper::getLabel(int position) const
86 {
87  if (m_labels.find(position) != m_labels.end()) {
88  return m_labels.at(position);
89  } else {
90  return "";
91  }
92 }
93 
94 LogRangeMapper::LogRangeMapper(int minpos, int maxpos,
95  double minval, double maxval,
96  QString unit, bool inverted) :
97  m_minpos(minpos),
98  m_maxpos(maxpos),
99  m_unit(unit),
100  m_inverted(inverted)
101 {
102  convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio);
103 
104 // cerr << "LogRangeMapper: minpos " << minpos << ", maxpos "
105 // << maxpos << ", minval " << minval << ", maxval "
106 // << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio
107 // << ", unit " << unit << endl;
108 
109  if (m_maxpos == m_minpos) {
110  throw std::logic_error("LogRangeMapper: maxpos must differ from minpos");
111  }
112 
114 
115 // cerr << "LogRangeMapper: maxlog = " << m_maxlog << endl;
116 }
117 
118 void
119 LogRangeMapper::convertMinMax(int minpos, int maxpos,
120  double minval, double maxval,
121  double &minlog, double &ratio)
122 {
123  static double thresh = powf(10, -10);
124  if (minval < thresh) minval = thresh;
125  minlog = log10(minval);
126  ratio = (maxpos - minpos) / (log10(maxval) - minlog);
127 }
128 
129 void
130 LogRangeMapper::convertRatioMinLog(double ratio, double minlog,
131  int minpos, int maxpos,
132  double &minval, double &maxval)
133 {
134  minval = pow(10, minlog);
135  maxval = pow(10, (maxpos - minpos) / ratio + minlog);
136 }
137 
138 int
140 {
141  int position = getPositionForValueUnclamped(value);
142  if (position < m_minpos) position = m_minpos;
143  if (position > m_maxpos) position = m_maxpos;
144  return position;
145 }
146 
147 int
149 {
150  static double thresh = pow(10, -10);
151  if (value < thresh) value = thresh;
152  int position = int(lrint((log10(value) - m_minlog) * m_ratio)) + m_minpos;
153  if (m_inverted) return m_maxpos - (position - m_minpos);
154  else return position;
155 }
156 
157 double
159 {
160  if (position < m_minpos) position = m_minpos;
161  if (position > m_maxpos) position = m_maxpos;
162  double value = getValueForPositionUnclamped(position);
163  return value;
164 }
165 
166 double
168 {
169  if (m_inverted) position = m_maxpos - (position - m_minpos);
170  double value = pow(10, (position - m_minpos) / m_ratio + m_minlog);
171  return value;
172 }
173 
175  QString unit) :
176  m_mappings(pointMappings),
177  m_unit(unit)
178 {
179  for (CoordMap::const_iterator i = m_mappings.begin();
180  i != m_mappings.end(); ++i) {
181  m_reverse[i->second] = i->first;
182  }
183 }
184 
185 int
187 {
188  int pos = getPositionForValueUnclamped(value);
189  CoordMap::const_iterator i = m_mappings.begin();
190  if (pos < i->second) pos = i->second;
191  i = m_mappings.end(); --i;
192  if (pos > i->second) pos = i->second;
193  return pos;
194 }
195 
196 int
198 {
199  double p = interpolate(&m_mappings, value);
200  return int(lrint(p));
201 }
202 
203 double
205 {
206  double val = getValueForPositionUnclamped(position);
207  CoordMap::const_iterator i = m_mappings.begin();
208  if (val < i->first) val = i->first;
209  i = m_mappings.end(); --i;
210  if (val > i->first) val = i->first;
211  return val;
212 }
213 
214 double
216 {
217  return interpolate(&m_reverse, position);
218 }
219 
220 template <typename T>
221 double
222 InterpolatingRangeMapper::interpolate(T *mapping, double value) const
223 {
224  // lower_bound: first element which does not compare less than value
225  typename T::const_iterator i =
226  mapping->lower_bound(typename T::key_type(value));
227 
228  if (i == mapping->begin()) {
229  // value is less than or equal to first element, so use the
230  // gradient from first to second and extend it
231  ++i;
232  }
233 
234  if (i == mapping->end()) {
235  // value is off the end, so use the gradient from penultimate
236  // to ultimate and extend it
237  --i;
238  }
239 
240  typename T::const_iterator j = i;
241  --j;
242 
243  double gradient = double(i->second - j->second) / double(i->first - j->first);
244 
245  return j->second + (value - j->first) * gradient;
246 }
247 
249  QString unit) :
250  m_mappings(pointMappings),
251  m_unit(unit)
252 {
254 
255  CoordMap::const_iterator first = m_mappings.begin();
256  CoordMap::const_iterator last = m_mappings.end();
257  --last;
258 
259  switch (m_type) {
260  case StraightLine:
261  m_mapper = new LinearRangeMapper(first->second, last->second,
262  first->first, last->first,
263  unit, false);
264  break;
265  case Logarithmic:
266  m_mapper = new LogRangeMapper(first->second, last->second,
267  first->first, last->first,
268  unit, false);
269  break;
270  case Interpolating:
272  break;
273  }
274 }
275 
277 {
278  delete m_mapper;
279 }
280 
283 {
284  // how do we work out whether a linear/log mapping is "close enough"?
285 
286  CoordMap::const_iterator first = mappings.begin();
287  CoordMap::const_iterator last = mappings.end();
288  --last;
289 
290  LinearRangeMapper linm(first->second, last->second,
291  first->first, last->first,
292  "", false);
293 
294  bool inadequate = false;
295 
296  for (CoordMap::const_iterator i = mappings.begin();
297  i != mappings.end(); ++i) {
298  int candidate = linm.getPositionForValue(i->first);
299  int diff = candidate - i->second;
300  if (diff < 0) diff = -diff;
301  if (diff > 1) {
302 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
303 // << ", straight-line mapping inadequate" << endl;
304  inadequate = true;
305  break;
306  }
307  }
308 
309  if (!inadequate) {
310  return StraightLine;
311  }
312 
313  LogRangeMapper logm(first->second, last->second,
314  first->first, last->first,
315  "", false);
316 
317  inadequate = false;
318 
319  for (CoordMap::const_iterator i = mappings.begin();
320  i != mappings.end(); ++i) {
321  int candidate = logm.getPositionForValue(i->first);
322  int diff = candidate - i->second;
323  if (diff < 0) diff = -diff;
324  if (diff > 1) {
325 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
326 // << ", log mapping inadequate" << endl;
327  inadequate = true;
328  break;
329  }
330  }
331 
332  if (!inadequate) {
333  return Logarithmic;
334  }
335 
336  return Interpolating;
337 }
338 
339 int
341 {
342  return m_mapper->getPositionForValue(value);
343 }
344 
345 double
347 {
348  return m_mapper->getValueForPosition(position);
349 }
350 
351 int
353 {
354  return m_mapper->getPositionForValueUnclamped(value);
355 }
356 
357 double
359 {
360  return m_mapper->getValueForPositionUnclamped(position);
361 }
double getValueForPositionUnclamped(int position) const override
Return the value mapped from the given position, without clamping.
Definition: RangeMapper.cpp:74
double getValueForPosition(int position) const override
Return the value mapped from the given position, clamping to the minimum and maximum extents of the m...
MappingType m_type
Definition: RangeMapper.h:260
int getPositionForValue(double value) const override
Return the position that maps to the given value, rounding to the nearest position and clamping to th...
int getPositionForValueUnclamped(double value) const override
Return the position that maps to the given value, rounding to the nearest position, without clamping.
double getValueForPositionUnclamped(int position) const override
Return the value mapped from the given position, without clamping.
virtual int getPositionForValue(double value) const =0
Return the position that maps to the given value, rounding to the nearest position and clamping to th...
MappingType chooseMappingTypeFor(const CoordMap &)
QString getLabel(int position) const override
The mapper may optionally provide special labels for one or more individual positions (such as the mi...
Definition: RangeMapper.cpp:85
std::map< double, int > CoordMap
Definition: RangeMapper.h:155
RangeMapper * m_mapper
Definition: RangeMapper.h:263
double interpolate(T *mapping, double v) const
int getPositionForValue(double value) const override
Return the position that maps to the given value, rounding to the nearest position and clamping to th...
static void convertRatioMinLog(double ratio, double minlog, int minpos, int maxpos, double &minval, double &maxval)
int getPositionForValueUnclamped(double value) const override
Return the position that maps to the given value, rounding to the nearest position, without clamping.
InterpolatingRangeMapper(CoordMap pointMappings, QString unit)
Given a series of (value, position) coordinate mappings, construct a range mapper that maps arbitrary...
virtual double getValueForPositionUnclamped(int position) const =0
Return the value mapped from the given position, without clamping.
double getValueForPosition(int position) const override
Return the value mapped from the given position, clamping to the minimum and maximum extents of the m...
Definition: RangeMapper.cpp:65
int getPositionForValueUnclamped(double value) const override
Return the position that maps to the given value, rounding to the nearest position, without clamping.
virtual double getValueForPosition(int position) const =0
Return the value mapped from the given position, clamping to the minimum and maximum extents of the m...
std::map< double, int > CoordMap
Definition: RangeMapper.h:204
virtual int getPositionForValueUnclamped(double value) const =0
Return the position that maps to the given value, rounding to the nearest position, without clamping.
int getPositionForValueUnclamped(double value) const override
Return the position that maps to the given value, rounding to the nearest position, without clamping.
Definition: RangeMapper.cpp:55
static void convertMinMax(int minpos, int maxpos, double minval, double maxval, double &minlog, double &ratio)
LinearRangeMapper(int minpos, int maxpos, double minval, double maxval, QString unit="", bool inverted=false, std::map< int, QString > labels={})
Map values in range minval->maxval linearly into integer range minpos->maxpos.
Definition: RangeMapper.cpp:25
double getValueForPositionUnclamped(int position) const override
Return the value mapped from the given position, without clamping.
double getValueForPosition(int position) const override
Return the value mapped from the given position, clamping to the minimum and maximum extents of the m...
QString m_unit
Definition: RangeMapper.h:148
LogRangeMapper(int minpos, int maxpos, double minval, double maxval, QString m_unit="", bool inverted=false)
Map values in range minval->maxval into integer range minpos->maxpos such that logs of the values are...
Definition: RangeMapper.cpp:94
std::map< int, QString > m_labels
Definition: RangeMapper.h:108
double getValueForPositionUnclamped(int position) const override
Return the value mapped from the given position, without clamping.
AutoRangeMapper(CoordMap pointMappings, QString unit)
Given a series of (value, position) coordinate mappings, construct a range mapper that maps arbitrary...
CoordMap m_mappings
Definition: RangeMapper.h:261
double getValueForPosition(int position) const override
Return the value mapped from the given position, clamping to the minimum and maximum extents of the m...
std::map< int, double > m_reverse
Definition: RangeMapper.h:188
int getPositionForValue(double value) const override
Return the position that maps to the given value, rounding to the nearest position and clamping to th...
Definition: RangeMapper.cpp:46
int getPositionForValue(double value) const override
Return the position that maps to the given value, rounding to the nearest position and clamping to th...