2#include <score/model/Skin.hpp>
6#include <QLinearGradient>
29inline QColor background()
31 return QColor(18, 18, 20);
33inline QColor gridColor()
35 return QColor(255, 255, 255, 28);
37inline QColor majorGridColor()
39 return QColor(255, 255, 255, 48);
41inline QColor textColor()
43 return QColor(170, 170, 175);
45inline QColor traceColor()
47 return score::Skin::instance().Light.main.brush.color();
49inline QColor warnColor()
51 return QColor(255, 196, 0);
53inline QColor overColor()
55 return QColor(235, 45, 40);
58inline const QFont& smallFont()
60 static const QFont font = [] {
61 QFont f = score::Skin::instance().SansFontSmall;
71 static constexpr std::array<QRgb, 8> c{
72 0xff5ec8f2, 0xfff2a65e, 0xff8be07a, 0xffe76f8f,
73 0xffb69cf5, 0xfff2e35e, 0xff5ef2c4, 0xffd0d0d0};
74 return QColor::fromRgba(c[std::size_t(i) % c.size()]);
79inline QColor
zoneColor(
double v,
double warn,
double over)
82 return QColor(235, 45, 40);
85 const double t = std::clamp((v - warn) / std::max(over - warn, 1e-9), 0., 1.);
86 return QColor(
int(200 + 50 * t),
int(205 - 40 * t), 40);
88 return QColor(76, 175, 80);
96zoneGradient(
double y_lo,
double y_hi,
double lo,
double hi,
double warn,
double over,
double alpha = 1.)
98 QLinearGradient g{QPointF{0., y_lo}, QPointF{0., y_hi}};
99 const double span = std::max(hi - lo, 1e-9);
100 auto stop = [&](
double v, QColor c) {
102 g.setColorAt(std::clamp((v - lo) / span, 0., 1.), c);
106 const double eps = 1e-6 * span;
107 for(
double edge : {warn, over})
108 if(edge - eps > lo && edge < hi)
110 stop(edge - eps,
zoneColor(edge - eps, warn, over));
132inline const std::array<QRgb, 256>& colorMap(
ColorMap m)
134 auto build = [](std::initializer_list<std::array<int, 3>> anchors) {
135 std::array<QRgb, 256> lut{};
136 const std::vector<std::array<int, 3>> a(anchors);
137 const int n = int(a.size()) - 1;
138 for(
int i = 0; i < 256; i++)
140 const double t = i / 255. * n;
141 const int k = std::min(
int(t), n - 1);
142 const double f = t - k;
143 auto mix = [&](
int c) {
return int(std::lround(a[k][c] * (1 - f) + a[k + 1][c] * f)); };
144 lut[i] = qRgb(mix(0), mix(1), mix(2));
148 static const auto inferno = build(
157 static const auto viridis = build(
166 static const auto gray = build({{0, 0, 0}, {255, 255, 255}});
167 static const auto magma = build(
177 static const auto turbo = build(
187 static const auto coolwarm
188 = build({{59, 76, 192}, {141, 176, 254}, {221, 221, 221}, {244, 154, 123}, {180, 4, 38}});
191 case ColorMap::Viridis:
195 case ColorMap::Magma:
197 case ColorMap::Turbo:
211 const double k = hz / 1000.;
212 return QString::number(k,
'g', k < 10 ? 2 : 3) +
"k";
214 return QString::number(hz,
'g', 3);
221inline std::vector<Tick>
222linearTicks(
double lo,
double hi,
double pixels,
double min_spacing = 18., QString unit = {})
224 std::vector<Tick> ticks;
225 if(!(hi > lo) || pixels <= 0)
227 const double span = hi - lo;
228 const double raw = span * min_spacing / pixels;
229 const double mag = std::pow(10., std::floor(std::log10(raw)));
231 for(
double m : {1., 2., 2.5, 5., 10.})
237 const double first = std::ceil(lo / step) * step;
238 for(
double v = first; v <= hi + step * 1e-6; v += step)
240 const double r = std::abs(v) < step * 1e-6 ? 0. : v;
241 ticks.push_back({r, QString::number(r,
'g', 4) + unit,
true});
249 std::vector<Tick> ticks;
250 if(lo <= 0 || hi <= lo)
252 const double decades = std::log10(hi / lo);
253 const bool dense = pixels / std::max(decades, 1e-3) > 90.;
254 for(
double dec = std::pow(10., std::floor(std::log10(lo))); dec <= hi; dec *= 10.)
256 for(
double m : {1., 2., 5.})
258 const double f = dec * m;
261 const bool major = m == 1.;
262 if(!major && !dense && m == 5.)
264 ticks.push_back({f,
formatHz(f), major});
280 int width{}, height{};
281 std::vector<float> acc;
285 float lut_gamma{-1.f};
287 void resize(
int w,
int h)
291 if(w == width && h == height)
295 acc.assign(std::size_t(w) * h, 0.f);
296 image =
QImage(w, h, QImage::Format_ARGB32_Premultiplied);
297 image.fill(Qt::transparent);
300 void clear() { std::fill(acc.begin(), acc.end(), 0.f); }
302 void decay(
float factor)
308 void hit(
double x,
double y,
float energy = 1.f)
310 const int ix =
int(x), iy =
int(y);
313 acc[std::size_t(iy) * width + ix] +=
energy;
320 const double dx = x1 - x0, dy =
y1 -
y0;
321 const int n = std::max(1,
int(std::ceil(std::max(std::abs(dx), std::abs(dy)))));
322 const float e =
energy / n;
323 for(
int i = 0; i <= n; i++)
324 hit(x0 + dx * i / n,
y0 + dy * i / n, e);
327 void buildGamma(
float gamma)
329 if(gamma == lut_gamma)
332 for(
int i = 0; i <=
levels; i++)
337 template <
typename F>
338 void map(
float full,
float gamma, F&& f)
341 const float k =
levels / std::max(full, 1e-9f);
342 for(
int y = 0; y < height; y++)
344 auto*
line =
reinterpret_cast<QRgb*
>(image.scanLine(y));
345 const float* a = acc.data() + std::size_t(y) * width;
346 for(
int x = 0; x < width; x++)
354 std::array<QRgb, 256> shade;
355 for(
int i = 0; i < 256; i++)
357 map(full, gamma, [&](
uint8_t v) {
return shade[v]; });
363 const auto&
lut = colorMap(
cmap);
370drawLabel(QPainter& p,
const QRectF& r,
int flags,
const QString& text, QColor c = textColor())
373 p.setFont(smallFont());
375 if(!text.contains(QLatin1Char(
'\n'))
376 && QFontMetricsF{smallFont()}.horizontalAdvance(text) > r.width() + 0.5)
378 p.drawText(r, flags, text);
387template <
typename XOf>
388void drawXAxisLabels(QPainter& p,
const QRectF& plot,
const std::vector<Tick>& ticks, XOf x_of)
390 constexpr double gap = 6.;
391 const QFontMetricsF fm{smallFont()};
392 std::vector<std::pair<double, double>> taken;
393 auto place = [&](
const Tick& t) {
394 const double w = fm.horizontalAdvance(t.label);
397 const double x0 = std::clamp(x_of(t.value) - w / 2., plot.left(), plot.right() - w);
398 for(
auto [a, b] : taken)
399 if(x0 < b + gap && x0 + w > a - gap)
401 taken.emplace_back(x0, x0 + w);
402 drawLabel(p, QRectF(x0, plot.bottom() + 1, w + 1, 11), Qt::AlignLeft | Qt::AlignTop, t.label);
404 for(
const auto& t : ticks)
407 for(
const auto& t : ticks)
417template <
typename YOf>
418void drawYAxisLabels(QPainter& p,
const QRectF& plot,
const std::vector<Tick>& ticks, YOf y_of)
420 constexpr double h = 10., gap = 3.;
421 std::vector<double> taken;
422 auto place = [&](
const Tick& t) {
423 const double y0 = std::clamp(y_of(t.value) - h / 2., plot.top() - h / 2., plot.bottom() - h);
424 for(
double o : taken)
425 if(std::abs(o - y0) < h + gap)
429 p, QRectF(0, y0, plot.left() - 3, h), Qt::AlignRight | Qt::AlignVCenter, t.label);
431 for(
const auto& t : ticks)
434 for(
const auto& t : ticks)
440inline void drawReadout(QPainter& p, QPointF topLeft,
const QStringList& lines)
444 p.setFont(smallFont());
445 const QFontMetricsF fm{p.font()};
448 w = std::max(w, fm.horizontalAdvance(l));
449 const double lh = fm.height();
450 QRectF box{topLeft, QSizeF{w + 6, lh * lines.size() + 4}};
451 p.fillRect(box, QColor(0, 0, 0, 150));
452 p.setPen(textColor());
453 for(
int i = 0; i < lines.size(); i++)
454 p.drawText(QPointF{box.left() + 3, box.top() + 2 + fm.ascent() + i * lh}, lines[i]);
Definition BufferDecode.hpp:16
QColor channelColor(int i)
Colours for the n-th channel or trace, distinct on a dark background.
Definition Paint.hpp:69
QString formatHz(double hz)
"1.5k", "20k", "440"
Definition Paint.hpp:207
ColorMap
Definition Paint.hpp:122
@ CoolWarm
Diverging: blue below the middle, red above.
std::vector< Tick > linearTicks(double lo, double hi, double pixels, double min_spacing=18., QString unit={})
Definition Paint.hpp:222
std::vector< Tick > frequencyTicks(double lo, double hi, double pixels)
Ticks of a log frequency axis: decades labelled, 2 and 5 in between.
Definition Paint.hpp:247
void drawXAxisLabels(QPainter &p, const QRectF &plot, const std::vector< Tick > &ticks, XOf x_of)
Definition Paint.hpp:388
void drawLabel(QPainter &p, const QRectF &r, int flags, const QString &text, QColor c=textColor())
Draws text in the small monitor font, flags as for QPainter::drawText.
Definition Paint.hpp:370
void drawReadout(QPainter &p, QPointF topLeft, const QStringList &lines)
A readout box in a corner: semi-opaque so that it stays legible over traces.
Definition Paint.hpp:440
void drawYAxisLabels(QPainter &p, const QRectF &plot, const std::vector< Tick > &ticks, YOf y_of)
Definition Paint.hpp:418
QLinearGradient zoneGradient(double y_lo, double y_hi, double lo, double hi, double warn, double over, double alpha=1.)
Definition Paint.hpp:96
QColor zoneColor(double v, double warn, double over)
Definition Paint.hpp:79
static constexpr int levels
Resolution of the intensities the gamma table maps.
Definition Paint.hpp:278
void line(double x0, double y0, double x1, double y1, float energy=1.f)
Definition Paint.hpp:318
std::array< uint8_t, levels+1 > gamma_lut
Intensity level to 0-255 after the gamma, built when the gamma changes.
Definition Paint.hpp:284
void render(QColor color, float full, float gamma=0.5f)
Maps the accumulator to color at full intensity for full energy.
Definition Paint.hpp:352
void render(ColorMap cmap, float full, float gamma=0.5f)
Maps the accumulator through a colour map.
Definition Paint.hpp:361
void map(float full, float gamma, F &&f)
Calls f(row, x, level 0-255) for every pixel, full energy at 255.
Definition Paint.hpp:338