mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 16:39:57 +00:00
Interval features and new GC file format
This commit is contained in:
committed by
Sean Rhea
parent
2db45dc0c5
commit
29a9e41444
@@ -17,13 +17,20 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "AllPlotWindow.h"
|
||||
#include "AllPlot.h"
|
||||
#include "MainWindow.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideItem.h"
|
||||
#include "IntervalItem.h"
|
||||
#include "TimeUtils.h"
|
||||
#include "Settings.h"
|
||||
#include "Units.h" // for MILES_PER_KM
|
||||
#include <qwt_plot_panner.h>
|
||||
#include <qwt_plot_zoomer.h>
|
||||
#include <qwt_plot_picker.h>
|
||||
#include <qwt_plot_marker.h>
|
||||
|
||||
AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
|
||||
QWidget(mainWindow), mainWindow(mainWindow)
|
||||
@@ -108,6 +115,39 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
|
||||
|
||||
// TODO: zoomer doesn't interact well with automatic axis resizing
|
||||
|
||||
|
||||
// Interval selection
|
||||
allPicker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
|
||||
QwtPicker::RectSelection | QwtPicker::CornerToCorner|QwtPicker::DragSelection,
|
||||
QwtPicker::VLineRubberBand,
|
||||
QwtPicker::ActiveOnly, allPlot->canvas());
|
||||
allPicker->setRubberBandPen(QColor(Qt::blue));
|
||||
allPicker->setRubberBand(QwtPicker::CrossRubberBand);
|
||||
allPicker->setTrackerPen(QColor(Qt::blue));
|
||||
// now select rectangles
|
||||
allPicker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::RectSelection | QwtPicker::DragSelection);
|
||||
allPicker->setRubberBand(QwtPicker::VLineRubberBand);
|
||||
allPicker->setMousePattern(QwtEventPattern::MouseSelect1,
|
||||
Qt::LeftButton, Qt::ShiftModifier);
|
||||
|
||||
connect(allPicker, SIGNAL(moved(const QPoint &)),
|
||||
SLOT(plotPickerMoved(const QPoint &)));
|
||||
connect(allPicker, SIGNAL(appended(const QPoint &)),
|
||||
SLOT(plotPickerSelected(const QPoint &)));
|
||||
|
||||
allMarker1 = new QwtPlotMarker();
|
||||
allMarker1->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker1->attach(allPlot);
|
||||
allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
|
||||
|
||||
allMarker2 = new QwtPlotMarker();
|
||||
allMarker2->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker2->attach(allPlot);
|
||||
|
||||
allMarker3 = new QwtPlotMarker();
|
||||
allMarker3->setLineStyle(QwtPlotMarker::VLine);
|
||||
allMarker3->attach(allPlot);
|
||||
|
||||
vlayout->addWidget(allPlot);
|
||||
vlayout->addLayout(showLayout);
|
||||
vlayout->addLayout(smoothLayout);
|
||||
@@ -133,6 +173,8 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
|
||||
this, SLOT(setSmoothingFromLineEdit()));
|
||||
connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected()));
|
||||
connect(mainWindow, SIGNAL(zonesChanged()), this, SLOT(zonesChanged()));
|
||||
connect(mainWindow, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged()));
|
||||
connect(mainWindow, SIGNAL(intervalSelected()), this, SLOT(intervalSelected()));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -141,6 +183,7 @@ AllPlotWindow::rideSelected()
|
||||
RideItem *ride = mainWindow->rideItem();
|
||||
if (!ride)
|
||||
return;
|
||||
clearSelection(); // clear any ride interval selection data
|
||||
setAllPlotWidgets(ride);
|
||||
allPlot->setData(ride);
|
||||
allZoomer->setZoomBase();
|
||||
@@ -153,6 +196,20 @@ AllPlotWindow::zonesChanged()
|
||||
allPlot->replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::intervalsChanged()
|
||||
{
|
||||
allPlot->refreshIntervalMarkers();
|
||||
allPlot->replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::intervalSelected()
|
||||
{
|
||||
hideSelection();
|
||||
allPlot->replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setSmoothingFromSlider()
|
||||
{
|
||||
@@ -192,3 +249,181 @@ AllPlotWindow::setAllPlotWidgets(RideItem *ride)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::zoomInterval(IntervalItem *which)
|
||||
{
|
||||
QwtDoubleRect rect;
|
||||
|
||||
if (!allPlot->byDistance()) {
|
||||
rect.setLeft(which->start/60);
|
||||
rect.setRight(which->stop/60);
|
||||
} else {
|
||||
rect.setLeft(which->startKM);
|
||||
rect.setRight(which->stopKM);
|
||||
}
|
||||
rect.setTop(allPlot->wattsCurve->maxYValue()*1.1);
|
||||
rect.setBottom(0);
|
||||
allZoomer->zoom(rect);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::plotPickerSelected(const QPoint &pos)
|
||||
{
|
||||
// set start of selection in xunits (minutes assumed for now)
|
||||
setStartSelection(allPlot->invTransform(QwtPlot::xBottom, 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);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setStartSelection(double xValue)
|
||||
{
|
||||
selection++;
|
||||
|
||||
if (!allMarker1->isVisible() || allMarker1->xValue() != xValue) {
|
||||
|
||||
allMarker1->hide();
|
||||
allMarker2->hide();
|
||||
allMarker3->hide();
|
||||
allMarker1->setValue(xValue, allPlot->byDistance() ? 0 : 100);
|
||||
allMarker1->show();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
{
|
||||
bool useMetricUnits = allPlot->useMetricUnits;
|
||||
if (!allMarker2->isVisible() || allMarker2->xValue() != xValue) {
|
||||
allMarker2->setValue(xValue, allPlot->byDistance() ? 0 : 100);
|
||||
allMarker2->show();
|
||||
double x1, x2; // time or distance depending upon mode selected
|
||||
|
||||
// swap to low-high if neccessary
|
||||
if (allMarker1->xValue()>allMarker2->xValue()){
|
||||
x2 = allMarker1->xValue();
|
||||
x1 = allMarker2->xValue();
|
||||
} else {
|
||||
x1 = allMarker1->xValue();
|
||||
x2 = allMarker2->xValue();
|
||||
}
|
||||
double lwidth=allPlot->transform(QwtPlot::xBottom, x2)-allPlot->transform(QwtPlot::xBottom, x1);
|
||||
|
||||
allMarker3->setValue((x2-x1)/2+x1, 100);
|
||||
QColor marker_color = QColor(Qt::blue);
|
||||
marker_color.setAlpha(64);
|
||||
allMarker3->setLinePen(QPen(QBrush(marker_color), lwidth, Qt::SolidLine)) ;
|
||||
//allMarker3->setZ(-1000.0);
|
||||
allMarker3->show();
|
||||
|
||||
RideFile tmpRide = RideFile();
|
||||
|
||||
QTreeWidgetItem *which = mainwindow->allRideItems()->treeWidget()->selectedItems().first();
|
||||
RideItem *ride = (RideItem*)which;
|
||||
|
||||
double distance1 = -1;
|
||||
double distance2 = -1;
|
||||
double duration1 = -1;
|
||||
double duration2 = -1;
|
||||
double secsMoving = 0;
|
||||
double wattsTotal = 0;
|
||||
double bpmTotal = 0;
|
||||
int arrayLength = 0;
|
||||
|
||||
|
||||
// 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) {
|
||||
x1 *= KM_PER_MILE;
|
||||
x2 *= KM_PER_MILE;
|
||||
}
|
||||
|
||||
foreach (const RideFilePoint *point, ride->ride->dataPoints()) {
|
||||
if ((allPlot->byDistance()==true && point->km>=x1 && point->km<x2) ||
|
||||
(allPlot->byDistance()==false && point->secs/60>=x1 && point->secs/60<x2)) {
|
||||
|
||||
if (distance1 == -1) distance1 = point->km;
|
||||
distance2 = point->km;
|
||||
|
||||
if (duration1 == -1) duration1 = point->secs;
|
||||
duration2 = point->secs;
|
||||
|
||||
if (point->kph > 0.0)
|
||||
secsMoving += ride->ride->recIntSecs();
|
||||
wattsTotal += point->watts;
|
||||
bpmTotal += point->hr;
|
||||
++arrayLength;
|
||||
}
|
||||
}
|
||||
QString s("\n%1%2 %3 %4\n%5%6 %7%8 %9%10");
|
||||
s = s.arg(useMetricUnits ? distance2-distance1 : (distance2-distance1)*MILES_PER_KM, 0, 'f', 1);
|
||||
s = s.arg((useMetricUnits? "km":"m"));
|
||||
s = s.arg(time_to_string(duration2-duration1));
|
||||
if (duration2-duration1-secsMoving>1)
|
||||
s = s.arg("("+time_to_string(secsMoving)+")");
|
||||
else
|
||||
s = s.arg("");
|
||||
s = s.arg((useMetricUnits ? 1 : MILES_PER_KM) * (distance2-distance1)/secsMoving*3600, 0, 'f', 1);
|
||||
s = s.arg((useMetricUnits? "km/h":"m/h"));
|
||||
if (wattsTotal>0) {
|
||||
s = s.arg(wattsTotal/arrayLength, 0, 'f', 1);
|
||||
s = s.arg("W");
|
||||
}
|
||||
else{
|
||||
s = s.arg("");
|
||||
s = s.arg("");
|
||||
}
|
||||
if (bpmTotal>0) {
|
||||
s = s.arg(bpmTotal/arrayLength, 0, 'f', 0);
|
||||
s = s.arg("bpm");
|
||||
}
|
||||
else {
|
||||
s = s.arg("");
|
||||
s = s.arg("");
|
||||
}
|
||||
|
||||
allMarker1->setLabel(s);
|
||||
|
||||
if (newInterval) {
|
||||
|
||||
QTreeWidgetItem *allIntervals = (QTreeWidgetItem *)mainwindow->allIntervalItems();
|
||||
int count = allIntervals->childCount();
|
||||
|
||||
// are we adjusting an existing interval? - if so delete it and readd it
|
||||
if (count > 0) {
|
||||
IntervalItem *bottom = (IntervalItem *) allIntervals->child(count-1);
|
||||
if (bottom->name == name) delete allIntervals->takeChild(count-1);
|
||||
}
|
||||
|
||||
QTreeWidgetItem *last = new IntervalItem(ride->ride, name, duration1, duration2, distance1, distance2);
|
||||
allIntervals->addChild(last);
|
||||
|
||||
// now update the RideFileIntervals and all the plots etc
|
||||
mainwindow->updateRideFileIntervals();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::clearSelection()
|
||||
{
|
||||
selection = 0;
|
||||
hideSelection();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlotWindow::hideSelection()
|
||||
{
|
||||
allMarker1->setVisible(false);
|
||||
allMarker2->setVisible(false);
|
||||
allMarker3->setVisible(false);
|
||||
allPlot->replot();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user