]> git.mxchange.org Git - flightgear.git/commitdiff
Launcher: airport diagram runways can be clicked
authorJames Turner <jmt@March-hare.local>
Fri, 5 Jun 2015 08:26:40 +0000 (09:26 +0100)
committerJames Turner <zakalawe@mac.com>
Fri, 5 Jun 2015 12:27:23 +0000 (14:27 +0200)
 - indicate selected runway visually

src/GUI/AirportDiagram.cxx
src/GUI/AirportDiagram.hxx
src/GUI/QtLauncher.cxx
src/GUI/QtLauncher.hxx

index c460e2878cee9b50ee62db14a37a9486f1f86059..f347c16fd2a311c5133284737029e31302833b11 100644 (file)
 
 #include "AirportDiagram.hxx"
 
+#include <limits>
+
 #include <QPainter>
 #include <QDebug>
+#include <QVector2D>
+#include <QMouseEvent>
 
 #include <Airports/airport.hxx>
 #include <Airports/runways.hxx>
@@ -40,6 +44,49 @@ static float earth_radius_lat( float lat )
     return 1.0f / sqrt( a * a + b * b );
 }
 
+static double distanceToLineSegment(const QVector2D& p, const QVector2D& a,
+                                    const QVector2D& b, double* outT = NULL)
+{
+    QVector2D ab(b - a);
+    QVector2D ac(p - a);
+    
+    // Squared length, to avoid a sqrt
+    const qreal len2 = ab.lengthSquared();
+    
+    // Line null, the projection can't exist, we return the first point
+    if (qIsNull(len2)) {
+        if (outT) {
+            *outT = 0.0;
+        }
+        return (p - a).length();
+    }
+    
+    // Parametric value of the projection on the line
+    const qreal t = (ac.x() * ab.x() + ac.y() * ab.y()) / len2;
+    
+    if (t < 0.0) {
+        // Point is before the first point
+        if (outT) {
+            *outT = 0.0;
+        }
+        return (p - a).length();
+    } else if (t > 1.0) {
+        // Point is after the second point
+        if (outT) {
+            *outT = 1.0;
+        }
+        return (p - b).length();
+    } else {
+        if (outT) {
+            *outT = t;
+        }
+        
+        const QVector2D proj = a + t * ab;
+        return (proj - p).length();
+    }
+    
+    return 0.0;
+}
 
 AirportDiagram::AirportDiagram(QWidget* pr) :
 QWidget(pr)
@@ -65,6 +112,21 @@ void AirportDiagram::setAirport(FGAirportRef apt)
     update();
 }
 
+FGRunwayRef AirportDiagram::selectedRunway() const
+{
+    return m_selectedRunway;
+}
+
+void AirportDiagram::setSelectedRunway(FGRunwayRef r)
+{
+    if (r == m_selectedRunway) {
+        return;
+    }
+    
+    m_selectedRunway = r;
+    update();
+}
+
 void AirportDiagram::addRunway(FGRunwayRef rwy)
 {
     Q_FOREACH(RunwayData rd, m_runways) {
@@ -94,6 +156,22 @@ void AirportDiagram::addParking(FGParking* park)
     update();
 }
 
+QTransform AirportDiagram::transform() const
+{
+    // fit bounds within our available space, allowing for a margin
+    const int MARGIN = 32; // pixels
+    double ratioInX = (width() - MARGIN * 2) / m_bounds.width();
+    double ratioInY = (height() - MARGIN * 2) / m_bounds.height();
+    double scale = std::min(ratioInX, ratioInY);
+    
+    QTransform t;
+    t.translate(width() / 2, height() / 2); // center projection origin in the widget
+    t.scale(scale, scale);
+    // center the bounding box (may not be at the origin)
+    t.translate(-m_bounds.center().x(), -m_bounds.center().y());
+    return t;
+}
+
 void AirportDiagram::paintEvent(QPaintEvent* pe)
 {
     QPainter p(this);
@@ -106,11 +184,7 @@ void AirportDiagram::paintEvent(QPaintEvent* pe)
     double ratioInY = (height() - MARGIN * 2) / m_bounds.height();
     double scale = std::min(ratioInX, ratioInY);
 
-    QTransform t;
-    t.translate(width() / 2, height() / 2); // center projection origin in the widget
-    t.scale(scale, scale);
-    // center the bounding box (may not be at the origin)
-    t.translate(-m_bounds.center().x(), -m_bounds.center().y());
+    QTransform t(transform());
     p.setTransform(t);
 
 // pavements
@@ -128,16 +202,22 @@ void AirportDiagram::paintEvent(QPaintEvent* pe)
     }
 
 // runways
-    QPen pen(Qt::magenta);
     QFont f;
     f.setPixelSize(14);
     p.setFont(f);
 
     Q_FOREACH(const RunwayData& r, m_runways) {
+        QColor color(Qt::magenta);
+        if ((r.runway == m_selectedRunway) || (r.runway->reciprocalRunway() == m_selectedRunway)) {
+            color = Qt::yellow;
+        }
+        
         p.setTransform(t);
 
+        QPen pen(color);
         pen.setWidth(r.widthM);
         p.setPen(pen);
+        
         p.drawLine(r.p1, r.p2);
 
     // draw idents
@@ -147,7 +227,8 @@ void AirportDiagram::paintEvent(QPaintEvent* pe)
         p.rotate(r.runway->headingDeg());
         // invert scaling factor so we can use screen pixel sizes here
         p.scale(1.0 / scale, 1.0/ scale);
-
+        
+        p.setPen((r.runway == m_selectedRunway) ? Qt::yellow : Qt::magenta);
         p.drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop);
 
         FGRunway* recip = r.runway->reciprocalRunway();
@@ -158,10 +239,37 @@ void AirportDiagram::paintEvent(QPaintEvent* pe)
         p.rotate(recip->headingDeg());
         p.scale(1.0 / scale, 1.0/ scale);
 
+        p.setPen((r.runway->reciprocalRunway() == m_selectedRunway) ? Qt::yellow : Qt::magenta);
         p.drawText(QRect(-100, 5, 200, 200), recipIdent, Qt::AlignHCenter | Qt::AlignTop);
     }
 }
 
