PrimaryFlightDisplay.cpp 59.1 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 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 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
#define SEPARATE_COMPASS_ASPECTRATIO (3.0f/4.0f)

#define LINEWIDTH 0.0036f

//#define TAPES_TEXT_SIZE 0.028
//#define AI_TEXT_SIZE 0.040
//#define AI_TEXT_MIN_PIXELS 12
//#define AI_TEXT_MAX_PIXELS 36
//#define PANELS_TEXT_SIZE 0.030
//#define COMPASS_SCALE_TEXT_SIZE 0.16

#define SMALL_TEXT_SIZE 0.03f
#define MEDIUM_TEXT_SIZE (SMALL_TEXT_SIZE*1.2f)
#define LARGE_TEXT_SIZE (MEDIUM_TEXT_SIZE*1.2f)

#define SHOW_ZERO_ON_SCALES true

// all in units of display height
#define ROLL_SCALE_RADIUS 0.42f
#define ROLL_SCALE_TICKMARKLENGTH 0.04f
#define ROLL_SCALE_MARKERWIDTH 0.06f
#define ROLL_SCALE_MARKERHEIGHT 0.04f
// scale max. degrees
#define ROLL_SCALE_RANGE 60

// fraction of height to translate for each degree of pitch.
#define PITCHTRANSLATION 65.0
// 10 degrees for each line
#define PITCH_SCALE_RESOLUTION 5
#define PITCH_SCALE_MAJORWIDTH 0.1
#define PITCH_SCALE_MINORWIDTH 0.066

// 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.
#define PITCH_SCALE_WIDTHREDUCTION_FROM 30
#define PITCH_SCALE_WIDTHREDUCTION 0.3

#define PITCH_SCALE_HALFRANGE 15

// 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.

#define COMPASS_DISK_MAJORTICK 10
#define COMPASS_DISK_ARROWTICK 45
#define COMPASS_DISK_MAJORLINEWIDTH 0.006
#define COMPASS_DISK_MINORLINEWIDTH 0.004
#define COMPASS_DISK_RESOLUTION 10
#define COMPASS_SEPARATE_DISK_RESOLUTION 5
#define COMPASS_DISK_MARKERWIDTH 0.2
#define COMPASS_DISK_MARKERHEIGHT 0.133

#define CROSSTRACK_MAX 1000
#define CROSSTRACK_RADIUS 0.6

#define TAPE_GAUGES_TICKWIDTH_MAJOR 0.25
#define TAPE_GAUGES_TICKWIDTH_MINOR 0.15

// The altitude difference between top and bottom of scale
#define ALTIMETER_LINEAR_SPAN 50
// every 5 meters there is a tick mark
#define ALTIMETER_LINEAR_RESOLUTION 5
// every 10 meters there is a number
#define ALTIMETER_LINEAR_MAJOR_RESOLUTION 10

// Projected: An experiment. Make tape appear projected from a cylinder, like a French "drum" style gauge.
// The altitude difference between top and bottom of scale
#define ALTIMETER_PROJECTED_SPAN 50
// every 5 meters there is a tick mark
#define ALTIMETER_PROJECTED_RESOLUTION 5
// every 10 meters there is a number
#define ALTIMETER_PROJECTED_MAJOR_RESOLUTION 10
// min. and max. vertical velocity
//#define ALTIMETER_PROJECTED

// min. and max. vertical velocity
#define ALTIMETER_VVI_SPAN 5
#define ALTIMETER_VVI_WIDTH 0.2

// Now the same thing for airspeed!
#define AIRSPEED_LINEAR_SPAN 15
#define AIRSPEED_LINEAR_RESOLUTION 1
#define AIRSPEED_LINEAR_MAJOR_RESOLUTION 5

#define UNKNOWN_BATTERY -1
#define UNKNOWN_ATTITUDE 0
#define UNKNOWN_ALTITUDE -1000
#define UNKNOWN_SPEED -1
#define UNKNOWN_COUNT -1
#define UNKNOWN_GPSFIXTYPE -1

107 108
/*
 *@TODO:
dongfang's avatar
dongfang committed
109
 * global fixed pens (and painters too?)
110 111 112
 * repaint on demand multiple canvases
 * multi implementation with shared model class
 */
dongfang's avatar
dongfang committed
113
double PrimaryFlightDisplay_round(double value, int digits=0)
114
{
dongfang's avatar
dongfang committed
115
    return floor(value * pow(10, digits) + 0.5) / pow(10, digits);
116
}
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

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),

133
    uas(NULL),
134

135 136 137 138
    altimeterMode(GPS_MAIN),
    altimeterFrame(ASL),
    speedMode(GROUND_MAIN),

139 140 141 142
    roll(UNKNOWN_ATTITUDE),
    pitch(UNKNOWN_ATTITUDE),
    //    heading(NAN),
    heading(UNKNOWN_ATTITUDE),
143
    primaryAltitude(UNKNOWN_ALTITUDE),
144 145
    GPSAltitude(UNKNOWN_ALTITUDE),
    aboveHomeAltitude(UNKNOWN_ALTITUDE),
146
    primarySpeed(UNKNOWN_SPEED),
147
    groundspeed(UNKNOWN_SPEED),
148
    verticalVelocity(UNKNOWN_ALTITUDE),
