Added initial ability to filter chart lines.
This commit is contained in:
parent
4c02d19a51
commit
520989a7e6
|
@ -57,7 +57,8 @@ struct StatisticalChart {
|
||||||
std::vector<int> y;
|
std::vector<int> y;
|
||||||
|
|
||||||
Statistic::SegmentTree segmentTree;
|
Statistic::SegmentTree segmentTree;
|
||||||
QString id;
|
int id = 0;
|
||||||
|
QString idString;
|
||||||
QString name;
|
QString name;
|
||||||
int maxValue = 0;
|
int maxValue = 0;
|
||||||
int minValue = std::numeric_limits<int>::max();
|
int minValue = std::numeric_limits<int>::max();
|
||||||
|
|
|
@ -528,6 +528,7 @@ ChartWidget::ChartAnimationController::ChartAnimationController(
|
||||||
void ChartWidget::ChartAnimationController::setXPercentageLimits(
|
void ChartWidget::ChartAnimationController::setXPercentageLimits(
|
||||||
Data::StatisticalChart &chartData,
|
Data::StatisticalChart &chartData,
|
||||||
Limits xPercentageLimits,
|
Limits xPercentageLimits,
|
||||||
|
std::vector<ChartLineViewContext> &chartLinesViewContext,
|
||||||
crl::time now) {
|
crl::time now) {
|
||||||
if ((_animationValueXMin.to() == xPercentageLimits.min)
|
if ((_animationValueXMin.to() == xPercentageLimits.min)
|
||||||
&& (_animationValueXMax.to() == xPercentageLimits.max)) {
|
&& (_animationValueXMax.to() == xPercentageLimits.max)) {
|
||||||
|
@ -550,6 +551,24 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
|
||||||
float64(FindMaxValue(chartData, startXIndex, endXIndex)),
|
float64(FindMaxValue(chartData, startXIndex, endXIndex)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto minValue = std::numeric_limits<int>::max();
|
||||||
|
auto maxValue = 0;
|
||||||
|
for (auto &l : chartData.lines) {
|
||||||
|
const auto it = ranges::find_if(chartLinesViewContext, [&](const auto &a) {
|
||||||
|
return a.id == l.id;
|
||||||
|
});
|
||||||
|
if (it != end(chartLinesViewContext) && !it->enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto lineMax = l.segmentTree.rMaxQ(startXIndex, endXIndex);
|
||||||
|
const auto lineMin = l.segmentTree.rMinQ(startXIndex, endXIndex);
|
||||||
|
maxValue = std::max(lineMax, maxValue);
|
||||||
|
minValue = std::min(lineMin, minValue);
|
||||||
|
}
|
||||||
|
_finalHeightLimits = { float64(minValue), float64(maxValue) };
|
||||||
|
}
|
||||||
|
|
||||||
_animationValueHeightMin = anim::value(
|
_animationValueHeightMin = anim::value(
|
||||||
_animationValueHeightMin.current(),
|
_animationValueHeightMin.current(),
|
||||||
_finalHeightLimits.min);
|
_finalHeightLimits.min);
|
||||||
|
@ -612,7 +631,8 @@ void ChartWidget::ChartAnimationController::restartBottomLineAlpha() {
|
||||||
void ChartWidget::ChartAnimationController::tick(
|
void ChartWidget::ChartAnimationController::tick(
|
||||||
crl::time now,
|
crl::time now,
|
||||||
std::vector<ChartHorizontalLinesData> &horizontalLines,
|
std::vector<ChartHorizontalLinesData> &horizontalLines,
|
||||||
std::vector<BottomCaptionLineData> &dateLines) {
|
std::vector<BottomCaptionLineData> &dateLines,
|
||||||
|
std::vector<ChartLineViewContext> &chartLinesViewContext) {
|
||||||
if (!_animation.animating()) {
|
if (!_animation.animating()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -669,7 +689,33 @@ void ChartWidget::ChartAnimationController::tick(
|
||||||
const auto bottomLineAlphaFinished = isFinished(
|
const auto bottomLineAlphaFinished = isFinished(
|
||||||
_animValueBottomLineAlpha);
|
_animValueBottomLineAlpha);
|
||||||
|
|
||||||
if (xFinished && yFinished && alphaFinished && bottomLineAlphaFinished) {
|
auto chartLinesViewContextFinished = false;
|
||||||
|
{
|
||||||
|
auto finishedCount = 0;
|
||||||
|
for (auto &viewLine : chartLinesViewContext) {
|
||||||
|
if (!viewLine.startedAt) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto progress = (now - viewLine.startedAt)
|
||||||
|
/ (kXExpandingDuration * 2);
|
||||||
|
viewLine.alpha = std::clamp(
|
||||||
|
viewLine.enabled ? progress : (1. - progress),
|
||||||
|
0.,
|
||||||
|
1.);
|
||||||
|
if (progress >= 1.) {
|
||||||
|
finishedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chartLinesViewContextFinished =
|
||||||
|
(finishedCount == chartLinesViewContext.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (xFinished
|
||||||
|
&& yFinished
|
||||||
|
&& alphaFinished
|
||||||
|
&& bottomLineAlphaFinished
|
||||||
|
&& chartLinesViewContextFinished) {
|
||||||
const auto &lines = horizontalLines.back().lines;
|
const auto &lines = horizontalLines.back().lines;
|
||||||
if ((lines.front().absoluteValue == _animationValueHeightMin.to())
|
if ((lines.front().absoluteValue == _animationValueHeightMin.to())
|
||||||
&& lines.back().absoluteValue == _animationValueHeightMax.to()) {
|
&& lines.back().absoluteValue == _animationValueHeightMax.to()) {
|
||||||
|
@ -794,9 +840,41 @@ ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
|
||||||
, _chartArea(base::make_unique_q<RpMouseWidget>(this))
|
, _chartArea(base::make_unique_q<RpMouseWidget>(this))
|
||||||
, _footer(std::make_unique<Footer>(this))
|
, _footer(std::make_unique<Footer>(this))
|
||||||
, _animationController([=] { _chartArea->update(); }) {
|
, _animationController([=] { _chartArea->update(); }) {
|
||||||
|
|
||||||
|
const auto asd = Ui::CreateChild<Ui::AbstractButton>(this);
|
||||||
|
const auto oiu = std::make_shared<bool>(false);
|
||||||
|
asd->paintRequest(
|
||||||
|
) | rpl::start_with_next([=](const QRect &r) {
|
||||||
|
auto p = QPainter(asd);
|
||||||
|
p.setOpacity(0.3);
|
||||||
|
p.fillRect(r, Qt::darkRed);
|
||||||
|
}, asd->lifetime());
|
||||||
|
asd->setClickedCallback([=] {
|
||||||
|
*oiu = !(*oiu);
|
||||||
|
auto data = ChartLineViewContext{
|
||||||
|
.id = 1,
|
||||||
|
.enabled = (*oiu),
|
||||||
|
.startedAt = crl::now(),
|
||||||
|
};
|
||||||
|
_animatedChartLines.clear();
|
||||||
|
_animatedChartLines.push_back(data);
|
||||||
|
|
||||||
|
_animationController.setXPercentageLimits(
|
||||||
|
_chartData,
|
||||||
|
_animationController.currentXLimits(),
|
||||||
|
_animatedChartLines,
|
||||||
|
data.startedAt);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::start_with_next([=](const QSize &s) {
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
_footer->setGeometry(
|
_footer->setGeometry(
|
||||||
|
0,
|
||||||
|
s.height() - st::statisticsChartFooterHeight * 2,
|
||||||
|
s.width(),
|
||||||
|
st::statisticsChartFooterHeight);
|
||||||
|
asd->setGeometry(
|
||||||
0,
|
0,
|
||||||
s.height() - st::statisticsChartFooterHeight,
|
s.height() - st::statisticsChartFooterHeight,
|
||||||
s.width(),
|
s.width(),
|
||||||
|
@ -806,7 +884,7 @@ ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
|
||||||
0,
|
0,
|
||||||
s.width(),
|
s.width(),
|
||||||
s.height()
|
s.height()
|
||||||
- st::statisticsChartFooterHeight
|
- st::statisticsChartFooterHeight * 2
|
||||||
- st::statisticsChartFooterSkip);
|
- st::statisticsChartFooterSkip);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
@ -817,7 +895,8 @@ ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
|
||||||
width(),
|
width(),
|
||||||
st::statisticsChartHeight
|
st::statisticsChartHeight
|
||||||
+ st::statisticsChartFooterHeight
|
+ st::statisticsChartFooterHeight
|
||||||
+ st::statisticsChartFooterSkip);
|
+ st::statisticsChartFooterSkip
|
||||||
|
+ st::statisticsChartFooterHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect ChartWidget::chartAreaRect() const {
|
QRect ChartWidget::chartAreaRect() const {
|
||||||
|
@ -836,7 +915,11 @@ void ChartWidget::setupChartArea() {
|
||||||
|
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
|
|
||||||
_animationController.tick(now, _horizontalLines, _bottomLine.dates);
|
_animationController.tick(
|
||||||
|
now,
|
||||||
|
_horizontalLines,
|
||||||
|
_bottomLine.dates,
|
||||||
|
_animatedChartLines);
|
||||||
|
|
||||||
const auto chartRect = chartAreaRect();
|
const auto chartRect = chartAreaRect();
|
||||||
|
|
||||||
|
@ -884,6 +967,7 @@ void ChartWidget::setupChartArea() {
|
||||||
_animationController.currentXLimits(),
|
_animationController.currentXLimits(),
|
||||||
_animationController.currentHeightLimits(),
|
_animationController.currentHeightLimits(),
|
||||||
chartRect,
|
chartRect,
|
||||||
|
_animatedChartLines,
|
||||||
detailsPaintContext);
|
detailsPaintContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1005,6 +1089,7 @@ void ChartWidget::setupFooter() {
|
||||||
fullXLimits,
|
fullXLimits,
|
||||||
_footer->fullHeightLimits(),
|
_footer->fullHeightLimits(),
|
||||||
r,
|
r,
|
||||||
|
_animatedChartLines,
|
||||||
detailsPaintContext);
|
detailsPaintContext);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1024,6 +1109,7 @@ void ChartWidget::setupFooter() {
|
||||||
_animationController.setXPercentageLimits(
|
_animationController.setXPercentageLimits(
|
||||||
_chartData,
|
_chartData,
|
||||||
xPercentageLimits,
|
xPercentageLimits,
|
||||||
|
_animatedChartLines,
|
||||||
now);
|
now);
|
||||||
{
|
{
|
||||||
const auto finalXLimits = _animationController.finalXLimits();
|
const auto finalXLimits = _animationController.finalXLimits();
|
||||||
|
@ -1124,6 +1210,7 @@ void ChartWidget::setChartData(Data::StatisticalChart chartData) {
|
||||||
_animationController.setXPercentageLimits(
|
_animationController.setXPercentageLimits(
|
||||||
_chartData,
|
_chartData,
|
||||||
{ _chartData.xPercentage.front(), _chartData.xPercentage.back() },
|
{ _chartData.xPercentage.front(), _chartData.xPercentage.back() },
|
||||||
|
_animatedChartLines,
|
||||||
0);
|
0);
|
||||||
{
|
{
|
||||||
const auto finalXLimits = _animationController.finalXLimits();
|
const auto finalXLimits = _animationController.finalXLimits();
|
||||||
|
|
|
@ -47,6 +47,7 @@ private:
|
||||||
void setXPercentageLimits(
|
void setXPercentageLimits(
|
||||||
Data::StatisticalChart &chartData,
|
Data::StatisticalChart &chartData,
|
||||||
Limits xPercentageLimits,
|
Limits xPercentageLimits,
|
||||||
|
std::vector<ChartLineViewContext> &chartLinesViewContext,
|
||||||
crl::time now);
|
crl::time now);
|
||||||
void start();
|
void start();
|
||||||
void finish();
|
void finish();
|
||||||
|
@ -55,7 +56,8 @@ private:
|
||||||
void tick(
|
void tick(
|
||||||
crl::time now,
|
crl::time now,
|
||||||
std::vector<ChartHorizontalLinesData> &horizontalLines,
|
std::vector<ChartHorizontalLinesData> &horizontalLines,
|
||||||
std::vector<BottomCaptionLineData> &dateLines);
|
std::vector<BottomCaptionLineData> &dateLines,
|
||||||
|
std::vector<ChartLineViewContext> &chartLinesViewContext);
|
||||||
|
|
||||||
[[nodiscard]] Limits currentXLimits() const;
|
[[nodiscard]] Limits currentXLimits() const;
|
||||||
[[nodiscard]] Limits currentXIndices() const;
|
[[nodiscard]] Limits currentXIndices() const;
|
||||||
|
@ -112,6 +114,8 @@ private:
|
||||||
const std::unique_ptr<Footer> _footer;
|
const std::unique_ptr<Footer> _footer;
|
||||||
Data::StatisticalChart _chartData;
|
Data::StatisticalChart _chartData;
|
||||||
|
|
||||||
|
std::vector<ChartLineViewContext> _animatedChartLines;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
base::unique_qptr<PointDetailsWidget> widget;
|
base::unique_qptr<PointDetailsWidget> widget;
|
||||||
float64 currentX = 0;
|
float64 currentX = 0;
|
||||||
|
|
|
@ -23,11 +23,22 @@ void PaintLinearChartView(
|
||||||
const Limits &xPercentageLimits,
|
const Limits &xPercentageLimits,
|
||||||
const Limits &heightLimits,
|
const Limits &heightLimits,
|
||||||
const QRect &rect,
|
const QRect &rect,
|
||||||
|
const std::vector<ChartLineViewContext> &linesContext,
|
||||||
DetailsPaintContext &detailsPaintContext) {
|
DetailsPaintContext &detailsPaintContext) {
|
||||||
const auto currentMinHeight = rect.y(); //
|
const auto currentMinHeight = rect.y(); //
|
||||||
const auto currentMaxHeight = rect.height() + rect.y(); //
|
const auto currentMaxHeight = rect.height() + rect.y(); //
|
||||||
|
|
||||||
for (const auto &line : chartData.lines) {
|
for (const auto &line : chartData.lines) {
|
||||||
|
p.setOpacity(1.);
|
||||||
|
for (const auto &lineContext : linesContext) {
|
||||||
|
if (lineContext.id == line.id) {
|
||||||
|
p.setOpacity(lineContext.alpha);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!p.opacity()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const auto additionalP = (chartData.xPercentage.size() < 2)
|
const auto additionalP = (chartData.xPercentage.size() < 2)
|
||||||
? 0.
|
? 0.
|
||||||
: (chartData.xPercentage.front() * rect.width());
|
: (chartData.xPercentage.front() * rect.width());
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Statistic {
|
||||||
|
|
||||||
struct Limits;
|
struct Limits;
|
||||||
struct DetailsPaintContext;
|
struct DetailsPaintContext;
|
||||||
|
struct ChartLineViewContext;
|
||||||
|
|
||||||
void PaintLinearChartView(
|
void PaintLinearChartView(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
|
@ -23,6 +24,7 @@ void PaintLinearChartView(
|
||||||
const Limits &xPercentageLimits,
|
const Limits &xPercentageLimits,
|
||||||
const Limits &heightLimits,
|
const Limits &heightLimits,
|
||||||
const QRect &rect,
|
const QRect &rect,
|
||||||
|
const std::vector<ChartLineViewContext> &linesContext,
|
||||||
DetailsPaintContext &detailsPaintContext);
|
DetailsPaintContext &detailsPaintContext);
|
||||||
|
|
||||||
} // namespace Statistic
|
} // namespace Statistic
|
||||||
|
|
|
@ -25,4 +25,11 @@ struct DetailsPaintContext final {
|
||||||
std::vector<Dot> dots;
|
std::vector<Dot> dots;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ChartLineViewContext final {
|
||||||
|
int id = 0;
|
||||||
|
bool enabled = false;
|
||||||
|
crl::time startedAt = 0;
|
||||||
|
float64 alpha = 1.;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Statistic
|
} // namespace Statistic
|
||||||
|
|
|
@ -30,6 +30,7 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto result = Data::StatisticalChart();
|
auto result = Data::StatisticalChart();
|
||||||
|
auto columnIdCount = 0;
|
||||||
for (const auto &column : columns) {
|
for (const auto &column : columns) {
|
||||||
const auto array = column.toArray();
|
const auto array = column.toArray();
|
||||||
if (array.empty()) {
|
if (array.empty()) {
|
||||||
|
@ -46,7 +47,8 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
|
||||||
} else {
|
} else {
|
||||||
auto line = Data::StatisticalChart::Line();
|
auto line = Data::StatisticalChart::Line();
|
||||||
const auto length = array.size() - 1;
|
const auto length = array.size() - 1;
|
||||||
line.id = columnId;
|
line.id = (++columnIdCount);
|
||||||
|
line.idString = columnId;
|
||||||
line.y.resize(length);
|
line.y.resize(length);
|
||||||
for (auto i = 0; i < length; i++) {
|
for (auto i = 0; i < length; i++) {
|
||||||
const auto value = array.at(i + 1).toInt();
|
const auto value = array.at(i + 1).toInt();
|
||||||
|
@ -73,7 +75,7 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
|
||||||
|
|
||||||
const auto colorPattern = u"(.*)(#.*)"_q;
|
const auto colorPattern = u"(.*)(#.*)"_q;
|
||||||
for (auto &line : result.lines) {
|
for (auto &line : result.lines) {
|
||||||
const auto colorIt = colors.constFind(line.id);
|
const auto colorIt = colors.constFind(line.idString);
|
||||||
if (colorIt != colors.constEnd() && (*colorIt).isString()) {
|
if (colorIt != colors.constEnd() && (*colorIt).isString()) {
|
||||||
const auto match = QRegularExpression(colorPattern).match(
|
const auto match = QRegularExpression(colorPattern).match(
|
||||||
colorIt->toString());
|
colorIt->toString());
|
||||||
|
@ -82,7 +84,7 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
|
||||||
line.color = QColor(match.captured(2));
|
line.color = QColor(match.captured(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto nameIt = names.constFind(line.id);
|
const auto nameIt = names.constFind(line.idString);
|
||||||
if (nameIt != names.constEnd() && (*nameIt).isString()) {
|
if (nameIt != names.constEnd() && (*nameIt).isString()) {
|
||||||
line.name = nameIt->toString().replace('-', QChar(8212));
|
line.name = nameIt->toString().replace('-', QChar(8212));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user