+void AirportDiagram::mouseReleaseEvent(QMouseEvent* me)
+{
+    QTransform t(transform());
+    double minDist = std::numeric_limits<double>::max();
+    FGRunwayRef bestRunway;
+    
+    Q_FOREACH(const RunwayData& r, m_runways) {
+        QPointF p1(t.map(r.p1)), p2(t.map(r.p2));
+        double t;
+        double d = distanceToLineSegment(QVector2D(me->pos()),
+                                         QVector2D(p1),
+                                         QVector2D(p2), &t);
+        if (d < minDist) {
+            if (t > 0.5) {
+                bestRunway = r.runway->reciprocalRunway();
+            } else {
+                bestRunway = r.runway;
+            }
+            minDist = d;
+        }
+    }
+    
+    if (minDist < 16.0) {
+        emit clickedRunway(bestRunway);
+    }
+}
 
 void AirportDiagram::extendBounds(const QPointF& p)
 {
index 7343bb24cfa8982f4992a7b6d82f688c0f4063cf..07eaf829765514499e6f172e825dfd9a5d4701da 100644 (file)
@@ -26,6 +26,7 @@
 
 class AirportDiagram : public QWidget
 {
+    Q_OBJECT
 public:
     AirportDiagram(QWidget* pr);
 
@@ -33,17 +34,26 @@ public:
 
     void addRunway(FGRunwayRef rwy);
     void addParking(FGParking* park);
+    
+    FGRunwayRef selectedRunway() const;
+    void setSelectedRunway(FGRunwayRef r);
+Q_SIGNALS:
+    void clickedRunway(FGRunwayRef rwy);
+    
 protected:
     virtual void paintEvent(QPaintEvent* pe);
     // wheel event for zoom
 
     // mouse drag for pan
+    
+    virtual void mouseReleaseEvent(QMouseEvent* me);
 
 
 private:
     void extendBounds(const QPointF& p);
     QPointF project(const SGGeod& geod) const;
-
+    QTransform transform() const;
+    
     void buildTaxiways();
     void buildPavements();
 
@@ -72,4 +82,6 @@ private:
 
     QList<TaxiwayData> m_taxiways;
     QList<QPainterPath> m_pavements;
+    
+    FGRunwayRef m_selectedRunway;
 };
index a88c099a7824861a6e9bca8d2dedb30a3d389570..50b6dea07983dfd83480bdd0316457b3983b898d 100644 (file)
@@ -443,6 +443,9 @@ QtLauncher::QtLauncher() :
     connect(m_ui->onFinalCheckbox, SIGNAL(toggled(bool)),
             this, SLOT(updateAirportDescription()));
 
+    
+    connect(m_ui->airportDiagram, &AirportDiagram::clickedRunway,
+            this, &QtLauncher::onAirportDiagramClicked);
 
     connect(m_ui->runButton, SIGNAL(clicked()), this, SLOT(onRun()));
     connect(m_ui->quitButton, SIGNAL(clicked()), this, SLOT(onQuit()));
@@ -851,6 +854,17 @@ void QtLauncher::onAirportChanged()
     }
 }
 
+void QtLauncher::onAirportDiagramClicked(FGRunwayRef rwy)
+{
+    if (rwy) {
+        m_ui->runwayRadio->setChecked(true);
+        int rwyIndex = m_ui->runwayCombo->findText(QString::fromStdString(rwy->ident()));
+        m_ui->runwayCombo->setCurrentIndex(rwyIndex);
+    }
+    
+    updateAirportDescription();
+}
+
 void QtLauncher::onToggleTerrasync(bool enabled)
 {
     if (enabled) {
@@ -900,7 +914,8 @@ void QtLauncher::updateAirportDescription()
     QString locationOnAirport;
     if (m_ui->runwayRadio->isChecked()) {
         bool onFinal = m_ui->onFinalCheckbox->isChecked();
-        QString runwayName = (m_ui->runwayCombo->currentIndex() == 0) ?
+        int comboIndex = m_ui->runwayCombo->currentIndex();
+        QString runwayName = (comboIndex == 0) ?
             "active runway" :
             QString("runway %1").arg(m_ui->runwayCombo->currentText());
 
@@ -909,6 +924,11 @@ void QtLauncher::updateAirportDescription()
         } else {
             locationOnAirport = QString("on %1").arg(runwayName);
         }
+        
+        int runwayIndex = m_ui->runwayCombo->itemData(comboIndex).toInt();
+        FGRunwayRef rwy = (runwayIndex >= 0) ?
+            m_selectedAirport->getRunwayByIndex(runwayIndex) : FGRunwayRef();
+        m_ui->airportDiagram->setSelectedRunway(rwy);
     } else if (m_ui->parkingRadio->isChecked()) {
         locationOnAirport =  QString("at parking position %1").arg(m_ui->parkingCombo->currentText());
     }
index d705c82f0f183bc903f7374a6479946cbb81ca4b..6b6615af131a6958f66bfcc66792bcd21a01c3cf 100644 (file)
@@ -79,6 +79,8 @@ private slots:
     void onSubsytemIdleTimeout();
 
     void onEditPaths();
+    
+    void onAirportDiagramClicked(FGRunwayRef rwy);
 private:
     void setAirport(FGAirportRef ref);
     void updateSelectedAircraft();