mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Modify Aerolab to add interval highlight and zoom + auto offset
[cherry picked commit 0c7abe from master into release_3.0.0dev] Needs zoom to interval reinstated, which was lost in the v3 refactor.
This commit is contained in:
408
src/Aerolab.cpp
408
src/Aerolab.cpp
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "Aerolab.h"
|
||||
#include "AerolabWindow.h"
|
||||
#include "MainWindow.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideItem.h"
|
||||
@@ -42,11 +43,193 @@ max(double a, double b) { if (a > b) return a; else return b; }
|
||||
static inline double
|
||||
min(double a, double b) { if (a < b) return a; else return b; }
|
||||
|
||||
Aerolab::Aerolab(QWidget *parent):
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Interval plotting
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
class IntervalAerolabData : public QwtData
|
||||
{
|
||||
public:
|
||||
Aerolab *aerolab;
|
||||
MainWindow *mainWindow;
|
||||
IntervalAerolabData
|
||||
(
|
||||
Aerolab *aerolab,
|
||||
MainWindow *mainWindow
|
||||
) : aerolab( aerolab ), mainWindow( mainWindow ) { }
|
||||
|
||||
double x( size_t ) const;
|
||||
double y( size_t ) const;
|
||||
size_t size() const;
|
||||
virtual QwtData *copy() const;
|
||||
|
||||
void init();
|
||||
|
||||
IntervalItem *intervalNum( int ) const;
|
||||
|
||||
int intervalCount() const;
|
||||
};
|
||||
|
||||
/*
|
||||
* HELPER FUNCTIONS:
|
||||
* intervalNum - returns a pointer to the nth selected interval
|
||||
* intervalCount - returns the number of highlighted intervals
|
||||
*/
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// note this is operating on the children of allIntervals and not the
|
||||
// intervalWidget (QTreeWidget) -- this is why we do not use the
|
||||
// selectedItems() member. N starts a one not zero.
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
IntervalItem *IntervalAerolabData::intervalNum
|
||||
(
|
||||
int number
|
||||
) const
|
||||
{
|
||||
int highlighted = 0;
|
||||
const QTreeWidgetItem *allIntervals = mainWindow->allIntervalItems();
|
||||
for ( int ii = 0; ii < allIntervals->childCount(); ii++)
|
||||
|
||||
{
|
||||
IntervalItem *current = (IntervalItem *) allIntervals->child( ii );
|
||||
|
||||
if ( current == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if ( current->isSelected() == true )
|
||||
{
|
||||
++highlighted;
|
||||
}
|
||||
if ( highlighted == number )
|
||||
{
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// how many intervals selected?
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
int IntervalAerolabData::intervalCount() const
|
||||
{
|
||||
int highlighted = 0;
|
||||
|
||||
if ( mainWindow->allIntervalItems() != NULL )
|
||||
{
|
||||
const QTreeWidgetItem *allIntervals = mainWindow->allIntervalItems();
|
||||
for ( int ii = 0; ii < allIntervals->childCount(); ii++)
|
||||
{
|
||||
IntervalItem *current = (IntervalItem *) allIntervals->child( ii );
|
||||
if ( current != NULL )
|
||||
{
|
||||
if ( current->isSelected() == true )
|
||||
{
|
||||
++highlighted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return highlighted;
|
||||
}
|
||||
/*
|
||||
* INTERVAL HIGHLIGHTING CURVE
|
||||
* IntervalAerolabData - implements the qwtdata interface where
|
||||
* x,y return point co-ordinates and
|
||||
* size returns the number of points
|
||||
*/
|
||||
// The interval curve data is derived from the intervals that have
|
||||
// been selected in the MainWindow leftlayout for each selected
|
||||
// interval we return 4 data points; bottomleft, topleft, topright
|
||||
// and bottom right.
|
||||
//
|
||||
// the points correspond to:
|
||||
// bottom left = interval start, 0 watts
|
||||
// top left = interval start, maxwatts
|
||||
// top right = interval stop, maxwatts
|
||||
// bottom right = interval stop, 0 watts
|
||||
//
|
||||
double IntervalAerolabData::x
|
||||
(
|
||||
size_t number
|
||||
) const
|
||||
{
|
||||
// for each interval there are four points, which interval is this for?
|
||||
// interval numbers start at 1 not ZERO in the utility functions
|
||||
|
||||
double result = 0;
|
||||
|
||||
int interval_no = number ? 1 + number / 4 : 1;
|
||||
// get the interval
|
||||
IntervalItem *current = intervalNum( interval_no );
|
||||
|
||||
if ( current != NULL )
|
||||
{
|
||||
double multiplier = aerolab->useMetricUnits ? 1 : MILES_PER_KM;
|
||||
// which point are we returning?
|
||||
//qDebug() << "number = " << number << endl;
|
||||
switch ( number % 4 )
|
||||
{
|
||||
case 0 : result = aerolab->bydist ? multiplier * current->startKM : current->start/60; // bottom left
|
||||
break;
|
||||
case 1 : result = aerolab->bydist ? multiplier * current->startKM : current->start/60; // top left
|
||||
break;
|
||||
case 2 : result = aerolab->bydist ? multiplier * current->stopKM : current->stop/60; // bottom right
|
||||
break;
|
||||
case 3 : result = aerolab->bydist ? multiplier * current->stopKM : current->stop/60; // top right
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
double IntervalAerolabData::y
|
||||
(
|
||||
size_t number
|
||||
) const
|
||||
{
|
||||
// which point are we returning?
|
||||
double result = 0;
|
||||
switch ( number % 4 )
|
||||
{
|
||||
case 0 : result = -5000; // bottom left
|
||||
break;
|
||||
case 1 : result = 5000; // top left - set to out of bound value
|
||||
break;
|
||||
case 2 : result = 5000; // top right - set to out of bound value
|
||||
break;
|
||||
case 3 : result = -5000; // bottom right
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t IntervalAerolabData::size() const
|
||||
{
|
||||
return intervalCount() * 4;
|
||||
}
|
||||
|
||||
QwtData *IntervalAerolabData::copy() const
|
||||
{
|
||||
return new IntervalAerolabData( aerolab, mainWindow );
|
||||
}
|
||||
|
||||
|
||||
//**********************************************
|
||||
//** END IntervalAerolabData **
|
||||
//**********************************************
|
||||
|
||||
|
||||
Aerolab::Aerolab(
|
||||
AerolabWindow *parent,
|
||||
MainWindow *mainWindow
|
||||
):
|
||||
QwtPlot(parent),
|
||||
parent(parent),
|
||||
unit(0),
|
||||
rideItem(NULL),
|
||||
smooth(1), bydist(false) {
|
||||
smooth(1), bydist(true), autoEoffset(true) {
|
||||
|
||||
crr = 0.005;
|
||||
cda = 0.500;
|
||||
@@ -68,9 +251,19 @@ Aerolab::Aerolab(QWidget *parent):
|
||||
setAxisScale(xBottom, 0, 60);
|
||||
|
||||
veCurve = new QwtPlotCurve(tr("V-Elevation"));
|
||||
|
||||
altCurve = new QwtPlotCurve(tr("Elevation"));
|
||||
|
||||
// get rid of nasty blank space on right of the plot
|
||||
veCurve->setYAxis( yLeft );
|
||||
altCurve->setYAxis( yLeft );
|
||||
|
||||
intervalHighlighterCurve = new QwtPlotCurve();
|
||||
intervalHighlighterCurve->setBaseline(-5000);
|
||||
intervalHighlighterCurve->setYAxis( yLeft );
|
||||
intervalHighlighterCurve->setData( IntervalAerolabData( this, mainWindow ) );
|
||||
intervalHighlighterCurve->attach( this );
|
||||
this->legend()->remove( intervalHighlighterCurve ); // don't show in legend
|
||||
|
||||
grid = new QwtPlotGrid();
|
||||
grid->enableX(false);
|
||||
grid->attach(this);
|
||||
@@ -78,6 +271,7 @@ Aerolab::Aerolab(QWidget *parent):
|
||||
configChanged();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Aerolab::configChanged()
|
||||
{
|
||||
@@ -92,6 +286,16 @@ Aerolab::configChanged()
|
||||
QPen gridPen(GColor(CPLOTGRID));
|
||||
gridPen.setStyle(Qt::DotLine);
|
||||
grid->setPen(gridPen);
|
||||
|
||||
QPen ihlPen = QPen( GColor( CINTERVALHIGHLIGHTER ) );
|
||||
ihlPen.setWidth(1);
|
||||
intervalHighlighterCurve->setPen( ihlPen );
|
||||
|
||||
QColor ihlbrush = QColor(GColor(CINTERVALHIGHLIGHTER));
|
||||
ihlbrush.setAlpha(40);
|
||||
intervalHighlighterCurve->setBrush(ihlbrush); // fill below the line
|
||||
|
||||
this->legend()->remove( intervalHighlighterCurve ); // don't show in legend
|
||||
}
|
||||
|
||||
void
|
||||
@@ -157,7 +361,7 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) {
|
||||
if ( arrayLength == 0 )
|
||||
e = eoffset;
|
||||
|
||||
timeArray[arrayLength] = p1->secs;
|
||||
timeArray[arrayLength] = p1->secs / 60.0;
|
||||
if ( have_recorded_alt_curve )
|
||||
altArray[arrayLength] = (useMetricUnits
|
||||
? p1->alt
|
||||
@@ -172,8 +376,13 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) {
|
||||
}
|
||||
double f = 0.0;
|
||||
double a = 0.0;
|
||||
d += v * dt;
|
||||
distanceArray[arrayLength] = d/1000;
|
||||
|
||||
// Use km data insteed of formula for file with a stop (gap).
|
||||
//d += v * dt;
|
||||
//distanceArray[arrayLength] = d/1000;
|
||||
|
||||
distanceArray[arrayLength] = p1->km;
|
||||
|
||||
|
||||
|
||||
if( v > small_number ) {
|
||||
@@ -200,8 +409,8 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) {
|
||||
veCurve->setVisible(false);
|
||||
altCurve->setVisible(false);
|
||||
}
|
||||
|
||||
recalc(new_zoom);
|
||||
adjustEoffset();
|
||||
} else {
|
||||
setTitle("no data");
|
||||
|
||||
@@ -222,6 +431,26 @@ Aerolab::setAxisTitle(int axis, QString label)
|
||||
QwtPlot::setAxisTitle(axis, title);
|
||||
}
|
||||
|
||||
void
|
||||
Aerolab::adjustEoffset() {
|
||||
|
||||
if (autoEoffset && !altArray.empty()) {
|
||||
double idx = axisScaleDiv( QwtPlot::xBottom )->lowerBound();
|
||||
parent->eoffsetSlider->setEnabled(false);
|
||||
|
||||
if (bydist) {
|
||||
int v = 100*(altArray.at(rideItem->ride()->distanceIndex(idx))-veArray.at(rideItem->ride()->distanceIndex(idx)));
|
||||
parent->eoffsetSlider->setValue(intEoffset()+v);
|
||||
}
|
||||
else {
|
||||
int v = 100*(altArray.at(rideItem->ride()->timeIndex(60*idx))-veArray.at(rideItem->ride()->timeIndex(60*idx)));
|
||||
parent->eoffsetSlider->setValue(intEoffset()+v);
|
||||
}
|
||||
} else
|
||||
parent->eoffsetSlider->setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
struct DataPoint {
|
||||
double time, hr, watts, speed, cad, alt;
|
||||
DataPoint(double t, double h, double w, double s, double c, double a) :
|
||||
@@ -248,8 +477,7 @@ Aerolab::recalc( bool new_zoom ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QVector<double> &xaxis = distanceArray;
|
||||
QVector<double> &xaxis = (bydist?distanceArray:timeArray);
|
||||
int startingIndex = 0;
|
||||
int totalPoints = arrayLength - startingIndex;
|
||||
|
||||
@@ -264,9 +492,12 @@ Aerolab::recalc( bool new_zoom ) {
|
||||
}
|
||||
|
||||
if( new_zoom )
|
||||
setAxisScale(xBottom, 0.0, totalRideDistance);
|
||||
setAxisScale(xBottom, 0.0, (bydist?totalRideDistance:rideTimeSecs));
|
||||
|
||||
setYMax();
|
||||
|
||||
|
||||
setYMax(new_zoom );
|
||||
refreshIntervalMarkers();
|
||||
|
||||
replot();
|
||||
}
|
||||
@@ -274,19 +505,75 @@ Aerolab::recalc( bool new_zoom ) {
|
||||
|
||||
|
||||
void
|
||||
Aerolab::setYMax() {
|
||||
Aerolab::setYMax(bool new_zoom)
|
||||
{
|
||||
if (veCurve->isVisible())
|
||||
{
|
||||
|
||||
if (veCurve->isVisible()) {
|
||||
setAxisTitle(yLeft, "Elevation");
|
||||
if ( useMetricUnits )
|
||||
|
||||
{
|
||||
|
||||
setAxisTitle( yLeft, "Elevation (m)" );
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
setAxisTitle( yLeft, "Elevation (')" );
|
||||
|
||||
}
|
||||
|
||||
double minY = 0.0;
|
||||
double maxY = 0.0;
|
||||
|
||||
//************
|
||||
|
||||
//if (veCurve->isVisible()) {
|
||||
// setAxisTitle(yLeft, tr("Elevation"));
|
||||
if ( !altArray.empty() ) {
|
||||
setAxisScale(yLeft,
|
||||
min( veCurve->minYValue(), altCurve->minYValue() ) - 10,
|
||||
10.0 + max( veCurve->maxYValue(), altCurve->maxYValue() ) );
|
||||
// setAxisScale(yLeft,
|
||||
// min( veCurve->minYValue(), altCurve->minYValue() ) - 10,
|
||||
// 10.0 + max( veCurve->maxYValue(), altCurve->maxYValue() ) );
|
||||
|
||||
minY = min( veCurve->minYValue(), altCurve->minYValue() ) - 10;
|
||||
maxY = 10.0 + max( veCurve->maxYValue(), altCurve->maxYValue() );
|
||||
|
||||
} else {
|
||||
setAxisScale(yLeft,
|
||||
veCurve->minYValue() ,
|
||||
1.05 * veCurve->maxYValue() );
|
||||
//setAxisScale(yLeft,
|
||||
// veCurve->minYValue() ,
|
||||
// 1.05 * veCurve->maxYValue() );
|
||||
|
||||
if ( new_zoom )
|
||||
|
||||
{
|
||||
|
||||
minY = veCurve->minYValue();
|
||||
|
||||
maxY = veCurve->maxYValue();
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
minY = parent->getCanvasTop();
|
||||
|
||||
maxY = parent->getCanvasBottom();
|
||||
|
||||
}
|
||||
|
||||
//adjust eooffset
|
||||
// TODO
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
setAxisScale( yLeft, minY, maxY );
|
||||
setAxisLabelRotation(yLeft,270);
|
||||
setAxisLabelAlignment(yLeft,Qt::AlignVCenter);
|
||||
}
|
||||
@@ -305,12 +592,18 @@ Aerolab::setXTitle() {
|
||||
setAxisTitle(xBottom, tr("Time (minutes)"));
|
||||
}
|
||||
|
||||
void
|
||||
Aerolab::setAutoEoffset(int value)
|
||||
{
|
||||
autoEoffset = value;
|
||||
adjustEoffset();
|
||||
}
|
||||
|
||||
void
|
||||
Aerolab::setByDistance() {
|
||||
bydist = true;
|
||||
Aerolab::setByDistance(int value) {
|
||||
bydist = value;
|
||||
setXTitle();
|
||||
recalc(false);
|
||||
recalc(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -401,3 +694,72 @@ Aerolab::setIntEoffset(
|
||||
eoffset = (double) value / 100.0;
|
||||
recalc(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Aerolab::pointHover (QwtPlotCurve *curve, int index)
|
||||
{
|
||||
if ( index >= 0 && curve != intervalHighlighterCurve )
|
||||
{
|
||||
double x_value = curve->x( index );
|
||||
double y_value = curve->y( index );
|
||||
// output the tooltip
|
||||
|
||||
QString text = QString( "%1 %2 %3 %4 %5" )
|
||||
. arg( this->axisTitle( curve->xAxis() ).text() )
|
||||
. arg( x_value, 0, 'f', 3 )
|
||||
. arg( "\n" )
|
||||
. arg( this->axisTitle( curve->yAxis() ).text() )
|
||||
. arg( y_value, 0, 'f', 3 );
|
||||
|
||||
// set that text up
|
||||
tooltip->setText( text );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no point
|
||||
tooltip->setText( "" );
|
||||
}
|
||||
}
|
||||
|
||||
void Aerolab::refreshIntervalMarkers()
|
||||
{
|
||||
foreach( QwtPlotMarker *mrk, d_mrk )
|
||||
{
|
||||
mrk->detach();
|
||||
delete mrk;
|
||||
}
|
||||
d_mrk.clear();
|
||||
|
||||
QRegExp wkoAuto("^(Peak *[0-9]*(s|min)|Entire workout|Find #[0-9]*) *\\([^)]*\\)$");
|
||||
if ( rideItem->ride() )
|
||||
{
|
||||
foreach(const RideFileInterval &interval, rideItem->ride()->intervals()) {
|
||||
// skip WKO autogenerated peak intervals
|
||||
if (wkoAuto.exactMatch(interval.name))
|
||||
continue;
|
||||
QwtPlotMarker *mrk = new QwtPlotMarker;
|
||||
d_mrk.append(mrk);
|
||||
mrk->attach(this);
|
||||
mrk->setLineStyle(QwtPlotMarker::VLine);
|
||||
mrk->setLabelAlignment(Qt::AlignRight | Qt::AlignTop);
|
||||
mrk->setLinePen(QPen(GColor(CPLOTMARKER), 0, Qt::DashDotLine));
|
||||
QwtText text(interval.name);
|
||||
text.setFont(QFont("Helvetica", 10, QFont::Bold));
|
||||
text.setColor(GColor(CPLOTMARKER));
|
||||
if (!bydist)
|
||||
mrk->setValue(interval.start / 60.0, 0.0);
|
||||
else
|
||||
mrk->setValue((useMetricUnits ? 1 : MILES_PER_KM) *
|
||||
rideItem->ride()->timeToDistance(interval.start), 0.0);
|
||||
mrk->setLabel(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <qwt_plot.h>
|
||||
#include <qwt_data.h>
|
||||
#include <QtGui>
|
||||
#include "LTMWindow.h" // for tooltip/canvaspicker
|
||||
|
||||
// forward references
|
||||
class RideItem;
|
||||
@@ -32,6 +33,10 @@ class QwtPlotGrid;
|
||||
class QwtPlotMarker;
|
||||
class AerolabWindow;
|
||||
class MainWindow;
|
||||
class IntervalAerolabData;
|
||||
class LTMToolTip;
|
||||
class LTMCanvasPicker;
|
||||
|
||||
|
||||
class Aerolab : public QwtPlot {
|
||||
|
||||
@@ -40,27 +45,47 @@ class Aerolab : public QwtPlot {
|
||||
|
||||
|
||||
public:
|
||||
Aerolab(QWidget *parent);
|
||||
Aerolab( AerolabWindow *, MainWindow * );
|
||||
bool byDistance() const { return bydist; }
|
||||
bool useMetricUnits; // whether metric units are used (or imperial)
|
||||
void setData(RideItem *_rideItem, bool new_zoom);
|
||||
void setAxisTitle(int axis, QString label);
|
||||
|
||||
void refreshIntervalMarkers();
|
||||
|
||||
private:
|
||||
AerolabWindow *parent;
|
||||
|
||||
LTMToolTip *tooltip;
|
||||
LTMCanvasPicker *_canvasPicker; // allow point selection/hover
|
||||
|
||||
void adjustEoffset();
|
||||
|
||||
public slots:
|
||||
void setByDistance();
|
||||
|
||||
void setAutoEoffset(int value);
|
||||
void setByDistance(int value);
|
||||
void configChanged();
|
||||
|
||||
void pointHover( QwtPlotCurve *, int );
|
||||
|
||||
signals:
|
||||
|
||||
protected:
|
||||
friend class ::AerolabWindow;
|
||||
friend class ::IntervalAerolabData;
|
||||
|
||||
|
||||
QVariant unit;
|
||||
QwtPlotGrid *grid;
|
||||
QVector<QwtPlotMarker*> d_mrk;
|
||||
|
||||
// One curve to plot in the Course Profile:
|
||||
QwtPlotCurve *veCurve; // virtual elevation curve
|
||||
QwtPlotCurve *altCurve; // recorded elevation curve, if available
|
||||
|
||||
QwtPlotCurve *intervalHighlighterCurve; // highlight selected intervals on the Plot
|
||||
|
||||
RideItem *rideItem;
|
||||
|
||||
QVector<double> hrArray;
|
||||
@@ -76,6 +101,7 @@ class Aerolab : public QwtPlot {
|
||||
|
||||
int smooth;
|
||||
bool bydist;
|
||||
bool autoEoffset;
|
||||
int arrayLength;
|
||||
int iCrr;
|
||||
int iCda;
|
||||
@@ -89,7 +115,7 @@ class Aerolab : public QwtPlot {
|
||||
|
||||
double slope(double, double, double, double, double, double, double);
|
||||
void recalc(bool);
|
||||
void setYMax();
|
||||
void setYMax(bool);
|
||||
void setXTitle();
|
||||
void setIntCrr(int);
|
||||
void setIntCda(int);
|
||||
@@ -97,18 +123,20 @@ class Aerolab : public QwtPlot {
|
||||
void setIntEta(int);
|
||||
void setIntEoffset(int);
|
||||
void setIntTotalMass(int);
|
||||
double getCrr() const { return (double)crr; };
|
||||
double getCda() const { return (double)cda; };
|
||||
double getTotalMass() const { return (double)totalMass; };
|
||||
double getRho() const { return (double)rho; };
|
||||
double getEta() const { return (double)eta; };
|
||||
double getEoffset() const { return (double)eoffset; };
|
||||
int intCrr() const { return (int)( crr * 1000000 ); };
|
||||
int intCda() const { return (int)( cda * 100000); };
|
||||
int intTotalMass() const { return (int)( totalMass * 100); };
|
||||
int intRho() const { return (int)( rho * 10000); };
|
||||
int intEta() const { return (int)( eta * 10000); };
|
||||
int intEoffset() const { return (int)( eoffset * 100); };
|
||||
double getCrr() const { return (double)crr; }
|
||||
double getCda() const { return (double)cda; }
|
||||
double getTotalMass() const { return (double)totalMass; }
|
||||
double getRho() const { return (double)rho; }
|
||||
double getEta() const { return (double)eta; }
|
||||
double getEoffset() const { return (double)eoffset; }
|
||||
int intCrr() const { return (int)( crr * 1000000 ); }
|
||||
int intCda() const { return (int)( cda * 10000); }
|
||||
int intTotalMass() const { return (int)( totalMass * 100); }
|
||||
int intRho() const { return (int)( rho * 10000); }
|
||||
int intEta() const { return (int)( eta * 10000); }
|
||||
int intEoffset() const { return (int)( eoffset * 100); }
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // _GC_Aerolab_h
|
||||
|
||||
@@ -35,7 +35,7 @@ AerolabWindow::AerolabWindow(MainWindow *mainWindow) :
|
||||
QHBoxLayout *cLayout = new QHBoxLayout;
|
||||
|
||||
// Plot:
|
||||
aerolab = new Aerolab(this);
|
||||
aerolab = new Aerolab(this, mainWindow);
|
||||
|
||||
// Left controls layout:
|
||||
QVBoxLayout *leftControls = new QVBoxLayout;
|
||||
@@ -65,11 +65,14 @@ AerolabWindow::AerolabWindow(MainWindow *mainWindow) :
|
||||
QHBoxLayout *cdaLayout = new QHBoxLayout;
|
||||
QLabel *cdaLabel = new QLabel(tr("CdA"), this);
|
||||
cdaLabel->setFixedWidth(labelWidth1);
|
||||
cdaQLCDNumber = new QLCDNumber(7);
|
||||
cdaLineEdit = new QLineEdit();
|
||||
cdaLineEdit->setFixedWidth(50);
|
||||
cdaLineEdit->setText(QString("%1").arg(aerolab->getCda()) );
|
||||
/*cdaQLCDNumber = new QLCDNumber(7);
|
||||
cdaQLCDNumber->setMode(QLCDNumber::Dec);
|
||||
cdaQLCDNumber->setSmallDecimalPoint(false);
|
||||
cdaQLCDNumber->setSegmentStyle(QLCDNumber::Flat);
|
||||
cdaQLCDNumber->display(QString("%1").arg(aerolab->getCda()) );
|
||||
cdaQLCDNumber->display(QString("%1").arg(aerolab->getCda()) );*/
|
||||
cdaSlider = new QSlider(Qt::Horizontal);
|
||||
cdaSlider->setTickPosition(QSlider::TicksBelow);
|
||||
cdaSlider->setTickInterval(100);
|
||||
@@ -77,7 +80,8 @@ AerolabWindow::AerolabWindow(MainWindow *mainWindow) :
|
||||
cdaSlider->setMaximum(6000);
|
||||
cdaSlider->setValue(aerolab->intCda());
|
||||
cdaLayout->addWidget( cdaLabel );
|
||||
cdaLayout->addWidget( cdaQLCDNumber );
|
||||
//cdaLayout->addWidget( cdaQLCDNumber );
|
||||
cdaLayout->addWidget( cdaLineEdit );
|
||||
cdaLayout->addWidget( cdaSlider );
|
||||
|
||||
// Eta:
|
||||
@@ -159,16 +163,28 @@ AerolabWindow::AerolabWindow(MainWindow *mainWindow) :
|
||||
eoffsetSlider->setTickPosition(QSlider::TicksBelow);
|
||||
eoffsetSlider->setTickInterval(1000);
|
||||
eoffsetSlider->setMinimum(-30000);
|
||||
eoffsetSlider->setMaximum(30000);
|
||||
eoffsetSlider->setMaximum(100000);
|
||||
eoffsetSlider->setValue(aerolab->intEoffset());
|
||||
eoffsetLayout->addWidget( eoffsetLabel );
|
||||
eoffsetLayout->addWidget( eoffsetQLCDNumber );
|
||||
eoffsetLayout->addWidget( eoffsetSlider );
|
||||
|
||||
QCheckBox *eoffsetAuto = new QCheckBox(tr("eoffset auto"), this);
|
||||
eoffsetAuto->setCheckState(Qt::Checked);
|
||||
eoffsetLayout->addWidget(eoffsetAuto);
|
||||
|
||||
QHBoxLayout *smoothLayout = new QHBoxLayout;
|
||||
QComboBox *comboDistance = new QComboBox();
|
||||
comboDistance->addItem(tr("X Axis Shows Time"));
|
||||
comboDistance->addItem(tr("X Axis Shows Distance"));
|
||||
comboDistance->setCurrentIndex(1);
|
||||
smoothLayout->addWidget(comboDistance);
|
||||
|
||||
// Add to leftControls:
|
||||
rightControls->addLayout( mLayout );
|
||||
rightControls->addLayout( rhoLayout );
|
||||
rightControls->addLayout( eoffsetLayout );
|
||||
rightControls->addLayout( smoothLayout );
|
||||
|
||||
|
||||
// Assemble controls layout:
|
||||
@@ -182,27 +198,68 @@ AerolabWindow::AerolabWindow(MainWindow *mainWindow) :
|
||||
| QwtPicker::CornerToCorner);
|
||||
allZoomer->setTrackerMode(QwtPicker::AlwaysOff);
|
||||
allZoomer->setEnabled(true);
|
||||
allZoomer->setMousePattern( QwtEventPattern::MouseSelect2, Qt::RightButton, Qt::ControlModifier );
|
||||
allZoomer->setMousePattern( QwtEventPattern::MouseSelect3, Qt::RightButton );
|
||||
|
||||
// SIGNALs to SLOTs:
|
||||
//connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected()));
|
||||
connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected()));
|
||||
connect(crrSlider, SIGNAL(valueChanged(int)),this, SLOT(setCrrFromSlider()));
|
||||
connect(cdaSlider, SIGNAL(valueChanged(int)), this, SLOT(setCdaFromSlider()));
|
||||
connect(cdaLineEdit, SIGNAL(textChanged(const QString)), this, SLOT(setCdaFromText(const QString)));
|
||||
connect(mSlider, SIGNAL(valueChanged(int)),this, SLOT(setTotalMassFromSlider()));
|
||||
connect(rhoSlider, SIGNAL(valueChanged(int)), this, SLOT(setRhoFromSlider()));
|
||||
connect(etaSlider, SIGNAL(valueChanged(int)), this, SLOT(setEtaFromSlider()));
|
||||
connect(eoffsetSlider, SIGNAL(valueChanged(int)), this, SLOT(setEoffsetFromSlider()));
|
||||
connect(eoffsetAuto, SIGNAL(stateChanged(int)), this, SLOT(setAutoEoffset(int)));
|
||||
connect(comboDistance, SIGNAL(currentIndexChanged(int)), this, SLOT(setByDistance(int)));
|
||||
connect(mainWindow, SIGNAL(configChanged()), aerolab, SLOT(configChanged()));
|
||||
connect(mainWindow, SIGNAL(configChanged()), this, SLOT(configChanged()));
|
||||
connect(mainWindow, SIGNAL( intervalSelected() ), this, SLOT(intervalSelected()));
|
||||
connect(allZoomer, SIGNAL( zoomed(const QwtDoubleRect) ), this, SLOT(zoomChanged()));
|
||||
|
||||
|
||||
// Build the tab layout:
|
||||
vLayout->addWidget(aerolab);
|
||||
vLayout->addLayout(cLayout);
|
||||
setLayout(vLayout);
|
||||
|
||||
|
||||
// tooltip on hover over point
|
||||
//************************************
|
||||
aerolab->tooltip = new LTMToolTip( QwtPlot::xBottom,
|
||||
QwtPlot::yLeft,
|
||||
QwtPicker::PointSelection,
|
||||
QwtPicker::VLineRubberBand,
|
||||
QwtPicker::AlwaysOn,
|
||||
aerolab->canvas(),
|
||||
""
|
||||
);
|
||||
aerolab->tooltip->setSelectionFlags( QwtPicker::PointSelection | QwtPicker::RectSelection | QwtPicker::DragSelection);
|
||||
aerolab->tooltip->setRubberBand( QwtPicker::VLineRubberBand );
|
||||
aerolab->tooltip->setMousePattern( QwtEventPattern::MouseSelect1, Qt::LeftButton, Qt::ShiftModifier );
|
||||
aerolab->tooltip->setTrackerPen( QColor( Qt::black ) );
|
||||
QColor inv( Qt::white );
|
||||
inv.setAlpha( 0 );
|
||||
aerolab->tooltip->setRubberBandPen( inv );
|
||||
aerolab->tooltip->setEnabled( true );
|
||||
aerolab->_canvasPicker = new LTMCanvasPicker( aerolab );
|
||||
|
||||
connect( aerolab->_canvasPicker, SIGNAL( pointHover( QwtPlotCurve*, int ) ),
|
||||
aerolab, SLOT ( pointHover( QwtPlotCurve*, int ) ) );
|
||||
|
||||
|
||||
configChanged(); // pickup colors etc
|
||||
}
|
||||
|
||||
void
|
||||
AerolabWindow::zoomChanged()
|
||||
{
|
||||
RideItem *ride = myRideItem;
|
||||
aerolab->setData(ride, false);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AerolabWindow::configChanged()
|
||||
{
|
||||
@@ -219,7 +276,10 @@ AerolabWindow::rideSelected() {
|
||||
if (!ride)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
aerolab->setData(ride, true);
|
||||
|
||||
allZoomer->setZoomBase();
|
||||
}
|
||||
|
||||
@@ -234,6 +294,18 @@ AerolabWindow::setCrrFromSlider() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AerolabWindow::setCdaFromText(const QString text) {
|
||||
int value = 10000 * text.toDouble();
|
||||
if (aerolab->intCda() != value) {
|
||||
aerolab->setIntCda(value);
|
||||
//cdaQLCDNumber->display(QString("%1").arg(aerolab->getCda()));
|
||||
cdaSlider->setValue(aerolab->intCda());
|
||||
RideItem *ride = myRideItem;
|
||||
aerolab->setData(ride, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AerolabWindow::setCdaFromSlider() {
|
||||
|
||||
@@ -289,6 +361,23 @@ AerolabWindow::setEoffsetFromSlider() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AerolabWindow::setAutoEoffset(int value)
|
||||
{
|
||||
aerolab->setAutoEoffset(value);
|
||||
}
|
||||
|
||||
void
|
||||
AerolabWindow::setByDistance(int value)
|
||||
{
|
||||
aerolab->setByDistance(value);
|
||||
// refresh
|
||||
RideItem *ride = myRideItem;
|
||||
aerolab->setData(ride, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
AerolabWindow::zoomInterval(IntervalItem *which) {
|
||||
@@ -304,5 +393,30 @@ AerolabWindow::zoomInterval(IntervalItem *which) {
|
||||
rect.setTop(aerolab->veCurve->maxYValue()*1.1);
|
||||
rect.setBottom(aerolab->veCurve->minYValue()-10);
|
||||
allZoomer->zoom(rect);
|
||||
|
||||
aerolab->recalc(false);
|
||||
}
|
||||
|
||||
void AerolabWindow::intervalSelected()
|
||||
{
|
||||
RideItem *ride = myRideItem;
|
||||
if ( !ride )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// set the elevation data
|
||||
aerolab->setData( ride, true );
|
||||
}
|
||||
|
||||
double AerolabWindow::getCanvasTop() const
|
||||
{
|
||||
const QwtDoubleRect &canvasRect = allZoomer->zoomRect();
|
||||
return canvasRect.top();
|
||||
}
|
||||
|
||||
double AerolabWindow::getCanvasBottom() const
|
||||
{
|
||||
const QwtDoubleRect &canvasRect = allZoomer->zoomRect();
|
||||
return canvasRect.bottom();
|
||||
}
|
||||
|
||||
@@ -41,18 +41,25 @@ class AerolabWindow : public GcWindow {
|
||||
AerolabWindow(MainWindow *mainWindow);
|
||||
void setData(RideItem *ride);
|
||||
void zoomInterval(IntervalItem *); // zoom into a specified interval
|
||||
double getCanvasTop() const;
|
||||
double getCanvasBottom() const;
|
||||
|
||||
QSlider *eoffsetSlider;
|
||||
|
||||
public slots:
|
||||
void setCrrFromSlider();
|
||||
void setCdaFromSlider();
|
||||
void setCdaFromText(const QString text);
|
||||
void setTotalMassFromSlider();
|
||||
void setRhoFromSlider();
|
||||
void setEtaFromSlider();
|
||||
void setEoffsetFromSlider();
|
||||
void setAutoEoffset(int value);
|
||||
void setByDistance(int value);
|
||||
void rideSelected();
|
||||
void zoomChanged();
|
||||
void configChanged();
|
||||
|
||||
void intervalSelected();
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -77,7 +84,7 @@ class AerolabWindow : public GcWindow {
|
||||
QSlider *etaSlider;
|
||||
QLineEdit *etaLineEdit;
|
||||
QLCDNumber *etaQLCDNumber;
|
||||
QSlider *eoffsetSlider;
|
||||
|
||||
QLineEdit *eoffsetLineEdit;
|
||||
QLCDNumber *eoffsetQLCDNumber;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user