149

150 151
    font("Bitstream Vera Sans"),
    refreshTimer(new QTimer(this)),
dongfang's avatar
dongfang committed
152

153 154 155
    navigationCrosstrackError(INFINITY),
    navigationTargetBearing(UNKNOWN_ATTITUDE),

156 157
    layout(COMPASS_INTEGRATED),
    style(OVERLAY_HSI),
158

dongfang's avatar
dongfang committed
159
    redColor(QColor::fromHsvF(0, 0.75, 0.9)),
dongfang's avatar
dongfang committed
160 161 162 163 164 165 166
    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
167
    //    AIEdgePen(QColor::fromHsvF(0, 0, 0.65, 0.5)),
168

dongfang's avatar
dongfang committed
169 170 171
    instrumentBackground(QColor::fromHsvF(0, 0, 0.3, 0.3)),
    instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0))
{
172 173 174 175 176 177 178 179 180
    setMinimumSize(120, 80);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    // Connect with UAS
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
    if (UASManager::instance()->getActiveUAS() != NULL) setActiveUAS(UASManager::instance()->getActiveUAS());

    // Refresh timer
    refreshTimer->setInterval(updateInterval);
dongfang's avatar
dongfang committed
181
    //    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
182
    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
}

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
214 215 216 217 218 219 220 221 222 223
qreal constrain(qreal value, qreal min, qreal max) {
    if (value<min) value=min;
    else if(value>max) value=max;
    return value;
}

void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
    QWidget::resizeEvent(e);

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

dongfang's avatar
dongfang committed
226
    lineWidth = constrain(size*LINEWIDTH, 1, 6);
dongfang's avatar
dongfang committed
227 228 229 230 231 232 233 234 235
    fineLineWidth = constrain(size*LINEWIDTH*2/3, 1, 2);

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

    smallTestSize = size * SMALL_TEXT_SIZE;
    mediumTextSize = size * MEDIUM_TEXT_SIZE;
    largeTextSize = size * LARGE_TEXT_SIZE;

dongfang's avatar
dongfang committed
236 237
    /*
     * Try without layout Change-O-Matic. It was too complicated.
238 239
    qreal aspect = e->size().width() / e->size().height();
    if (aspect <= SEPARATE_COMPASS_ASPECTRATIO)
dongfang's avatar
dongfang committed
240 241
        layout = COMPASS_SEPARATED;
    else
242
        layout = COMPASS_INTEGRATED;
dongfang's avatar
dongfang committed
243
    */
244
    // qDebug("Width %d height %d decision %d", e->size().width(), e->size().height(), layout);
dongfang's avatar
dongfang committed
245
}
246

dongfang's avatar
dongfang committed
247 248 249 250 251 252 253 254 255
void PrimaryFlightDisplay::paintEvent(QPaintEvent *event)
{
    // Event is not needed
    // the event is ignored as this widget
    // is refreshed automatically
    Q_UNUSED(event);
    //makeDummyData();
    doPaint();
}
256

dongfang's avatar
dongfang committed
257 258 259
/*
 * Interface towards qgroundcontrol
 */
260 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)
{
    if (this->uas != NULL) {
        // Disconnect any previously connected active MAV
        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)));
270 271 272 273 274 275 276
        disconnect(this->uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
        disconnect(this->uas, SIGNAL(primarySpeedChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimarySpeed(UASInterface*,double,quint64)));
        disconnect(this->uas, SIGNAL(gpsSpeedChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSSpeed(UASInterface*,double,quint64)));
        disconnect(this->uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this, SLOT(updateClimbRate(UASInterface*, AltitudeMeasurementSource, double, quint64)));
        disconnect(this->uas, SIGNAL(primaryAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimaryAltitude(UASInterface*, double, quint64)));
        disconnect(this->uas, SIGNAL(gpsAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSAltitude(UASInterface*, double, quint64)));
        disconnect(this->uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
277

278 279 280 281 282 283
        //disconnect(this->uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, 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*)));
        //disconnect(this->uas, SIGNAL(armingChanged(bool)), this, SLOT(updateArmed(bool)));
        //disconnect(this->uas, SIGNAL(satelliteCountChanged(double, QString)), this, SLOT(updateSatelliteCount(double, QString)));
284
        //disconnect(this->uas, SIGNAL(localizationChanged(UASInterface* uas, int fix)), this, SLOT(updateGPSFixType(UASInterface*,int)));
285 286 287 288 289 290 291
    }

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

293 294 295 296 297 298
        //connect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, 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(armingChanged(bool)), this, SLOT(updateArmed(bool)));
        //connect(uas, SIGNAL(satelliteCountChanged(double, QString)), this, SLOT(updateSatelliteCount(double, QString)));
299

dongfang's avatar
dongfang committed
300
        //connect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
301
        //connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
302
        connect(uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
303 304 305 306 307 308
        connect(uas, SIGNAL(primarySpeedChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimarySpeed(UASInterface*,double,quint64)));
        connect(uas, SIGNAL(gpsSpeedChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSSpeed(UASInterface*,double,quint64)));
        connect(uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this, SLOT(updateClimbRate(UASInterface*, AltitudeMeasurementSource, double, quint64)));
        connect(uas, SIGNAL(primaryAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimaryAltitude(UASInterface*, double, quint64)));
        connect(uas, SIGNAL(gpsAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSAltitude(UASInterface*, double, quint64)));
        connect(uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
309 310 311 312 313 314 315 316 317 318 319 320

        // 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);
    if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
    {
dongfang's avatar
dongfang committed
321
        // TODO: Units conversion? // Called from UAS.cc l. 646
322 323
        this->roll = roll * (180.0 / M_PI);
        this->pitch = pitch * (180.0 / M_PI);
dongfang's avatar
dongfang committed
324 325 326
        yaw = yaw * (180.0 / M_PI);
        if (yaw<0) yaw+=360;
        this->heading = yaw;
327 328
    }
    // TODO: Else-part. We really should have an "attitude bad or unknown" indication instead of just freezing.
329

330
    //qDebug("r,p,y: %f,%f,%f", roll, pitch, yaw);
331 332 333 334 335 336 337 338 339 340 341
}

/*
 * TODO! Implementation or removal of this.
 * Currently a dummy.
 */
void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(component);
    Q_UNUSED(timestamp);
dongfang's avatar
dongfang committed
342
    // Called from UAS.cc l. 616
dongfang's avatar
dongfang committed
343
    if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw)) {
dongfang's avatar
dongfang committed
344 345 346 347 348 349
        this->roll = roll * (180.0 / M_PI);
        this->pitch = pitch * (180.0 / M_PI);
        yaw = yaw * (180.0 / M_PI);
        if (yaw<0) yaw+=360;
        this->heading = yaw;
    }
