PrimaryFlightDisplay.cc 53.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
#include "PrimaryFlightDisplay.h"
#include "UASManager.h"

//#include "ui_primaryflightdisplay.h"
#include <QDebug>
#include <QRectF>
#include <cmath>
#include <QPen>
#include <QPainter>
#include <QPainterPath>
#include <QResizeEvent>
dongfang's avatar
dongfang committed
12 13
#include <QtCore/qmath.h>
//#include <cmath>
14

15 16
#if 0
// Left in but ifdef'ed out since there is commented out code below that uses it
17
static const float SEPARATE_COMPASS_ASPECTRATIO = 3.0f/4.0f;
18
#endif
19 20 21 22
static const float LINEWIDTH = 0.0036f;
static const float SMALL_TEXT_SIZE = 0.028f;
static const float MEDIUM_TEXT_SIZE = SMALL_TEXT_SIZE*1.2f;
static const float LARGE_TEXT_SIZE = MEDIUM_TEXT_SIZE*1.2f;
23

24
static const bool SHOW_ZERO_ON_SCALES = true;
25 26

// all in units of display height
27 28 29 30
static const float ROLL_SCALE_RADIUS = 0.42f;
static const float ROLL_SCALE_TICKMARKLENGTH = 0.04f;
static const float ROLL_SCALE_MARKERWIDTH = 0.06f;
static const float ROLL_SCALE_MARKERHEIGHT = 0.04f;
31
// scale max. degrees
32
static const int ROLL_SCALE_RANGE = 60;
33 34

// fraction of height to translate for each degree of pitch.
35 36 37 38
static const float PITCHTRANSLATION = 65;
// 5 degrees for each line
static const int PITCH_SCALE_RESOLUTION = 5;
static const float PITCH_SCALE_MAJORWIDTH = 0.1f;
39
static const float PITCH_SCALE_MINORWIDTH = 0.066f;
40 41 42 43

// Beginning from PITCH_SCALE_WIDTHREDUCTION_FROM degrees of +/- pitch, the
// width of the lines is reduced, down to PITCH_SCALE_WIDTHREDUCTION times
// the normal width. This helps keep orientation in extreme attitudes.
44 45
static const int PITCH_SCALE_WIDTHREDUCTION_FROM = 30;
static const float PITCH_SCALE_WIDTHREDUCTION = 0.3f;
46

47
static const int PITCH_SCALE_HALFRANGE = 15;
48 49 50 51 52

// The number of degrees to either side of the heading to draw the compass disk.
// 180 is valid, this will draw a complete disk. If the disk is partly clipped
// away, less will do.

53 54 55
static const int  COMPASS_DISK_MAJORTICK = 10;
static const int  COMPASS_DISK_ARROWTICK = 45;
static const int  COMPASS_DISK_RESOLUTION = 10;
56 57
static const float COMPASS_DISK_MARKERWIDTH = 0.2f;
static const float COMPASS_DISK_MARKERHEIGHT = 0.133f;
58

59
static const int  CROSSTRACK_MAX = 1000;
60
static const float CROSSTRACK_RADIUS = 0.6f;
61

62 63
static const float TAPE_GAUGES_TICKWIDTH_MAJOR = 0.25f;
static const float TAPE_GAUGES_TICKWIDTH_MINOR = 0.15f;
64 65

// The altitude difference between top and bottom of scale
66
static const int ALTIMETER_LINEAR_SPAN = 50;
67
// every 5 meters there is a tick mark
68
static const int ALTIMETER_LINEAR_RESOLUTION = 5;
69
// every 10 meters there is a number
70
static const int ALTIMETER_LINEAR_MAJOR_RESOLUTION = 10;
71 72

// min. and max. vertical velocity
73 74
static const int ALTIMETER_VVI_SPAN = 5;
static const float ALTIMETER_VVI_WIDTH = 0.2f;
75 76

// Now the same thing for airspeed!
77 78 79
static const int AIRSPEED_LINEAR_SPAN = 15;
static const int AIRSPEED_LINEAR_RESOLUTION = 1;
static const int AIRSPEED_LINEAR_MAJOR_RESOLUTION = 5;
80

81 82
/*
 *@TODO:
dongfang's avatar
dongfang committed
83
 * global fixed pens (and painters too?)
84 85 86
 * repaint on demand multiple canvases
 * multi implementation with shared model class
 */
dongfang's avatar
dongfang committed
87
double PrimaryFlightDisplay_round(double value, int digits=0)
88
{
89
    return floor(value * pow(10.0, digits) + 0.5) / pow(10.0, digits);
90
}
91

92 93 94 95 96 97
qreal PrimaryFlightDisplay_constrain(qreal value, qreal min, qreal max) {
    if (value<min) value=min;
    else if(value>max) value=max;
    return value;
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
const int PrimaryFlightDisplay::tickValues[] = {10, 20, 30, 45, 60};
const QString PrimaryFlightDisplay::compassWindNames[] = {
    QString("N"),
    QString("NE"),
    QString("E"),
    QString("SE"),
    QString("S"),
    QString("SW"),
    QString("W"),
    QString("NW")
};

PrimaryFlightDisplay::PrimaryFlightDisplay(int width, int height, QWidget *parent) :
    QWidget(parent),

113
    uas(NULL),
114

115 116 117
    roll(0),
    pitch(0),
    heading(0),
118

Don Gagne's avatar
Don Gagne committed
119 120
    altitudeAMSL(std::numeric_limits<double>::quiet_NaN()),
    altitudeRelative(std::numeric_limits<double>::quiet_NaN()),
121

Don Gagne's avatar
Don Gagne committed
122 123 124
    groundSpeed(std::numeric_limits<double>::quiet_NaN()),
    airSpeed(std::numeric_limits<double>::quiet_NaN()),
    climbRate(std::numeric_limits<double>::quiet_NaN()),
125

126
    navigationCrosstrackError(0),
Don Gagne's avatar
Don Gagne committed
127
    navigationTargetBearing(std::numeric_limits<double>::quiet_NaN()),
128

129 130
    layout(COMPASS_INTEGRATED),
    style(OVERLAY_HSI),
131

dongfang's avatar
dongfang committed
132
    redColor(QColor::fromHsvF(0, 0.75, 0.9)),
dongfang's avatar
dongfang committed
133 134 135 136 137 138 139
    amberColor(QColor::fromHsvF(0.12, 0.6, 1.0)),
    greenColor(QColor::fromHsvF(0.25, 0.8, 0.8)),

    lineWidth(2),
    fineLineWidth(1),

    instrumentEdgePen(QColor::fromHsvF(0, 0, 0.65, 0.5)),
dongfang's avatar
dongfang committed
140
    instrumentBackground(QColor::fromHsvF(0, 0, 0.3, 0.3)),
141 142 143
    instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0)),

    font("Bitstream Vera Sans"),
144 145 146
    refreshTimer(new QTimer(this)),
    _valuesChanged(false),
    _valuesLastPainted(QGC::groundTimeMilliseconds())
dongfang's avatar
dongfang committed
147
{
148 149
    Q_UNUSED(width);
    Q_UNUSED(height);
150

151 152 153
    setMinimumSize(120, 80);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

Lorenz Meier's avatar
Lorenz Meier committed
154 155
    setActiveUAS(UASManager::instance()->getActiveUAS());

156
    // Connect with UAS signal
157 158
    //connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(addUAS(UASInterface*)));
    connect(UASManager::instance(), SIGNAL(UASDeleted(UASInterface*)), this, SLOT(forgetUAS(UASInterface*)));
159
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
160

161 162
    // Refresh timer
    refreshTimer->setInterval(updateInterval);
dongfang's avatar
dongfang committed
163
    //    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
164
    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(checkUpdate()));
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

PrimaryFlightDisplay::~PrimaryFlightDisplay()
{
    refreshTimer->stop();
}


QSize PrimaryFlightDisplay::sizeHint() const
{
    return QSize(width(), (width()*3.0f)/4);
}

