Qt Virtual Chart Table (QVCT)
CInstrumentGAArtificialHorizon.cpp
Go to the documentation of this file.
1 // INDENTING (emacs/vi): -*- mode:c++; tab-width:2; c-basic-offset:2; intent-tabs-mode:nil; -*- ex: set tabstop=2 expandtab:
2 
3 /*
4  * Qt Virtual Chart Table (QVCT)
5  * Copyright (C) 2012 Cedric Dufour <http://cedric.dufour.name>
6  * Author: Cedric Dufour <http://cedric.dufour.name>
7  *
8  * The Qt Virtual Chart Table (QVCT) is free software:
9  * you can redistribute it and/or modify it under the terms of the GNU General
10  * Public License as published by the Free Software Foundation, Version 3.
11  *
12  * The Qt Virtual Chart Table (QVCT) is distributed in the hope
13  * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
14  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  *
16  * See the GNU General Public License for more details.
17  */
18 
19 // C/C++
20 #include <cmath>
21 
22 // QT
23 #include <QBrush>
24 #include <QDateTime>
25 #include <QFont>
26 #include <QPainter>
27 #include <QPainterPath>
28 #include <QPointF>
29 #include <QPen>
30 #include <QPixmap>
31 #include <QSizeF>
32 #include <QWidget>
33 
34 // QVCT
35 #include "QVCTRuntime.hpp"
38 
39 
40 //------------------------------------------------------------------------------
41 // CONSTRUCTORS / DESTRUCTOR
42 //------------------------------------------------------------------------------
43 
45  : CInstrument( _poVesselCockpit )
46  , bForceRedraw( true )
47  , fdPitch( CDataCourse::UNDEFINED_BEARING )
48  , fdRoll( CDataCourse::UNDEFINED_BEARING )
49  , fdBearing( CDataCourse::UNDEFINED_BEARING )
50  , fdTime( CDataTime::UNDEFINED_TIME )
51 {
52 }
53 
54 
55 //------------------------------------------------------------------------------
56 // METHODS: CInstrument (implement/override)
57 //------------------------------------------------------------------------------
58 
60 {
61  // Retrieve parameters and values
62  CVesselPoint* __poVesselPoint = CInstrument::useVesselPoint();
63  double __fdSpeed = __poVesselPoint ? __poVesselPoint->GroundCourse.getSpeed() : CDataCourse::UNDEFINED_SPEED;
64  double __fdSpeedVertical = __poVesselPoint ? __poVesselPoint->GroundCourse.getSpeedVertical() : CDataCourse::UNDEFINED_SPEED;
65  double __fdBearing = __poVesselPoint ? __poVesselPoint->GroundCourse.getBearing() : CDataCourse::UNDEFINED_BEARING;
66  double __fdTime = __poVesselPoint ? __poVesselPoint->getTime() : CDataTime::UNDEFINED_TIME;
67 
68  // Compute pitch and roll angles
70  if( __poVesselPoint )
71  {
72  if( __fdSpeed != CDataCourse::UNDEFINED_SPEED
73  && __fdSpeedVertical != CDataCourse::UNDEFINED_SPEED
74  && fabs( __fdSpeed ) > 5.0 ) // under 5m/s (10kt), aircraft is most likely still grounded
75  {
76  __fdPitch = atan( __fdSpeedVertical / __fdSpeed ) * QVCT::RAD2DEG;
77  }
78  if( __fdBearing != CDataCourse::UNDEFINED_BEARING
79  && __fdTime != CDataTime::UNDEFINED_TIME )
80  {
83  {
84  if( __fdTime - fdTime > 0.0 )
85  {
86  double __fdSpeedAngular = ( __fdBearing - fdBearing ) / ( __fdTime - fdTime );
87  __fdRoll = atan( __fdSpeed * __fdSpeedAngular*QVCT::DEG2RAD / 9.81 ) * QVCT::RAD2DEG;
88  }
89  else
90  __fdRoll = fdRoll;
91  }
92  fdBearing = __fdBearing;
93  fdTime = __fdTime;
94  }
95  }
96 
97  // Redraw pixmap only if required
98  if( bForceRedraw
99  || qPixmapBuffer.size() != QWidget::size()
100  || fdPitch != __fdPitch
101  || fdRoll != __fdRoll )
102  {
103  // Draw
104  if( bForceRedraw
105  || qPixmapBufferBackground.size() != QWidget::size() )
106  {
107  drawStatic();
108  }
109  drawDynamic( __poVesselPoint, __fdPitch, __fdRoll );
110 
111  // Update cache parameters and values
112  bForceRedraw = false;
113  fdPitch = __fdPitch;
114  fdRoll = __fdRoll;
115  }
116 
117  // Draw pixmap
118  QPainter __qPainter;
119  __qPainter.begin( this );
120  __qPainter.setRenderHints( QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing );
121  __qPainter.drawPixmap( 0, 0, qPixmapBuffer );
122  __qPainter.end();
123 }
124 
125 
126 //------------------------------------------------------------------------------
127 // METHODS
128 //------------------------------------------------------------------------------
129 
130 //
131 // OTHER
132 //
133 
135 {
136  // Widget geometry
137  QPointF __qPointFCenter = QPointF( QRectF( QWidget::rect() ).center() );
138  double __fdSizeFactor = ( QWidget::width() < QWidget::height() ? (double)QWidget::width() : (double)QWidget::height() ) / 1200.0;
139 
140  // BACKGROUND
141  {
142  // Widget geometry (cont'd)
143  QTransform __qTransform;
144  __qTransform.translate( __qPointFCenter.x(), __qPointFCenter.y() );
145  __qTransform.scale( __fdSizeFactor, __fdSizeFactor );
146 
147  // Painter and resources
148  if( qPixmapBufferBackground.size() != QWidget::size() ) qPixmapBufferBackground = QPixmap( QWidget::size() );
149  qPixmapBufferBackground.fill( Qt::transparent );
150  QPainter __qPainter( &qPixmapBufferBackground );
151  QPen __qPen;
152  QFont __qFont;
153  __qPainter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing );
154  __qPainter.setWorldTransform( __qTransform );
155 
156  // Draw
157 
158  // ... delimiting corona
159  __qPen.setColor( Qt::white );
160  __qPen.setWidth( 10.0 );
161  __qPainter.setPen( __qPen );
162  __qPainter.setBrush( QColor( 32, 32, 32, 255 ) );
163  __qPainter.drawEllipse( QPointF( 0.0, 0.0 ), 495.0, 495.0 );
164 
165  // ... small ticks
166  for( int __i=-3; __i<=3; __i++ )
167  {
168  double __fdAngle = (double)__i * QVCT::PI / 18.0;
169  QPointF __qPointFAngle( sin( __fdAngle ), -cos( __fdAngle ) );
170  __qPainter.drawLine( 450.0*__qPointFAngle, 480.0*__qPointFAngle );
171  }
172 
173  // ... large ticks
174  for( int __i=-3; __i<=3; __i++ )
175  {
176  double __fdAngle = (double)__i * QVCT::PI / 6.0;
177  QPointF __qPointFAngle( sin( __fdAngle ), -cos( __fdAngle ) );
178  __qPainter.drawLine( 420.0*__qPointFAngle, 480.0*__qPointFAngle );
179  }
180 
181  // ... horizon ticks
182  __qPen.setWidth( 15.0 );
183  __qPainter.setPen( __qPen );
184  for( int __i=-1; __i<=1; __i++ )
185  {
186  if( !__i ) continue;
187  double __fdAngle = (double)__i * QVCT::PI / 2.0;
188  QPointF __qPointFAngle( sin( __fdAngle ), -cos( __fdAngle ) );
189  __qPainter.drawLine( 390.0*__qPointFAngle, 480.0*__qPointFAngle );
190  }
191 
192  // ... 45-degrees ticks
193  __qPen.setColor( Qt::white );
194  __qPen.setWidth( 0 );
195  __qPainter.setPen( __qPen );
196  __qPainter.setBrush( Qt::white );
197  __qPainter.rotate( -45.0 );
198  for( int __i=0; __i<3; __i++ )
199  {
200  static const QPointF __pqPointFPolygon[3] =
201  {
202  QPointF( -30.0, -480.0 ),
203  QPointF( 0.0, -410.0 ),
204  QPointF( +30.0, -480.0 ),
205  };
206  __qPainter.drawConvexPolygon( __pqPointFPolygon, 3 );
207  __qPainter.rotate( 45.0 );
208  }
209 
210  // ... data
211  __qPen.setColor( Qt::lightGray );
212  __qPen.setWidth( 5.0 );
213  __qPainter.setPen( __qPen );
214  __qPainter.setBrush( QColor( 32, 32, 32, 255 ) );
215  __qFont.setPixelSize( 50.0 );
216  __qFont.setBold( false );
217  __qPainter.setFont( __qFont );
218  __qPainter.setWorldTransform( __qTransform );
219  {
220  QRectF __qRectFFrame( QPointF( -150.0, -60.0 ), QSizeF( 300.0, 120.0 ) );
221  QRectF __qRectFText( QPointF( -125.0, -30.0 ), QSizeF( 250.0, 60.0 ) );
222  __qPainter.drawRoundedRect( __qRectFFrame.translated( QPointF( -425.0, +515.0 ) ), 10.0, 10.0 );
223  __qPainter.drawText( __qRectFText.translated( QPointF( -440.0, +540.0 ) ), Qt::AlignLeft, "PITCH" );
224  __qPainter.drawRoundedRect( __qRectFFrame.translated( QPointF( +425.0, +515.0 ) ), 10.0, 10.0 );
225  __qPainter.drawText( __qRectFText.translated( QPointF( +440.0, +540.0 ) ), Qt::AlignRight, "ROLL" );
226  }
227  } // BACKGROUND
228 
229  // FOREGROUND
230  {
231  // Widget geometry (cont'd)
232  QTransform __qTransform;
233  __qTransform.translate( __qPointFCenter.x()*2.0, __qPointFCenter.y()*2.0 );
234  __qTransform.scale( __fdSizeFactor, __fdSizeFactor );
235 
236  // Painter and resources
237  if( qPixmapBufferForeground.size() != QWidget::size()*2.0 ) qPixmapBufferForeground = QPixmap( QWidget::size()*2.0 );
238  qPixmapBufferForeground.fill( QColor( 224, 128, 0 ) );
239  QPainter __qPainter( &qPixmapBufferForeground );
240  QPen __qPen;
241  QFont __qFont;
242  __qPainter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing );
243  __qPainter.setWorldTransform( __qTransform );
244 
245  // Draw
246 
247  // ... sky
248  __qPen.setColor( Qt::white );
249  __qPen.setWidth( 0 );
250  __qPainter.setPen( __qPen );
251  __qPainter.setBrush( QColor( 0, 192, 255 ) );
252  __qPainter.drawRect( -1000.0, -1000.0, 2000.0, 1000.0 );
253 
254  // ... pitch marks
255  __qPen.setWidth( 10.0 );
256  __qPainter.setPen( __qPen );
257  for( int __i=-6; __i<=6; __i++ )
258  {
259  __qPainter.drawLine( QPointF( -30.0, __i*30.0 ), QPointF( +30.0, __i*30.0 ) );
260  }
261  __qFont.setPixelSize( 40.0 );
262  __qFont.setBold( true );
263  __qPainter.setFont( __qFont );
264  for( int __i=-3; __i<=3; __i++ )
265  {
266  int __j = fabs( __i );
267  __qPainter.drawLine( QPointF( -75.0-__j*25.0, __i*60.0 ), QPointF( +75.0+__j*25.0, __i*60.0 ) );
268  if( !__i ) continue;
269  QRectF __qRectFText( QPointF( -40.0, -20.0 ), QSizeF( 80.0, 40.0 ) );
270  __qPainter.drawText( __qRectFText.translated( QPointF( -125.0-__j*25.0, __i*60.0 ) ), Qt::AlignCenter, QString::number( __j*10 ) );
271  __qPainter.drawText( __qRectFText.translated( QPointF( +125.0+__j*25.0, __i*60.0 ) ), Qt::AlignCenter, QString::number( __j*10 ) );
272  }
273 
274  // ... horizon mark
275  __qPen.setWidth( 15.0 );
276  __qPainter.setPen( __qPen );
277  __qPainter.drawLine( QPointF( -1000.0, 0.0 ), QPointF( +1000.0, 0.0 ) );
278  } // FOREGROUND
279 
280 }
281 
282 void CInstrumentGAArtificialHorizon::drawDynamic( const CVesselPoint* _poVesselPoint, double _fdPitch, double _fdRoll )
283 {
284  double __fdPitchConverted = _fdPitch != CDataCourse::UNDEFINED_BEARING ? _fdPitch : 0.0;
285  double __fdRollConverted = _fdRoll != CDataCourse::UNDEFINED_BEARING ? _fdRoll : 0.0;
286 
287  // Widget geometry
288  QPointF __qPointFCenter = QPointF( QRectF( QWidget::rect() ).center() );
289  double __fdSizeFactor = ( QWidget::width() < QWidget::height() ? (double)QWidget::width() : (double)QWidget::height() ) / 1200.0;
290  QTransform __qTransformNonScaled;
291  __qTransformNonScaled.translate( __qPointFCenter.x(), __qPointFCenter.y() );
292  QTransform __qTransformScaled;
293  __qTransformScaled.translate( __qPointFCenter.x(), __qPointFCenter.y() );
294  __qTransformScaled.scale( __fdSizeFactor, __fdSizeFactor );
295 
296  // Painter and resources
298  QPainter __qPainter( &qPixmapBuffer );
299  QPen __qPen;
300  QFont __qFont;
301  __qPainter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform );
302 
303  // Draw
304 
305  // ... foreground
306  QPainterPath __qPainterPath;
307  __qPainterPath.addEllipse( __qPointFCenter, 390.0*__fdSizeFactor, 390.0*__fdSizeFactor );
308  __qPainter.setClipPath( __qPainterPath );
309  __qPainter.setWorldTransform( __qTransformNonScaled );
310  __qPainter.translate( 0.0, __fdPitchConverted*6.0*__fdSizeFactor );
311  __qPainter.rotate( -__fdRollConverted );
312  __qPainter.drawPixmap( QPointF( -(double)qPixmapBufferForeground.width()/2.0, -(double)qPixmapBufferForeground.height()/2.0 ), qPixmapBufferForeground );
313  __qPainter.setClipping( false );
314  __qPen.setColor( Qt::black );
315  __qPen.setWidth( 10.0 );
316  __qPainter.setPen( __qPen );
317  __qPainter.setWorldTransform( __qTransformScaled );
318  __qPainter.drawEllipse( QPointF( 0.0, 0.0 ), 390.0, 390.0 );
319 
320  // ... attitude marks
321  __qPen.setColor( Qt::black );
322  __qPen.setWidth( 0 );
323  __qPen.setCapStyle( Qt::SquareCap );
324  __qPainter.setPen( __qPen );
325  __qPainter.setBrush( QColor( 255, 224, 0 ) );
326  __qPainter.setPen( __qPen );
327  __qPainter.drawRoundedRect( QRectF( QPointF( -175.0, -10.0 ), QPointF( -50.0, +10.0 ) ), 10.0, 10.0 );
328  __qPainter.drawEllipse( QPointF( 0.0, 0.0 ), 10.0, 10.0 );
329  __qPainter.drawRoundedRect( QRectF( QPointF( +50.0, -10.0 ), QPointF( +175.0, +10.0 ) ), 10.0, 10.0 );
330  __qPainter.rotate( -__fdRollConverted );
331  {
332  static const QPointF __pqPointFPolygon[3] =
333  {
334  QPointF( -30.0, -310.0 ),
335  QPointF( 0.0, -380.0 ),
336  QPointF( +30.0, -310.0 ),
337  };
338  __qPainter.drawConvexPolygon( __pqPointFPolygon, 3 );
339  }
340 
341  // ... data
342  __qPen.setColor( Qt::white );
343  __qPainter.setPen( __qPen );
344  __qFont.setPixelSize( 50.0 );
345  __qFont.setBold( false );
346  __qPainter.setFont( __qFont );
347  __qPainter.setWorldTransform( __qTransformScaled );
348  {
349  QRectF __qRectFText( QPointF( -125.0, -30.0 ), QSizeF( 250.0, 60.0 ) );
350  QString __qsPITCH( "n/a" ), __qsROLL( "n/a" );
351  if( _poVesselPoint )
352  {
353  if( _fdPitch != CDataCourse::UNDEFINED_BEARING )
354  __qsPITCH = CUnitBearing::toString( _fdPitch, CUnitBearing::DEG, 0 );
355  if( _fdRoll != CDataCourse::UNDEFINED_BEARING )
356  __qsROLL = CUnitBearing::toString( _fdRoll, CUnitBearing::DEG, 0 );
357  }
358  __qFont.setItalic( !_poVesselPoint || _fdPitch == CDataCourse::UNDEFINED_BEARING || !_poVesselPoint->GroundCourseValidity.isValidSpeed() || !_poVesselPoint->GroundCourseValidity.isValidSpeedVertical() );
359  __qPainter.setFont( __qFont );
360  __qPainter.drawText( __qRectFText.translated( QPointF( -425.0, +490.0 ) ), Qt::AlignCenter, __qsPITCH );
361  __qFont.setItalic( !_poVesselPoint || _fdRoll == CDataCourse::UNDEFINED_BEARING || !_poVesselPoint->GroundCourseValidity.isValidBearing() );
362  __qPainter.setFont( __qFont );
363  __qPainter.drawText( __qRectFText.translated( QPointF( +425.0, +490.0 ) ), Qt::AlignCenter, __qsROLL );
364  }
365 
366  // ... OFF flag
367  if( !_poVesselPoint )
368  {
369  __qPen.setColor( Qt::black );
370  __qPen.setWidth( 0 );
371  __qPainter.setPen( __qPen );
372  __qPainter.setBrush( QBrush( Qt::red ) );
373  __qFont.setBold( true );
374  __qFont.setItalic( false );
375  __qPainter.setFont( __qFont );
376  QRectF __qRectFText( QPointF( -75.0, -30.0 ), QSizeF( 150.0, 60.0 ) );
377  __qPainter.drawRect( __qRectFText.translated( QPointF( +150.0, -50.0 ) ) );
378  __qPainter.drawText( __qRectFText.translated( QPointF( +150.0, -50.0 ) ), Qt::AlignCenter, "OFF" );
379  }
380 }
CDataCourse GroundCourse
Ground course.
CDataCourseValidity GroundCourseValidity
Ground course validity.
bool isValidSpeedVertical() const
Returns the vertical speed's (global) validity status.
bool isValidSpeed() const
Returns the horizontal speed's (global) validity status.
bool isValidBearing() const
Returns the bearing's (global) validity status.
(Geographical) Course data [bearing, horizontal/vertical speeds]
Definition: CDataCourse.hpp:34
double getSpeedVertical() const
Returns this course's vertical speed, in meters per second.
static constexpr double UNDEFINED_BEARING
Specific value for an undefined bearing.
Definition: CDataCourse.hpp:42
static constexpr double UNDEFINED_SPEED
Specific value for an undefined speed.
Definition: CDataCourse.hpp:44
double getBearing() const
Returns this course's bearing, in degrees.
double getSpeed() const
Returns this course's horizontal speed, in meters per second.
Time data.
Definition: CDataTime.hpp:36
double getTime() const
Returns this time's time, in seconds from Unix epoch.
Definition: CDataTime.hpp:89
static constexpr double UNDEFINED_TIME
Specific value for an undefined time.
Definition: CDataTime.hpp:44
QPixmap qPixmapBufferBackground
Pixmap buffer used to render and cache the instrument's (static) background.
double fdRoll
Roll (angle) at which the last rendering was achieved.
double fdBearing
Bearing at which the last rendering was achieved.
double fdTime
Time at which the last rendering was achieved.
double fdPitch
Pitch (angle) at which the last rendering was achieved.
bool bForceRedraw
Forces the redraw of the instrument (not matter its cache content)
CInstrumentGAArtificialHorizon(CVesselCockpit *_poVesselCockpit)
QPixmap qPixmapBuffer
Pixmap buffer used to render and cache the instrument.
virtual void draw()
Draws the instrument (resets if the vessel is undefined)
QPixmap qPixmapBufferForeground
Pixmap buffer used to render and cache the instrument's (static) foreground.
void drawDynamic(const CVesselPoint *_poVesselPoint, double _fdPitch, double _fdRoll)
Generic instrument.
Definition: CInstrument.hpp:39
CVesselPoint * useVesselPoint()
Returns parent cockpit's associated vessel (0 if the vessel is undefined)
Definition: CInstrument.cpp:54
@ DEG
degrees
static QString toString(double _fdValue, EUnit _eUnit, int _iPrecision=0)
Returns the formatted represention of the given value, using the specified format/unit and decimal pr...
[UI] Generic vessel's cockpit view (window)
[UI] Vessel overlay point (item) / vessel
static constexpr double PI
Definition: QVCT.hpp:45
static constexpr double RAD2DEG
Definition: QVCT.hpp:47
static constexpr double DEG2RAD
Definition: QVCT.hpp:46