annotate base/ScaleTickIntervals.h @ 1411:1f0d071e7ce6 scale-ticks

More tests & fixes
author Chris Cannam
date Wed, 03 May 2017 18:26:26 +0100
parents c4af57d59434
children b7a9edee85e0
rev   line source
Chris@1407 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1407 2
Chris@1407 3 /*
Chris@1407 4 Sonic Visualiser
Chris@1407 5 An audio file viewer and annotation editor.
Chris@1407 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1407 7 This file copyright 2006-2017 Chris Cannam and QMUL.
Chris@1407 8
Chris@1407 9 This program is free software; you can redistribute it and/or
Chris@1407 10 modify it under the terms of the GNU General Public License as
Chris@1407 11 published by the Free Software Foundation; either version 2 of the
Chris@1407 12 License, or (at your option) any later version. See the file
Chris@1407 13 COPYING included with this distribution for more information.
Chris@1407 14 */
Chris@1407 15
Chris@1407 16 #ifndef SV_SCALE_TICK_INTERVALS_H
Chris@1407 17 #define SV_SCALE_TICK_INTERVALS_H
Chris@1407 18
Chris@1407 19 #include <string>
Chris@1407 20 #include <vector>
Chris@1407 21 #include <cmath>
Chris@1407 22
Chris@1411 23 //#define DEBUG_SCALE_TICK_INTERVALS 1
Chris@1411 24
Chris@1411 25 #ifdef DEBUG_SCALE_TICK_INTERVALS
Chris@1407 26 #include <iostream>
Chris@1411 27 #endif
Chris@1407 28
Chris@1407 29 class ScaleTickIntervals
Chris@1407 30 {
Chris@1407 31 public:
Chris@1407 32 struct Range {
Chris@1407 33 double min; // start of value range
Chris@1407 34 double max; // end of value range
Chris@1411 35 int n; // number of divisions (will be at most n+1 ticks)
Chris@1407 36 };
Chris@1407 37
Chris@1407 38 struct Tick {
Chris@1407 39 double value; // value this tick represents
Chris@1407 40 std::string label; // value as written
Chris@1407 41 };
Chris@1407 42
Chris@1407 43 struct Ticks {
Chris@1407 44 double initial; // value of first tick
Chris@1407 45 double spacing; // increment between ticks
Chris@1407 46 double roundTo; // what all displayed values should be rounded to
Chris@1407 47 bool fixed; // whether to use fixed precision (%f rather than %e)
Chris@1407 48 int precision; // number of dp (%f) or sf (%e)
Chris@1407 49 std::vector<Tick> ticks; // computed tick values and labels
Chris@1407 50 };
Chris@1407 51
Chris@1407 52 static Ticks linear(Range r) {
Chris@1407 53
Chris@1407 54 if (r.n < 1) {
Chris@1407 55 return {};
Chris@1407 56 }
Chris@1407 57 if (r.max < r.min) {
Chris@1407 58 return linear({ r.max, r.min, r.n });
Chris@1407 59 }
Chris@1407 60
Chris@1407 61 double inc = (r.max - r.min) / r.n;
Chris@1407 62 if (inc == 0) {
Chris@1411 63 #ifdef DEBUG_SCALE_TICK_INTERVALS
Chris@1411 64 std::cerr << "inc == 0, using trivial range" << std::endl;
Chris@1411 65 #endif
Chris@1411 66 double roundTo = r.min;
Chris@1411 67 if (roundTo <= 0.0) {
Chris@1411 68 roundTo = 1.0;
Chris@1411 69 }
Chris@1411 70 Ticks t { r.min, 1.0, roundTo, true, 1, {} };
Chris@1407 71 explode(r, t);
Chris@1407 72 return t;
Chris@1407 73 }
Chris@1408 74
Chris@1408 75 double digInc = log10(inc);
Chris@1408 76 double digMax = log10(fabs(r.max));
Chris@1408 77 double digMin = log10(fabs(r.min));
Chris@1408 78
Chris@1409 79 int precInc = int(trunc(digInc));
Chris@1409 80 if (double(precInc) != digInc) {
Chris@1409 81 precInc -= 1;
Chris@1409 82 }
Chris@1408 83
Chris@1408 84 bool fixed = false;
Chris@1408 85 if (precInc > -4 && precInc < 4) {
Chris@1408 86 fixed = true;
Chris@1408 87 } else if ((digMax >= -3.0 && digMax <= 2.0) &&
Chris@1408 88 (digMin >= -3.0 && digMin <= 3.0)) {
Chris@1408 89 fixed = true;
Chris@1408 90 }
Chris@1408 91
Chris@1408 92 int precRange = int(ceil(digMax - digInc));
Chris@1408 93
Chris@1408 94 int prec = 1;
Chris@1408 95
Chris@1408 96 if (fixed) {
Chris@1410 97 if (precInc < 0) {
Chris@1410 98 prec = -precInc;
Chris@1410 99 } else if (precInc > 0) {
Chris@1410 100 prec = 0;
Chris@1408 101 }
Chris@1408 102 } else {
Chris@1408 103 prec = precRange;
Chris@1408 104 }
Chris@1408 105
Chris@1411 106 double roundTo = pow(10.0, precInc);
Chris@1411 107
Chris@1411 108 #ifdef DEBUG_SCALE_TICK_INTERVALS
Chris@1408 109 std::cerr << "\nmin = " << r.min << ", max = " << r.max << ", n = " << r.n
Chris@1408 110 << ", inc = " << inc << std::endl;
Chris@1408 111 std::cerr << "digMax = " << digMax << ", digInc = " << digInc
Chris@1408 112 << std::endl;
Chris@1408 113 std::cerr << "fixed = " << fixed << ", inc = " << inc
Chris@1408 114 << ", precInc = " << precInc << ", precRange = " << precRange
Chris@1408 115 << ", prec = " << prec << std::endl;
Chris@1408 116 std::cerr << "roundTo = " << roundTo << std::endl;
Chris@1411 117 #endif
Chris@1408 118
Chris@1407 119 inc = round(inc / roundTo) * roundTo;
Chris@1408 120 if (inc < roundTo) inc = roundTo;
Chris@1408 121
Chris@1407 122 double min = ceil(r.min / roundTo) * roundTo;
Chris@1407 123 if (min > r.max) min = r.max;
Chris@1407 124
Chris@1408 125 Ticks t { min, inc, roundTo, fixed, prec, {} };
Chris@1407 126 explode(r, t);
Chris@1407 127 return t;
Chris@1407 128 }
Chris@1407 129
Chris@1407 130 private:
Chris@1407 131 static void explode(const Range &r, Ticks &t) {
Chris@1411 132 #ifdef DEBUG_SCALE_TICK_INTERVALS
Chris@1407 133 std::cerr << "initial = " << t.initial << ", spacing = " << t.spacing
Chris@1407 134 << ", roundTo = " << t.roundTo << ", fixed = " << t.fixed
Chris@1407 135 << ", precision = " << t.precision << std::endl;
Chris@1411 136 #endif
Chris@1407 137 auto makeTick = [&](double value) {
Chris@1407 138 const int buflen = 40;
Chris@1407 139 char buffer[buflen];
Chris@1407 140 snprintf(buffer, buflen,
Chris@1407 141 t.fixed ? "%.*f" : "%.*e",
Chris@1407 142 t.precision, value);
Chris@1407 143 return Tick({ value, std::string(buffer) });
Chris@1407 144 };
Chris@1411 145 double eps = 1e-7;
Chris@1411 146 if (t.spacing < eps * 10.0) {
Chris@1411 147 eps = t.spacing / 10.0;
Chris@1411 148 }
Chris@1411 149 for (double value = t.initial; value < r.max + eps; value += t.spacing) {
Chris@1407 150 value = t.roundTo * round(value / t.roundTo);
Chris@1407 151 t.ticks.push_back(makeTick(value));
Chris@1407 152 }
Chris@1407 153 }
Chris@1407 154 };
Chris@1407 155
Chris@1407 156 #endif