mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 22:27:20 +02:00
Added animation to pie chart while changing its parts.
This commit is contained in:
parent
e9496fb612
commit
4d269f6e97
5 changed files with 134 additions and 30 deletions
|
@ -279,7 +279,7 @@ void PointDetailsWidget::setXIndex(int xIndex) {
|
|||
_lines.reserve(_chartData.lines.size());
|
||||
auto hasPositiveValues = false;
|
||||
const auto parts = _maxPercentageWidth
|
||||
? PiePartsPercentage(
|
||||
? PiePartsPercentageByIndices(
|
||||
_chartData,
|
||||
nullptr,
|
||||
{ float64(xIndex), float64(xIndex) }).parts
|
||||
|
|
|
@ -14,25 +14,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Statistic {
|
||||
|
||||
PiePartData PiePartsPercentage(
|
||||
const Data::StatisticalChart &chartData,
|
||||
const std::shared_ptr<LinesFilterController> &linesFilter,
|
||||
const Limits &xIndices) {
|
||||
const std::vector<float64> &sums,
|
||||
float64 totalSum,
|
||||
bool round) {
|
||||
auto result = PiePartData();
|
||||
result.parts.reserve(chartData.lines.size());
|
||||
auto sums = std::vector<float64>();
|
||||
sums.reserve(chartData.lines.size());
|
||||
auto totalSum = 0.;
|
||||
for (const auto &line : chartData.lines) {
|
||||
auto sum = 0;
|
||||
for (auto i = xIndices.min; i <= xIndices.max; i++) {
|
||||
sum += line.y[i];
|
||||
}
|
||||
if (linesFilter) {
|
||||
sum *= linesFilter->alpha(line.id);
|
||||
}
|
||||
totalSum += sum;
|
||||
sums.push_back(sum);
|
||||
}
|
||||
result.parts.reserve(sums.size());
|
||||
auto stackedPercentage = 0.;
|
||||
|
||||
auto sumPercDiffs = 0.;
|
||||
|
@ -46,7 +32,9 @@ PiePartData PiePartsPercentage(
|
|||
constexpr auto kPerChar = '%';
|
||||
for (auto k = 0; k < sums.size(); k++) {
|
||||
const auto rawPercentage = totalSum ? (sums[k] / totalSum) : 0.;
|
||||
const auto rounded = 0.01 * std::round(rawPercentage * 100.);
|
||||
const auto rounded = round
|
||||
? (0.01 * std::round(rawPercentage * 100.))
|
||||
: rawPercentage;
|
||||
roundedPercentagesSum += rounded;
|
||||
const auto diff = rawPercentage - rounded;
|
||||
sumPercDiffs += diff;
|
||||
|
@ -68,7 +56,7 @@ PiePartData PiePartsPercentage(
|
|||
});
|
||||
result.pieHasSinglePart |= (rounded == 1.);
|
||||
}
|
||||
{
|
||||
if (round) {
|
||||
const auto index = (roundedPercentagesSum > 1.)
|
||||
? maxPercDiffIndex
|
||||
: minPercDiffIndex;
|
||||
|
@ -85,4 +73,25 @@ PiePartData PiePartsPercentage(
|
|||
return result;
|
||||
}
|
||||
|
||||
PiePartData PiePartsPercentageByIndices(
|
||||
const Data::StatisticalChart &chartData,
|
||||
const std::shared_ptr<LinesFilterController> &linesFilter,
|
||||
const Limits &xIndices) {
|
||||
auto sums = std::vector<float64>();
|
||||
sums.reserve(chartData.lines.size());
|
||||
auto totalSum = 0.;
|
||||
for (const auto &line : chartData.lines) {
|
||||
auto sum = 0;
|
||||
for (auto i = xIndices.min; i <= xIndices.max; i++) {
|
||||
sum += line.y[i];
|
||||
}
|
||||
if (linesFilter) {
|
||||
sum *= linesFilter->alpha(line.id);
|
||||
}
|
||||
totalSum += sum;
|
||||
sums.push_back(sum);
|
||||
}
|
||||
return PiePartsPercentage(sums, totalSum, true);
|
||||
}
|
||||
|
||||
} // namespace Statistic
|
||||
|
|
|
@ -27,6 +27,11 @@ struct PiePartData final {
|
|||
};
|
||||
|
||||
[[nodiscard]] PiePartData PiePartsPercentage(
|
||||
const std::vector<float64> &sums,
|
||||
float64 totalSum,
|
||||
bool round);
|
||||
|
||||
[[nodiscard]] PiePartData PiePartsPercentageByIndices(
|
||||
const Data::StatisticalChart &chartData,
|
||||
const std::shared_ptr<LinesFilterController> &linesFilter,
|
||||
const Limits &xIndices);
|
||||
|
|
|
@ -87,6 +87,57 @@ inline float64 InterpolationRatio(float64 from, float64 to, float64 result) {
|
|||
|
||||
} // namespace
|
||||
|
||||
void StackLinearChartView::ChangingPiePartController::setParts(
|
||||
const std::vector<PiePartData::Part> &was,
|
||||
const std::vector<PiePartData::Part> &now) {
|
||||
if (_animValues.size() != was.size()) {
|
||||
_animValues = std::vector<anim::value>(was.size(), anim::value());
|
||||
for (auto i = 0; i < was.size(); i++) {
|
||||
_animValues[i] = anim::value(
|
||||
was[i].roundedPercentage,
|
||||
now[i].roundedPercentage);
|
||||
}
|
||||
} else {
|
||||
for (auto i = 0; i < was.size(); i++) {
|
||||
_animValues[i] = anim::value(
|
||||
_animValues[i].current(),
|
||||
now[i].roundedPercentage);
|
||||
}
|
||||
}
|
||||
_startedAt = crl::now();
|
||||
_isFinished = false;
|
||||
}
|
||||
|
||||
void StackLinearChartView::ChangingPiePartController::update() {
|
||||
const auto progress = std::clamp(
|
||||
(crl::now() - _startedAt) / float64(st::slideWrapDuration),
|
||||
0.,
|
||||
1.);
|
||||
auto totalSum = 0.;
|
||||
auto finished = true;
|
||||
auto result = std::vector<float64>();
|
||||
result.reserve(_animValues.size());
|
||||
for (auto &anim : _animValues) {
|
||||
anim.update(progress, anim::easeOutCubic);
|
||||
if (finished && (anim.current() != anim.to())) {
|
||||
finished = false;
|
||||
}
|
||||
const auto value = anim.current();
|
||||
result.push_back(value);
|
||||
totalSum += value;
|
||||
}
|
||||
_isFinished = finished;
|
||||
_current = PiePartsPercentage(result, totalSum, false);
|
||||
}
|
||||
|
||||
PiePartData StackLinearChartView::ChangingPiePartController::current() const {
|
||||
return _current;
|
||||
}
|
||||
|
||||
bool StackLinearChartView::ChangingPiePartController::isFinished() const {
|
||||
return _isFinished;
|
||||
}
|
||||
|
||||
StackLinearChartView::StackLinearChartView() {
|
||||
_piePartAnimation.init([=] { AbstractChartView::update(); });
|
||||
}
|
||||
|
@ -185,7 +236,7 @@ void StackLinearChartView::saveZoomRange(const PaintContext &c) {
|
|||
}
|
||||
|
||||
void StackLinearChartView::savePieTextParts(const PaintContext &c) {
|
||||
auto data = PiePartsPercentage(
|
||||
auto data = PiePartsPercentageByIndices(
|
||||
c.chartData,
|
||||
linesFilterController(),
|
||||
_transition.zoomedInRangeXIndices);
|
||||
|
@ -504,14 +555,33 @@ void StackLinearChartView::paintZoomed(QPainter &p, const PaintContext &c) {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto wasZoomedInRangeXIndices = _transition.zoomedInRangeXIndices;
|
||||
saveZoomRange(c);
|
||||
const auto partsData = PiePartsPercentage(
|
||||
const auto &[zoomedStart, zoomedEnd] = _transition.zoomedInRangeXIndices;
|
||||
const auto partsData = PiePartsPercentageByIndices(
|
||||
c.chartData,
|
||||
linesFilterController(),
|
||||
_transition.zoomedInRangeXIndices);
|
||||
const auto xIndicesChanged = (wasZoomedInRangeXIndices.min != zoomedStart)
|
||||
|| (wasZoomedInRangeXIndices.max != zoomedEnd);
|
||||
if (xIndicesChanged) {
|
||||
const auto wasParts = PiePartsPercentageByIndices(
|
||||
c.chartData,
|
||||
linesFilterController(),
|
||||
wasZoomedInRangeXIndices);
|
||||
_changingPieController.setParts(wasParts.parts, partsData.parts);
|
||||
if (!_piePartAnimation.animating()) {
|
||||
_piePartAnimation.start();
|
||||
}
|
||||
}
|
||||
if (!_changingPieController.isFinished()) {
|
||||
_changingPieController.update();
|
||||
}
|
||||
_pieHasSinglePart = partsData.pieHasSinglePart;
|
||||
const auto &parts = partsData.parts;
|
||||
applyParts(parts);
|
||||
applyParts(partsData.parts);
|
||||
const auto &parts = _changingPieController.isFinished()
|
||||
? partsData.parts
|
||||
: _changingPieController.current().parts;
|
||||
|
||||
p.fillRect(c.rect + QMargins(0, 0, 0, st::lineWidth), st::boxBg);
|
||||
const auto center = QPointF(c.rect.center());
|
||||
|
@ -547,14 +617,12 @@ void StackLinearChartView::paintZoomed(QPainter &p, const PaintContext &c) {
|
|||
selectedLineIndex = k;
|
||||
}
|
||||
}
|
||||
if (_piePartController.isFinished()) {
|
||||
if (_piePartController.isFinished() && _changingPieController.isFinished()) {
|
||||
_piePartAnimation.stop();
|
||||
}
|
||||
paintPieText(p, c);
|
||||
|
||||
if (selectedLineIndex >= 0) {
|
||||
const auto &[zoomedStart, zoomedEnd] =
|
||||
_transition.zoomedInRangeXIndices;
|
||||
const auto &line = c.chartData.lines[selectedLineIndex];
|
||||
auto sum = 0;
|
||||
for (auto i = zoomedStart; i <= zoomedEnd; i++) {
|
||||
|
@ -633,11 +701,13 @@ void StackLinearChartView::paintZoomedFooter(
|
|||
}
|
||||
|
||||
void StackLinearChartView::paintPieText(QPainter &p, const PaintContext &c) {
|
||||
constexpr auto kMinPercentage = 0.03;
|
||||
constexpr auto kMinPercentage = 0.039;
|
||||
if (_transition.progress == 1.) {
|
||||
savePieTextParts(c);
|
||||
}
|
||||
const auto &parts = _transition.textParts;
|
||||
const auto &parts = _changingPieController.isFinished()
|
||||
? _transition.textParts
|
||||
: _changingPieController.current().parts;
|
||||
|
||||
const auto center = QPointF(c.rect.center());
|
||||
const auto side = (c.rect.width() / 2.) * kCircleSizeRatio;
|
||||
|
|
|
@ -121,8 +121,28 @@ private:
|
|||
LineId _selected = -1;
|
||||
|
||||
};
|
||||
|
||||
class ChangingPiePartController final {
|
||||
public:
|
||||
void setParts(
|
||||
const std::vector<PiePartData::Part> &was,
|
||||
const std::vector<PiePartData::Part> &now);
|
||||
void update();
|
||||
[[nodiscard]] PiePartData current() const;
|
||||
[[nodiscard]] bool isFinished() const;
|
||||
|
||||
private:
|
||||
crl::time _startedAt = 0;;
|
||||
std::vector<anim::value> _animValues;
|
||||
PiePartData _current;
|
||||
bool _isFinished = true;
|
||||
|
||||
};
|
||||
|
||||
PiePartController _piePartController;
|
||||
ChangingPiePartController _changingPieController;
|
||||
Ui::Animations::Basic _piePartAnimation;
|
||||
|
||||
bool _pieHasSinglePart = false;
|
||||
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue