From 05ee4f40dab6f78a4423d9704834b8fb751db141 Mon Sep 17 00:00:00 2001 From: human705 <63119383+human705@users.noreply.github.com> Date: Thu, 30 Apr 2020 18:26:40 -0400 Subject: [PATCH] Add Elevation Widget to Train Video Overlays (#3411) * 1 - Add forceSquareRatio to MeterWidget * 2 - Adjust default colors and add background text. * 3 - Add Elevation widget, included in default layout visible only on slope mode. Based on the work done by Vianney Voyer Co-authored-by: Peter --- src/Resources/xml/video-layout.xml | 9 +++ src/Train/MeterWidget.cpp | 120 +++++++++++++++++++++++++++-- src/Train/MeterWidget.h | 13 +++- src/Train/VideoLayoutParser.cpp | 6 +- src/Train/VideoWindow.cpp | 17 ++++ 5 files changed, 155 insertions(+), 10 deletions(-) diff --git a/src/Resources/xml/video-layout.xml b/src/Resources/xml/video-layout.xml index d87fedfea..5f5dad050 100644 --- a/src/Resources/xml/video-layout.xml +++ b/src/Resources/xml/video-layout.xml @@ -83,4 +83,13 @@ load + + + + + + + + + diff --git a/src/Train/MeterWidget.cpp b/src/Train/MeterWidget.cpp index 35305c812..a5ae0f586 100644 --- a/src/Train/MeterWidget.cpp +++ b/src/Train/MeterWidget.cpp @@ -19,6 +19,8 @@ #include #include #include "MeterWidget.h" +#include "ErgFile.h" +#include "Context.h" MeterWidget::MeterWidget(QString Name, QWidget *parent, QString Source) : QWidget(parent), m_Name(Name), m_container(parent), m_Source(Source) { @@ -37,12 +39,13 @@ MeterWidget::MeterWidget(QString Name, QWidget *parent, QString Source) : QWidge m_OutlineColor = QColor(128,128,128,180); m_MainFont = QFont(this->font().family(), 64); m_AltFont = QFont(this->font().family(), 48); - m_BackgroundColor = QColor(96, 96, 96, 200); + m_BackgroundColor = QColor(96, 96, 96, 0); m_RangeMin = 0; m_RangeMax = 100; m_Angle = 180.0; m_SubRange = 10; boundingRectVisibility = false; + forceSquareRatio = true; } void MeterWidget::SetRelativeSize(float RelativeWidth, float RelativeHeight) @@ -75,7 +78,15 @@ void MeterWidget::AdjustSizePos() void MeterWidget::ComputeSize() { - m_Width = m_Height = (m_container->width() * m_RelativeWidth + m_container->height() * m_RelativeHeight) / 2; + if (forceSquareRatio) + { + m_Width = m_Height = (m_container->width() * m_RelativeWidth + m_container->height() * m_RelativeHeight) / 2; + } + else + { + m_Width = m_container->width() * m_RelativeWidth; + m_Height = m_container->height() * m_RelativeHeight; + } } QSize MeterWidget::sizeHint() const @@ -119,12 +130,7 @@ void MeterWidget::setBoundingRectVisibility(bool show, QColor boundingRectColor TextMeterWidget::TextMeterWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source) { -} - -void TextMeterWidget::ComputeSize() -{ - m_Width = m_container->width() * m_RelativeWidth; - m_Height = m_container->height() * m_RelativeHeight; + forceSquareRatio = false; } void TextMeterWidget::paintEvent(QPaintEvent* paintevent) @@ -132,6 +138,7 @@ void TextMeterWidget::paintEvent(QPaintEvent* paintevent) MeterWidget::paintEvent(paintevent); m_MainBrush = QBrush(m_MainColor); + m_BackgroundBrush = QBrush(m_BackgroundColor); m_OutlinePen = QPen(m_OutlineColor); m_OutlinePen.setWidth(1); m_OutlinePen.setStyle(Qt::SolidLine); @@ -140,6 +147,12 @@ void TextMeterWidget::paintEvent(QPaintEvent* paintevent) QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); + //draw background + painter.setPen(Qt::NoPen); + painter.setBrush(m_BackgroundBrush); + if (Text!=QString("")) + painter.drawRect (0, 0, m_Width, m_Height); + QPainterPath my_painterPath; my_painterPath.addText(QPointF(0,0),m_MainFont,Text); my_painterPath.addText(QPointF(QFontMetrics(m_MainFont).width(Text), 0),m_AltFont,AltText); @@ -297,3 +310,94 @@ void NeedleMeterWidget::paintEvent(QPaintEvent* paintevent) painter.drawPath(my_painterPath); painter.restore(); } + +ElevationMeterWidget::ElevationMeterWidget(QString Name, QWidget *parent, QString Source, Context *context) : MeterWidget(Name, parent, Source), context(context) +{ + forceSquareRatio = false; + gradientValue = 0.0; +} + +void ElevationMeterWidget::paintEvent(QPaintEvent* paintevent) +{ + // TODO : show Power when not in slope simulation mode + if (!context || !context->currentErgFile() || context->currentErgFile()->Points.size()<=1) + return; + + MeterWidget::paintEvent(paintevent); + + m_MainBrush = QBrush(m_MainColor); + m_BackgroundBrush = QBrush(m_BackgroundColor); + m_OutlinePen = QPen(m_OutlineColor); + m_OutlinePen.setWidth(1); + m_OutlinePen.setStyle(Qt::SolidLine); + + //painter + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // Find min/max + double minX, minY, maxX, maxY, cyclistX=0.0; + // (based on ErgFilePlot.cpp) + minX=maxX=context->currentErgFile()->Points[0].x; // meters + minY=maxY=context->currentErgFile()->Points[0].y; // meters or altitude??? + foreach(ErgFilePoint x, context->currentErgFile()->Points) + { + if (x.y > maxY) maxY = x.y; + if (x.x > maxX) maxX = x.x; + if (x.y < minY) minY = x.y; + if (x.x < minX) minX = x.x; + } + // check if slope shown will not be too inconsistent (based on widget's width/height ratio) + // we accept 20 times i.e. 5% gradient will be shown as 45° + 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; + double bubbleSize = (double)m_Height*0.010f; + minY -= (maxY-minY) * 0.20f; // add 20% as bottom headroom (slope gradient will be shown there in a bubble) + + // this->Value should hold the current distance in meters. + cyclistX = (this->Value * 1000.0 - minX) * (double)m_Width / (maxX-minX); + + //Get point to create the polygon + QPolygon polygon; + polygon << QPoint(0.0, (double)m_Height); + double x, y, pt=0; + double nextX = 1; + for( 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. + polygon << QPoint(x, (double)m_Height - y); + nextX = floor(x) + 1.0; + } + polygon << QPoint((double) m_Width, (double)m_Height); + polygon << QPoint(fmin((double) m_Width,cyclistX+bubbleSize), (double)m_Height); + polygon << QPoint(cyclistX, (double)m_Height-bubbleSize); + polygon << QPoint(fmax(0.0, cyclistX-bubbleSize), (double)m_Height); + + painter.setPen(m_OutlinePen); + painter.setBrush(m_BackgroundBrush); + painter.drawPolygon(polygon); + + m_OutlinePen = QPen(m_MainColor); + m_OutlinePen.setWidth(1); + m_OutlinePen.setStyle(Qt::SolidLine); + painter.setPen(m_OutlinePen); + painter.drawLine(cyclistX, 0.0, cyclistX, (double)m_Height-bubbleSize); + + //Cosmetic enhancment: Display grade as #.#% + std::string sGrad; + QString s_grad =""; + s_grad = ((-1.0 < this->gradientValue && this->gradientValue < 0.0)?QString("-"):QString("")) + QString::number((int) this->gradientValue); + s_grad += QString(".") + QString::number(abs((int)(this->gradientValue * 10.0) % 10)) + QString("%"); + + // Display gradient text to the right of the line until the middle, then display to the left of the line + if (cyclistX < m_Width*0.5) { + painter.drawText((double)cyclistX+5, ((double)m_Height * 0.95), s_grad); + } else { + painter.drawText((double)cyclistX-45, ((double)m_Height * 0.95), s_grad); + } +} diff --git a/src/Train/MeterWidget.h b/src/Train/MeterWidget.h index 44eae3469..b699424df 100644 --- a/src/Train/MeterWidget.h +++ b/src/Train/MeterWidget.h @@ -20,6 +20,7 @@ #define _MeterWidget_h 1 #include +#include "Context.h" class MeterWidget : public QWidget { @@ -59,6 +60,7 @@ class MeterWidget : public QWidget float m_RangeMin, m_RangeMax; float m_Angle; int m_SubRange; + bool forceSquareRatio; QColor m_MainColor; QColor m_ScaleColor; @@ -83,7 +85,6 @@ class TextMeterWidget : public MeterWidget { public: explicit TextMeterWidget(QString name, QWidget *parent = 0, QString Source = QString("None")); - virtual void ComputeSize(); virtual void paintEvent(QPaintEvent* paintevent); }; @@ -109,5 +110,15 @@ class NeedleMeterWidget : public MeterWidget virtual void paintEvent(QPaintEvent* paintevent); }; +class ElevationMeterWidget : public MeterWidget +{ + public: + explicit ElevationMeterWidget(QString name, QWidget *parent = 0, QString Source = QString("None"), Context *context = NULL); + virtual void paintEvent(QPaintEvent* paintevent); + float gradientValue; + void setContext(Context *context) { this->context = context; } + private: + Context *context; +}; #endif // _MeterWidget_h diff --git a/src/Train/VideoLayoutParser.cpp b/src/Train/VideoLayoutParser.cpp index 002c220bc..43646e6a8 100644 --- a/src/Train/VideoLayoutParser.cpp +++ b/src/Train/VideoLayoutParser.cpp @@ -47,7 +47,7 @@ void VideoLayoutParser::SetDefaultValues() meterWidget->m_MainColor = QColor(255,0,0,200); meterWidget->m_ScaleColor = QColor(255,255,255,200); meterWidget->m_OutlineColor = QColor(100,100,100,200); - meterWidget->m_BackgroundColor = QColor(100,100,100,200); + meterWidget->m_BackgroundColor = QColor(100,100,100,0); meterWidget->m_MainFont = QFont(meterWidget->font().family(), 64); meterWidget->m_AltFont = QFont(meterWidget->font().family(), 48); } @@ -134,6 +134,10 @@ bool VideoLayoutParser::startElement( const QString&, const QString&, { meterWidget = new CircularBargraphMeterWidget(meterName, containerWidget, source); } + else if (meterType == QString("Elevation")) + { + meterWidget = new ElevationMeterWidget(meterName, containerWidget, source); + } else { qDebug() << QObject::tr("Error creating meter"); diff --git a/src/Train/VideoWindow.cpp b/src/Train/VideoWindow.cpp index 7e98bc865..ab35a9f95 100644 --- a/src/Train/VideoWindow.cpp +++ b/src/Train/VideoWindow.cpp @@ -316,6 +316,23 @@ void VideoWindow::telemetryUpdate(RealtimeData rtd) p_meterWidget->Text = QString::number((int)p_meterWidget->Value); p_meterWidget->AltText = QString(".") +QString::number((int)(p_meterWidget->Value * 10.0) - (((int) p_meterWidget->Value) * 10)) + (metric ? tr(" kph") : tr(" mph")); } + else if (p_meterWidget->Source() == QString("Elevation")) + { + // Do not show in ERG mode + if (rtd.mode == ERG || rtd.mode == MRC) + { + p_meterWidget->setWindowOpacity(0); // Hide the widget + } + p_meterWidget->Value = rtd.getDistance(); + ElevationMeterWidget* elevationMeterWidget = dynamic_cast(p_meterWidget); + if (!elevationMeterWidget) + qDebug() << "Error: Elevation keyword used but widget is not elevation type"; + else + { + elevationMeterWidget->setContext(context); + elevationMeterWidget->gradientValue = rtd.getSlope(); + } + } else if (p_meterWidget->Source() == QString("Cadence")) { p_meterWidget->Value = rtd.getCadence();