void PrimaryFlightDisplay::showEvent(QShowEvent* event)
{
    // React only to internal (pre-display)
    // events
    QWidget::showEvent(event);
    refreshTimer->start(updateInterval);
    emit visibilityChanged(true);
}

void PrimaryFlightDisplay::hideEvent(QHideEvent* event)
{
    // React only to internal (pre-display)
    // events
    refreshTimer->stop();
    QWidget::hideEvent(event);
    emit visibilityChanged(false);
}

dongfang's avatar
dongfang committed
196 197 198 199
void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
    QWidget::resizeEvent(e);

    qreal size = e->size().width();
dongfang's avatar
dongfang committed
200
    //if(e->size().height()<size) size = e->size().height();
dongfang's avatar
dongfang committed
201

202 203
    lineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH, 1, 6);
    fineLineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH*2/3, 1, 2);
dongfang's avatar
dongfang committed
204 205 206 207

    instrumentEdgePen.setWidthF(fineLineWidth);
    //AIEdgePen.setWidthF(fineLineWidth);

208
    smallTextSize = size * SMALL_TEXT_SIZE;
dongfang's avatar
dongfang committed
209 210 211
    mediumTextSize = size * MEDIUM_TEXT_SIZE;
    largeTextSize = size * LARGE_TEXT_SIZE;

dongfang's avatar
dongfang committed
212 213
    /*
     * Try without layout Change-O-Matic. It was too complicated.
214 215
    qreal aspect = e->size().width() / e->size().height();
    if (aspect <= SEPARATE_COMPASS_ASPECTRATIO)
dongfang's avatar
dongfang committed
216 217
        layout = COMPASS_SEPARATED;
    else
218
        layout = COMPASS_INTEGRATED;
dongfang's avatar
dongfang committed
219
    */
220
    // qDebug("Width %d height %d decision %d", e->size().width(), e->size().height(), layout);
dongfang's avatar
dongfang committed
221
}
222

dongfang's avatar
dongfang committed
223 224 225 226 227
void PrimaryFlightDisplay::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    doPaint();
}
228

229 230 231 232 233 234 235 236 237
void PrimaryFlightDisplay::checkUpdate()
{
    if (uas && (_valuesChanged || (QGC::groundTimeMilliseconds() - _valuesLastPainted) > 260)) {
        update();
        _valuesChanged = false;
        _valuesLastPainted = QGC::groundTimeMilliseconds();
    }
}

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
///*
// * Interface towards qgroundcontrol
// */
//void PrimaryFlightDisplay::addUAS(UASInterface* uas)
//{
//    if (uas)
//    {
//        if (!this->uas)
//        {
//            setActiveUAS(uas);
//        }
//    }
//}

