2020-06-19 05:03:38 +02:00
// viz1090, a vizualizer for dump1090 ADSB output
//
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
// Copyright (C) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
2019-09-08 01:11:20 +02:00
# include "SDL2/SDL2_rotozoom.h"
2019-09-09 06:23:38 +02:00
# include "SDL2/SDL2_gfxPrimitives.h"
2020-06-10 20:28:52 +02:00
2020-03-08 02:22:20 +01:00
# include "View.h"
2021-03-20 05:13:43 +01:00
# include "AircraftLabel.h"
2020-06-12 06:55:04 +02:00
# include <iostream>
2020-06-13 05:32:42 +02:00
# include <thread>
2020-06-12 06:55:04 +02:00
2020-06-15 00:26:26 +02:00
using fmilliseconds = std : : chrono : : duration < float , std : : milli > ;
using fseconds = std : : chrono : : duration < float > ;
2020-06-12 06:55:04 +02:00
2020-06-15 00:26:26 +02:00
static std : : chrono : : high_resolution_clock : : time_point now ( ) {
return std : : chrono : : high_resolution_clock : : now ( ) ;
2020-06-12 06:55:04 +02:00
}
2020-06-15 00:26:26 +02:00
static float elapsed ( std : : chrono : : high_resolution_clock : : time_point ref ) {
2020-06-27 05:40:03 +02:00
return ( fmilliseconds { now ( ) - ref } ) . count ( ) ;
2020-06-12 06:55:04 +02:00
}
2020-06-15 00:26:26 +02:00
static float elapsed_s ( std : : chrono : : high_resolution_clock : : time_point ref ) {
2020-06-27 05:40:03 +02:00
return ( fseconds { now ( ) - ref } ) . count ( ) ;
2017-09-17 16:46:48 +02:00
}
2020-03-08 02:22:20 +01:00
static float clamp ( float in , float min , float max ) {
2020-03-07 05:51:47 +01:00
float out = in ;
if ( in < min ) {
out = min ;
}
if ( in > max ) {
out = max ;
}
return out ;
}
2023-06-01 01:58:22 +02:00
2020-03-08 02:22:20 +01:00
static void CROSSVP ( float * v , float * u , float * w )
2019-09-09 06:23:38 +02:00
{
v [ 0 ] = u [ 1 ] * w [ 2 ] - u [ 2 ] * ( w ) [ 1 ] ;
v [ 1 ] = u [ 2 ] * w [ 0 ] - u [ 0 ] * ( w ) [ 2 ] ;
v [ 2 ] = u [ 0 ] * w [ 1 ] - u [ 1 ] * ( w ) [ 0 ] ;
}
SDL_Color setColor ( uint8_t r , uint8_t g , uint8_t b ) {
SDL_Color out ;
out . r = r ;
out . g = g ;
out . b = b ;
return out ;
}
2022-10-20 07:11:44 +02:00
float lerp ( float a , float b , float factor ) {
if ( factor > 1.0f ) {
factor = 1.0f ;
}
if ( factor < 0.0f ) {
factor = 0.0f ;
}
return ( 1.0f - factor ) * a + factor * b ;
}
float lerpAngle ( float a , float b , float factor ) {
float diff = fabs ( b - a ) ;
if ( diff > 180.0f )
{
if ( b > a )
{
a + = 360.0f ;
}
else
{
b + = 360.0f ;
}
}
float value = ( a + ( ( b - a ) * factor ) ) ;
if ( value > = 0.0f & & value < = 360.0f )
return value ;
return fmod ( value , 360.0f ) ;
}
2020-02-18 03:11:05 +01:00
SDL_Color lerpColor ( SDL_Color aColor , SDL_Color bColor , float factor ) {
if ( factor > 1.0f ) {
factor = 1.0f ;
}
if ( factor < 0.0f ) {
factor = 0.0f ;
}
SDL_Color out ;
out . r = ( 1.0f - factor ) * aColor . r + factor * bColor . r ;
out . g = ( 1.0f - factor ) * aColor . g + factor * bColor . g ;
out . b = ( 1.0f - factor ) * aColor . b + factor * bColor . b ;
return out ;
}
2020-03-08 02:22:20 +01:00
int View : : screenDist ( float d ) {
2020-03-19 06:22:59 +01:00
float scale_factor = ( screen_width > screen_height ) ? screen_width : screen_height ;
2020-06-14 05:54:21 +02:00
// return round(0.95 * scale_factor * 0.5 * fabs(d) / maxDist);
return round ( scale_factor * 0.5 * fabs ( d ) / maxDist ) ;
2019-09-09 06:23:38 +02:00
}
2020-03-08 05:28:55 +01:00
void View : : pxFromLonLat ( float * dx , float * dy , float lon , float lat ) {
2019-09-09 06:23:38 +02:00
if ( ! lon | | ! lat ) {
* dx = 0 ;
* dy = 0 ;
return ;
}
2020-06-14 05:54:21 +02:00
// for accurate reprojection use the extra cos term
* dx = LATLONMULT * ( lon - centerLon ) * cos ( ( ( lat + centerLat ) / 2.0f ) * M_PI / 180.0f ) ;
* dy = LATLONMULT * ( lat - centerLat ) ;
2019-09-09 06:23:38 +02:00
}
2020-03-08 02:22:20 +01:00
void View : : latLonFromScreenCoords ( float * lat , float * lon , int x , int y ) {
2020-03-19 06:22:59 +01:00
float scale_factor = ( screen_width > screen_height ) ? screen_width : screen_height ;
2020-01-20 07:22:58 +01:00
2020-03-19 06:22:59 +01:00
float dx = maxDist * ( x - ( screen_width > > 1 ) ) / ( 0.95 * scale_factor * 0.5 ) ;
2021-04-20 07:02:49 +02:00
float dy = maxDist * ( y - ( screen_height > > 1 ) ) / ( 0.95 * scale_factor * 0.5 ) ;
2020-01-20 07:22:58 +01:00
2020-03-19 06:22:59 +01:00
* lat = 180.0f * dy / ( 6371.0 * M_PI ) + centerLat ;
* lon = 180.0 * dx / ( cos ( ( ( * lat + centerLat ) / 2.0f ) * M_PI / 180.0f ) * 6371.0 * M_PI ) + centerLon ;
2020-01-20 07:22:58 +01:00
}
2019-09-09 06:23:38 +02:00
2020-03-08 02:22:20 +01:00
void View : : screenCoords ( int * outX , int * outY , float dx , float dy ) {
2020-03-19 06:22:59 +01:00
* outX = ( screen_width > > 1 ) + ( ( dx > 0 ) ? 1 : - 1 ) * screenDist ( dx ) ;
2021-04-20 07:02:49 +02:00
* outY = ( screen_height > > 1 ) + ( ( dy > 0 ) ? - 1 : 1 ) * screenDist ( dy ) ;
2019-09-09 06:23:38 +02:00
}
2020-03-08 02:22:20 +01:00
int View : : outOfBounds ( int x , int y ) {
2020-06-18 07:01:18 +02:00
return outOfBounds ( x , y , 0 , 0 , screen_width , screen_height ) ;
}
int View : : outOfBounds ( int x , int y , int left , int top , int right , int bottom ) {
if ( x < left | | x > = right | | y < top | | y > = bottom ) {
2019-09-09 06:23:38 +02:00
return 1 ;
} else {
return 0 ;
}
}
2021-03-20 18:01:36 +01:00
//
// Fonts should probably go in Style
//
2020-03-19 06:22:59 +01:00
2020-06-17 00:03:05 +02:00
TTF_Font * View : : loadFont ( const char * name , int size )
2020-03-19 06:22:59 +01:00
{
TTF_Font * font = TTF_OpenFont ( name , size ) ;
if ( font = = NULL )
{
printf ( " Failed to open Font %s: %s \n " , name , TTF_GetError ( ) ) ;
exit ( 1 ) ;
}
return font ;
}
void View : : closeFont ( TTF_Font * font )
{
if ( font ! = NULL )
{
TTF_CloseFont ( font ) ;
}
}
2021-03-20 05:13:43 +01:00
void View : : font_init ( ) {
mapFont = loadFont ( " font/TerminusTTF-4.46.0.ttf " , 12 * screen_uiscale ) ;
mapBoldFont = loadFont ( " font/TerminusTTF-Bold-4.46.0.ttf " , 12 * screen_uiscale ) ;
listFont = loadFont ( " font/TerminusTTF-4.46.0.ttf " , 12 * screen_uiscale ) ;
messageFont = loadFont ( " font/TerminusTTF-Bold-4.46.0.ttf " , 12 * screen_uiscale ) ;
labelFont = loadFont ( " font/TerminusTTF-Bold-4.46.0.ttf " , 12 * screen_uiscale ) ;
mapFontWidth = 5 * screen_uiscale ;
mapFontHeight = 12 * screen_uiscale ;
messageFontWidth = 6 * screen_uiscale ;
messageFontHeight = 12 * screen_uiscale ;
labelFontWidth = 6 * screen_uiscale ;
labelFontHeight = 12 * screen_uiscale ;
}
2020-03-19 06:22:59 +01:00
//
// SDL Utils
//
void View : : SDL_init ( ) {
if ( SDL_Init ( SDL_INIT_VIDEO ) < 0 ) {
printf ( " Could not initialize SDL: %s \n " , SDL_GetError ( ) ) ;
exit ( 1 ) ;
}
if ( TTF_Init ( ) < 0 ) {
printf ( " Couldn't initialize SDL TTF: %s \n " , SDL_GetError ( ) ) ;
exit ( 1 ) ;
}
SDL_ShowCursor ( SDL_DISABLE ) ;
Uint32 flags = 0 ;
if ( fullscreen ) {
flags = flags | SDL_WINDOW_FULLSCREEN_DESKTOP ;
}
if ( screen_width = = 0 ) {
SDL_DisplayMode DM ;
SDL_GetCurrentDisplayMode ( 0 , & DM ) ;
screen_width = DM . w ;
screen_height = DM . h ;
}
2020-06-11 09:11:52 +02:00
window = SDL_CreateWindow ( " viz1090 " , SDL_WINDOWPOS_CENTERED_DISPLAY ( screen_index ) , SDL_WINDOWPOS_CENTERED_DISPLAY ( screen_index ) , screen_width , screen_height , flags ) ;
2022-10-21 20:06:07 +02:00
renderer = SDL_CreateRenderer ( window , - 1 , SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC ) ;
2020-03-19 06:22:59 +01:00
mapTexture = SDL_CreateTexture ( renderer ,
SDL_PIXELFORMAT_ARGB8888 ,
SDL_TEXTUREACCESS_TARGET ,
screen_width , screen_height ) ;
mapMoved = 1 ;
mapTargetLon = 0 ;
mapTargetLat = 0 ;
mapTargetMaxDist = 0 ;
if ( fullscreen ) {
//SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother.
SDL_RenderSetLogicalSize ( renderer , screen_width , screen_height ) ;
}
}
//
2021-03-20 05:13:43 +01:00
// Status boxes -> move to separate class
2020-03-19 06:22:59 +01:00
//
2020-06-17 00:03:05 +02:00
void View : : drawStatusBox ( int * left , int * top , std : : string label , std : : string message , SDL_Color color ) {
int labelWidth = ( label . length ( ) + ( ( label . length ( ) > 0 ) ? 1 : 0 ) ) * labelFontWidth ;
int messageWidth = ( message . length ( ) + ( ( message . length ( ) > 0 ) ? 1 : 0 ) ) * messageFontWidth ;
2020-03-19 06:22:59 +01:00
if ( * left + labelWidth + messageWidth + PAD > screen_width ) {
* left = PAD ;
* top = * top - messageFontHeight - PAD ;
}
// filled black background
if ( messageWidth ) {
2020-06-23 07:21:47 +02:00
roundedBoxRGBA ( renderer , * left , * top , * left + labelWidth + messageWidth , * top + messageFontHeight , ROUND_RADIUS , style . buttonBackground . r , style . buttonBackground . g , style . buttonBackground . b , SDL_ALPHA_OPAQUE ) ;
2020-03-19 06:22:59 +01:00
}
// filled label box
if ( labelWidth ) {
roundedBoxRGBA ( renderer , * left , * top , * left + labelWidth , * top + messageFontHeight , ROUND_RADIUS , color . r , color . g , color . b , SDL_ALPHA_OPAQUE ) ;
}
// outline message box
if ( messageWidth ) {
roundedRectangleRGBA ( renderer , * left , * top , * left + labelWidth + messageWidth , * top + messageFontHeight , ROUND_RADIUS , color . r , color . g , color . b , SDL_ALPHA_OPAQUE ) ;
}
2021-03-20 05:13:43 +01:00
Label currentLabel ;
currentLabel . setFont ( labelFont ) ;
2021-03-20 19:43:48 +01:00
currentLabel . setColor ( style . buttonBackground ) ;
2021-03-20 05:13:43 +01:00
currentLabel . setPosition ( * left + labelFontWidth / 2 , * top ) ;
currentLabel . setText ( label ) ;
currentLabel . draw ( renderer ) ;
currentLabel . setFont ( messageFont ) ;
2021-03-20 19:43:48 +01:00
currentLabel . setColor ( color ) ;
2021-03-20 05:13:43 +01:00
currentLabel . setPosition ( * left + labelWidth + messageFontWidth / 2 , * top ) ;
currentLabel . setText ( message ) ;
currentLabel . draw ( renderer ) ;
2020-03-19 06:22:59 +01:00
* left = * left + labelWidth + messageWidth + PAD ;
}
2022-10-22 02:37:39 +02:00
void View : : drawCenteredStatusBox ( std : : string label , std : : string message , SDL_Color color ) {
int labelWidth = ( label . length ( ) + ( ( label . length ( ) > 0 ) ? 1 : 0 ) ) * labelFontWidth ;
int messageWidth = ( message . length ( ) + ( ( message . length ( ) > 0 ) ? 1 : 0 ) ) * messageFontWidth ;
int left = ( screen_width - ( labelWidth + messageWidth ) ) / 2 ;
int top = ( screen_height - labelFontHeight ) / 2 ;
drawStatusBox ( & left , & top , label , message , color ) ;
}
2020-03-19 06:22:59 +01:00
void View : : drawStatus ( ) {
int left = PAD ;
int top = screen_height - messageFontHeight - PAD ;
2022-10-22 02:37:39 +02:00
if ( fps ) {
char fps [ 60 ] = " " ;
snprintf ( fps , 40 , " %.1f " , 1000.0 / lastFrameTime ) ;
2022-10-23 20:34:22 +02:00
drawStatusBox ( & left , & top , " fps " , fps , style . grey_dark ) ;
2022-10-22 02:37:39 +02:00
}
2020-03-19 06:22:59 +01:00
2022-10-22 02:37:39 +02:00
if ( ! appData - > connected ) {
2022-10-23 20:34:22 +02:00
drawStatusBox ( & left , & top , " init " , " connecting " , style . red ) ;
2022-10-22 02:37:39 +02:00
} else {
char strLoc [ 20 ] = " " ;
snprintf ( strLoc , 20 , " %3.3fN %3.3f%c " , centerLat , fabs ( centerLon ) , ( centerLon > 0 ) ? ' E ' : ' W ' ) ;
drawStatusBox ( & left , & top , " loc " , strLoc , style . buttonColor ) ;
2020-03-19 06:22:59 +01:00
2022-10-22 02:37:39 +02:00
char strPlaneCount [ 10 ] = " " ;
snprintf ( strPlaneCount , 10 , " %d/%d " , appData - > numVisiblePlanes , appData - > numPlanes ) ;
drawStatusBox ( & left , & top , " disp " , strPlaneCount , style . buttonColor ) ;
2020-03-19 06:22:59 +01:00
2022-10-22 02:37:39 +02:00
char strMsgRate [ 18 ] = " " ;
snprintf ( strMsgRate , 18 , " %.0f/s " , appData - > msgRate ) ;
drawStatusBox ( & left , & top , " rate " , strMsgRate , style . buttonColor ) ;
char strSig [ 18 ] = " " ;
snprintf ( strSig , 18 , " %.0f%% " , 100.0 * appData - > avgSig / 1024.0 ) ;
drawStatusBox ( & left , & top , " sAvg " , strSig , style . buttonColor ) ;
}
2020-03-19 06:22:59 +01:00
2022-10-22 02:37:39 +02:00
if ( map . loaded < 100 ) {
char loaded [ 20 ] = " " ;
snprintf ( loaded , 20 , " loading map %d%% " , map . loaded ) ;
2022-10-23 20:34:22 +02:00
drawStatusBox ( & left , & top , " init " , loaded , style . orange ) ;
2022-10-22 02:37:39 +02:00
}
2020-03-19 06:22:59 +01:00
}
//
// Main drawing
//
2020-03-08 02:22:20 +01:00
void View : : drawPlaneOffMap ( int x , int y , int * returnx , int * returny , SDL_Color planeColor ) {
2019-09-09 06:23:38 +02:00
2020-03-19 06:22:59 +01:00
float arrowWidth = 6.0 * screen_uiscale ;
2019-09-09 06:23:38 +02:00
2020-03-19 06:22:59 +01:00
float inx = x - ( screen_width > > 1 ) ;
2021-04-20 07:02:49 +02:00
float iny = y - ( screen_height > > 1 ) ;
2019-09-09 06:23:38 +02:00
float outx , outy ;
outx = inx ;
outy = iny ;
2021-04-20 07:02:49 +02:00
if ( abs ( inx ) > abs ( y - ( screen_height > > 1 ) ) * static_cast < float > ( screen_width > > 1 ) / static_cast < float > ( screen_height > > 1 ) ) { //left / right quadrants
2020-03-19 06:22:59 +01:00
outx = ( screen_width > > 1 ) * ( ( inx > 0 ) ? 1.0 : - 1.0 ) ;
2019-09-09 06:23:38 +02:00
outy = ( outx ) * iny / ( inx ) ;
} else { // up / down quadrants
2021-04-20 07:02:49 +02:00
outy = screen_height * ( ( iny > 0 ) ? 0.5 : - 0.5 ) ;
2019-09-09 06:23:38 +02:00
outx = ( outy ) * inx / ( iny ) ;
}
2020-03-19 06:22:59 +01:00
// circleRGBA (renderer,(screen_width>>1) + outx, screen_height * CENTEROFFSET + outy,50,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE);
// thickLineRGBA(renderer,screen_width>>1,screen_height * CENTEROFFSET, (screen_width>>1) + outx, screen_height * CENTEROFFSET + outy,arrowWidth,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE);
2019-09-09 06:23:38 +02:00
2020-02-27 07:44:30 +01:00
float inmag = sqrt ( inx * inx + iny * iny ) ;
float vec [ 3 ] ;
2019-09-09 06:23:38 +02:00
vec [ 0 ] = inx / inmag ;
vec [ 1 ] = iny / inmag ;
vec [ 2 ] = 0 ;
2020-02-27 07:44:30 +01:00
float up [ ] = { 0 , 0 , 1 } ;
2019-09-09 06:23:38 +02:00
2020-02-27 07:44:30 +01:00
float out [ 3 ] ;
2019-09-09 06:23:38 +02:00
CROSSVP ( out , vec , up ) ;
int x1 , x2 , x3 , y1 , y2 , y3 ;
// arrow 1
2020-03-19 06:22:59 +01:00
x1 = ( screen_width > > 1 ) + outx - 2.0 * arrowWidth * vec [ 0 ] + round ( - arrowWidth * out [ 0 ] ) ;
2021-04-20 07:02:49 +02:00
y1 = ( screen_height > > 1 ) + outy - 2.0 * arrowWidth * vec [ 1 ] + round ( - arrowWidth * out [ 1 ] ) ;
2020-03-19 06:22:59 +01:00
x2 = ( screen_width > > 1 ) + outx - 2.0 * arrowWidth * vec [ 0 ] + round ( arrowWidth * out [ 0 ] ) ;
2021-04-20 07:02:49 +02:00
y2 = ( screen_height > > 1 ) + outy - 2.0 * arrowWidth * vec [ 1 ] + round ( arrowWidth * out [ 1 ] ) ;
2020-03-19 06:22:59 +01:00
x3 = ( screen_width > > 1 ) + outx - arrowWidth * vec [ 0 ] ;
2021-04-20 07:02:49 +02:00
y3 = ( screen_height > > 1 ) + outy - arrowWidth * vec [ 1 ] ;
2022-10-27 21:18:47 +02:00
filledTrigonRGBA ( renderer , x1 , y1 , x2 , y2 , x3 , y3 , planeColor . r , planeColor . g , planeColor . b , SDL_ALPHA_OPAQUE ) ;
2019-09-09 06:23:38 +02:00
// arrow 2
2020-03-19 06:22:59 +01:00
x1 = ( screen_width > > 1 ) + outx - 3.0 * arrowWidth * vec [ 0 ] + round ( - arrowWidth * out [ 0 ] ) ;
2021-04-20 07:02:49 +02:00
y1 = ( screen_height > > 1 ) + outy - 3.0 * arrowWidth * vec [ 1 ] + round ( - arrowWidth * out [ 1 ] ) ;
2020-03-19 06:22:59 +01:00
x2 = ( screen_width > > 1 ) + outx - 3.0 * arrowWidth * vec [ 0 ] + round ( arrowWidth * out [ 0 ] ) ;
2021-04-20 07:02:49 +02:00
y2 = ( screen_height > > 1 ) + outy - 3.0 * arrowWidth * vec [ 1 ] + round ( arrowWidth * out [ 1 ] ) ;
2020-03-19 06:22:59 +01:00
x3 = ( screen_width > > 1 ) + outx - 2.0 * arrowWidth * vec [ 0 ] ;
2021-04-20 07:02:49 +02:00
y3 = ( screen_height > > 1 ) + outy - 2.0 * arrowWidth * vec [ 1 ] ;
2022-10-27 21:18:47 +02:00
filledTrigonRGBA ( renderer , x1 , y1 , x2 , y2 , x3 , y3 , planeColor . r , planeColor . g , planeColor . b , SDL_ALPHA_OPAQUE ) ;
2019-09-09 06:23:38 +02:00
* returnx = x3 ;
* returny = y3 ;
}
2020-03-08 02:22:20 +01:00
void View : : drawPlaneIcon ( int x , int y , float heading , SDL_Color planeColor )
2019-09-09 06:23:38 +02:00
{
2020-03-19 06:22:59 +01:00
float body = 8.0 * screen_uiscale ;
float wing = 6.0 * screen_uiscale ;
2022-10-21 20:06:07 +02:00
float wingThick = 0.5 ;
2020-03-19 06:22:59 +01:00
float tail = 3.0 * screen_uiscale ;
2022-10-22 02:37:39 +02:00
float tailThick = 0.35 ;
2020-06-27 05:40:03 +02:00
float bodyWidth = screen_uiscale ;
2019-09-09 06:23:38 +02:00
2020-02-27 07:44:30 +01:00
float vec [ 3 ] ;
2019-09-09 06:23:38 +02:00
vec [ 0 ] = sin ( heading * M_PI / 180 ) ;
vec [ 1 ] = - cos ( heading * M_PI / 180 ) ;
vec [ 2 ] = 0 ;
2020-02-27 07:44:30 +01:00
float up [ ] = { 0 , 0 , 1 } ;
2019-09-09 06:23:38 +02:00
2020-02-27 07:44:30 +01:00
float out [ 3 ] ;
2019-09-09 06:23:38 +02:00
CROSSVP ( out , vec , up ) ;
int x1 , x2 , y1 , y2 ;
//body
2020-06-27 05:40:03 +02:00
x1 = x + round ( - bodyWidth * out [ 0 ] ) ;
y1 = y + round ( - bodyWidth * out [ 1 ] ) ;
x2 = x + round ( bodyWidth * out [ 0 ] ) ;
y2 = y + round ( bodyWidth * out [ 1 ] ) ;
2019-09-09 06:23:38 +02:00
2022-10-22 02:37:39 +02:00
filledTrigonRGBA ( renderer , x1 , y1 , x2 , y2 , x + round ( - body * vec [ 0 ] ) , y + round ( - body * vec [ 1 ] ) , planeColor . r , planeColor . g , planeColor . b , SDL_ALPHA_OPAQUE ) ;
filledTrigonRGBA ( renderer , x1 , y1 , x2 , y2 , x + round ( body * vec [ 0 ] ) , y + round ( body * vec [ 1 ] ) , planeColor . r , planeColor . g , planeColor . b , SDL_ALPHA_OPAQUE ) ;
2020-06-27 05:40:03 +02:00
2022-10-22 02:37:39 +02:00
//x1 = x + round(8*vec[0]);
//y1 = y + round(8*vec[1]);
//x2 = x + round(16*vec[0]);
//y2 = y + round(16*vec[1]);
2020-06-27 05:40:03 +02:00
2022-10-22 02:37:39 +02:00
//lineRGBA(renderer,x1,y1,x2,y2, style.white.r, style.white.g, style.white.b, SDL_ALPHA_OPAQUE);
2020-06-27 05:40:03 +02:00
// x1 = x + round(-body*vec[0] + bodyWidth*out[0]);
// y1 = y + round(-body*vec[1] + bodyWidth*out[1]);
// x2 = x + round(body*vec[0] + bodyWidth*out[0]);
// y2 = y + round(body*vec[1] + bodyWidth*out[1]);
// lineRGBA(renderer,x,y,x2,y2,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE);
//trigonRGBA(renderer, x + round(-wing*.35*out[0]), y + round(-wing*.35*out[1]), x + round(wing*.35*out[0]), y + round(wing*.35*out[1]), x1, y1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE);
// circleRGBA(renderer, x2,y2,screen_uiscale,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE);
2019-09-09 06:23:38 +02:00
//wing
x1 = x + round ( - wing * out [ 0 ] ) ;
y1 = y + round ( - wing * out [ 1 ] ) ;
x2 = x + round ( wing * out [ 0 ] ) ;
y2 = y + round ( wing * out [ 1 ] ) ;
2022-10-20 07:11:44 +02:00
filledTrigonRGBA ( renderer , x1 , y1 , x2 , y2 , x + round ( body * wingThick * vec [ 0 ] ) , y + round ( body * wingThick * vec [ 1 ] ) , planeColor . r , planeColor . g , planeColor . b , SDL_ALPHA_OPAQUE ) ;
2019-09-09 06:23:38 +02:00
//tail
2022-10-22 02:37:39 +02:00
x1 = x + round ( - body * .75 * vec [ 0 ] - tail * out [ 0 ] ) ;
y1 = y + round ( - body * .75 * vec [ 1 ] - tail * out [ 1 ] ) ;
x2 = x + round ( - body * .75 * vec [ 0 ] + tail * out [ 0 ] ) ;
y2 = y + round ( - body * .75 * vec [ 1 ] + tail * out [ 1 ] ) ;
2019-09-09 06:23:38 +02:00
2022-10-20 07:11:44 +02:00
filledTrigonRGBA ( renderer , x1 , y1 , x2 , y2 , x + round ( - body * tailThick * vec [ 0 ] ) , y + round ( - body * tailThick * vec [ 1 ] ) , planeColor . r , planeColor . g , planeColor . b , SDL_ALPHA_OPAQUE ) ;
2019-09-09 06:23:38 +02:00
}
2020-06-18 07:01:18 +02:00
void View : : drawTrails ( int left , int top , int right , int bottom ) {
2023-06-01 01:58:22 +02:00
int currentX , currentY , prevX , prevY , colorVal ;
2020-06-15 00:26:26 +02:00
float dx , dy ;
2019-09-09 06:23:38 +02:00
2020-06-18 07:01:18 +02:00
Aircraft * p = appData - > aircraftList . head ;
2023-06-01 01:58:22 +02:00
int count = 0 ;
2020-06-18 07:01:18 +02:00
while ( p ) {
2023-06-01 01:58:22 +02:00
if ( p - > lonHistory . empty ( ) ) {
p = p - > next ;
continue ;
}
2019-09-09 06:23:38 +02:00
2020-06-18 07:01:18 +02:00
std : : vector < float > : : iterator lon_idx = p - > lonHistory . begin ( ) ;
std : : vector < float > : : iterator lat_idx = p - > latHistory . begin ( ) ;
std : : vector < float > : : iterator heading_idx = p - > headingHistory . begin ( ) ;
2020-02-19 00:37:41 +01:00
2020-06-23 07:21:47 +02:00
float age = 0 ;
2019-09-09 06:23:38 +02:00
2020-06-23 07:21:47 +02:00
for ( ; std : : next ( lon_idx ) ! = p - > lonHistory . end ( ) ; + + lon_idx , + + lat_idx , + + heading_idx , age + = 1.0 ) {
2019-09-09 06:23:38 +02:00
2020-06-18 07:01:18 +02:00
pxFromLonLat ( & dx , & dy , * ( std : : next ( lon_idx ) ) , * ( std : : next ( lat_idx ) ) ) ;
screenCoords ( & currentX , & currentY , dx , dy ) ;
2019-09-09 06:23:38 +02:00
2020-06-18 07:01:18 +02:00
pxFromLonLat ( & dx , & dy , * lon_idx , * lat_idx ) ;
2019-09-09 06:23:38 +02:00
2020-06-18 07:01:18 +02:00
screenCoords ( & prevX , & prevY , dx , dy ) ;
if ( outOfBounds ( currentX , currentY , left , top , right , bottom ) & & outOfBounds ( prevX , prevY , left , top , right , bottom ) ) {
continue ;
}
2019-09-09 06:23:38 +02:00
2022-09-22 07:22:09 +02:00
SDL_Color color = lerpColor ( { 255 , 0 , 0 , 255 } , { 255 , 200 , 0 , 255 } , age / static_cast < float > ( p - > lonHistory . size ( ) ) ) ;
2023-06-01 01:58:22 +02:00
color = lerpColor ( color , style . planeGoneColor , elapsed_s ( p - > msSeen ) / DISPLAY_ACTIVE ) ;
color = lerpColor ( color , style . black , - 1.0f + ( elapsed_s ( p - > msSeen ) / DISPLAY_ACTIVE ) ) ;
2022-10-30 01:06:00 +02:00
2023-06-01 01:58:22 +02:00
colorVal = ( uint8_t ) clamp ( 512.0 * ( age / static_cast < float > ( p - > lonHistory . size ( ) ) ) , 0 , 255 ) ;
2020-06-27 05:40:03 +02:00
lineRGBA ( renderer , prevX , prevY , currentX , currentY , color . r , color . g , color . b , colorVal ) ;
2023-06-01 01:58:22 +02:00
}
2020-03-13 00:56:26 +01:00
2023-06-01 01:58:22 +02:00
if ( elapsed_s ( p - > msSeen ) > DISPLAY_ACTIVE ) {
//lineRGBA(renderer, currentX-4, currentY-4, currentX+4, currentY+4, style.planeGoneColor.r, style.planeGoneColor.g, style.planeGoneColor.b, colorVal);
//lineRGBA(renderer, currentX+4, currentY-4, currentX-4, currentY+4, style.planeGoneColor.r, style.planeGoneColor.g, style.planeGoneColor.b, colorVal);
SDL_Color color = lerpColor ( style . planeGoneColor , style . black , - 1.0f + ( elapsed_s ( p - > msSeen ) / DISPLAY_ACTIVE ) ) ;
circleRGBA ( renderer , currentX , currentY , 5 , color . r , color . g , color . b , colorVal ) ;
2020-06-18 07:01:18 +02:00
}
p = p - > next ;
2019-09-09 06:23:38 +02:00
}
}
2020-03-08 02:22:20 +01:00
void View : : drawScaleBars ( )
2019-09-09 06:23:38 +02:00
{
2020-02-08 08:04:52 +01:00
int scalePower = 0 ;
int scaleBarDist = screenDist ( ( float ) pow ( 10 , scalePower ) ) ;
2020-03-02 07:58:10 +01:00
char scaleLabel [ 13 ] = " " ;
2020-02-08 08:04:52 +01:00
2020-03-19 06:22:59 +01:00
lineRGBA ( renderer , 10 , 10 , 10 , 10 * screen_uiscale , style . scaleBarColor . r , style . scaleBarColor . g , style . scaleBarColor . b , 255 ) ;
2020-02-08 08:04:52 +01:00
2020-03-19 06:22:59 +01:00
while ( scaleBarDist < screen_width ) {
lineRGBA ( renderer , 10 + scaleBarDist , 8 , 10 + scaleBarDist , 16 * screen_uiscale , style . scaleBarColor . r , style . scaleBarColor . g , style . scaleBarColor . b , 255 ) ;
2020-02-08 08:04:52 +01:00
2020-03-19 06:22:59 +01:00
if ( metric ) {
2021-04-20 07:02:49 +02:00
snprintf ( scaleLabel , 13 , " %dkm " , static_cast < int > ( pow ( 10 , scalePower ) ) ) ;
2020-02-08 08:04:52 +01:00
} else {
2021-04-20 07:02:49 +02:00
snprintf ( scaleLabel , 13 , " %dmi " , static_cast < int > ( pow ( 10 , scalePower ) ) ) ;
2020-02-08 08:04:52 +01:00
}
2021-03-20 05:13:43 +01:00
Label currentLabel ;
currentLabel . setFont ( mapFont ) ;
2021-03-20 19:43:48 +01:00
currentLabel . setColor ( style . scaleBarColor ) ;
2021-03-20 05:13:43 +01:00
currentLabel . setPosition ( 10 + scaleBarDist , 15 * screen_uiscale ) ;
currentLabel . setText ( scaleLabel ) ;
currentLabel . draw ( renderer ) ;
2020-02-08 08:04:52 +01:00
scalePower + + ;
2021-04-20 07:02:49 +02:00
scaleBarDist = screenDist ( powf ( 10 , scalePower ) ) ;
2020-02-08 08:04:52 +01:00
}
scalePower - - ;
2021-04-20 07:02:49 +02:00
scaleBarDist = screenDist ( powf ( 10 , scalePower ) ) ;
2020-02-08 08:04:52 +01:00
2020-03-19 06:22:59 +01:00
lineRGBA ( renderer , 10 , 10 + 5 * screen_uiscale , 10 + scaleBarDist , 10 + 5 * screen_uiscale , style . scaleBarColor . r , style . scaleBarColor . g , style . scaleBarColor . b , 255 ) ;
2019-09-09 06:23:38 +02:00
}
2020-06-18 07:01:18 +02:00
void View : : drawLines ( int left , int top , int right , int bottom , int bailTime ) {
float screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max ;
latLonFromScreenCoords ( & screen_lat_min , & screen_lon_min , left , top ) ;
latLonFromScreenCoords ( & screen_lat_max , & screen_lon_max , right , bottom ) ;
2020-06-23 07:21:47 +02:00
drawLinesRecursive ( & ( map . root ) , screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max , style . geoColor ) ;
2020-06-21 06:44:30 +02:00
2020-06-23 07:21:47 +02:00
drawLinesRecursive ( & ( map . airport_root ) , screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max , style . airportColor ) ;
2020-06-18 07:01:18 +02:00
drawTrails ( left , top , right , bottom ) ;
}
2020-06-21 06:44:30 +02:00
void View : : drawLinesRecursive ( QuadTree * tree , float screen_lat_min , float screen_lat_max , float screen_lon_min , float screen_lon_max , SDL_Color color ) {
2020-06-27 05:40:03 +02:00
2020-06-14 07:15:47 +02:00
if ( tree = = NULL ) {
return ;
}
if ( tree - > lat_min > screen_lat_max | | screen_lat_min > tree - > lat_max ) {
return ;
}
if ( tree - > lon_min > screen_lon_max | | screen_lon_min > tree - > lon_max ) {
return ;
}
2020-06-21 06:44:30 +02:00
drawLinesRecursive ( tree - > nw , screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max , color ) ;
2020-06-14 07:15:47 +02:00
2020-06-21 06:44:30 +02:00
drawLinesRecursive ( tree - > sw , screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max , color ) ;
2020-06-14 07:15:47 +02:00
2020-06-21 06:44:30 +02:00
drawLinesRecursive ( tree - > ne , screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max , color ) ;
2020-06-14 07:15:47 +02:00
2020-06-21 06:44:30 +02:00
drawLinesRecursive ( tree - > se , screen_lat_min , screen_lat_max , screen_lon_min , screen_lon_max , color ) ;
2020-06-14 07:15:47 +02:00
std : : vector < Line * > : : iterator currentLine ;
for ( currentLine = tree - > lines . begin ( ) ; currentLine ! = tree - > lines . end ( ) ; + + currentLine ) {
int x1 , y1 , x2 , y2 ;
float dx , dy ;
pxFromLonLat ( & dx , & dy , ( * currentLine ) - > start . lon , ( * currentLine ) - > start . lat ) ;
screenCoords ( & x1 , & y1 , dx , dy ) ;
pxFromLonLat ( & dx , & dy , ( * currentLine ) - > end . lon , ( * currentLine ) - > end . lat ) ;
screenCoords ( & x2 , & y2 , dx , dy ) ;
if ( outOfBounds ( x1 , y1 ) & & outOfBounds ( x2 , y2 ) ) {
continue ;
}
if ( x1 = = x2 & & y1 = = y2 ) {
continue ;
}
2020-06-21 06:44:30 +02:00
lineRGBA ( renderer , x1 , y1 , x2 , y2 , color . r , color . g , color . b , 255 ) ;
2020-06-14 07:15:47 +02:00
}
2020-06-19 04:29:45 +02:00
// //Debug quadtree
// int tl_x,tl_y,tr_x,tr_y,bl_x,bl_y,br_x,br_y;
// float dx,dy;
2020-06-18 08:42:39 +02:00
2020-06-19 04:29:45 +02:00
// pxFromLonLat(&dx, &dy, tree->lon_min, tree->lat_min);
// screenCoords(&tl_x, &tl_y, dx, dy);
2020-06-18 08:42:39 +02:00
2020-06-19 04:29:45 +02:00
// pxFromLonLat(&dx, &dy, tree->lon_max, tree->lat_min);
// screenCoords(&tr_x, &tr_y, dx, dy);
2020-06-18 08:42:39 +02:00
2020-06-19 04:29:45 +02:00
// pxFromLonLat(&dx, &dy, tree->lon_min, tree->lat_max);
// screenCoords(&bl_x, &bl_y, dx, dy);
2020-06-18 08:42:39 +02:00
2020-06-19 04:29:45 +02:00
// pxFromLonLat(&dx, &dy, tree->lon_max, tree->lat_max);
// screenCoords(&br_x, &br_y, dx, dy);
2020-06-18 08:42:39 +02:00
2020-06-19 04:29:45 +02:00
// lineRGBA(renderer, tl_x, tl_y, tr_x, tr_y, 50, 50, 50, 255);
// lineRGBA(renderer, tr_x, tr_y, br_x, br_y, 50, 50, 50, 255);
// lineRGBA(renderer, bl_x, bl_y, br_x, br_y, 50, 50, 50, 255);
// lineRGBA(renderer, tl_x, tl_y, bl_x, bl_y, 50, 50, 50, 255);
// // pixelRGBA(renderer,tl_x, tl_y,255,0,0,255);
// pixelRGBA(renderer,tr_x, tr_y,0,255,0,255);
// pixelRGBA(renderer,bl_x, bl_y,0,0,255,255);
// pixelRGBA(renderer,br_x, br_y,255,255,0,255);
2020-06-14 07:15:47 +02:00
}
2020-06-20 07:04:57 +02:00
void View : : drawPlaceNames ( ) {
2021-03-20 19:43:48 +01:00
//pre-generating labels in map will trade memory for TTF calls - need to compare when there are a lot of labels on screen
2021-03-20 05:13:43 +01:00
Label currentLabel ;
currentLabel . setFont ( mapFont ) ;
2021-03-20 19:43:48 +01:00
currentLabel . setColor ( style . geoColor ) ;
2021-03-20 05:13:43 +01:00
for ( std : : vector < MapLabel * > : : iterator label = map . mapnames . begin ( ) ; label ! = map . mapnames . end ( ) ; + + label ) {
2020-06-20 07:04:57 +02:00
float dx , dy ;
int x , y ;
pxFromLonLat ( & dx , & dy , ( * label ) - > location . lon , ( * label ) - > location . lat ) ;
screenCoords ( & x , & y , dx , dy ) ;
if ( outOfBounds ( x , y ) ) {
continue ;
}
2021-03-20 05:13:43 +01:00
currentLabel . setText ( ( * label ) - > text ) ;
currentLabel . setPosition ( x , y ) ;
currentLabel . draw ( renderer ) ;
2020-06-20 07:04:57 +02:00
}
2020-06-21 06:44:30 +02:00
2021-03-20 05:13:43 +01:00
for ( std : : vector < MapLabel * > : : iterator label = map . airportnames . begin ( ) ; label ! = map . airportnames . end ( ) ; + + label ) {
2020-06-21 06:44:30 +02:00
float dx , dy ;
int x , y ;
pxFromLonLat ( & dx , & dy , ( * label ) - > location . lon , ( * label ) - > location . lat ) ;
screenCoords ( & x , & y , dx , dy ) ;
if ( outOfBounds ( x , y ) ) {
continue ;
}
2021-03-20 05:13:43 +01:00
currentLabel . setText ( ( * label ) - > text ) ;
currentLabel . setPosition ( x , y ) ;
currentLabel . draw ( renderer ) ;
2020-06-21 06:44:30 +02:00
}
2020-06-20 07:04:57 +02:00
}
2020-01-20 07:22:58 +01:00
2020-06-18 07:01:18 +02:00
void View : : drawGeography ( ) {
2020-01-22 08:24:29 +01:00
2022-10-27 21:18:47 +02:00
if ( ( mapRedraw & & ! mapMoved ) | | ( mapAnimating & & elapsed ( lastRedraw ) > 8 * FRAMETIME ) | | elapsed ( lastRedraw ) > 2000 | | ( map . loaded < 100 & & elapsed ( lastRedraw ) > 250 ) ) {
2020-01-20 07:22:58 +01:00
2020-06-18 07:01:18 +02:00
SDL_SetRenderTarget ( renderer , mapTexture ) ;
SDL_SetRenderDrawColor ( renderer , style . backgroundColor . r , style . backgroundColor . g , style . backgroundColor . b , 255 ) ;
2020-01-20 07:22:58 +01:00
2020-06-18 07:01:18 +02:00
SDL_RenderClear ( renderer ) ;
drawLines ( 0 , 0 , screen_width , screen_height , 0 ) ;
2020-06-20 07:04:57 +02:00
drawPlaceNames ( ) ;
2020-06-14 05:54:21 +02:00
2020-06-18 07:01:18 +02:00
SDL_SetRenderTarget ( renderer , NULL ) ;
2020-01-20 07:22:58 +01:00
2020-06-18 07:01:18 +02:00
mapMoved = 0 ;
mapRedraw = 0 ;
mapAnimating = 0 ;
2020-06-14 05:54:21 +02:00
2020-06-18 07:01:18 +02:00
lastRedraw = now ( ) ;
currentLon = centerLon ;
currentLat = centerLat ;
currentMaxDist = maxDist ;
2020-01-20 07:22:58 +01:00
}
2020-06-18 07:01:18 +02:00
SDL_SetRenderDrawColor ( renderer , style . backgroundColor . r , style . backgroundColor . g , style . backgroundColor . b , 255 ) ;
2020-01-20 07:22:58 +01:00
2020-06-18 07:01:18 +02:00
SDL_RenderClear ( renderer ) ;
2020-01-20 07:22:58 +01:00
2020-06-18 07:01:18 +02:00
int shiftx = 0 ;
int shifty = 0 ;
if ( mapMoved ) {
float dx , dy ;
int x1 , y1 , x2 , y2 ;
pxFromLonLat ( & dx , & dy , currentLon , currentLat ) ;
screenCoords ( & x1 , & y1 , dx , dy ) ;
pxFromLonLat ( & dx , & dy , centerLon , centerLat ) ;
screenCoords ( & x2 , & y2 , dx , dy ) ;
shiftx = x1 - x2 ;
shifty = y1 - y2 ;
SDL_Rect dest ;
dest . x = shiftx + ( screen_width / 2 ) * ( 1 - currentMaxDist / maxDist ) ;
dest . y = shifty + ( screen_height / 2 ) * ( 1 - currentMaxDist / maxDist ) ;
dest . w = screen_width * currentMaxDist / maxDist ;
dest . h = screen_height * currentMaxDist / maxDist ;
//left
if ( dest . x > 0 ) {
drawLines ( 0 , 0 , dest . x , screen_height , FRAMETIME / 4 ) ;
}
//top
if ( dest . y > 0 ) {
drawLines ( 0 , screen_height - dest . y , screen_width , screen_height , FRAMETIME / 4 ) ;
}
//right
if ( dest . x + dest . w < screen_width ) {
drawLines ( dest . x + dest . w , 0 , screen_width , screen_height , FRAMETIME / 4 ) ;
}
//bottom
if ( dest . y + dest . h < screen_height ) {
drawLines ( 0 , 0 , screen_width , screen_height - dest . y - dest . h , FRAMETIME / 4 ) ;
}
//attempt rest before bailing
//drawGeography(dest.x, screen_height - dest.y, dest.x + dest.w, screen_height - dest.y - dest.h, 1);
SDL_RenderCopy ( renderer , mapTexture , NULL , & dest ) ;
2020-06-14 05:54:21 +02:00
2020-06-18 07:01:18 +02:00
mapRedraw = 1 ;
mapMoved = 0 ;
} else {
SDL_RenderCopy ( renderer , mapTexture , NULL , NULL ) ;
}
2019-09-09 06:23:38 +02:00
}
2020-03-08 02:22:20 +01:00
void View : : drawPlaneText ( Aircraft * p ) {
2021-03-20 05:13:43 +01:00
if ( ! p - > label ) {
p - > label = new AircraftLabel ( p , metric , screen_width , screen_height , mapFont ) ;
2020-12-12 07:05:14 +01:00
}
2021-03-20 05:13:43 +01:00
p - > label - > update ( ) ;
2021-03-20 18:01:36 +01:00
p - > label - > draw ( renderer , ( p = = selectedAircraft ) ) ;
2020-02-18 07:47:42 +01:00
}
2019-09-09 08:17:40 +02:00
2022-09-22 07:22:09 +02:00
void View : : moveLabels ( float dx , float dy ) {
Aircraft * p = appData - > aircraftList . head ;
while ( p ) {
if ( p - > label ) {
p - > label - > move ( dx , dy ) ;
}
p = p - > next ;
}
}
2022-10-20 07:11:44 +02:00
void View : : resolveLabelConflicts ( ) {
2020-12-08 01:30:49 +01:00
Aircraft * p = appData - > aircraftList . head ;
while ( p ) {
2021-03-20 05:13:43 +01:00
if ( p - > label ) {
p - > label - > clearAcceleration ( ) ;
}
2020-12-08 01:30:49 +01:00
p = p - > next ;
}
p = appData - > aircraftList . head ;
2019-09-16 02:54:06 +02:00
while ( p ) {
2021-03-20 05:13:43 +01:00
if ( p - > label ) {
p - > label - > calculateForces ( appData - > aircraftList . head ) ;
2020-12-09 08:15:01 +01:00
}
2019-09-16 02:54:06 +02:00
p = p - > next ;
}
2020-03-19 06:22:59 +01:00
p = appData - > aircraftList . head ;
2019-09-16 02:54:06 +02:00
while ( p ) {
2020-12-08 01:30:49 +01:00
2021-03-20 05:13:43 +01:00
if ( p - > label ) {
p - > label - > applyForces ( ) ;
2022-10-20 07:11:44 +02:00
// if(p->label->getIsChanging()) {
// highFramerate = true;
// }
2020-12-08 01:30:49 +01:00
}
2021-03-20 05:13:43 +01:00
2019-09-16 02:54:06 +02:00
p = p - > next ;
}
2019-09-09 08:17:40 +02:00
}
2020-03-08 02:22:20 +01:00
void View : : drawPlanes ( ) {
2020-03-19 06:22:59 +01:00
Aircraft * p = appData - > aircraftList . head ;
2019-09-09 08:17:40 +02:00
SDL_Color planeColor ;
2019-09-09 06:23:38 +02:00
2020-03-19 06:22:59 +01:00
if ( selectedAircraft ) {
mapTargetLon = selectedAircraft - > lon ;
mapTargetLat = selectedAircraft - > lat ;
2020-02-18 08:17:19 +01:00
}
2020-03-19 06:22:59 +01:00
p = appData - > aircraftList . head ;
2019-09-09 06:23:38 +02:00
2019-09-09 08:17:40 +02:00
while ( p ) {
2020-03-08 05:28:55 +01:00
if ( p - > lon & & p - > lat ) {
2020-06-21 06:44:30 +02:00
// if lon lat argments were not provided, start by snapping to the first plane we see
if ( centerLon = = 0 & & centerLat = = 0 ) {
mapTargetLon = p - > lon ;
mapTargetLat = p - > lat ;
}
2020-03-08 05:28:55 +01:00
int x , y ;
2019-09-09 06:23:38 +02:00
2022-10-27 21:18:47 +02:00
float dx , dy ;
2020-03-08 05:28:55 +01:00
pxFromLonLat ( & dx , & dy , p - > lon , p - > lat ) ;
screenCoords ( & x , & y , dx , dy ) ;
2019-09-09 06:23:38 +02:00
2020-06-15 00:26:26 +02:00
float age_ms = elapsed ( p - > created ) ;
2020-03-08 05:28:55 +01:00
if ( age_ms < 500 ) {
2022-10-20 07:11:44 +02:00
//highFramerate = true;
2020-06-15 00:39:02 +02:00
float ratio = age_ms / 500.0f ;
float radius = ( 1.0f - ratio * ratio ) * screen_width / 8 ;
for ( float theta = 0 ; theta < 2 * M_PI ; theta + = M_PI / 4 ) {
pixelRGBA ( renderer , x + radius * cos ( theta ) , y + radius * sin ( theta ) , style . planeColor . r , style . planeColor . g , style . planeColor . b , 255 * ratio ) ;
}
// circleRGBA(renderer, x, y, 500 - age_ms, 255,255, 255, (uint8_t)(255.0 * age_ms / 500.0));
2022-10-27 21:18:47 +02:00
} else if ( 1000 * DISPLAY_ACTIVE - elapsed ( p - > msSeen ) > 500 ) {
2020-03-08 05:28:55 +01:00
if ( MODES_ACFLAGS_HEADING_VALID ) {
int usex = x ;
int usey = y ;
2022-10-20 07:11:44 +02:00
float useHeading = static_cast < float > ( p - > track ) ;
p - > x = usex ;
p - > y = usey ;
2020-03-08 05:28:55 +01:00
2021-04-20 07:02:49 +02:00
planeColor = lerpColor ( style . planeColor , style . planeGoneColor , elapsed_s ( p - > msSeen ) / DISPLAY_ACTIVE ) ;
2022-10-27 21:18:47 +02:00
if ( elapsed_s ( p - > msSeen ) > DISPLAY_ACTIVE / 2 ) {
arcRGBA ( renderer , x , y , 8 , 0 , 360 * 2.0 * ( elapsed_s ( p - > msSeen ) / DISPLAY_ACTIVE - 0.5 ) , planeColor . r , planeColor . g , planeColor . b , 255 ) ;
}
2020-03-19 06:22:59 +01:00
2020-03-22 04:30:23 +01:00
if ( p = = selectedAircraft ) {
planeColor = style . selectedColor ;
}
2022-10-20 07:11:44 +02:00
2020-03-08 05:28:55 +01:00
if ( outOfBounds ( x , y ) ) {
2021-03-20 05:13:43 +01:00
drawPlaneOffMap ( x , y , & ( p - > x ) , & ( p - > y ) , planeColor ) ;
2020-03-08 05:28:55 +01:00
} else {
2022-10-20 07:11:44 +02:00
if ( elapsed ( p - > msSeenLatLon ) < 500 ) {
//highFramerate = true;
circleRGBA ( renderer , p - > x , p - > y , elapsed ( p - > msSeenLatLon ) * screen_width / ( 8192 ) , 127 , 127 , 127 , 255 - ( uint8_t ) ( 255.0 * elapsed ( p - > msSeenLatLon ) / 500.0 ) ) ;
pxFromLonLat ( & dx , & dy , p - > getLastLon ( ) , p - > getLastLat ( ) ) ;
screenCoords ( & x , & y , dx , dy ) ;
usex = lerp ( x , usex , elapsed ( p - > msSeenLatLon ) / 500.0 ) ;
usey = lerp ( y , usey , elapsed ( p - > msSeenLatLon ) / 500.0 ) ;
useHeading = lerpAngle ( p - > getLastHeading ( ) , useHeading , elapsed ( p - > msSeenLatLon ) / 500.0 ) ;
}
2020-06-21 06:44:30 +02:00
2022-10-20 07:11:44 +02:00
drawPlaneIcon ( usex , usey , useHeading , planeColor ) ;
2020-03-08 05:28:55 +01:00
}
2020-12-09 08:15:01 +01:00
drawPlaneText ( p ) ;
2019-09-09 06:23:38 +02:00
}
2022-10-27 21:18:47 +02:00
} else {
circleRGBA ( renderer , x , y , 8 * ( 1000 * DISPLAY_ACTIVE - elapsed ( p - > msSeen ) ) / 500 , style . planeGoneColor . r , style . planeGoneColor . g , style . planeGoneColor . b , 255 ) ;
}
2019-09-09 06:23:38 +02:00
}
p = p - > next ;
}
}
2020-03-08 02:22:20 +01:00
void View : : animateCenterAbsolute ( float x , float y ) {
2020-03-19 06:22:59 +01:00
float scale_factor = ( screen_width > screen_height ) ? screen_width : screen_height ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
float dx = - 1.0 * ( 0.75 * ( double ) screen_width / ( double ) screen_height ) * ( x - screen_width / 2 ) * maxDist / ( 0.95 * scale_factor * 0.5 ) ;
float dy = 1.0 * ( y - screen_height / 2 ) * maxDist / ( 0.95 * scale_factor * 0.5 ) ;
2020-03-07 00:44:45 +01:00
2022-09-22 07:22:09 +02:00
moveLabels ( x , y ) ;
2020-03-07 00:44:45 +01:00
float outLat = dy * ( 1.0 / 6371.0 ) * ( 180.0f / M_PI ) ;
2020-03-19 06:22:59 +01:00
float outLon = dx * ( 1.0 / 6371.0 ) * ( 180.0f / M_PI ) / cos ( ( ( centerLat ) / 2.0f ) * M_PI / 180.0f ) ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
mapTargetLon = centerLon - outLon ;
mapTargetLat = centerLat - outLat ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
mapTargetMaxDist = 0.25 * maxDist ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
mapMoved = 1 ;
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-07 00:44:45 +01:00
}
2020-03-08 02:22:20 +01:00
void View : : moveCenterAbsolute ( float x , float y ) {
2020-03-19 06:22:59 +01:00
float scale_factor = ( screen_width > screen_height ) ? screen_width : screen_height ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
float dx = - 1.0 * ( 0.75 * ( double ) screen_width / ( double ) screen_height ) * ( x - screen_width / 2 ) * maxDist / ( 0.95 * scale_factor * 0.5 ) ;
float dy = 1.0 * ( y - screen_height / 2 ) * maxDist / ( 0.95 * scale_factor * 0.5 ) ;
2020-03-07 00:44:45 +01:00
2022-09-22 07:22:09 +02:00
moveLabels ( x , y ) ;
2020-03-07 00:44:45 +01:00
float outLat = dy * ( 1.0 / 6371.0 ) * ( 180.0f / M_PI ) ;
2020-03-19 06:22:59 +01:00
float outLon = dx * ( 1.0 / 6371.0 ) * ( 180.0f / M_PI ) / cos ( ( ( centerLat ) / 2.0f ) * M_PI / 180.0f ) ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
centerLon + = outLon ;
centerLat + = outLat ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
mapTargetLon = 0 ;
mapTargetLat = 0 ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
mapMoved = 1 ;
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-07 00:44:45 +01:00
}
2020-03-08 02:22:20 +01:00
void View : : moveCenterRelative ( float dx , float dy ) {
2020-03-06 00:02:40 +01:00
//
// need to make lonlat to screen conversion class - this is just the inverse of the stuff in draw.c, without offsets
//
2022-09-22 07:22:09 +02:00
moveLabels ( dx , dy ) ;
2020-03-19 06:22:59 +01:00
float scale_factor = ( screen_width > screen_height ) ? screen_width : screen_height ;
2020-03-06 00:02:40 +01:00
2020-06-12 06:55:04 +02:00
dx = - 1.0 * dx * maxDist / ( 0.95 * scale_factor * 0.5 ) ;
dy = 1.0 * dy * maxDist / ( 0.95 * scale_factor * 0.5 ) ;
2020-03-06 00:02:40 +01:00
2020-03-07 00:44:45 +01:00
float outLat = dy * ( 1.0 / 6371.0 ) * ( 180.0f / M_PI ) ;
2020-03-06 00:02:40 +01:00
2020-03-19 06:22:59 +01:00
float outLon = dx * ( 1.0 / 6371.0 ) * ( 180.0f / M_PI ) / cos ( ( ( centerLat ) / 2.0f ) * M_PI / 180.0f ) ;
2021-06-11 06:54:11 +02:00
2020-03-19 06:22:59 +01:00
centerLon + = outLon ;
centerLat + = outLat ;
2020-03-06 00:02:40 +01:00
2020-03-19 06:22:59 +01:00
mapTargetLon = 0 ;
mapTargetLat = 0 ;
2020-03-07 00:44:45 +01:00
2020-03-19 06:22:59 +01:00
mapMoved = 1 ;
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-06 00:02:40 +01:00
}
2020-03-08 02:22:20 +01:00
void View : : zoomMapToTarget ( ) {
2020-03-19 06:22:59 +01:00
if ( mapTargetMaxDist ) {
if ( fabs ( mapTargetMaxDist - maxDist ) > 0.0001 ) {
maxDist + = 0.1 * ( mapTargetMaxDist - maxDist ) ;
2020-06-16 23:40:03 +02:00
mapAnimating = 1 ;
mapMoved = 1 ;
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-07 00:44:45 +01:00
} else {
2020-03-19 06:22:59 +01:00
mapTargetMaxDist = 0 ;
2020-03-07 00:44:45 +01:00
}
}
}
2020-03-08 02:22:20 +01:00
void View : : moveMapToTarget ( ) {
2020-03-19 06:22:59 +01:00
if ( mapTargetLon & & mapTargetLat ) {
if ( fabs ( mapTargetLon - centerLon ) > 0.0001 | | fabs ( mapTargetLat - centerLat ) > 0.0001 ) {
centerLon + = 0.1 * ( mapTargetLon - centerLon ) ;
centerLat + = 0.1 * ( mapTargetLat - centerLat ) ;
2020-03-07 00:44:45 +01:00
2020-06-16 23:40:03 +02:00
mapAnimating = 1 ;
2020-03-19 06:22:59 +01:00
mapMoved = 1 ;
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-07 00:44:45 +01:00
} else {
2020-03-19 06:22:59 +01:00
mapTargetLon = 0 ;
mapTargetLat = 0 ;
2020-03-07 00:44:45 +01:00
}
}
}
2020-06-23 07:21:47 +02:00
// void View::drawMouse() {
// if(!mouseMoved) {
// return;
// }
2020-06-15 00:26:26 +02:00
2020-06-23 07:21:47 +02:00
// if(elapsed(mouseMovedTime) > 1000) {
// mouseMoved = false;
// return;
// }
2020-03-06 00:02:40 +01:00
2020-06-23 07:21:47 +02:00
// int alpha = (int)(255.0f - 255.0f * (float)elapsed(mouseMovedTime) / 1000.0f);
2020-03-06 00:02:40 +01:00
2020-06-23 07:21:47 +02:00
// lineRGBA(renderer, mousex - 10 * screen_uiscale, mousey, mousex + 10 * screen_uiscale, mousey, white.r, white.g, white.b, alpha);
// lineRGBA(renderer, mousex, mousey - 10 * screen_uiscale, mousex, mousey + 10 * screen_uiscale, white.r, white.g, white.b, alpha);
// }
2020-03-06 00:02:40 +01:00
2020-03-19 06:22:59 +01:00
void View : : drawClick ( ) {
if ( clickx & & clicky ) {
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-19 06:22:59 +01:00
2020-06-12 06:55:04 +02:00
int radius = .25 * elapsed ( clickTime ) ;
2021-04-20 07:02:49 +02:00
int alpha = 128 - static_cast < int > ( 0.5 * elapsed ( clickTime ) ) ;
2020-03-19 06:22:59 +01:00
if ( alpha < 0 ) {
alpha = 0 ;
clickx = 0 ;
clicky = 0 ;
}
2020-06-23 07:21:47 +02:00
filledCircleRGBA ( renderer , clickx , clicky , radius , style . clickColor . r , style . clickColor . g , style . clickColor . b , alpha ) ;
2020-03-19 06:22:59 +01:00
}
2020-03-22 04:30:23 +01:00
if ( selectedAircraft ) {
// this logic should be in input, register a callback for click?
int boxSize ;
2020-06-12 06:55:04 +02:00
if ( elapsed ( clickTime ) < 300 ) {
2021-04-20 07:02:49 +02:00
boxSize = static_cast < int > ( 20.0 * ( 1.0 - ( 1.0 - elapsed ( clickTime ) / 300.0 ) * cos ( sqrt ( elapsed ( clickTime ) ) ) ) ) ;
2020-03-22 04:30:23 +01:00
} else {
boxSize = 20 ;
}
2021-03-20 05:13:43 +01:00
//rectangleRGBA(renderer, selectedAircraft->x - boxSize, selectedAircraft->y - boxSize, selectedAircraft->x + boxSize, selectedAircraft->y + boxSize, style.selectedColor.r, style.selectedColor.g, style.selectedColor.b, 255);
lineRGBA ( renderer , selectedAircraft - > x - boxSize , selectedAircraft - > y - boxSize , selectedAircraft - > x - boxSize / 2 , selectedAircraft - > y - boxSize , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
lineRGBA ( renderer , selectedAircraft - > x - boxSize , selectedAircraft - > y - boxSize , selectedAircraft - > x - boxSize , selectedAircraft - > y - boxSize / 2 , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
2020-03-22 04:30:23 +01:00
2021-03-20 05:13:43 +01:00
lineRGBA ( renderer , selectedAircraft - > x + boxSize , selectedAircraft - > y - boxSize , selectedAircraft - > x + boxSize / 2 , selectedAircraft - > y - boxSize , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
lineRGBA ( renderer , selectedAircraft - > x + boxSize , selectedAircraft - > y - boxSize , selectedAircraft - > x + boxSize , selectedAircraft - > y - boxSize / 2 , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
2020-03-22 04:30:23 +01:00
2021-03-20 05:13:43 +01:00
lineRGBA ( renderer , selectedAircraft - > x + boxSize , selectedAircraft - > y + boxSize , selectedAircraft - > x + boxSize / 2 , selectedAircraft - > y + boxSize , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
lineRGBA ( renderer , selectedAircraft - > x + boxSize , selectedAircraft - > y + boxSize , selectedAircraft - > x + boxSize , selectedAircraft - > y + boxSize / 2 , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
2020-03-22 04:30:23 +01:00
2021-03-20 05:13:43 +01:00
lineRGBA ( renderer , selectedAircraft - > x - boxSize , selectedAircraft - > y + boxSize , selectedAircraft - > x - boxSize / 2 , selectedAircraft - > y + boxSize , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
lineRGBA ( renderer , selectedAircraft - > x - boxSize , selectedAircraft - > y + boxSize , selectedAircraft - > x - boxSize , selectedAircraft - > y + boxSize / 2 , style . selectedColor . r , style . selectedColor . g , style . selectedColor . b , 255 ) ;
2020-03-22 04:30:23 +01:00
}
2020-03-19 06:22:59 +01:00
}
void View : : registerClick ( int tapcount , int x , int y ) {
if ( tapcount = = 1 ) {
Aircraft * p = appData - > aircraftList . head ;
2020-03-08 02:22:20 +01:00
Aircraft * selection = NULL ;
2020-03-07 00:44:45 +01:00
while ( p ) {
2020-03-19 06:22:59 +01:00
if ( x & & y ) {
2021-03-20 05:13:43 +01:00
if ( ( p - > x - x ) * ( p - > x - x ) + ( p - > y - y ) * ( p - > y - y ) < 900 ) {
2020-03-07 00:44:45 +01:00
if ( selection ) {
2021-03-20 05:13:43 +01:00
if ( ( p - > x - x ) * ( p - > x - x ) + ( p - > y - y ) * ( p - > y - y ) <
( selection - > x - x ) * ( selection - > x - x ) + ( selection - > y - y ) * ( selection - > y - y ) ) {
2020-03-07 00:44:45 +01:00
selection = p ;
}
} else {
selection = p ;
}
}
}
p = p - > next ;
}
2020-03-19 06:22:59 +01:00
selectedAircraft = selection ;
} else if ( tapcount = = 2 ) {
mapTargetMaxDist = 0.25 * maxDist ;
animateCenterAbsolute ( x , y ) ;
2020-03-07 00:44:45 +01:00
}
2020-03-19 06:22:59 +01:00
clickx = x ;
clicky = y ;
2020-06-12 06:55:04 +02:00
clickTime = now ( ) ;
2020-03-19 06:22:59 +01:00
}
void View : : registerMouseMove ( int x , int y ) {
2020-06-15 00:26:26 +02:00
mouseMoved = true ;
2020-06-12 06:55:04 +02:00
mouseMovedTime = now ( ) ;
2020-03-19 06:22:59 +01:00
this - > mousex = x ;
this - > mousey = y ;
2020-12-08 01:30:49 +01:00
// aircraft debug
// Aircraft *mouse = appData->aircraftList.find(1);
// if(mouse != NULL) {
// latLonFromScreenCoords(&(mouse->lat), &(mouse->lon), x, screen_height-y);
// mouse->live = 1;
// }
2022-10-20 07:11:44 +02:00
highFramerate = true ;
2020-03-07 00:44:45 +01:00
}
2019-09-09 06:23:38 +02:00
//
//
//
2020-03-08 02:22:20 +01:00
void View : : draw ( ) {
2022-10-21 20:06:07 +02:00
drawStartTime = now ( ) ;
2022-10-20 07:11:44 +02:00
2022-10-27 21:18:47 +02:00
int targetFrameTime = 30 ;
2022-10-20 07:11:44 +02:00
// if(highFramerate) {
// targetFrameTime = 15;
// }
// highFramerate = false;
2022-10-21 20:06:07 +02:00
if ( lastFrameTime < targetFrameTime ) {
2022-10-23 20:34:22 +02:00
SDL_Delay ( static_cast < Uint32 > ( targetFrameTime - lastFrameTime ) ) ;
2022-10-21 20:06:07 +02:00
}
2022-10-23 20:34:22 +02:00
2020-03-07 00:44:45 +01:00
moveMapToTarget ( ) ;
zoomMapToTarget ( ) ;
2022-10-22 02:37:39 +02:00
2020-12-09 08:15:01 +01:00
drawGeography ( ) ;
2022-10-23 20:34:22 +02:00
drawScaleBars ( ) ;
2020-03-07 00:44:45 +01:00
2022-10-23 20:34:22 +02:00
if ( appData - > connected ) {
for ( int i = 0 ; i < 8 ; i + + ) {
// if(resolveLabelConflicts() < 0.001f) {
// break;
// }
resolveLabelConflicts ( ) ;
}
2020-02-18 07:19:45 +01:00
2022-10-23 20:34:22 +02:00
drawPlanes ( ) ;
}
2020-06-13 05:32:42 +02:00
2019-09-08 21:29:21 +02:00
drawStatus ( ) ;
2020-06-18 07:01:18 +02:00
//drawMouse();
2020-03-22 04:30:23 +01:00
drawClick ( ) ;
2020-03-06 00:02:40 +01:00
2020-03-19 06:22:59 +01:00
SDL_RenderPresent ( renderer ) ;
2022-10-20 07:11:44 +02:00
2022-10-21 20:06:07 +02:00
lastFrameTime = elapsed ( drawStartTime ) ;
2020-03-08 02:22:20 +01:00
}
2020-03-19 06:22:59 +01:00
View : : View ( AppData * appData ) {
this - > appData = appData ;
2022-10-22 02:37:39 +02:00
startupState = 0 ;
2020-03-19 06:22:59 +01:00
// Display options
screen_uiscale = 1 ;
screen_width = 0 ;
screen_height = 0 ;
screen_depth = 32 ;
2020-12-08 06:19:53 +01:00
fps = 0 ;
2020-03-19 06:22:59 +01:00
fullscreen = 0 ;
screen_index = 0 ;
2022-10-20 07:11:44 +02:00
highFramerate = false ;
2022-10-23 20:34:22 +02:00
lastFrameTime = 0 ;
2022-10-20 07:11:44 +02:00
2020-06-21 06:44:30 +02:00
centerLon = 0 ;
centerLat = 0 ;
2020-03-19 06:22:59 +01:00
maxDist = 25.0 ;
2020-06-13 05:32:42 +02:00
mapMoved = 1 ;
mapRedraw = 1 ;
2020-03-19 06:22:59 +01:00
selectedAircraft = NULL ;
2022-10-22 02:37:39 +02:00
std : : thread t1 ( & Map : : load , & map ) ;
t1 . detach ( ) ;
2020-03-19 06:22:59 +01:00
}
View : : ~ View ( ) {
closeFont ( mapFont ) ;
closeFont ( mapBoldFont ) ;
closeFont ( messageFont ) ;
closeFont ( labelFont ) ;
closeFont ( listFont ) ;
TTF_Quit ( ) ;
SDL_Quit ( ) ;
2020-06-12 06:55:04 +02:00
}