comparison base/ScaleTickIntervals.h @ 1407:25ed6dde2ce0 scale-ticks

Scale tick labeller, and tests (some failing so far)
author Chris Cannam
date Wed, 03 May 2017 13:02:08 +0100
parents
children f89365917d02
comparison
equal deleted inserted replaced
1406:09751743647e 1407:25ed6dde2ce0
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-2017 Chris Cannam and 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_SCALE_TICK_INTERVALS_H
17 #define SV_SCALE_TICK_INTERVALS_H
18
19 #include <string>
20 #include <vector>
21 #include <cmath>
22
23 #include <iostream>
24
25
26 class ScaleTickIntervals
27 {
28 public:
29 struct Range {
30 double min; // start of value range
31 double max; // end of value range
32 int n; // number of divisions requested (will be n+1 ticks)
33 };
34
35 struct Tick {
36 double value; // value this tick represents
37 std::string label; // value as written
38 };
39
40 struct Ticks {
41 double initial; // value of first tick
42 double spacing; // increment between ticks
43 double roundTo; // what all displayed values should be rounded to
44 bool fixed; // whether to use fixed precision (%f rather than %e)
45 int precision; // number of dp (%f) or sf (%e)
46 std::vector<Tick> ticks; // computed tick values and labels
47 };
48
49 static Ticks linear(Range r) {
50
51 if (r.n < 1) {
52 return {};
53 }
54 if (r.max < r.min) {
55 return linear({ r.max, r.min, r.n });
56 }
57
58 double inc = (r.max - r.min) / r.n;
59 if (inc == 0) {
60 Ticks t { r.min, 1.0, r.min, false, 1, {} };
61 explode(r, t);
62 return t;
63 }
64
65 double ilg = log10(inc);
66 int prec = int((ilg > 0.0) ? round(ilg) : trunc(ilg)) - 1;
67 int dp = 0, sf = 0;
68 bool fixed = false;
69 if (prec < 0) {
70 dp = -prec;
71 sf = 1; // was 2, but should probably vary
72 } else {
73 sf = prec;
74 }
75 if (sf == 0) {
76 sf = 1;
77 }
78 if (prec > -4 && prec < 4) {
79 fixed = true;
80 }
81
82 double roundTo = pow(10.0, prec);
83 inc = round(inc / roundTo) * roundTo;
84 double min = ceil(r.min / roundTo) * roundTo;
85 if (min > r.max) min = r.max;
86
87 Ticks t { min, inc, roundTo, fixed, fixed ? dp : sf, {} };
88 explode(r, t);
89 return t;
90 }
91
92 private:
93 static void explode(const Range &r, Ticks &t) {
94 std::cerr << "initial = " << t.initial << ", spacing = " << t.spacing
95 << ", roundTo = " << t.roundTo << ", fixed = " << t.fixed
96 << ", precision = " << t.precision << std::endl;
97 auto makeTick = [&](double value) {
98 const int buflen = 40;
99 char buffer[buflen];
100 snprintf(buffer, buflen,
101 t.fixed ? "%.*f" : "%.*e",
102 t.precision, value);
103 return Tick({ value, std::string(buffer) });
104 };
105 for (double value = t.initial; value <= r.max; value += t.spacing) {
106 value = t.roundTo * round(value / t.roundTo);
107 t.ticks.push_back(makeTick(value));
108 }
109 }
110 };
111
112 #endif