HUD.cc 54 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2
/*=====================================================================

3
QGroundControl Open Source Ground Control Station
pixhawk's avatar
pixhawk committed
4

5
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
pixhawk's avatar
pixhawk committed
6

7
This file is part of the QGROUNDCONTROL project
pixhawk's avatar
pixhawk committed
8

9
    QGROUNDCONTROL is free software: you can redistribute it and/or modify
pixhawk's avatar
pixhawk committed
10 11 12 13
    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.

14
    QGROUNDCONTROL is distributed in the hope that it will be useful,
pixhawk's avatar
pixhawk committed
15 16 17 18 19
    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
20
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
pixhawk's avatar
pixhawk committed
21 22 23 24 25 26 27 28 29 30 31

======================================================================*/

/**
 * @file
 *   @brief Head Up Display (HUD)
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

32
#include <QShowEvent>
33 34 35 36
#include <QContextMenuEvent>
#include <QMenu>
#include <QDesktopServices>
#include <QFileDialog>
37

pixhawk's avatar
pixhawk committed
38 39
#include <QDebug>
#include <cmath>
pixhawk's avatar
pixhawk committed
40
#include <qmath.h>
pixhawk's avatar
pixhawk committed
41
#include <limits>
pixhawk's avatar
pixhawk committed
42 43

#include "UASManager.h"
44
#include "UAS.h"
pixhawk's avatar
pixhawk committed
45 46
#include "HUD.h"
#include "MG.h"
47
#include "QGC.h"
48
#include "MainWindow.h"
pixhawk's avatar
pixhawk committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

// Fix for some platforms, e.g. windows
#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif

/**
 * @warning The HUD widget will not start painting its content automatically
 *          to update the view, start the auto-update by calling HUD::start().
 *
 * @param width
 * @param height
 * @param parent
 */
HUD::HUD(int width, int height, QWidget* parent)
64
    : QGLWidget(QGLFormat(QGL::SampleBuffers), parent),
65 66 67 68 69 70 71 72 73
      uas(NULL),
      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),
74
      vGaugeSpacing(65.0f),
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
      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),
      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),
120
      yImageFactor(1.0),
121 122
      imageLoggingEnabled(false),
      imageRequested(false)
pixhawk's avatar
pixhawk committed
123 124 125 126
{
    // Set auto fill to false
    setAutoFillBackground(false);

127 128
    // Set minimum size
    setMinimumSize(80, 60);
129 130
    // Set preferred size
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
LM's avatar
LM committed
131
    scalingFactor = this->width()/vwidth;
132

133
    // Generate a background image that's dependent on the current color scheme.
pixhawk's avatar
pixhawk committed
134
    QImage fill = QImage(width, height, QImage::Format_Indexed8);
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    if (((MainWindow*)parent)->getStyle() == MainWindow::QGC_MAINWINDOW_STYLE_LIGHT)
    {
        fill.fill(255);
    }
    else
    {
        fill.fill(0);
    }
    glImage = QGLWidget::convertToGLFormat(fill);

    // Now set the other default colors based on the current color scheme.
    if (((MainWindow*)parent)->getStyle() == MainWindow::QGC_MAINWINDOW_STYLE_LIGHT)
    {
        defaultColor = QColor(0x01, 0x47, 0x01);
        setPointColor = QColor(0x82, 0x17, 0x82);
        warningColor = Qt::darkYellow;
        criticalColor = Qt::darkRed;
        infoColor = QColor(0x07, 0x82, 0x07);
        fuelColor = criticalColor;
    }
    else
    {
        defaultColor = QColor(70, 200, 70);
        setPointColor = QColor(200, 20, 200);
        warningColor = Qt::yellow;
        criticalColor = Qt::red;
        infoColor = QColor(20, 200, 20);
        fuelColor = criticalColor;
    }
pixhawk's avatar
pixhawk committed
164

165 166 167
    //QString imagePath = "/Users/user/Desktop/frame0000.png";
    //qDebug() << __FILE__ << __LINE__ << "template image:" << imagePath;
    //fill = QImage(imagePath);
pixhawk's avatar
pixhawk committed
168 169 170


    // Refresh timer
171
    refreshTimer->setInterval(updateInterval);
172
    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
pixhawk's avatar
pixhawk committed
173 174

    // Resize to correct size and fill with image
pixhawk's avatar
pixhawk committed
175
    resize(this->width(), this->height());
176
    //glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
pixhawk's avatar
pixhawk committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190

    // 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);
191 192
    font = fontDatabase.font(fontFamilyName, "Roman", qMax(5,(int)(10.0f*scalingFactor*1.2f+0.5f)));
    QFont* fontPtr = &font;
193
    if (!fontPtr) {
194
        qDebug() << "ERROR! FONT NOT LOADED!";
195
    } else {
196 197
        if (font.family() != fontFamilyName) qDebug() << "ERROR! WRONG FONT LOADED: " << fontFamilyName;
    }
pixhawk's avatar
pixhawk committed
198 199

    // Connect with UAS
200 201
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));

202 203 204
    createActions();

    if (UASManager::instance()->getActiveUAS() != NULL) setActiveUAS(UASManager::instance()->getActiveUAS());
pixhawk's avatar
pixhawk committed
205 206 207 208
}

HUD::~HUD()
{
209
    refreshTimer->stop();
pixhawk's avatar
pixhawk committed
210 211
}

212 213
QSize HUD::sizeHint() const
{
214
    return QSize(width(), (width()*3.0f)/4);
215 216
}