void PrimaryFlightDisplay::forgetUAS(UASInterface* uas)
253
{
254
    if (this->uas != NULL && this->uas == uas) {
255
        // Disconnect any previously connected active MAV
256 257 258 259 260
        disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
        disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
        disconnect(this->uas, SIGNAL(speedChanged(UASInterface*, double, double, quint64)), this, SLOT(updateSpeed(UASInterface*, double, double, quint64)));
        disconnect(this->uas, SIGNAL(altitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
        disconnect(this->uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
261
    }
262 263 264 265 266 267 268 269
}

/**
 *
 * @param uas the UAS/MAV to monitor/display with the HUD
 */
void PrimaryFlightDisplay::setActiveUAS(UASInterface* uas)
{
tstellanova's avatar
tstellanova committed
270 271 272
    if (uas == this->uas)
        return; //no need to rewire

273 274
    // Disconnect the previous one (if any)
    forgetUAS(this->uas);
275 276 277 278 279 280

    if (uas) {
        // Now connect the new UAS
        // Setup communication
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
281 282
        connect(uas, SIGNAL(speedChanged(UASInterface*, double, double, quint64)), this, SLOT(updateSpeed(UASInterface*, double, double, quint64)));
        connect(uas, SIGNAL(altitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
283
        connect(uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
284 285 286 287 288 289 290 291 292 293

        // Set new UAS
        this->uas = uas;
    }
}

void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
294

295
        // Called from UAS.cc l. 616
296
        if (isinf(roll)) {
Don Gagne's avatar
Don Gagne committed
297
            this->roll = std::numeric_limits<double>::quiet_NaN();
298
        } else {
299 300 301 302 303 304 305 306

            float rolldeg = roll * (180.0 / M_PI);

            if (fabsf(roll - rolldeg) > 2.5f) {
                _valuesChanged = true;
            }

            this->roll = rolldeg;
307
        }
308

309
        if (isinf(pitch)) {
Don Gagne's avatar
Don Gagne committed
310
            this->pitch = std::numeric_limits<double>::quiet_NaN();
311
        } else {
312 313 314 315 316 317 318 319

            float pitchdeg = pitch * (180.0 / M_PI);

            if (fabsf(pitch - pitchdeg) > 2.5f) {
                _valuesChanged = true;
            }

            this->pitch = pitchdeg;
320 321
        }

322
        if (isinf(yaw)) {
Don Gagne's avatar
Don Gagne committed
323
            this->heading = std::numeric_limits<double>::quiet_NaN();
324
        } else {
325

326 327
            yaw = yaw * (180.0 / M_PI);
            if (yaw<0) yaw+=360;
328 329 330 331 332

            if (fabsf(heading - yaw) > 10.0f) {
                _valuesChanged = true;
            }

333 334
            this->heading = yaw;
        }
Lorenz Meier's avatar
Lorenz Meier committed
335

336 337 338 339 340
}

void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(component);
341
    this->updateAttitude(uas, roll, pitch, yaw, timestamp);
342 343
}

344
void PrimaryFlightDisplay::updateSpeed(UASInterface* uas, double _groundSpeed, double _airSpeed, quint64 timestamp)
345 346 347 348
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

349 350 351 352 353 354 355 356
    if (fabsf(groundSpeed - _groundSpeed) > 0.5f) {
        _valuesChanged = true;
    }

    if (fabsf(airSpeed - _airSpeed) > 1.0f) {
        _valuesChanged = true;
    }

357 358
    groundSpeed = _groundSpeed;
    airSpeed = _airSpeed;
359
}
360

361
void PrimaryFlightDisplay::updateAltitude(UASInterface* uas, double _altitudeAMSL, double _altitudeRelative, double _climbRate, quint64 timestamp) {
362 363
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
364 365 366 367 368 369 370 371 372 373 374 375 376

    if (fabsf(altitudeAMSL - _altitudeAMSL) > 0.5f) {
        _valuesChanged = true;
    }

    if (fabsf(altitudeRelative - _altitudeRelative) > 0.5f) {
        _valuesChanged = true;
    }

    if (fabsf(climbRate - _climbRate) > 0.5f) {
        _valuesChanged = true;
    }

377 378 379
    altitudeAMSL = _altitudeAMSL;
    altitudeRelative = _altitudeRelative;
    climbRate = _climbRate;
380 381
}

382 383 384 385 386 387 388 389
void PrimaryFlightDisplay::updateNavigationControllerErrors(UASInterface* uas, double altitudeError, double speedError, double xtrackError) {
    Q_UNUSED(uas);
    this->navigationAltitudeError = altitudeError;
    this->navigationSpeedError = speedError;
    this->navigationCrosstrackError = xtrackError;
}


390 391 392
/*
 * Private and such
 */
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408

// TODO: Move to UAS. Real working implementation.
bool PrimaryFlightDisplay::isAirplane() {
    if (!this->uas)
        return false;
    switch(this->uas->getSystemType()) {
    case MAV_TYPE_GENERIC:
    case MAV_TYPE_FIXED_WING:
    case MAV_TYPE_AIRSHIP:
    case MAV_TYPE_FLAPPING_WING:
        return true;
    default:
        return false;
    }
}

409 410 411 412 413 414 415
// TODO: Implement. Should return true when navigating.
// That would be (APM) in AUTO and RTL modes.
// This could forward to a virtual on UAS bool isNavigatingAutonomusly() or whatever.
bool PrimaryFlightDisplay::shouldDisplayNavigationData() {
    return true;
}

416 417 418
void PrimaryFlightDisplay::drawTextCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
419
        float pixelSize,
420 421 422
        float x,
        float y)
{
dongfang's avatar
dongfang committed
423
    font.setPixelSize(pixelSize);
424 425 426 427 428 429 430 431 432 433 434
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignCenter |  Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextLeftCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
435
        float pixelSize,
436 437 438
        float x,
        float y)
{
dongfang's avatar
dongfang committed
439
    font.setPixelSize(pixelSize);
440 441 442 443 444 445 446 447 448 449 450
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignLeft | Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/, y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextRightCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
451
        float pixelSize,
452 453 454
        float x,
        float y)
{
dongfang's avatar
dongfang committed
455
    font.setPixelSize(pixelSize);
456 457 458 459 460 461 462 463 464 465 466
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignRight | Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/ -bounds.width(), y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextCenterTop (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
467
        float pixelSize,
468 469 470
        float x,
        float y)
{
dongfang's avatar
dongfang committed
471
    font.setPixelSize(pixelSize);
472 473 474 475 476 477 478 479 480 481 482
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignCenter | Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y+bounds.height() /*+bounds.y()*/, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextCenterBottom (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
483
        float pixelSize,
484 485 486
        float x,
        float y)
{
dongfang's avatar
dongfang committed
487
    font.setPixelSize(pixelSize);
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignCenter;
    painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y /*+bounds.y()*/, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawInstrumentBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.drawRect(edge);
}

void PrimaryFlightDisplay::fillInstrumentBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

dongfang's avatar
dongfang committed
508 509 510 511 512 513 514
void PrimaryFlightDisplay::fillInstrumentOpagueBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentOpagueBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

515
qreal pitchAngleToTranslation(qreal viewHeight, float pitch) {
516
    if (isnan(pitch))
517
        return 0;
518
    return pitch * viewHeight / PITCHTRANSLATION;
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
}

void PrimaryFlightDisplay::drawAIAirframeFixedFeatures(QPainter& painter, QRectF area) {
    // red line from -7/10 to -5/10 half-width
    // red line from 7/10 to 5/10 half-width
    // red slanted line from -2/10 half-width to 0
    // red slanted line from 2/10 half-width to 0
    // red arrow thing under roll scale
    // prepareTransform(painter, width, height);
    painter.resetTransform();
    painter.translate(area.center());

    qreal w = area.width();
    qreal h = area.height();

dongfang's avatar
dongfang committed
534
    QPen pen;
535
    pen.setWidthF(lineWidth * 1.5f);
dongfang's avatar
dongfang committed
536 537
    pen.setColor(redColor);
    painter.setPen(pen);
538

539 540
    float length = 0.15f;
    float side = 0.5f;
dongfang's avatar
dongfang committed
541 542 543 544
    // The 2 lines at sides.
    painter.drawLine(QPointF(-side*w, 0), QPointF(-(side-length)*w, 0));
    painter.drawLine(QPointF(side*w, 0), QPointF((side-length)*w, 0));

545
    float rel = length/qSqrt(2.0f);
dongfang's avatar
dongfang committed
546 547 548 549 550 551 552 553
    // The gull
    painter.drawLine(QPointF(rel*w, rel*w/2), QPoint(0, 0));
    painter.drawLine(QPointF(-rel*w, rel*w/2), QPoint(0, 0));

    // The roll scale marker.
    QPainterPath markerPath(QPointF(0, -w*ROLL_SCALE_RADIUS+1));
    markerPath.lineTo(-h*ROLL_SCALE_MARKERWIDTH/2, -w*(ROLL_SCALE_RADIUS-ROLL_SCALE_MARKERHEIGHT)+1);
    markerPath.lineTo(h*ROLL_SCALE_MARKERWIDTH/2, -w*(ROLL_SCALE_RADIUS-ROLL_SCALE_MARKERHEIGHT)+1);
554 555 556 557
    markerPath.closeSubpath();
    painter.drawPath(markerPath);
}

558 559 560 561 562 563 564 565 566 567 568 569 570 571
inline qreal min4(qreal a, qreal b, qreal c, qreal d) {
    if(b<a) a=b;
    if(c<a) a=c;
    if(d<a) a=d;
    return a;
}

inline qreal max4(qreal a, qreal b, qreal c, qreal d) {
    if(b>a) a=b;
    if(c>a) a=c;
    if(d>a) a=d;
    return a;
}

572 573
void PrimaryFlightDisplay::drawAIGlobalFeatures(
        QPainter& painter,
574 575
        QRectF mainArea,
        QRectF paintArea) {
576

577
    float displayRoll = this->roll;
578
    if (isnan(displayRoll))
579 580
        displayRoll = 0;

581
    painter.resetTransform();
582
    painter.translate(mainArea.center());
583

584 585
    qreal pitchPixels = pitchAngleToTranslation(mainArea.height(), pitch);
    qreal gradientEnd = pitchAngleToTranslation(mainArea.height(), 60);
586

587 588
    //painter.rotate(-roll);
    //painter.translate(0, pitchPixels);
589

590 591
    //    QTransform forwardTransform;
    //forwardTransform.translate(mainArea.center().x(), mainArea.center().y());
592
    painter.rotate(-displayRoll);
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
    painter.translate(0, pitchPixels);

    // Calculate the radius of area we need to paint to cover all.
    QTransform rtx = painter.transform().inverted();

    QPointF topLeft = rtx.map(paintArea.topLeft());
    QPointF topRight = rtx.map(paintArea.topRight());
    QPointF bottomLeft = rtx.map(paintArea.bottomLeft());
    QPointF bottomRight = rtx.map(paintArea.bottomRight());
    // Just KISS... make a rectangluar basis.
    qreal minx = min4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x());
    qreal maxx = max4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x());
    qreal miny = min4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y());
    qreal maxy = max4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y());

    QPointF hzonLeft = QPoint(minx, 0);
    QPointF hzonRight = QPoint(maxx, 0);

    QPainterPath skyPath(hzonLeft);
    skyPath.lineTo(QPointF(minx, miny));
    skyPath.lineTo(QPointF(maxx, miny));
    skyPath.lineTo(hzonRight);
615 616
    skyPath.closeSubpath();

617
    QLinearGradient skyGradient(0, -gradientEnd, 0, 0);
618 619 620 621 622
    skyGradient.setColorAt(0, QColor::fromHsvF(0.6, 1.0, 0.7));
    skyGradient.setColorAt(1, QColor::fromHsvF(0.6, 0.25, 0.9));
    QBrush skyBrush(skyGradient);
    painter.fillPath(skyPath, skyBrush);

623 624 625 626
    QPainterPath groundPath(hzonRight);
    groundPath.lineTo(maxx, maxy);
    groundPath.lineTo(minx, maxy);
    groundPath.lineTo(hzonLeft);
627 628
    groundPath.closeSubpath();

629
    QLinearGradient groundGradient(0, gradientEnd, 0, 0);
630 631 632 633 634
    groundGradient.setColorAt(0, QColor::fromHsvF(0.25, 1, 0.5));
    groundGradient.setColorAt(1, QColor::fromHsvF(0.25, 0.25, 0.5));
    QBrush groundBrush(groundGradient);
    painter.fillPath(groundPath, groundBrush);

dongfang's avatar
dongfang committed
635 636 637 638 639
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(greenColor);
    painter.setPen(pen);

640 641
    QPointF start(-mainArea.width(), 0);
    QPoint end(mainArea.width(), 0);
642 643 644 645 646 647
    painter.drawLine(start, end);
}

