diff --git a/src/CPPlot.cpp b/src/CPPlot.cpp index cc950868c..4c1620bb3 100644 --- a/src/CPPlot.cpp +++ b/src/CPPlot.cpp @@ -748,12 +748,14 @@ CPPlot::plotBests() const double *values = bestsCache->meanMaxArray(rideSeries).constData() + 1; // we can only do shading of the bests curve - // when we have power and the user wants it to + // when we have power or speed and the user wants it to // be a rainbow curve. Otherwise its gonna be plain int shadingCP = 0; double shadingRatio = 1.0; if ((rideSeries == RideFile::wattsKg || rideSeries == RideFile::watts) && shadeMode) shadingCP = dateCP; if (rideSeries == RideFile::wattsKg && shadeMode) shadingRatio = appsettings->cvalue(context->athlete->cyclist, GC_WEIGHT).toDouble(); + double shadingCV = 0.0; + if (rideSeries == RideFile::kph && shadeMode) shadingCV = dateCV; //For veloclinic plot we need to start by using a 2 parameters model if (criticalSeries == CriticalPowerWindow::veloclinicplot) { @@ -779,7 +781,7 @@ CPPlot::plotBests() } if (showBest) { - if (shadingCP == 0) { + if (shadingCP == 0 && shadingCV == 0.0) { // PLAIN CURVE @@ -877,7 +879,7 @@ CPPlot::plotBests() curve->attach(this); bestsCurves.append(curve); - } else { + } else if (shadingCP > 0) { // // RAINBOW CURVE We are plotting power AND the user wants a rainbow @@ -968,11 +970,88 @@ CPPlot::plotBests() allZoneLabels.append(label_mark); } + high = low; + ++zone; + } + } else if (shadingCV > 0.0) { + + // + // RAINBOW CURVE We are plotting speed AND the user wants a rainbow + // + + // set zones from shading CV + QList pace_zone; + int n_zones = context->athlete->paceZones()->lowsFromCV(&pace_zone, shadingCV); + + // now run through each zone and create a curve + int high = maxNonZero - 1; + int zone = 0; + while (zone < n_zones && high > 0) { + + // create the curve + QwtPlotCurve *curve = new QwtPlotCurve(""); + bestsCurves.append(curve); + curve->attach(this); + + // get range for the curve + int low = high - 1; + int nextZone = zone + 1; + if (nextZone >= pace_zone.size()) + low = 0; + else { + while ((low > 0) && (values[low] < pace_zone[nextZone])) + --low; + } + + // set samples + curve->setSamples(time.data() + low, values + low, high - low + 1); + + // set the pen color and line width etc + QColor color = paceZoneColor(zone, n_zones); + if (appsettings->value(this, GC_ANTIALIAS, true).toBool() == true) + curve->setRenderHint(QwtPlotItem::RenderAntialiased); + QPen pen(color.darker(200)); + pen.setColor(GColor(CCP)); //XXX color ? + double width = appsettings->value(this, GC_LINEWIDTH, 0.5).toDouble(); + pen.setWidth(width); + curve->setPen(pen); + + // use a linear gradient + if (shadeMode && shadingCV) { // 0 value means no shading please - and only if proper value for shadingCV + color.setAlpha(128); + QColor color1 = color.darker(); + QLinearGradient linearGradient(0, 0, 0, height()); + linearGradient.setColorAt(0.0, color); + linearGradient.setColorAt(1.0, color1); + linearGradient.setSpread(QGradient::PadSpread); + curve->setBrush(linearGradient); // fill below the line + } + + // now the labels + if (shadeMode) { + + QwtText text(context->athlete->paceZones()->getDefaultZoneName(zone)); + text.setFont(QFont("Helvetica", 20, QFont::Bold)); + color.setAlpha(255); + text.setColor(color); + QwtPlotMarker *label_mark = new QwtPlotMarker(); + + // place the text in the geometric mean in time, at a decent power + double x, y; + x = sqrt(time[low] * time[high]); + y = (values[low] + values[high]) / 5; + + label_mark->setValue(x, y); + label_mark->setLabel(text); + label_mark->attach(this); + allZoneLabels.append(label_mark); + } + high = low; ++zone; } } - } +} // X-AXIS diff --git a/src/CPPlot.h b/src/CPPlot.h index 9d05423e0..e7410a0e6 100644 --- a/src/CPPlot.h +++ b/src/CPPlot.h @@ -73,6 +73,7 @@ class CPPlot : public QwtPlot void setShadeMode(int x); void setShadeIntervals(int x); void setDateCP(int x) { dateCP = x; } + void setDateCV(double x) { dateCV = x; } void setSeries(CriticalPowerWindow::CriticalSeriesType); void setPlotType(int index); void setModel(int sanI1, int sanI2, int anI1, int anI2, @@ -137,6 +138,7 @@ class CPPlot : public QwtPlot Context *context; RideFileCache *rideCache, *bestsCache; int dateCP; + double dateCV; // settings RideFile::SeriesType rideSeries; diff --git a/src/CriticalPowerWindow.cpp b/src/CriticalPowerWindow.cpp index 254b9d4b7..daf19b845 100644 --- a/src/CriticalPowerWindow.cpp +++ b/src/CriticalPowerWindow.cpp @@ -157,7 +157,7 @@ CriticalPowerWindow::CriticalPowerWindow(Context *context, bool rangemode) : // shading shadeCheck = new QCheckBox(this); - QLabel *shading = new QLabel(tr("Power Shading")); + QLabel *shading = new QLabel(tr("Zone Shading")); shadeCheck->setChecked(true); cl->addRow(shading, shadeCheck); @@ -1090,6 +1090,13 @@ CriticalPowerWindow::rideSelected() } else { cpPlot->setDateCP(0); } + if (context->athlete->paceZones()) { + int paceZoneRange = context->athlete->paceZones()->whichRange(currentRide->dateTime.date()); + double CV = paceZoneRange >= 0.0 ? context->athlete->paceZones()->getCV(paceZoneRange) : 0.0; + cpPlot->setDateCV(CV); + } else { + cpPlot->setDateCV(0.0); + } cpPlot->setRide(currentRide); if (!rangemode && currentRide->ride() && currentRide->ride()->dataPoints().count() == 0) @@ -1475,6 +1482,19 @@ CriticalPowerWindow::dateRangeChanged(DateRange dateRange) cpPlot->setDateCP(dateCP); } + // lets work out the average CV configure value + if (context->athlete->paceZones()) { + int fromZoneRange = context->athlete->paceZones()->whichRange(cfrom); + int toZoneRange = context->athlete->paceZones()->whichRange(cto); + + double CVfrom = fromZoneRange >= 0 ? context->athlete->paceZones()->getCV(fromZoneRange) : 0.0; + double CVto = toZoneRange >= 0 ? context->athlete->paceZones()->getCV(toZoneRange) : CVfrom; + if (CVfrom == 0.0) CVfrom = CVto; + double dateCV = (CVfrom + CVto) / 2.0; + + cpPlot->setDateCV(dateCV); + } + cpPlot->setDateRange(dateRange.from, dateRange.to); }