mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
Dan Connelly's MEGA patch.
It includes both powerzones and weekly summary plots. Thanks Dan.
This commit is contained in:
@@ -17,18 +17,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PowerHist.h"
|
#include "PowerHist.h"
|
||||||
|
#include "RideItem.h"
|
||||||
#include "RideFile.h"
|
#include "RideFile.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "Zones.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <qpainter.h>
|
||||||
#include <qwt_plot_curve.h>
|
#include <qwt_plot_curve.h>
|
||||||
#include <qwt_plot_grid.h>
|
#include <qwt_plot_grid.h>
|
||||||
#include <qwt_plot_zoomer.h>
|
#include <qwt_plot_zoomer.h>
|
||||||
#include <qwt_scale_engine.h>
|
#include <qwt_scale_engine.h>
|
||||||
|
#include <qwt_text.h>
|
||||||
#include <qwt_legend.h>
|
#include <qwt_legend.h>
|
||||||
#include <qwt_data.h>
|
#include <qwt_data.h>
|
||||||
|
|
||||||
class penTooltip: public QwtPlotZoomer
|
static inline int
|
||||||
|
max(int a, int b) { if (a > b) return a; else return b; }
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
min(int a, int b) { if (a < b) return a; else return b; }
|
||||||
|
|
||||||
|
// define a background class to handle shading of power zones
|
||||||
|
// draws power zone bands IF zones are defined and the option
|
||||||
|
// to draw bonds has been selected
|
||||||
|
class PowerHistBackground: public QwtPlotItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
penTooltip(QwtPlotCanvas *canvas):
|
penTooltip(QwtPlotCanvas *canvas):
|
||||||
@@ -53,15 +66,176 @@ public:
|
|||||||
PowerHist::PowerHist() :
|
PowerHist::PowerHist() :
|
||||||
array(NULL), binw(20), withz(true), lny(false)
|
array(NULL), binw(20), withz(true), lny(false)
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
PowerHist *parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PowerHistBackground(PowerHist *_parent)
|
||||||
|
{
|
||||||
|
setZ(0.0);
|
||||||
|
parent = _parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int rtti() const
|
||||||
|
{
|
||||||
|
return QwtPlotItem::Rtti_PlotUserItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void draw(QPainter *painter,
|
||||||
|
const QwtScaleMap &xMap, const QwtScaleMap &,
|
||||||
|
const QRect &rect) const
|
||||||
|
{
|
||||||
|
RideItem *rideItem = parent->rideItem;
|
||||||
|
|
||||||
|
if (! rideItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Zones **zones = rideItem->zones;
|
||||||
|
int zone_range = rideItem->zoneRange();
|
||||||
|
|
||||||
|
if (parent->shadeZones() && zones && *zones && (zone_range >= 0)) {
|
||||||
|
QList <int> zone_lows = (*zones)->getZoneLows(zone_range);
|
||||||
|
int num_zones = zone_lows.size();
|
||||||
|
if (num_zones > 0) {
|
||||||
|
for (int z = 0; z < num_zones; z ++) {
|
||||||
|
QRect r = rect;
|
||||||
|
|
||||||
|
QColor shading_color =
|
||||||
|
zoneColor(z, num_zones);
|
||||||
|
shading_color.setHsv(
|
||||||
|
shading_color.hue(),
|
||||||
|
shading_color.saturation() / 4,
|
||||||
|
shading_color.value()
|
||||||
|
);
|
||||||
|
r.setLeft(xMap.transform(zone_lows[z]));
|
||||||
|
if (z + 1 < num_zones)
|
||||||
|
r.setRight(xMap.transform(zone_lows[z + 1]));
|
||||||
|
if (r.right() >= r.left())
|
||||||
|
painter->fillRect(r, shading_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Zone labels are drawn if power zone bands are enabled, automatically
|
||||||
|
// at the center of the plot
|
||||||
|
class PowerHistZoneLabel: public QwtPlotItem
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
PowerHist *parent;
|
||||||
|
int zone_number;
|
||||||
|
double watts;
|
||||||
|
QwtText text;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PowerHistZoneLabel(PowerHist *_parent, int _zone_number)
|
||||||
|
{
|
||||||
|
parent = _parent;
|
||||||
|
zone_number = _zone_number;
|
||||||
|
|
||||||
|
RideItem *rideItem = parent->rideItem;
|
||||||
|
|
||||||
|
if (! rideItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Zones **zones = rideItem->zones;
|
||||||
|
int zone_range = rideItem->zoneRange();
|
||||||
|
|
||||||
|
setZ(1.0 + zone_number / 100.0);
|
||||||
|
|
||||||
|
// create new zone labels if we're shading
|
||||||
|
if (parent->shadeZones() && zones && *zones && (zone_range >= 0)) {
|
||||||
|
QList <int> zone_lows = (*zones)->getZoneLows(zone_range);
|
||||||
|
QList <QString> zone_names = (*zones)->getZoneNames(zone_range);
|
||||||
|
int num_zones = zone_lows.size();
|
||||||
|
assert(zone_names.size() == num_zones);
|
||||||
|
if (zone_number < num_zones) {
|
||||||
|
watts =
|
||||||
|
(
|
||||||
|
(zone_number + 1 < num_zones) ?
|
||||||
|
0.5 * (zone_lows[zone_number] + zone_lows[zone_number + 1]) :
|
||||||
|
(
|
||||||
|
(zone_number > 0) ?
|
||||||
|
(1.5 * zone_lows[zone_number] - 0.5 * zone_lows[zone_number - 1]) :
|
||||||
|
2.0 * zone_lows[zone_number]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
text = QwtText(zone_names[zone_number]);
|
||||||
|
text.setFont(QFont("Helvetica",24, QFont::Bold));
|
||||||
|
QColor text_color = zoneColor(zone_number, num_zones);
|
||||||
|
text_color.setAlpha(64);
|
||||||
|
text.setColor(text_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int rtti() const
|
||||||
|
{
|
||||||
|
return QwtPlotItem::Rtti_PlotUserItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(QPainter *painter,
|
||||||
|
const QwtScaleMap &xMap, const QwtScaleMap &,
|
||||||
|
const QRect &rect) const
|
||||||
|
{
|
||||||
|
if (parent->shadeZones()) {
|
||||||
|
int x = xMap.transform(watts);
|
||||||
|
int y = (rect.bottom() + rect.top()) / 2;
|
||||||
|
|
||||||
|
// the following code based on source for QwtPlotMarker::draw()
|
||||||
|
QRect tr(QPoint(0, 0), text.textSize(painter->font()));
|
||||||
|
tr.moveCenter(QPoint(y, -x));
|
||||||
|
painter->rotate(90); // rotate text to avoid overlap: this needs to be fixed
|
||||||
|
text.draw(painter, tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerHist::PowerHist():
|
||||||
|
selected(wattsShaded),
|
||||||
|
rideItem(NULL),
|
||||||
|
wattsArrayLength(0),
|
||||||
|
nmArrayLength(0),
|
||||||
|
hrArrayLength(0),
|
||||||
|
kphArrayLength(0),
|
||||||
|
cadArrayLength(0),
|
||||||
|
binw(20),
|
||||||
|
withz(true),
|
||||||
|
settings(GC_SETTINGS_CO, GC_SETTINGS_APP),
|
||||||
|
unit(settings.value(GC_UNIT)),
|
||||||
|
lny(false)
|
||||||
|
{
|
||||||
|
useMetricUnits = (unit.toString() == "Metric");
|
||||||
|
|
||||||
|
wattsArray = new QVector<unsigned int>(1024);
|
||||||
|
nmArray = new QVector<unsigned int>(512);
|
||||||
|
hrArray = new QVector<unsigned int>(256);
|
||||||
|
kphArray = new QVector<unsigned int>(1024);
|
||||||
|
cadArray = new QVector<unsigned int>(256);
|
||||||
|
|
||||||
|
// create a background object for shading
|
||||||
|
bg = new PowerHistBackground(this);
|
||||||
|
bg->attach(this);
|
||||||
|
|
||||||
setCanvasBackground(Qt::white);
|
setCanvasBackground(Qt::white);
|
||||||
|
|
||||||
setAxisTitle(xBottom, "Power (watts)");
|
setParameterAxisTitle();
|
||||||
setAxisTitle(yLeft, "Cumulative Time (minutes)");
|
setAxisTitle(yLeft, "Cumulative Time (minutes)");
|
||||||
|
|
||||||
curve = new QwtPlotCurve("Power");
|
curve = new QwtPlotCurve("");
|
||||||
curve->setStyle(QwtPlotCurve::Steps);
|
curve->setStyle(QwtPlotCurve::Steps);
|
||||||
curve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
curve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||||
curve->setPen(QPen(Qt::red));
|
QPen *pen = new QPen(Qt::black);
|
||||||
|
pen->setWidth(2.0);
|
||||||
|
curve->setPen(*pen);
|
||||||
|
QColor brush_color = Qt::black;
|
||||||
|
brush_color.setAlpha(64);
|
||||||
|
curve->setBrush(brush_color); // fill below the line
|
||||||
|
delete pen;
|
||||||
curve->attach(this);
|
curve->attach(this);
|
||||||
|
|
||||||
grid = new QwtPlotGrid();
|
grid = new QwtPlotGrid();
|
||||||
@@ -71,37 +245,141 @@ PowerHist::PowerHist() :
|
|||||||
grid->setPen(gridPen);
|
grid->setPen(gridPen);
|
||||||
grid->attach(this);
|
grid->attach(this);
|
||||||
|
|
||||||
|
zoneLabels = QList <PowerHistZoneLabel *>::QList();
|
||||||
QwtPlotZoomer* zoomer = new penTooltip(this->canvas());
|
QwtPlotZoomer* zoomer = new penTooltip(this->canvas());
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerHist::~PowerHist() {
|
PowerHist::~PowerHist() {
|
||||||
|
delete bg;
|
||||||
delete curve;
|
delete curve;
|
||||||
delete grid;
|
delete grid;
|
||||||
|
if (wattsArrayLength)
|
||||||
|
delete [] wattsArray;
|
||||||
|
if (nmArrayLength)
|
||||||
|
delete [] nmArray;
|
||||||
|
if (hrArrayLength)
|
||||||
|
delete [] hrArray;
|
||||||
|
if (kphArrayLength)
|
||||||
|
delete [] kphArray;
|
||||||
|
if (cadArrayLength)
|
||||||
|
delete [] cadArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static const variables from PoweHist.h:
|
||||||
|
// discritized unit for smoothing
|
||||||
|
const double PowerHist::wattsDelta;
|
||||||
|
const double PowerHist::nmDelta;
|
||||||
|
const double PowerHist::hrDelta;
|
||||||
|
const double PowerHist::kphDelta;
|
||||||
|
const double PowerHist::cadDelta;
|
||||||
|
|
||||||
|
// digits for text entry validator
|
||||||
|
const int PowerHist::wattsDigits;
|
||||||
|
const int PowerHist::nmDigits;
|
||||||
|
const int PowerHist::hrDigits;
|
||||||
|
const int PowerHist::kphDigits;
|
||||||
|
const int PowerHist::cadDigits;
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerHist::refreshZoneLabels()
|
||||||
|
{
|
||||||
|
// delete any existing power zone labels
|
||||||
|
if (zoneLabels.size()) {
|
||||||
|
QListIterator<PowerHistZoneLabel *> i(zoneLabels);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
PowerHistZoneLabel *label = i.next();
|
||||||
|
label->detach();
|
||||||
|
delete label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zoneLabels.clear();
|
||||||
|
|
||||||
|
if (! rideItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((selected == wattsShaded) || (selected == wattsUnshaded)) {
|
||||||
|
Zones **zones = rideItem->zones;
|
||||||
|
int zone_range = rideItem->zoneRange();
|
||||||
|
|
||||||
|
// generate labels for existing zones
|
||||||
|
if (zones && *zones && (zone_range >= 0)) {
|
||||||
|
int num_zones = (*zones)->numZones(zone_range);
|
||||||
|
for (int z = 0; z < num_zones; z ++) {
|
||||||
|
PowerHistZoneLabel *label = new PowerHistZoneLabel(this, z);
|
||||||
|
label->attach(this);
|
||||||
|
zoneLabels.append(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PowerHist::recalc()
|
PowerHist::recalc()
|
||||||
{
|
{
|
||||||
|
QVector<unsigned int> *array;
|
||||||
|
int arrayLength = 0;
|
||||||
|
double delta;
|
||||||
|
|
||||||
|
// make sure the interval length is set
|
||||||
|
if (dt <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((selected == wattsShaded) ||
|
||||||
|
(selected == wattsUnshaded)
|
||||||
|
) {
|
||||||
|
array = wattsArray;
|
||||||
|
delta = wattsDelta;
|
||||||
|
arrayLength = wattsArrayLength;
|
||||||
|
}
|
||||||
|
else if (selected == nm) {
|
||||||
|
array = nmArray;
|
||||||
|
delta = nmDelta;
|
||||||
|
arrayLength = nmArrayLength;
|
||||||
|
}
|
||||||
|
else if (selected == hr) {
|
||||||
|
array = hrArray;
|
||||||
|
delta = hrDelta;
|
||||||
|
arrayLength = hrArrayLength;
|
||||||
|
}
|
||||||
|
else if (selected == kph) {
|
||||||
|
array = kphArray;
|
||||||
|
delta = kphDelta;
|
||||||
|
arrayLength = kphArrayLength;
|
||||||
|
}
|
||||||
|
else if (selected == cad) {
|
||||||
|
array = cadArray;
|
||||||
|
delta = cadDelta;
|
||||||
|
arrayLength = cadArrayLength;
|
||||||
|
}
|
||||||
|
|
||||||
if (!array)
|
if (!array)
|
||||||
return;
|
return;
|
||||||
int count = (int) ceil((arrayLength - 1) / binw);
|
|
||||||
QVector<double> smoothWatts(count+1);
|
int count = int(ceil((arrayLength - 1) / binw));
|
||||||
QVector<double> smoothTime(count+1);
|
|
||||||
|
// allocate space for data, plus beginning and ending point
|
||||||
|
QVector<double> parameterValue(count+2);
|
||||||
|
QVector<double> totalTime(count+2);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < count; ++i) {
|
for (i = 1; i <= count; ++i) {
|
||||||
int low = i * binw;
|
int high = i * binw;
|
||||||
int high = low + binw;
|
int low = high - binw;
|
||||||
if (low==0 && !withz)
|
if (low==0 && !withz)
|
||||||
low++;
|
low++;
|
||||||
smoothWatts[i] = low;
|
parameterValue[i] = high * delta;
|
||||||
smoothTime[i] = 1e-9; // non-zero for log axis
|
totalTime[i] = 1e-9; // nonzero to accomodate log plot
|
||||||
while (low < high)
|
while (low < high)
|
||||||
smoothTime[i] += array[low++] / 60.0;
|
totalTime[i] += dt * (*array)[low++];
|
||||||
}
|
}
|
||||||
smoothTime[i] = 1e-9;
|
totalTime[i] = 1e-9; // nonzero to accomodate log plot
|
||||||
smoothWatts[i] = i * binw;
|
parameterValue[i] = i * delta * binw;
|
||||||
curve->setData(smoothWatts.data(), smoothTime.data(), count+1);
|
totalTime[0] = 1e-9;
|
||||||
setAxisScale(xBottom, 0.0, smoothWatts[count]);
|
parameterValue[0] = 0;
|
||||||
|
curve->setData(parameterValue.data(), totalTime.data(), count + 2);
|
||||||
|
setAxisScale(xBottom, 0.0, parameterValue[count + 1]);
|
||||||
|
|
||||||
|
refreshZoneLabels();
|
||||||
|
|
||||||
setYMax();
|
setYMax();
|
||||||
replot();
|
replot();
|
||||||
}
|
}
|
||||||
@@ -109,35 +387,136 @@ PowerHist::recalc()
|
|||||||
void
|
void
|
||||||
PowerHist::setYMax()
|
PowerHist::setYMax()
|
||||||
{
|
{
|
||||||
setAxisScale(yLeft, (lny ? 0.1 : 0.0), curve->maxYValue() * 1.1);
|
static const double tmin = 1.0/60;
|
||||||
|
setAxisScale(yLeft, (lny ? tmin : 0.0), curve->maxYValue() * 1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PowerHist::setData(RideFile *ride)
|
PowerHist::setData(RideItem *_rideItem)
|
||||||
{
|
{
|
||||||
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
|
rideItem = _rideItem;
|
||||||
QMap<double,double> powerHist;
|
|
||||||
QListIterator<RideFilePoint*> j(ride->dataPoints());
|
RideFile *ride = rideItem->ride;
|
||||||
while (j.hasNext()) {
|
|
||||||
const RideFilePoint *p1 = j.next();
|
if (ride) {
|
||||||
if (powerHist.contains(p1->watts))
|
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
|
||||||
powerHist[p1->watts] += ride->recIntSecs();
|
|
||||||
else
|
static const int maxSize = 4096;
|
||||||
powerHist[p1->watts] = ride->recIntSecs();
|
|
||||||
|
// recording interval in minutes
|
||||||
|
dt = ride->recIntSecs() / 60.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < wattsArray->size(); i++)
|
||||||
|
(*wattsArray)[i] = 0;
|
||||||
|
for (int i = 0; i < nmArray->size(); i++)
|
||||||
|
(*nmArray)[i] = 0;
|
||||||
|
for (int i = 0; i < hrArray->size(); i++)
|
||||||
|
(*hrArray)[i] = 0;
|
||||||
|
for (int i = 0; i < kphArray->size(); i++)
|
||||||
|
(*kphArray)[i] = 0;
|
||||||
|
for (int i = 0; i < cadArray->size(); i++)
|
||||||
|
(*cadArray)[i] = 0;
|
||||||
|
|
||||||
|
QListIterator<RideFilePoint*> j(ride->dataPoints());
|
||||||
|
|
||||||
|
int maxWattsIndex = 0;
|
||||||
|
int maxNmIndex = 0;
|
||||||
|
int maxHrIndex = 0;
|
||||||
|
int maxKphIndex = 0;
|
||||||
|
int maxCadIndex = 0;
|
||||||
|
|
||||||
|
// unit conversion factor for imperial units for selected parameters
|
||||||
|
double torque_factor = (useMetricUnits ? 1.0 : 0.73756215);
|
||||||
|
double speed_factor = (useMetricUnits ? 1.0 : 0.62137119);
|
||||||
|
|
||||||
|
while (j.hasNext()) {
|
||||||
|
const RideFilePoint *p1 = j.next();
|
||||||
|
|
||||||
|
int wattsIndex = int(floor(p1->watts / wattsDelta));
|
||||||
|
if (wattsIndex < maxSize) {
|
||||||
|
if (wattsIndex >= wattsArray->size()) {
|
||||||
|
int size = wattsArray->size();
|
||||||
|
int newsize = min(1.5 * wattsIndex, maxSize);
|
||||||
|
wattsArray->resize(newsize);
|
||||||
|
for (int i = size + 1; i < newsize; i ++)
|
||||||
|
(*wattsArray)[i] = 0;
|
||||||
|
}
|
||||||
|
(*wattsArray)[wattsIndex] ++;
|
||||||
|
if (wattsIndex >= maxWattsIndex)
|
||||||
|
maxWattsIndex = wattsIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nmIndex = int(floor(p1->nm * torque_factor / nmDelta));
|
||||||
|
if (nmIndex < maxSize) {
|
||||||
|
if (nmIndex >= nmArray->size()) {
|
||||||
|
int size = nmArray->size();
|
||||||
|
int newsize = min(1.5 * nmIndex, maxSize);
|
||||||
|
nmArray->resize(newsize);
|
||||||
|
for (int i = size + 1; i < newsize; i ++)
|
||||||
|
(*nmArray)[i] = 0;
|
||||||
|
}
|
||||||
|
(*nmArray)[nmIndex] ++;
|
||||||
|
if (nmIndex >= maxNmIndex)
|
||||||
|
maxNmIndex = nmIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hrIndex = int(floor(p1->hr / hrDelta));
|
||||||
|
if (hrIndex < maxSize) {
|
||||||
|
if (hrIndex >= hrArray->size()) {
|
||||||
|
int size = hrArray->size();
|
||||||
|
int newsize = min(3 * size / 2, maxSize);
|
||||||
|
hrArray->resize(newsize);
|
||||||
|
for (int i = size + 1; i < newsize; i ++)
|
||||||
|
(*hrArray)[i] = 0;
|
||||||
|
}
|
||||||
|
(*hrArray)[hrIndex] ++;
|
||||||
|
if (hrIndex >= maxHrIndex)
|
||||||
|
maxHrIndex = hrIndex;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int kphIndex = int(floor(p1->kph * speed_factor / kphDelta));
|
||||||
|
if (kphIndex < maxSize) {
|
||||||
|
if (kphIndex >= kphArray->size()) {
|
||||||
|
int size = kphArray->size();
|
||||||
|
int newsize = min(1.5 * kphIndex, maxSize);
|
||||||
|
kphArray->resize(newsize);
|
||||||
|
for (int i = size + 1; i < newsize; i ++)
|
||||||
|
(*kphArray)[i] = 0;
|
||||||
|
}
|
||||||
|
(*kphArray)[kphIndex] ++;
|
||||||
|
if (kphIndex >= maxKphIndex)
|
||||||
|
maxKphIndex = kphIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cadIndex = int(floor(p1->cad / cadDelta));
|
||||||
|
if (cadIndex < maxSize) {
|
||||||
|
if (cadIndex >= cadArray->size()) {
|
||||||
|
int size = cadArray->size();
|
||||||
|
int newsize = min(1.5 * cadIndex, maxSize);
|
||||||
|
cadArray->resize(newsize);
|
||||||
|
for (int i = size + 1; i < newsize; i ++)
|
||||||
|
(*cadArray)[i] = 0;
|
||||||
|
}
|
||||||
|
(*cadArray)[cadIndex] ++;
|
||||||
|
if (cadIndex >= maxCadIndex)
|
||||||
|
maxCadIndex = cadIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wattsArrayLength = maxWattsIndex + 1;
|
||||||
|
nmArrayLength = maxNmIndex + 1;
|
||||||
|
hrArrayLength = maxHrIndex + 1;
|
||||||
|
kphArrayLength = maxKphIndex + 1;
|
||||||
|
cadArrayLength = maxCadIndex + 1;
|
||||||
|
|
||||||
|
recalc();
|
||||||
}
|
}
|
||||||
assert(powerHist.keys().first() >= 0);
|
else {
|
||||||
int maxPower = (int) round(powerHist.keys().last());
|
setTitle("no data");
|
||||||
delete [] array;
|
replot();
|
||||||
arrayLength = maxPower + 1;
|
|
||||||
array = new double[arrayLength];
|
|
||||||
for (int i = 0; i < arrayLength; ++i)
|
|
||||||
array[i] = 0.0;
|
|
||||||
QMapIterator<double,double> i(powerHist);
|
|
||||||
while (i.hasNext()) {
|
|
||||||
i.next();
|
|
||||||
array[(int) round(i.key())] += i.value();
|
|
||||||
}
|
}
|
||||||
recalc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -147,6 +526,57 @@ PowerHist::setBinWidth(int value)
|
|||||||
recalc();
|
recalc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
PowerHist::getDelta()
|
||||||
|
{
|
||||||
|
switch (selected) {
|
||||||
|
case wattsShaded:
|
||||||
|
case wattsUnshaded:
|
||||||
|
return wattsDelta;
|
||||||
|
case nm:
|
||||||
|
return nmDelta;
|
||||||
|
case hr:
|
||||||
|
return hrDelta;
|
||||||
|
case kph:
|
||||||
|
return kphDelta;
|
||||||
|
case cad:
|
||||||
|
return cadDelta;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PowerHist::getDigits()
|
||||||
|
{
|
||||||
|
switch (selected) {
|
||||||
|
case wattsShaded:
|
||||||
|
case wattsUnshaded:
|
||||||
|
return wattsDigits;
|
||||||
|
case nm:
|
||||||
|
return nmDigits;
|
||||||
|
case hr:
|
||||||
|
return hrDigits;
|
||||||
|
case kph:
|
||||||
|
return kphDigits;
|
||||||
|
case cad:
|
||||||
|
return cadDigits;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PowerHist::setBinWidthRealUnits(double value)
|
||||||
|
{
|
||||||
|
setBinWidth(round(value / getDelta()));
|
||||||
|
return binw;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
PowerHist::getBinWidthRealUnits()
|
||||||
|
{
|
||||||
|
return binw * getDelta();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PowerHist::setWithZeros(bool value)
|
PowerHist::setWithZeros(bool value)
|
||||||
{
|
{
|
||||||
@@ -178,3 +608,96 @@ PowerHist::setlnY(bool value)
|
|||||||
replot();
|
replot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerHist::setParameterAxisTitle()
|
||||||
|
{
|
||||||
|
setAxisTitle(
|
||||||
|
xBottom,
|
||||||
|
((selected == wattsShaded) ||
|
||||||
|
(selected == wattsUnshaded)
|
||||||
|
) ?
|
||||||
|
"watts" :
|
||||||
|
((selected == hr) ?
|
||||||
|
"beats/minute" :
|
||||||
|
((selected == cad) ?
|
||||||
|
"revolutions/min" :
|
||||||
|
useMetricUnits ?
|
||||||
|
((selected == nm) ?
|
||||||
|
"newton-meters" :
|
||||||
|
((selected == kph) ?
|
||||||
|
"km/hr" :
|
||||||
|
"undefined"
|
||||||
|
)
|
||||||
|
) :
|
||||||
|
((selected == nm) ?
|
||||||
|
"ft-lb" :
|
||||||
|
((selected == kph) ?
|
||||||
|
"miles/hr" :
|
||||||
|
"undefined"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerHist::setSelection(Selection selection) {
|
||||||
|
if (selected == selection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
selected = selection;
|
||||||
|
setParameterAxisTitle();
|
||||||
|
recalc();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerHist::fixSelection() {
|
||||||
|
|
||||||
|
Selection s = selected;
|
||||||
|
RideFile *ride = rideItem->ride;
|
||||||
|
|
||||||
|
if (ride)
|
||||||
|
do {
|
||||||
|
if ((s == wattsShaded) || (s == wattsUnshaded))
|
||||||
|
if (ride->areDataPresent()->watts)
|
||||||
|
setSelection(s);
|
||||||
|
else
|
||||||
|
s = nm;
|
||||||
|
|
||||||
|
else if (s == nm)
|
||||||
|
if (ride->areDataPresent()->nm)
|
||||||
|
setSelection(s);
|
||||||
|
else
|
||||||
|
s = hr;
|
||||||
|
|
||||||
|
else if (s == hr)
|
||||||
|
if (ride->areDataPresent()->hr)
|
||||||
|
setSelection(s);
|
||||||
|
else
|
||||||
|
s = kph;
|
||||||
|
|
||||||
|
else if (s == kph)
|
||||||
|
if (ride->areDataPresent()->kph)
|
||||||
|
setSelection(s);
|
||||||
|
else
|
||||||
|
s = cad;
|
||||||
|
|
||||||
|
else if (s == cad)
|
||||||
|
if (ride->areDataPresent()->cad)
|
||||||
|
setSelection(s);
|
||||||
|
else
|
||||||
|
s = wattsShaded;
|
||||||
|
} while (s != selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PowerHist::shadeZones() const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
rideItem &&
|
||||||
|
rideItem->ride &&
|
||||||
|
selected == wattsShaded
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user