void PrimaryFlightDisplay::drawPitchScale(
        QPainter& painter,
        QRectF area,
dongfang's avatar
dongfang committed
648
        float intrusion,
649 650 651 652
        bool drawNumbersLeft,
        bool drawNumbersRight
        ) {

653 654
    Q_UNUSED(intrusion);
    
655
    float displayPitch = this->pitch;
656
    if (isnan(displayPitch))
657 658
        displayPitch = 0;

dongfang's avatar
dongfang committed
659 660 661
    // The area should be quadratic but if not width is the major size.
    qreal w = area.width();
    if (w<area.height()) w = area.height();
662

dongfang's avatar
dongfang committed
663 664 665 666
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
667 668 669 670

    QTransform savedTransform = painter.transform();

    // find the mark nearest center
671
    int snap = qRound((double)(displayPitch/PITCH_SCALE_RESOLUTION))*PITCH_SCALE_RESOLUTION;
672 673 674 675
    int _min = snap-PITCH_SCALE_HALFRANGE;
    int _max = snap+PITCH_SCALE_HALFRANGE;
    for (int degrees=_min; degrees<=_max; degrees+=PITCH_SCALE_RESOLUTION) {
        bool isMajor = degrees % (PITCH_SCALE_RESOLUTION*2) == 0;
676 677 678 679 680 681 682 683 684 685 686 687
        float linewidth =  isMajor ? PITCH_SCALE_MAJORWIDTH : PITCH_SCALE_MINORWIDTH;
        if (abs(degrees) > PITCH_SCALE_WIDTHREDUCTION_FROM) {
            // we want: 1 at PITCH_SCALE_WIDTHREDUCTION_FROM and PITCH_SCALE_WIDTHREDUCTION at 90.
            // That is PITCH_SCALE_WIDTHREDUCTION + (1-PITCH_SCALE_WIDTHREDUCTION) * f(pitch)
            // where f(90)=0 and f(PITCH_SCALE_WIDTHREDUCTION_FROM)=1
            // f(p) = (90-p) * 1/(90-PITCH_SCALE_WIDTHREDUCTION_FROM)
            // or PITCH_SCALE_WIDTHREDUCTION + f(pitch) - f(pitch) * PITCH_SCALE_WIDTHREDUCTION
            // or PITCH_SCALE_WIDTHREDUCTION (1-f(pitch)) + f(pitch)
            int fromVertical = abs(pitch>=0 ? 90-pitch : -90-pitch);
            float temp = fromVertical * 1/(90.0f-PITCH_SCALE_WIDTHREDUCTION_FROM);
            linewidth *= (PITCH_SCALE_WIDTHREDUCTION * (1-temp) + temp);
        }
dongfang's avatar
dongfang committed
688

689
        float shift = pitchAngleToTranslation(w, displayPitch-degrees);
dongfang's avatar
dongfang committed
690 691 692 693

        // TODO: Intrusion detection and evasion. That is, don't draw
        // where the compass has intruded.

694
        painter.translate(0, shift);
dongfang's avatar
dongfang committed
695 696
        QPointF start(-linewidth*w, 0);
        QPointF end(linewidth*w, 0);
697 698 699 700 701 702 703
        painter.drawLine(start, end);

        if (isMajor && (drawNumbersLeft||drawNumbersRight)) {
            int displayDegrees = degrees;
            if(displayDegrees>90) displayDegrees = 180-displayDegrees;
            else if (displayDegrees<-90) displayDegrees = -180 - displayDegrees;
            if (SHOW_ZERO_ON_SCALES || degrees) {
704
                QString s_number;
705
                if (isnan(this->pitch))
706 707 708
                    s_number.sprintf("-");
                else
                    s_number.sprintf("%d", displayDegrees);
dongfang's avatar
dongfang committed
709 710
                if (drawNumbersLeft)  drawTextRightCenter(painter, s_number, mediumTextSize, -PITCH_SCALE_MAJORWIDTH * w-10, 0);
                if (drawNumbersRight) drawTextLeftCenter(painter, s_number, mediumTextSize, PITCH_SCALE_MAJORWIDTH * w+10, 0);
711 712 713 714 715 716 717 718 719 720 721 722 723
            }
        }

        painter.setTransform(savedTransform);
    }
}

void PrimaryFlightDisplay::drawRollScale(
        QPainter& painter,
        QRectF area,
        bool drawTicks,
        bool drawNumbers) {

724
    qreal w = area.width();
dongfang's avatar
dongfang committed
725
    if (w<area.height()) w = area.height();
726

dongfang's avatar
dongfang committed
727 728 729 730
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
731 732 733 734 735 736

    // We should really do these transforms but they are assumed done by caller.
    // painter.resetTransform();
    // painter.translate(area.center());
    // painter.rotate(roll);

737
    qreal _size = w * ROLL_SCALE_RADIUS*2;
dongfang's avatar
dongfang committed
738
    QRectF arcArea(-_size/2, - _size/2, _size, _size);
739
    painter.drawArc(arcArea, (90-ROLL_SCALE_RANGE)*16, ROLL_SCALE_RANGE*2*16);
dongfang's avatar
dongfang committed
740
    // painter.drawEllipse(QPoint(0,0),200,200);
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
    if (drawTicks) {
        int length = sizeof(tickValues)/sizeof(int);
        qreal previousRotation = 0;
        for (int i=0; i<length*2+1; i++) {
            int degrees = (i==length) ? 0 : (i>length) ?-tickValues[i-length-1] : tickValues[i];
            //degrees = 180 - degrees;
            painter.rotate(degrees - previousRotation);
            previousRotation = degrees;

            QPointF start(0, -_size/2);
            QPointF end(0, -(1.0+ROLL_SCALE_TICKMARKLENGTH)*_size/2);

            painter.drawLine(start, end);

            QString s_number; //= QString("%d").arg(degrees);
            if (SHOW_ZERO_ON_SCALES || degrees)
                s_number.sprintf("%d", abs(degrees));

            if (drawNumbers) {
760
                drawTextCenterBottom(painter, s_number, mediumTextSize, 0, -(ROLL_SCALE_RADIUS+ROLL_SCALE_TICKMARKLENGTH*1.7)*w);
761 762 763 764 765 766 767
            }
        }
    }
}

void PrimaryFlightDisplay::drawAIAttitudeScales(
        QPainter& painter,
dongfang's avatar
dongfang committed
768 769
        QRectF area,
        float intrusion
dongfang's avatar
dongfang committed
770
        ) {
771
    float displayRoll = this->roll;
772
    if (isnan(displayRoll))
773
        displayRoll = 0;
774 775 776
    // To save computations, we do these transformations once for both scales:
    painter.resetTransform();
    painter.translate(area.center());
777
    painter.rotate(-displayRoll);
778 779 780 781
    QTransform saved = painter.transform();

    drawRollScale(painter, area, true, true);
    painter.setTransform(saved);
dongfang's avatar
dongfang committed
782
    drawPitchScale(painter, area, intrusion, true, true);
783 784
}

