Overview Bubble on Trends View

.. repurpose the interval bubble to show activities instead of
   intervals.

.. changed the animation/transition to work better when looking at
   seasons by resizing the axes before updating the points.
This commit is contained in:
Mark Liversedge
2020-06-26 22:38:54 +01:00
parent ac5ae5027b
commit 13c91cf67d
3 changed files with 265 additions and 141 deletions

View File

@@ -133,6 +133,7 @@ OverviewWindow::getConfiguration() const
}
break;
case OverviewItemType::INTERVAL:
case OverviewItemType::ACTIVITIES:
{
IntervalOverviewItem *interval = reinterpret_cast<IntervalOverviewItem*>(item);
config += "\"xsymbol\":\"" + QString("%1").arg(interval->xsymbol) + "\",";
@@ -456,6 +457,7 @@ OverviewWindow::setConfiguration(QString config)
break;
case OverviewItemType::INTERVAL :
case OverviewItemType::ACTIVITIES:
{
QString xsymbol=obj["xsymbol"].toString();
QString ysymbol=obj["ysymbol"].toString();

View File

@@ -51,16 +51,17 @@ static bool _registerItems()
ChartSpaceItemRegistry &registry = ChartSpaceItemRegistry::instance();
// Register TYPE SHORT DESCRIPTION SCOPE CREATOR
registry.addItem(OverviewItemType::METRIC, QObject::tr("Metric"), QObject::tr("Metric and Sparkline"), OverviewScope::ANALYSIS|OverviewScope::TRENDS, MetricOverviewItem::create);
registry.addItem(OverviewItemType::KPI, QObject::tr("KPI"), QObject::tr("KPI calculation and progress bar"), OverviewScope::ANALYSIS|OverviewScope::TRENDS, KPIOverviewItem::create);
registry.addItem(OverviewItemType::TOPN, QObject::tr("Bests"), QObject::tr("Ranked list of bests"), OverviewScope::TRENDS, TopNOverviewItem::create);
registry.addItem(OverviewItemType::META, QObject::tr("Metadata"), QObject::tr("Metadata and Sparkline"), OverviewScope::ANALYSIS, MetaOverviewItem::create);
registry.addItem(OverviewItemType::ZONE, QObject::tr("Zones"), QObject::tr("Zone Histogram"), OverviewScope::ANALYSIS|OverviewScope::TRENDS, ZoneOverviewItem::create);
registry.addItem(OverviewItemType::RPE, QObject::tr("RPE"), QObject::tr("RPE Widget"), OverviewScope::ANALYSIS, RPEOverviewItem::create);
registry.addItem(OverviewItemType::INTERVAL, QObject::tr("Intervals"), QObject::tr("Interval Bubble Chart"), OverviewScope::ANALYSIS, IntervalOverviewItem::create);
registry.addItem(OverviewItemType::PMC, QObject::tr("PMC"), QObject::tr("PMC Status Summary"), OverviewScope::ANALYSIS, PMCOverviewItem::create);
registry.addItem(OverviewItemType::ROUTE, QObject::tr("Route"), QObject::tr("Route Summary"), OverviewScope::ANALYSIS, RouteOverviewItem::create);
registry.addItem(OverviewItemType::DONUT, QObject::tr("Donut"), QObject::tr("Metric breakdown by category"), OverviewScope::TRENDS, DonutOverviewItem::create);
registry.addItem(OverviewItemType::METRIC, QObject::tr("Metric"), QObject::tr("Metric and Sparkline"), OverviewScope::ANALYSIS|OverviewScope::TRENDS, MetricOverviewItem::create);
registry.addItem(OverviewItemType::KPI, QObject::tr("KPI"), QObject::tr("KPI calculation and progress bar"), OverviewScope::ANALYSIS|OverviewScope::TRENDS, KPIOverviewItem::create);
registry.addItem(OverviewItemType::TOPN, QObject::tr("Bests"), QObject::tr("Ranked list of bests"), OverviewScope::TRENDS, TopNOverviewItem::create);
registry.addItem(OverviewItemType::META, QObject::tr("Metadata"), QObject::tr("Metadata and Sparkline"), OverviewScope::ANALYSIS, MetaOverviewItem::create);
registry.addItem(OverviewItemType::ZONE, QObject::tr("Zones"), QObject::tr("Zone Histogram"), OverviewScope::ANALYSIS|OverviewScope::TRENDS, ZoneOverviewItem::create);
registry.addItem(OverviewItemType::RPE, QObject::tr("RPE"), QObject::tr("RPE Widget"), OverviewScope::ANALYSIS, RPEOverviewItem::create);
registry.addItem(OverviewItemType::INTERVAL, QObject::tr("Intervals"), QObject::tr("Interval Bubble Chart"), OverviewScope::ANALYSIS, IntervalOverviewItem::createInterval);
registry.addItem(OverviewItemType::ACTIVITIES, QObject::tr("Activities"), QObject::tr("Activities Bubble Chart"), OverviewScope::TRENDS, IntervalOverviewItem::createActivities);
registry.addItem(OverviewItemType::PMC, QObject::tr("PMC"), QObject::tr("PMC Status Summary"), OverviewScope::ANALYSIS, PMCOverviewItem::create);
registry.addItem(OverviewItemType::ROUTE, QObject::tr("Route"), QObject::tr("Route Summary"), OverviewScope::ANALYSIS, RouteOverviewItem::create);
registry.addItem(OverviewItemType::DONUT, QObject::tr("Donut"), QObject::tr("Metric breakdown by category"), OverviewScope::TRENDS, DonutOverviewItem::create);
return true;
}
@@ -402,18 +403,15 @@ MetaOverviewItem::~MetaOverviewItem()
IntervalOverviewItem::IntervalOverviewItem(ChartSpace *parent, QString name, QString xsymbol, QString ysymbol, QString zsymbol) : ChartSpaceItem(parent, name)
{
this->type = OverviewItemType::INTERVAL;
if (parent->scope == OverviewScope::ANALYSIS) this->type = OverviewItemType::INTERVAL;
if (parent->scope == OverviewScope::TRENDS) this->type = OverviewItemType::ACTIVITIES;
this->xsymbol = xsymbol;
this->ysymbol = ysymbol;
this->zsymbol = zsymbol;
// we may plot the metric sparkline if the tile is big enough
bubble = new BubbleViz(this, "intervals");
RideMetricFactory &factory = RideMetricFactory::instance();
const RideMetric *xm = factory.rideMetric(xsymbol);
const RideMetric *ym = factory.rideMetric(ysymbol);
bubble->setAxisNames(xm ? xm->name() : "NA", ym ? ym->name() : "NA");
}
IntervalOverviewItem::~IntervalOverviewItem()
@@ -1409,11 +1407,80 @@ RouteOverviewItem::setData(RideItem *item)
}
}
void
IntervalOverviewItem::setDateRange(DateRange dr)
{
// for metrics lets truncate to today
if (dr.to > QDate::currentDate()) dr.to = QDate::currentDate();
RideMetricFactory &factory = RideMetricFactory::instance();
const RideMetric *xm = factory.rideMetric(xsymbol);
const RideMetric *ym = factory.rideMetric(ysymbol);
xdp = xm->precision();
ydp = ym->precision();
bubble->setAxisNames(xm ? xm->name() : "NA", ym ? ym->name() : "NA");
Specification spec;
spec.setDateRange(dr);
setFilter(this, spec);
double minx = 0;
double maxx = 0;
double miny = 0;
double maxy = 0;
bool first=true;
QList<BPointF> points;
foreach(RideItem *item, parent->context->athlete->rideCache->rides()) {
if (!spec.pass(item)) continue;
// get the x and y VALUE
double x = item->getForSymbol(xsymbol, parent->context->athlete->useMetricUnits);
double y = item->getForSymbol(ysymbol, parent->context->athlete->useMetricUnits);
double z = item->getForSymbol(zsymbol, parent->context->athlete->useMetricUnits);
BPointF add;
add.x = x;
add.y = y;
add.z = z;
add.fill = item->color;
if (add.fill.red() == 1 && add.fill.green() == 1 && add.fill.blue() == 1) add.fill = GColor(CPLOTMARKER);
add.label = item->getText("Workout Code","blank");
points << add;
if (first || x<minx) minx=x;
if (first || y<miny) miny=y;
if (first || x>maxx) maxx=x;
if (first || y>maxy) maxy=y;
first = false;
}
// set scale
double ydiff = (maxy-miny) / 10.0f;
if (miny >= 0 && ydiff > miny) miny = ydiff;
double xdiff = (maxx-minx) / 10.0f;
if (minx >= 0 && xdiff > minx) minx = xdiff;
maxx=ceil(maxx); minx=floor(minx);
maxy=ceil(maxy); miny=floor(miny);
// set range before points to filter
bubble->setPoints(points, minx,maxx,miny,maxy);
}
void
IntervalOverviewItem::setData(RideItem *item)
{
if (item == NULL || item->ride() == NULL) return;
RideMetricFactory &factory = RideMetricFactory::instance();
const RideMetric *xm = factory.rideMetric(xsymbol);
const RideMetric *ym = factory.rideMetric(ysymbol);
xdp = xm->precision();
ydp = ym->precision();
bubble->setAxisNames(xm ? xm->name() : "NA", ym ? ym->name() : "NA");
double minx = 999999999;
double maxx =-999999999;
double miny = 999999999;
@@ -1451,8 +1518,7 @@ IntervalOverviewItem::setData(RideItem *item)
maxy=round(maxy); miny=round(miny);
// set range before points to filter
bubble->setRange(minx,maxx,miny,maxy);
bubble->setPoints(points);
bubble->setPoints(points, minx,maxx,miny,maxy);
}
@@ -2192,7 +2258,7 @@ OverviewItemConfig::OverviewItemConfig(ChartSpaceItem *item) : QWidget(item->par
}
// metric1, metric2 and metric3
if (item->type == OverviewItemType::INTERVAL) {
if (item->type == OverviewItemType::INTERVAL || item->type == OverviewItemType::ACTIVITIES) {
metric1 = new MetricSelect(this, item->parent->context, MetricSelect::Metric);
connect(metric1, SIGNAL(textChanged(QString)), this, SLOT(dataChanged()));
layout->addRow(tr("X Axis Metric"), metric1);
@@ -2399,6 +2465,7 @@ OverviewItemConfig::setWidgets()
break;
case OverviewItemType::INTERVAL:
case OverviewItemType::ACTIVITIES:
{
IntervalOverviewItem *mi = dynamic_cast<IntervalOverviewItem*>(item);
name->setText(mi->name);
@@ -2504,6 +2571,7 @@ OverviewItemConfig::dataChanged()
break;
case OverviewItemType::INTERVAL:
case OverviewItemType::ACTIVITIES:
{
IntervalOverviewItem *mi = dynamic_cast<IntervalOverviewItem*>(item);
mi->name = name->text();
@@ -2769,13 +2837,24 @@ BubbleViz::BubbleViz(IntervalOverviewItem *parent, QString name) : QGraphicsItem
setGeometry(20,20,100,100);
setZValue(11);
setAcceptHoverEvents(true);
animator=new QPropertyAnimation(this, "transition");
group = new QSequentialAnimationGroup(this);
QParallelAnimationGroup *par = new QParallelAnimationGroup(this);
xaxisAnimation=new QPropertyAnimation(this, "xaxis");
yaxisAnimation=new QPropertyAnimation(this, "yaxis");
par->addAnimation(xaxisAnimation);
par->addAnimation(yaxisAnimation);
group->addAnimation(par);
transitionAnimation=new QPropertyAnimation(this, "transition");
group->addAnimation(transitionAnimation);
}
BubbleViz::~BubbleViz()
{
animator->stop();
delete animator;
group->stop();
delete group;
}
QVariant BubbleViz::itemChange(GraphicsItemChange change, const QVariant &value)
@@ -2835,8 +2914,18 @@ bool scoresBiggerThan(const BubbleVizTuple i1, const BubbleVizTuple i2)
return i1.score > i2.score;
}
void
BubbleViz::setPoints(QList<BPointF> p)
BubbleViz::setPoints(QList<BPointF> p, double minx, double maxx, double miny, double maxy)
{
xaxisAnimation->setStartValue(QPointF(this->minx,this->maxx));
xaxisAnimation->setEndValue(QPointF(minx,maxx));
yaxisAnimation->setStartValue(QPointF(this->miny,this->maxy));
yaxisAnimation->setEndValue(QPointF(miny,maxy));
xaxisAnimation->setEasingCurve(QEasingCurve::OutQuad);
xaxisAnimation->setDuration(400);
yaxisAnimation->setEasingCurve(QEasingCurve::OutQuad);
yaxisAnimation->setDuration(400);
transition = -1; // work on axis first
oldpoints = this->points;
oldmean = this->mean;
@@ -2894,12 +2983,12 @@ BubbleViz::setPoints(QList<BPointF> p)
oldpoints = matches;
// stop any transition animation currently running
animator->stop();
animator->setStartValue(0);
animator->setEndValue(256);
animator->setEasingCurve(QEasingCurve::OutQuad);
animator->setDuration(1000);
animator->start();
group->stop();
transitionAnimation->setStartValue(0);
transitionAnimation->setEndValue(256);
transitionAnimation->setEasingCurve(QEasingCurve::OutQuad);
transitionAnimation->setDuration(400);
group->start();
}
static double pointDistance(QPointF a, QPointF b)
@@ -2936,100 +3025,139 @@ BubbleViz::paint(QPainter*painter, const QStyleOptionGraphicsItem *, QWidget*)
double yratio = plotarea.height() / (maxy*1.03); // boundary space
// old values when transitioning
double oxratio = plotarea.width() / (oldmaxx*1.03);
double oyratio = plotarea.height() / (oldmaxy*1.03); // boundary space
double oxratio = plotarea.width() / (maxx*1.03);
double oyratio = plotarea.height() / (maxy*1.03); // boundary space
// run through each point
double area = 10000; // max size
if (parent->parent->scope == OverviewScope::TRENDS) area /= 2; // smaller on trends so many to see
// remember the one we are nearest
BPointF nearest;
double nearvalue = -1;
int index=0;
foreach(BPointF point, points) {
if (transition >= 0) {
if (point.x < minx || point.x > maxx ||
point.y < miny || point.y > maxy ||
!std::isfinite(point.z) || std::isnan(point.z)) {
int index=0;
foreach(BPointF point, points) {
if (point.x < minx || point.x > maxx ||
point.y < miny || point.y > maxy ||
!std::isfinite(point.z) || std::isnan(point.z)) {
index++;
continue;
}
// resize if transitioning
QPointF center(plotarea.left() + (xratio * point.x), plotarea.bottom() - (yratio * point.y));
int alpha = 200;
if (parent->parent->scope == OverviewScope::TRENDS) alpha /= 2;
double size = (point.z / mean) * area;
if (size > area * 6) size=area*6;
if (size < 600) size=600;
if (transition < 256 && oldpoints.count()) {
if (oldpoints[index].x != 0 || oldpoints[index].y != 0) {
// where it was
QPointF oldcenter = QPointF(plotarea.left() + (oxratio * oldpoints[index].x),
plotarea.bottom() - (oyratio * oldpoints[index].y));
// transition to new point
center.setX(center.x() - (double(255-transition) * ((center.x()-oldcenter.x())/255.0f)));
center.setY(center.y() - (double(255-transition) * ((center.y()-oldcenter.y())/255.0f)));
// transition bubble size
double oldsize = (oldpoints[index].z / oldmean) * area;
if (oldsize > area * 6) oldsize=area*6;
if (oldsize < 600) oldsize=600;
size = size - (double(255-transition) * ((size-oldsize)/255.0f));
} else {
// just make it appear
alpha = ((parent->parent->scope == OverviewScope::TRENDS ? 100 : 200.0f)/255.0f) * transition;
}
}
// once transitioned clear them away
if (transition == 256 && oldpoints.count()) oldpoints.clear();
QColor color = point.fill;
color.setAlpha(alpha);
painter->setBrush(color);
painter->setPen(QColor(150,150,150));
double radius = sqrt(size/3.1415927f);
painter->drawEllipse(center, radius, radius);
// is the cursor hovering over me?
double distance;
if (transition == 256 && hover && (distance=pointDistance(center, plotarea.topLeft()+plotpos)) <= radius) {
// is this the nearest ?
if (nearvalue == -1 || distance < nearvalue) {
nearest = point;
nearvalue = distance;
}
}
index++;
continue;
}
// resize if transitioning
QPointF center(plotarea.left() + (xratio * point.x), plotarea.bottom() - (yratio * point.y));
int alpha = 200;
// if we're transitioning
while (transition < 256 && index < oldpoints.count()) {
QPointF oldcenter = QPointF(plotarea.left() + (oxratio * oldpoints[index].x),
plotarea.bottom() - (oyratio * oldpoints[index].y));
double size = (point.z / mean) * area;
if (size > area * 6) size=area*6;
if (size < 600) size=600;
// fade out
QColor color = oldpoints[index].fill;
double alpha = ((parent->parent->scope == OverviewScope::TRENDS ? 100 : 200.0f)/255.0f) * transition;
color.setAlpha(alpha);
painter->setBrush(color);
painter->setPen(Qt::NoPen);
if (transition < 256 && oldpoints.count()) {
if (oldpoints[index].x != 0 || oldpoints[index].y != 0) {
// where it was
QPointF oldcenter = QPointF(plotarea.left() + (oxratio * oldpoints[index].x),
plotarea.bottom() - (oyratio * oldpoints[index].y));
double size = (oldpoints[index].z/oldmean) * area;
if (size > area * 6) size=area*6;
if (size < 600) size=600;
double radius = sqrt(size/3.1415927f);
painter->drawEllipse(oldcenter, radius, radius);
// transition to new point
center.setX(center.x() - (double(255-transition) * ((center.x()-oldcenter.x())/255.0f)));
center.setY(center.y() - (double(255-transition) * ((center.y()-oldcenter.y())/255.0f)));
// transition bubble size
double oldsize = (oldpoints[index].z / oldmean) * area;
if (oldsize > area * 6) oldsize=area*6;
if (oldsize < 600) oldsize=600;
size = size - (double(255-transition) * ((size-oldsize)/255.0f));
} else {
// just make it appear
alpha = (200.0f/255.0f) * transition;
}
// hide the old ones
index++;
}
// once transitioned clear them away
if (transition == 256 && oldpoints.count()) oldpoints.clear();
} else {
// when transition is -1 we are rescaling the axes first
int index=0;
foreach(BPointF point, oldpoints) {
QColor color = point.fill;
color.setAlpha(alpha);
painter->setBrush(color);
painter->setPen(QColor(150,150,150));
double radius = sqrt(size/3.1415927f);
painter->drawEllipse(center, radius, radius);
// is the cursor hovering over me?
double distance;
if (transition == 256 && hover && (distance=pointDistance(center, plotarea.topLeft()+plotpos)) <= radius) {
// is this the nearest ?
if (nearvalue == -1 || distance < nearvalue) {
nearest = point;
nearvalue = distance;
if (point.x < minx || point.x > maxx ||
point.y < miny || point.y > maxy ||
!std::isfinite(point.z) || std::isnan(point.z)) {
index++;
continue;
}
// resize if transitioning
QPointF center(plotarea.left() + (xratio * point.x), plotarea.bottom() - (yratio * point.y));
int alpha = 200;
if (parent->parent->scope == OverviewScope::TRENDS) alpha /= 2;
double size = (point.z / mean) * area;
if (size > area * 6) size=area*6;
if (size < 600) size=600;
QColor color = point.fill;
color.setAlpha(alpha);
painter->setBrush(color);
painter->setPen(QColor(150,150,150));
double radius = sqrt(size/3.1415927f);
painter->drawEllipse(center, radius, radius);
index++;
}
index++;
}
// if we're transitioning
while (transition < 256 && index < oldpoints.count()) {
QPointF oldcenter = QPointF(plotarea.left() + (oxratio * oldpoints[index].x),
plotarea.bottom() - (oyratio * oldpoints[index].y));
// fade out
QColor color = oldpoints[index].fill;
color.setAlpha(200 - (200.0f/255.0f) * double(transition));
painter->setBrush(color);
painter->setPen(Qt::NoPen);
double size = (oldpoints[index].z/oldmean) * area;
if (size > area * 6) size=area*6;
if (size < 600) size=600;
double radius = sqrt(size/3.1415927f);
painter->drawEllipse(oldcenter, radius, radius);
// hide the old ones
index++;
}
painter->setBrush(Qt::NoBrush);
@@ -3042,19 +3170,6 @@ BubbleViz::paint(QPainter*painter, const QStyleOptionGraphicsItem *, QWidget*)
painter->setPen(Qt::red);
//DIAG painter->drawRect(xlabelspace);
// x-axis title
QRectF xtitlespace = QRectF(plotarea.x(), xlabelspace.bottom(), plotarea.width(), ROWHEIGHT);
painter->setPen(Qt::yellow);
//DIAG painter->drawRect(xtitlespace);
QTextOption midcenter;
midcenter.setAlignment(Qt::AlignVCenter|Qt::AlignCenter);
painter->setPen(QColor(150,150,150));
painter->setFont(parent->parent->smallfont);
painter->drawText(xtitlespace, xlabel, midcenter);
// y-axis labels
QRectF ylabelspace = QRectF(plotarea.x()-20-ROWHEIGHT, plotarea.y(), ROWHEIGHT, plotarea.height());
painter->setPen(Qt::red);
@@ -3080,19 +3195,25 @@ BubbleViz::paint(QPainter*painter, const QStyleOptionGraphicsItem *, QWidget*)
// x-axis range
QFontMetrics sfm(parent->parent->smallfont);
QRectF bminx = sfm.tightBoundingRect(QString("%1").arg(minx));
QRectF bmaxx = sfm.tightBoundingRect(QString("%1").arg(maxx));
painter->drawText(xlabelspace.left() + (minx*xratio) - (bminx.width()/2), xlabelspace.bottom(), QString("%1").arg(minx));
painter->drawText(xlabelspace.left() + (maxx*xratio) - (bmaxx.width()/2), xlabelspace.bottom(), QString("%1").arg(maxx));
QRectF bminx = sfm.tightBoundingRect(QString("%1").arg(round(minx)));
QRectF bmaxx = sfm.tightBoundingRect(QString("%1").arg(round(maxx)));
painter->drawText(xlabelspace.left() + (minx*xratio) - (bminx.width()/2), xlabelspace.bottom(), QString("%1").arg(round(minx)));
painter->drawText(xlabelspace.left() + (maxx*xratio) - (bmaxx.width()/2), xlabelspace.bottom(), QString("%1").arg(round(maxx)));
// x-axis title - offset from minx
QRectF xtitlespace = QRectF(plotarea.x() + (minx*xratio), xlabelspace.bottom(), plotarea.width() - (minx*xratio), ROWHEIGHT);
painter->setPen(QColor(150,150,150));
painter->setFont(parent->parent->smallfont);
painter->drawText(xtitlespace, xlabel, Qt::AlignCenter|Qt::AlignVCenter);
// draw minimum value
painter->drawLine(QPointF(plotarea.left(), plotarea.bottom() - (miny*yratio)),
QPointF(plotarea.left(), plotarea.bottom() - (maxy*yratio)));
// y-axis range
QRectF bminy = sfm.tightBoundingRect(QString("%1").arg(miny));
QRectF bmaxy = sfm.tightBoundingRect(QString("%1").arg(maxy));
painter->drawText(ylabelspace.right() - bmaxy.width(), ylabelspace.bottom()-(maxy*yratio) + (bmaxy.height()/2), QString("%1").arg(maxy));
painter->drawText(ylabelspace.right() - bminy.width(), ylabelspace.bottom()-(miny*yratio) + (bminy.height()/2), QString("%1").arg(miny));
QRectF bminy = sfm.tightBoundingRect(QString("%1").arg(round(miny)));
QRectF bmaxy = sfm.tightBoundingRect(QString("%1").arg(round(maxy)));
painter->drawText(ylabelspace.right() - bmaxy.width(), ylabelspace.bottom()-(maxy*yratio) + (bmaxy.height()/2), QString("%1").arg(round(maxy)));
painter->drawText(ylabelspace.right() - bminy.width(), ylabelspace.bottom()-(miny*yratio) + (bminy.height()/2), QString("%1").arg(round(miny)));
// hover point?
painter->setPen(GColor(CPLOTMARKER));
@@ -3106,14 +3227,14 @@ BubbleViz::paint(QPainter*painter, const QStyleOptionGraphicsItem *, QWidget*)
QPointF center(plotarea.left() + (xratio * nearest.x), plotarea.bottom() - (yratio * nearest.y));
// xlabel
QString xlab = QString("%1").arg(nearest.x, 0, 'f', 0);
QString xlab = Utils::removeDP(QString("%1").arg(nearest.x,0,'f',parent->xdp));
bminx = tfm.tightBoundingRect(QString("%1").arg(xlab));
bminx.moveTo(center.x() - (bminx.width()/2), xlabelspace.bottom()-bminx.height());
painter->fillRect(bminx, QBrush(GColor(CCARDBACKGROUND))); // overwrite range labels
painter->drawText(center.x() - (bminx.width()/2), xlabelspace.bottom(), xlab);
// ylabel
QString ylab = QString("%1").arg(nearest.y, 0, 'f', 0);
QString ylab = Utils::removeDP(QString("%1").arg(nearest.y,0,'f',parent->ydp ? 1 : 0));
bminy = tfm.tightBoundingRect(QString("%1").arg(ylab));
bminy.moveTo(ylabelspace.right() - bminy.width(), center.y() - (bminy.height()/2));
painter->fillRect(bminy, QBrush(GColor(CCARDBACKGROUND))); // overwrite range labels

View File

@@ -46,7 +46,7 @@ class ProgressBar;
#define ROUTEPOINTS 250
// types we use start from 100 to avoid clashing with main chart types
enum OverviewItemType { RPE=100, METRIC, META, ZONE, INTERVAL, PMC, ROUTE, KPI, TOPN, DONUT };
enum OverviewItemType { RPE=100, METRIC, META, ZONE, INTERVAL, PMC, ROUTE, KPI, TOPN, DONUT, ACTIVITIES };
//
// Configuration widget for ALL Overview Items
@@ -392,13 +392,15 @@ class IntervalOverviewItem : public ChartSpaceItem
void itemPaint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);
void itemGeometryChanged();
void setData(RideItem *item);
void setDateRange(DateRange) {} // doesn't support trends view
void setDateRange(DateRange);
QWidget *config() { return new OverviewItemConfig(this); }
// create and config
static ChartSpaceItem *create(ChartSpace *parent) { return new IntervalOverviewItem(parent, tr("Intervals"), "elapsed_time", "average_power", "workout_time"); }
static ChartSpaceItem *createInterval(ChartSpace *parent) { return new IntervalOverviewItem(parent, tr("Intervals"), "elapsed_time", "average_power", "workout_time"); }
static ChartSpaceItem *createActivities(ChartSpace *parent) { return new IntervalOverviewItem(parent, tr("Activities"), "workout_time", "average_power", "coggan_tss"); }
QString xsymbol, ysymbol, zsymbol;
int xdp, ydp;
BubbleViz *bubble;
};
@@ -430,6 +432,8 @@ class BubbleViz : public QObject, public QGraphicsItem
// want a meta property for property animation
Q_PROPERTY(int transition READ getTransition WRITE setTransition)
Q_PROPERTY(QPointF yaxis READ getYAxis WRITE setYAxis)
Q_PROPERTY(QPointF xaxis READ getXAxis WRITE setXAxis)
public:
BubbleViz(IntervalOverviewItem *parent, QString name=""); // create and say how many days
@@ -443,19 +447,15 @@ class BubbleViz : public QObject, public QGraphicsItem
int getTransition() const {return transition;}
void setTransition(int x) { if (transition !=x) {transition=x; update();}}
// null members for now just get hooked up
void setPoints(QList<BPointF>points);
// axes
QPointF getYAxis() const { return QPointF(miny,maxy); }
void setYAxis(QPointF x) { miny=x.x(); maxy=x.y(); update(); }
QPointF getXAxis() const { return QPointF(minx,maxx); }
void setXAxis(QPointF x) { minx=x.x(); maxx=x.y(); update(); }
// null members for now just get hooked up
void setPoints(QList<BPointF>points, double minx, double maxx, double miny, double maxy);
void setRange(double minx, double maxx, double miny, double maxy) {
oldminx = this->minx;
oldminy = this->miny;
oldmaxx = this->maxx;
oldmaxy = this->maxy;
this->minx=minx;
this->maxx=maxx;
this->miny=miny;
this->maxy=maxy;
}
void setAxisNames(QString xlabel, QString ylabel) { this->xlabel=xlabel; this->ylabel=ylabel; update(); }
// needed as pure virtual in QGraphicsItem
@@ -482,8 +482,9 @@ class BubbleViz : public QObject, public QGraphicsItem
QList <BPointF> oldpoints; // for animation
int transition;
double oldmean;
double oldminx,oldmaxx,oldminy,oldmaxy;
QPropertyAnimation *animator;
QSequentialAnimationGroup *group;
QPropertyAnimation *transitionAnimation, *xaxisAnimation, *yaxisAnimation;
// chart settings
QList <BPointF> points;