Mercurial > hg > svcore
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/ScaleTickIntervals.h Wed May 03 13:02:08 2017 +0100 @@ -0,0 +1,112 @@ +/* -*- 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