Example #1
0
static void OnAirspacePaintListItem(WindowControl * Sender, LKSurface& Surface){

  TCHAR label[40];
  (void)Sender;
  if (DrawListIndex < AIRSPACECLASSCOUNT){
    int i = DrawListIndex;
	LK_tcsncpy(label, CAirspaceManager::Instance().GetAirspaceTypeText(i), 39);
    int w0, w1, w2, x0;
    if (ScreenLandscape) {
      w0 = 202*ScreenScale;
    } else {
      w0 = 225*ScreenScale;
    }
	// LKTOKEN  _@M789_ = "Warn"
    w1 = Surface.GetTextWidth(MsgToken(789))+ScreenScale*10;
	// LKTOKEN  _@M241_ = "Display"
    w2 = Surface.GetTextWidth(MsgToken(241))+ScreenScale*10;
    x0 = w0-w1-w2;

    Surface.SetTextColor(RGB_BLACK);
    Surface.DrawTextClip(2*ScreenScale, 2*ScreenScale,
                   label, x0-ScreenScale*10);

    if (colormode) {

      Surface.SelectObject(LK_WHITE_PEN);
      Surface.SelectObject(LKBrush_White);
      Surface.Rectangle(x0, 2*ScreenScale,w0, 22*ScreenScale);
      Surface.SetTextColor(MapWindow::GetAirspaceColourByClass(i));
      Surface.SetBkColor(LKColor(0xFF, 0xFF, 0xFF));
      Surface.SelectObject(MapWindow::GetAirspaceBrushByClass(i));
      Surface.Rectangle(x0, 2*ScreenScale,w0, 22*ScreenScale);

    } else {

      bool iswarn;
      bool isdisplay;

      iswarn = (MapWindow::iAirspaceMode[i]>=2);
      isdisplay = ((MapWindow::iAirspaceMode[i]%2)>0);
      if (iswarn) {
	// LKTOKEN  _@M789_ = "Warn"
        _tcscpy(label, MsgToken(789));
        Surface.DrawText(w0-w1-w2, 2*ScreenScale, label);
      }
      if (isdisplay) {
	// LKTOKEN  _@M241_ = "Display"
        _tcscpy(label, MsgToken(241));
        Surface.DrawText(w0-w2, 2*ScreenScale, label);
      }

    }

  }
}
void Statistics::DrawBarChart(LKSurface& Surface, const RECT& rc, LeastSquares* lsdata) {
  int i;
LKColor Col;
  if (unscaled_x || unscaled_y) {
    return;
  }

if(INVERTCOLORS)
  Col = RGB_GREEN.ChangeBrightness(0.5);
else
  Col = RGB_WHITE;

  LKPen hpBar(PEN_SOLID, IBLSCALE(1), Col);
  LKBrush hbBar(Col);
  const auto oldpen = Surface.SelectObject(hpBar);
  const auto oldbrush = Surface.SelectObject(hbBar);


  int xmin, ymin, xmax, ymax;

  for (i= 0; i<lsdata->sum_n; i++) {
    xmin = (int)((i+1+0.2)*xscale)+rc.left+BORDER_X;
    ymin = (int)((y_max-y_min)*yscale)+rc.top;
    xmax = (int)((i+1+0.8)*xscale)+rc.left+BORDER_X;
    ymax = (int)((y_max-lsdata->ystore[i])*yscale)+rc.top;
    Surface.Rectangle(xmin, ymin, xmax, ymax);
  }

  Surface.SelectObject(oldpen);
  Surface.SelectObject(oldbrush);
}
static void OnPaintAirspacePicto(WindowControl * Sender, LKSurface& Surface){
	  (void)Sender;

	  WndFrame  *wPicto = ((WndFrame *)wf->FindByName(TEXT("frmAirspacePicto")));
	  LKASSERT(wPicto!=NULL);
	  const RECT rc = wPicto->GetClientRect();
	  Surface.SelectObject(LKPen_Petrol_C2);

	  Surface.SelectObject(LKBrush_Petrol);
	  Surface.Rectangle(rc.left,rc.top,rc.right,rc.bottom);


	  Surface.SetBkColor(RGB_LIGHTGREY);
      /****************************************************************
       * for drawing the airspace pictorial, we need the original data.
       * copy contain only base class property, not geo data, 
       * original data are shared ressources ! 
       * for that we need to grant all called methods are thread safe
       ****************************************************************/
   {
      CCriticalSection::CGuard guard(CAirspaceManager::Instance().MutexRef());
      CAirspace* airspace = CAirspaceManager::Instance().GetAirspacesForDetails();
      if(airspace) {
        airspace->DrawPicto(Surface, rc);
      }
   }
}
Example #4
0
//
// Box black, text white, or inverted.
// Clip region is normally MapRect for forcing writing on any part of the screen.
// Or DrawRect for the current terrain area.
// Or, of course, anything else.
// A note about DrawRect: in main moving map, DrawRect is the part of screen excluding BottomBar,
// when the bottom bar is opaque. So choose carefully.
//
void MapWindow::LKWriteBoxedText(LKSurface& Surface, const RECT& clipRect, const TCHAR* wText, int x, int y, const short align ,
	const LKColor& dir_rgb, const LKColor& inv_rgb  ) {

  LKColor oldTextColor = Surface.SetTextColor(INVERTCOLORS?dir_rgb:inv_rgb);

  SIZE tsize;
  Surface.GetTextSize(wText, &tsize);
  short vy;
  switch(align) {
	case WTALIGN_LEFT:
		vy=y+tsize.cy+NIBLSCALE(2)+1;
		if (vy>=clipRect.bottom) return;
		Surface.Rectangle(x, y, x+tsize.cx+NIBLSCALE(8), vy);
        x += NIBLSCALE(4);
		break;
	case WTALIGN_RIGHT:
		vy=y+tsize.cy+NIBLSCALE(2)+1;
		if (vy>=clipRect.bottom) return;
        Surface.Rectangle(x-tsize.cx-NIBLSCALE(8), y, x, vy);
        x -= (tsize.cx+NIBLSCALE(4));
		break;
	case WTALIGN_CENTER:
		vy=y+(tsize.cy/2)+NIBLSCALE(1)+1;
		if (vy>=clipRect.bottom) return;
		Surface.Rectangle(x-(tsize.cx/2)-NIBLSCALE(4),
			y-(tsize.cy/2)-NIBLSCALE(1)-1,
			x+(tsize.cx/2)+NIBLSCALE(4),
			vy);
		x -= (tsize.cx/2);
		// just a trick to avoid calculating:
		// y -= ((tsize.cy/2)+NIBLSCALE(1));
		y -= (vy-y);
		break;
  }
  #ifdef __linux__
  y += NIBLSCALE(1)+1;
  #else
  y += NIBLSCALE(1);
  #endif


  Surface.DrawText(x, y, wText);

  //SetTextColor(hDC,RGB_BLACK);   THIS WAS FORCED BLACk SO FAR 121005
  Surface.SetTextColor(oldTextColor);
}
static void OnAirspacePatternsPaintListItem(WindowControl * Sender, LKSurface& Surface) {
    (void) Sender;
    if ((DrawListIndex < NUMAIRSPACEBRUSHES) &&(DrawListIndex >= 0)) {
        int i = DrawListIndex;
        Surface.SelectObject(LKBrush_White);
        Surface.SelectObject(LK_BLACK_PEN);
        Surface.SetBkColor(LKColor(0xFF, 0xFF, 0xFF));
        Surface.SelectObject(MapWindow::GetAirspaceBrush(i));
        Surface.SetTextColor(LKColor(0x00, 0x00, 0x00));
        Surface.Rectangle(100 * ScreenScale, 2 * ScreenScale, 180 * ScreenScale, 22 * ScreenScale);
    }
}
Example #6
0
static void OnProgressPaint(WindowControl * Sender, LKSurface& Surface) {
  RECT PrintAreaR = Sender->GetClientRect();
    
  const auto oldFont = Surface.SelectObject(MapWindowBoldFont);

  Surface.FillRect(&PrintAreaR, LKBrush_Petrol);

  // Create text area

  // we cannot use LKPen here because they are not still initialised for startup menu. no problem
  LKPen hP(PEN_SOLID,NIBLSCALE(1),RGB_GREEN);
  auto ohP = Surface.SelectObject(hP);
  const auto ohB = Surface.SelectObject(LKBrush_Petrol);
  Surface.Rectangle(PrintAreaR.left,PrintAreaR.top,PrintAreaR.right,PrintAreaR.bottom);
  Surface.SelectObject(ohP);
  hP.Release();

  hP.Create(PEN_SOLID,NIBLSCALE(1),RGB_BLACK);
  ohP = Surface.SelectObject(hP);
  Surface.SelectObject(LK_HOLLOW_BRUSH);
  InflateRect(&PrintAreaR, -NIBLSCALE(2), -NIBLSCALE(2));
  Surface.Rectangle(PrintAreaR.left,PrintAreaR.top,PrintAreaR.right,PrintAreaR.bottom);

  Surface.SetTextColor(RGB_WHITE);
  Surface.SetBackgroundTransparent();

  InflateRect(&PrintAreaR, -NIBLSCALE(2), -NIBLSCALE(2));
  
  const TCHAR* text = Sender->GetCaption();
  Surface.DrawText(text, &PrintAreaR, DT_VCENTER|DT_SINGLELINE);

  Surface.SelectObject(ohB);
  Surface.SelectObject(ohP);
  Surface.SelectObject(oldFont);
   
}
static void OnAirspaceColoursPaintListItem(WindowControl * Sender, LKSurface& Surface){
  (void)Sender;
  if ((DrawListIndex < NUMAIRSPACECOLORS) &&(DrawListIndex>=0)) {
    int i = DrawListIndex;
    Surface.SelectObject(LKBrush_White);
    Surface.SelectObject(LK_BLACK_PEN);
    Surface.SetBkColor(LKColor(0xFF, 0xFF, 0xFF));
    Surface.SelectObject(MapWindow::GetAirspaceSldBrush(i)); // this is the solid brush
    Surface.SetTextColor(MapWindow::GetAirspaceColour(i));
    Surface.Rectangle(
              100*ScreenScale, 
              2*ScreenScale,
              180*ScreenScale,
              22*ScreenScale);
  }
}
Example #8
0
void MapWindow::VGTextInBox(LKSurface& Surface, unsigned short nslot, short numlines, const TCHAR* wText1, const TCHAR* wText2, const TCHAR *wText3, int x, int y, const LKColor& trgb, const LKBrush& bbrush) {

#if BUGSTOP
    LKASSERT(wText1 != NULL);
#endif
    if (!wText1) return;

    LKColor oldTextColor = Surface.SetTextColor(trgb);

    SIZE tsize;
    int tx, ty;


    Sideview_VGBox_Number++;

    Surface.SelectObject(line1Font);
    Surface.GetTextSize(wText1, &tsize);
    int line1fontYsize = tsize.cy;

    short vy = y + (boxSizeY / 2);
    Surface.SelectObject(bbrush);
    Surface.Rectangle(
            x - (boxSizeX / 2),
            y - (boxSizeY / 2)-1,
            x + (boxSizeX / 2),
            vy-1);

    Sideview_VGBox[nslot].top = y - (boxSizeY / 2);
    Sideview_VGBox[nslot].left = x - (boxSizeX / 2);
    Sideview_VGBox[nslot].bottom = vy;
    Sideview_VGBox[nslot].right = x + (boxSizeX / 2);

    PixelRect ClipRect(Sideview_VGBox[nslot]);
    ClipRect.Grow(-1*NIBLSCALE(2), 0); // text padding

    //
    // LINE 1
    //
    tx = max<PixelScalar>(ClipRect.left, x - (tsize.cx / 2));
    ty = y - (vy - y);

    Surface.DrawText(tx, ty, wText1, &ClipRect);

    if (numlines == 1) goto _end;
#if BUGSTOP
    LKASSERT(wText2 != NULL);
#endif
    if (!wText2) goto _end;

    //
    // LINE 2
    //
    Surface.SetTextColor(RGB_BLACK);
    Surface.SelectObject(line2Font);
    Surface.GetTextSize(wText2, &tsize);
    tx = x - (tsize.cx / 2);
    ty += line1fontYsize - NIBLSCALE(2);
    Surface.DrawText(tx, ty, wText2);

    if (numlines == 2) goto _end;
#if BUGSTOP
    LKASSERT(wText3 != NULL);
#endif
    if (!wText3) goto _end;

    //
    // LINE 3
    //
    Surface.SetTextColor(RGB_BLACK);
    Surface.GetTextSize(wText3, &tsize);
    tx = x - (tsize.cx / 2);
    ty += tsize.cy - NIBLSCALE(2);
    Surface.DrawText(tx, ty, wText3);

_end:
    Surface.SetTextColor(oldTextColor);
    return;
}
Example #9
0
// This is painting traffic icons on the screen.
void MapWindow::LKDrawFLARMTraffic(LKSurface& Surface, const RECT& rc, const ScreenProjection& _Proj, const POINT& Orig_Aircraft) {

  if (!EnableFLARMMap) return;

  if (!DrawInfo.FLARM_Available) return;

  // init scaled coords for traffic icon
static int	iCircleSize = 7;
static int	iRectangleSize = 4;
  static short tscaler=0;
  if (DoInit[MDI_DRAWFLARMTRAFFIC]) {

	switch (ScreenSize) {
		case ss480x640:
		case ss480x800:
		case ss800x480:
		case ss640x480:
			iCircleSize = 9;
			iRectangleSize = 5;
			tscaler=NIBLSCALE(7)-2;
			break;
		case ss240x320:
		case ss272x480:
		case ss320x240:
		case ss480x272:
		case ss480x234:
		case ss400x240:
			iCircleSize = 7;
			iRectangleSize = 4;
			tscaler=NIBLSCALE(13)-2;
			break;
		default:
			iCircleSize = 7;
			iRectangleSize = 4;
			tscaler=NIBLSCALE(7);
			break;
	}


	DoInit[MDI_DRAWFLARMTRAFFIC]=false;
  }

  POINT Arrow[5];

  TCHAR lbuffer[50];

  const auto hpold = Surface.SelectObject(LKPen_Black_N1);

  int i;
  int painted=0;

//  double dX, dY;

  TextInBoxMode_t displaymode = {0};
  displaymode.NoSetFont = 1;

  #if 0
  double screenrange = GetApproxScreenRange();
  double scalefact = screenrange/6000.0; // FIX 100820
  #endif




  const auto oldfont = Surface.SelectObject(LK8MapFont);

  for (i=0,painted=0; i<FLARM_MAX_TRAFFIC; i++) {

	// limit to 10 icons map traffic
	if (painted>=10) {
		i=FLARM_MAX_TRAFFIC;
		continue;
	}

	if ( (DrawInfo.FLARM_Traffic[i].ID !=0) && (DrawInfo.FLARM_Traffic[i].Status != LKT_ZOMBIE) ) {

		painted++;

		double target_lon;
		double target_lat;

		target_lon = DrawInfo.FLARM_Traffic[i].Longitude;
		target_lat = DrawInfo.FLARM_Traffic[i].Latitude;

		#if (0) // No scaling, wrong
		if ((EnableFLARMMap==2)&&(scalefact>1.0)) {

			double distance;
			double bearing;

			DistanceBearing(DrawInfo.Latitude, DrawInfo.Longitude, target_lat, target_lon, &distance, &bearing);
			FindLatitudeLongitude(DrawInfo.Latitude, DrawInfo.Longitude, bearing, distance*scalefact, &target_lat, &target_lon);
		}
		#endif
      
		POINT sc, sc_name, sc_av;
		sc = _Proj.LonLat2Screen(target_lon, target_lat);

		sc_name = sc;

		sc_name.y -= NIBLSCALE(16);
		sc_av = sc_name;

		_tcscpy(lbuffer,_T(""));
		if (DrawInfo.FLARM_Traffic[i].Cn && DrawInfo.FLARM_Traffic[i].Cn[0]!=_T('?')) { // 100322
			_tcscat(lbuffer,DrawInfo.FLARM_Traffic[i].Cn);
		}
		if (DrawInfo.FLARM_Traffic[i].Average30s>=0.1) {
                  size_t len = _tcslen(lbuffer);
                  if (len > 0)
                    _stprintf(lbuffer + len,_T(":%.1f"),LIFTMODIFY*DrawInfo.FLARM_Traffic[i].Average30s);
                  else
                    _stprintf(lbuffer,_T("%.1f"),LIFTMODIFY*DrawInfo.FLARM_Traffic[i].Average30s);
		}

		displaymode.Border=1;

		if (_tcslen(lbuffer)>0)
		TextInBox(Surface, &rc, lbuffer, sc.x+tscaler, sc.y+tscaler, &displaymode, false);

		// red circle
		if ((DrawInfo.FLARM_Traffic[i].AlarmLevel>0) && (DrawInfo.FLARM_Traffic[i].AlarmLevel<4)) {
			DrawBitmapIn(Surface, sc, hFLARMTraffic,true);
		}
#if 1 // 1
		Arrow[0].x = -4;
		Arrow[0].y = 5;
		Arrow[1].x = 0;
		Arrow[1].y = -6;
		Arrow[2].x = 4;
		Arrow[2].y = 5;
		Arrow[3].x = 0;
		Arrow[3].y = 2;
		Arrow[4].x = -4;
		Arrow[4].y = 5;

		for (int q=0; q < 5; q++)
		{
			Arrow[q].x  = (LONG) ((double)Arrow[q].x * 1.7);
			Arrow[q].y  = (LONG) ((double)Arrow[q].y * 1.7);
		}
#else

		Arrow[0].x = scaler[0];
		Arrow[0].y = scaler[1];
		Arrow[1].x = 0;
		Arrow[1].y = scaler[2];
		Arrow[2].x = scaler[3];
		Arrow[2].y = scaler[1];
		Arrow[3].x = 0;
		Arrow[3].y = scaler[4];
		Arrow[4].x = scaler[0];
		Arrow[4].y = scaler[1];
#endif


/*
		switch (DrawInfo.FLARM_Traffic[i].Status) { // 100321
			case LKT_GHOST:
				Surface.SelectObject(yellowBrush);
				break;
			case LKT_ZOMBIE:
				Surface.SelectObject(redBrush);
				break;
			default:
				Surface.SelectObject(greenBrush);
				break;
		}
*/
		  /*************************************************************************
		   * calculate climb color
		   *************************************************************************/

		  int iVarioIdx = (int)(2*DrawInfo.FLARM_Traffic[i].Average30s  -0.5)+NO_VARIO_COLORS/2;
		  if(iVarioIdx < 0) iVarioIdx =0;
		  if(iVarioIdx >= NO_VARIO_COLORS) iVarioIdx =NO_VARIO_COLORS-1;
		  Surface.SelectObject(*variobrush[iVarioIdx]);

		  switch (DrawInfo.FLARM_Traffic[i].Status) { // 100321
			case LKT_GHOST:
				Surface.Rectangle(sc.x-iRectangleSize,  sc.y-iRectangleSize,sc.x+iRectangleSize, sc.y+iRectangleSize);
				break;
			case LKT_ZOMBIE:
				Surface.DrawCircle(sc.x,  sc.x, iCircleSize, rc, true );
				break;
			default:
				PolygonRotateShift(Arrow, 5, sc.x, sc.y, DrawInfo.FLARM_Traffic[i].TrackBearing - DisplayAngle);
				Surface.Polygon(Arrow,5);
				break;
		  }

	}
  }

  Surface.SelectObject(oldfont);
  Surface.SelectObject(hpold);

}
void MapWindow::RenderMapWindowBg(LKSurface& Surface, const RECT& rc,
        const POINT &Orig,
        const POINT &Orig_Aircraft) {

    if ( (LKSurface::AlphaBlendSupported() && BarOpacity < 100) || mode.AnyPan() ) {
        RECT newRect = {0, 0, ScreenSizeX, ScreenSizeY};
        MapWindow::ChangeDrawRect(newRect);
    } else {
        RECT newRect = {0, 0, ScreenSizeX, ScreenSizeY - BottomSize - (ScreenSizeY-MapRect.bottom)};
        MapWindow::ChangeDrawRect(newRect);
    }


    if (QUICKDRAW) {
        goto _skip_calcs;
    }


    // Here we calculate arrival altitude, GD etc for map waypoints. Splitting with multicalc will result in delayed
    // updating of visible landables, for example. The nearest pages do this separately, with their own sorting.
    // Basically we assume -like for nearest- that values will not change that much in the multicalc split time.
    // Target and tasks are recalculated in real time in any case. Nearest too. 
    LKCalculateWaypointReachable(false);

_skip_calcs:

    CalculateScreenPositionsThermalSources();

    // Make the glide amoeba out of the latlon points, converting them to screen
    // (This function is updated for supporting multimaps )
    CalculateScreenPositionsGroundline();

    if (PGZoomTrigger) {
        if (!mode.Is(Mode::MODE_PANORAMA)) {
            mode.Special(Mode::MODE_SPECIAL_PANORAMA, true);
            LastZoomTrigger = DrawInfo.Time;

            Message::AddMessage(1000, 3, gettext(TEXT("_@M872_"))); // LANDSCAPE ZOOM FOR 20s
            LKSound(TEXT("LK_TONEUP.WAV"));
        } else {
            // previously called, see if time has passed
            if (DrawInfo.Time > (LastZoomTrigger + 20.0)) {
                // time has passed, lets go back
                LastZoomTrigger = 0; // just for safety
                mode.Special(Mode::MODE_SPECIAL_PANORAMA, false);
                PGZoomTrigger = false;
                Message::AddMessage(1500, 3, gettext(TEXT("_@M873_"))); // BACK TO NORMAL ZOOM
                LKSound(TEXT("LK_TONEDOWN.WAV"));
            }
        }
    }

    // 
    // "Checkpoint Charlie"
    // This is were we process stuff for anything else but main map.
    // We let the calculations run also for MapSpace modes.
    // But for multimaps, we can also draw some more stuff..
    // We are also sent back here from next code, when we detect that
    // the MapSpace mode has changed from MAP to something else while we
    // were rendering.
    //
QuickRedraw:
    //
    if (DONTDRAWTHEMAP) {
        DrawMapSpace(Surface, rc);
        // Is this a "shared map" environment? 
        if (IsMultiMapShared()) {
            // Shared map, of course not MSN_MAP, since dontdrawthemap was checked
            //
            const bool bDrawGauges = IsMultimapOverlaysGauges();
            if (bDrawGauges && IsThermalBarVisible()) {
                DrawThermalBand(Surface, rc);
            }

            if (IsMultimapOverlaysText()) {
                DrawLook8000(Surface, rc);
            }
            if (bDrawGauges) {
                if (LKVarioBar) {
                    LKDrawVario(Surface, rc);
                }
                DrawFinalGlide(Surface, rc);
            }

        } else {
            // Not in map painting environment 
            // ex. nearest pages, but also MAPRADAR..
        }

        // 
        DrawBottomBar(Surface, rc);
#ifdef DRAWDEBUG
        DrawDebug(hdc, rc);
#endif
        // no need to do SelectObject as at the bottom of function
        return;
    }

    // When no terrain is painted, set a background0
    // Remember that in this case we have plenty of cpu time to spend for best result
    if (!IsMultimapTerrain() || !DerivedDrawInfo.TerrainValid || !RasterTerrain::isTerrainLoaded()) {

        // display border and fill background..
        Surface.SelectObject(hInvBackgroundBrush[BgMapColor]);
        Surface.SelectObject(LK_WHITE_PEN);

        Surface.Rectangle(rc.left, rc.top, rc.right, rc.bottom);
        // We force LK painting black values on screen depending on the background color in use
        // TODO make it an array once settled
        // blackscreen would force everything to be painted white, instead
        LKTextBlack = BgMapColorTextBlack[BgMapColor];
        if (BgMapColor > 6) BlackScreen = true;
        else BlackScreen = false;
    } else {
        LKTextBlack = false;
        BlackScreen = false;
    }

    // Logic of DONTDRAWTHEMAP is the following:
    // We are rendering the screen page here. If we are here, we passed Checkpoint Charlie.
    // So we were, at charlie, in MSM_MAP: preparing the main map stuff.
    // If we detect that MapSpace has CHANGED while we were doing our job here,
    // it means that the user has clicked meanwhile. He desires another page, so let's
    // reset our intentions and go back to beginning, or nearby..
    // We have a new job to do, for another MapSpace, no more MAP.
    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    bool terrainpainted = false;

    if ((IsMultimapTerrain() && (DerivedDrawInfo.TerrainValid)
            && RasterTerrain::isTerrainLoaded())
            ) {
        // sunelevation is never used, it is still a todo in Terrain
        double sunelevation = 40.0;
        double sunazimuth = GetAzimuth();

        LockTerrainDataGraphics();
        if (DONTDRAWTHEMAP) { // 100318
            UnlockTerrainDataGraphics();
            goto QuickRedraw;
        }
        DrawTerrain(Surface, DrawRect, sunazimuth, sunelevation);
        terrainpainted = true;
        if (DONTDRAWTHEMAP) {
            UnlockTerrainDataGraphics();
            goto QuickRedraw;
        }
        if (!QUICKDRAW) {
            // SHADED terrain unreachable, aka glide amoeba. This is not the outlined perimeter!
#ifdef GTL2
            if (((FinalGlideTerrain == 2) || (FinalGlideTerrain == 4)) &&
                    DerivedDrawInfo.TerrainValid) {
#else
            if ((FinalGlideTerrain == 2) && DerivedDrawInfo.TerrainValid) {
#endif
                DrawTerrainAbove(Surface, DrawRect);
            }
        }
        UnlockTerrainDataGraphics();
    }

    //
    // REMINDER: WE ARE IN MAIN MAP HERE: MSM_MAP ONLY, OR PANNING MODE!
    // MAPSPACEMODE CAN STILL CHANGE, DUE TO USER INPUT. BUT WE GOT HERE IN
    // EITHER PAN OR MSM_MAP.
    //

    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    if (IsMultimapTopology()) {
        DrawTopology(Surface, DrawRect);
    } else {
        // If no topology wanted, but terrain painted, we paint only water stuff
        if (terrainpainted) DrawTopology(Surface, DrawRect, true);
    }
#if 0
    StartupStore(_T("... Experimental1=%.0f\n"), Experimental1);
    StartupStore(_T("... Experimental2=%.0f\n"), Experimental2);
    Experimental1 = 0.0;
    Experimental2 = 0.0;
#endif

    // Topology labels are printed first, using OLD wps positions from previous run!
    // Reset for topology labels decluttering engine occurs also in another place here!
    ResetLabelDeclutter();

    if ((Flags_DrawTask || TargetDialogOpen) && ValidTaskPoint(ActiveWayPoint) && ValidTaskPoint(1)) {
        DrawTaskAAT(Surface, DrawRect);
    }


    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    if (IsMultimapAirspace()) {
        DrawAirSpace(Surface, rc);
    }

    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    // In QUICKDRAW dont draw trail, thermals, glide terrain
    if (QUICKDRAW) goto _skip_stuff;
#define  TRAIL_OVER_AIRFIELD
#ifndef TRAIL_OVER_AIRFIELD
    if (TrailActive) {
        LKDrawLongTrail(hdc, Orig_Aircraft, DrawRect);
        // NEED REWRITING
        LKDrawTrail(hdc, Orig_Aircraft, DrawRect);
    }
#endif
    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    DrawThermalEstimate(Surface, DrawRect);
    if (OvertargetMode == OVT_THER) DrawThermalEstimateMultitarget(Surface, DrawRect);

    // draw red cross on glide through terrain marker
    if (FinalGlideTerrain && DerivedDrawInfo.TerrainValid) {
        DrawGlideThroughTerrain(Surface, DrawRect);
    }

    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

_skip_stuff:

    if (IsMultimapAirspace() && AirspaceWarningMapLabels) {
        DrawAirspaceLabels(Surface, DrawRect, Orig_Aircraft);
        if (DONTDRAWTHEMAP) { // 100319
            goto QuickRedraw;
        }
    }

    if (IsMultimapWaypoints()) {
        DrawWaypointsNew(Surface, DrawRect);
    }
#ifdef TRAIL_OVER_AIRFIELD
    if (TrailActive) {
        LKDrawLongTrail(Surface, Orig_Aircraft, DrawRect);
        // NEED REWRITING
        LKDrawTrail(Surface, Orig_Aircraft, DrawRect);
    }
#endif
    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    if ((Flags_DrawTask || TargetDialogOpen) && ValidTaskPoint(ActiveWayPoint) && ValidTaskPoint(1)) {
        DrawTask(Surface, DrawRect, Orig_Aircraft);

    }
    if (Flags_DrawFAI) {
        if (MapWindow::DerivedDrawInfo.Flying) { // FAI optimizer does not depend on tasks, being based on trace
            DrawFAIOptimizer(Surface, DrawRect, Orig_Aircraft);
        } else { // not flying => show FAI sectors for the task
            if (ValidTaskPoint(ActiveWayPoint) && ValidTaskPoint(1)) {
                DrawTaskSectors(Surface, DrawRect);
            }
        }
    }


    // In QUICKDRAW do not paint other useless stuff
    if (QUICKDRAW) {
        if (extGPSCONNECT) DrawBearing(Surface, DrawRect);
        goto _skip_2;
    }

    // ---------------------------------------------------

    DrawTeammate(Surface, rc);

    if (extGPSCONNECT) {
        DrawBestCruiseTrack(Surface, Orig_Aircraft);
        DrawBearing(Surface, DrawRect);
    }

    // draw wind vector at aircraft
    if (NOTANYPAN) {
        DrawWindAtAircraft2(Surface, Orig_Aircraft, DrawRect);
    } else if (mode.Is(Mode::MODE_TARGET_PAN)) {
        DrawWindAtAircraft2(Surface, Orig, rc);
    }

    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    // Draw traffic and other specifix LK gauges
    LKDrawFLARMTraffic(Surface, DrawRect, Orig_Aircraft);

    // ---------------------------------------------------
_skip_2:

    if (NOTANYPAN) {
        if (IsThermalBarVisible() && IsMultimapOverlaysGauges()) {
            DrawThermalBand(Surface, rc);
        }

        if (IsMultimapOverlaysText()) {
            DrawLook8000(Surface, rc);
        }
        DrawBottomBar(Surface, rc);
    }

    if (DONTDRAWTHEMAP) {
        goto QuickRedraw;
    }

    if (IsMultimapOverlaysGauges() && (LKVarioBar && NOTANYPAN))
        LKDrawVario(Surface, rc);

    if (NOTANYPAN) {
        if (TrackBar) {
            DrawHeading(Surface, Orig, DrawRect);
            if (ISGAAIRCRAFT) DrawFuturePos(Surface, Orig, DrawRect);
        }
    }

    // Draw glider or paraglider
    if (extGPSCONNECT) {
        DrawAircraft(Surface, Orig_Aircraft);
    }



#if USETOPOMARKS
    // marks on top...
    DrawMarks(hdc, rc);
#endif

    if (ISGAAIRCRAFT && IsMultimapOverlaysGauges() && NOTANYPAN) DrawHSIarc(Surface, Orig, DrawRect);

    if (!INPAN) {
        DrawMapScale(Surface, rc, zoom.BigZoom()); // unused BigZoom
        DrawCompass(Surface, rc, DisplayAngle);
    }

    if (IsMultimapOverlaysGauges() && NOTANYPAN) DrawFinalGlide(Surface, rc);


#ifdef DRAWDEBUG
    DrawDebug(hdc, rc);
#endif

}
Example #11
0
void MapWindow::LKDrawVario(LKSurface& Surface, const RECT& rc) {

    static PixelRect vrc, mrc, hrc, htrc, hbrc;

    static BrushReference greenBrush, darkyellowBrush, orangeBrush, redBrush;
    static BrushReference lakeBrush, blueBrush, indigoBrush;

    static PenReference borderPen; // Pen for border of vario bar and vario bricks ( white or black)
    static BrushReference forgroundBrush; // Brush used for draw middle thick or monochrome brick ( same color of borderPen )

    /* 
     * this array define vario Value for each brick, ( positive value )
     *  Number of brick for positive value is defined by this array size.
     */
    static const double positive_vario_step[] = {0.05, 0.25, 0.50, 0.75, 1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00, 3.50, 4.50, 5.00, 6.00, 7.00};
    static const unsigned positive_brick_count = array_size(positive_vario_step);

    static BrushReference positiveBrush[positive_brick_count];
    static PixelRect positiveBricks[positive_brick_count];

    /* 
     * this array define vario Value for each brick, ( negative value )
     *  Number of brick for negative value is defined by this array size.
     */
    static const double negative_vario_step[] = {-0.05, -0.25, -0.50, -0.75, -1.00, -1.25, -1.50, -1.75, -2.00, -2.50, -3.00, -3.50, -4.50, -5.00, -6.00, -7.00};
    static const unsigned negative_brick_count = array_size(negative_vario_step);

    static BrushReference negativeBrush[negative_brick_count];
    static PixelRect negativeBricks[negative_brick_count];

    static short startInitCounter = 0;
    static bool dogaugeinit = true;
    static double max_positiveGload;
    static double max_negativeGload;

    if (DoInit[MDI_DRAWVARIO]) {

        const int boxthick = IBLSCALE(BOXTHICK);
        const int hpixelseparate = (LKVarioBar > vBarVarioGR) ? 0 : IBLSCALE(PIXELSEPARATE);
        const int vpixelseparate = IBLSCALE(PIXELSEPARATE);
        const int variowidth = LKVarioSize;

        startInitCounter = 0;
        dogaugeinit = true;

        // initial fullscale G loads for 2D driving.
        // These values are then rescaled (only increased) automatically.
        max_positiveGload = 0.1;
        max_negativeGload = -0.1;


        borderPen = (BgMapColor > POSCOLOR) ? LKPen_White_N0 : LKPen_Black_N0;
        forgroundBrush = (BgMapColor > POSCOLOR) ? LKBrush_White : LKBrush_Black;
        
        greenBrush = LKBrush_Green;
        darkyellowBrush = LKBrush_DarkYellow2;
        orangeBrush = LKBrush_Orange;
        redBrush = LKBrush_Red;
        lakeBrush = LKBrush_Lake;
        blueBrush = LKBrush_Blue;
        indigoBrush = LKBrush_Indigo;


        const short lkvariobar = (LKVarioBar > vBarVarioGR) ? LKVarioBar - vBarVarioGR : LKVarioBar;
        switch (lkvariobar) {
            default:
                LKASSERT(false); // wrong config value or disabled, in any case, it's BUG
                // no break; for avoid to have unitialized Brush array.
            case vBarVarioColor:
                // set default background in case of missing values
                std::fill(std::begin(positiveBrush), std::end(positiveBrush), forgroundBrush);
                std::fill(std::begin(negativeBrush), std::end(negativeBrush), forgroundBrush);
                
                static_assert(array_size(positiveBrush)> 15, "\"positiveBrush\" size must be greater than 15, check \"positive_vario_step\" size");
                
                positiveBrush[15] = redBrush;
                positiveBrush[14] = redBrush;
                positiveBrush[13] = redBrush;
                positiveBrush[12] = redBrush;
                positiveBrush[11] = orangeBrush;
                positiveBrush[10] = orangeBrush;
                positiveBrush[9] = orangeBrush;
                positiveBrush[8] = orangeBrush;
                positiveBrush[7] = darkyellowBrush;
                positiveBrush[6] = darkyellowBrush;
                positiveBrush[5] = darkyellowBrush;
                positiveBrush[4] = darkyellowBrush;
                positiveBrush[3] = greenBrush;
                positiveBrush[2] = greenBrush;
                positiveBrush[1] = greenBrush;
                positiveBrush[0] = greenBrush;

                negativeBrush[0] = lakeBrush;
                negativeBrush[1] = lakeBrush;
                negativeBrush[2] = lakeBrush;
                negativeBrush[3] = lakeBrush;
                negativeBrush[4] = blueBrush;
                negativeBrush[5] = blueBrush;
                negativeBrush[6] = blueBrush;
                negativeBrush[7] = blueBrush;
                negativeBrush[8] = indigoBrush;
                negativeBrush[9] = indigoBrush;
                negativeBrush[10] = indigoBrush;
                negativeBrush[11] = indigoBrush;
                negativeBrush[12] = forgroundBrush;
                negativeBrush[13] = forgroundBrush;
                negativeBrush[14] = forgroundBrush;
                negativeBrush[15] = forgroundBrush;

                static_assert(array_size(negativeBrush)> 15, "\"negativeBrush\" size must be greater than 15, check \"negative_vario_step\" size");

                break;
            case vBarVarioMono:
                std::fill(std::begin(positiveBrush), std::end(positiveBrush), forgroundBrush);
                std::fill(std::begin(negativeBrush), std::end(negativeBrush), forgroundBrush);
                break;
            case vBarVarioRB:
                std::fill(std::begin(positiveBrush), std::end(positiveBrush), redBrush);
                std::fill(std::begin(negativeBrush), std::end(negativeBrush), blueBrush);
                break;
            case vBarVarioGR:
                std::fill(std::begin(positiveBrush), std::end(positiveBrush), greenBrush);
                std::fill(std::begin(negativeBrush), std::end(negativeBrush), redBrush);
                break;
        }

        // vario paint area
        vrc.left = rc.left;
        vrc.top = rc.top;
        vrc.right = vrc.left + variowidth;
        vrc.bottom = rc.bottom - BottomSize;

        // meter area
        mrc.left = vrc.left + hpixelseparate;
        mrc.top = vrc.top + vpixelseparate;
        mrc.right = vrc.right - hpixelseparate;
        mrc.bottom = vrc.bottom - vpixelseparate;

        // half vario separator for positive and negative values
        const double vmiddle_height = NIBLSCALE(2) - ((mrc.bottom - mrc.top) % 2);
        const double vmiddle = ((mrc.bottom - mrc.top) / 2.0) + mrc.top;

        hrc.top = vrc.top + vmiddle - (vmiddle_height / 2);
        hrc.bottom = vrc.top + vmiddle + (vmiddle_height / 2);
        hrc.left = vrc.left;
        hrc.right = vrc.right;

        // half top meter area
        htrc.left = mrc.left;
        htrc.right = mrc.right;
        htrc.bottom = hrc.top - vpixelseparate;
        htrc.top = mrc.top + vpixelseparate;

        // half bottom meter area
        hbrc.left = mrc.left;
        hbrc.right = mrc.right;
        hbrc.top = hrc.bottom + vpixelseparate;
        hbrc.bottom = mrc.bottom - vpixelseparate;

        // pixel height of each positive brick
        const int positive_brick_size = (htrc.bottom - htrc.top - (boxthick * (positive_brick_count - 1))) / positive_brick_count;
        const int positive_brick_advance = positive_brick_size + boxthick;

        // Pre-calculate brick positions for half top
        for (unsigned i = 0; i < positive_brick_count; ++i) {
            RECT& brc = positiveBricks[i];
            brc.left = htrc.left;
            brc.right = htrc.right - NIBLSCALE(4);
            brc.bottom = htrc.bottom - (i * positive_brick_advance);
            brc.top = brc.bottom - positive_brick_size;
        }
        // update last box for hide rounding artefact 
        positiveBricks[positive_brick_count - 1].top = htrc.top;

        // pixel height of each negative brick
        const int negative_brick_size = (hbrc.bottom - hbrc.top - (boxthick * (negative_brick_count - 1))) / negative_brick_count;
        const int negative_brick_advance = negative_brick_size + boxthick;

        // Pre-calculate brick positions for half bottom
        for (unsigned i = 0; i < negative_brick_count; ++i) {
            RECT& brc = negativeBricks[i];
            brc.left = hbrc.left;
            brc.right = hbrc.right - NIBLSCALE(4);
            brc.top = hbrc.top + (i * negative_brick_advance);
            brc.bottom = brc.top + negative_brick_size;
        }
        // update last box for hide rounding artefact 
        negativeBricks[negative_brick_count - 1].bottom = hbrc.bottom;

        DoInit[MDI_DRAWVARIO] = false;
    } // END of INIT

    double vario_value = 0; // can be vario, vario netto or STF offset, depending of config and map mode
    double mc_value = 0; // current MacCready value, used only for Vario or VarioNetto.

    if (ISCAR && DrawInfo.Speed > 0) {
        // Heading is setting Gload, but Heading is not calculated while steady!
        // For this case, we force vario_value to 0.
        //
        // Since we use currently a scale 0-6 for vario, we can use 0-2 for cars.
        // This accounts for an acceleration topscale of 0-100kmh in 13.9 seconds.
        // Not a big acceleration, but very good for normal car usage.
        // We make this concept dynamical, and different for positive and negative accelerations.
        // Because negative accelerations are much higher, on a car. Of course!
        //
        if (DerivedDrawInfo.Gload > 0) {
            if (DerivedDrawInfo.Gload > max_positiveGload) {
                max_positiveGload = DerivedDrawInfo.Gload;
                StartupStore(_T("..... NEW MAXPOSITIVE G=%f\n"), max_positiveGload);
            }
            LKASSERT(max_positiveGload > 0);
            vario_value = (DerivedDrawInfo.Gload / max_positiveGload)*6;
            //StartupStore(_T("Speed=%f G=%f max=%f val=%f\n"),DrawInfo.Speed, DerivedDrawInfo.Gload, max_positiveGload,vario_value);
        }
        if (DerivedDrawInfo.Gload < 0) {
            if (DerivedDrawInfo.Gload < max_negativeGload) {
                max_negativeGload = DerivedDrawInfo.Gload;
                StartupStore(_T("..... NEW MAXNEGATIVE G=%f\n"), max_negativeGload);
            }
            LKASSERT(max_negativeGload < 0);
            vario_value = (DerivedDrawInfo.Gload / max_negativeGload)*-6;
            //StartupStore(_T("Speed=%f G=%f max=%f val=%f\n"),DrawInfo.Speed, DerivedDrawInfo.Gload, max_negativeGload,vario_value);
        }

    } else if (MapWindow::mode.Is(MapWindow::Mode::MODE_CIRCLING) || LKVarioVal == vValVarioVario) {
        if (DrawInfo.VarioAvailable) {
            // UHM. I think we are not painting values correctly for knots &c.
            //vario_value = LIFTMODIFY*DrawInfo.Vario;
            vario_value = DrawInfo.Vario;
        } else {
            vario_value = DerivedDrawInfo.Vario;
        }
        mc_value = MACCREADY;
    } else {
        switch (LKVarioVal) {
            default:
            case vValVarioNetto:
                vario_value = DerivedDrawInfo.NettoVario;
                // simple hack for avoid to used polar curve : glider_sink_rate = Vario - NettoVario;
                mc_value = MACCREADY + (DerivedDrawInfo.Vario - DerivedDrawInfo.NettoVario);
                break;
            case vValVarioSoll:
                double ias;
                if (DrawInfo.AirspeedAvailable && DrawInfo.VarioAvailable)
                    ias = DrawInfo.IndicatedAirspeed;
                else
                    ias = DerivedDrawInfo.IndicatedAirspeedEstimated;

                // m/s 0-nnn autolimit to 20m/s full scale (72kmh diff)
                vario_value = clamp(DerivedDrawInfo.VOpt - ias, -20., 20.);
                vario_value /= 3.3333; // 0-20  -> 0-6
                vario_value *= -1; // if up, push down
                break;
        }
    }


    // Backup selected Brush & Pen
    LKSurface::OldPen oldPen = Surface.SelectObject(LK_NULL_PEN);
    LKSurface::OldBrush oldBrush = Surface.SelectObject(LKBrush_Hollow);
    
    // draw Vario box ( only if not transparent )
    if (LKVarioBar <= vBarVarioGR) {
        Surface.SelectObject(borderPen);
        Surface.SelectObject(hInvBackgroundBrush[BgMapColor]);
        Surface.Rectangle(vrc.left, vrc.top, vrc.right, vrc.bottom);
    }
    // draw middle separator for 0 scale indicator
    Surface.FillRect(&hrc, forgroundBrush);

    Surface.SelectObject(borderPen);
    if (dogaugeinit) {

        // this is causing problems on emulators and condor and most of the times when the gps has no valid date
        // so we don't use seconds, but loop counter
        if (startInitCounter++ > 2) {
            dogaugeinit = false;
        }

        // Demo show all bricks
        for (unsigned i = 0; i < positive_brick_count; ++i) {
            const RECT& brc = positiveBricks[i];
            Surface.SelectObject(positiveBrush[i]);
            Surface.Rectangle(brc.left, brc.top, brc.right, brc.bottom);
        }

        for (unsigned i = 0; i < negative_brick_count; ++i) {
            const RECT& brc = negativeBricks[i];
            Surface.SelectObject(negativeBrush[i]);
            Surface.Rectangle(brc.left, brc.top, brc.right, brc.bottom);
        }

    } else {
        // Draw Real Vario Data

        // Draw Positive Brick 
        for (unsigned i = 0; i < positive_brick_count && vario_value >= positive_vario_step[i]; ++i) {
            const RECT& brc = positiveBricks[i];
            Surface.SelectObject(positiveBrush[i]);
            Surface.Rectangle(brc.left, brc.top, brc.right, brc.bottom);
        }

        // Draw Negative Brick 
        for (unsigned i = 0; i < negative_brick_count && vario_value <= negative_vario_step[i]; ++i) {
            const RECT& brc = negativeBricks[i];
            Surface.SelectObject(negativeBrush[i]);
            Surface.Rectangle(brc.left, brc.top, brc.right, brc.bottom);
        }

        // Draw MacCready Indicator
        const auto step_iterator = std::upper_bound(std::begin(positive_vario_step), std::end(positive_vario_step), mc_value);
        size_t mc_brick_idx = std::distance(std::begin(positive_vario_step), step_iterator);
        if (mc_brick_idx > 1) {
            const PixelRect& brc_next = positiveBricks[mc_brick_idx];
            const PixelRect& brc_Prev = positiveBricks[mc_brick_idx-1];
            
            const PixelSize IconSize = hMcVario.GetSize();
            const PixelSize DrawSize = {
                vrc.GetSize().cx,
                IconSize.cy * vrc.GetSize().cx / IconSize.cx
            };
            const RasterPoint DrawPos = {
                vrc.left,
                brc_Prev.top + ((brc_next.bottom - brc_Prev.top) / 2) + (IconSize.cy / 2)
            };
            hMcVario.Draw(Surface, DrawPos.x, DrawPos.y, DrawSize.cx, DrawSize.cy);
        }
    }
    // cleanup
    Surface.SelectObject(oldPen);
    Surface.SelectObject(oldBrush);
}
Example #12
0
//
// Called by LKDrawLook8000, this is what happens when we change mapspace mode, advancing through types.
// We paint infopages, nearest, tri, etc.etc.
// Normally there is plenty of cpu available because the map is not even calculated.
// This is why we bring to the Draw thread, in the nearest pages case, also calculations.
//
void MapWindow::DrawMapSpace(LKSurface& Surface,  const RECT& rc) {

  BrushReference hB;

  TextInBoxMode_t TextDisplayMode = {0};
  TCHAR Buffer[LKSIZEBUFFERLARGE*2];
#ifdef DRAWLKSTATUS
  bool dodrawlkstatus=false;
#endif


#ifndef DITHER
  if (MapSpaceMode==MSM_WELCOME) {
	if (INVERTCOLORS)
		hB=LKBrush_Petrol;
	  else
		hB=LKBrush_Mlight;
  } else {
	if (INVERTCOLORS)
	  hB=LKBrush_Mdark;
	else
	  hB=LKBrush_Mlight;

  }
#else
  if (INVERTCOLORS)
      hB=LKBrush_Black;
  else
      hB=LKBrush_White;
#endif

  const auto oldfont = Surface.SelectObject(LKINFOFONT); // save font
  if (MapSpaceMode==MSM_WELCOME) {
      LKBitmap WelcomeBitmap = LoadSplash(_T("LKPROFILE"));
      if(WelcomeBitmap) {
          DrawSplash(Surface, WelcomeBitmap);
      }
  } else {
      Surface.FillRect(&rc, hB);
  }

  // Paint borders in green, but only in nearest pages and welcome, and not in DITHER mode
  // In case we want it in dithered mode, some changes are ready to be used.
  #ifndef DITHER
  if (MapSpaceMode==MSM_WELCOME || (!IsMultiMap() && MapSpaceMode!=MSM_MAP) )
  {
     #ifdef DITHER
     LKPen BorderPen(PEN_SOLID, ScreenThinSize, INVERTCOLORS?RGB_WHITE:RGB_BLACK);
     #else
     LKPen BorderPen(PEN_SOLID, ScreenThinSize, INVERTCOLORS?RGB_GREEN:RGB_DARKGREEN);
     #endif
     auto OldPen = Surface.SelectObject(BorderPen);
     auto OldBrush = Surface.SelectObject(LK_HOLLOW_BRUSH);

     Surface.Rectangle(rc.left, rc.top, rc.right, rc.bottom - BottomSize);

     Surface.SelectObject(OldPen);
     Surface.SelectObject(OldBrush);
  }
  #endif


#ifdef DRAWLKSTATUS
  if (LKevent==LKEVENT_NEWRUN) dodrawlkstatus=true;
#endif

  // We are entering mapspacemodes with no initial check on configured subpages.
  // Thus we need to ensure that the page is really available, or find the first valid.
  // However, this will prevent direct customkey access to pages!
  // Instead, we do it when we call next page from InfoPageChange
  // if (!ConfIP[ModeIndex][CURTYPE]) NextModeType();
  switch (MapSpaceMode) {
	case MSM_WELCOME:
#if 0
		SetModeType(LKMODE_MAP,MP_MOVING);
		RefreshMap();
		break;
#endif
#if (1)
		if (!DrawInfo.NAVWarning) {
		static double firsttime=DrawInfo.Time;
		// delayed automatic exit from welcome mode
		if ( DrawInfo.Time > (firsttime+3.0) ) {
			SetModeType(LKMODE_MAP,MP_MOVING);
			LKevent=LKEVENT_NONE;
			LKSound(_T("LK_BEEP1.WAV"));
			RefreshMap();
			break;
		}
		}
#endif
		if(GlobalModelType==MODELTYPE_PNA_MINIMAP)
		{
			SetModeType(LKMODE_MAP,MP_MOVING);
			LKevent=LKEVENT_NONE;
			break;
		}

		DrawWelcome8000(Surface, rc);
		break;
	case MSM_MAPTRK:
		SetSideviewPage(IM_HEADING);
		LKDrawMultimap_Asp(Surface,rc);
		break;
	case MSM_MAPWPT:
		#if 0
		// If there is no destination, force jump to the map
		if (GetOvertargetIndex()<0) {
			SetModeType(LKMODE_MAP,MP_MOVING);
			LKevent=LKEVENT_NONE;
			break;
		}
		#endif
		SetSideviewPage(IM_NEXT_WP);
		LKDrawMultimap_Asp(Surface,rc);
		break;
	case MSM_MAPASP:
		SetSideviewPage(IM_NEAR_AS);
		LKDrawMultimap_Asp(Surface,rc);
		break;
	case MSM_MAPRADAR:
		LKDrawMultimap_Radar(Surface,rc);
		break;
	case MSM_VISUALGLIDE:
		SetSideviewPage(IM_VISUALGLIDE);
		LKDrawMultimap_Asp(Surface,rc);
		break;
	case MSM_MAPTEST:
		LKDrawMultimap_Test(Surface,rc);
		break;
	case MSM_LANDABLE:
	case MSM_NEARTPS:
	case MSM_AIRPORTS:
	case MSM_COMMON:
	case MSM_RECENT:
	case MSM_AIRSPACES:
	case MSM_THERMALS:
	case MSM_TRAFFIC:
		DrawNearest(Surface, rc);
		break;
	case MSM_MAP:
		break;
	case MSM_INFO_THERMAL:
	case MSM_INFO_CRUISE:
	case MSM_INFO_TASK:
	case MSM_INFO_AUX:
	case MSM_INFO_TRI:
	case MSM_INFO_HSI:
	case MSM_INFO_TRF:
	case MSM_INFO_TARGET:
	case MSM_INFO_CONTEST:
		DrawInfoPage(Surface,rc, false);
		break;

  default:
    memset((void*)&TextDisplayMode, 0, sizeof(TextDisplayMode));
    TextDisplayMode.Color = RGB_WHITE;
    TextDisplayMode.NoSetFont = 1;
    TextDisplayMode.AlligneCenter = 1;
    Surface.SelectObject(LK8TargetFont);
    _stprintf(Buffer,TEXT("MapSpaceMode=%d"),MapSpaceMode);
    TextInBox(Surface, &rc, Buffer, (rc.right+rc.left)/2, NIBLSCALE(50) , &TextDisplayMode, false);
    break;
  }
#ifdef DRAWLKSTATUS
  // no need to clear dodrawlkstatus, it is already reset at each run
  if (dodrawlkstatus) DrawLKStatus(hdc, rc);
#endif
  Surface.SelectObject(oldfont);
}
void RenderAirspaceTerrain(LKSurface& Surface, double PosLat, double PosLon, double brg, DiagrammStruct* psDiag) {
    RECT rc = psDiag->rc;
    //rc.bottom +=BORDER_Y;
    double range = psDiag->fXMax - psDiag->fXMin; // km
    double hmax = psDiag->fYMax;
    double lat, lon;
    int i, j;

    if (!IsDithered() && IsMultimapTerrain()) {
        RenderSky(Surface, rc, SKY_HORIZON_COL, SKY_SPACE_COL, GC_NO_COLOR_STEPS);
    } else {
        Surface.FillRect(&rc, MapWindow::hInvBackgroundBrush[BgMapColor]);
    }
    FindLatitudeLongitude(PosLat, PosLon, brg, psDiag->fXMin, &lat, &lon);
    POINT apTerrainPolygon[AIRSPACE_SCANSIZE_X + 4] = {};
    double d_lat[AIRSPACE_SCANSIZE_X] = {};
    double d_lon[AIRSPACE_SCANSIZE_X] = {};
    double d_h[AIRSPACE_SCANSIZE_X] = {};

#define   FRAMEWIDTH 2
    RasterTerrain::Lock(); // want most accurate rounding here
    RasterTerrain::SetTerrainRounding(0, 0);
    double fj;
    for (j = 0; j < AIRSPACE_SCANSIZE_X; j++) { // scan range
        fj = (double) j * 1.0 / (double) (AIRSPACE_SCANSIZE_X - 1);
        FindLatitudeLongitude(lat, lon, brg, range*fj, &d_lat[j], &d_lon[j]);
        d_h[j] = RasterTerrain::GetTerrainHeight(d_lat[j], d_lon[j]);
        if (d_h[j] == TERRAIN_INVALID) d_h[j] = 0; //@ 101027 BUGFIX
        hmax = max(hmax, d_h[j]);
    }

    RasterTerrain::Unlock();


    /********************************************************************************
     * scan line
     ********************************************************************************/
    if (IsMultimapAirspace())
        Sideview_iNoHandeldSpaces = CAirspaceManager::Instance().ScanAirspaceLineList(d_lat, d_lon, d_h, Sideview_pHandeled, MAX_NO_SIDE_AS); //  Sideview_pHandeled[GC_MAX_NO];
    else
        Sideview_iNoHandeldSpaces = 0;
#if BUGSTOP
    LKASSERT(Sideview_iNoHandeldSpaces < MAX_NO_SIDE_AS);
#endif
    if (Sideview_iNoHandeldSpaces >= MAX_NO_SIDE_AS) Sideview_iNoHandeldSpaces = MAX_NO_SIDE_AS - 1;

    /********************************************************************************
     * bubble sort to start with biggest airspaces
     ********************************************************************************/
    int iSizeLookupTable[MAX_NO_SIDE_AS];
    for (i = 0; i < Sideview_iNoHandeldSpaces; i++)
        iSizeLookupTable[i] = i;

    for (i = 0; i < Sideview_iNoHandeldSpaces; i++) {
#if BUGSTOP
        LKASSERT(iSizeLookupTable[i] < MAX_NO_SIDE_AS);
#endif
        for (j = i; j < Sideview_iNoHandeldSpaces; j++) {
#if BUGSTOP
            LKASSERT(iSizeLookupTable[j] < MAX_NO_SIDE_AS);
#endif
            if (iSizeLookupTable[i] >= MAX_NO_SIDE_AS) continue;
            if (iSizeLookupTable[j] >= MAX_NO_SIDE_AS) continue;

            if (Sideview_pHandeled[iSizeLookupTable[i]].iAreaSize < Sideview_pHandeled[iSizeLookupTable[j]].iAreaSize) {
                int iTmp = iSizeLookupTable[i];
                iSizeLookupTable[i] = iSizeLookupTable[j];
                iSizeLookupTable[j] = iTmp;
            }
        }
    }
    /**********************************************************************************
     * transform into diagram coordinates
     **********************************************************************************/
    double dx1 = (double) (rc.right) / (double) (AIRSPACE_SCANSIZE_X - 1);
    int x0 = rc.left;
    LKASSERT(Sideview_iNoHandeldSpaces < MAX_NO_SIDE_AS);
    for (i = 0; i < Sideview_iNoHandeldSpaces; i++) {
        Sideview_pHandeled[i].rc.left = (long) ((Sideview_pHandeled[i].rc.left) * dx1) + x0 - FRAMEWIDTH / 2;
        Sideview_pHandeled[i].rc.right = (long) ((Sideview_pHandeled[i].rc.right) * dx1) + x0 + FRAMEWIDTH / 2;

        Sideview_pHandeled[i].rc.bottom = CalcHeightCoordinat((double) Sideview_pHandeled[i].rc.bottom, psDiag) + FRAMEWIDTH / 2;
        Sideview_pHandeled[i].rc.top = CalcHeightCoordinat((double) Sideview_pHandeled[i].rc.top, psDiag) - FRAMEWIDTH / 2;

        Sideview_pHandeled[i].iMaxBase = Sideview_pHandeled[i].rc.bottom;
        Sideview_pHandeled[i].iMinTop = Sideview_pHandeled[i].rc.top;

        int iN = Sideview_pHandeled[i].iNoPolyPts;
#if BUGSTOP
        LKASSERT(iN < GC_MAX_POLYGON_PTS);
#endif
        if (iN >= GC_MAX_POLYGON_PTS) iN = GC_MAX_POLYGON_PTS - 1;

        if (Sideview_pHandeled[i].bRectAllowed == false) {
            for (j = 0; j < iN; j++) {
                Sideview_pHandeled[i].apPolygon[j].x = (long) (((Sideview_pHandeled[i].apPolygon[j].x) * dx1) + x0);
                Sideview_pHandeled[i].apPolygon[j].y = CalcHeightCoordinat((double) Sideview_pHandeled[i].apPolygon[j].y, psDiag);
                if (j != iN - 1) {
                    if ((j < iN / 2)) {
                        Sideview_pHandeled[i].iMaxBase = min((long) Sideview_pHandeled[i].iMaxBase, (long) Sideview_pHandeled[i].apPolygon[j].y);
                    } else {
                        Sideview_pHandeled[i].iMinTop = max((long) Sideview_pHandeled[i].iMinTop, (long) Sideview_pHandeled[i].apPolygon[j].y);
                    }
                }
            }
        }
    }
    /**********************************************************************************
     * draw airspaces
     **********************************************************************************/
    const auto oldpen = Surface.SelectObject(LK_NULL_PEN);
    _TCHAR text [80];
    LKASSERT(Sideview_iNoHandeldSpaces < MAX_NO_SIDE_AS);
    for (int m = 0; m < Sideview_iNoHandeldSpaces; m++) {
        int iSizeIdx = iSizeLookupTable[m];
#if BUGSTOP
        LKASSERT(iSizeIdx < MAX_NO_SIDE_AS && iSizeIdx >= 0);
#endif
        if (iSizeIdx >= MAX_NO_SIDE_AS) iSizeIdx = MAX_NO_SIDE_AS - 1;

        int type = Sideview_pHandeled[iSizeIdx].iType;
        RECT rcd = Sideview_pHandeled[iSizeIdx].rc;
        LKColor FrameColor;
        double fFrameColFact;
        if (Sideview_pHandeled[iSizeIdx].bEnabled) {
            Surface.SelectObject(MapWindow::GetAirspaceBrushByClass(type));
            Surface.SetTextColor(MapWindow::GetAirspaceColourByClass(type));
            fFrameColFact = 0.8;
            FrameColor = MapWindow::GetAirspaceColourByClass(type);
        } else {
            Surface.SelectObject(LKBrush_Hollow);
            Surface.SetTextColor(RGB_GGREY);
            FrameColor = RGB_GGREY;
            fFrameColFact = 1.2;
        }
        if (INVERTCOLORS)
            fFrameColFact *= 0.8;
        else
            fFrameColFact *= 1.2;
        LKColor Color = FrameColor.ChangeBrightness(fFrameColFact);
        LKPen mpen2(PEN_SOLID, FRAMEWIDTH, Color);
        const auto oldpen2 = Surface.SelectObject(mpen2);

        if (Sideview_pHandeled[iSizeIdx].bRectAllowed == true)
            Surface.Rectangle(rcd.left + 1, rcd.top, rcd.right, rcd.bottom);
        else
            Surface.Polygon(Sideview_pHandeled[iSizeIdx].apPolygon, Sideview_pHandeled[iSizeIdx].iNoPolyPts);

        Surface.SelectObject(oldpen2);

        if (Sideview_pHandeled[iSizeIdx].bEnabled)
            Surface.SetTextColor(Sideview_TextColor); // RGB_MENUTITLEFG
        else
            Surface.SetTextColor(RGB_GGREY);

        /***********************************************
         * build view overlap for centering text
         ***********************************************/
        rcd.bottom = min(rcd.bottom, Sideview_pHandeled[iSizeIdx].iMaxBase);
        rcd.top = max(rcd.top, Sideview_pHandeled[iSizeIdx].iMinTop);

        rcd.left = max(rcd.left, rc.left);
        rcd.right = min(rcd.right, rc.right);
        rcd.bottom = min(rcd.bottom, rc.bottom);
        rcd.top = max(rcd.top, rc.top);
        SIZE textsize;
        SIZE aispacesize = {rcd.right - rcd.left, rcd.bottom - rcd.top};

        LK_tcsncpy(text, Sideview_pHandeled[iSizeIdx].szAS_Name, NAME_SIZE - 1/* sizeof(text)/sizeof(text[0])*/);
        Surface.GetTextSize(text, &textsize);

        int x = rcd.left + aispacesize.cx / 2;
        ;
        int y = rcd.top + aispacesize.cy / 2;
        //  int iTextheight =  tsize.cy;
        int iOffset = 0;
        BOOL blongtext = false;
        if (aispacesize.cy > (2 * textsize.cy) && (textsize.cx < aispacesize.cx)) {
            iOffset = textsize.cy / 2;
        }


        if ((textsize.cx < aispacesize.cx) && (textsize.cy < aispacesize.cy)) {
            Surface.DrawText(x - textsize.cx / 2, y - iOffset - textsize.cy / 2, text);
            blongtext = true;
        }

        LK_tcsncpy(text, CAirspaceManager::Instance().GetAirspaceTypeShortText(Sideview_pHandeled[iSizeIdx].iType), NAME_SIZE);
        Surface.GetTextSize(text, &textsize);
        if (textsize.cx < aispacesize.cx) {
            if (2 * textsize.cy < aispacesize.cy) {
                Surface.DrawText(x - textsize.cx / 2, y + iOffset - textsize.cy / 2, text);
            } else {
                if ((textsize.cy < aispacesize.cy) && (!blongtext))
                    Surface.DrawText(x - textsize.cx / 2, y - iOffset - textsize.cy / 2, text);
            }
        }
    }
    Surface.SelectObject(oldpen);

    /**********************************************************************************
     * draw airspace frames in reversed order
     **********************************************************************************/
#ifdef OUTLINE_2ND
    for (int m = 0; m < Sideview_iNoHandeldSpaces; m++) {
        int iSizeIdx = iSizeLookupTable[Sideview_iNoHandeldSpaces - m - 1];
        if (Sideview_pHandeled[iSizeIdx].bEnabled) {
#if BUGSTOP
            LKASSERT(iSizeIdx < MAX_NO_SIDE_AS);
#endif
            if (iSizeIdx >= MAX_NO_SIDE_AS) iSizeIdx = MAX_NO_SIDE_AS - 1;

            int type = Sideview_pHandeled[iSizeIdx].iType;
            RECT rcd = Sideview_pHandeled[iSizeIdx].rc;
            LKColor FrameColor = MapWindow::GetAirspaceColourByClass(type);
            double fFrameColFact;
            Surface.SelectObject(LKBrush_Hollow);
            if (Sideview_pHandeled[iSizeIdx].bEnabled) {
                //		Surface.SelectObject(MapWindow::GetAirspaceBrushByClass(type));
                Surface.SetTextColor(MapWindow::GetAirspaceColourByClass(type));
                fFrameColFact = 0.8;
            } else {
                Surface.SetTextColor(RGB_GGREY);
                FrameColor = RGB_GGREY;
                fFrameColFact = 1.2;
            }

            if (INVERTCOLORS)
                fFrameColFact *= 0.8;
            else
                fFrameColFact *= 1.2;
            LKColor lColor = FrameColor.ChangeBrightness(fFrameColFact);
            LKPen mpen2(PEN_SOLID, FRAMEWIDTH, lColor);
            const auto oldpen2 = Surface.SelectObject(mpen2);

            if (Sideview_pHandeled[iSizeIdx].bRectAllowed == true)
                Surface.Rectangle(rcd.left + 1, rcd.top, rcd.right, rcd.bottom);
            else
                Surface.Polygon(Sideview_pHandeled[iSizeIdx].apPolygon, Sideview_pHandeled[iSizeIdx].iNoPolyPts);


            Surface.SelectObject(oldpen2);
        }
    }
#endif

    /*************************************************************
     * draw ground
     *************************************************************/

    // draw ground

    /*********************************************************************
     * draw terrain
     *********************************************************************/
    LKPen hpHorizonGround(PEN_SOLID, IBLSCALE(1) + 1, LKColor(126, 62, 50));
    LKBrush hbHorizonGround(GROUND_COLOUR);
    const auto oldPen = Surface.SelectObject(hpHorizonGround);
    const auto oldBrush = Surface.SelectObject(hbHorizonGround);

    for (j = 0; j < AIRSPACE_SCANSIZE_X; j++) { // scan range
        apTerrainPolygon[j].x = iround(j * dx1) + x0;
        apTerrainPolygon[j].y = CalcHeightCoordinat(d_h[j], psDiag);
    }

    apTerrainPolygon[AIRSPACE_SCANSIZE_X].x = iround(AIRSPACE_SCANSIZE_X * dx1) + x0;
    ; // x0;
    apTerrainPolygon[AIRSPACE_SCANSIZE_X].y = CalcHeightCoordinat(0, psDiag); //iBottom;

    apTerrainPolygon[AIRSPACE_SCANSIZE_X + 1].x = iround(0 * dx1) + x0; //iround(j*dx1)+x0;
    apTerrainPolygon[AIRSPACE_SCANSIZE_X + 1].y = CalcHeightCoordinat(0, psDiag); //iBottom;
    apTerrainPolygon[AIRSPACE_SCANSIZE_X + 2] = apTerrainPolygon[0];
    
    static_assert(array_size(apTerrainPolygon) >= AIRSPACE_SCANSIZE_X + 3, "wrong array size");
    Surface.Polygon(apTerrainPolygon, AIRSPACE_SCANSIZE_X + 3);

    Surface.SelectObject(oldPen);
    Surface.SelectObject(oldBrush);

    /*********************************************************************
     * draw sea
     *********************************************************************/
#ifdef MSL_SEA_DRAW
    // draw sea
    if (psDiag->fYMin < GC_SEA_LEVEL_TOLERANCE) {
        RECT sea = {rc.left, rc.bottom, rc.right, rc.bottom + SV_BORDER_Y};
        #ifndef UNDITHER
        RenderSky(Surface, sea, RGB_STEEL_BLUE, RGB_ROYAL_BLUE, 7);
        #else
        RenderSky(Surface, sea, RGB_BLACK, RGB_BLACK, 2);
        #endif
    }
#else
    if (psDiag->fYMin < GC_SEA_LEVEL_TOLERANCE)
        Rectangle(hdc, rc.left, rc.bottom, rc.right, rc.bottom + BORDER_Y);
#endif

    Surface.SetTextColor(Sideview_TextColor); // RGB_MENUTITLEFG
}