mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Fix 3596: Fix display of route elevation. (#3664)
Simplified the meterwidget elevation display loop, bug was that it was skipping the final route point. Add minY to ergfile so range of y can be obtained without computing each time, so can remove search loop from meterwidget.
This commit is contained in:
committed by
GitHub
parent
f47c3003a7
commit
d4aebbb441
@@ -1381,7 +1381,7 @@ ErgFile::~ErgFile()
|
||||
}
|
||||
|
||||
bool
|
||||
ErgFile::isValid()
|
||||
ErgFile::isValid() const
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
@@ -1617,12 +1617,12 @@ int ErgFile::nextText(long x)
|
||||
void
|
||||
ErgFile::calculateMetrics()
|
||||
{
|
||||
|
||||
// reset metrics
|
||||
XP = CP = AP = IsoPower = IF = RI = BikeStress = BS = SVI = VI = 0;
|
||||
ELE = ELEDIST = GRADE = 0;
|
||||
|
||||
maxY = 0; // we need to reset it
|
||||
minY = 0;
|
||||
maxY = 0;
|
||||
|
||||
// is it valid?
|
||||
if (!isValid()) return;
|
||||
@@ -1631,24 +1631,29 @@ ErgFile::calculateMetrics()
|
||||
|
||||
ErgFilePoint last;
|
||||
bool first = true;
|
||||
foreach (ErgFilePoint p, Points) {
|
||||
|
||||
// set the maximum Y value
|
||||
if (p.y > maxY) maxY= p.y;
|
||||
foreach(ErgFilePoint p, Points) {
|
||||
|
||||
if (first == true) {
|
||||
minY = p.y;
|
||||
maxY = p.y;
|
||||
first = false;
|
||||
} else if (p.y > last.y) {
|
||||
}
|
||||
else {
|
||||
minY = std::min(minY, p.y);
|
||||
maxY = std::max(maxY, p.y);
|
||||
|
||||
ELEDIST += p.x - last.x;
|
||||
ELE += p.y - last.y;
|
||||
if (p.y > last.y) {
|
||||
ELEDIST += p.x - last.x;
|
||||
ELE += p.y - last.y;
|
||||
}
|
||||
}
|
||||
last = p;
|
||||
}
|
||||
if (ELE == 0 || ELEDIST == 0) GRADE = 0;
|
||||
else GRADE = ELE/ELEDIST * 100;
|
||||
else GRADE = ELE / ELEDIST * 100;
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
|
||||
QVector<double> rolling(30);
|
||||
rolling.fill(0.0f);
|
||||
@@ -1675,28 +1680,37 @@ ErgFile::calculateMetrics()
|
||||
int skcount = 0;
|
||||
|
||||
ErgFilePoint last;
|
||||
foreach (ErgFilePoint p, Points) {
|
||||
bool first = true;
|
||||
foreach(ErgFilePoint p, Points) {
|
||||
|
||||
// set the maximum Y value
|
||||
if (p.y > maxY) maxY= p.y;
|
||||
// set the minimum/maximum Y value
|
||||
if (first) {
|
||||
minY = p.y;
|
||||
maxY = p.y;
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
minY = std::min(minY, p.y);
|
||||
maxY = std::min(maxY, p.y);
|
||||
}
|
||||
|
||||
while (nextSecs < p.x) {
|
||||
|
||||
// CALCULATE IsoPower
|
||||
apsum += last.y;
|
||||
sum += last.y;
|
||||
sum += last.y;
|
||||
sum -= rolling[index];
|
||||
|
||||
// update 30s circular buffer
|
||||
rolling[index] = last.y;
|
||||
if (index == 29) index=0;
|
||||
if (index == 29) index = 0;
|
||||
else index++;
|
||||
|
||||
total += pow(sum/30, 4);
|
||||
count ++;
|
||||
total += pow(sum / 30, 4);
|
||||
count++;
|
||||
|
||||
// CALCULATE XPOWER
|
||||
while ((weighted > NEGLIGIBLE) && ((nextSecs / 1000) > (lastSecs/1000) + 1000 + EPSILON)) {
|
||||
while ((weighted > NEGLIGIBLE) && ((nextSecs / 1000) > (lastSecs / 1000) + 1000 + EPSILON)) {
|
||||
weighted *= attenuation;
|
||||
lastSecs += 1000;
|
||||
sktotal += pow(weighted, 4.0);
|
||||
|
||||
@@ -118,7 +118,7 @@ class ErgFile
|
||||
void parseErg2(QString p = ""); // ergdb
|
||||
void parseTTS(); // its ahh tts
|
||||
|
||||
bool isValid(); // is the file valid or not?
|
||||
bool isValid() const; // is the file valid or not?
|
||||
|
||||
double Cp;
|
||||
int format; // ERG, CRS, MRC, ERG2 currently supported
|
||||
@@ -150,7 +150,7 @@ class ErgFile
|
||||
int MaxWatts; // maxWatts in this ergfile (scaling)
|
||||
bool valid; // did it parse ok?
|
||||
int mode;
|
||||
bool StrictGradient; // should gradient be strict or smoothed?
|
||||
bool StrictGradient; // should gradient be strict or smoothed?
|
||||
|
||||
int leftPoint, rightPoint; // current points we are between
|
||||
int interpolatorReadIndex; // next point to be fed to interpolator
|
||||
@@ -159,15 +159,15 @@ class ErgFile
|
||||
QList<ErgFileLap> Laps; // interval markers in the file
|
||||
QList<ErgFileText> Texts; // texts to display
|
||||
|
||||
GeoPointInterpolator gpi; // Location interpolator
|
||||
GeoPointInterpolator gpi; // Location interpolator
|
||||
|
||||
void calculateMetrics(); // calculate IsoPower value for ErgFile
|
||||
void calculateMetrics(); // calculate IsoPower value for ErgFile
|
||||
|
||||
// Metrics for this workout
|
||||
double maxY; // maximum Y value
|
||||
double minY, maxY; // minimum and maximum Y value
|
||||
double CP;
|
||||
double AP, IsoPower, IF, BikeStress, VI; // Coggan for erg / mrc
|
||||
double XP, RI, BS, SVI; // Skiba for erg / mrc
|
||||
double XP, RI, BS, SVI; // Skiba for erg / mrc
|
||||
double ELE, ELEDIST, GRADE; // crs
|
||||
|
||||
Context *context;
|
||||
|
||||
@@ -353,52 +353,62 @@ void NeedleMeterWidget::paintEvent(QPaintEvent* paintevent)
|
||||
}
|
||||
|
||||
ElevationMeterWidget::ElevationMeterWidget(QString Name, QWidget *parent, QString Source, Context *context) : MeterWidget(Name, parent, Source), context(context),
|
||||
m_minX(0.), m_maxX(0.), m_savedWidth(0), m_savedHeight(0), gradientValue(0.)
|
||||
m_minX(0.), m_maxX(0.), m_savedWidth(0), m_savedHeight(0), m_savedMinY(0), m_savedMaxY(0), gradientValue(0.)
|
||||
{
|
||||
forceSquareRatio = false;
|
||||
}
|
||||
|
||||
// Compute polygon for elevation graph based on widget size.
|
||||
// This is two full scans of the ergfile point array.
|
||||
void ElevationMeterWidget::lazyDimensionCompute(void)
|
||||
void ElevationMeterWidget::lazySetup(void)
|
||||
{
|
||||
// Nothing to compute unless there is an erg file.
|
||||
if (!context || !context->currentErgFile())
|
||||
// Nothing to compute unless there is a valid erg file.
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
// Compute if size has changed
|
||||
if (m_savedWidth != m_Width || m_savedHeight != m_Height) {
|
||||
const ErgFile* ergFile = context->currentErgFile();
|
||||
if (!ergFile || !ergFile->isValid())
|
||||
return;
|
||||
|
||||
// Determine extents of route
|
||||
double minX, minY, maxX, maxY;
|
||||
minX = maxX = context->currentErgFile()->Points[0].x; // meters
|
||||
minY = maxY = context->currentErgFile()->Points[0].y; // meters or altitude???
|
||||
foreach(ErgFilePoint x, context->currentErgFile()->Points) {
|
||||
minX = std::min(minX, x.x);
|
||||
minY = std::min(minY, x.y);
|
||||
maxX = std::max(maxX, x.x);
|
||||
maxY = std::max(maxY, x.y);
|
||||
}
|
||||
// Compute if size has changed. Store truncated values to allow equality comparison.
|
||||
double minX = 0.;
|
||||
double maxX = floor(ergFile->Duration);
|
||||
double minY = floor(ergFile->minY);
|
||||
double maxY = floor(ergFile->maxY);
|
||||
if (m_savedWidth != m_Width || m_savedHeight != m_Height || m_savedMinY != minY || m_savedMaxY != maxY) {
|
||||
|
||||
if (m_Width != 0 && (maxY - minY) / 0.05 < (double)m_Height * 0.80 * (maxX - minX) / (double)m_Width)
|
||||
maxY = minY + (double)m_Height * 0.80 * (maxX - minX) / (double)m_Width * 0.05;
|
||||
m_savedMinY = minY;
|
||||
m_savedMaxY = maxY;
|
||||
|
||||
if (m_Width != 0 && (maxY - minY) / 0.05 < m_Height * 0.80 * (maxX - minX) / m_Width)
|
||||
maxY = minY + m_Height * 0.80 * (maxX - minX) / m_Width * 0.05;
|
||||
minY -= (maxY - minY) * 0.20f; // add 20% as bottom headroom (slope gradient will be shown there in a bubble)
|
||||
|
||||
// Populate elevation route polygon
|
||||
m_elevationPolygon.clear();
|
||||
m_elevationPolygon << QPoint(0.0, (double)m_Height);
|
||||
double x = 0, y = 0;
|
||||
double nextX = 1;
|
||||
for (double pt=0; pt < context->currentErgFile()->Points.size(); pt++) {
|
||||
for (; x < nextX && pt < context->currentErgFile()->Points.size(); pt++) {
|
||||
x = (context->currentErgFile()->Points[pt].x - minX) * (double)m_Width / (maxX - minX);
|
||||
y = (context->currentErgFile()->Points[pt].y - minY) * (double)m_Height / (maxY - minY);
|
||||
}
|
||||
// Add points to polygon only once every time the x coordinate integer part changes.
|
||||
m_elevationPolygon << QPoint(x, (double)m_Height - y);
|
||||
nextX = floor(x) + 1.0;
|
||||
m_elevationPolygon << QPoint(0.0, m_Height);
|
||||
|
||||
// Scaling Multiples.
|
||||
const double xScale = m_Width / (maxX - minX);
|
||||
const double yScale = m_Height / (maxY - minY);
|
||||
|
||||
int lastPixelX = -1; // always draw first point.
|
||||
foreach(const ErgFilePoint &p, ergFile->Points) {
|
||||
|
||||
int pixelX = (int)floor((p.x - minX) * xScale);
|
||||
|
||||
// Skip over segment points that are less than a pixel to the right of the last segment we drew.
|
||||
if (pixelX == lastPixelX)
|
||||
continue;
|
||||
|
||||
int pixelY = (int)(m_Height - floor((p.y - minY) * yScale));
|
||||
|
||||
m_elevationPolygon << QPoint(pixelX, pixelY);
|
||||
|
||||
lastPixelX = pixelX;
|
||||
}
|
||||
m_elevationPolygon << QPoint((double)m_Width, (double)m_Height);
|
||||
|
||||
// Complete a final segment from elevation profile to bottom right of display rect.
|
||||
m_elevationPolygon << QPoint(m_Width, m_Height);
|
||||
|
||||
// Save distance extent, used to situate rider location within widget display.
|
||||
m_minX = minX;
|
||||
@@ -431,7 +441,7 @@ void ElevationMeterWidget::paintEvent(QPaintEvent* paintevent)
|
||||
// Lazy compute of min, max and route elevation polygon on init or if dimensions
|
||||
// change. Ideally we'd use the resize event but we are computing from ergfile
|
||||
// which isnt available when initial resize occurs.
|
||||
lazyDimensionCompute();
|
||||
lazySetup();
|
||||
|
||||
double bubbleSize = (double)m_Height * 0.010f;
|
||||
|
||||
|
||||
@@ -124,12 +124,12 @@ class ElevationMeterWidget : public MeterWidget
|
||||
Context* context;
|
||||
QPolygon m_elevationPolygon;
|
||||
double m_minX, m_maxX;
|
||||
int m_savedWidth, m_savedHeight;
|
||||
int m_savedWidth, m_savedHeight, m_savedMinY, m_savedMaxY;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* paintevent);
|
||||
|
||||
void lazyDimensionCompute(void);
|
||||
void lazySetup(void);
|
||||
|
||||
public:
|
||||
explicit ElevationMeterWidget(QString name, QWidget *parent = 0, QString Source = QString("None"), Context *context = NULL);
|
||||
|
||||
Reference in New Issue
Block a user