diff --git a/src/AllPlot.cpp b/src/AllPlot.cpp index e2bec0d60..a16dde105 100644 --- a/src/AllPlot.cpp +++ b/src/AllPlot.cpp @@ -152,7 +152,12 @@ class AllPlotZoneLabel: public QwtPlotItem ); text = QwtText(zone_names[zone_number]); - text.setFont(QFont("Helvetica",24, QFont::Bold)); + if (_parent->referencePlot == NULL) { + text.setFont(QFont("Helvetica",24, QFont::Bold)); + } else { + text.setFont(QFont("Helvetica",12, QFont::Bold)); + } + QColor text_color = zoneColor(zone_number, num_zones); text_color.setAlpha(64); text.setColor(text_color); @@ -202,6 +207,8 @@ AllPlot::AllPlot(QWidget *parent, MainWindow *mainWindow): boost::shared_ptr settings = GetApplicationSettings(); unit = settings->value(GC_UNIT); + referencePlot = NULL; + useMetricUnits = (unit.toString() == "Metric"); smooth = settings->value(GC_RIDE_PLOT_SMOOTHING).toInt(); @@ -311,6 +318,10 @@ void AllPlot::refreshZoneLabels() void AllPlot::recalc() { + if (referencePlot !=NULL){ + return; + } + if (timeArray.empty()) return; int rideTimeSecs = (int) ceil(timeArray[arrayLength - 1]); @@ -337,13 +348,13 @@ AllPlot::recalc() QList list; - QVector smoothWatts(rideTimeSecs + 1); - QVector smoothHr(rideTimeSecs + 1); - QVector smoothSpeed(rideTimeSecs + 1); - QVector smoothCad(rideTimeSecs + 1); - QVector smoothTime(rideTimeSecs + 1); - QVector smoothDistance(rideTimeSecs + 1); - QVector smoothAltitude(rideTimeSecs + 1); + smoothWatts.resize(rideTimeSecs + 1); //(rideTimeSecs + 1); + smoothHr.resize(rideTimeSecs + 1); + smoothSpeed.resize(rideTimeSecs + 1); + smoothCad.resize(rideTimeSecs + 1); + smoothTime.resize(rideTimeSecs + 1); + smoothDistance.resize(rideTimeSecs + 1); + smoothAltitude.resize(rideTimeSecs + 1); for (int secs = 0; ((secs < smooth) && (secs < rideTimeSecs)); ++secs) { @@ -471,7 +482,10 @@ AllPlot::setYMax() { if (wattsCurve->isVisible()) { setAxisTitle(yLeft, "Watts"); - setAxisScale(yLeft, 0.0, 1.05 * wattsCurve->maxYValue()); + if (referencePlot == NULL) + setAxisScale(yLeft, 0.0, 1.05 * wattsCurve->maxYValue()); + else + setAxisScale(yLeft, 0.0, 1.05 * referencePlot->wattsCurve->maxYValue()); setAxisLabelRotation(yLeft,270); setAxisLabelAlignment(yLeft,Qt::AlignVCenter); } @@ -480,11 +494,17 @@ AllPlot::setYMax() QStringList labels; if (hrCurve->isVisible()) { labels << "BPM"; - ymax = hrCurve->maxYValue(); + if (referencePlot == NULL) + ymax = hrCurve->maxYValue(); + else + ymax = referencePlot->hrCurve->maxYValue(); } if (cadCurve->isVisible()) { labels << "RPM"; - ymax = qMax(ymax, cadCurve->maxYValue()); + if (referencePlot == NULL) + ymax = qMax(ymax, cadCurve->maxYValue()); + else + ymax = qMax(ymax, referencePlot->cadCurve->maxYValue()); } setAxisTitle(yLeft2, labels.join(" / ")); setAxisScale(yLeft2, 0.0, 1.05 * ymax); @@ -493,14 +513,24 @@ AllPlot::setYMax() } if (speedCurve->isVisible()) { setAxisTitle(yRight, (useMetricUnits ? tr("KPH") : tr("MPH"))); - setAxisScale(yRight, 0.0, 1.05 * speedCurve->maxYValue()); + if (referencePlot == NULL) + setAxisScale(yRight, 0.0, 1.05 * speedCurve->maxYValue()); + else + setAxisScale(yRight, 0.0, 1.05 * referencePlot->speedCurve->maxYValue()); setAxisLabelRotation(yRight,90); setAxisLabelAlignment(yRight,Qt::AlignVCenter); } if (altCurve->isVisible()) { setAxisTitle(yRight2, useMetricUnits ? tr("Meters") : tr("Feet")); - double ymin = altCurve->minYValue(); - double ymax = qMax(ymin + 100, 1.05 * altCurve->maxYValue()); + double ymin,ymax; + + if (referencePlot == NULL) { + ymin = altCurve->minYValue(); + ymax = qMax(ymin + 100, 1.05 * altCurve->maxYValue()); + } else { + ymin = referencePlot->altCurve->minYValue(); + ymax = qMax(ymin + 100, 1.05 * referencePlot->altCurve->maxYValue()); + } setAxisScale(yRight2, ymin, ymax); setAxisLabelRotation(yRight2,90); setAxisLabelAlignment(yRight2,Qt::AlignVCenter); @@ -523,9 +553,86 @@ AllPlot::setXTitle() } void -AllPlot::setData(RideItem *_rideItem) +AllPlot::setDataP(AllPlot *plot, int startidx, int stopidx) +{ + if (plot == NULL) return; + + referencePlot = plot; + + rideItem = plot->rideItem; + shade_zones = plot->shade_zones; + bydist = plot->bydist; + + arrayLength = stopidx-startidx; + wattsArray = plot->wattsArray.mid(startidx, stopidx-startidx); + hrArray = plot->hrArray.mid(startidx, stopidx-startidx); + speedArray = plot->speedArray.mid(startidx, stopidx-startidx); + cadArray = plot->cadArray.mid(startidx, stopidx-startidx); + altArray = plot->altArray.mid(startidx, stopidx-startidx); + timeArray = plot->timeArray.mid(startidx, stopidx-startidx); + distanceArray = plot->distanceArray.mid(startidx, stopidx-startidx); + + if (bydist) { + startidx = plot->distanceIndex(plot->distanceArray[startidx]); + stopidx = plot->distanceIndex(plot->distanceArray[(stopidx>=plot->distanceArray.size()?plot->distanceArray.size()-1:stopidx)])-1; + } else { + startidx = plot->timeIndex(plot->timeArray[startidx]/60); + stopidx = plot->timeIndex(plot->timeArray[(stopidx>=plot->timeArray.size()?plot->timeArray.size()-1:stopidx)]/60)-1; + } + + smoothWatts = plot->smoothWatts.mid(startidx, stopidx-startidx); + smoothHr = plot->smoothHr.mid(startidx, stopidx-startidx); + smoothSpeed = plot->smoothSpeed.mid(startidx, stopidx-startidx); + smoothCad = plot->smoothCad.mid(startidx, stopidx-startidx); + smoothAltitude = plot->smoothAltitude.mid(startidx, stopidx-startidx); + smoothTime = plot->smoothTime.mid(startidx, stopidx-startidx); + smoothDistance = plot->smoothDistance.mid(startidx, stopidx-startidx); + + QVector &xaxis = bydist ? smoothDistance : smoothTime; + + // attach appropriate curves + wattsCurve->detach(); + hrCurve->detach(); + speedCurve->detach(); + cadCurve->detach(); + altCurve->detach(); + + wattsCurve->setData(xaxis, smoothWatts); + hrCurve->setData(xaxis, smoothHr); + speedCurve->setData(xaxis, smoothSpeed); + cadCurve->setData(xaxis, smoothCad); + altCurve->setData(xaxis, smoothAltitude); + + setYMax(); + + setAxisMaxMajor(yLeft, 5); + setAxisMaxMajor(yLeft2, 5); + setAxisMaxMajor(yRight, 5); + setAxisMaxMajor(yRight2, 5); + + wattsCurve->setVisible(plot->wattsCurve->isVisible()); + hrCurve->setVisible(plot->hrCurve->isVisible()); + speedCurve->setVisible(plot->speedCurve->isVisible()); + cadCurve->setVisible(plot->cadCurve->isVisible()); + altCurve->setVisible(plot->altCurve->isVisible()); + + if (!smoothWatts.empty()) wattsCurve->attach(this); + if (!smoothHr.empty()) hrCurve->attach(this); + if (!smoothSpeed.empty()) speedCurve->attach(this); + if (!smoothCad.empty()) cadCurve->attach(this); + if (!smoothAltitude.empty()) altCurve->attach(this); + + refreshIntervalMarkers(); + refreshZoneLabels(); + + replot(); +} + +void +AllPlot::setDataI(RideItem *_rideItem) { rideItem = _rideItem; + if (_rideItem == NULL) return; wattsArray.clear(); @@ -676,6 +783,35 @@ AllPlot::setByDistance(int id) recalc(); } +struct ComparePoints { + bool operator()(const double p1, const double p2) { + return p1 < p2; + } +}; + +int +AllPlot::timeIndex(double min) const +{ + // return index offset for specified time + QVector::const_iterator i = std::lower_bound( + smoothTime.begin(), smoothTime.end(), min, ComparePoints()); + if (i == smoothTime.end()) + return smoothTime.size(); + return i - smoothTime.begin(); +} + + + +int +AllPlot::distanceIndex(double km) const +{ + // return index offset for specified distance in km + QVector::const_iterator i = std::lower_bound( + smoothDistance.begin(), smoothDistance.end(), km, ComparePoints()); + if (i == smoothDistance.end()) + return smoothDistance.size(); + return i - smoothDistance.begin(); +} /*---------------------------------------------------------------------- * Interval plotting *--------------------------------------------------------------------*/ diff --git a/src/AllPlot.h b/src/AllPlot.h index 941e523f2..6348e967c 100644 --- a/src/AllPlot.h +++ b/src/AllPlot.h @@ -52,7 +52,15 @@ class AllPlot : public QwtPlot void refreshZoneLabels(); void refreshIntervalMarkers(); - void setData(RideItem *_rideItem); + void setDataI(RideItem *_rideItem); + void setDataP(AllPlot *plot, int startidx, int stopidx); + + int timeIndex(double) const; // get index offset for time in secs + int distanceIndex(double) const; // get index offset for distance in KM + + QwtPlotMarker *allMarker1; + QwtPlotMarker *allMarker2; + QwtPlotMarker *allMarker3; public slots: @@ -95,6 +103,15 @@ class AllPlot : public QwtPlot QVector timeArray; QVector distanceArray; QVector altArray; + + QVector smoothWatts; + QVector smoothHr; + QVector smoothSpeed; + QVector smoothCad; + QVector smoothTime; + QVector smoothDistance; + QVector smoothAltitude; + int arrayLength; int smooth; @@ -112,6 +129,9 @@ class AllPlot : public QwtPlot int showSpeedState; int showCadState; int showAltState; + + private: + AllPlot *referencePlot; }; #endif // _GC_AllPlot_h diff --git a/src/AllPlotWindow.cpp b/src/AllPlotWindow.cpp index bdf8e3eb6..c9f8a9b0a 100644 --- a/src/AllPlotWindow.cpp +++ b/src/AllPlotWindow.cpp @@ -27,6 +27,7 @@ #include "TimeUtils.h" #include "Settings.h" #include "Units.h" // for MILES_PER_KM +#include #include #include #include @@ -41,6 +42,12 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : QLabel *showLabel = new QLabel(tr("Show:"), this); showLayout->addWidget(showLabel); + showStack = new QCheckBox(tr("Stacked view"), this); + showStack->setCheckState(Qt::Unchecked); + showLayout->addWidget(showStack); + + stackWidth = 15; + QCheckBox *showGrid = new QCheckBox(tr("Grid"), this); showGrid->setCheckState(Qt::Checked); showLayout->addWidget(showGrid); @@ -135,38 +142,51 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : connect(allPicker, SIGNAL(appended(const QPoint &)), SLOT(plotPickerSelected(const QPoint &))); - allMarker1 = new QwtPlotMarker(); + QwtPlotMarker* allMarker1 = new QwtPlotMarker(); allMarker1->setLineStyle(QwtPlotMarker::VLine); allMarker1->attach(allPlot); allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); + allPlot->allMarker1=allMarker1; - allMarker2 = new QwtPlotMarker(); + QwtPlotMarker* allMarker2 = new QwtPlotMarker(); allMarker2->setLineStyle(QwtPlotMarker::VLine); allMarker2->attach(allPlot); + allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); + allPlot->allMarker2=allMarker2; - allMarker3 = new QwtPlotMarker(); + QwtPlotMarker* allMarker3 = new QwtPlotMarker(); allMarker3->setLineStyle(QwtPlotMarker::VLine); allMarker3->attach(allPlot); + allPlot->allMarker3=allMarker3; + + + stackFrame = new QScrollArea(); + stackFrame->hide(); vlayout->addWidget(allPlot); + vlayout->addWidget(stackFrame); vlayout->addLayout(showLayout); vlayout->addLayout(smoothLayout); + + setLayout(vlayout); connect(showPower, SIGNAL(currentIndexChanged(int)), - allPlot, SLOT(showPower(int))); + this, SLOT(setShowPower(int))); connect(showHr, SIGNAL(stateChanged(int)), - allPlot, SLOT(showHr(int))); + this, SLOT(setShowHr(int))); connect(showSpeed, SIGNAL(stateChanged(int)), - allPlot, SLOT(showSpeed(int))); + this, SLOT(setShowSpeed(int))); connect(showCad, SIGNAL(stateChanged(int)), - allPlot, SLOT(showCad(int))); + this, SLOT(setShowCad(int))); connect(showAlt, SIGNAL(stateChanged(int)), - allPlot, SLOT(showAlt(int))); + this, SLOT(setShowAlt(int))); connect(showGrid, SIGNAL(stateChanged(int)), - allPlot, SLOT(showGrid(int))); + this, SLOT(setShowGrid(int))); + connect(showStack, SIGNAL(stateChanged(int)), + this, SLOT(setShowStack(int))); connect(comboDistance, SIGNAL(currentIndexChanged(int)), - allPlot, SLOT(setByDistance(int))); + this, SLOT(setByDistance(int))); connect(smoothSlider, SIGNAL(valueChanged(int)), this, SLOT(setSmoothingFromSlider())); connect(smoothLineEdit, SIGNAL(editingFinished()), @@ -185,8 +205,13 @@ AllPlotWindow::rideSelected() return; clearSelection(); // clear any ride interval selection data setAllPlotWidgets(ride); - allPlot->setData(ride); + allPlot->setDataI(ride); allZoomer->setZoomBase(); + + // update stacked view if that is set + int showit = showStack->isChecked(); + setShowStack(0); // zap whats there + setShowStack(showit); } void @@ -201,20 +226,23 @@ AllPlotWindow::intervalsChanged() { allPlot->refreshIntervalMarkers(); allPlot->replot(); + foreach (AllPlot *plot, allPlots) { + plot->refreshIntervalMarkers(); + plot->replot(); + } } void AllPlotWindow::intervalSelected() { hideSelection(); - allPlot->replot(); } void AllPlotWindow::setSmoothingFromSlider() { if (allPlot->smoothing() != smoothSlider->value()) { - allPlot->setSmoothing(smoothSlider->value()); + setSmoothing(smoothSlider->value()); smoothLineEdit->setText(QString("%1").arg(allPlot->smoothing())); } } @@ -224,7 +252,7 @@ AllPlotWindow::setSmoothingFromLineEdit() { int value = smoothLineEdit->text().toInt(); if (value != allPlot->smoothing()) { - allPlot->setSmoothing(value); + setSmoothing(value); smoothSlider->setValue(value); } } @@ -269,39 +297,91 @@ AllPlotWindow::zoomInterval(IntervalItem *which) void AllPlotWindow::plotPickerSelected(const QPoint &pos) { + QwtPlotPicker* pick = qobject_cast(sender()); + AllPlot* plot = qobject_cast(pick->plot()); + // set start of selection in xunits (minutes assumed for now) - setStartSelection(allPlot->invTransform(QwtPlot::xBottom, pos.x())); + setStartSelection(plot, pos.x()); } void AllPlotWindow::plotPickerMoved(const QPoint &pos) { QString name = QString("Selection #%1 ").arg(selection); - // set end of selection in xunits (minutes assumed for now) - setEndSelection(allPlot->invTransform(QwtPlot::xBottom, pos.x()), true, name); + QwtPlotPicker* pick = qobject_cast(sender()); + AllPlot* plot = qobject_cast(pick->plot()); + + if (allPlots.length()>0) { + int posX = pos.x(); + int posY = plot->y()+pos.y()+50; + + foreach (AllPlot *_plot, allPlots) { + _plot->allMarker1->setValue(plot->allMarker1->value()); + + if (_plot->y()<=plot->y() && posY<_plot->y()){ + if (_plot->transform(QwtPlot::xBottom, _plot->allMarker2->xValue())>0){ + setEndSelection(_plot, 0, false, name); + _plot->allMarker2->setLabel(QString("")); + } + } + else if (_plot->y()>=plot->y() && posY>_plot->y()+_plot->height()){ + if (_plot->transform(QwtPlot::xBottom, _plot->allMarker2->xValue())width()){ + setEndSelection(_plot, plot->width(), false, name); + } + } + else if (posY>_plot->y() && posY<_plot->y()+_plot->height()){ + if (pos.x()<6) + posX = 6; + else if (!_plot->byDistance() && pos.x()>_plot->transform(QwtPlot::xBottom, _plot->timeArray[_plot->timeArray.size()-1])) + posX = _plot->transform(QwtPlot::xBottom, _plot->timeArray[_plot->timeArray.size()-1]); + else if (plot->byDistance() && pos.x()>_plot->transform(QwtPlot::xBottom, _plot->distanceArray[_plot->distanceArray.size()-1])) + posX = _plot->transform(QwtPlot::xBottom, _plot->distanceArray[_plot->distanceArray.size()-1]); + + setEndSelection(_plot, posX, true, name); + if (plot->y()<_plot->y()) { + plot->allMarker1->setLabel(_plot->allMarker1->label()); + plot->allMarker2->setLabel(_plot->allMarker2->label()); + } + } else {//if (_plot->y()height() || _plot->y()y()+3*plot->height()) + //hide plot for 3 next plots + _plot->allMarker1->hide(); + _plot->allMarker2->hide(); + _plot->allMarker3->hide(); + } + } + } + else // set end of selection in xunits (minutes assumed for now) + setEndSelection(plot, pos.x(), true, name); } void -AllPlotWindow::setStartSelection(double xValue) +AllPlotWindow::setStartSelection(AllPlot* plot, int xPosition) { + double xValue = plot->invTransform(QwtPlot::xBottom, xPosition); + QwtPlotMarker* allMarker1 = plot->allMarker1; + selection++; if (!allMarker1->isVisible() || allMarker1->xValue() != xValue) { - allMarker1->hide(); - allMarker2->hide(); - allMarker3->hide(); - allMarker1->setValue(xValue, allPlot->byDistance() ? 0 : 100); + hideSelection(); + allMarker1->setValue(xValue, plot->byDistance() ? 0 : 100); allMarker1->show(); } } void -AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name) +AllPlotWindow::setEndSelection(AllPlot* plot, int xPosition, bool newInterval, QString name) { - bool useMetricUnits = allPlot->useMetricUnits; + double xValue = plot->invTransform(QwtPlot::xBottom, xPosition); + + QwtPlotMarker* allMarker1 = plot->allMarker1; + QwtPlotMarker* allMarker2 = plot->allMarker2; + QwtPlotMarker* allMarker3 = plot->allMarker3; + + bool useMetricUnits = plot->useMetricUnits; if (!allMarker2->isVisible() || allMarker2->xValue() != xValue) { - allMarker2->setValue(xValue, allPlot->byDistance() ? 0 : 100); + allMarker2->setValue(xValue, plot->byDistance() ? 0 : 100); allMarker2->show(); double x1, x2; // time or distance depending upon mode selected @@ -313,7 +393,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name) x1 = allMarker1->xValue(); x2 = allMarker2->xValue(); } - double lwidth=allPlot->transform(QwtPlot::xBottom, x2)-allPlot->transform(QwtPlot::xBottom, x1); + double lwidth=plot->transform(QwtPlot::xBottom, x2)-plot->transform(QwtPlot::xBottom, x1); allMarker3->setValue((x2-x1)/2+x1, 100); QColor marker_color = QColor(Qt::blue); @@ -340,14 +420,14 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name) // if we are in distance mode then x1 and x2 are distances // we need to make sure they are in KM for the rest of this // code. - if (allPlot->byDistance() && useMetricUnits == false) { + if (plot->byDistance() && useMetricUnits == false) { x1 *= KM_PER_MILE; x2 *= KM_PER_MILE; } foreach (const RideFilePoint *point, ride->ride()->dataPoints()) { - if ((allPlot->byDistance()==true && point->km>=x1 && point->kmbyDistance()==false && point->secs/60>=x1 && point->secs/60byDistance()==true && point->km>=x1 && point->kmbyDistance()==false && point->secs/60>=x1 && point->secs/60km; distance2 = point->km; @@ -389,7 +469,14 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name) s = s.arg(""); } - allMarker1->setLabel(s); + if (allMarker1->xValue()xValue()) { + allMarker1->setLabel(s); + allMarker2->setLabel(QString("")); + } + else { + allMarker2->setLabel(s); + allMarker1->setLabel(QString("")); + } if (newInterval) { @@ -425,9 +512,244 @@ AllPlotWindow::clearSelection() void AllPlotWindow::hideSelection() { - allMarker1->setVisible(false); - allMarker2->setVisible(false); - allMarker3->setVisible(false); + allPlot->allMarker1->setVisible(false); + allPlot->allMarker2->setVisible(false); + allPlot->allMarker3->setVisible(false); allPlot->replot(); + + foreach (AllPlot *plot, allPlots) { + plot->allMarker1->setVisible(false); + plot->allMarker2->setVisible(false); + plot->allMarker3->setVisible(false); + plot->replot(); + } +} + +void +AllPlotWindow::setShowPower(int value) +{ + allPlot->showPower(value); + if (allPlots.count()>0) + resetStackedDatas(); +} + +void +AllPlotWindow::setShowHr(int value) +{ + allPlot->showHr(value); + foreach (AllPlot *plot, allPlots) + plot->showHr(value); +} + +void +AllPlotWindow::setShowSpeed(int value) +{ + allPlot->showSpeed(value); + foreach (AllPlot *plot, allPlots) + plot->showSpeed(value); +} + +void +AllPlotWindow::setShowCad(int value) +{ + allPlot->showCad(value); + foreach (AllPlot *plot, allPlots) + plot->showCad(value); +} + +void +AllPlotWindow::setShowAlt(int value) +{ + allPlot->showAlt(value); + foreach (AllPlot *plot, allPlots) + plot->showAlt(value); +} + +void +AllPlotWindow::setShowGrid(int value) +{ + allPlot->showGrid(value); + foreach (AllPlot *plot, allPlots) + plot->showGrid(value); +} + +void +AllPlotWindow::setByDistance(int value) +{ + allPlot->setByDistance(value); + if (allPlots.count()>0) { + setShowStack(false); + setShowStack(true); + } +} + +void +AllPlotWindow::setSmoothing(int value) +{ + allPlot->setSmoothing(value); + + if (allPlots.count()>0) + resetStackedDatas(); +} + +void +AllPlotWindow::resetStackedDatas() +{ + + int _stackWidth = stackWidth; + if (allPlot->byDistance()) + _stackWidth = stackWidth/3; + + for(int i = 0 ; i < allPlots.count() ; i++) + { + AllPlot *plot = allPlots[i]; + int startIndex, stopIndex; + if (plot->byDistance()) { + startIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i); + stopIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*(i+1)); + } else { + startIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*i); + stopIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*(i+1)); + } + + // Update AllPlot for stacked view + plot->setDataP(allPlot, startIndex, stopIndex); + } +} + +void +AllPlotWindow::setShowStack(int value) +{ + if (value) { + int _stackWidth = stackWidth; + if (allPlot->byDistance()) + _stackWidth = stackWidth/3; + + allPlot->hide(); + + QVBoxLayout *vLayout = new QVBoxLayout(); + + RideItem* rideItem = allPlot->rideItem; + double duration = rideItem->ride()->dataPoints().last()->secs; + double distance = (allPlot->useMetricUnits ? 1 : MILES_PER_KM) * rideItem->ride()->dataPoints().last()->km; + int nbplot; + + if (allPlot->byDistance()) + nbplot = (int)floor(distance/_stackWidth)+1; + else + nbplot = (int)floor(duration/_stackWidth/60)+1; + + for(int i = 0 ; i < nbplot ; i++) + { + AllPlot *_allPlot = new AllPlot(this, mainWindow); + allPlots.append(_allPlot); + addPickers(_allPlot); + + int startIndex, stopIndex; + if (allPlot->byDistance()) { + startIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i); + stopIndex = allPlot->rideItem->ride()->distanceIndex((allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*(i+1)); + } else { + startIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*i); + stopIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*(i+1)); + } + vLayout->addWidget(_allPlot); + + // Update AllPlot for stacked view + _allPlot->setDataP(allPlot, startIndex, stopIndex); + _allPlot->setAxisScale(QwtPlot::xBottom, _stackWidth*i, _stackWidth*(i+1), 1); + + if (i==0){ + // First plot view title and legend + _allPlot->setTitle(allPlot->title()); + _allPlot->plotLayout()->setLegendPosition(QwtPlot::TopLegend); + _allPlot->setFixedHeight(200); + } + else { + _allPlot->setTitle(""); + _allPlot->insertLegend(NULL); + _allPlot->setFixedHeight(150); + } + + // No x axis titles + _allPlot->setAxisTitle(QwtPlot::xBottom,NULL); + // Smaller y axis Titles + QFont axisFont = QFont("Helvetica",10, QFont::Normal); + QwtText text = _allPlot->axisTitle(QwtPlot::yLeft); + text.setFont(axisFont); + _allPlot->setAxisTitle(QwtPlot::yLeft,text); + text = _allPlot->axisTitle(QwtPlot::yLeft2); + text.setFont(axisFont); + _allPlot->setAxisTitle(QwtPlot::yLeft2,text); + text = _allPlot->axisTitle(QwtPlot::yRight); + text.setFont(axisFont); + _allPlot->setAxisTitle(QwtPlot::yRight,text); + text = _allPlot->axisTitle(QwtPlot::yRight2); + text.setFont(axisFont); + _allPlot->setAxisTitle(QwtPlot::yRight2,text); + } + + QWidget *stack = new QWidget(); + stack->setLayout(vLayout); + + stackFrame->setWidgetResizable(true); + stackFrame->setWidget(stack); + stackFrame->show(); + } else { + stackFrame->hide(); + allPlot->show(); + + if (allPlots.count()>1) { + foreach (AllPlot *plot, allPlots) { + //layout()->removeWidget(plot); + delete plot; + allPlots.removeOne(plot); + } + } + } +} + +void +AllPlotWindow::addPickers(AllPlot *_allPlot) +{ + QwtPlotMarker* allMarker1 = new QwtPlotMarker(); + allMarker1->setLineStyle(QwtPlotMarker::VLine); + allMarker1->attach(_allPlot); + allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); + _allPlot->allMarker1 = allMarker1; + + QwtPlotMarker* allMarker2 = new QwtPlotMarker(); + allMarker2->setLineStyle(QwtPlotMarker::VLine); + allMarker2->attach(_allPlot); + allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); + _allPlot->allMarker2 = allMarker2; + + QwtPlotMarker* allMarker3 = new QwtPlotMarker(); + allMarker3->setLineStyle(QwtPlotMarker::VLine); + allMarker3->attach(_allPlot); + _allPlot->allMarker3 = allMarker3; + + // Interval selection 2 + QwtPlotPicker* allPicker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, + QwtPicker::RectSelection | QwtPicker::CornerToCorner|QwtPicker::DragSelection, + QwtPicker::NoRubberBand, + QwtPicker::ActiveOnly, _allPlot->canvas()); + allPickers.append(allPicker); + allPicker->setRubberBandPen(QColor(Qt::blue)); + allPicker->setRubberBand(QwtPicker::NoRubberBand); + allPicker->setTrackerPen(QColor(Qt::blue)); + // now select rectangles + allPicker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::RectSelection | QwtPicker::DragSelection); + allPicker->setMousePattern(QwtEventPattern::MouseSelect1, + Qt::LeftButton, Qt::ShiftModifier); + + + + //void appended(const QwtPlotPicker* pick, const QwtDoublePoint &pos); + connect(allPicker, SIGNAL(appended(const QPoint &)), + SLOT(plotPickerSelected(const QPoint &))); + connect(allPicker, SIGNAL(moved(const QPoint &)), + SLOT(plotPickerMoved(const QPoint &))); + } diff --git a/src/AllPlotWindow.h b/src/AllPlotWindow.h index 72d56d8fd..bc28e6027 100644 --- a/src/AllPlotWindow.h +++ b/src/AllPlotWindow.h @@ -38,8 +38,8 @@ class AllPlotWindow : public QWidget AllPlotWindow(MainWindow *mainWindow); void setData(RideItem *ride); - void setStartSelection(double seconds); - void setEndSelection(double seconds, bool newInterval, QString name); + void setStartSelection(AllPlot* plot, int xPosition); + void setEndSelection(AllPlot* plot, int xPosition, bool newInterval, QString name); void clearSelection(); void hideSelection(); void zoomInterval(IntervalItem *); // zoom into a specified interval @@ -53,6 +53,16 @@ class AllPlotWindow : public QWidget void zonesChanged(); void intervalsChanged(); + void setShowStack(int state); + void setShowPower(int state); + void setShowHr(int state); + void setShowSpeed(int state); + void setShowCad(int state); + void setShowAlt(int state); + void setShowGrid(int state); + void setSmoothing(int value); + void setByDistance(int value); + protected: // whilst we refactor, lets make friend @@ -63,13 +73,15 @@ class AllPlotWindow : public QWidget MainWindow *mainWindow; AllPlot *allPlot; + QList allPlots; + QList allPickers; + + QScrollArea *stackFrame; QwtPlotPanner *allPanner; QwtPlotZoomer *allZoomer; QwtPlotPicker *allPicker; int selection; - QwtPlotMarker *allMarker1; - QwtPlotMarker *allMarker2; - QwtPlotMarker *allMarker3; + QCheckBox *showStack; QCheckBox *showHr; QCheckBox *showSpeed; QCheckBox *showCad; @@ -80,8 +92,12 @@ class AllPlotWindow : public QWidget private: void showInfo(QString); + void resetStackedDatas(); + int stackWidth; private slots: + void addPickers(AllPlot *allPlot2); + void plotPickerMoved(const QPoint &); void plotPickerSelected(const QPoint &); }; diff --git a/src/RideFile.cpp b/src/RideFile.cpp index 0413a2d9d..e65a51d95 100644 --- a/src/RideFile.cpp +++ b/src/RideFile.cpp @@ -55,7 +55,13 @@ RideFile::fillInIntervals() mark(); } -struct ComparePoints { +struct ComparePointKm { + bool operator()(const RideFilePoint *p1, const RideFilePoint *p2) { + return p1->km < p2->km; + } +}; + +struct ComparePointSecs { bool operator()(const RideFilePoint *p1, const RideFilePoint *p2) { return p1->secs < p2->secs; } @@ -67,7 +73,7 @@ RideFile::intervalBegin(const RideFileInterval &interval) const RideFilePoint p; p.secs = interval.start; QVector::const_iterator i = std::lower_bound( - dataPoints_.begin(), dataPoints_.end(), &p, ComparePoints()); + dataPoints_.begin(), dataPoints_.end(), &p, ComparePointSecs()); if (i == dataPoints_.end()) return dataPoints_.size(); return i - dataPoints_.begin(); @@ -84,10 +90,38 @@ RideFile::timeToDistance(double secs) const if (secs < dataPoints_.first()->secs) return dataPoints_.first()->km; if (secs > dataPoints_.last()->secs) return dataPoints_.last()->km; - QVector::const_iterator i = std::lower_bound(dataPoints_.begin(), dataPoints_.end(), &p, ComparePoints()); + QVector::const_iterator i = std::lower_bound(dataPoints_.begin(), dataPoints_.end(), &p, ComparePointSecs()); return (*i)->km; } +int +RideFile::timeIndex(double secs) const +{ + // return index offset for specified time + RideFilePoint p; + p.secs = secs; + + QVector::const_iterator i = std::lower_bound( + dataPoints_.begin(), dataPoints_.end(), &p, ComparePointSecs()); + if (i == dataPoints_.end()) + return dataPoints_.size(); + return i - dataPoints_.begin(); +} + +int +RideFile::distanceIndex(double km) const +{ + // return index offset for specified distance in km + RideFilePoint p; + p.km = km; + + QVector::const_iterator i = std::lower_bound( + dataPoints_.begin(), dataPoints_.end(), &p, ComparePointKm()); + if (i == dataPoints_.end()) + return dataPoints_.size(); + return i - dataPoints_.begin(); +} + void RideFile::writeAsCsv(QFile &file, bool bIsMetric) const { @@ -159,7 +193,7 @@ RideFileFactory::rideFileRegExp() const { QStringList suffixList = RideFileFactory::instance().suffixes(); QString s("^(\\d\\d\\d\\d)_(\\d\\d)_(\\d\\d)_(\\d\\d)_(\\d\\d)_(\\d\\d)\\.(%1)$"); - return QRegExp(s.arg(suffixList.join("|")), Qt::CaseInsensitive); + return QRegExp(s.arg(suffixList.join("|"))); } RideFile *RideFileFactory::openRideFile(QFile &file, diff --git a/src/RideFile.h b/src/RideFile.h index 14294acfd..f2365f32e 100644 --- a/src/RideFile.h +++ b/src/RideFile.h @@ -121,6 +121,8 @@ class RideFile void resetDataPresent(); double timeToDistance(double) const; // get distance in km at time in secs + int timeIndex(double) const; // get index offset for time in secs + int distanceIndex(double) const; // get index offset for distance in KM QMap > metricOverrides; };