]> git.mxchange.org Git - flightgear.git/blob - src/GUI/BaseDiagram.cxx
Work on LocationWidget for Qt launcher
[flightgear.git] / src / GUI / BaseDiagram.cxx
1 // BaseDiagram.cxx - part of GUI launcher using Qt5
2 //
3 // Written by James Turner, started December 2014.
4 //
5 // Copyright (C) 2014 James Turner <zakalawe@mac.com>
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #include "BaseDiagram.hxx"
22
23 #include <limits>
24
25 #include <QPainter>
26 #include <QDebug>
27 #include <QVector2D>
28 #include <QMouseEvent>
29
30 /* equatorial and polar earth radius */
31 const float rec  = 6378137;          // earth radius, equator (?)
32 const float rpol = 6356752.314f;      // earth radius, polar   (?)
33
34 //Returns Earth radius at a given latitude (Ellipsoide equation with two equal axis)
35 static float earth_radius_lat( float lat )
36 {
37     double a = cos(lat)/rec;
38     double b = sin(lat)/rpol;
39     return 1.0f / sqrt( a * a + b * b );
40 }
41
42 BaseDiagram::BaseDiagram(QWidget* pr) :
43     QWidget(pr),
44     m_autoScalePan(true),
45     m_wheelAngleDeltaAccumulator(0)
46 {
47     setSizePolicy(QSizePolicy::MinimumExpanding,
48                   QSizePolicy::MinimumExpanding);
49     setMinimumSize(100, 100);
50 }
51
52 QTransform BaseDiagram::transform() const
53 {
54     QTransform t;
55     t.translate(width() / 2, height() / 2); // center projection origin in the widget
56     t.scale(m_scale, m_scale);
57
58     // apply any pan offset that exists
59     t.translate(m_panOffset.x(), m_panOffset.y());
60     // center the bounding box (may not be at the origin)
61     t.translate(-m_bounds.center().x(), -m_bounds.center().y());
62     return t;
63 }
64
65 void BaseDiagram::paintEvent(QPaintEvent* pe)
66 {
67     QPainter p(this);
68     p.setRenderHints(QPainter::Antialiasing);
69     p.fillRect(rect(), QColor(0x3f, 0x3f, 0x3f));
70
71     if (m_autoScalePan) {
72         // fit bounds within our available space, allowing for a margin
73         const int MARGIN = 32; // pixels
74         double ratioInX = (width() - MARGIN * 2) / m_bounds.width();
75         double ratioInY = (height() - MARGIN * 2) / m_bounds.height();
76         m_scale = std::min(ratioInX, ratioInY);
77     }
78
79     QTransform t(transform());
80     p.setTransform(t);
81
82     paintContents(&p);
83 }
84
85 void BaseDiagram::mousePressEvent(QMouseEvent *me)
86 {
87     m_lastMousePos = me->pos();
88     m_didPan = false;
89 }
90
91 void BaseDiagram::mouseMoveEvent(QMouseEvent *me)
92 {
93     m_autoScalePan = false;
94
95     QPointF delta = me->pos() - m_lastMousePos;
96     m_lastMousePos = me->pos();
97
98     // offset is stored in metres so we don't have to modify it when
99     // zooming
100     m_panOffset += (delta / m_scale);
101     m_didPan = true;
102
103     update();
104 }
105
106 int intSign(int v)
107 {
108     return (v == 0) ? 0 : (v < 0) ? -1 : 1;
109 }
110
111 void BaseDiagram::wheelEvent(QWheelEvent *we)
112 {
113     m_autoScalePan = false;
114
115     int delta = we->angleDelta().y();
116     if (intSign(m_wheelAngleDeltaAccumulator) != intSign(delta)) {
117         m_wheelAngleDeltaAccumulator = 0;
118     }
119
120     m_wheelAngleDeltaAccumulator += delta;
121     if (m_wheelAngleDeltaAccumulator > 120) {
122         m_wheelAngleDeltaAccumulator = 0;
123         m_scale *= 2.0;
124     } else if (m_wheelAngleDeltaAccumulator < 120) {
125         m_wheelAngleDeltaAccumulator = 0;
126         m_scale *= 0.5;
127     }
128
129     update();
130 }
131
132 void BaseDiagram::paintContents(QPainter*)
133 {
134
135 }
136
137 void BaseDiagram::recomputeBounds(bool resetZoom)
138 {
139     m_bounds = QRectF();
140     doComputeBounds();
141
142     if (resetZoom) {
143         m_autoScalePan = true;
144         m_scale = 1.0;
145         m_panOffset = QPointF();
146     }
147
148     update();
149 }
150
151 void BaseDiagram::doComputeBounds()
152 {
153     // no-op in the base class
154 }
155
156 void BaseDiagram::extendBounds(const QPointF& p)
157 {
158     if (p.x() < m_bounds.left()) {
159         m_bounds.setLeft(p.x());
160     } else if (p.x() > m_bounds.right()) {
161         m_bounds.setRight(p.x());
162     }
163
164     if (p.y() < m_bounds.top()) {
165         m_bounds.setTop(p.y());
166     } else if (p.y() > m_bounds.bottom()) {
167         m_bounds.setBottom(p.y());
168     }
169 }
170
171 QPointF BaseDiagram::project(const SGGeod& geod) const
172 {
173     double r = earth_radius_lat(geod.getLatitudeRad());
174     double ref_lat = m_projectionCenter.getLatitudeRad(),
175     ref_lon = m_projectionCenter.getLongitudeRad(),
176     lat = geod.getLatitudeRad(),
177     lon = geod.getLongitudeRad(),
178     lonDiff = lon - ref_lon;
179
180     double c = acos( sin(ref_lat) * sin(lat) + cos(ref_lat) * cos(lat) * cos(lonDiff) );
181     if (c == 0.0) {
182         // angular distance from center is 0
183         return QPointF(0.0, 0.0);
184     }
185
186     double k = c / sin(c);
187     double x, y;
188     if (ref_lat == (90 * SG_DEGREES_TO_RADIANS))
189     {
190         x = (SGD_PI / 2 - lat) * sin(lonDiff);
191         y = -(SGD_PI / 2 - lat) * cos(lonDiff);
192     }
193     else if (ref_lat == -(90 * SG_DEGREES_TO_RADIANS))
194     {
195         x = (SGD_PI / 2 + lat) * sin(lonDiff);
196         y = (SGD_PI / 2 + lat) * cos(lonDiff);
197     }
198     else
199     {
200         x = k * cos(lat) * sin(lonDiff);
201         y = k * ( cos(ref_lat) * sin(lat) - sin(ref_lat) * cos(lat) * cos(lonDiff) );
202     }
203
204     return QPointF(x, -y) * r;
205 }