350
    // qDebug("(2) r,p,y: %f,%f,%f", roll, pitch, yaw);
351

352 353 354 355 356 357
}

/*
 * TODO! Examine what data comes with this call, should we consider it airspeed, ground speed or
 * should we not consider it at all?
 */
358 359 360 361 362 363 364 365 366 367
void PrimaryFlightDisplay::updatePrimarySpeed(UASInterface* uas, double speed, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

    primarySpeed = speed;
    didReceivePrimarySpeed = true;
}

void PrimaryFlightDisplay::updateGPSSpeed(UASInterface* uas, double speed, quint64 timestamp)
368 369 370 371
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

372 373
    groundspeed = speed;
    if (!didReceivePrimarySpeed)
374 375
        primarySpeed = speed;
}
376

377
void PrimaryFlightDisplay::updateClimbRate(UASInterface* uas, double climbRate, quint64 timestamp) {
378 379 380 381 382
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    verticalVelocity = climbRate;
}

383 384 385 386 387 388 389 390
void PrimaryFlightDisplay::updatePrimaryAltitude(UASInterface* uas, double altitude, quint64 timestamp) {
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    primaryAltitude = altitude;
    didReceivePrimaryAltitude = true;
}

void PrimaryFlightDisplay::updateGPSAltitude(UASInterface* uas, double altitude, quint64 timestamp) {
391 392
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
393 394
    GPSAltitude = altitude;
    if (!didReceivePrimaryAltitude)
395
        primaryAltitude = altitude;
396 397
}

398 399 400 401 402 403 404 405
void PrimaryFlightDisplay::updateNavigationControllerErrors(UASInterface* uas, double altitudeError, double speedError, double xtrackError) {
    Q_UNUSED(uas);
    this->navigationAltitudeError = altitudeError;
    this->navigationSpeedError = speedError;
    this->navigationCrosstrackError = xtrackError;
}


406 407 408
/*
 * Private and such
 */
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

// 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;
    }
}

425 426 427 428 429 430 431
// 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;
}

432 433 434
void PrimaryFlightDisplay::drawTextCenter (
        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::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
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::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
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::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
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
    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
499
        float pixelSize,
500 501 502
        float x,
        float y)
{
dongfang's avatar
dongfang committed
503
    font.setPixelSize(pixelSize);
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
    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
524 525 526 527 528 529 530
void PrimaryFlightDisplay::fillInstrumentOpagueBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentOpagueBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

531
qreal pitchAngleToTranslation(qreal viewHeight, float pitch) {
532
    return pitch * viewHeight / PITCHTRANSLATION;
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
}

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
548
    QPen pen;
dongfang's avatar
dongfang committed
549
    pen.setWidthF(lineWidth * 1.5);
dongfang's avatar
dongfang committed
550 551
    pen.setColor(redColor);
    painter.setPen(pen);
552

dongfang's avatar
dongfang committed
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
    float length = 0.15;
    float side = 0.5;
    // 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));

    float rel = length/qSqrt(2);
    // 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);
568 569 570 571
    markerPath.closeSubpath();
    painter.drawPath(markerPath);
}

572 573 574 575 576 577 578 579 580 581 582 583 584 585
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;
}

586 587
void PrimaryFlightDisplay::drawAIGlobalFeatures(
        QPainter& painter,
588 589
        QRectF mainArea,
        QRectF paintArea) {
590 591

    painter.resetTransform();
592
    painter.translate(mainArea.center());
593

594 595
    qreal pitchPixels = pitchAngleToTranslation(mainArea.height(), pitch);
    qreal gradientEnd = pitchAngleToTranslation(mainArea.height(), 60);
596

597 598
    //painter.rotate(-roll);
    //painter.translate(0, pitchPixels);
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
    //    QTransform forwardTransform;
    //forwardTransform.translate(mainArea.center().x(), mainArea.center().y());
    painter.rotate(-roll);
    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);
