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); } } }
// // 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); } }
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); } }
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; }
// 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 }
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); }
// // 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 }