void MapWindow::DrawTask(Canvas &canvas) { if (task == NULL) return; /* RLD bearing is invalid if GPS not connected and in non-sim mode, but we can still draw targets */ bool draw_bearing = Basic().track_available; bool draw_route = draw_bearing; if (draw_bearing) { if (Calculated().planned_route.size()>2) { draw_bearing = false; } else { draw_route = false; } } ProtectedTaskManager::Lease task_manager(*task); const AbstractTask *task = task_manager->GetActiveTask(); if (task && task->CheckTask()) { RenderTaskPoint::TargetVisibility target_visibility = IsNearSelf() ? RenderTaskPoint::ACTIVE : RenderTaskPoint::ALL; OZRenderer ozv(look.task, airspace_renderer.GetLook(), GetMapSettings().airspace); RenderTaskPoint tpv(canvas, render_projection, look.task, /* we're accessing the OrderedTask here, which may be invalid at this point, but it will be used only if active, so it's ok */ task_manager->GetOrderedTask().GetTaskProjection(), ozv, draw_bearing, target_visibility, Basic().location); TaskRenderer dv(tpv, render_projection.GetScreenBounds()); dv.Draw(*task); } if (draw_route) DrawRoute(canvas); }
void GlueMapWindow::UpdateMapScale() { /* not using MapWindowBlackboard here because these methods are called by the main thread */ const DerivedInfo &calculated = CommonInterface::Calculated(); MapSettings &settings = CommonInterface::SetMapSettings(); const bool circling = CommonInterface::GetUIState().display_mode == DisplayMode::CIRCLING; if (circling && settings.circle_zoom_enabled) return; if (!IsNearSelf()) return; fixed distance = calculated.auto_zoom_distance; if (settings.auto_zoom_enabled && positive(distance)) { // Calculate distance percentage between plane symbol and map edge // 50: centered 100: at edge of map int auto_zoom_factor = circling ? 50 : 100 - settings.glider_screen_position; // Leave 5% of full distance for target display auto_zoom_factor -= 5; // Adjust to account for map scale units auto_zoom_factor *= 8; distance /= fixed(auto_zoom_factor) / 100; // Clip map auto zoom range to reasonable values distance = Clamp(distance, fixed(525), settings.max_auto_zoom_distance / 10); visible_projection.SetFreeMapScale(distance); settings.cruise_scale = visible_projection.GetScale(); OnProjectionModified(); } }
void GlueMapWindow::UpdateProjection() { const PixelRect rc = GetClientRect(); /* not using MapWindowBlackboard here because these methods are called by the main thread */ const NMEAInfo &basic = CommonInterface::Basic(); const DerivedInfo &calculated = CommonInterface::Calculated(); const MapSettings &settings_map = CommonInterface::GetMapSettings(); const bool circling = CommonInterface::GetUIState().display_mode == DisplayMode::CIRCLING; const RasterPoint center = rc.GetCenter(); if (circling || !IsNearSelf()) visible_projection.SetScreenOrigin(center.x, center.y); else if (settings_map.cruise_orientation == DisplayOrientation::NORTH_UP || settings_map.cruise_orientation == DisplayOrientation::WIND_UP) { RasterPoint offset{0, 0}; if (settings_map.glider_screen_position != 50 && settings_map.map_shift_bias != MapShiftBias::NONE) { fixed x = fixed(0); fixed y = fixed(0); if (settings_map.map_shift_bias == MapShiftBias::TRACK) { if (basic.track_available && basic.ground_speed_available && /* 8 m/s ~ 30 km/h */ basic.ground_speed > fixed(8)) { auto angle = basic.track.Reciprocal() - visible_projection.GetScreenAngle(); const auto sc = angle.SinCos(); x = sc.first; y = sc.second; } } else if (settings_map.map_shift_bias == MapShiftBias::TARGET) { if (calculated.task_stats.current_leg.solution_remaining.IsDefined()) { auto angle = calculated.task_stats.current_leg.solution_remaining .vector.bearing.Reciprocal() - visible_projection.GetScreenAngle(); const auto sc = angle.SinCos(); x = sc.first; y = sc.second; } } fixed position_factor = fixed(50 - settings_map.glider_screen_position) / 100; offset.x = PixelScalar(x * (rc.right - rc.left) * position_factor); offset.y = PixelScalar(y * (rc.top - rc.bottom) * position_factor); offset_history.Add(offset); offset = offset_history.GetAverage(); } visible_projection.SetScreenOrigin(center.x + offset.x, center.y + offset.y); } else visible_projection.SetScreenOrigin(center.x, ((rc.top - rc.bottom) * settings_map.glider_screen_position / 100) + rc.bottom); if (!IsNearSelf()) { /* no-op - the Projection's location is updated manually */ } else if (circling && calculated.thermal_locator.estimate_valid) { const fixed d_t = calculated.thermal_locator.estimate_location.Distance(basic.location); if (!positive(d_t)) { SetLocationLazy(basic.location); } else { const fixed d_max = Double(visible_projection.GetMapScale()); const fixed t = std::min(d_t, d_max)/d_t; SetLocation(basic.location.Interpolate(calculated.thermal_locator.estimate_location, t)); } } else if (basic.location_available) // Pan is off SetLocationLazy(basic.location); else if (!visible_projection.IsValid() && terrain != nullptr) /* if there's no GPS fix yet and no home waypoint, start at the map center, to avoid showing a fully white map, which confuses users */ SetLocation(terrain->GetTerrainCenter()); visible_projection.UpdateScreenBounds(); }
void GlueMapWindow::UpdateProjection() { const PixelRect rc = GetClientRect(); /* not using MapWindowBlackboard here because these methods are called by the main thread */ const NMEAInfo &basic = CommonInterface::Basic(); const DerivedInfo &calculated = CommonInterface::Calculated(); const MapSettings &settings_map = CommonInterface::GetMapSettings(); RasterPoint center; center.x = (rc.left + rc.right) / 2; center.y = (rc.top + rc.bottom) / 2; if (InCirclingMode() || !IsNearSelf()) visible_projection.SetScreenOrigin(center.x, center.y); else if (settings_map.cruise_orientation == NORTHUP) { RasterPoint offset{0, 0}; if (settings_map.glider_screen_position != 50 && settings_map.map_shift_bias != MAP_SHIFT_BIAS_NONE) { fixed x = fixed_zero; fixed y = fixed_zero; if (settings_map.map_shift_bias == MAP_SHIFT_BIAS_TRACK) { if (basic.track_available && basic.ground_speed_available && /* 8 m/s ~ 30 km/h */ basic.ground_speed > fixed_int_constant(8)) { const auto sc = basic.track.Reciprocal().SinCos(); x = sc.first; y = sc.second; } } else if (settings_map.map_shift_bias == MAP_SHIFT_BIAS_TARGET) { if (calculated.task_stats.current_leg.solution_remaining.IsDefined()) { const auto sc =calculated.task_stats.current_leg.solution_remaining .vector.bearing.Reciprocal().SinCos(); x = sc.first; y = sc.second; } } fixed position_factor = fixed(50 - settings_map.glider_screen_position) / 100; offset.x = PixelScalar(x * (rc.right - rc.left) * position_factor); offset.y = PixelScalar(y * (rc.top - rc.bottom) * position_factor); offset_history.Add(offset); offset = offset_history.GetAverage(); } visible_projection.SetScreenOrigin(center.x + offset.x, center.y + offset.y); } else visible_projection.SetScreenOrigin(center.x, ((rc.top - rc.bottom) * settings_map.glider_screen_position / 100) + rc.bottom); if (!IsNearSelf()) { /* no-op - the Projection's location is updated manually */ } else if (InCirclingMode() && calculated.thermal_locator.estimate_valid) { const fixed d_t = calculated.thermal_locator.estimate_location.Distance(basic.location); if (!positive(d_t)) { SetLocationLazy(basic.location); } else { const fixed d_max = Double(visible_projection.GetMapScale()); const fixed t = std::min(d_t, d_max)/d_t; SetLocation(basic.location.Interpolate(calculated.thermal_locator.estimate_location, t)); } } else if (basic.location_available) // Pan is off SetLocationLazy(basic.location); visible_projection.UpdateScreenBounds(); }
/** * Draw the final glide groundline (and shading) to the buffer * and copy the transparent buffer to the canvas * @param canvas The drawing canvas * @param rc The area to draw in * @param buffer The drawing buffer */ void MapWindow::DrawTerrainAbove(Canvas &canvas) { // Don't draw at all if // .. no GPS fix // .. not flying // .. feature disabled // .. feature inaccessible if (!Basic().location_available || !Calculated().flight.flying || GetComputerSettings().features.final_glide_terrain == FeaturesSettings::FinalGlideTerrain::OFF || route_planner == nullptr) return; // Create a visitor for the Reach code TriangleCompound visitor(render_projection); // Fill the TriangleCompound with all TriangleFans in range route_planner->AcceptInRange(render_projection.GetScreenBounds(), visitor); // Exit early if not fans found if (visitor.fans.empty()) return; // @todo: update this rendering // Don't draw shade if // .. shade feature disabled // .. pan mode activated if (GetComputerSettings().features.final_glide_terrain == FeaturesSettings::FinalGlideTerrain::SHADE && IsNearSelf()) { #ifdef ENABLE_OPENGL const ScopeVertexPointer vp(&visitor.fans.points[0]); const GLEnable stencil_test(GL_STENCIL_TEST); glClear(GL_STENCIL_BUFFER_BIT); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); COLOR_WHITE.Set(); visitor.fans.DrawFill(canvas); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_NOTEQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); const GLBlend blend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); canvas.Clear(Color(255, 255, 255, 77)); #elif defined(USE_GDI) // Get a buffer for drawing a mask Canvas &buffer = buffer_canvas; // Set the pattern colors buffer.SetBackgroundOpaque(); buffer.SetBackgroundColor(COLOR_WHITE); buffer.SetTextColor(Color(0xd0, 0xd0, 0xd0)); // Paint the whole buffer canvas with a pattern brush (small dots) buffer.Clear(look.above_terrain_brush); // Select the TerrainLine pen buffer.SelectHollowBrush(); buffer.Select(look.reach_pen_thick); buffer.SetBackgroundColor(Color(0xf0, 0xf0, 0xf0)); // Draw the TerrainLine polygons visitor.fans.DrawOutline(buffer); // Select a white brush (will later be transparent) buffer.SelectNullPen(); buffer.SelectWhiteBrush(); // Draw the TerrainLine polygons to remove the // brush pattern from the polygon areas visitor.fans.DrawFill(buffer); // Copy everything non-white to the buffer canvas.CopyTransparentWhite(0, 0, render_projection.GetScreenWidth(), render_projection.GetScreenHeight(), buffer, 0, 0); /* skip the separate terrain line step below, because we have done it already */ return; #endif } if (visitor.fans.size() == 1) { /* only one fan: we can draw a simple polygon */ #ifdef ENABLE_OPENGL const ScopeVertexPointer vp(&visitor.fans.points[0]); look.reach_pen.Bind(); #else // Select the TerrainLine pen canvas.SelectHollowBrush(); canvas.Select(look.reach_pen); canvas.SetBackgroundOpaque(); canvas.SetBackgroundColor(COLOR_WHITE); // drop out extraneous line from origin #endif // Draw the TerrainLine polygon visitor.fans.DrawOutline(canvas); #ifdef ENABLE_OPENGL look.reach_pen.Unbind(); #endif } else { /* more than one fan (turning reach enabled): we have to use a stencil to draw the outline, because the fans may overlap */ #ifdef ENABLE_OPENGL const ScopeVertexPointer vp(&visitor.fans.points[0]); glEnable(GL_STENCIL_TEST); glClear(GL_STENCIL_BUFFER_BIT); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); COLOR_WHITE.Set(); visitor.fans.DrawFill(canvas); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_NOTEQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); look.reach_pen_thick.Bind(); visitor.fans.DrawOutline(canvas); look.reach_pen_thick.Unbind(); glDisable(GL_STENCIL_TEST); #elif defined(USE_GDI) || defined(USE_MEMORY_CANVAS) // Get a buffer for drawing a mask Canvas &buffer = buffer_canvas; // Paint the whole buffer canvas white ( = transparent) buffer.ClearWhite(); // Select the TerrainLine pen buffer.SelectHollowBrush(); buffer.Select(look.reach_pen_thick); buffer.SetBackgroundOpaque(); buffer.SetBackgroundColor(Color(0xf0, 0xf0, 0xf0)); // Draw the TerrainLine polygons visitor.fans.DrawOutline(buffer); // Select a white brush (will later be transparent) buffer.SelectNullPen(); buffer.SelectWhiteBrush(); // Draw the TerrainLine polygons again to remove // the lines connecting all the polygons // // This removes half of the TerrainLine line width !! visitor.fans.DrawFill(buffer); // Copy everything non-white to the buffer canvas.CopyTransparentWhite(0, 0, render_projection.GetScreenWidth(), render_projection.GetScreenHeight(), buffer, 0, 0); #endif } }