199 lines
4.3 KiB
C++
199 lines
4.3 KiB
C++
#include "visualizerbars.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <qbrush.h>
|
|
#include <qpainter.h>
|
|
#include <qpainterpath.h>
|
|
#include <qpen.h>
|
|
|
|
namespace ZShell::internal {
|
|
|
|
VisualizerBars::VisualizerBars(QQuickItem* parent)
|
|
: QQuickPaintedItem(parent) {
|
|
setAntialiasing(true);
|
|
}
|
|
|
|
void VisualizerBars::advance(qreal dt) {
|
|
if (m_displayValues.isEmpty() || m_settled)
|
|
return;
|
|
|
|
// dt is in seconds (from FrameAnimation.frameTime), convert to ms
|
|
const qreal dtMs = dt * 1000.0;
|
|
const qreal tau = m_animationDuration / 3.0;
|
|
const qreal alpha = 1.0 - std::exp(-dtMs / tau);
|
|
|
|
bool allSettled = true;
|
|
|
|
for (qsizetype i = 0; i < m_displayValues.size(); ++i) {
|
|
const double diff = m_targetValues[i] - m_displayValues[i];
|
|
|
|
if (std::abs(diff) > 0.001) {
|
|
m_displayValues[i] += diff * alpha;
|
|
allSettled = false;
|
|
} else {
|
|
m_displayValues[i] = m_targetValues[i];
|
|
}
|
|
}
|
|
|
|
update();
|
|
|
|
if (allSettled && !m_settled) {
|
|
m_settled = true;
|
|
emit settledChanged();
|
|
}
|
|
}
|
|
|
|
void VisualizerBars::paint(QPainter* painter) {
|
|
if (m_displayValues.isEmpty())
|
|
return;
|
|
|
|
painter->setRenderHint(QPainter::Antialiasing, true);
|
|
painter->setPen(Qt::NoPen);
|
|
|
|
const qreal h = height();
|
|
const qreal maxBarHeight = h * 0.4;
|
|
|
|
QLinearGradient gradient(0, h - maxBarHeight, 0, h);
|
|
gradient.setColorAt(0, m_primaryColor);
|
|
gradient.setColorAt(1, m_secondaryColor);
|
|
painter->setBrush(gradient);
|
|
|
|
drawSide(painter, false);
|
|
drawSide(painter, true);
|
|
}
|
|
|
|
void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
|
|
const qreal w = width();
|
|
const qreal h = height();
|
|
const auto count = m_displayValues.size();
|
|
|
|
if (count == 0)
|
|
return;
|
|
|
|
const qreal sideWidth = w * 0.4;
|
|
const qreal slotWidth = sideWidth / static_cast<qreal>(count);
|
|
const qreal barWidth = slotWidth - m_spacing;
|
|
|
|
if (barWidth <= 0)
|
|
return;
|
|
|
|
const qreal sideOffset = rightSide ? w * 0.6 : 0;
|
|
const qreal maxBarHeight = h * 0.4;
|
|
|
|
for (qsizetype i = 0; i < count; ++i) {
|
|
const qsizetype valueIndex = rightSide ? i : (count - i - 1);
|
|
const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0);
|
|
const qreal barHeight = value * maxBarHeight;
|
|
|
|
if (barHeight <= 0)
|
|
continue;
|
|
|
|
const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset;
|
|
const qreal y = h - barHeight;
|
|
const qreal r = std::min({ m_rounding, barWidth / 2.0, barHeight });
|
|
|
|
QPainterPath path;
|
|
path.moveTo(x, h);
|
|
path.lineTo(x, y + r);
|
|
|
|
if (r > 0) {
|
|
path.arcTo(x, y, r * 2, r * 2, 180, -90);
|
|
path.lineTo(x + barWidth - r, y);
|
|
path.arcTo(x + barWidth - r * 2, y, r * 2, r * 2, 90, -90);
|
|
} else {
|
|
path.lineTo(x, y);
|
|
path.lineTo(x + barWidth, y);
|
|
}
|
|
|
|
path.lineTo(x + barWidth, h);
|
|
path.closeSubpath();
|
|
|
|
painter->drawPath(path);
|
|
}
|
|
}
|
|
|
|
QVector<double> VisualizerBars::values() const {
|
|
return m_targetValues;
|
|
}
|
|
|
|
void VisualizerBars::setValues(const QVector<double>& values) {
|
|
m_targetValues = values;
|
|
|
|
if (m_displayValues.size() != values.size()) {
|
|
m_displayValues.resize(values.size(), 0.0);
|
|
}
|
|
|
|
if (m_settled) {
|
|
m_settled = false;
|
|
emit settledChanged();
|
|
}
|
|
|
|
emit valuesChanged();
|
|
}
|
|
|
|
bool VisualizerBars::settled() const {
|
|
return m_settled;
|
|
}
|
|
|
|
QColor VisualizerBars::primaryColor() const {
|
|
return m_primaryColor;
|
|
}
|
|
|
|
void VisualizerBars::setPrimaryColor(const QColor& color) {
|
|
if (m_primaryColor == color)
|
|
return;
|
|
m_primaryColor = color;
|
|
emit primaryColorChanged();
|
|
update();
|
|
}
|
|
|
|
QColor VisualizerBars::secondaryColor() const {
|
|
return m_secondaryColor;
|
|
}
|
|
|
|
void VisualizerBars::setSecondaryColor(const QColor& color) {
|
|
if (m_secondaryColor == color)
|
|
return;
|
|
m_secondaryColor = color;
|
|
emit secondaryColorChanged();
|
|
update();
|
|
}
|
|
|
|
qreal VisualizerBars::rounding() const {
|
|
return m_rounding;
|
|
}
|
|
|
|
void VisualizerBars::setRounding(qreal rounding) {
|
|
if (qFuzzyCompare(m_rounding, rounding))
|
|
return;
|
|
m_rounding = rounding;
|
|
emit roundingChanged();
|
|
update();
|
|
}
|
|
|
|
qreal VisualizerBars::spacing() const {
|
|
return m_spacing;
|
|
}
|
|
|
|
void VisualizerBars::setSpacing(qreal spacing) {
|
|
if (qFuzzyCompare(m_spacing, spacing))
|
|
return;
|
|
m_spacing = spacing;
|
|
emit spacingChanged();
|
|
update();
|
|
}
|
|
|
|
int VisualizerBars::animationDuration() const {
|
|
return m_animationDuration;
|
|
}
|
|
|
|
void VisualizerBars::setAnimationDuration(int duration) {
|
|
if (m_animationDuration == duration)
|
|
return;
|
|
m_animationDuration = duration;
|
|
emit animationDurationChanged();
|
|
}
|
|
|
|
} // namespace ZShell::internal
|