217 218
void HUD::showEvent(QShowEvent* event)
{
219 220
    // React only to internal (pre-display)
    // events
221
    QGLWidget::showEvent(event);
222
    refreshTimer->start(updateInterval);
LM's avatar
LM committed
223
    emit visibilityChanged(true);
224 225 226 227 228 229
}

void HUD::hideEvent(QHideEvent* event)
{
    // React only to internal (pre-display)
    // events
230
    refreshTimer->stop();
LM's avatar
LM committed
231 232
    QGLWidget::hideEvent(event);
    emit visibilityChanged(false);
pixhawk's avatar
pixhawk committed
233 234
}

235 236 237
void HUD::contextMenuEvent (QContextMenuEvent* event)
{
    QMenu menu(this);
238 239 240 241
    // Update actions
    enableHUDAction->setChecked(hudInstrumentsEnabled);
    enableVideoAction->setChecked(videoEnabled);

242 243 244 245
    menu.addAction(enableHUDAction);
    //menu.addAction(selectHUDColorAction);
    menu.addAction(enableVideoAction);
    menu.addAction(selectOfflineDirectoryAction);
246
    menu.addAction(selectSaveDirectoryAction);
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    menu.exec(event->globalPos());
}

void HUD::createActions()
{
    enableHUDAction = new QAction(tr("Enable HUD"), this);
    enableHUDAction->setStatusTip(tr("Show the HUD 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)));

264
    selectOfflineDirectoryAction = new QAction(tr("Load image log"), this);
265 266
    selectOfflineDirectoryAction->setStatusTip(tr("Load previously logged images into simulation / replay"));
    connect(selectOfflineDirectoryAction, SIGNAL(triggered()), this, SLOT(selectOfflineDirectory()));
267 268 269 270 271

    selectSaveDirectoryAction = new QAction(tr("Save images to directory"), this);
    selectSaveDirectoryAction->setStatusTip(tr("Save images from image stream to a directory"));
    selectSaveDirectoryAction->setCheckable(true);
    connect(selectSaveDirectoryAction, SIGNAL(triggered(bool)), this, SLOT(saveImages(bool)));
272 273
}

pixhawk's avatar
pixhawk committed
274 275 276 277 278 279
/**
 *
 * @param uas the UAS/MAV to monitor/display with the HUD
 */
void HUD::setActiveUAS(UASInterface* uas)
{
280
    if (this->uas != NULL) {
pixhawk's avatar
pixhawk committed
281
        // Disconnect any previously connected active MAV
282
        disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
283
        disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
284 285 286 287
        disconnect(this->uas, SIGNAL(batteryChanged(UASInterface*, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, int)));
        disconnect(this->uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
        disconnect(this->uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
        disconnect(this->uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
288

289
        disconnect(this->uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
pixhawk's avatar
pixhawk committed
290
        disconnect(this->uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
291 292
        disconnect(this->uas, SIGNAL(speedChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
        disconnect(this->uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
pixhawk's avatar
pixhawk committed
293

294
        // Try to disconnect the image link
295
        UAS* u = dynamic_cast<UAS*>(this->uas);
296
        if (u) {
297
            disconnect(u, SIGNAL(imageStarted(quint64)), this, SLOT(startImage(quint64)));
LM's avatar
LM committed
298
            disconnect(u, SIGNAL(imageReady(UASInterface*)), this, SLOT(copyImage()));
299 300
        }
    }
301

302
    if (uas) {
303 304 305
        // Now connect the new UAS
        // Setup communication
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
306
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
307 308 309 310 311 312
        connect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, int)));
        connect(uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
        connect(uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
        connect(uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));

        connect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
pixhawk's avatar
pixhawk committed
313
        connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
314 315 316 317 318
        connect(uas, SIGNAL(speedChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateSpeed(UASInterface*,double,double,double,quint64)));
        connect(uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));

        // Try to connect the image link
        UAS* u = dynamic_cast<UAS*>(uas);
319
        if (u) {
320
            connect(u, SIGNAL(imageStarted(quint64)), this, SLOT(startImage(quint64)));
LM's avatar
LM committed
321
            connect(u, SIGNAL(imageReady(UASInterface*)), this, SLOT(copyImage()));
322
        }
323

324 325 326
        // Set new UAS
        this->uas = uas;
    }
pixhawk's avatar
pixhawk committed
327 328
}

329 330 331 332 333 334 335
//void HUD::updateAttitudeThrustSetPoint(UASInterface* uas, double rollDesired, double pitchDesired, double yawDesired, double thrustDesired, quint64 msec)
//{
////    updateValue(uas, "roll desired", rollDesired, msec);
////    updateValue(uas, "pitch desired", pitchDesired, msec);
////    updateValue(uas, "yaw desired", yawDesired, msec);
////    updateValue(uas, "thrust desired", thrustDesired, msec);
//}
pixhawk's avatar
pixhawk committed
336 337 338

void HUD::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
{
339 340
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
341 342 343 344 345 346
    if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
    {
        this->roll = roll;
        this->pitch = pitch*3.35f; // Constant here is the 'focal length' of the projection onto the plane
        this->yaw = yaw;
    }
pixhawk's avatar
pixhawk committed
347 348
}

349 350 351 352
void HUD::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
353 354 355 356
    if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
    {
        attitudes.insert(component, QVector3D(roll, pitch*3.35f, yaw)); // Constant here is the 'focal length' of the projection onto the plane
    }
357 358
}

pixhawk's avatar
pixhawk committed
359 360
void HUD::updateBattery(UASInterface* uas, double voltage, double percent, int seconds)
{
361
    Q_UNUSED(uas);
362
    Q_UNUSED(seconds);
363
    fuelStatus = tr("BAT [%1% | %2V]").arg(percent, 2, 'f', 0, QChar('0')).arg(voltage, 4, 'f', 1, QChar('0'));
364
    if (percent < 20.0f) {
pixhawk's avatar
pixhawk committed
365
        fuelColor = warningColor;
366
    } else if (percent < 10.0f) {
pixhawk's avatar
pixhawk committed
367
        fuelColor = criticalColor;
368
    } else {
pixhawk's avatar
pixhawk committed
369 370 371 372 373 374 375 376
        fuelColor = infoColor;
    }
}

void HUD::receiveHeartbeat(UASInterface*)
{
}

377
void HUD::updateThrust(UASInterface* uas, double thrust)
pixhawk's avatar
pixhawk committed
378
{
379 380 381
    Q_UNUSED(uas);
    Q_UNUSED(thrust);
//    updateValue(uas, "thrust", thrust, MG::TIME::getGroundTimeNow());
pixhawk's avatar
pixhawk committed
382 383 384 385
}

void HUD::updateLocalPosition(UASInterface* uas,double x,double y,double z,quint64 timestamp)
{
386 387 388 389 390
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    this->xPos = x;
    this->yPos = y;
    this->zPos = z;
pixhawk's avatar
pixhawk committed
391 392
}

393
void HUD::updateGlobalPosition(UASInterface* uas,double lat, double lon, double altitude, quint64 timestamp)
pixhawk's avatar
pixhawk committed
394
{
395 396 397 398 399
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    this->lat = lat;
    this->lon = lon;
    this->alt = altitude;
pixhawk's avatar
pixhawk committed
400 401 402 403
}

void HUD::updateSpeed(UASInterface* uas,double x,double y,double z,quint64 timestamp)
{
404 405 406 407 408
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    this->xSpeed = x;
    this->ySpeed = y;
    this->zSpeed = z;
pixhawk's avatar
pixhawk committed
409 410 411
    double newTotalSpeed = sqrt(xSpeed*xSpeed + ySpeed*ySpeed + zSpeed*zSpeed);
    totalAcc = (newTotalSpeed - totalSpeed) / ((double)(lastSpeedUpdate - timestamp)/1000.0);
    totalSpeed = newTotalSpeed;
pixhawk's avatar
pixhawk committed
412 413 414 415 416 417 418 419
}

/**
 * Updates the current system state, but only if the uas matches the currently monitored uas.
 *
 * @param uas the system the state message originates from
 * @param state short state text, displayed in HUD
 */
420
void HUD::updateState(UASInterface* uas,QString state)
pixhawk's avatar
pixhawk committed
421
{
422 423
    // Only one UAS is connected at a time
    Q_UNUSED(uas);
424 425 426 427 428 429 430 431 432
    this->state = state;
}

/**
 * Updates the current system mode, but only if the uas matches the currently monitored uas.
 *
 * @param uas the system the state message originates from
 * @param mode short mode text, displayed in HUD
 */
433
void HUD::updateMode(int id,QString mode, QString description)
434
{
435
    // Only one UAS is connected at a time
436 437
    Q_UNUSED(id);
    Q_UNUSED(description);
438
    this->mode = mode;
pixhawk's avatar
pixhawk committed
439 440 441 442
}

void HUD::updateLoad(UASInterface* uas, double load)
{
443 444 445
    Q_UNUSED(uas);
    this->load = load;
    //updateValue(uas, "load", load, MG::TIME::getGroundTimeNow());
pixhawk's avatar
pixhawk committed
446 447 448 449 450 451 452 453
}

/**
 * @param y coordinate in pixels to be converted to reference mm units
 * @return the screen coordinate relative to the QGLWindow origin
 */
float HUD::refToScreenX(float x)
{
454
    //qDebug() << "sX: " << (scalingFactor * x) << "Orig:" << x;
pixhawk's avatar
pixhawk committed
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
    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 HUD::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 HUD::paintCenterBackground(float roll, float pitch, float yaw)
{
474 475
    Q_UNUSED(yaw);

pixhawk's avatar
pixhawk committed
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    // Center indicator is 100 mm wide
    float referenceWidth = 70.0;
    float referenceHeight = 70.0;

    // HUD 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);
498 499

    // Move based on the yaw difference
500
    //glTranslatef(yaw, 0.0f, 0.0f);
501

pixhawk's avatar
pixhawk committed
502
    // Rotate based on the bank
503
    glRotatef((roll/M_PI)*180.0f, 0.0f, 0.0f, 1.0f);
pixhawk's avatar
pixhawk committed
504 505 506

    // Translate in the direction of the rotation based
    // on the pitch. On the 777, a pitch of 1 degree = 2 mm
507
    //glTranslatef(0, ((-pitch/M_PI)*180.0f * vPitchPerDeg), 0);
508
    glTranslatef(0.0f, (-pitch * vPitchPerDeg * 16.5f), 0.0f);
pixhawk's avatar
pixhawk committed
509 510 511 512 513

    // Ground
    glColor3ub(179,102,0);

    glBegin(GL_POLYGON);
514
    glVertex2f(-300,-900);
pixhawk's avatar
pixhawk committed
515 516
    glVertex2f(-300,0);
    glVertex2f(300,0);
517 518
    glVertex2f(300,-900);
    glVertex2f(-300,-900);
pixhawk's avatar
pixhawk committed
519 520 521 522 523 524 525
    glEnd();

    // Sky
    glColor3ub(0,153,204);

    glBegin(GL_POLYGON);
    glVertex2f(-300,0);
526 527
    glVertex2f(-300,900);
    glVertex2f(300,900);
pixhawk's avatar
pixhawk committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    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 HUD::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);

549 550
    QFont font("Bitstream Vera Sans");
    // Enforce minimum font size of 5 pixels
551
    int fSize = qMax(5, (int)(fontSize*scalingFactor*1.26f));
552
    font.setPixelSize(fSize);
pixhawk's avatar
pixhawk committed
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

    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 HUD::initializeGL()
{
    bool antialiasing = true;

    // Antialiasing setup
572
    if(antialiasing) {
pixhawk's avatar
pixhawk committed
573
        glEnable(GL_MULTISAMPLE);
pixhawk's avatar
pixhawk committed
574 575 576 577 578 579 580 581 582
        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);
583
    } else {
584 585 586
        glDisable(GL_BLEND);
        glDisable(GL_POINT_SMOOTH);
        glDisable(GL_LINE_SMOOTH);
pixhawk's avatar
pixhawk committed
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
    }
}

/**
 * @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 HUD::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 HUD::paintRollPitchStrips()
{
}


pixhawk's avatar
pixhawk committed
627
void HUD::paintEvent(QPaintEvent *event)
pixhawk's avatar
pixhawk committed
628
{
629
    // Event is not needed
630 631
    // the event is ignored as this widget
    // is refreshed automatically
632
    Q_UNUSED(event);
633 634 635 636
}

void HUD::paintHUD()
{
637
    if (isVisible()) {
638 639 640
        //    static quint64 interval = 0;
        //    qDebug() << "INTERVAL:" << MG::TIME::getGroundTimeNow() - interval << __FILE__ << __LINE__;
        //    interval = MG::TIME::getGroundTimeNow();
lm's avatar
lm committed
641

642
#if (QGC_EVENTLOOP_DEBUG)
643
        qDebug() << "EVENTLOOP:" << __FILE__ << __LINE__;
644 645
#endif

646 647
        // Read out most important values to limit hash table lookups
        // Low-pass roll, pitch and yaw
648 649
        rollLP = roll;//rollLP * 0.2f + 0.8f * roll;
        pitchLP = pitch;//pitchLP * 0.2f + 0.8f * pitch;
650
        yawLP = (!isinf(yaw) && !isnan(yaw)) ? yaw : yawLP;//yawLP * 0.2f + 0.8f * yaw;
pixhawk's avatar
pixhawk committed
651

652 653
        // Translate for yaw
        const float maxYawTrans = 60.0f;
654

655 656 657
        float newYawDiff = yawDiff;
        if (isinf(newYawDiff)) newYawDiff = yawDiff;
        if (newYawDiff > M_PI) newYawDiff = newYawDiff - M_PI;
658

659
        if (newYawDiff < -M_PI) newYawDiff = newYawDiff + M_PI;
660

661
        newYawDiff = yawDiff * 0.8 + newYawDiff * 0.2;
662

663
        yawDiff = newYawDiff;
664

665
        yawInt += newYawDiff;
666

667 668
        if (yawInt > M_PI) yawInt = (float)M_PI;
        if (yawInt < -M_PI) yawInt = (float)-M_PI;
669

670
        float yawTrans = yawInt * (float)maxYawTrans;
671
        yawInt *= 0.6f;
lm's avatar
lm committed
672

673
        if ((yawTrans < 5.0) && (yawTrans > -5.0)) yawTrans = 0;
lm's avatar
lm committed
674

675 676
        // Negate to correct direction
        yawTrans = -yawTrans;
lm's avatar
lm committed
677

678 679
        yawTrans = 0;

680
        //qDebug() << "yaw translation" << yawTrans << "integral" << yawInt << "difference" << yawDiff << "yaw" << yaw;
681

682 683 684 685 686
        // 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;
pixhawk's avatar
pixhawk committed
687 688 689



690 691 692 693 694 695 696 697
        // 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
698 699
        if (videoEnabled) {
            if (nextOfflineImage != "" && QFileInfo(nextOfflineImage).exists()) {
700 701
                qDebug() << __FILE__ << __LINE__ << "template image:" << nextOfflineImage;
                QImage fill = QImage(nextOfflineImage);
702

703
                glImage = QGLWidget::convertToGLFormat(fill);
704

705 706 707
                // Reset to save load efforts
                nextOfflineImage = "";
            }
708

709 710 711 712
        }

        if (dataStreamEnabled || videoEnabled)
        {
pixhawk's avatar
pixhawk committed
713 714
            glRasterPos2i(0, 0);

LM's avatar
LM committed
715 716 717 718
            xImageFactor = width() / (float)glImage.width();
            yImageFactor = height() / (float)glImage.height();
            float imageFactor = qMin(xImageFactor, yImageFactor);
            glPixelZoom(imageFactor, imageFactor);
719 720
            // Resize to correct size and fill with image
            glDrawPixels(glImage.width(), glImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
LM's avatar
LM committed
721
            //qDebug() << "DRAWING GL IMAGE";
722
        } else {
723 724
            // Blue / brown background
            paintCenterBackground(roll, pitch, yawTrans);
725 726 727 728 729 730 731
        }

        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();

        // END OF OPENGL PAINTING

732
        if (hudInstrumentsEnabled) {
733

734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
            //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
            // BATTERY
749
            paintText(fuelStatus, fuelColor, 6.0f, (-vwidth/2.0) + 10, -vheight/2.0 + 6, &painter);
750
            // Waypoint
751 752 753 754 755 756 757
            paintText(waypointName, defaultColor, 6.0f, (-vwidth/3.0) + 10, +vheight/3.0 + 15, &painter);

            QPen linePen(Qt::SolidLine);
            linePen.setWidth(refLineWidthToPen(1.0f));
            linePen.setColor(defaultColor);
            painter.setBrush(Qt::NoBrush);
            painter.setPen(linePen);
758

759 760 761 762 763 764
            // YAW INDICATOR
            //
            //      .
            //    .   .
            //   .......
            //
765 766
            const float yawIndicatorWidth = 12.0f;
            const float yawIndicatorY = vheight/2.0f - 15.0f;
767 768 769 770 771 772
            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.drawPolyline(yawIndicator);
773
            painter.setPen(linePen);
pixhawk's avatar
pixhawk committed
774

775
            // CENTER
pixhawk's avatar
pixhawk committed
776

777 778 779 780 781
            // HEADING INDICATOR
            //
            //    __      __
            //       \/\/
            //
782
            const float hIndicatorWidth = 20.0f;
783 784 785 786 787 788 789 790 791 792 793 794
            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.drawPolyline(hIndicator);
pixhawk's avatar
pixhawk committed
795 796


797
            // SETPOINT
798
            const float centerWidth = 8.0f;
799 800
            // 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));
pixhawk's avatar
pixhawk committed
801

802
            const float centerCrossWidth = 20.0f;
803 804 805 806 807 808
            // 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)));
pixhawk's avatar
pixhawk committed
809

810

pixhawk's avatar
pixhawk committed
811

812
            // COMPASS
813 814
            const float compassY = -vheight/2.0f + 6.0f;
            QRectF compassRect(QPointF(refToScreenX(-12.0f), refToScreenY(compassY)), QSizeF(refToScreenX(24.0f), refToScreenY(12.0f)));
815
            painter.setBrush(Qt::NoBrush);
816 817
            painter.setPen(linePen);
            painter.drawRoundedRect(compassRect, 3, 3);
818
            QString yawAngle;
pixhawk's avatar
pixhawk committed
819

820
            //    const float yawDeg = ((values.value("yaw", 0.0f)/M_PI)*180.0f)+180.f;
pixhawk's avatar
pixhawk committed
821

822
            // YAW is in compass-human readable format, so 0 .. 360 deg.
barthess's avatar
barthess committed
823
            float yawDeg = (yawLP / M_PI) * 180.0f;
824 825 826
            if (yawDeg < 0) yawDeg += 360;
            if (yawDeg > 360) yawDeg -= 360;
            /* final safeguard for really stupid systems */
827 828
            int yawCompass = static_cast<int>(yawDeg) % 360;
            yawAngle.sprintf("%03d", yawCompass);
829 830 831 832
            paintText(yawAngle, defaultColor,8.5f, -9.8f, compassY+ 1.7f, &painter);

            painter.setBrush(Qt::NoBrush);
            painter.setPen(linePen);
pixhawk's avatar
pixhawk committed
833

834
            // CHANGE RATE STRIPS
835
            drawChangeRateStrip(-95.0f, -60.0f, 40.0f, -10.0f, 10.0f, -zSpeed, &painter);
pixhawk's avatar
pixhawk committed
836

837
            // CHANGE RATE STRIPS
838
            drawChangeRateStrip(95.0f, -60.0f, 40.0f, -10.0f, 10.0f, totalAcc, &painter,true);
839 840

            // GAUGES
pixhawk's avatar
pixhawk committed
841

842
            // Left altitude gauge
pixhawk's avatar
pixhawk committed
843 844
            float gaugeAltitude;

845
            if (this->alt != 0) {
pixhawk's avatar
pixhawk committed
846
                gaugeAltitude = alt;
847
            } else {
pixhawk's avatar
pixhawk committed
848 849 850
                gaugeAltitude = -zPos;
            }

851 852 853
            painter.setBrush(Qt::NoBrush);
            painter.setPen(linePen);

854
            drawChangeIndicatorGauge(-vGaugeSpacing, 35.0f, 15.0f, 10.0f, gaugeAltitude, defaultColor, &painter, false);
855
            paintText("alt m", defaultColor, 5.5f, -73.0f, 50, &painter);
856

857
            // Right speed gauge
858
            drawChangeIndicatorGauge(vGaugeSpacing, 35.0f, 15.0f, 10.0f, totalSpeed, defaultColor, &painter, false);
859
            paintText("v m/s", defaultColor, 5.5f, 55.0f, 50, &painter);
pixhawk's avatar
pixhawk committed
860 861


862 863 864 865 866 867 868 869
            // 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);

870 871 872 873 874 875 876
            // Old single-component pitch drawing
//            // 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);
877

878
//            // PITCH
879

880
//            paintPitchLines(pitchLP, &painter);
881

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
            QColor attColor = painter.pen().color();

            // Draw multi-component attitude
            foreach (QVector3D att, attitudes.values())
            {
                attColor = attColor.darker(200);
                painter.setPen(attColor);
                // Rotate view and draw all roll-dependent indicators
                painter.rotate((att.x()/M_PI)* -180.0f);

                painter.translate(0, (-att.y()/(float)M_PI)* -180.0f * refToScreenY(1.8f));

                //qDebug() << "ROLL" << roll << "PITCH" << pitch << "YAW DIFF" << valuesDot.value("roll", 0.0f);

                // PITCH

                paintPitchLines(att.y(), &painter);
899 900
                painter.translate(0, -(-att.y()/(float)M_PI)* -180.0f * refToScreenY(1.8f));
                painter.rotate(-(att.x()/M_PI)* -180.0f);
901
            }
902

903
            painter.end();
904
        } else {
905 906 907 908
            QPainter painter;
            painter.begin(this);
            painter.end();
        }
909 910
        //glDisable(GL_MULTISAMPLE);

911 912


913 914
        //glFlush();
    }
pixhawk's avatar
pixhawk committed
915 916
}

pixhawk's avatar
pixhawk committed
917 918 919 920 921 922 923 924 925 926 927 928

/**
 * @param pitch pitch angle in degrees (-180 to 180)
 */
void HUD::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;
929
    const float posLimit = sqrt(pow(vwidth, 2.0f) + pow(vheight, 2.0f))*3.0f;
pixhawk's avatar
pixhawk committed
930 931 932 933 934 935

    const float offsetAbs = pitch * yDeg;

    float offset = pitch;
    if (offset < 0) offset = -offset;
    int offsetCount = 0;
936 937 938 939
    while (offset > lineDistance) {
        offset -= lineDistance;
        offsetCount++;
    }
pixhawk's avatar
pixhawk committed
940 941 942 943 944 945 946 947 948 949 950

    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;

951
    while (posY < posLimit) {
pixhawk's avatar
pixhawk committed
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
        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;


981
    while (posY < posLimit) {
pixhawk's avatar
pixhawk committed
982 983 984 985 986 987 988 989 990 991 992 993 994
        paintPitchLineNeg(label.sprintf("%3d", iNeg), 0.0f, posY, painter);
        posY += posIncrement;
        iNeg -= (int)lineDistance;
    }
}

void HUD::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;
995 996
    const float textSize = pitchHeight * 1.6f;
    const float lineWidth = 1.5f;
pixhawk's avatar
pixhawk committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008

    // 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
1009
    paintText(text, defaultColor, textSize, refPosX-pitchWidth/2.0 + 0.75f, refPosY + pitchHeight - 1.3f, painter);
pixhawk's avatar
pixhawk committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

    // 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 HUD::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;
1022
    const float textSize = pitchHeight * 1.6f;
pixhawk's avatar
pixhawk committed
1023 1024
    const float segmentWidth = ((pitchWidth - pitchGap)/2.0f) / 7.0f; ///< Four lines and three gaps -> 7 segments

1025
    const float lineWidth = 1.5f;
pixhawk's avatar
pixhawk committed
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036

    // 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
1037
    for (int i = 0; i < 7; i+=2) {
pixhawk's avatar
pixhawk committed
1038 1039 1040
        drawLine(refPosX-pitchWidth/2.0+(i*segmentWidth), refPosY, refPosX-pitchWidth/2.0+(i*segmentWidth)+segmentWidth, refPosY, lineWidth, defaultColor, painter);
    }
    // Text left
1041
    paintText(text, defaultColor, textSize, refPosX-pitchWidth/2.0f + 0.75f, refPosY + pitchHeight - 1.3f, painter);
pixhawk's avatar
pixhawk committed
1042 1043 1044 1045

    // Right vertical line
    drawLine(refPosX+pitchGap/2.0, refPosY, refPosX+pitchGap/2.0, refPosY-pitchHeight, lineWidth, defaultColor, painter);
    // Right horizontal line with four segments
1046
    for (int i = 0; i < 7; i+=2) {
pixhawk's avatar
pixhawk committed
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
        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 HUD::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 HUD::rotatePolygonClockWiseRad(QPolygonF& p, float angle, QPointF origin)
{
    // Standard 2x2 rotation matrix, counter-clockwise
    //
    //   |  cos(phi)   sin(phi) |
    //   | -sin(phi)   cos(phi) |
    //
1087
    for (int i = 0; i < p.size(); i++) {
pixhawk's avatar
pixhawk committed
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
        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 HUD::drawPolygon(QPolygonF refPolygon, QPainter* painter)
{
    // Scale coordinates
    QPolygonF draw(refPolygon.size());
1103
    for (int i = 0; i < refPolygon.size(); i++) {
pixhawk's avatar
pixhawk committed
1104 1105 1106 1107 1108 1109 1110 1111
        QPointF curr;
        curr.setX(refToScreenX(refPolygon.at(i).x()));
        curr.setY(refToScreenY(refPolygon.at(i).y()));
        draw.replace(i, curr);
    }
    painter->drawPolygon(draw);
}

1112
void HUD::drawChangeRateStrip(float xRef, float yRef, float height, float minRate, float maxRate, float value, QPainter* painter,bool reverse)
pixhawk's avatar
pixhawk committed
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
{
    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;
1132
    const float lineWidth = 1.5f;
pixhawk's avatar
pixhawk committed
1133 1134 1135

    // Indicator lines
    // Top horizontal line
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
    if (reverse)
    {
        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);

        QFont font("Bitstream Vera Sans");
        // Enforce minimum font size of 5 pixels
        //int fSize = qMax(5, (int)(6.0f*scalingFactor*1.26f));
        font.setPixelSize(6.0f * 1.26f);

        QFontMetrics metrics = QFontMetrics(font);
        paintText(label, defaultColor, 6.0f, (xRef-width) - metrics.width(label), yRef+height-((scaledValue - minRate)/(maxRate-minRate))*height - 1.6f, painter);
    }
    else
    {
        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, 6.0f, xRef+width/2.0f, yRef+height-((scaledValue - minRate)/(maxRate-minRate))*height - 1.6f, painter);
    }
pixhawk's avatar
pixhawk committed
1173 1174
}

1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
//void HUD::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<QString, float> 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);
//    }
//}
pixhawk's avatar
pixhawk committed
1251 1252 1253 1254 1255 1256 1257

void HUD::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.setColor(defaultColor);
1258
    circlePen.setWidth(refLineWidthToPen(2.0f));
pixhawk's avatar
pixhawk committed
1259 1260
    painter->setBrush(Qt::NoBrush);
    painter->setPen(circlePen);
1261
    drawCircle(xRef, yRef, radius, 200.0f, 170.0f, 1.5f, color, painter);
pixhawk's avatar
pixhawk committed
1262 1263 1264 1265

    QString label;
    label.sprintf("%05.1f", value);

1266 1267
    float textSize = radius / 2.5;

pixhawk's avatar
pixhawk committed
1268
    // Draw the value
1269
    paintText(label, color, textSize, xRef-textSize*1.7f, yRef-textSize*0.4f, painter);
pixhawk's avatar
pixhawk committed
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308

    // 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 HUD::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 HUD::drawEllipse(float refX, float refY, float radiusX, float radiusY, float startDeg, float endDeg, float lineWidth, const QColor& color, QPainter* painter)
{
lm's avatar
lm committed
1309 1310
    Q_UNUSED(startDeg);
    Q_UNUSED(endDeg);
pixhawk's avatar
pixhawk committed
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
    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 HUD::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 HUD::resizeGL(int w, int h)
{
Lorenz Meier's avatar
Lorenz Meier committed
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
    if (isVisible()) {
        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();
    }
pixhawk's avatar
pixhawk committed
1335 1336
}

1337
void HUD::selectWaypoint(int uasId, int id)
pixhawk's avatar
pixhawk committed
1338
{
1339 1340
    Q_UNUSED(uasId);
    waypointName = tr("WP") + QString::number(id);
pixhawk's avatar
pixhawk committed
1341 1342 1343 1344 1345
}

void HUD::setImageSize(int width, int height, int depth, int channels)
{
    // Allocate raw image in correct size
1346
    if (width != receivedWidth || height != receivedHeight || depth != receivedDepth || channels != receivedChannels || image == NULL) {
pixhawk's avatar
pixhawk committed
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
        // Set new size
        if (width > 0) receivedWidth  = width;
        if (height > 0) receivedHeight = height;
        if (depth > 1) receivedDepth = depth;
        if (channels > 1) receivedChannels = channels;

        rawExpectedBytes = (receivedWidth * receivedHeight * receivedDepth * receivedChannels) / 8;
        bytesPerLine = rawExpectedBytes / receivedHeight;
        // Delete old buffers if necessary
        rawImage = NULL;
        if (rawBuffer1 != NULL) delete rawBuffer1;
        if (rawBuffer2 != NULL) delete rawBuffer2;

        rawBuffer1 = (unsigned char*)malloc(rawExpectedBytes);
        rawBuffer2 = (unsigned char*)malloc(rawExpectedBytes);
        rawImage = rawBuffer1;
        // TODO check if old image should be deleted

        // Set image format
        // 8 BIT GREYSCALE IMAGE
1367
        if (depth <= 8 && channels == 1) {
pixhawk's avatar
pixhawk committed
1368 1369 1370
            image = new QImage(receivedWidth, receivedHeight, QImage::Format_Indexed8);
            // Create matching color table
            image->setNumColors(256);
1371
            for (int i = 0; i < 256; i++) {
pixhawk's avatar
pixhawk committed
1372 1373 1374 1375 1376 1377
                image->setColor(i, qRgb(i, i, i));
                //qDebug() << __FILE__ << __LINE__ << std::hex << i;
            }

        }
        // 32 BIT COLOR IMAGE WITH ALPHA VALUES (#ARGB)
1378
        else {
pixhawk's avatar
pixhawk committed
1379 1380 1381 1382
            image = new QImage(receivedWidth, receivedHeight, QImage::Format_ARGB32);
        }

        // Fill first channel of image with black pixels
1383 1384 1385 1386 1387 1388 1389 1390
        if (MainWindow::instance()->getStyle() == MainWindow::QGC_MAINWINDOW_STYLE_LIGHT)
        {
            image->fill(255);
        }
        else
        {
            image->fill(0);
        }
pixhawk's avatar
pixhawk committed
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
        glImage = QGLWidget::convertToGLFormat(*image);

        qDebug() << __FILE__ << __LINE__ << "Setting up image";

        // Set size once
        setFixedSize(receivedWidth, receivedHeight);
        setMinimumSize(receivedWidth, receivedHeight);
        setMaximumSize(receivedWidth, receivedHeight);
        // Lock down the size
        //setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
        //resize(receivedWidth, receivedHeight);
    }

}

void HUD::startImage(int imgid, int width, int height, int depth, int channels)
{
1408
    Q_UNUSED(imgid);
pixhawk's avatar
pixhawk committed
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420
    //qDebug() << "HUD: starting image (" << width << "x" << height << ", " << depth << "bits) with " << channels << "channels";

    // Copy previous image to screen if it hasn't been finished properly
    finishImage();

    // Reset image size if necessary
    setImageSize(width, height, depth, channels);
    imageStarted = true;
}

void HUD::finishImage()
{
1421
    if (imageStarted) {
pixhawk's avatar
pixhawk committed
1422 1423 1424 1425 1426 1427 1428
        commitRawDataToGL();
        imageStarted = false;
    }
}

void HUD::commitRawDataToGL()
{
1429
    qDebug() << __FILE__ << __LINE__ << "Copying raw data to GL buffer:" << rawImage << receivedWidth << receivedHeight << image->format();
1430
    if (image != NULL) {
pixhawk's avatar
pixhawk committed
1431 1432
        QImage::Format format = image->format();
        QImage* newImage = new QImage(rawImage, receivedWidth, receivedHeight, format);
1433
        if (format == QImage::Format_Indexed8) {
pixhawk's avatar
pixhawk committed
1434 1435
            // Create matching color table
            newImage->setNumColors(256);
1436
            for (int i = 0; i < 256; i++) {
pixhawk's avatar
pixhawk committed
1437 1438 1439 1440 1441 1442 1443 1444 1445
                newImage->setColor(i, qRgb(i, i, i));
                //qDebug() << __FILE__ << __LINE__ << std::hex << i;
            }
        }

        glImage = QGLWidget::convertToGLFormat(*newImage);
        delete image;
        image = newImage;
        // Switch buffers
1446
        if (rawImage == rawBuffer1) {
pixhawk's avatar
pixhawk committed
1447 1448
            rawImage = rawBuffer2;
            //qDebug() << "Now buffer 2";
1449
        } else {
pixhawk's avatar
pixhawk committed
1450 1451 1452 1453
            rawImage = rawBuffer1;
            //qDebug() << "Now buffer 1";
        }
    }
1454
    update();
pixhawk's avatar
pixhawk committed
1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
}

void HUD::saveImage(QString fileName)
{
    image->save(fileName);
}

void HUD::saveImage()
{
    //Bring up popup
    QString fileName = "output.png";
    saveImage(fileName);
}

1469 1470
void HUD::startImage(quint64 timestamp)
{
1471
    if (videoEnabled && offlineDirectory != "") {
1472 1473 1474 1475 1476 1477 1478 1479
        // Load and diplay image file
        nextOfflineImage = QString(offlineDirectory + "/%1.bmp").arg(timestamp);
    }
}

void HUD::selectOfflineDirectory()
{
    QString fileName = QFileDialog::getExistingDirectory(this, tr("Select image directory"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation));
1480
    if (fileName != "") {
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
        offlineDirectory = fileName;
    }
}

void HUD::enableHUDInstruments(bool enabled)
{
    hudInstrumentsEnabled = enabled;
}

void HUD::enableVideo(bool enabled)
{
    videoEnabled = enabled;
}

pixhawk's avatar
pixhawk committed
1495 1496
void HUD::setPixels(int imgid, const unsigned char* imageData, int length, int startIndex)
{
1497
    Q_UNUSED(imgid);
pixhawk's avatar
pixhawk committed
1498 1499
    //    qDebug() << "at" << __FILE__ << __LINE__ << ": Received startindex" << startIndex << "and length" << length << "(" << startIndex+length << "of" << rawExpectedBytes << "bytes)";

1500 1501
    if (imageStarted)
    {
pixhawk's avatar
pixhawk committed
1502 1503
        //if (rawLastIndex != startIndex) qDebug() << "PACKET LOSS!";

1504 1505
        if (startIndex+length > rawExpectedBytes)
        {
pixhawk's avatar
pixhawk committed
1506
            qDebug() << "HUD: OVERFLOW! startIndex:" << startIndex << "length:" << length << "image raw size" << ((receivedWidth * receivedHeight * receivedChannels * receivedDepth) / 8) - 1;
1507 1508 1509
        }
        else
        {
pixhawk's avatar
pixhawk committed
1510 1511 1512 1513 1514
            memcpy(rawImage+startIndex, imageData, length);

            rawLastIndex = startIndex+length;

            // Check if we just reached the end of the image
1515 1516
            if (startIndex+length == rawExpectedBytes)
            {
pixhawk's avatar
pixhawk committed
1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
                //qDebug() << "HUD: END OF IMAGE REACHED!";
                finishImage();
                rawLastIndex = 0;
            }
        }

        //        for (int i = 0; i < length; i++)
        //        {
        //            for (int j = 0; j < receivedChannels; j++)
        //            {
        //                unsigned int x = (startIndex+i) % receivedWidth;
        //                unsigned int y = (unsigned int)((startIndex+i) / receivedWidth);
        //                qDebug() << "Setting pixel" << x << "," << y << "to" << (unsigned int)*(rawImage+startIndex+i);
        //            }
        //        }
    }
}
1534

LM's avatar
LM committed
1535
void HUD::copyImage()
1536
{
pixhawk's avatar
pixhawk committed
1537
    if (isVisible() && hudInstrumentsEnabled)
lm's avatar
lm committed
1538
    {
1539
        //qDebug() << "HUD::copyImage()";
1540 1541 1542 1543
        UAS* u = dynamic_cast<UAS*>(this->uas);
        if (u)
        {
            this->glImage = QGLWidget::convertToGLFormat(u->getImage());
1544

1545 1546 1547 1548 1549 1550
            // Save to directory if logging is enabled
            if (imageLoggingEnabled)
            {
                u->getImage().save(QString("%1/%2.png").arg(imageLogDirectory).arg(imageLogCounter));
                imageLogCounter++;
            }
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561
        }
    }
}

void HUD::saveImages(bool save)
{
    if (save)
    {
        QFileDialog dialog(this);
        dialog.setFileMode(QFileDialog::DirectoryOnly);

1562
        imageLogDirectory = QFileDialog::getExistingDirectory(this, tr("Select image log directory"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation));
1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576

        qDebug() << "Logging to:" << imageLogDirectory;

        if (imageLogDirectory != "")
        {
            imageLogCounter = 0;
            imageLoggingEnabled = true;
            qDebug() << "Logging on";
        }
        else
        {
            imageLoggingEnabled = false;
            selectSaveDirectoryAction->setChecked(false);
        }
lm's avatar
lm committed
1577
    }
1578 1579 1580 1581 1582 1583
    else
    {
        imageLoggingEnabled = false;
        selectSaveDirectoryAction->setChecked(false);
    }

1584
}