Mercurial > hg > svcore
view 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 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006-2017 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #ifndef SV_SCALE_TICK_INTERVALS_H #define SV_SCALE_TICK_INTERVALS_H #include <string> #include <vector> #include <cmath> #include <iostream> class ScaleTickIntervals { public: struct Range { double min; // start of value range double max; // end of value range int n; // number of divisions requested (will be n+1 ticks) }; struct Tick { double value; // value this tick represents std::string label; // value as written }; struct Ticks { double initial; // value of first tick double spacing; // increment between ticks double roundTo; // what all displayed values should be rounded to bool fixed; // whether to use fixed precision (%f rather than %e) int precision; // number of dp (%f) or sf (%e) std::vector<Tick> ticks; // computed tick values and labels }; static Ticks linear(Range r) { if (r.n < 1) { return {}; } if (r.max < r.min) { return linear({ r.max, r.min, r.n }); } double inc = (r.max - r.min) / r.n; if (inc == 0) { Ticks t { r.min, 1.0, r.min, false, 1, {} }; explode(r, t); return t; } double ilg = log10(inc); int prec = int((ilg > 0.0) ? round(ilg) : trunc(ilg)) - 1; int dp = 0, sf = 0; bool fixed = false; if (prec < 0) { dp = -prec; sf = 1; // was 2, but should probably vary } else { sf = prec; } if (sf == 0) { sf = 1; } if (prec > -4 && prec < 4) { fixed = true; } double roundTo = pow(10.0, prec); inc = round(inc / roundTo) * roundTo; double min = ceil(r.min / roundTo) * roundTo; if (min > r.max) min = r.max; Ticks t { min, inc, roundTo, fixed, fixed ? dp : sf, {} }; explode(r, t); return t; } private: static void explode(const Range &r, Ticks &t) { std::cerr << "initial = " << t.initial << ", spacing = " << t.spacing << ", roundTo = " << t.roundTo << ", fixed = " << t.fixed << ", precision = " << t.precision << std::endl; auto makeTick = [&](double value) { const int buflen = 40; char buffer[buflen]; snprintf(buffer, buflen, t.fixed ? "%.*f" : "%.*e", t.precision, value); return Tick({ value, std::string(buffer) }); }; for (double value = t.initial; value <= r.max; value += t.spacing) { value = t.roundTo * round(value / t.roundTo); t.ticks.push_back(makeTick(value)); } } }; #endif