625 626
    skyPath.closeSubpath();

627 628
    // TODO: The gradient is wrong now.
    QLinearGradient skyGradient(0, -gradientEnd, 0, 0);
629 630 631 632 633
    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);

634 635 636 637
    QPainterPath groundPath(hzonRight);
    groundPath.lineTo(maxx, maxy);
    groundPath.lineTo(minx, maxy);
    groundPath.lineTo(hzonLeft);
638 639
    groundPath.closeSubpath();

640
    QLinearGradient groundGradient(0, gradientEnd, 0, 0);
641 642 643 644 645
    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
646 647 648 649 650
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(greenColor);
    painter.setPen(pen);

651 652
    QPointF start(-mainArea.width(), 0);
    QPoint end(mainArea.width(), 0);
653 654 655 656 657 658
    painter.drawLine(start, end);
}

void PrimaryFlightDisplay::drawPitchScale(
        QPainter& painter,
        QRectF area,
dongfang's avatar
dongfang committed
659
        float intrusion,
660 661 662 663
        bool drawNumbersLeft,
        bool drawNumbersRight
        ) {

dongfang's avatar
dongfang committed
664 665 666
    // The area should be quadratic but if not width is the major size.
    qreal w = area.width();
    if (w<area.height()) w = area.height();
667

dongfang's avatar
dongfang committed
668 669 670 671
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
672 673 674 675 676 677 678 679 680

    QTransform savedTransform = painter.transform();

    // find the mark nearest center
    int snap = round(pitch/PITCH_SCALE_RESOLUTION)*PITCH_SCALE_RESOLUTION;
    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;
681 682 683 684 685 686 687 688 689 690 691 692
        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
693 694 695 696 697 698

        float shift = pitchAngleToTranslation(w, pitch-degrees);

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

699
        painter.translate(0, shift);
dongfang's avatar
dongfang committed
700 701
        QPointF start(-linewidth*w, 0);
        QPointF end(linewidth*w, 0);
702 703 704 705 706 707 708 709 710
        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) {
                QString s_number; //= QString("%d").arg(degrees);
                s_number.sprintf("%d", displayDegrees);
dongfang's avatar
dongfang committed
711 712
                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);
713 714 715 716 717 718 719 720 721 722 723 724 725
            }
        }

        painter.setTransform(savedTransform);
    }
}

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

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

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

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

739
    qreal _size = w * ROLL_SCALE_RADIUS*2;
dongfang's avatar
dongfang committed
740
    QRectF arcArea(-_size/2, - _size/2, _size, _size);
741
    painter.drawArc(arcArea, (90-ROLL_SCALE_RANGE)*16, ROLL_SCALE_RANGE*2*16);
dongfang's avatar
dongfang committed
742
    // painter.drawEllipse(QPoint(0,0),200,200);
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
    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) {
762
                drawTextCenterBottom(painter, s_number, mediumTextSize, 0, -(ROLL_SCALE_RADIUS+ROLL_SCALE_TICKMARKLENGTH*1.7)*w);
763 764 765 766 767 768 769
            }
        }
    }
}

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

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

