1 // BaseDiagram.cxx - part of GUI launcher using Qt5
3 // Written by James Turner, started December 2014.
5 // Copyright (C) 2014 James Turner <zakalawe@mac.com>
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.
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.
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.
21 #include "BaseDiagram.hxx"
28 #include <QMouseEvent>
30 /* equatorial and polar earth radius */
31 const float rec = 6378137; // earth radius, equator (?)
32 const float rpol = 6356752.314f; // earth radius, polar (?)
34 //Returns Earth radius at a given latitude (Ellipsoide equation with two equal axis)
35 static float earth_radius_lat( float lat )
37 double a = cos(lat)/rec;
38 double b = sin(lat)/rpol;
39 return 1.0f / sqrt( a * a + b * b );
42 BaseDiagram::BaseDiagram(QWidget* pr) :
45 m_wheelAngleDeltaAccumulator(0)
47 setSizePolicy(QSizePolicy::MinimumExpanding,
48 QSizePolicy::MinimumExpanding);
49 setMinimumSize(100, 100);
52 QTransform BaseDiagram::transform() const
55 t.translate(width() / 2, height() / 2); // center projection origin in the widget
56 t.scale(m_scale, m_scale);
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());
65 void BaseDiagram::paintEvent(QPaintEvent* pe)
68 p.setRenderHints(QPainter::Antialiasing);
69 p.fillRect(rect(), QColor(0x3f, 0x3f, 0x3f));
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);
79 QTransform t(transform());
85 void BaseDiagram::mousePressEvent(QMouseEvent *me)
87 m_lastMousePos = me->pos();
91 void BaseDiagram::mouseMoveEvent(QMouseEvent *me)
93 m_autoScalePan = false;
95 QPointF delta = me->pos() - m_lastMousePos;
96 m_lastMousePos = me->pos();
98 // offset is stored in metres so we don't have to modify it when
100 m_panOffset += (delta / m_scale);
108 return (v == 0) ? 0 : (v < 0) ? -1 : 1;
111 void BaseDiagram::wheelEvent(QWheelEvent *we)
113 m_autoScalePan = false;
115 int delta = we->angleDelta().y();
119 if (intSign(m_wheelAngleDeltaAccumulator) != intSign(delta)) {
120 m_wheelAngleDeltaAccumulator = 0;
123 m_wheelAngleDeltaAccumulator += delta;
124 if (m_wheelAngleDeltaAccumulator > 120) {
125 m_wheelAngleDeltaAccumulator = 0;
127 } else if (m_wheelAngleDeltaAccumulator < -120) {
128 m_wheelAngleDeltaAccumulator = 0;
135 void BaseDiagram::paintContents(QPainter*)
140 void BaseDiagram::recomputeBounds(bool resetZoom)
146 m_autoScalePan = true;
148 m_panOffset = QPointF();
154 void BaseDiagram::doComputeBounds()
156 // no-op in the base class
159 void BaseDiagram::extendBounds(const QPointF& p)
161 if (p.x() < m_bounds.left()) {
162 m_bounds.setLeft(p.x());
163 } else if (p.x() > m_bounds.right()) {
164 m_bounds.setRight(p.x());
167 if (p.y() < m_bounds.top()) {
168 m_bounds.setTop(p.y());
169 } else if (p.y() > m_bounds.bottom()) {
170 m_bounds.setBottom(p.y());
174 QPointF BaseDiagram::project(const SGGeod& geod) const
176 double r = earth_radius_lat(geod.getLatitudeRad());
177 double ref_lat = m_projectionCenter.getLatitudeRad(),
178 ref_lon = m_projectionCenter.getLongitudeRad(),
179 lat = geod.getLatitudeRad(),
180 lon = geod.getLongitudeRad(),
181 lonDiff = lon - ref_lon;
183 double c = acos( sin(ref_lat) * sin(lat) + cos(ref_lat) * cos(lat) * cos(lonDiff) );
185 // angular distance from center is 0
186 return QPointF(0.0, 0.0);
189 double k = c / sin(c);
191 if (ref_lat == (90 * SG_DEGREES_TO_RADIANS))
193 x = (SGD_PI / 2 - lat) * sin(lonDiff);
194 y = -(SGD_PI / 2 - lat) * cos(lonDiff);
196 else if (ref_lat == -(90 * SG_DEGREES_TO_RADIANS))
198 x = (SGD_PI / 2 + lat) * sin(lonDiff);
199 y = (SGD_PI / 2 + lat) * cos(lonDiff);
203 x = k * cos(lat) * sin(lonDiff);
204 y = k * ( cos(ref_lat) * sin(lat) - sin(ref_lat) * cos(lat) * cos(lonDiff) );
207 return QPointF(x, -y) * r;