From eef20c57380cd3067598e70449e52deb932f9d88 Mon Sep 17 00:00:00 2001 From: LM Date: Thu, 14 Jul 2011 16:10:18 +0200 Subject: [PATCH] Working on starting version of video streamer --- qgcvideo.pro | 16 +- src/apps/qgcvideo/QGCVideoApp.cc | 76 +- src/apps/qgcvideo/QGCVideoApp.h | 40 +- src/apps/qgcvideo/QGCVideoApp.ui | 24 - src/apps/qgcvideo/QGCVideoMainWindow.cc | 45 + src/apps/qgcvideo/QGCVideoMainWindow.h | 53 ++ src/apps/qgcvideo/QGCVideoMainWindow.ui | 82 ++ src/apps/qgcvideo/QGCVideoWidget.cc | 1127 +++++++++++++++++++++++ src/apps/qgcvideo/QGCVideoWidget.h | 166 ++++ src/ui/DebugConsole.cc | 18 +- src/ui/WaypointList.cc | 2 +- src/ui/WaypointView.cc | 41 + src/ui/WaypointView.ui | 88 +- 13 files changed, 1632 insertions(+), 146 deletions(-) delete mode 100644 src/apps/qgcvideo/QGCVideoApp.ui create mode 100644 src/apps/qgcvideo/QGCVideoMainWindow.cc create mode 100644 src/apps/qgcvideo/QGCVideoMainWindow.h create mode 100644 src/apps/qgcvideo/QGCVideoMainWindow.ui create mode 100644 src/apps/qgcvideo/QGCVideoWidget.cc create mode 100644 src/apps/qgcvideo/QGCVideoWidget.h diff --git a/qgcvideo.pro b/qgcvideo.pro index dfee8079f..8e289ef17 100644 --- a/qgcvideo.pro +++ b/qgcvideo.pro @@ -1,7 +1,7 @@ # Video streaming application for simple UDP direct byte streaming -QT += svg network +QT += svg network opengl TEMPLATE = app TARGET = qgcvideo @@ -24,18 +24,22 @@ INCLUDEPATH += . \ src/ui \ src/comm \ include/ui \ - src/ui/mavlink \ - src/apps/qgcstreamer + src/apps/qgcvideo \ # Input -HEADERS += src/apps/qgcvideo/QGCVideoApp.h +HEADERS += \ + src/apps/qgcvideo/QGCVideoMainWindow.h \ + src/apps/qgcvideo/QGCVideoApp.h \ + src/apps/qgcvideo/QGCVideoWidget.h SOURCES += \ + src/apps/qgcvideo/main.cc \ + src/apps/qgcvideo/QGCVideoMainWindow.cc \ src/apps/qgcvideo/QGCVideoApp.cc \ - src/apps/qgcvideo/main.cc + src/apps/qgcvideo/QGCVideoWidget.cc FORMS += \ - src/apps/qgcvideo/QGCVideoApp.ui + src/apps/qgcvideo/QGCVideoMainWindow.ui RESOURCES = mavground.qrc diff --git a/src/apps/qgcvideo/QGCVideoApp.cc b/src/apps/qgcvideo/QGCVideoApp.cc index bc1938816..e8c7716d0 100644 --- a/src/apps/qgcvideo/QGCVideoApp.cc +++ b/src/apps/qgcvideo/QGCVideoApp.cc @@ -1,44 +1,92 @@ /*===================================================================== - + QGroundControl Open Source Ground Control Station - + (c) 2009 - 2011 QGROUNDCONTROL PROJECT - + This file is part of the QGROUNDCONTROL project - + QGROUNDCONTROL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + QGROUNDCONTROL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with QGROUNDCONTROL. If not, see . - + ======================================================================*/ /** * @file - * @brief Main executable + * @brief Implementation of main application class + * * @author Lorenz Meier * */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "QGCVideoApp.h" -#include "ui_QGCVideoApp.h" -QGCVideoApp::QGCVideoApp(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::QGCVideoApp) + +/** + * @brief Constructor for the main application. + * + * This constructor initializes and starts the whole application. It takes standard + * command-line parameters + * + * @param argc The number of command-line parameters + * @param argv The string array of parameters + **/ + +QGCVideoApp::QGCVideoApp(int &argc, char* argv[]) : QApplication(argc, argv) { - ui->setupUi(this); + this->setApplicationName("QGC Video Streamer"); + this->setApplicationVersion("v. 1.0.0 (Beta)"); + this->setOrganizationName(QLatin1String("QGroundControl")); + this->setOrganizationDomain("http://qgroundcontrol.org"); + + QSettings::setDefaultFormat(QSettings::IniFormat); + // Exit main application when last window is closed + connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit())); + + // Set application font + QFontDatabase fontDatabase = QFontDatabase(); + const QString fontFileName = ":/general/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app + const QString fontFamilyName = "Bitstream Vera Sans"; + if(!QFile::exists(fontFileName)) printf("ERROR! font file: %s DOES NOT EXIST!\n", fontFileName.toStdString().c_str()); + fontDatabase.addApplicationFont(fontFileName); + setFont(fontDatabase.font(fontFamilyName, "Roman", 12)); + + // Create main window + QMainWindow* window = new QMainWindow(); + //window->setCentralWidget(new XMLCommProtocolWidget(window)); + window->setWindowTitle(applicationName() + " " + applicationVersion()); + window->show(); } +/** + * @brief Destructor for the groundstation. It destroys all loaded instances. + * + **/ QGCVideoApp::~QGCVideoApp() { - delete ui; } + diff --git a/src/apps/qgcvideo/QGCVideoApp.h b/src/apps/qgcvideo/QGCVideoApp.h index b07a0eee8..52bb05b44 100644 --- a/src/apps/qgcvideo/QGCVideoApp.h +++ b/src/apps/qgcvideo/QGCVideoApp.h @@ -1,52 +1,58 @@ /*===================================================================== - + QGroundControl Open Source Ground Control Station - + (c) 2009 - 2011 QGROUNDCONTROL PROJECT - + This file is part of the QGROUNDCONTROL project - + QGROUNDCONTROL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + QGROUNDCONTROL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with QGROUNDCONTROL. If not, see . - + ======================================================================*/ /** * @file - * @brief Main executable + * @brief Definition of main application class + * * @author Lorenz Meier * */ + #ifndef QGCVIDEOAPP_H #define QGCVIDEOAPP_H -#include +#include -namespace Ui { - class QGCVideoApp; -} - -class QGCVideoApp : public QMainWindow +/** + * @brief The main application and management class. + * + * This class is started by the main method and provides + * the central management unit of the groundstation application. + * + **/ +class QGCVideoApp : public QApplication { Q_OBJECT public: - explicit QGCVideoApp(QWidget *parent = 0); + QGCVideoApp(int &argc, char* argv[]); ~QGCVideoApp(); +protected: + private: - Ui::QGCVideoApp *ui; }; -#endif // QGCVIDEOAPP_H +#endif /* QGCVIDEOAPP_H */ diff --git a/src/apps/qgcvideo/QGCVideoApp.ui b/src/apps/qgcvideo/QGCVideoApp.ui deleted file mode 100644 index 989e852e8..000000000 --- a/src/apps/qgcvideo/QGCVideoApp.ui +++ /dev/null @@ -1,24 +0,0 @@ - - - - - QGCVideoApp - - - - 0 - 0 - 800 - 600 - - - - MainWindow - - - - - - - - diff --git a/src/apps/qgcvideo/QGCVideoMainWindow.cc b/src/apps/qgcvideo/QGCVideoMainWindow.cc new file mode 100644 index 000000000..6d25d65ce --- /dev/null +++ b/src/apps/qgcvideo/QGCVideoMainWindow.cc @@ -0,0 +1,45 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2011 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/** + * @file + * @brief Implementation of main window + * + * @author Dominik Honegger + * + */ + +#include "QGCVideoMainWindow.h" +#include "ui_QGCVideoMainWindow.h" + +QGCVideoMainWindow::QGCVideoMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::QGCVideoMainWindow) +{ + ui->setupUi(this); +} + +QGCVideoMainWindow::~QGCVideoMainWindow() +{ + delete ui; +} diff --git a/src/apps/qgcvideo/QGCVideoMainWindow.h b/src/apps/qgcvideo/QGCVideoMainWindow.h new file mode 100644 index 000000000..150bf3f2a --- /dev/null +++ b/src/apps/qgcvideo/QGCVideoMainWindow.h @@ -0,0 +1,53 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2011 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/** + * @file + * @brief Definition of main window + * + * @author Dominik Honegger + * + */ + +#ifndef QGCVIDEOMAINWINDOW_H +#define QGCVIDEOMAINWINDOW_H + +#include + +namespace Ui { + class QGCVideoMainWindow; +} + +class QGCVideoMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit QGCVideoMainWindow(QWidget *parent = 0); + ~QGCVideoMainWindow(); + +private: + Ui::QGCVideoMainWindow *ui; +}; + +#endif // QGCVIDEOMAINWINDOW_H diff --git a/src/apps/qgcvideo/QGCVideoMainWindow.ui b/src/apps/qgcvideo/QGCVideoMainWindow.ui new file mode 100644 index 000000000..d35161055 --- /dev/null +++ b/src/apps/qgcvideo/QGCVideoMainWindow.ui @@ -0,0 +1,82 @@ + + + QGCVideoMainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + + + + + + + + + + + + Video 1 + + + + + + + Video 2 + + + + + + + Video 3 + + + + + + + Video 4 + + + + + + + + + 0 + 0 + 800 + 22 + + + + + + + + QGCVideoWidget + QWidget +
QGCVideoWidget.h
+ 1 +
+
+ + +
diff --git a/src/apps/qgcvideo/QGCVideoWidget.cc b/src/apps/qgcvideo/QGCVideoWidget.cc new file mode 100644 index 000000000..1ec9cbcd3 --- /dev/null +++ b/src/apps/qgcvideo/QGCVideoWidget.cc @@ -0,0 +1,1127 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2010 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Head Up Display (QGCVideoWidget) + * + * @author Lorenz Meier + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "QGCVideoWidget.h" +#include "QGC.h" + +// Fix for some platforms, e.g. windows +#ifndef GL_MULTISAMPLE +#define GL_MULTISAMPLE 0x809D +#endif + +template +inline bool isnan(T value) +{ + return value != value; + +} + +// requires #include +template +inline bool isinf(T value) +{ + return std::numeric_limits::has_infinity && (value == std::numeric_limits::infinity() || (-1*value) == std::numeric_limits::infinity()); +} + +/** + * @warning The QGCVideoWidget widget will not start painting its content automatically + * to update the view, start the auto-update by calling QGCVideoWidget::start(). + * + * @param width + * @param height + * @param parent + */ +QGCVideoWidget::QGCVideoWidget(QWidget* parent) + : QGLWidget(QGLFormat(QGL::SampleBuffers), parent), + yawInt(0.0f), + mode(tr("UNKNOWN MODE")), + state(tr("UNKNOWN STATE")), + fuelStatus(tr("00.0V (00m:00s)")), + xCenterOffset(0.0f), + yCenterOffset(0.0f), + vwidth(200.0f), + vheight(150.0f), + vGaugeSpacing(50.0f), + vPitchPerDeg(6.0f), ///< 4 mm y translation per degree) + rawBuffer1(NULL), + rawBuffer2(NULL), + rawImage(NULL), + rawLastIndex(0), + rawExpectedBytes(0), + bytesPerLine(1), + imageStarted(false), + receivedDepth(8), + receivedChannels(1), + receivedWidth(640), + receivedHeight(480), + defaultColor(QColor(70, 200, 70)), + setPointColor(QColor(200, 20, 200)), + warningColor(Qt::yellow), + criticalColor(Qt::red), + infoColor(QColor(20, 200, 20)), + fuelColor(criticalColor), + warningBlinkRate(5), + refreshTimer(new QTimer(this)), + noCamera(true), + hardwareAcceleration(true), + strongStrokeWidth(1.5f), + normalStrokeWidth(1.0f), + fineStrokeWidth(0.5f), + waypointName(""), + roll(0.0f), + pitch(0.0f), + yaw(0.0f), + rollLP(0.0f), + pitchLP(0.0f), + yawLP(0.0f), + yawDiff(0.0f), + xPos(0.0), + yPos(0.0), + zPos(0.0), + xSpeed(0.0), + ySpeed(0.0), + zSpeed(0.0), + lastSpeedUpdate(0), + totalSpeed(0.0), + totalAcc(0.0), + lat(0.0), + lon(0.0), + alt(0.0), + load(0.0f), + offlineDirectory(""), + nextOfflineImage(""), + hudInstrumentsEnabled(true), + videoEnabled(false), + xImageFactor(1.0), + yImageFactor(1.0), + imageRequested(false) +{ + // Set auto fill to false + setAutoFillBackground(false); + + // Set minimum size + setMinimumSize(80, 60); + // Set preferred size + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + scalingFactor = this->width()/vwidth; + + // Fill with black background + QImage fill = QImage(640, 480, QImage::Format_Indexed8); + fill.setNumColors(3); + fill.setColor(0, qRgb(0, 0, 0)); + fill.setColor(1, qRgb(0, 0, 0)); + fill.setColor(2, qRgb(0, 0, 0)); + fill.fill(0); + + //QString imagePath = "/Users/user/Desktop/frame0000.png"; + //qDebug() << __FILE__ << __LINE__ << "template image:" << imagePath; + //fill = QImage(imagePath); + + glImage = QGLWidget::convertToGLFormat(fill); + + // Refresh timer + refreshTimer->setInterval(updateInterval); + connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintQGCVideoWidget())); + + // Resize to correct size and fill with image + //glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits()); + + // Set size once + //setFixedSize(fill.size()); + //setMinimumSize(fill.size()); + //setMaximumSize(fill.size()); + // Lock down the size + //setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + + fontDatabase = QFontDatabase(); + const QString fontFileName = ":/general/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app + const QString fontFamilyName = "Bitstream Vera Sans"; + if(!QFile::exists(fontFileName)) qDebug() << "ERROR! font file: " << fontFileName << " DOES NOT EXIST!"; + + fontDatabase.addApplicationFont(fontFileName); + font = fontDatabase.font(fontFamilyName, "Roman", qMax(5,(int)(10.0f*scalingFactor*1.2f+0.5f))); + QFont* fontPtr = &font; + if (!fontPtr) { + qDebug() << "ERROR! FONT NOT LOADED!"; + } else { + if (font.family() != fontFamilyName) qDebug() << "ERROR! WRONG FONT LOADED: " << fontFamilyName; + } + + createActions(); +} + +QGCVideoWidget::~QGCVideoWidget() +{ + refreshTimer->stop(); +} + +QSize QGCVideoWidget::sizeHint() const +{ + return QSize(width(), (width()*3.0f)/4); +} + +void QGCVideoWidget::showEvent(QShowEvent* event) +{ + // React only to internal (pre-display) + // events + Q_UNUSED(event) + refreshTimer->start(updateInterval); +} + +void QGCVideoWidget::hideEvent(QHideEvent* event) +{ + // React only to internal (pre-display) + // events + Q_UNUSED(event); + refreshTimer->stop(); +} + +void QGCVideoWidget::contextMenuEvent (QContextMenuEvent* event) +{ + QMenu menu(this); + // Update actions + enableHUDAction->setChecked(hudInstrumentsEnabled); + enableVideoAction->setChecked(videoEnabled); + + menu.addAction(enableHUDAction); + //menu.addAction(selectQGCVideoWidgetColorAction); + menu.addAction(enableVideoAction); + menu.addAction(selectOfflineDirectoryAction); + //menu.addAction(selectVideoChannelAction); + menu.exec(event->globalPos()); +} + +void QGCVideoWidget::createActions() +{ + enableHUDAction = new QAction(tr("Enable QGCVideoWidget"), this); + enableHUDAction->setStatusTip(tr("Show the QGCVideoWidget instruments in this window")); + enableHUDAction->setCheckable(true); + enableHUDAction->setChecked(hudInstrumentsEnabled); + connect(enableHUDAction, SIGNAL(triggered(bool)), this, SLOT(enableHUDInstruments(bool))); + + enableVideoAction = new QAction(tr("Enable Video Live feed"), this); + enableVideoAction->setStatusTip(tr("Show the video live feed")); + enableVideoAction->setCheckable(true); + enableVideoAction->setChecked(videoEnabled); + connect(enableVideoAction, SIGNAL(triggered(bool)), this, SLOT(enableVideo(bool))); + + selectOfflineDirectoryAction = new QAction(tr("Select image log"), this); + selectOfflineDirectoryAction->setStatusTip(tr("Load previously logged images into simulation / replay")); + connect(selectOfflineDirectoryAction, SIGNAL(triggered()), this, SLOT(selectOfflineDirectory())); +} + +/** + * @param y coordinate in pixels to be converted to reference mm units + * @return the screen coordinate relative to the QGLWindow origin + */ +float QGCVideoWidget::refToScreenX(float x) +{ + //qDebug() << "sX: " << (scalingFactor * x); + return (scalingFactor * x); +} +/** + * @param x coordinate in pixels to be converted to reference mm units + * @return the screen coordinate relative to the QGLWindow origin + */ +float QGCVideoWidget::refToScreenY(float y) +{ + //qDebug() << "sY: " << (scalingFactor * y); + return (scalingFactor * y); +} + +/** + * This functions works in the OpenGL view, which is already translated by + * the x and y center offsets. + * + */ +void QGCVideoWidget::paintCenterBackground(float roll, float pitch, float yaw) +{ + // Center indicator is 100 mm wide + float referenceWidth = 70.0; + float referenceHeight = 70.0; + + // QGCVideoWidget is assumed to be 200 x 150 mm + // so that positions can be hardcoded + // but can of course be scaled. + + double referencePositionX = vwidth / 2.0 - referenceWidth/2.0; + double referencePositionY = vheight / 2.0 - referenceHeight/2.0; + + //this->width()/2.0+(xCenterOffset*scalingFactor), this->height()/2.0+(yCenterOffset*scalingFactor); + + setupGLView(referencePositionX, referencePositionY, referenceWidth, referenceHeight); + + // Store current position in the model view + // the position will be restored after drawing + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + // Move to the center of the window + glTranslatef(referenceWidth/2.0f,referenceHeight/2.0f,0); + + // Move based on the yaw difference + glTranslatef(yaw, 0.0f, 0.0f); + + // Rotate based on the bank + glRotatef((roll/M_PI)*180.0f, 0.0f, 0.0f, 1.0f); + + // Translate in the direction of the rotation based + // on the pitch. On the 777, a pitch of 1 degree = 2 mm + //glTranslatef(0, ((-pitch/M_PI)*180.0f * vPitchPerDeg), 0); + glTranslatef(0.0f, (-pitch * vPitchPerDeg * 16.5f), 0.0f); + + // Ground + glColor3ub(179,102,0); + + glBegin(GL_POLYGON); + glVertex2f(-300,-300); + glVertex2f(-300,0); + glVertex2f(300,0); + glVertex2f(300,-300); + glVertex2f(-300,-300); + glEnd(); + + // Sky + glColor3ub(0,153,204); + + glBegin(GL_POLYGON); + glVertex2f(-300,0); + glVertex2f(-300,300); + glVertex2f(300,300); + glVertex2f(300,0); + glVertex2f(-300,0); + + glEnd(); +} + +/** + * Paint text on top of the image and OpenGL drawings + * + * @param text chars to write + * @param color text color + * @param fontSize text size in mm + * @param refX position in reference units (mm of the real instrument). This is relative to the measurement unit position, NOT in pixels. + * @param refY position in reference units (mm of the real instrument). This is relative to the measurement unit position, NOT in pixels. + */ +void QGCVideoWidget::paintText(QString text, QColor color, float fontSize, float refX, float refY, QPainter* painter) +{ + QPen prevPen = painter->pen(); + float pPositionX = refToScreenX(refX) - (fontSize*scalingFactor*0.072f); + float pPositionY = refToScreenY(refY) - (fontSize*scalingFactor*0.212f); + + QFont font("Bitstream Vera Sans"); + // Enforce minimum font size of 5 pixels + int fSize = qMax(5, (int)(fontSize*scalingFactor*1.26f)); + font.setPixelSize(fSize); + + QFontMetrics metrics = QFontMetrics(font); + int border = qMax(4, metrics.leading()); + QRect rect = metrics.boundingRect(0, 0, width() - 2*border, int(height()*0.125), + Qt::AlignLeft | Qt::TextWordWrap, text); + painter->setPen(color); + painter->setFont(font); + painter->setRenderHint(QPainter::TextAntialiasing); + painter->drawText(pPositionX, pPositionY, + rect.width(), rect.height(), + Qt::AlignCenter | Qt::TextWordWrap, text); + painter->setPen(prevPen); +} + +void QGCVideoWidget::initializeGL() +{ + bool antialiasing = true; + + // Antialiasing setup + if(antialiasing) { + glEnable(GL_MULTISAMPLE); + glEnable(GL_BLEND); + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_POINT_SMOOTH); + glEnable(GL_LINE_SMOOTH); + + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + } else { + glDisable(GL_BLEND); + glDisable(GL_POINT_SMOOTH); + glDisable(GL_LINE_SMOOTH); + } +} + +/** + * @param referencePositionX horizontal position in the reference mm-unit space + * @param referencePositionY horizontal position in the reference mm-unit space + * @param referenceWidth width in the reference mm-unit space + * @param referenceHeight width in the reference mm-unit space + */ +void QGCVideoWidget::setupGLView(float referencePositionX, float referencePositionY, float referenceWidth, float referenceHeight) +{ + int pixelWidth = (int)(referenceWidth * scalingFactor); + int pixelHeight = (int)(referenceHeight * scalingFactor); + // Translate and scale the GL view in the virtual reference coordinate units on the screen + int pixelPositionX = (int)((referencePositionX * scalingFactor) + xCenterOffset); + int pixelPositionY = this->height() - (referencePositionY * scalingFactor) + yCenterOffset - pixelHeight; + + //qDebug() << "Pixel x" << pixelPositionX << "pixelY" << pixelPositionY; + //qDebug() << "xCenterOffset:" << xCenterOffset << "yCenterOffest" << yCenterOffset + + + //The viewport is established at the correct pixel position and clips everything + // out of the desired instrument location + glViewport(pixelPositionX, pixelPositionY, pixelWidth, pixelHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // The ortho projection is setup in a way that so that the drawing is done in the + // reference coordinate space + glOrtho(0, referenceWidth, 0, referenceHeight, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + //glScalef(scaleX, scaleY, 1.0f); +} + +void QGCVideoWidget::paintRollPitchStrips() +{ +} + + +void QGCVideoWidget::paintEvent(QPaintEvent *event) +{ + // Event is not needed + // the event is ignored as this widget + // is refreshed automatically + Q_UNUSED(event); +} + +void QGCVideoWidget::paintHUD() +{ + if (isVisible()) { + // static quint64 interval = 0; + // qDebug() << "INTERVAL:" << MG::TIME::getGroundTimeNow() - interval << __FILE__ << __LINE__; + // interval = MG::TIME::getGroundTimeNow(); + +#if (QGC_EVENTLOOP_DEBUG) + qDebug() << "EVENTLOOP:" << __FILE__ << __LINE__; +#endif + + // Read out most important values to limit hash table lookups + // Low-pass roll, pitch and yaw + rollLP = rollLP * 0.2f + 0.8f * roll; + pitchLP = pitchLP * 0.2f + 0.8f * pitch; + yawLP = yawLP * 0.2f + 0.8f * yaw; + + // Translate for yaw + const float maxYawTrans = 60.0f; + + float newYawDiff = yawDiff; + if (isinf(newYawDiff)) newYawDiff = yawDiff; + if (newYawDiff > M_PI) newYawDiff = newYawDiff - M_PI; + + if (newYawDiff < -M_PI) newYawDiff = newYawDiff + M_PI; + + newYawDiff = yawDiff * 0.8 + newYawDiff * 0.2; + + yawDiff = newYawDiff; + + yawInt += newYawDiff; + + if (yawInt > M_PI) yawInt = (float)M_PI; + if (yawInt < -M_PI) yawInt = (float)-M_PI; + + float yawTrans = yawInt * (float)maxYawTrans; + yawInt *= 0.6f; + + if ((yawTrans < 5.0) && (yawTrans > -5.0)) yawTrans = 0; + + // Negate to correct direction + yawTrans = -yawTrans; + + //qDebug() << "yaw translation" << yawTrans << "integral" << yawInt << "difference" << yawDiff << "yaw" << yaw; + + // Update scaling factor + // adjust scaling to fit both horizontally and vertically + scalingFactor = this->width()/vwidth; + double scalingFactorH = this->height()/vheight; + if (scalingFactorH < scalingFactor) scalingFactor = scalingFactorH; + + + + // OPEN GL PAINTING + // Store model view matrix to be able to reset it to the previous state + makeCurrent(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Fill with black background + if (videoEnabled) { + if (nextOfflineImage != "" && QFileInfo(nextOfflineImage).exists()) { + qDebug() << __FILE__ << __LINE__ << "template image:" << nextOfflineImage; + QImage fill = QImage(nextOfflineImage); + + glImage = QGLWidget::convertToGLFormat(fill); + + // Reset to save load efforts + nextOfflineImage = ""; + } + + glRasterPos2i(0, 0); + + xImageFactor = width() / (float)glImage.width(); + yImageFactor = height() / (float)glImage.height(); + float imageFactor = qMin(xImageFactor, yImageFactor); + glPixelZoom(imageFactor, imageFactor); + // Resize to correct size and fill with image + glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits()); + //qDebug() << "DRAWING GL IMAGE"; + } else { + // Blue / brown background + paintCenterBackground(roll, pitch, yawTrans); + } + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + // END OF OPENGL PAINTING + + if (hudInstrumentsEnabled) { + + //glEnable(GL_MULTISAMPLE); + + // QT PAINTING + //makeCurrent(); + QPainter painter; + painter.begin(this); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setRenderHint(QPainter::HighQualityAntialiasing, true); + painter.translate((this->vwidth/2.0+xCenterOffset)*scalingFactor, (this->vheight/2.0+yCenterOffset)*scalingFactor); + + + + // COORDINATE FRAME IS NOW (0,0) at CENTER OF WIDGET + + + // Draw all fixed indicators + // MODE + paintText(mode, infoColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 10, &painter); + // STATE + paintText(state, infoColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 15, &painter); + // BATTERY + paintText(fuelStatus, fuelColor, 2.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 20, &painter); + // Waypoint + paintText(waypointName, defaultColor, 2.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter); + + // YAW INDICATOR + // + // . + // . . + // ....... + // + const float yawIndicatorWidth = 4.0f; + const float yawIndicatorY = vheight/2.0f - 10.0f; + QPolygon yawIndicator(4); + yawIndicator.setPoint(0, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY))); + yawIndicator.setPoint(1, QPoint(refToScreenX(yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth))); + yawIndicator.setPoint(2, QPoint(refToScreenX(-yawIndicatorWidth/2.0f), refToScreenY(yawIndicatorY+yawIndicatorWidth))); + yawIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(yawIndicatorY))); + painter.setPen(defaultColor); + painter.drawPolyline(yawIndicator); + + // CENTER + + // HEADING INDICATOR + // + // __ __ + // \/\/ + // + const float hIndicatorWidth = 7.0f; + const float hIndicatorY = -25.0f; + const float hIndicatorYLow = hIndicatorY + hIndicatorWidth / 6.0f; + const float hIndicatorSegmentWidth = hIndicatorWidth / 7.0f; + QPolygon hIndicator(7); + hIndicator.setPoint(0, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f), refToScreenY(hIndicatorY))); + hIndicator.setPoint(1, QPoint(refToScreenX(0.0f-hIndicatorWidth/2.0f+hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY))); + hIndicator.setPoint(2, QPoint(refToScreenX(0.0f-hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow))); + hIndicator.setPoint(3, QPoint(refToScreenX(0.0f), refToScreenY(hIndicatorY))); + hIndicator.setPoint(4, QPoint(refToScreenX(0.0f+hIndicatorSegmentWidth*1.0f), refToScreenY(hIndicatorYLow))); + hIndicator.setPoint(5, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f-hIndicatorSegmentWidth*1.75f), refToScreenY(hIndicatorY))); + hIndicator.setPoint(6, QPoint(refToScreenX(0.0f+hIndicatorWidth/2.0f), refToScreenY(hIndicatorY))); + painter.setPen(defaultColor); + painter.drawPolyline(hIndicator); + + + // SETPOINT + const float centerWidth = 4.0f; + painter.setPen(defaultColor); + painter.setBrush(Qt::NoBrush); + // TODO + //painter.drawEllipse(QPointF(refToScreenX(qMin(10.0f, values.value("roll desired", 0.0f) * 10.0f)), refToScreenY(qMin(10.0f, values.value("pitch desired", 0.0f) * 10.0f))), refToScreenX(centerWidth/2.0f), refToScreenX(centerWidth/2.0f)); + + const float centerCrossWidth = 10.0f; + // left + painter.drawLine(QPointF(refToScreenX(-centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(-centerCrossWidth / 2.0f), refToScreenY(0.0f))); + // right + painter.drawLine(QPointF(refToScreenX(centerWidth / 2.0f), refToScreenY(0.0f)), QPointF(refToScreenX(centerCrossWidth / 2.0f), refToScreenY(0.0f))); + // top + painter.drawLine(QPointF(refToScreenX(0.0f), refToScreenY(-centerWidth / 2.0f)), QPointF(refToScreenX(0.0f), refToScreenY(-centerCrossWidth / 2.0f))); + + + + // COMPASS + const float compassY = -vheight/2.0f + 10.0f; + QRectF compassRect(QPointF(refToScreenX(-5.0f), refToScreenY(compassY)), QSizeF(refToScreenX(10.0f), refToScreenY(5.0f))); + painter.setBrush(Qt::NoBrush); + painter.setPen(Qt::SolidLine); + painter.setPen(defaultColor); + painter.drawRoundedRect(compassRect, 2, 2); + QString yawAngle; + + // const float yawDeg = ((values.value("yaw", 0.0f)/M_PI)*180.0f)+180.f; + + // YAW is in compass-human readable format, so 0 - 360deg. This is normal in aviation, not -180 - +180. + const float yawDeg = ((yawLP/M_PI)*180.0f)+180.0f+180.0f; + int yawCompass = static_cast(yawDeg) % 360; + yawAngle.sprintf("%03d", yawCompass); + paintText(yawAngle, defaultColor, 3.5f, -4.3f, compassY+ 0.97f, &painter); + + // CHANGE RATE STRIPS + drawChangeRateStrip(-51.0f, -50.0f, 15.0f, -1.0f, 1.0f, -zSpeed, &painter); + + // CHANGE RATE STRIPS + drawChangeRateStrip(49.0f, -50.0f, 15.0f, -1.0f, 1.0f, totalAcc, &painter); + + // GAUGES + + // Left altitude gauge + float gaugeAltitude; + + if (this->alt != 0) { + gaugeAltitude = alt; + } else { + gaugeAltitude = -zPos; + } + + drawChangeIndicatorGauge(-vGaugeSpacing, -15.0f, 10.0f, 2.0f, gaugeAltitude, defaultColor, &painter, false); + + // Right speed gauge + drawChangeIndicatorGauge(vGaugeSpacing, -15.0f, 10.0f, 5.0f, totalSpeed, defaultColor, &painter, false); + + + // Waypoint name + if (waypointName != "") paintText(waypointName, defaultColor, 2.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter); + + // MOVING PARTS + + + painter.translate(refToScreenX(yawTrans), 0); + + // Rotate view and draw all roll-dependent indicators + painter.rotate((rollLP/M_PI)* -180.0f); + + painter.translate(0, (-pitchLP/(float)M_PI)* -180.0f * refToScreenY(1.8f)); + + //qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f); + + // PITCH + + paintPitchLines(pitchLP, &painter); + painter.end(); + } else { + QPainter painter; + painter.begin(this); + painter.end(); + } + //glDisable(GL_MULTISAMPLE); + + + + //glFlush(); + } +} + + +/** + * @param pitch pitch angle in degrees (-180 to 180) + */ +void QGCVideoWidget::paintPitchLines(float pitch, QPainter* painter) +{ + QString label; + + const float yDeg = vPitchPerDeg; + const float lineDistance = 5.0f; ///< One pitch line every 10 degrees + const float posIncrement = yDeg * lineDistance; + float posY = posIncrement; + const float posLimit = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f)); + + const float offsetAbs = pitch * yDeg; + + float offset = pitch; + if (offset < 0) offset = -offset; + int offsetCount = 0; + while (offset > lineDistance) { + offset -= lineDistance; + offsetCount++; + } + + int iPos = (int)(0.5f + lineDistance); ///< The first line + int iNeg = (int)(-0.5f - lineDistance); ///< The first line + + offset *= yDeg; + + + painter->setPen(defaultColor); + + posY = -offsetAbs + posIncrement; //+ 100;// + lineDistance; + + while (posY < posLimit) { + paintPitchLinePos(label.sprintf("%3d", iPos), 0.0f, -posY, painter); + posY += posIncrement; + iPos += (int)lineDistance; + } + + + + // HORIZON + // + // ------------ ------------ + // + const float pitchWidth = 30.0f; + const float pitchGap = pitchWidth / 2.5f; + const QColor horizonColor = defaultColor; + const float diagonal = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f)); + const float lineWidth = refLineWidthToPen(0.5f); + + // Left horizon + drawLine(0.0f-diagonal, offsetAbs, 0.0f-pitchGap/2.0f, offsetAbs, lineWidth, horizonColor, painter); + // Right horizon + drawLine(0.0f+pitchGap/2.0f, offsetAbs, 0.0f+diagonal, offsetAbs, lineWidth, horizonColor, painter); + + + + label.clear(); + + posY = offsetAbs + posIncrement; + + + while (posY < posLimit) { + paintPitchLineNeg(label.sprintf("%3d", iNeg), 0.0f, posY, painter); + posY += posIncrement; + iNeg -= (int)lineDistance; + } +} + +void QGCVideoWidget::paintPitchLinePos(QString text, float refPosX, float refPosY, QPainter* painter) +{ + //painter->setPen(QPen(QBrush, normalStrokeWidth)); + + const float pitchWidth = 30.0f; + const float pitchGap = pitchWidth / 2.5f; + const float pitchHeight = pitchWidth / 12.0f; + const float textSize = pitchHeight * 1.1f; + const float lineWidth = 0.5f; + + // Positive pitch indicator: + // + // _______ _______ + // |10 | + // + + // Left vertical line + drawLine(refPosX-pitchWidth/2.0f, refPosY, refPosX-pitchWidth/2.0f, refPosY+pitchHeight, lineWidth, defaultColor, painter); + // Left horizontal line + drawLine(refPosX-pitchWidth/2.0f, refPosY, refPosX-pitchGap/2.0f, refPosY, lineWidth, defaultColor, painter); + // Text left + paintText(text, defaultColor, textSize, refPosX-pitchWidth/2.0 + 0.75f, refPosY + pitchHeight - 1.75f, painter); + + // Right vertical line + drawLine(refPosX+pitchWidth/2.0f, refPosY, refPosX+pitchWidth/2.0f, refPosY+pitchHeight, lineWidth, defaultColor, painter); + // Right horizontal line + drawLine(refPosX+pitchWidth/2.0f, refPosY, refPosX+pitchGap/2.0f, refPosY, lineWidth, defaultColor, painter); +} + +void QGCVideoWidget::paintPitchLineNeg(QString text, float refPosX, float refPosY, QPainter* painter) +{ + const float pitchWidth = 30.0f; + const float pitchGap = pitchWidth / 2.5f; + const float pitchHeight = pitchWidth / 12.0f; + const float textSize = pitchHeight * 1.1f; + const float segmentWidth = ((pitchWidth - pitchGap)/2.0f) / 7.0f; ///< Four lines and three gaps -> 7 segments + + const float lineWidth = 0.1f; + + // Negative pitch indicator: + // + // -10 + // _ _ _ _| |_ _ _ _ + // + // + + // Left vertical line + drawLine(refPosX-pitchGap/2.0, refPosY, refPosX-pitchGap/2.0, refPosY-pitchHeight, lineWidth, defaultColor, painter); + // Left horizontal line with four segments + for (int i = 0; i < 7; i+=2) { + drawLine(refPosX-pitchWidth/2.0+(i*segmentWidth), refPosY, refPosX-pitchWidth/2.0+(i*segmentWidth)+segmentWidth, refPosY, lineWidth, defaultColor, painter); + } + // Text left + paintText(text, defaultColor, textSize, refPosX-pitchWidth/2.0f + 0.75f, refPosY + pitchHeight - 1.75f, painter); + + // Right vertical line + drawLine(refPosX+pitchGap/2.0, refPosY, refPosX+pitchGap/2.0, refPosY-pitchHeight, lineWidth, defaultColor, painter); + // Right horizontal line with four segments + for (int i = 0; i < 7; i+=2) { + drawLine(refPosX+pitchWidth/2.0f-(i*segmentWidth), refPosY, refPosX+pitchWidth/2.0f-(i*segmentWidth)-segmentWidth, refPosY, lineWidth, defaultColor, painter); + } +} + +void rotatePointClockWise(QPointF& p, float angle) +{ + // Standard 2x2 rotation matrix, counter-clockwise + // + // | cos(phi) sin(phi) | + // | -sin(phi) cos(phi) | + // + + //p.setX(cos(angle) * p.x() + sin(angle) * p.y()); + //p.setY(-sin(angle) * p.x() + cos(angle) * p.y()); + + + p.setX(cos(angle) * p.x() + sin(angle)* p.y()); + p.setY((-1.0f * sin(angle) * p.x()) + cos(angle) * p.y()); +} + +float QGCVideoWidget::refLineWidthToPen(float line) +{ + return line * 2.50f; +} + +/** + * Rotate a polygon around a point + * + * @param p polygon to rotate + * @param origin the rotation center + * @param angle rotation angle, in radians + * @return p Polygon p rotated by angle around the origin point + */ +void QGCVideoWidget::rotatePolygonClockWiseRad(QPolygonF& p, float angle, QPointF origin) +{ + // Standard 2x2 rotation matrix, counter-clockwise + // + // | cos(phi) sin(phi) | + // | -sin(phi) cos(phi) | + // + for (int i = 0; i < p.size(); i++) { + QPointF curr = p.at(i); + + const float x = curr.x(); + const float y = curr.y(); + + curr.setX(((cos(angle) * (x-origin.x())) + (-sin(angle) * (y-origin.y()))) + origin.x()); + curr.setY(((sin(angle) * (x-origin.x())) + (cos(angle) * (y-origin.y()))) + origin.y()); + p.replace(i, curr); + } +} + +void QGCVideoWidget::drawPolygon(QPolygonF refPolygon, QPainter* painter) +{ + // Scale coordinates + QPolygonF draw(refPolygon.size()); + for (int i = 0; i < refPolygon.size(); i++) { + QPointF curr; + curr.setX(refToScreenX(refPolygon.at(i).x())); + curr.setY(refToScreenY(refPolygon.at(i).y())); + draw.replace(i, curr); + } + painter->drawPolygon(draw); +} + +void QGCVideoWidget::drawChangeRateStrip(float xRef, float yRef, float height, float minRate, float maxRate, float value, QPainter* painter) +{ + QBrush brush(defaultColor, Qt::NoBrush); + painter->setBrush(brush); + QPen rectPen(Qt::SolidLine); + rectPen.setWidth(0); + rectPen.setColor(defaultColor); + painter->setPen(rectPen); + + float scaledValue = value; + + // Saturate value + if (value > maxRate) scaledValue = maxRate; + if (value < minRate) scaledValue = minRate; + + // x (Origin: xRef, yRef) + // - + // | + // | + // | + // = + // | + // -0.005 >| + // | + // - + + const float width = height / 8.0f; + const float lineWidth = 0.5f; + + // Indicator lines + // Top horizontal line + drawLine(xRef, yRef, xRef+width, yRef, lineWidth, defaultColor, painter); + // Vertical main line + drawLine(xRef+width/2.0f, yRef, xRef+width/2.0f, yRef+height, lineWidth, defaultColor, painter); + // Zero mark + drawLine(xRef, yRef+height/2.0f, xRef+width, yRef+height/2.0f, lineWidth, defaultColor, painter); + // Horizontal bottom line + drawLine(xRef, yRef+height, xRef+width, yRef+height, lineWidth, defaultColor, painter); + + // Text + QString label; + label.sprintf("< %+06.2f", value); + paintText(label, defaultColor, 3.0f, xRef+width/2.0f, yRef+height-((scaledValue - minRate)/(maxRate-minRate))*height - 1.6f, painter); +} + +//void QGCVideoWidget::drawSystemIndicator(float xRef, float yRef, int maxNum, float maxWidth, float maxHeight, QPainter* painter) +//{ +// Q_UNUSED(maxWidth); +// Q_UNUSED(maxHeight); +// if (values.size() > 0) +// { +// QString selectedKey = values.begin().key(); +// // | | | | | | +// // | | | | | | +// // x speed: 2.54 + +// // One column per value +// QMapIterator value(values); + +// float x = xRef; +// float y = yRef; + +// const float vspacing = 1.0f; +// float width = 1.5f; +// float height = 1.5f; +// const float hspacing = 0.6f; + +// // TODO ensure that instrument stays smaller than maxWidth and maxHeight + + +// int i = 0; +// while (value.hasNext() && i < maxNum) +// { +// value.next(); +// QBrush brush(Qt::SolidPattern); + + +// if (value.value() < 0.01f && value.value() > -0.01f) +// { +// brush.setColor(Qt::gray); +// } +// else if (value.value() > 0.01f) +// { +// brush.setColor(Qt::blue); +// } +// else +// { +// brush.setColor(Qt::yellow); +// } + +// painter->setBrush(brush); +// painter->setPen(Qt::NoPen); + +// // Draw current value colormap +// painter->drawRect(refToScreenX(x), refToScreenY(y), refToScreenX(width), refToScreenY(height)); + +// // Draw change rate colormap +// painter->drawRect(refToScreenX(x), refToScreenY(y+height+hspacing), refToScreenX(width), refToScreenY(height)); + +// // Draw mean value colormap +// painter->drawRect(refToScreenX(x), refToScreenY(y+2.0f*(height+hspacing)), refToScreenX(width), refToScreenY(height)); + +// // Add spacing +// x += width+vspacing; + +// // Iterate +// i++; +// } + +// // Draw detail label +// QString detail = "NO DATA AVAILABLE"; + +// if (values.contains(selectedKey)) +// { +// detail = values.find(selectedKey).key(); +// detail.append(": "); +// detail.append(QString::number(values.find(selectedKey).value())); +// } +// paintText(detail, QColor(255, 255, 255), 3.0f, xRef, yRef+3.0f*(height+hspacing)+1.0f, painter); +// } +//} + +void QGCVideoWidget::drawChangeIndicatorGauge(float xRef, float yRef, float radius, float expectedMaxChange, float value, const QColor& color, QPainter* painter, bool solid) +{ + // Draw the circle + QPen circlePen(Qt::SolidLine); + if (!solid) circlePen.setStyle(Qt::DotLine); + circlePen.setWidth(refLineWidthToPen(0.5f)); + circlePen.setColor(defaultColor); + painter->setBrush(Qt::NoBrush); + painter->setPen(circlePen); + drawCircle(xRef, yRef, radius, 200.0f, 170.0f, 1.0f, color, painter); + + QString label; + label.sprintf("%05.1f", value); + + // Draw the value + paintText(label, color, 4.5f, xRef-7.5f, yRef-2.0f, painter); + + // Draw the needle + // Scale the rotation so that the gauge does one revolution + // per max. change + const float rangeScale = (2.0f * M_PI) / expectedMaxChange; + const float maxWidth = radius / 10.0f; + const float minWidth = maxWidth * 0.3f; + + QPolygonF p(6); + + p.replace(0, QPointF(xRef-maxWidth/2.0f, yRef-radius * 0.5f)); + p.replace(1, QPointF(xRef-minWidth/2.0f, yRef-radius * 0.9f)); + p.replace(2, QPointF(xRef+minWidth/2.0f, yRef-radius * 0.9f)); + p.replace(3, QPointF(xRef+maxWidth/2.0f, yRef-radius * 0.5f)); + p.replace(4, QPointF(xRef, yRef-radius * 0.46f)); + p.replace(5, QPointF(xRef-maxWidth/2.0f, yRef-radius * 0.5f)); + + rotatePolygonClockWiseRad(p, value*rangeScale, QPointF(xRef, yRef)); + + QBrush indexBrush; + indexBrush.setColor(defaultColor); + indexBrush.setStyle(Qt::SolidPattern); + painter->setPen(Qt::SolidLine); + painter->setPen(defaultColor); + painter->setBrush(indexBrush); + drawPolygon(p, painter); +} + +void QGCVideoWidget::drawLine(float refX1, float refY1, float refX2, float refY2, float width, const QColor& color, QPainter* painter) +{ + QPen pen(Qt::SolidLine); + pen.setWidth(refLineWidthToPen(width)); + pen.setColor(color); + painter->setPen(pen); + painter->drawLine(QPoint(refToScreenX(refX1), refToScreenY(refY1)), QPoint(refToScreenX(refX2), refToScreenY(refY2))); +} + +void QGCVideoWidget::drawEllipse(float refX, float refY, float radiusX, float radiusY, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter) +{ + Q_UNUSED(startDeg); + Q_UNUSED(endDeg); + QPen pen(painter->pen().style()); + pen.setWidth(refLineWidthToPen(lineWidth)); + pen.setColor(color); + painter->setPen(pen); + painter->drawEllipse(QPointF(refToScreenX(refX), refToScreenY(refY)), refToScreenX(radiusX), refToScreenY(radiusY)); +} + +void QGCVideoWidget::drawCircle(float refX, float refY, float radius, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter) +{ + drawEllipse(refX, refY, radius, radius, startDeg, endDeg, lineWidth, color, painter); +} + +void QGCVideoWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w, 0, h, -1, 1); + glMatrixMode(GL_MODELVIEW); + glPolygonMode(GL_FRONT, GL_FILL); + //FIXME + paintHUD(); +} + +void QGCVideoWidget::commitRawDataToGL() +{ + qDebug() << __FILE__ << __LINE__ << "Copying raw data to GL buffer:" << rawImage << receivedWidth << receivedHeight << image->format(); + if (image != NULL) { + QImage::Format format = image->format(); + QImage* newImage = new QImage(rawImage, receivedWidth, receivedHeight, format); + if (format == QImage::Format_Indexed8) { + // Create matching color table + newImage->setNumColors(256); + for (int i = 0; i < 256; i++) { + newImage->setColor(i, qRgb(i, i, i)); + //qDebug() << __FILE__ << __LINE__ << std::hex << i; + } + } + + glImage = QGLWidget::convertToGLFormat(*newImage); + delete image; + image = newImage; + // Switch buffers + if (rawImage == rawBuffer1) { + rawImage = rawBuffer2; + //qDebug() << "Now buffer 2"; + } else { + rawImage = rawBuffer1; + //qDebug() << "Now buffer 1"; + } + } + update(); +} + +void QGCVideoWidget::saveImage(QString fileName) +{ + image->save(fileName); +} + +void QGCVideoWidget::saveImage() +{ + //Bring up popup + QString fileName = "output.png"; + saveImage(fileName); +} + +void QGCVideoWidget::copyImage(const QImage& img) +{ + qDebug() << "QGCVideoWidget::copyImage()"; + this->glImage = QGLWidget::convertToGLFormat(img); +} diff --git a/src/apps/qgcvideo/QGCVideoWidget.h b/src/apps/qgcvideo/QGCVideoWidget.h new file mode 100644 index 000000000..d88a64580 --- /dev/null +++ b/src/apps/qgcvideo/QGCVideoWidget.h @@ -0,0 +1,166 @@ +#ifndef QGCVIDEOWIDGET_H +#define QGCVIDEOWIDGET_H + +#include +#include +#include +#include +#include + + +/** + * @brief Video Display + * + * Displays video data from RAM via OpenGL + */ +class QGCVideoWidget : public QGLWidget +{ + Q_OBJECT +public: + QGCVideoWidget(QWidget* parent = NULL); + ~QGCVideoWidget(); + + void resizeGL(int w, int h); + +public slots: + void initializeGL(); + + void saveImage(); + void saveImage(QString fileName); + /** @brief Copy an image from an external buffer */ + void copyImage(const QImage& img); + void enableHUDInstruments(bool enabled) { hudInstrumentsEnabled = enabled; } + + +protected slots: + void paintCenterBackground(float roll, float pitch, float yaw); + void paintRollPitchStrips(); + void paintPitchLines(float pitch, QPainter* painter); + /** @brief Paint text on top of the image and OpenGL drawings */ + void paintText(QString text, QColor color, float fontSize, float refX, float refY, QPainter* painter); + /** @brief Setup the OpenGL view for drawing a sub-component of the HUD */ + void setupGLView(float referencePositionX, float referencePositionY, float referenceWidth, float referenceHeight); + void paintHUD(); + void paintPitchLinePos(QString text, float refPosX, float refPosY, QPainter* painter); + void paintPitchLineNeg(QString text, float refPosX, float refPosY, QPainter* painter); + + void drawLine(float refX1, float refY1, float refX2, float refY2, float width, const QColor& color, QPainter* painter); + void drawEllipse(float refX, float refY, float radiusX, float radiusY, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter); + void drawCircle(float refX, float refY, float radius, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter); + + void drawChangeRateStrip(float xRef, float yRef, float height, float minRate, float maxRate, float value, QPainter* painter); + void drawChangeIndicatorGauge(float xRef, float yRef, float radius, float expectedMaxChange, float value, const QColor& color, QPainter* painter, bool solid=true); + + void drawPolygon(QPolygonF refPolygon, QPainter* painter); + +protected: + void commitRawDataToGL(); + /** @brief Convert reference coordinates to screen coordinates */ + float refToScreenX(float x); + /** @brief Convert reference coordinates to screen coordinates */ + float refToScreenY(float y); + /** @brief Convert mm line widths to QPen line widths */ + float refLineWidthToPen(float line); + /** @brief Rotate a polygon around a point clockwise */ + void rotatePolygonClockWiseRad(QPolygonF& p, float angle, QPointF origin); + /** @brief Preferred Size */ + QSize sizeHint() const; + /** @brief Start updating widget */ + void showEvent(QShowEvent* event); + /** @brief Stop updating widget */ + void hideEvent(QHideEvent* event); + void contextMenuEvent (QContextMenuEvent* event); + void createActions(); + + static const int updateInterval = 40; + + QImage* image; ///< Double buffer image + QImage glImage; ///< The background / camera image + float yawInt; ///< The yaw integral. Used to damp the yaw indication. + QString mode; ///< The current vehicle mode + QString state; ///< The current vehicle state + QString fuelStatus; ///< Current fuel level / battery voltage + double scalingFactor; ///< Factor used to scale all absolute values to screen coordinates + float xCenterOffset, yCenterOffset; ///< Offset from center of window in mm coordinates + float vwidth; ///< Virtual width of this window, 200 mm per default. This allows to hardcode positions and aspect ratios. This virtual image plane is then scaled to the window size. + float vheight; ///< Virtual height of this window, 150 mm per default + float vGaugeSpacing; ///< Virtual spacing of the gauges from the center, 50 mm per default + float vPitchPerDeg; ///< Virtual pitch to mm conversion. Currently one degree is 3 mm up/down in the pitch markings + + int xCenter; ///< Center of the HUD instrument in pixel coordinates. Allows to off-center the whole instrument in its OpenGL window, e.g. to fit another instrument + int yCenter; ///< Center of the HUD instrument in pixel coordinates. Allows to off-center the whole instrument in its OpenGL window, e.g. to fit another instrument + + // Image buffers + unsigned char* rawBuffer1; ///< Double buffer 1 for the image + unsigned char* rawBuffer2; ///< Double buffer 2 for the image + unsigned char* rawImage; ///< Pointer to current complete image + int rawLastIndex; ///< The last byte index received of the image + int rawExpectedBytes; ///< Number of raw image bytes expected. Calculated by: image depth * channels * widht * height / 8 + int bytesPerLine; ///< Bytes per image line. Is calculated as: image depth * channels * width / 8 + bool imageStarted; ///< If an image is currently in transmission + int receivedDepth; ///< Image depth in bit for the current image + int receivedChannels; ///< Number of color channels + int receivedWidth; ///< Width in pixels of the current image + int receivedHeight; ///< Height in pixels of the current image + + // HUD colors + QColor defaultColor; ///< Color for most HUD elements, e.g. pitch lines, center cross, change rate gauges + QColor setPointColor; ///< Color for the current control set point, e.g. yaw desired + QColor warningColor; ///< Color for warning messages + QColor criticalColor; ///< Color for caution messages + QColor infoColor; ///< Color for normal/default messages + QColor fuelColor; ///< Current color for the fuel message, can be info, warning or critical color + + // Blink rates + int warningBlinkRate; ///< Blink rate of warning messages, will be rounded to the refresh rate + + QTimer* refreshTimer; ///< The main timer, controls the update rate + QPainter* hudPainter; + QFont font; ///< The HUD font, per default the free Bitstream Vera SANS, which is very close to actual HUD fonts + QFontDatabase fontDatabase;///< Font database, only used to load the TrueType font file (the HUD font is directly loaded from file rather than from the system) + bool noCamera; ///< No camera images available, draw the ground/sky box to indicate the horizon + bool hardwareAcceleration; ///< Enable hardware acceleration + + float strongStrokeWidth; ///< Strong line stroke width, used throughout the HUD + float normalStrokeWidth; ///< Normal line stroke width, used throughout the HUD + float fineStrokeWidth; ///< Fine line stroke width, used throughout the HUD + + QString waypointName; ///< Waypoint name displayed in HUD + float roll; + float pitch; + float yaw; + float rollLP; + float pitchLP; + float yawLP; + double yawDiff; + double xPos; + double yPos; + double zPos; + double xSpeed; + double ySpeed; + double zSpeed; + quint64 lastSpeedUpdate; + double totalSpeed; + double totalAcc; + double lat; + double lon; + double alt; + float load; + QString offlineDirectory; + QString nextOfflineImage; + bool hudInstrumentsEnabled; + bool videoEnabled; + float xImageFactor; + float yImageFactor; + QAction* enableHUDAction; + QAction* enableVideoAction; + QAction* selectOfflineDirectoryAction; + QAction* selectVideoChannelAction; + void paintEvent(QPaintEvent *event); + bool imageRequested; + +}; + +#endif // QGCVIDEOWIDGET_H + + diff --git a/src/ui/DebugConsole.cc b/src/ui/DebugConsole.cc index 0031b1569..6ab322d2d 100644 --- a/src/ui/DebugConsole.cc +++ b/src/ui/DebugConsole.cc @@ -80,15 +80,14 @@ DebugConsole::DebugConsole(QWidget *parent) : //lineBufferTimer.setInterval(100); // 100 Hz //lineBufferTimer.start(); + loadSettings(); + // Enable traffic measurements connect(&snapShotTimer, SIGNAL(timeout()), this, SLOT(updateTrafficMeasurements())); snapShotTimer.setInterval(snapShotInterval); snapShotTimer.start(); - -// // Set hex checkbox checked -// m_ui->hexCheckBox->setChecked(!convertToAscii); -// m_ui->mavlinkCheckBox->setChecked(filterMAVLINK); -// m_ui->holdCheckBox->setChecked(autoHold); + // Update measurements the first time + updateTrafficMeasurements(); // Get a list of all existing links links = QList(); @@ -114,17 +113,8 @@ DebugConsole::DebugConsole(QWidget *parent) : connect(m_ui->addSymbolButton, SIGNAL(clicked()), this, SLOT(appendSpecialSymbol())); // Connect Checkbox connect(m_ui->specialComboBox, SIGNAL(highlighted(QString)), this, SLOT(specialSymbolSelected(QString))); - // Set add button invisible if auto add checkbox is checked - //connect(m_ui->specialCheckBox, SIGNAL(clicked(bool)), m_ui->addSymbolButton, SLOT(setHidden(bool))); // Allow to send via return connect(m_ui->sendText, SIGNAL(returnPressed()), this, SLOT(sendBytes())); - - loadSettings(); - -// // Warn user about not activated hold -// if (!m_ui->holdCheckBox->isChecked()) { -// m_ui->receiveText->appendHtml(QString("%2\n").arg(QColor(Qt::red).name(), tr("WARNING: You have NOT enabled auto-hold (stops updating the console if huge amounts of serial data arrive). Updating the console consumes significant CPU load, so if you receive more than about 5 KB/s of serial data, make sure to enable auto-hold if not using the console."))); -// } } void DebugConsole::hideEvent(QHideEvent* event) diff --git a/src/ui/WaypointList.cc b/src/ui/WaypointList.cc index f45bd7712..e2013f5c2 100644 --- a/src/ui/WaypointList.cc +++ b/src/ui/WaypointList.cc @@ -52,7 +52,7 @@ WaypointList::WaypointList(QWidget *parent, UASInterface* uas) : m_ui->setupUi(this); listLayout = new QVBoxLayout(m_ui->listWidget); - listLayout->setSpacing(6); + listLayout->setSpacing(0); listLayout->setMargin(0); listLayout->setAlignment(Qt::AlignTop); m_ui->listWidget->setLayout(listLayout); diff --git a/src/ui/WaypointView.cc b/src/ui/WaypointView.cc index e06b66eb8..2d817cd86 100644 --- a/src/ui/WaypointView.cc +++ b/src/ui/WaypointView.cc @@ -129,6 +129,8 @@ void WaypointView::changedAutoContinue(int state) void WaypointView::updateActionView(int action) { + // Remove stretch item at index 17 (m_ui->removeSpacer) + m_ui->horizontalLayout->takeAt(17); // expose ui based on action switch(action) { @@ -140,6 +142,7 @@ void WaypointView::updateActionView(int action) m_ui->holdTimeSpinBox->hide(); m_ui->acceptanceSpinBox->hide(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 82); m_ui->takeOffAngleSpinBox->show(); break; case MAV_CMD_NAV_LAND: @@ -151,6 +154,7 @@ void WaypointView::updateActionView(int action) m_ui->holdTimeSpinBox->hide(); m_ui->acceptanceSpinBox->hide(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 26); break; case MAV_CMD_NAV_RETURN_TO_LAUNCH: m_ui->orbitSpinBox->hide(); @@ -161,6 +165,7 @@ void WaypointView::updateActionView(int action) m_ui->holdTimeSpinBox->hide(); m_ui->acceptanceSpinBox->hide(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 26); break; case MAV_CMD_NAV_WAYPOINT: m_ui->orbitSpinBox->hide(); @@ -168,6 +173,7 @@ void WaypointView::updateActionView(int action) m_ui->turnsSpinBox->hide(); m_ui->holdTimeSpinBox->show(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 1); m_ui->autoContinue->show(); m_ui->acceptanceSpinBox->show(); @@ -181,6 +187,7 @@ void WaypointView::updateActionView(int action) m_ui->holdTimeSpinBox->hide(); m_ui->acceptanceSpinBox->hide(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 25); m_ui->orbitSpinBox->show(); break; case MAV_CMD_NAV_LOITER_TURNS: @@ -190,6 +197,7 @@ void WaypointView::updateActionView(int action) m_ui->holdTimeSpinBox->hide(); m_ui->acceptanceSpinBox->hide(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 20); m_ui->orbitSpinBox->show(); m_ui->turnsSpinBox->show(); break; @@ -200,6 +208,7 @@ void WaypointView::updateActionView(int action) m_ui->autoContinue->hide(); m_ui->acceptanceSpinBox->hide(); m_ui->customActionWidget->hide(); + m_ui->horizontalLayout->insertStretch(17, 20); m_ui->orbitSpinBox->show(); m_ui->holdTimeSpinBox->show(); break; @@ -527,6 +536,38 @@ void WaypointView::updateValues() } wp->blockSignals(false); + + QColor backGroundColor = QGC::colorBackground; + + static int lastId = -1; + int currId = wp->getId() % 2; + + if (currId != lastId) + { + + qDebug() << "COLOR ID: " << currId; + if (currId == 1) + { + //backGroundColor = backGroundColor.lighter(150); + backGroundColor = QColor("#252528").lighter(150); + } + else + { + backGroundColor = QColor("#252528").lighter(250); + } + qDebug() << "COLOR:" << backGroundColor.name(); + + // Update color based on id + QString groupBoxStyle = QString("QGroupBox {padding: 0px; margin: 0px; border: 0px; background-color: %1; }").arg(backGroundColor.name()); + QString labelStyle = QString("QWidget {background-color: %1; color: #DDDDDF; border-color: #EEEEEE; }").arg(backGroundColor.name()); + QString checkBoxStyle = QString("QCheckBox {background-color: %1; color: #454545; border-color: #EEEEEE; }").arg(backGroundColor.name()); + + m_ui->autoContinue->setStyleSheet(checkBoxStyle); + m_ui->selectedBox->setStyleSheet(checkBoxStyle); + m_ui->idLabel->setStyleSheet(labelStyle); + m_ui->groupBox->setStyleSheet(groupBoxStyle); + lastId = currId; + } } void WaypointView::setCurrent(bool state) diff --git a/src/ui/WaypointView.ui b/src/ui/WaypointView.ui index 6939a62a0..7e19b7f4a 100644 --- a/src/ui/WaypointView.ui +++ b/src/ui/WaypointView.ui @@ -26,13 +26,12 @@ Form - QWidget#colorIcon {} + QWidget#colorIcon {} QWidget { background-color: #252528; color: #DDDDDF; -border-color: #EEEEEE; -background-clip: border; +border-color: #CCCCCF; } QCheckBox { @@ -56,10 +55,6 @@ margin-top: 1ex; /* leave space at the top for the title */ font: bold 8px; } -QGroupBox#heartbeatIcon { - background-color: red; -} - QDockWidget { font: bold; border: 1px solid #32345E; @@ -81,67 +76,7 @@ QPushButton { QPushButton:pressed { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #444444, stop: 1 #555555); } - -QPushButton#landButton { - color: #000000; - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #ffee01, stop:1 #ae8f00) url("ICONDIR/control/emergency-button.png"); -} - -QPushButton:pressed#landButton { - color: #000000; - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #bbaa00, stop:1 #a05b00) url("ICONDIR/control/emergency-button.png"); -} - -QPushButton#killButton { - color: #000000; - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #ffb917, stop:1 #b37300) url("ICONDIR/control/emergency-button.png"); -} - -QPushButton:pressed#killButton { - color: #000000; - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #bb8500, stop:1 #903000) url("ICONDIR/control/emergency-button.png"); -} - -QProgressBar { - border: 1px solid white; - border-radius: 4px; - text-align: center; - padding: 2px; - color: white; - background-color: #111111; -} - -QProgressBar:horizontal { - height: 12px; -} - -QProgressBar QLabel { - font-size: 8px; -} - -QProgressBar:vertical { - width: 12px; -} - -QProgressBar::chunk { - background-color: #656565; -} - -QProgressBar::chunk#batteryBar { - background-color: green; -} - -QProgressBar::chunk#speedBar { - background-color: yellow; -} - -QProgressBar::chunk#thrustBar { - background-color: orange; -} + @@ -161,12 +96,12 @@ QProgressBar::chunk#thrustBar { - + 2 - 2 + 4 @@ -622,6 +557,19 @@ Time to stay at this position before advancing + + + + Qt::Horizontal + + + + 5 + 20 + + + + -- 2.22.0