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