dongfang's avatar
dongfang committed
784 785 786
void PrimaryFlightDisplay::drawAICompassDisk(QPainter& painter, QRectF area, float halfspan) {
    float start = heading - halfspan;
    float end = heading + halfspan;
dongfang's avatar
dongfang committed
787

dongfang's avatar
dongfang committed
788 789
    int firstTick = ceil(start / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
    int lastTick = floor(end / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
790 791 792 793

    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();
dongfang's avatar
dongfang committed
794 795
    painter.setBrush(instrumentBackground);
    painter.setPen(instrumentEdgePen);
796 797 798
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
799 800
    QPen scalePen(Qt::black);
    scalePen.setWidthF(fineLineWidth);
801

dongfang's avatar
dongfang committed
802
    for (int tickYaw = firstTick; tickYaw <= lastTick; tickYaw += COMPASS_DISK_RESOLUTION) {
803 804 805 806 807 808 809
        int displayTick = tickYaw;
        if (displayTick < 0) displayTick+=360;
        else if (displayTick>=360) displayTick-=360;

        // yaw is in center.
        float off = tickYaw - heading;
        // wrap that to ]-180..180]
dongfang's avatar
dongfang committed
810
        if (off<=-180) off+= 360; else if (off>180) off -= 360;
811 812 813 814

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

        if (displayTick==30 || displayTick==60 ||
dongfang's avatar
dongfang committed
818 819
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
820 821 822 823
                displayTick==300 || displayTick==330) {
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
dongfang's avatar
dongfang committed
824 825
            painter.setPen(scalePen);
            drawTextCenter(painter, s_number, /*COMPASS_SCALE_TEXT_SIZE*radius*/ smallTestSize, 0, -innerRadius*0.75);
826
        } else {
dongfang's avatar
dongfang committed
827
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
828 829 830 831 832
                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
833
                    painter.setPen(scalePen);
834 835 836 837 838 839 840
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
                if (displayTick%90 == 0) {
                    // Also draw a label
dongfang's avatar
dongfang committed
841 842 843
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
844
                }
dongfang's avatar
dongfang committed
845
            }
846
        }
dongfang's avatar
dongfang committed
847
        // draw the scale lines. If an arrow was drawn, stay off from it.
848

dongfang's avatar
dongfang committed
849 850
        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);
851

dongfang's avatar
dongfang committed
852
        painter.setPen(scalePen);
853 854 855 856
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

dongfang's avatar
dongfang committed
857
    painter.setPen(scalePen);
858 859 860 861 862 863 864 865
    //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
866 867 868 869 870 871 872 873 874
    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);
875
    painter.setPen(instrumentEdgePen);
dongfang's avatar
dongfang committed
876
    painter.drawRoundedRect(digitalCompassRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
877 878

    /* final safeguard for really stupid systems */
dongfang's avatar
dongfang committed
879
    int digitalCompassValue = static_cast<int>(round(heading)) % 360;
880

dongfang's avatar
dongfang committed
881 882
    QString s_digitalCompass;
    s_digitalCompass.sprintf("%03d", digitalCompassValue);
883

dongfang's avatar
dongfang committed
884 885 886 887
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
888

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

891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
//  dummy
//  navigationTargetBearing = 10;
//  navigationCrosstrackError = 500;

    // The CDI
    if (shouldDisplayNavigationData() && navigationTargetBearing != UNKNOWN_ATTITUDE && !isinf(navigationCrosstrackError)) {
        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
910

911 912 913 914 915 916 917 918 919 920 921 922 923 924
        float sillyHeading = 0;
        float angle = sillyHeading - navigationTargetBearing; // TODO: sign.
        painter.rotate(-angle);

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

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

/*
dongfang's avatar
dongfang committed
925 926 927 928 929 930 931 932 933 934 935 936 937
void PrimaryFlightDisplay::drawSeparateCompassDisk(QPainter& painter, QRectF area) {
    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();

    painter.setBrush(instrumentOpagueBackground);
    painter.setPen(instrumentEdgePen);
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

    QPen scalePen(Qt::white);
    scalePen.setWidthF(fineLineWidth);

938
    for (int displayTick=0; displayTick<360; displayTick+=COMPASS_SEPARATE_DISK_RESOLUTION) {
dongfang's avatar
dongfang committed
939 940 941 942 943 944 945 946 947 948 949 950 951 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 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
        // yaw is in center.
        float off = displayTick - heading;
        // wrap that to ]-180..180]
        if (off<=-180) off+= 360; else if (off>180) off -= 360;

        painter.translate(area.center());
        painter.rotate(off);
        bool drewArrow = false;
        bool isMajor = displayTick % COMPASS_DISK_MAJORTICK == 0;

        if (displayTick==30 || displayTick==60 ||
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
                displayTick==300 || displayTick==330) {
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
            painter.setPen(scalePen);
            drawTextCenter(painter, s_number, mediumTextSize, 0, -innerRadius*0.75);
        } else {
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
                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();
                    painter.setPen(scalePen);
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
                if (displayTick%90 == 0) {
                    // Also draw a label
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
                }
            }
        }
        // draw the scale lines. If an arrow was drawn, stay off from it.

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

        painter.setPen(scalePen);
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

    painter.setPen(scalePen);
    //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
998
    QRectF headingNumberRect(-radius/3, radius*0.12, radius*2/3, radius*0.28);
dongfang's avatar
dongfang committed
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    painter.setPen(instrumentEdgePen);
    painter.drawRoundedRect(headingNumberRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);

    //    if (heading < 0) heading += 360;
    //    else if (heading >= 360) heading -= 360;
    int yawCompass = static_cast<int>(heading) % 360;

    QString yawAngle;
    yawAngle.sprintf("%03d", yawCompass);

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

    drawTextCenter(painter, yawAngle, largeTextSize, 0, radius/4);
}
1016
*/
dongfang's avatar
dongfang committed
1017

1018 1019 1020
void PrimaryFlightDisplay::drawAltimeter(
        QPainter& painter,
        QRectF area, // the area where to draw the tape.
dongfang's avatar
dongfang committed
1021
        float primaryAltitude,
1022
        float secondaryAltitude,
1023
        float vv
1024
    ) {
1025 1026

    painter.resetTransform();
1027
    fillInstrumentBackground(painter, area);
1028

dongfang's avatar
dongfang committed
1029 1030
    QPen pen;
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
1031

dongfang's avatar
dongfang committed
1032 1033
    pen.setColor(Qt::white);
    painter.setPen(pen);
1034

dongfang's avatar
dongfang committed
1035 1036
    float h = area.height();
    float w = area.width();
1037
    float secondaryAltitudeBoxHeight = mediumTextSize * 2;
dongfang's avatar
dongfang committed
1038
    // The height where we being with new tickmarks.
dongfang's avatar
dongfang committed
1039
    float effectiveHalfHeight = h*0.45;
1040 1041 1042 1043 1044

    // not yet implemented: Display of secondary altitude.
    // if (isAirplane())
    //    effectiveHalfHeight-= secondaryAltitudeBoxHeight;

dongfang's avatar
dongfang committed
1045 1046 1047 1048 1049 1050
    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
1051
    float numbersLeft = 0.42*w;
dongfang's avatar
dongfang committed
1052 1053
    float markerTip = (tickmarkLeft*2+tickmarkRightMajor)/3;
    float scaleCenterAltitude = primaryAltitude == UNKNOWN_ALTITUDE ? 0 : primaryAltitude;
1054 1055 1056 1057

    // altitude scale
#ifdef ALTIMETER_PROJECTED
    float range = 1.2;
dongfang's avatar
dongfang committed
1058 1059
    float start = scaleCenterAltitude - ALTIMETER_PROJECTED_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_PROJECTED_SPAN/2;
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
    int firstTick = ceil(start / ALTIMETER_PROJECTED_RESOLUTION) * ALTIMETER_PROJECTED_RESOLUTION;
    int lastTick = floor(end / ALTIMETER_PROJECTED_RESOLUTION) * ALTIMETER_PROJECTED_RESOLUTION;
    for (int tickAlt = firstTick; tickAlt <= lastTick; tickAlt += ALTIMETER_PROJECTED_RESOLUTION) {
        // a number between 0 and 1. Use as radians directly.
        float r = range*(tickAlt-altitude)/(ALTIMETER_PROJECTED_SPAN/2);
        float y = effectiveHalfHeight * sin(r);
        scale = cos(r);
        if (scale<0) scale = -scale;
        bool hasText = tickAlt % ALTIMETER_PROJECTED_MAJOR_RESOLUTION == 0;
#else
dongfang's avatar
dongfang committed
1070 1071
    float start = scaleCenterAltitude - ALTIMETER_LINEAR_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_LINEAR_SPAN/2;
1072 1073 1074
    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
1075 1076
        float y = (tickAlt-scaleCenterAltitude)*effectiveHalfHeight/(ALTIMETER_LINEAR_SPAN/2);
        bool isMajor = tickAlt % ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0;
1077 1078 1079
#endif
        painter.resetTransform();
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
1080 1081 1082 1083
        pen.setColor(tickAlt<0 ? redColor : Qt::white);
        painter.setPen(pen);
        if (isMajor) {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0);
1084
            QString s_alt;
dongfang's avatar
dongfang committed
1085
            s_alt.sprintf("%d", abs(tickAlt));
dongfang's avatar
dongfang committed
1086
            drawTextLeftCenter(painter, s_alt, mediumTextSize, numbersLeft, 0);
dongfang's avatar
dongfang committed
1087 1088
        } else {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0);
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
        }
    }

    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
1101

dongfang's avatar
dongfang committed
1102
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
1103 1104 1105
    pen.setColor(Qt::white);
    painter.setPen(pen);

1106 1107 1108 1109
    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
1110 1111
    pen.setColor(Qt::white);
    painter.setPen(pen);
dongfang's avatar
dongfang committed
1112

1113
    QString s_alt;
dongfang's avatar
dongfang committed
1114 1115 1116 1117 1118
    if(primaryAltitude == UNKNOWN_ALTITUDE)
        s_alt.sprintf("---");
    else
        s_alt.sprintf("%3.0f", primaryAltitude);

1119
    float xCenter = (markerTip+rightEdge)/2;
dongfang's avatar
dongfang committed
1120
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
dongfang's avatar
dongfang committed
1121 1122

    if (vv == UNKNOWN_ALTITUDE) return;
1123 1124
    float vvPixHeight = -vv/ALTIMETER_VVI_SPAN * effectiveHalfHeight;
    if (abs (vvPixHeight)<markerHalfHeight) return; // hidden behind marker.
dongfang's avatar
dongfang committed
1125

1126
    float vvSign = vvPixHeight>0 ? 1 : -1; // reverse y sign
dongfang's avatar
dongfang committed
1127 1128 1129

    // 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);
1130
    QPointF vvArrowEnd(rightEdge - w*ALTIMETER_VVI_WIDTH/2, vvPixHeight);
dongfang's avatar
dongfang committed
1131 1132
    painter.drawLine(vvArrowBegin, vvArrowEnd);

1133 1134 1135 1136 1137 1138 1139
    // 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;

    float xcenter = rightEdge-w*ALTIMETER_VVI_WIDTH/2;

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

1142
    vvArrowHead = QPointF(xcenter-vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize);
dongfang's avatar
dongfang committed
1143
    painter.drawLine(vvArrowHead, vvArrowEnd);
1144 1145
}

1146 1147
void PrimaryFlightDisplay::drawVelocityMeter(
        QPainter& painter,
1148 1149 1150
        QRectF area,
        float speed,
        float secondarySpeed
1151 1152 1153
        ) {

    painter.resetTransform();
1154
    fillInstrumentBackground(painter, area);
1155 1156 1157 1158 1159 1160 1161

    QPen pen;
    pen.setWidthF(lineWidth);

    float h = area.height();
    float w = area.width();
    float effectiveHalfHeight = h*0.45;
dongfang's avatar
dongfang committed
1162
    float markerHalfHeight = mediumTextSize;
dongfang's avatar
dongfang committed
1163
    float leftEdge = instrumentEdgePen.widthF()*2;
dongfang's avatar
dongfang committed
1164 1165 1166 1167 1168 1169 1170 1171 1172
    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:

    float centerScaleSpeed =
1173
            speed == UNKNOWN_SPEED ? 0 : speed;
dongfang's avatar
dongfang committed
1174 1175 1176

    float start = centerScaleSpeed - AIRSPEED_LINEAR_SPAN/2;
    float end = centerScaleSpeed + AIRSPEED_LINEAR_SPAN/2;
1177 1178 1179 1180

    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
1181 1182 1183 1184
        pen.setColor(tickSpeed<0 ? redColor : Qt::white);
        painter.setPen(pen);

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

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

1190
        if (hasText) {
dongfang's avatar
dongfang committed
1191
            painter.drawLine(tickmarkLeftMajor, 0, tickmarkRight, 0);
1192
            QString s_speed;
dongfang's avatar
dongfang committed
1193
            s_speed.sprintf("%d", abs(tickSpeed));
dongfang's avatar
dongfang committed
1194
            drawTextRightCenter(painter, s_speed, mediumTextSize, numbersRight, 0);
dongfang's avatar
dongfang committed
1195 1196
        } else {
            painter.drawLine(tickmarkLeftMinor, 0, tickmarkRight, 0);
1197 1198 1199 1200
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
dongfang's avatar
dongfang committed
1201 1202 1203 1204
    markerPath.lineTo(markerTip-markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(leftEdge, markerHalfHeight);
    markerPath.lineTo(leftEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip-markerHalfHeight, -markerHalfHeight);
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
    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;
1221
    if (speed == UNKNOWN_SPEED)
dongfang's avatar
dongfang committed
1222 1223
        s_alt.sprintf("---");
    else
1224
        s_alt.sprintf("%3.1f", speed);
dongfang's avatar
dongfang committed
1225
    float xCenter = (markerTip+leftEdge)/2;
1226 1227 1228
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
}

dongfang's avatar
dongfang committed
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283

#define TOP         (1<<0)
#define BOTTOM      (1<<1)
#define LEFT        (1<<2)
#define RIGHT       (1<<3)

#define TOP_2       (1<<4)
#define BOTTOM_2    (1<<5)
#define LEFT_2      (1<<6)
#define RIGHT_2     (1<<7)

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));
    } else if (where & LEFT_2) {
        area.setX(save.x() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & RIGHT) {
        area.setWidth(save.width()-consumed-margin);
    } else if (where & RIGHT_2) {
        area.setWidth(save.width()-consumed-margin/2);
    } else {
        area.setWidth(save.width()-consumed);
    }

    if (where & TOP) {
        area.setY(save.y() + (consumed = margin));
    } else if (where & TOP_2) {
        area.setY(save.y() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & BOTTOM) {
        area.setHeight(save.height()-consumed-margin);
    } else if (where & BOTTOM_2) {
        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) {
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_2);
    applyMargin(panel2, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel3, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel4, margin, BOTTOM|LEFT_2|RIGHT);
1284 1285
}

dongfang's avatar
dongfang committed
1286 1287 1288 1289 1290 1291 1292 1293 1294
void setMarginsForCornerLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_2);
    applyMargin(panel2, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel3, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel4, margin, BOTTOM|LEFT_2|RIGHT);
}