dongfang's avatar
dongfang committed
785
void PrimaryFlightDisplay::drawAICompassDisk(QPainter& painter, QRectF area, float halfspan) {
786
    float displayHeading = this->heading;
787
    if (isnan(displayHeading))
788 789 790 791
        displayHeading = 0;

    float start = displayHeading - halfspan;
    float end = displayHeading + halfspan;
dongfang's avatar
dongfang committed
792

dongfang's avatar
dongfang committed
793 794
    int firstTick = ceil(start / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
    int lastTick = floor(end / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
795 796 797 798

    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();
dongfang's avatar
dongfang committed
799 800
    painter.setBrush(instrumentBackground);
    painter.setPen(instrumentEdgePen);
801 802 803
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
804 805
    QPen scalePen(Qt::black);
    scalePen.setWidthF(fineLineWidth);
806

dongfang's avatar
dongfang committed
807
    for (int tickYaw = firstTick; tickYaw <= lastTick; tickYaw += COMPASS_DISK_RESOLUTION) {
808 809 810 811 812
        int displayTick = tickYaw;
        if (displayTick < 0) displayTick+=360;
        else if (displayTick>=360) displayTick-=360;

        // yaw is in center.
813
        float off = tickYaw - displayHeading;
814
        // wrap that to ]-180..180]
dongfang's avatar
dongfang committed
815
        if (off<=-180) off+= 360; else if (off>180) off -= 360;
816 817 818 819

        painter.translate(area.center());
        painter.rotate(off);
        bool drewArrow = false;
dongfang's avatar
dongfang committed
820
        bool isMajor = displayTick % COMPASS_DISK_MAJORTICK == 0;
821

822
        // If heading unknown, still draw marks but no numbers.
823
        if (!isnan(this->heading) &&
824
                (displayTick==30 || displayTick==60 ||
dongfang's avatar
dongfang committed
825 826
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
827 828
                displayTick==300 || displayTick==330)
        ) {
829 830 831
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
dongfang's avatar
dongfang committed
832
            painter.setPen(scalePen);
833
            drawTextCenter(painter, s_number, smallTextSize, 0, -innerRadius*0.75);
834
        } else {
dongfang's avatar
dongfang committed
835
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
836 837 838 839 840
                if (displayTick!=0) {
                    QPainterPath markerPath(QPointF(0, -innerRadius*(1-COMPASS_DISK_MARKERHEIGHT/2)));
                    markerPath.lineTo(innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
                    markerPath.lineTo(-innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
                    markerPath.closeSubpath();
dongfang's avatar
dongfang committed
841
                    painter.setPen(scalePen);
842 843 844 845 846
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
847
                // If heading unknown, still draw marks but no N S E W.
848
                if (!isnan(this->heading) && displayTick%90 == 0) {
849
                    // Also draw a label
dongfang's avatar
dongfang committed
850 851 852
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
853
                }
dongfang's avatar
dongfang committed
854
            }
855
        }
dongfang's avatar
dongfang committed
856
        // draw the scale lines. If an arrow was drawn, stay off from it.
857

dongfang's avatar
dongfang committed
858 859
        QPointF p_start = drewArrow ? QPoint(0, -innerRadius*0.94) : QPoint(0, -innerRadius);
        QPoint p_end = isMajor ? QPoint(0, -innerRadius*0.86) : QPoint(0, -innerRadius*0.90);
860

dongfang's avatar
dongfang committed
861
        painter.setPen(scalePen);
862 863 864 865
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

dongfang's avatar
dongfang committed
866
    painter.setPen(scalePen);
867 868 869 870 871 872 873 874
    //painter.setBrush(Qt::SolidPattern);
    painter.translate(area.center());
    QPainterPath markerPath(QPointF(0, -radius-2));
    markerPath.lineTo(radius*COMPASS_DISK_MARKERWIDTH/2,  -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
    markerPath.lineTo(-radius*COMPASS_DISK_MARKERWIDTH/2, -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
    markerPath.closeSubpath();
    painter.drawPath(markerPath);

dongfang's avatar
dongfang committed
875 876 877 878 879 880 881 882 883
    qreal digitalCompassYCenter = -radius*0.52;
    qreal digitalCompassHeight = radius*0.28;

    QPointF digitalCompassBottom(0, digitalCompassYCenter+digitalCompassHeight);
    QPointF  digitalCompassAbsoluteBottom = painter.transform().map(digitalCompassBottom);

    qreal digitalCompassUpshift = digitalCompassAbsoluteBottom.y()>height() ? digitalCompassAbsoluteBottom.y()-height() : 0;

    QRectF digitalCompassRect(-radius/3, -radius*0.52-digitalCompassUpshift, radius*2/3, radius*0.28);
884
    painter.setPen(instrumentEdgePen);
dongfang's avatar
dongfang committed
885
    painter.drawRoundedRect(digitalCompassRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
886

dongfang's avatar
dongfang committed
887
    QString s_digitalCompass;
888

889
    if (isnan(this->heading))
890 891 892 893 894 895
        s_digitalCompass.sprintf("---");
    else {
    /* final safeguard for really stupid systems */
        int digitalCompassValue = static_cast<int>(qRound((double)heading)) % 360;
        s_digitalCompass.sprintf("%03d", digitalCompassValue);
    }
896

dongfang's avatar
dongfang committed
897 898 899 900
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
901

dongfang's avatar
dongfang committed
902
    drawTextCenter(painter, s_digitalCompass, largeTextSize, 0, -radius*0.38-digitalCompassUpshift);
903

904 905 906 907 908
//  dummy
//  navigationTargetBearing = 10;
//  navigationCrosstrackError = 500;

    // The CDI
909
    if (shouldDisplayNavigationData() && !isnan(navigationTargetBearing) && !isinf(navigationCrosstrackError)) {
910 911 912 913 914 915 916 917 918 919 920 921 922
        painter.resetTransform();
        painter.translate(area.center());
        // TODO : Sign might be wrong?
        // TODO : The case where error exceeds max. Truncate to max. and make that visible somehow.
        bool errorBeyondRadius = false;
        if (abs(navigationCrosstrackError) > CROSSTRACK_MAX) {
            errorBeyondRadius = true;
            navigationCrosstrackError = navigationCrosstrackError>0 ? CROSSTRACK_MAX : -CROSSTRACK_MAX;
        }

        float r = radius * CROSSTRACK_RADIUS;
        float x = navigationCrosstrackError / CROSSTRACK_MAX * r;
        float y = qSqrt(r*r - x*x); // the positive y, there is also a negative.
dongfang's avatar
dongfang committed
923

924 925 926 927 928 929 930
        float sillyHeading = 0;
        float angle = sillyHeading - navigationTargetBearing; // TODO: sign.
        painter.rotate(-angle);

        QPen pen;
        pen.setWidthF(lineWidth);
        pen.setColor(Qt::black);
931 932 933
        if(errorBeyondRadius) {
            pen.setStyle(Qt::DotLine);
        }
934 935 936 937 938 939
        painter.setPen(pen);

        painter.drawLine(QPointF(x, y), QPointF(x, -y));
    }
}

940 941
void PrimaryFlightDisplay::drawAltimeter(
        QPainter& painter,
942
        QRectF area
943
    ) {
944

945 946 947
    float primaryAltitude = altitudeAMSL;
    float secondaryAltitude = 0;

948
    painter.resetTransform();
949
    fillInstrumentBackground(painter, area);
950

dongfang's avatar
dongfang committed
951 952
    QPen pen;
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
953

dongfang's avatar
dongfang committed
954 955
    pen.setColor(Qt::white);
    painter.setPen(pen);
956

dongfang's avatar
dongfang committed
957 958
    float h = area.height();
    float w = area.width();
959
    float secondaryAltitudeBoxHeight = mediumTextSize * 2;
dongfang's avatar
dongfang committed
960
    // The height where we being with new tickmarks.
dongfang's avatar
dongfang committed
961
    float effectiveHalfHeight = h*0.45;
962 963

    // not yet implemented: Display of secondary altitude.
964
    if (!isnan(secondaryAltitude)) {
965 966
        effectiveHalfHeight-= secondaryAltitudeBoxHeight;
    }
967

dongfang's avatar
dongfang committed
968 969 970 971 972 973
    float markerHalfHeight = mediumTextSize*0.8;
    float leftEdge = instrumentEdgePen.widthF()*2;
    float rightEdge = w-leftEdge;
    float tickmarkLeft = leftEdge;
    float tickmarkRightMajor = tickmarkLeft+TAPE_GAUGES_TICKWIDTH_MAJOR*w;
    float tickmarkRightMinor = tickmarkLeft+TAPE_GAUGES_TICKWIDTH_MINOR*w;
dongfang's avatar
dongfang committed
974
    float numbersLeft = 0.42*w;
dongfang's avatar
dongfang committed
975
    float markerTip = (tickmarkLeft*2+tickmarkRightMajor)/3;
976
    float scaleCenterAltitude = isnan(primaryAltitude) ? 0 : primaryAltitude;
977 978

    // altitude scale
dongfang's avatar
dongfang committed
979 980
    float start = scaleCenterAltitude - ALTIMETER_LINEAR_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_LINEAR_SPAN/2;
981 982 983
    int firstTick = ceil(start / ALTIMETER_LINEAR_RESOLUTION) * ALTIMETER_LINEAR_RESOLUTION;
    int lastTick = floor(end / ALTIMETER_LINEAR_RESOLUTION) * ALTIMETER_LINEAR_RESOLUTION;
    for (int tickAlt = firstTick; tickAlt <= lastTick; tickAlt += ALTIMETER_LINEAR_RESOLUTION) {
dongfang's avatar
dongfang committed
984 985
        float y = (tickAlt-scaleCenterAltitude)*effectiveHalfHeight/(ALTIMETER_LINEAR_SPAN/2);
        bool isMajor = tickAlt % ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0;
986

987 988
        painter.resetTransform();
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
989 990 991 992
        pen.setColor(tickAlt<0 ? redColor : Qt::white);
        painter.setPen(pen);
        if (isMajor) {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0);
993
            QString s_alt;
dongfang's avatar
dongfang committed
994
            s_alt.sprintf("%d", abs(tickAlt));
dongfang's avatar
dongfang committed
995
            drawTextLeftCenter(painter, s_alt, mediumTextSize, numbersLeft, 0);
dongfang's avatar
dongfang committed
996 997
        } else {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0);
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
    markerPath.lineTo(markerTip+markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(rightEdge, markerHalfHeight);
    markerPath.lineTo(rightEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip+markerHalfHeight, -markerHalfHeight);
    markerPath.closeSubpath();

    painter.resetTransform();
    painter.translate(area.left(), area.center().y());
dongfang's avatar
dongfang committed
1010

dongfang's avatar
dongfang committed
1011
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
1012 1013 1014
    pen.setColor(Qt::white);
    painter.setPen(pen);

1015 1016 1017 1018
    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
1019 1020
    pen.setColor(Qt::white);
    painter.setPen(pen);
dongfang's avatar
dongfang committed
1021

1022
    QString s_alt;
1023
    if (isnan(primaryAltitude))
dongfang's avatar
dongfang committed
1024 1025 1026 1027
        s_alt.sprintf("---");
    else
        s_alt.sprintf("%3.0f", primaryAltitude);

1028
    float xCenter = (markerTip+rightEdge)/2;
1029
    drawTextCenter(painter, s_alt, mediumTextSize, xCenter, 0);
dongfang's avatar
dongfang committed
1030

1031
    // draw simple in-tape VVI.
1032 1033 1034 1035
    if (!isnan(climbRate)) {
        float vvPixHeight = -climbRate/ALTIMETER_VVI_SPAN * effectiveHalfHeight;
        if (abs (vvPixHeight) < markerHalfHeight)
            return; // hidden behind marker.
dongfang's avatar
dongfang committed
1036

1037
        float vvSign = vvPixHeight>0 ? 1 : -1; // reverse y sign
dongfang's avatar
dongfang committed
1038

1039 1040 1041 1042
        // QRectF vvRect(rightEdge - w*ALTIMETER_VVI_WIDTH, markerHalfHeight*vvSign, w*ALTIMETER_VVI_WIDTH, abs(vvPixHeight)*vvSign);
        QPointF vvArrowBegin(rightEdge - w*ALTIMETER_VVI_WIDTH/2, markerHalfHeight*vvSign);
        QPointF vvArrowEnd(rightEdge - w*ALTIMETER_VVI_WIDTH/2, vvPixHeight);
        painter.drawLine(vvArrowBegin, vvArrowEnd);
dongfang's avatar
dongfang committed
1043

1044 1045 1046
        // Yeah this is a repitition of above code but we are goigd to trash it all anyway, so no fix.
        float vvArowHeadSize = abs(vvPixHeight - markerHalfHeight*vvSign);
        if (vvArowHeadSize > w*ALTIMETER_VVI_WIDTH/3) vvArowHeadSize = w*ALTIMETER_VVI_WIDTH/3;
1047

1048
        float xcenter = rightEdge-w*ALTIMETER_VVI_WIDTH/2;
1049

1050 1051
        QPointF vvArrowHead(xcenter+vvArowHeadSize, vvPixHeight - vvSign *vvArowHeadSize);
        painter.drawLine(vvArrowHead, vvArrowEnd);
dongfang's avatar
dongfang committed
1052

1053 1054
        vvArrowHead = QPointF(xcenter-vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize);
        painter.drawLine(vvArrowHead, vvArrowEnd);
1055 1056 1057
    }

    // print secondary altitude
1058
    if (!isnan(secondaryAltitude)) {
1059 1060 1061 1062 1063 1064 1065 1066 1067
        QRectF saBox(area.x(), area.y()-secondaryAltitudeBoxHeight, w, secondaryAltitudeBoxHeight);
        painter.resetTransform();
        painter.translate(saBox.center());
        QString s_salt;
        s_salt.sprintf("%3.0f", secondaryAltitude);
        drawTextCenter(painter, s_salt, mediumTextSize, 0, 0);
    }

    // print target altitude (if applicable)
1068 1069
}

1070 1071
void PrimaryFlightDisplay::drawVelocityMeter(
        QPainter& painter,
1072
        QRectF area
1073 1074 1075
        ) {

    painter.resetTransform();
1076
    fillInstrumentBackground(painter, area);
1077 1078 1079 1080 1081 1082 1083

    QPen pen;
    pen.setWidthF(lineWidth);

    float h = area.height();
    float w = area.width();
    float effectiveHalfHeight = h*0.45;
dongfang's avatar
dongfang committed
1084
    float markerHalfHeight = mediumTextSize;
dongfang's avatar
dongfang committed
1085
    float leftEdge = instrumentEdgePen.widthF()*2;
dongfang's avatar
dongfang committed
1086 1087 1088 1089 1090 1091 1092
    float tickmarkRight = w-leftEdge;
    float tickmarkLeftMajor = tickmarkRight-w*TAPE_GAUGES_TICKWIDTH_MAJOR;
    float tickmarkLeftMinor = tickmarkRight-w*TAPE_GAUGES_TICKWIDTH_MINOR;
    float numbersRight = 0.42*w;
    float markerTip = (tickmarkLeftMajor+tickmarkRight*2)/3;

    // Select between air and ground speed:
1093
    float speed = (isAirplane() && !isnan(airSpeed)) ? airSpeed : groundSpeed;
1094
    float centerScaleSpeed = isnan(speed) ? 0 : speed;
dongfang's avatar
dongfang committed
1095 1096 1097

    float start = centerScaleSpeed - AIRSPEED_LINEAR_SPAN/2;
    float end = centerScaleSpeed + AIRSPEED_LINEAR_SPAN/2;
1098 1099 1100 1101

    int firstTick = ceil(start / AIRSPEED_LINEAR_RESOLUTION) * AIRSPEED_LINEAR_RESOLUTION;
    int lastTick = floor(end / AIRSPEED_LINEAR_RESOLUTION) * AIRSPEED_LINEAR_RESOLUTION;
    for (int tickSpeed = firstTick; tickSpeed <= lastTick; tickSpeed += AIRSPEED_LINEAR_RESOLUTION) {
dongfang's avatar
dongfang committed
1102 1103 1104 1105
        pen.setColor(tickSpeed<0 ? redColor : Qt::white);
        painter.setPen(pen);

        float y = (tickSpeed-centerScaleSpeed)*effectiveHalfHeight/(AIRSPEED_LINEAR_SPAN/2);
1106 1107
        bool hasText = tickSpeed % AIRSPEED_LINEAR_MAJOR_RESOLUTION == 0;
        painter.resetTransform();
dongfang's avatar
dongfang committed
1108

1109
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
1110

1111
        if (hasText) {
dongfang's avatar
dongfang committed
1112
            painter.drawLine(tickmarkLeftMajor, 0, tickmarkRight, 0);
1113
            QString s_speed;
dongfang's avatar
dongfang committed
1114
            s_speed.sprintf("%d", abs(tickSpeed));
dongfang's avatar
dongfang committed
1115
            drawTextRightCenter(painter, s_speed, mediumTextSize, numbersRight, 0);
dongfang's avatar
dongfang committed
1116 1117
        } else {
            painter.drawLine(tickmarkLeftMinor, 0, tickmarkRight, 0);
1118 1119 1120 1121
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
dongfang's avatar
dongfang committed
1122 1123 1124 1125
    markerPath.lineTo(markerTip-markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(leftEdge, markerHalfHeight);
    markerPath.lineTo(leftEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip-markerHalfHeight, -markerHalfHeight);
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
    markerPath.closeSubpath();

    painter.resetTransform();
    painter.translate(area.left(), area.center().y());

    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);

    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

    pen.setColor(Qt::white);
    painter.setPen(pen);
    QString s_alt;
1142
    if (isnan(speed))
dongfang's avatar
dongfang committed
1143 1144
        s_alt.sprintf("---");
    else
1145
        s_alt.sprintf("%3.1f", speed);
dongfang's avatar
dongfang committed
1146
    float xCenter = (markerTip+leftEdge)/2;
1147 1148 1149
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
}

1150 1151 1152 1153
static const int TOP = (1<<0);
static const int BOTTOM = (1<<1);
static const int LEFT = (1<<2);
static const int RIGHT = (1<<3);
dongfang's avatar
dongfang committed
1154

1155 1156 1157 1158
static const int TOP_HALF = (1<<4);
static const int BOTTOM_HALF = (1<<5);
static const int LEFT_HALF = (1<<6);
static const int RIGHT_HALF = (1<<7);
dongfang's avatar
dongfang committed
1159 1160 1161 1162 1163 1164 1165 1166 1167

void applyMargin(QRectF& area, float margin, int where) {
    if (margin < 0.01) return;

    QRectF save(area);
    qreal consumed;

    if (where & LEFT) {
        area.setX(save.x() + (consumed = margin));
1168
    } else if (where & LEFT_HALF) {
dongfang's avatar
dongfang committed
1169 1170 1171 1172 1173 1174 1175
        area.setX(save.x() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & RIGHT) {
        area.setWidth(save.width()-consumed-margin);
1176
    } else if (where & RIGHT_HALF) {
dongfang's avatar
dongfang committed
1177 1178 1179 1180 1181 1182 1183
        area.setWidth(save.width()-consumed-margin/2);
    } else {
        area.setWidth(save.width()-consumed);
    }

    if (where & TOP) {
        area.setY(save.y() + (consumed = margin));
1184
    } else if (where & TOP_HALF) {
dongfang's avatar
dongfang committed
1185 1186 1187 1188 1189 1190 1191
        area.setY(save.y() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & BOTTOM) {
        area.setHeight(save.height()-consumed-margin);
1192
    } else if (where & BOTTOM_HALF) {
dongfang's avatar
dongfang committed
1193 1194 1195 1196 1197 1198 1199
        area.setHeight(save.height()-consumed-margin/2);
    } else {
        area.setHeight(save.height()-consumed);
    }
}

void setMarginsForInlineLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
1200 1201 1202 1203
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_HALF);
    applyMargin(panel2, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel3, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel4, margin, BOTTOM|LEFT_HALF|RIGHT);
1204 1205
}

dongfang's avatar
dongfang committed
1206
void setMarginsForCornerLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
1207 1208 1209 1210
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_HALF);
    applyMargin(panel2, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel3, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel4, margin, BOTTOM|LEFT_HALF|RIGHT);
dongfang's avatar
dongfang committed
1211 1212 1213 1214
}

inline qreal tapesGaugeWidthFor(qreal containerWidth, qreal preferredAIWidth) {
    qreal result = (containerWidth - preferredAIWidth) / 2.0f;
dongfang's avatar
dongfang committed
1215
    qreal minimum = containerWidth / 5.5f;
dongfang's avatar
dongfang committed
1216 1217 1218 1219 1220
    if (result < minimum) result = minimum;
    return result;
}

void PrimaryFlightDisplay::doPaint() {
1221 1222 1223 1224 1225
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);

dongfang's avatar
dongfang committed
1226
    qreal margin = height()/100.0f;
1227

1228 1229 1230 1231 1232
    // The AI centers on this area.
    QRectF AIMainArea;
    // The AI paints on this area. It should contain the AIMainArea.
    QRectF AIPaintArea;

dongfang's avatar
dongfang committed
1233 1234 1235 1236 1237 1238 1239
    QRectF compassArea;
    QRectF altimeterArea;
    QRectF velocityMeterArea;
    QRectF sensorsStatsArea;
    QRectF linkStatsArea;
    QRectF sysStatsArea;
    QRectF missionStatsArea;
1240 1241

    painter.fillRect(rect(), Qt::black);
dongfang's avatar
dongfang committed
1242 1243
    qreal tapeGaugeWidth;

dongfang's avatar
dongfang committed
1244 1245 1246
    qreal compassHalfSpan = 180;
    float compassAIIntrusion = 0;

dongfang's avatar
dongfang committed
1247
    switch(layout) {
1248
    /*
dongfang's avatar
dongfang committed
1249 1250 1251 1252 1253 1254
    case FEATUREPANELS_IN_CORNERS: {
        tapeGaugeWidth = tapesGaugeWidthFor(width(), height());

        // A layout optimal for a container wider than it is high.
        // The AI gets full height and if tapes are transparent, also full width. If tapes are opague, then
        // the AI gets a width to be perfectly square.
1255 1256
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1257
                    0,
1258
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth * 2: width(),
dongfang's avatar
dongfang committed
1259 1260
                    height());

1261 1262
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1263 1264 1265 1266
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.

        qreal sidePanelsHeight = height();

1267
        altimeterArea = QRectF(AIMainArea.right(), height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);
dongfang's avatar
dongfang committed
1268 1269 1270
        velocityMeterArea = QRectF (0, height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);

        sensorsStatsArea = QRectF(0, 0, tapeGaugeWidth, sidePanelsHeight/5);
1271
        linkStatsArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1272
        sysStatsArea = QRectF(0, sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
1273
        missionStatsArea =QRectF(AIMainArea.right(), sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1274

1275 1276
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1277 1278 1279 1280
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForCornerLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1281

dongfang's avatar
dongfang committed
1282
        // Compass is inside the AI ans within its margins also.
1283 1284
        compassArea = QRectF(AIMainArea.x()+AIMainArea.width()*0.60, AIMainArea.y()+AIMainArea.height()*0.80,
                             AIMainArea.width()/2, AIMainArea.width()/2);
dongfang's avatar
dongfang committed
1285 1286
        break;
    }
1287

dongfang's avatar
dongfang committed
1288 1289 1290 1291
    case FEATUREPANELS_AT_BOTTOM: {
        // A layout for containers with about the same width and height.
        // qreal minor = min(width(), height());
        // qreal major = max(width(), height());
1292

dongfang's avatar
dongfang committed
1293
        qreal aiheight = height()*4.0f/5;
1294

dongfang's avatar
dongfang committed
1295
        tapeGaugeWidth = tapesGaugeWidthFor(width(), aiheight);
1296

1297 1298
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1299
                    0,
1300
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth*2 : width(),
dongfang's avatar
dongfang committed
1301
                    aiheight);
1302

1303 1304
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1305
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.
1306
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1307
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1308

dongfang's avatar
dongfang committed
1309 1310
        qreal panelsHeight = height() / 5.0f;
        qreal panelsWidth = width() / 4.0f;
1311

1312 1313 1314 1315
        sensorsStatsArea = QRectF(0, AIMainArea.bottom(), panelsWidth, panelsHeight);
        linkStatsArea = QRectF(panelsWidth, AIMainArea.bottom(), panelsWidth, panelsHeight);
        sysStatsArea = QRectF(panelsWidth*2, AIMainArea.bottom(), panelsWidth, panelsHeight);
        missionStatsArea =QRectF(panelsWidth*3, AIMainArea.bottom(), panelsWidth, panelsHeight);
1316

1317 1318
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1319 1320 1321 1322
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1323

dongfang's avatar
dongfang committed
1324
        // Compass is inside the AI ans within its margins also.
1325 1326
        compassArea = QRectF(AIMainArea.x()+AIMainArea.width()*0.60, AIMainArea.y()+AIMainArea.height()*0.80,
                             AIMainArea.width()/2, AIMainArea.width()/2);
dongfang's avatar
dongfang committed
1327 1328
        break;
    }
1329

1330 1331 1332
    */

    case COMPASS_INTEGRATED: {
dongfang's avatar
dongfang committed
1333
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());
1334
        qreal aiheight = height();
dongfang's avatar
dongfang committed
1335 1336
        qreal aiwidth = width()-tapeGaugeWidth*2;
        if (aiheight > aiwidth) aiheight = aiwidth;
1337 1338 1339 1340

        AIMainArea = QRectF(
                    tapeGaugeWidth,
                    0,
dongfang's avatar
dongfang committed
1341
                    aiwidth,
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
                    aiheight);

        AIPaintArea = QRectF(
                    0,
                    0,
                    width(),
                    height());

        // Tape gauges get so much width that the AI area not covered by them is perfectly square.
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);

        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }

dongfang's avatar
dongfang committed
1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
        qreal compassRelativeWidth = 0.75;
        qreal compassBottomMargin = 0.78;

        qreal compassSize = compassRelativeWidth  * AIMainArea.width();  // Diameter is this times the width.

        qreal compassCenterY;
        //if (heightSurplus >= 0) compassCenterY =  AIMainArea.bottom() + compassSize/2;
        compassCenterY = AIMainArea.bottom() + compassSize / 4;

        if (height() - compassCenterY > AIMainArea.width()/2*compassBottomMargin)
            compassCenterY = height()-AIMainArea.width()/2*compassBottomMargin;

        // TODO: This is bad style...
        compassCenterY = (compassCenterY * 2 + AIMainArea.bottom() + compassSize / 4) / 3;

        compassArea = QRectF(AIMainArea.x()+(1-compassRelativeWidth)/2*AIMainArea.width(),
                             compassCenterY-compassSize/2,
                             compassSize,
                             compassSize);

        if (height()-compassCenterY < compassSize/2) {
            compassHalfSpan = acos((compassCenterY-height())*2/compassSize) * 180/M_PI + COMPASS_DISK_RESOLUTION;
            if (compassHalfSpan > 180) compassHalfSpan = 180;
        }

        compassAIIntrusion = compassSize/2 + AIMainArea.bottom() - compassCenterY;
        if (compassAIIntrusion<0) compassAIIntrusion = 0;

1389 1390
        break;
    }
dongfang's avatar
dongfang committed
1391 1392 1393 1394
    case COMPASS_SEPARATED: {
        // A layout for containers higher than their width.
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());

1395 1396
        qreal aiheight = width() - tapeGaugeWidth*2;
        qreal panelsHeight = 0;
dongfang's avatar
dongfang committed
1397

1398 1399
        AIMainArea = QRectF(
                    tapeGaugeWidth,
dongfang's avatar
dongfang committed
1400
                    0,
1401
                    width()-tapeGaugeWidth*2,
dongfang's avatar
dongfang committed
1402 1403
                    aiheight);

1404 1405 1406 1407 1408 1409 1410
        AIPaintArea = style == OVERLAY_HSI ?
                    QRectF(
                    0,
                    0,
                    width(),
                    height() - panelsHeight) : AIMainArea;

dongfang's avatar
dongfang committed
1411
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1412
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1413

1414
        /*
dongfang's avatar
dongfang committed
1415
        qreal panelsWidth = width() / 4.0f;
dongfang's avatar
dongfang committed
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
        if(remainingHeight > width()) {
            // very tall layout, place panels below compass.
            sensorsStatsArea = QRectF(0, height()-panelsHeight, panelsWidth, panelsHeight);
            linkStatsArea = QRectF(panelsWidth, height()-panelsHeight, panelsWidth, panelsHeight);
            sysStatsArea = QRectF(panelsWidth*2, height()-panelsHeight, panelsWidth, panelsHeight);
            missionStatsArea =QRectF(panelsWidth*3, height()-panelsHeight, panelsWidth, panelsHeight);
            if (style == OPAGUE_TAPES) {
                setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
            }
            compassCenter = QPoint(width()/2, (AIArea.bottom()+height()-panelsHeight)/2);
dongfang's avatar
dongfang committed
1426
            maxCompassDiam = fmin(width(), height()-AIArea.height()-panelsHeight);
dongfang's avatar
dongfang committed
1427 1428
        } else {
            // Remaining part is wider than high; place panels in corners around compass
1429
            // Naaah it is really ugly, do not do that.
dongfang's avatar
dongfang committed
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
            sensorsStatsArea = QRectF(0, AIArea.bottom(), panelsWidth, panelsHeight);
            linkStatsArea = QRectF(width()-panelsWidth, AIArea.bottom(), panelsWidth, panelsHeight);
            sysStatsArea = QRectF(0, height()-panelsHeight, panelsWidth, panelsHeight);
            missionStatsArea =QRectF(width()-panelsWidth, height()-panelsHeight, panelsWidth, panelsHeight);
            if (style == OPAGUE_TAPES) {
                setMarginsForCornerLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
            }
            compassCenter = QPoint(width()/2, (AIArea.bottom()+height())/2);
            // diagonal between 2 panel corners
            qreal xd = width()-panelsWidth*2;
            qreal yd = height()-panelsHeight - AIArea.bottom();
dongfang's avatar
dongfang committed
1441 1442 1443 1444 1445 1446 1447 1448
            maxCompassDiam = qSqrt(xd*xd + yd*yd);
            if (maxCompassDiam > remainingHeight) {
                maxCompassDiam = width() - panelsWidth*2;
                if (maxCompassDiam > remainingHeight) {
                    // If still too large, lower.
                    // compassCenter.setY();
                }
            }
dongfang's avatar
dongfang committed
1449
        }
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
        */
        /*
        sensorsStatsArea = QRectF(0, height()-panelsHeight, panelsWidth, panelsHeight);
        linkStatsArea = QRectF(panelsWidth, height()-panelsHeight, panelsWidth, panelsHeight);
        sysStatsArea = QRectF(panelsWidth*2, height()-panelsHeight, panelsWidth, panelsHeight);
        missionStatsArea =QRectF(panelsWidth*3, height()-panelsHeight, panelsWidth, panelsHeight);
        */

        QPoint compassCenter = QPoint(width()/2, AIMainArea.bottom()+width()/2);
        qreal compassDiam = width() * 0.8;
dongfang's avatar
dongfang committed
1460
        compassArea = QRectF(compassCenter.x()-compassDiam/2, compassCenter.y()-compassDiam/2, compassDiam, compassDiam);
dongfang's avatar
dongfang committed
1461 1462 1463
        break;
    }
    }
1464

dongfang's avatar
dongfang committed
1465
    bool hadClip = painter.hasClipping();
dongfang's avatar
dongfang committed
1466

dongfang's avatar
dongfang committed
1467
    painter.setClipping(true);
1468
    painter.setClipRect(AIPaintArea);
1469

1470
    drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea);
dongfang's avatar
dongfang committed
1471
    drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion);
1472
    drawAIAirframeFixedFeatures(painter, AIMainArea);
1473

1474 1475 1476
   // if(layout ==COMPASS_SEPARATED)
        //drawSeparateCompassDisk(painter, compassArea);
   // else
dongfang's avatar
dongfang committed
1477
        drawAICompassDisk(painter, compassArea, compassHalfSpan);
1478

dongfang's avatar
dongfang committed
1479
    painter.setClipping(hadClip);
1480

1481
    drawAltimeter(painter, altimeterArea);
1482

1483
    drawVelocityMeter(painter, velocityMeterArea);
1484

1485
    /*
dongfang's avatar
dongfang committed
1486 1487 1488 1489
    drawSensorsStatsPanel(painter, sensorsStatsArea);
    drawLinkStatsPanel(painter, linkStatsArea);
    drawSysStatsPanel(painter, sysStatsArea);
    drawMissionStatsPanel(painter, missionStatsArea);
1490 1491
    */
    /*
dongfang's avatar
dongfang committed
1492 1493 1494
    if (style == OPAGUE_TAPES) {
        drawInstrumentBackground(painter, AIArea);
    }
1495
    */
dongfang's avatar
dongfang committed
1496

dongfang's avatar
dongfang committed
1497
    painter.end();
1498
}
1499 1500

void PrimaryFlightDisplay:: createActions() {}