diff --git a/src/CpintPlot.cpp b/src/CpintPlot.cpp index b56bf3a4a..3ee7a4680 100644 --- a/src/CpintPlot.cpp +++ b/src/CpintPlot.cpp @@ -329,7 +329,7 @@ CpintPlot::deriveCPParameters() } // update t0 if we're using that model - if (useT0) + if (model == 2) t0 = tau / (bests->meanMaxArray(series)[1] / cp - 1) - 1 / 60.0; } while ((fabs(tau - tau_prev) > tau_delta_max) || @@ -354,9 +354,13 @@ CpintPlot::plot_CP_curve(CpintPlot *thisPlot, // the plot we're currently di if (cp <= 0) return; + // if no model, then there's nothing to do + if (model == 0) + return; + // populate curve data with a CP curve const int curve_points = 100; - double tmin = useT0 ? 1.00/60.00 : tau; // we want to see the entire curve for 3 model + double tmin = model == 2 ? 1.00/60.00 : tau; // we want to see the entire curve for 3 model double tmax = 180.0; QVector cp_curve_power(curve_points); QVector cp_curve_time(curve_points); @@ -375,7 +379,7 @@ CpintPlot::plot_CP_curve(CpintPlot *thisPlot, // the plot we're currently di // generate a plot QString curve_title; #if 0 //XXX ? - if (useT0) { + if (model == 2) { curve_title.sprintf("CP=%.1f w; W'/CP=%.2f m; t0=%.1f s", cp, tau, 60 * t0); @@ -460,7 +464,7 @@ CpintPlot::plot_CP_curve(CpintPlot *thisPlot, // the plot we're currently di - if (useExtendedCP) { + if (model == 3) { extendedCPCurve4 = ecp->getPlotCurveForExtendedCP_4_3(athleteModeleCP4); extendedCPCurve4->attach(thisPlot); @@ -505,7 +509,9 @@ CpintPlot::clear_CP_Curves() void CpintPlot::plot_allCurve(CpintPlot *thisPlot, int n_values, - const double *power_values) + const double *power_values, + QColor plotColor, + bool forcePlotColor) { //clear_CP_Curves(); @@ -555,6 +561,8 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) curve->setRenderHint(QwtPlotItem::RenderAntialiased); QPen pen(color.darker(200)); + if (forcePlotColor) // not default + pen.setColor(plotColor); pen.setWidth(2.0); curve->setPen(pen); curve->attach(thisPlot); @@ -610,7 +618,7 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, QwtPlotCurve *curve = new QwtPlotCurve(tr("maximal power")); if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) curve->setRenderHint(QwtPlotItem::RenderAntialiased); - QPen pen(GColor(CCP)); + QPen pen((plotColor)); pen.setWidth(appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble()); curve->setPen(pen); QColor brush_color = GColor(CCP); @@ -654,6 +662,11 @@ void CpintPlot::calculate(RideItem *rideItem) { clear_CP_Curves(); + + // Compare Mode + if (context->isCompareDateRanges) + return calculateForDateRanges(context->compareDateRanges); + if (!rideItem) return; QString fileName = rideItem->fileName; @@ -678,7 +691,7 @@ CpintPlot::calculate(RideItem *rideItem) cp = tau = t0 = 0; deriveCPParameters(); - if (useExtendedCP) { + if (model == 3) { // calculate extended CP model from all-time best data //athleteModeleCP2 = ecp->deriveExtendedCP_2_3_Parameters(bests, series, sanI1, sanI2, anI1, anI2, aeI1, aeI2, laeI1, laeI2); athleteModeleCP4 = ecp->deriveExtendedCP_4_3_Parameters(true, bests, series, sanI1, sanI2, anI1, anI2, aeI1, aeI2, laeI1, laeI2); @@ -700,7 +713,7 @@ CpintPlot::calculate(RideItem *rideItem) CPCurve->setPen(pen); } - if (useExtendedCP && CPCurve) CPCurve->setVisible(false); + if (model == 3 && CPCurve) CPCurve->setVisible(false); else if (CPCurve) CPCurve->setVisible(true); } @@ -712,7 +725,7 @@ CpintPlot::calculate(RideItem *rideItem) for (int i = 0; i < bests->meanMaxArray(series).size(); ++i) { if (bests->meanMaxArray(series)[i] > 0) maxNonZero = i; } - plot_allCurve(this, maxNonZero, bests->meanMaxArray(series).constData() + 1); + plot_allCurve(this, maxNonZero, bests->meanMaxArray(series).constData() + 1, GColor(CCP), false); } } else { @@ -878,6 +891,9 @@ CpintPlot::calculate(RideItem *rideItem) } refreshReferenceLines(rideItem); + + if (context->isCompareIntervals) + return calculateForIntervals(context->compareIntervals); replot(); } @@ -947,7 +963,7 @@ CpintPlot::setShadeMode(int x) // model parameters! void -CpintPlot::setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, int laeI1, int laeI2, bool useT0, bool useExtendedCP) +CpintPlot::setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, int laeI1, int laeI2, int model) { this->anI1 = double(anI1) / double(60.00f); this->anI2 = double(anI2) / double(60.00f); @@ -959,8 +975,7 @@ CpintPlot::setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2 this->laeI1 = double(laeI1) / double(60.00f); this->laeI2 = double(laeI2) / double(60.00f); - this->useT0 = useT0; - this->useExtendedCP = useExtendedCP; + this->model = model; // wipe away previous effort if (CPCurve) { @@ -1269,3 +1284,117 @@ CpintPlot::calculateCentile(RideItem *rideItem) qDebug() << "end plotting " << elapsed.elapsed(); } + +void +CpintPlot::calculateForDateRanges(QList compareDateRanges) +{ + clear_CP_Curves(); + // If no range + if (compareDateRanges.count() == 0) return; + + int shadeModeOri = shadeMode; + int modelOri = model; + + double ymax = 0; + QList bests; + + model = 0; // no model in compareDateRanges + + // prepare aggregates + for (int i = 0; i < compareDateRanges.size(); ++i) { + CompareDateRange range = compareDateRanges.at(i); + if (range.isChecked()) { + RideFileCache bestsForRange(context, range.start, range.end, isFiltered, files, rangemode); + bests.append(bestsForRange); + + if (bestsForRange.meanMaxArray(series).size()) { + int maxNonZero = 0; + for (int i = 0; i < bestsForRange.meanMaxArray(series).size(); ++i) { + if (bestsForRange.meanMaxArray(series)[i] > 0) maxNonZero = i; + } + if (i>0) + shadeMode = 0; + plot_allCurve(this, maxNonZero, bestsForRange.meanMaxArray(series).constData() + 1, range.color, true); + + foreach(double v, bestsForRange.meanMaxArray(series)) { + if (v > ymax) ymax = v; + } + } + } + } + setAxisScale(yLeft, 0, 1.1*ymax); + shadeMode = shadeModeOri; + model = modelOri; + + replot(); +} + +void +CpintPlot::calculateForIntervals(QList compareIntervals) +{ + // unselect current intervals + for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { + context->athlete->allIntervalItems()->child(i)->setSelected(false); + } + + // Remove curve from current Ride + if (thisCurve) { + delete thisCurve; + thisCurve = NULL; + } + + + // If no intervals + if (compareIntervals.count() == 0) return; + + // prepare aggregates + for (int i = 0; i < compareIntervals.size(); ++i) { + CompareInterval interval = compareIntervals.at(i); + + if (interval.isChecked()) { + // compute the mean max + QVectorvector; + MeanMaxComputer thread1(interval.data, vector, series); thread1.run(); + thread1.wait(); + + // no data! + if (vector.count() == 0) return; + + // create curve data arrays + plot_interval(this, vector, interval.color); + } + } + + replot(); +} + +void +CpintPlot::plot_interval(CpintPlot *thisPlot, QVector vector, QColor intervalColor) +{ + int i=0; + QVectorx; + QVectory; + x.resize(vector.size()); + y.resize(vector.size()); + foreach(float yv, vector) { x << i / 60.00; y << yv; i++; } + + // create a curve! + QwtPlotCurve *curve = new QwtPlotCurve(); + if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) + curve->setRenderHint(QwtPlotItem::RenderAntialiased); + + // set its color - based upon index in intervals! + QPen pen(intervalColor); + pen.setWidth(2.0); + pen.setStyle(Qt::DotLine); + intervalColor.setAlpha(64); + QBrush brush = QBrush(intervalColor); + curve->setBrush(brush); + curve->setPen(pen); + curve->setSamples(x.data(), y.data(), x.count()-1); + + // attach and register + curve->attach(thisPlot); + + allCurves.append(curve); +} diff --git a/src/CpintPlot.h b/src/CpintPlot.h index c45a8d761..50a1d4cac 100644 --- a/src/CpintPlot.h +++ b/src/CpintPlot.h @@ -89,10 +89,10 @@ class CpintPlot : public QwtPlot const QwtPlotCurve *getThisCurve() const { return thisCurve; } const QwtPlotCurve *getCPCurve() const { return CPCurve; } - void setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, int laeI1, int laeI2, bool useT0, bool useExtendedCP); + void setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, int laeI1, int laeI2, int model); // model type & intervals - bool useT0, useExtendedCP; + int model; double sanI1, sanI2, anI1, anI2, aeI1, aeI2, laeI1, laeI2; double cp, tau, t0; // CP model parameters @@ -119,7 +119,8 @@ class CpintPlot : public QwtPlot void showGrid(int state); void calculate(RideItem *rideItem); void plot_CP_curve(CpintPlot *plot, double cp, double tau, double t0n); - void plot_allCurve(CpintPlot *plot, int n_values, const double *power_values); + void plot_allCurve(CpintPlot *plot, int n_values, const double *power_values, QColor plotColor, bool forcePlotColor); + void plot_interval(CpintPlot *plot, QVector vector, QColor plotColor); void configChanged(); void pointHover(QwtPlotCurve *curve, int index); void setShadeMode(int x); @@ -128,6 +129,9 @@ class CpintPlot : public QwtPlot void setFilter(QStringList); void setRidePlotStyle(int index); + void calculateForDateRanges(QList compareDateRanges); + void calculateForIntervals(QList compareIntervals); + protected: QString path; diff --git a/src/CriticalPowerWindow.cpp b/src/CriticalPowerWindow.cpp index 3056063d9..6b1e66ce5 100644 --- a/src/CriticalPowerWindow.cpp +++ b/src/CriticalPowerWindow.cpp @@ -182,16 +182,22 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, Context *context, boo // model config // 2 or 3 point model ? modelCombo= new QComboBox(this); + modelCombo->addItem("None"); modelCombo->addItem("2 parameter"); modelCombo->addItem("3 parameter"); modelCombo->addItem("ExtendedCP"); - modelCombo->setCurrentIndex(0); + modelCombo->setCurrentIndex(1); cl->addWidget(new QLabel("")); //spacing cl->addRow(new QLabel(tr("CP Model")), modelCombo); cl->addRow(new QLabel(tr(" "))); - cl->addRow(new QLabel(tr("Search Interval")), new QLabel(tr("(seconds)"))); + + intervalLabel = new QLabel(tr("Search Interval")); + secondsLabel = new QLabel(tr("(seconds)")); + cl->addRow(intervalLabel, secondsLabel); + + anLabel = new QLabel(tr("Anaerobic")); anI1SpinBox = new QDoubleSpinBox(this); anI1SpinBox->setDecimals(0); @@ -212,7 +218,9 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, Context *context, boo QHBoxLayout *anLayout = new QHBoxLayout; anLayout->addWidget(anI1SpinBox); anLayout->addWidget(anI2SpinBox); - cl->addRow(new QLabel(tr("Anaerobic")), anLayout); + cl->addRow(anLabel, anLayout); + + aeLabel = new QLabel(tr("Aerobic")); aeI1SpinBox = new QDoubleSpinBox(this); aeI1SpinBox->setDecimals(0); @@ -233,7 +241,7 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, Context *context, boo QHBoxLayout *aeLayout = new QHBoxLayout; aeLayout->addWidget(aeI1SpinBox); aeLayout->addWidget(aeI2SpinBox); - cl->addRow(new QLabel(tr("Aerobic")), aeLayout); + cl->addRow(aeLabel, aeLayout); sanI1SpinBox = new QDoubleSpinBox(this); sanI1SpinBox->setDecimals(0); @@ -293,11 +301,19 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, Context *context, boo if (rangemode) { connect(this, SIGNAL(dateRangeChanged(DateRange)), SLOT(dateRangeChanged(DateRange))); + + // Compare + connect(context, SIGNAL(compareDateRangesStateChanged(bool)), SLOT(forceReplot())); + connect(context, SIGNAL(compareDateRangesChanged()), SLOT(forceReplot())); } else { // when working on a ride we can selecct intervals! connect(cComboSeason, SIGNAL(currentIndexChanged(int)), this, SLOT(seasonSelected(int))); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); - connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged())); + connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged())); + + // Compare + connect(context, SIGNAL(compareIntervalsStateChanged(bool)), SLOT(forceReplot())); + connect(context, SIGNAL(compareIntervalsChanged()), SLOT(forceReplot())); } connect(seriesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setSeries(int))); @@ -355,45 +371,89 @@ CriticalPowerWindow::modelChanged() active = true; switch (modelCombo->currentIndex()) { - case 0 : // 2 param model + case 0 : // None + intervalLabel->hide(); + secondsLabel->hide(); sanLabel->hide(); sanI1SpinBox->hide(); sanI2SpinBox->hide(); + anLabel->hide(); + anI1SpinBox->hide(); + anI2SpinBox->hide(); + aeLabel->hide(); + aeI1SpinBox->hide(); + aeI2SpinBox->hide(); laeLabel->hide(); laeI1SpinBox->hide(); laeI2SpinBox->hide(); + // No default values ! + + break; + + case 1 : // 2 param model + + intervalLabel->show(); + secondsLabel->show(); + anLabel->show(); + sanLabel->hide(); + sanI1SpinBox->hide(); + sanI2SpinBox->hide(); + anLabel->show(); + anI1SpinBox->show(); + anI2SpinBox->show(); + aeLabel->show(); + aeI1SpinBox->show(); + aeI2SpinBox->show(); + laeLabel->hide(); + laeI1SpinBox->hide(); + laeI2SpinBox->hide(); + + // Default values anI1SpinBox->setValue(180); anI2SpinBox->setValue(360); aeI1SpinBox->setValue(1800); aeI2SpinBox->setValue(3600); + break; - case 1 : // 3 param model + case 2 : // 3 param model + intervalLabel->show(); + secondsLabel->show(); sanLabel->hide(); sanI1SpinBox->hide(); sanI2SpinBox->hide(); + anLabel->show(); + anI1SpinBox->show(); + anI2SpinBox->show(); + aeLabel->show(); + aeI1SpinBox->show(); + aeI2SpinBox->show(); laeLabel->hide(); laeI1SpinBox->hide(); laeI2SpinBox->hide(); + // Default values anI1SpinBox->setValue(1800); anI2SpinBox->setValue(2400); aeI1SpinBox->setValue(2400); aeI2SpinBox->setValue(3600); + break; - case 2 : // ExtendedCP + case 3 : // ExtendedCP sanLabel->show(); + secondsLabel->show(); sanI1SpinBox->show(); sanI2SpinBox->show(); laeLabel->show(); laeI1SpinBox->show(); laeI2SpinBox->show(); + // Default values sanI1SpinBox->setValue(20); sanI2SpinBox->setValue(90); anI1SpinBox->setValue(120); @@ -402,6 +462,7 @@ CriticalPowerWindow::modelChanged() aeI2SpinBox->setValue(3000); laeI1SpinBox->setValue(4000); laeI2SpinBox->setValue(30000); + break; } active = false; @@ -424,8 +485,7 @@ CriticalPowerWindow::modelParametersChanged() aeI2SpinBox->value(), laeI1SpinBox->value(), laeI2SpinBox->value(), - modelCombo->currentIndex() == 1 ? true : false, - modelCombo->currentIndex() == 2 ? true : false); + modelCombo->currentIndex()); // and apply if (amVisible() && myRideItem != NULL) { diff --git a/src/CriticalPowerWindow.h b/src/CriticalPowerWindow.h index b5857a8f5..2a33c48a3 100644 --- a/src/CriticalPowerWindow.h +++ b/src/CriticalPowerWindow.h @@ -216,7 +216,10 @@ class CriticalPowerWindow : public GcChartWindow #endif QList intervalCurves; + QLabel *intervalLabel, *secondsLabel; QLabel *sanLabel; + QLabel *anLabel; + QLabel *aeLabel; QLabel *laeLabel; QDoubleSpinBox *sanI1SpinBox, *sanI2SpinBox;