inline qreal tapesGaugeWidthFor(qreal containerWidth, qreal preferredAIWidth) {
    qreal result = (containerWidth - preferredAIWidth) / 2.0f;
dongfang's avatar
dongfang committed
1295
    qreal minimum = containerWidth / 5.5f;
dongfang's avatar
dongfang committed
1296 1297 1298 1299 1300
    if (result < minimum) result = minimum;
    return result;
}

void PrimaryFlightDisplay::doPaint() {
1301 1302 1303 1304 1305
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);

dongfang's avatar
dongfang committed
1306
    qreal margin = height()/100.0f;
1307

1308 1309 1310 1311 1312
    // 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
1313 1314 1315 1316 1317 1318 1319
    QRectF compassArea;
    QRectF altimeterArea;
    QRectF velocityMeterArea;
    QRectF sensorsStatsArea;
    QRectF linkStatsArea;
    QRectF sysStatsArea;
    QRectF missionStatsArea;
1320 1321

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

dongfang's avatar
dongfang committed
1324 1325 1326
    qreal compassHalfSpan = 180;
    float compassAIIntrusion = 0;

dongfang's avatar
dongfang committed
1327
    switch(layout) {
1328
    /*
dongfang's avatar
dongfang committed
1329 1330 1331 1332 1333 1334
    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.
1335 1336
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1337
                    0,
1338
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth * 2: width(),
dongfang's avatar
dongfang committed
1339 1340
                    height());

1341 1342
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1343 1344 1345 1346
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.

        qreal sidePanelsHeight = height();

1347
        altimeterArea = QRectF(AIMainArea.right(), height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);
dongfang's avatar
dongfang committed
1348 1349 1350
        velocityMeterArea = QRectF (0, height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);

        sensorsStatsArea = QRectF(0, 0, tapeGaugeWidth, sidePanelsHeight/5);
1351
        linkStatsArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1352
        sysStatsArea = QRectF(0, sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
1353
        missionStatsArea =QRectF(AIMainArea.right(), sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1354

1355 1356
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1357 1358 1359 1360
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForCornerLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1361

dongfang's avatar
dongfang committed
1362
        // Compass is inside the AI ans within its margins also.
1363 1364
        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
1365 1366
        break;
    }
1367

dongfang's avatar
dongfang committed
1368 1369 1370 1371
    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());
1372

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

dongfang's avatar
dongfang committed
1375
        tapeGaugeWidth = tapesGaugeWidthFor(width(), aiheight);
1376

1377 1378
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1379
                    0,
1380
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth*2 : width(),
dongfang's avatar
dongfang committed
1381
                    aiheight);
1382

1383 1384
        AIPaintArea = AIMainArea;

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

dongfang's avatar
dongfang committed
1389 1390
        qreal panelsHeight = height() / 5.0f;
        qreal panelsWidth = width() / 4.0f;
1391

1392 1393 1394 1395
        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);
1396

1397 1398
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1399 1400 1401 1402
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1403

dongfang's avatar
dongfang committed
1404
        // Compass is inside the AI ans within its margins also.
1405 1406
        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
1407 1408
        break;
    }
1409

1410 1411 1412
    */

    case COMPASS_INTEGRATED: {
dongfang's avatar
dongfang committed
1413
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());
1414
        qreal aiheight = height();
dongfang's avatar
dongfang committed
1415 1416
        qreal aiwidth = width()-tapeGaugeWidth*2;
        if (aiheight > aiwidth) aiheight = aiwidth;
1417 1418 1419 1420

        AIMainArea = QRectF(
                    tapeGaugeWidth,
                    0,
dongfang's avatar
dongfang committed
1421
                    aiwidth,
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
                    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
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
        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;

1469 1470
        break;
    }
dongfang's avatar
dongfang committed
1471 1472 1473 1474
    case COMPASS_SEPARATED: {
        // A layout for containers higher than their width.
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());

1475 1476
        qreal aiheight = width() - tapeGaugeWidth*2;
        qreal panelsHeight = 0;
dongfang's avatar
dongfang committed
1477

1478 1479
        AIMainArea = QRectF(
                    tapeGaugeWidth,
dongfang's avatar
dongfang committed
1480
                    0,
1481
                    width()-tapeGaugeWidth*2,
dongfang's avatar
dongfang committed
1482 1483
                    aiheight);

1484 1485 1486 1487 1488 1489 1490
        AIPaintArea = style == OVERLAY_HSI ?
                    QRectF(
                    0,
                    0,
                    width(),
                    height() - panelsHeight) : AIMainArea;

dongfang's avatar
dongfang committed
1491
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1492
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1493

1494
        /*
dongfang's avatar
dongfang committed
1495
        qreal panelsWidth = width() / 4.0f;
dongfang's avatar
dongfang committed
1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
        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
1506
            maxCompassDiam = fmin(width(), height()-AIArea.height()-panelsHeight);
dongfang's avatar
dongfang committed
1507 1508
        } else {
            // Remaining part is wider than high; place panels in corners around compass
1509
            // Naaah it is really ugly, do not do that.
dongfang's avatar
dongfang committed
1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520
            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
1521 1522 1523 1524 1525 1526 1527 1528
            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
1529
        }
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
        */
        /*
        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
1540
        compassArea = QRectF(compassCenter.x()-compassDiam/2, compassCenter.y()-compassDiam/2, compassDiam, compassDiam);
dongfang's avatar
dongfang committed
1541 1542 1543
        break;
    }
    }
1544

dongfang's avatar
dongfang committed
1545
    bool hadClip = painter.hasClipping();
dongfang's avatar
dongfang committed
1546

dongfang's avatar
dongfang committed
1547
    painter.setClipping(true);
1548
    painter.setClipRect(AIPaintArea);
1549

1550
    drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea);
dongfang's avatar
dongfang committed
1551
    drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion);
1552
    drawAIAirframeFixedFeatures(painter, AIMainArea);
1553

1554 1555 1556
   // if(layout ==COMPASS_SEPARATED)
        //drawSeparateCompassDisk(painter, compassArea);
   // else
dongfang's avatar
dongfang committed
1557
        drawAICompassDisk(painter, compassArea, compassHalfSpan);
1558

dongfang's avatar
dongfang committed
1559
    painter.setClipping(hadClip);
1560

1561
    drawAltimeter(painter, altimeterArea, primaryAltitude, GPSAltitude, verticalVelocity);
1562

1563
    drawVelocityMeter(painter, velocityMeterArea, primarySpeed, groundspeed);
1564

1565
    /*
dongfang's avatar
dongfang committed
1566 1567 1568 1569
    drawSensorsStatsPanel(painter, sensorsStatsArea);
    drawLinkStatsPanel(painter, linkStatsArea);
    drawSysStatsPanel(painter, sysStatsArea);
    drawMissionStatsPanel(painter, missionStatsArea);
1570 1571
    */
    /*
dongfang's avatar
dongfang committed
1572 1573 1574
    if (style == OPAGUE_TAPES) {
        drawInstrumentBackground(painter, AIArea);
    }
1575
    */
dongfang's avatar
dongfang committed
1576

dongfang's avatar
dongfang committed
1577
    painter.end();
1578
}
1579 1580

void PrimaryFlightDisplay:: createActions() {}