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:
ericchristoffersen
2020-11-13 04:05:28 -08:00
committed by GitHub
parent f47c3003a7
commit d4aebbb441
4 changed files with 84 additions and 60 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);