void WaypointListRenderer::Draw(Canvas &canvas, const PixelRect rc, const Waypoint &waypoint, fixed distance, fixed arrival_altitude, const DialogLook &dialog_look, const WaypointLook &look, const WaypointRendererSettings &settings) { const PixelScalar line_height = rc.bottom - rc.top; const Font &name_font = *dialog_look.list.font; const Font &small_font = *dialog_look.small_font; // Y-Coordinate of the second row PixelScalar top2 = rc.top + name_font.GetHeight() + Layout::FastScale(4); // Use small font for details canvas.Select(small_font); // Draw distance and arrival altitude StaticString<256> buffer; TCHAR dist[20], alt[20], radio[20]; FormatUserDistanceSmart(distance, dist, true); FormatRelativeUserAltitude(arrival_altitude, alt, true); buffer.Format(_T("%s: %s - %s: %s"), _("Distance"), dist, _("Arrival Alt"), alt); if (waypoint.radio_frequency.IsDefined()) { waypoint.radio_frequency.Format(radio, ARRAY_SIZE(radio)); buffer.AppendFormat(_T(" - %s MHz"), radio); } UPixelScalar left = rc.left + line_height + Layout::FastScale(2); canvas.text_clipped(left, top2, rc, buffer); // Draw waypoint name canvas.Select(name_font); canvas.text_clipped(left, rc.top + Layout::FastScale(2), rc, waypoint.name.c_str()); // Draw icon RasterPoint pt = { PixelScalar(rc.left + line_height / 2), PixelScalar(rc.top + line_height / 2) }; WaypointIconRenderer::Reachability reachable = positive(arrival_altitude) ? WaypointIconRenderer::ReachableTerrain : WaypointIconRenderer::Unreachable; WaypointIconRenderer wir(settings, look, canvas); wir.Draw(waypoint, pt, reachable); }
void FlarmTrafficControl::PaintRelativeAltitude(Canvas &canvas, PixelRect rc, fixed relative_altitude) const { // Format relative altitude TCHAR buffer[20]; Unit unit = Units::GetUserAltitudeUnit(); FormatRelativeUserAltitude(relative_altitude, buffer, false); // Calculate unit size canvas.Select(look.info_units_font); const unsigned unit_width = UnitSymbolRenderer::GetSize(canvas, unit).cx; const unsigned unit_height = UnitSymbolRenderer::GetAscentHeight(look.info_units_font, unit); const unsigned space_width = unit_width / 3; // Calculate value size canvas.Select(look.info_values_font); const unsigned value_height = look.info_values_font.GetAscentHeight(); const unsigned value_width = canvas.CalcTextSize(buffer).cx; // Calculate positions const unsigned max_height = std::max(unit_height, value_height); // Paint value canvas.DrawText(rc.right - unit_width - space_width - value_width, rc.bottom - value_height, buffer); // Paint unit canvas.Select(look.info_units_font); UnitSymbolRenderer::Draw(canvas, RasterPoint(rc.right - unit_width, rc.bottom - unit_height), unit, look.unit_fraction_pen); // Paint label canvas.Select(look.info_labels_font); const unsigned label_width = canvas.CalcTextSize(_("Rel. Alt.")).cx; canvas.DrawText(rc.right - label_width, rc.bottom - max_height - look.info_labels_font.GetHeight(), _("Rel. Alt.")); }
static const TCHAR * FormatGlideResult(TCHAR *buffer, size_t size, const GlideResult &result, const GlideSettings &settings) { switch (result.validity) { case GlideResult::Validity::OK: FormatRelativeUserAltitude(result.SelectAltitudeDifference(settings), buffer, size); return buffer; case GlideResult::Validity::WIND_EXCESSIVE: case GlideResult::Validity::MACCREADY_INSUFFICIENT: return _("Too much wind"); case GlideResult::Validity::NO_SOLUTION: return _("No solution"); } return NULL; }
void InfoBoxData::SetValueFromArrival(fixed new_value) { FormatRelativeUserAltitude(new_value, value.buffer(), false); SetValueUnit(Units::current.altitude_unit); }
static void Draw(Canvas &canvas, PixelRect rc, const ArrivalAltitudeMapItem &item, const TwoTextRowsRenderer &row_renderer, const FinalGlideBarLook &look) { const unsigned line_height = rc.GetHeight(); bool reach_relevant = item.reach.IsReachRelevant(); int arrival_altitude = item.reach.terrain_valid == ReachResult::Validity::VALID ? item.reach.terrain : item.reach.direct; if (item.HasElevation()) arrival_altitude -= item.elevation; bool reachable = item.reach.terrain_valid != ReachResult::Validity::UNREACHABLE && arrival_altitude >= 0; // Draw final glide arrow icon const PixelPoint pt(rc.left + line_height / 2, rc.top + line_height / 2); BulkPixelPoint arrow[] = { { -7, -3 }, { 0, 4 }, { 7, -3 } }; Angle arrow_angle = reachable ? Angle::HalfCircle() : Angle::Zero(); PolygonRotateShift(arrow, ARRAY_SIZE(arrow), pt, arrow_angle); if (reachable) { canvas.Select(look.brush_above); canvas.Select(look.pen_above); } else { canvas.Select(look.brush_below); canvas.Select(look.pen_below); } canvas.DrawPolygon(arrow, ARRAY_SIZE(arrow)); const unsigned text_padding = Layout::GetTextPadding(); rc.left += line_height + text_padding; // Format title row TCHAR altitude_buffer[32]; StaticString<256> buffer; buffer.clear(); if (item.HasElevation()) { int relative_arrival_altitude = item.reach.direct - item.elevation; FormatRelativeUserAltitude(relative_arrival_altitude, altitude_buffer, ARRAY_SIZE(altitude_buffer)); buffer.AppendFormat(_T("%s %s, "), altitude_buffer, _("AGL")); } buffer.AppendFormat(_T("%s %s"), FormatUserAltitude(item.reach.direct).c_str(), _("MSL")); // Draw title row row_renderer.DrawFirstRow(canvas, rc, buffer); // Format comment row if (reach_relevant) { buffer.Format(_T("%s: "), _("around terrain")); if (item.HasElevation()) { int relative_arrival_altitude = item.reach.terrain - item.elevation; FormatRelativeUserAltitude(relative_arrival_altitude, altitude_buffer, ARRAY_SIZE(altitude_buffer)); buffer.AppendFormat(_T("%s %s, "), altitude_buffer, _("AGL")); } buffer.AppendFormat(_T("%s %s, "), FormatUserAltitude(item.reach.terrain).c_str(), _("MSL")); } else if (item.HasElevation() && item.reach.direct >= item.elevation && item.reach.terrain_valid == ReachResult::Validity::UNREACHABLE) { buffer.UnsafeFormat(_T("%s "), _("Unreachable due to terrain.")); } else { buffer.clear(); } buffer += _("Arrival altitude incl. safety height"); // Draw comment row row_renderer.DrawSecondRow(canvas, rc, buffer); }
/** * Draws the GliderLink traffic icons onto the given canvas * @param canvas Canvas for drawing */ void MapWindow::DrawGLinkTraffic(Canvas &canvas, const PixelPoint aircraft_pos) const { #ifdef ANDROID // Return if FLARM icons on moving map are disabled if (!GetMapSettings().show_flarm_on_map) return; const GliderLinkTrafficList &traffic = Basic().glink_data.traffic; if (traffic.IsEmpty()) return; const MoreData &basic = Basic(); const WindowProjection &projection = render_projection; canvas.Select(*traffic_look.font); // Circle through the GliderLink targets for (const auto &traf : traffic.list) { // Save the location of the target GeoPoint target_loc = traf.location; // Points for the screen coordinates for the icon, name and average climb PixelPoint sc, sc_name, sc_av, sc_alt; // If FLARM target not on the screen, move to the next one if (!projection.GeoToScreenIfVisible(target_loc, sc)) continue; // Draw the callsign above the icon sc_name = sc; sc_name.x -= Layout::Scale(10); sc_name.y -= Layout::Scale(15); // Draw the average climb to the right of the icon sc_av = sc; sc_av.x += Layout::Scale(10); sc_av.y -= Layout::Scale(8); // Location of altitude label sc_alt = sc; sc_alt.x -= Layout::Scale(10); sc_alt.y -= Layout::Scale(0); TextInBoxMode mode; mode.shape = LabelShape::OUTLINED; mode.align = TextInBoxMode::Alignment::RIGHT; // If callsign/name available draw it to the canvas if (traf.HasName() && !StringIsEmpty(traf.name)) TextInBox(canvas, traf.name, sc_name.x, sc_name.y, mode, GetClientRect()); if (traf.climb_rate_received) { // If average climb data available draw it to the canvas TCHAR label_avg[100]; FormatUserVerticalSpeed(traf.climb_rate, label_avg, false); mode.align = TextInBoxMode::Alignment::LEFT; TextInBox(canvas, label_avg, sc_av.x, sc_av.y, mode, GetClientRect()); } // use GPS altitude to be consistent with GliderLink if(basic.gps_altitude_available && traf.altitude_received && fabs(double(traf.altitude) - basic.gps_altitude) >= 100.0) { // If average climb data available draw it to the canvas TCHAR label_alt[100]; double alt = (double(traf.altitude) - basic.gps_altitude) / 100.0; FormatRelativeUserAltitude(alt, label_alt, false); mode.align = TextInBoxMode::Alignment::RIGHT; TextInBox(canvas, label_alt, sc_alt.x, sc_alt.y, mode, GetClientRect()); } TrafficRenderer::Draw(canvas, traffic_look, traf, traf.track - projection.GetScreenAngle(), sc); } #endif }
/** * Paints the traffic symbols on the given canvas * @param canvas The canvas to paint on */ void FlarmTrafficWindow::PaintRadarTarget(Canvas &canvas, const FlarmTraffic &traffic, unsigned i) { // Save relative East/North fixed x = traffic.relative_east; fixed y = -traffic.relative_north; // Calculate the distance in pixels fixed scale = RangeScale(traffic.distance); // Don't display distracting, far away targets in WarningMode if (WarningMode() && !traffic.HasAlarm() && scale == fixed(radius)) return; // x and y are not between 0 and 1 (distance will be handled via scale) if (!traffic.distance.IsZero()) { x /= traffic.distance; y /= traffic.distance; } else { x = fixed_zero; y = fixed_zero; } if (!enable_north_up) { // Rotate x and y to have a track up display FastRotation::Pair p = fr.Rotate(x, y); x = fixed(p.first); y = fixed(p.second); } // Calculate screen coordinates sc[i].x = radar_mid.x + iround(x * scale); sc[i].y = radar_mid.y + iround(y * scale); const Color *text_color; const Pen *target_pen, *circle_pen; const Brush *target_brush, *arrow_brush; bool hollow_brush = false; unsigned circles = 0; // Set the arrow color depending on alarm level switch (traffic.alarm_level) { case FlarmTraffic::AlarmType::LOW: text_color = &look.default_color; target_pen = circle_pen = &look.warning_pen; target_brush = &look.warning_brush; arrow_brush = &look.default_brush; circles = 1; break; case FlarmTraffic::AlarmType::IMPORTANT: case FlarmTraffic::AlarmType::URGENT: text_color = &look.default_color; target_pen = circle_pen = &look.alarm_pen; target_brush = &look.alarm_brush; arrow_brush = &look.default_brush; circles = 2; break; case FlarmTraffic::AlarmType::NONE: if (WarningMode()) { text_color = &look.passive_color; target_pen = &look.passive_pen; arrow_brush = &look.passive_brush; hollow_brush = true; } else { // Search for team color FlarmFriends::Color team_color = FlarmFriends::GetFriendColor(traffic.id); // If no color found but target is teammate if (team_color == FlarmFriends::NONE && settings.team_flarm_tracking && traffic.id == settings.team_flarm_id) // .. use yellow color team_color = FlarmFriends::GREEN; // If team color found -> draw a colored circle around the target if (team_color != FlarmFriends::NONE) { switch (team_color) { case FlarmFriends::GREEN: circle_pen = &look.team_pen_green; break; case FlarmFriends::BLUE: circle_pen = &look.team_pen_blue; break; case FlarmFriends::YELLOW: circle_pen = &look.team_pen_yellow; break; case FlarmFriends::MAGENTA: circle_pen = &look.team_pen_magenta; break; default: break; } circles = 1; } if (!small && static_cast<unsigned> (selection) == i) { text_color = &look.selection_color; target_brush = arrow_brush = &look.selection_brush; target_pen = &look.selection_pen; } else { hollow_brush = true; if (traffic.IsPassive()) { text_color = &look.passive_color; target_pen = &look.passive_pen; arrow_brush = &look.passive_brush; } else { text_color = &look.default_color; target_pen = &look.default_pen; arrow_brush = &look.default_brush; } } } break; } if (circles > 0) { canvas.SelectHollowBrush(); canvas.Select(*circle_pen); canvas.circle(sc[i].x, sc[i].y, Layout::FastScale(small ? 8 : 16)); if (circles == 2) canvas.circle(sc[i].x, sc[i].y, Layout::FastScale(small ? 10 : 19)); } // Create an arrow polygon RasterPoint Arrow[5]; if (small) { Arrow[0].x = -3; Arrow[0].y = 4; Arrow[1].x = 0; Arrow[1].y = -5; Arrow[2].x = 3; Arrow[2].y = 4; Arrow[3].x = 0; Arrow[3].y = 2; Arrow[4].x = -3; Arrow[4].y = 4; } else { Arrow[0].x = -6; Arrow[0].y = 8; Arrow[1].x = 0; Arrow[1].y = -10; Arrow[2].x = 6; Arrow[2].y = 8; Arrow[3].x = 0; Arrow[3].y = 5; Arrow[4].x = -6; Arrow[4].y = 8; } // Rotate and shift the arrow PolygonRotateShift(Arrow, 5, sc[i].x, sc[i].y, traffic.track - (enable_north_up ? Angle::Zero() : heading)); // Select pen and brush canvas.Select(*target_pen); if (hollow_brush) canvas.SelectHollowBrush(); else canvas.Select(*target_brush); // Draw the polygon canvas.polygon(Arrow, 5); if (small) { if (WarningMode() && !traffic.HasAlarm()) return; const short relalt = iround(Units::ToUserAltitude(traffic.relative_altitude) / 100); // if (relative altitude is other than zero) if (relalt == 0) return; // Write the relativ altitude devided by 100 to the Buffer StaticString<10> buffer; buffer.Format(_T("%d"), abs(relalt)); // Select font canvas.SetBackgroundTransparent(); canvas.Select(look.side_info_font); canvas.SetTextColor(*text_color); // Calculate size of the output string PixelSize tsize = canvas.CalcTextSize(buffer); UPixelScalar dist = Layout::FastScale(traffic.HasAlarm() ? 12 : 8); // Draw string canvas.text(sc[i].x + dist, sc[i].y - tsize.cy / 2, buffer); // Set target_brush for the up/down arrow canvas.Select(*arrow_brush); canvas.SelectNullPen(); // Prepare the triangular polygon RasterPoint triangle[4]; triangle[0].x = 0; triangle[0].y = -4; triangle[1].x = 3; triangle[1].y = 0; triangle[2].x = -3; triangle[2].y = 0; // Flip = -1 for arrow pointing downwards short flip = 1; if (relalt < 0) flip = -1; // Shift the arrow to the right position for (int j = 0; j < 3; j++) { triangle[j].x = Layout::FastScale(triangle[j].x); triangle[j].y = Layout::FastScale(triangle[j].y); triangle[j].x = sc[i].x + dist + triangle[j].x + tsize.cx / 2; triangle[j].y = sc[i].y + flip * (triangle[j].y - tsize.cy / 2); } triangle[3].x = triangle[0].x; triangle[3].y = triangle[0].y; // Draw the arrow canvas.DrawTriangleFan(triangle, 4); return; } // if warning exists -> don't draw vertical speeds if (WarningMode()) return; // if vertical speed to small or negative -> skip this one if (side_display_type == 1 && (!traffic.climb_rate_avg30s_available || traffic.climb_rate_avg30s < fixed(0.5) || traffic.IsPowered())) return; // Select font canvas.SetBackgroundTransparent(); canvas.Select(look.side_info_font); // Format string TCHAR tmp[10]; if (side_display_type == 1) FormatUserVerticalSpeed(traffic.climb_rate_avg30s, tmp, false); else FormatRelativeUserAltitude(traffic.relative_altitude, tmp, true); PixelSize sz = canvas.CalcTextSize(tmp); // Draw vertical speed shadow canvas.SetTextColor(COLOR_WHITE); canvas.text(sc[i].x + Layout::FastScale(11) + 1, sc[i].y - sz.cy / 2 + 1, tmp); canvas.text(sc[i].x + Layout::FastScale(11) - 1, sc[i].y - sz.cy / 2 - 1, tmp); // Select color canvas.SetTextColor(*text_color); // Draw vertical speed canvas.text(sc[i].x + Layout::FastScale(11), sc[i].y - sz.cy / 2, tmp); }
void AirspaceWarningListHandler::OnPaintItem(Canvas &canvas, const PixelRect paint_rc, unsigned i) { TCHAR buffer[128]; // This constant defines the margin that should be respected // for renderring within the paint_rc area. const int padding = 2; if (i == 0 && warning_list.empty()) { /* the warnings were emptied between the opening of the dialog and this refresh, so only need to display "No Warnings" for top item, otherwise exit immediately */ canvas.DrawText(paint_rc.left + Layout::Scale(padding), paint_rc.top + Layout::Scale(padding), _("No Warnings")); return; } assert(i < warning_list.size()); const WarningItem &warning = warning_list[i]; const AbstractAirspace &airspace = *warning.airspace; const AirspaceInterceptSolution &solution = warning.solution; const UPixelScalar text_height = 12, text_top = 1; // word "inside" is used as the etalon, because it is longer than "near" and // currently (9.4.2011) there is no other possibility for the status text. const int status_width = canvas.CalcTextWidth(_T("inside")); // "1888" is used in order to have enough space for 4-digit heights with "AGL" const int altitude_width = canvas.CalcTextWidth(_T("1888 m AGL")); // Dynamic columns scaling - "name" column is flexible, altitude and state // columns are fixed-width. const PixelScalar left0 = Layout::FastScale(padding), left2 = paint_rc.right - Layout::FastScale(padding) - (status_width + 2 * Layout::FastScale(padding)), left1 = left2 - Layout::FastScale(padding) - altitude_width; PixelRect rc_text_clip = paint_rc; rc_text_clip.right = left1 - Layout::FastScale(padding); if (!warning.ack_expired) canvas.SetTextColor(COLOR_GRAY); { // name, altitude info _sntprintf(buffer, 21, _T("%s %s"), airspace.GetName(), AirspaceFormatter::GetClass(airspace)); canvas.DrawClippedText(paint_rc.left + left0, paint_rc.top + Layout::Scale(text_top), rc_text_clip, buffer); AirspaceFormatter::FormatAltitudeShort(buffer, airspace.GetTop()); canvas.DrawText(paint_rc.left + left1, paint_rc.top + Layout::Scale(text_top), buffer); AirspaceFormatter::FormatAltitudeShort(buffer, airspace.GetBase()); canvas.DrawText(paint_rc.left + left1, paint_rc.top + Layout::Scale(text_top + text_height), buffer); } if (warning.state != AirspaceWarning::WARNING_INSIDE && warning.state > AirspaceWarning::WARNING_CLEAR && solution.IsValid()) { _stprintf(buffer, _T("%d secs"), (int)solution.elapsed_time); if (positive(solution.distance)) _stprintf(buffer + _tcslen(buffer), _T(" dist %d m"), (int)solution.distance); else { /* the airspace is right above or below us - show the vertical distance */ _tcscat(buffer, _T(" vertical ")); fixed delta = solution.altitude - CommonInterface::Basic().nav_altitude; FormatRelativeUserAltitude(delta, buffer + _tcslen(buffer), true); } canvas.DrawClippedText(paint_rc.left + left0, paint_rc.top + Layout::Scale(text_top + text_height), rc_text_clip, buffer); } /* draw the warning state indicator */ Color state_color; const TCHAR *state_text; if (warning.state == AirspaceWarning::WARNING_INSIDE) { state_color = warning.ack_expired ? inside_color : inside_ack_color; state_text = _T("inside"); } else if (warning.state > AirspaceWarning::WARNING_CLEAR) { state_color = warning.ack_expired ? near_color : near_ack_color; state_text = _T("near"); } else { state_color = COLOR_WHITE; state_text = NULL; } const PixelSize state_text_size = canvas.CalcTextSize(state_text != NULL ? state_text : _T("W")); if (state_color != COLOR_WHITE) { /* colored background */ PixelRect rc; rc.left = paint_rc.left + left2; rc.top = paint_rc.top + Layout::FastScale(padding); rc.right = paint_rc.right - Layout::FastScale(padding); rc.bottom = paint_rc.bottom - Layout::FastScale(padding); canvas.DrawFilledRectangle(rc, state_color); /* on this background we just painted, we must use black color for the state text; our caller might have selected a different color, override it here */ canvas.SetTextColor(COLOR_BLACK); } if (state_text != NULL) { // -- status text will be centered inside its table cell: canvas.DrawText(paint_rc.left + left2 + Layout::FastScale(padding) + (status_width / 2) - (canvas.CalcTextWidth(state_text) / 2), (paint_rc.bottom + paint_rc.top - state_text_size.cy) / 2, state_text); } }
/** * Paints the basic info for the selected target on the given canvas * @param canvas The canvas to paint on */ void FlarmTrafficControl2::PaintTrafficInfo(Canvas &canvas) const { // Don't paint numbers if no plane selected if (selection == -1 && !WarningMode()) return; // Shortcut to the selected traffic FlarmTraffic traffic = data.list[WarningMode() ? warning : selection]; assert(traffic.IsDefined()); // Temporary string TCHAR tmp[20]; // Temporary string size PixelSize sz; PixelRect rc; rc.left = padding; rc.top = padding; rc.right = canvas.get_width() - padding; rc.bottom = canvas.get_height() - padding; // Set the text color and background switch (traffic.alarm_level) { case FlarmTraffic::AlarmType::LOW: case FlarmTraffic::AlarmType::INFO_ALERT: canvas.SetTextColor(look.warning_color); break; case FlarmTraffic::AlarmType::IMPORTANT: case FlarmTraffic::AlarmType::URGENT: canvas.SetTextColor(look.alarm_color); break; case FlarmTraffic::AlarmType::NONE: canvas.SetTextColor(look.default_color); break; } canvas.SetBackgroundTransparent(); // Climb Rate if (!WarningMode() && traffic.climb_rate_avg30s_available) { FormatUserVerticalSpeed(traffic.climb_rate_avg30s, tmp, 20); canvas.Select(look.info_values_font); sz = canvas.CalcTextSize(tmp); canvas.text(rc.right - sz.cx, rc.top + look.info_labels_font.GetHeight(), tmp); canvas.Select(look.info_labels_font); sz = canvas.CalcTextSize(_("Vario")); canvas.text(rc.right - sz.cx, rc.top, _("Vario")); } // Distance FormatUserDistanceSmart(traffic.distance, tmp, 20, fixed(1000)); canvas.Select(look.info_values_font); sz = canvas.CalcTextSize(tmp); canvas.text(rc.left, rc.bottom - sz.cy, tmp); canvas.Select(look.info_labels_font); canvas.text(rc.left, rc.bottom - look.info_values_font.GetHeight() - look.info_labels_font.GetHeight(), _("Distance")); // Relative Height FormatRelativeUserAltitude(traffic.relative_altitude, tmp, 20); canvas.Select(look.info_values_font); sz = canvas.CalcTextSize(tmp); canvas.text(rc.right - sz.cx, rc.bottom - sz.cy, tmp); canvas.Select(look.info_labels_font); sz = canvas.CalcTextSize(_("Rel. Alt.")); canvas.text(rc.right - sz.cx, rc.bottom - look.info_values_font.GetHeight() - look.info_labels_font.GetHeight(), _("Rel. Alt.")); // ID / Name unsigned font_size; if (traffic.HasName()) { canvas.Select(look.call_sign_font); font_size = look.call_sign_font.GetHeight(); if (!traffic.HasAlarm()) canvas.SetTextColor(look.selection_color); _tcscpy(tmp, traffic.name); } else { font_size = look.info_labels_font.GetHeight(); traffic.id.Format(tmp); } if (!WarningMode()) { // Team color dot FlarmFriends::Color team_color = FlarmFriends::GetFriendColor(traffic.id); // If no color found but target is teammate if (team_color == FlarmFriends::Color::NONE && settings.team_flarm_tracking && traffic.id == settings.team_flarm_id) // .. use yellow color team_color = FlarmFriends::Color::GREEN; // If team color found -> draw a colored circle around the target if (team_color != FlarmFriends::Color::NONE) { switch (team_color) { case FlarmFriends::Color::GREEN: canvas.Select(look.team_brush_green); break; case FlarmFriends::Color::BLUE: canvas.Select(look.team_brush_blue); break; case FlarmFriends::Color::YELLOW: canvas.Select(look.team_brush_green); break; case FlarmFriends::Color::MAGENTA: canvas.Select(look.team_brush_magenta); break; default: break; } canvas.SelectNullPen(); canvas.DrawCircle(rc.left + Layout::FastScale(7), rc.top + (font_size / 2), Layout::FastScale(7)); rc.left += Layout::FastScale(16); } } canvas.text(rc.left, rc.top, tmp); }
void MapItemListRenderer::Draw(Canvas &canvas, const PixelRect rc, const ArrivalAltitudeMapItem &item, const DialogLook &dialog_look, const FinalGlideBarLook &look) { const UPixelScalar line_height = rc.bottom - rc.top; bool elevation_available = !RasterBuffer::IsSpecial((short)item.elevation); bool reach_relevant = item.reach.IsReachRelevant(); RoughAltitude arrival_altitude = item.reach.terrain_valid == ReachResult::Validity::VALID ? item.reach.terrain : item.reach.direct; if (elevation_available) arrival_altitude -= item.elevation; bool reachable = item.reach.terrain_valid != ReachResult::Validity::UNREACHABLE && arrival_altitude.IsPositive(); // Draw final glide arrow icon RasterPoint pt = { (PixelScalar)(rc.left + line_height / 2), (PixelScalar)(rc.top + line_height / 2) }; RasterPoint arrow[] = { { -7, -3 }, { 0, 4 }, { 7, -3 } }; Angle arrow_angle = reachable ? Angle::HalfCircle() : Angle::Zero(); PolygonRotateShift(arrow, ARRAY_SIZE(arrow), pt.x, pt.y, arrow_angle, 100); if (reachable) { canvas.Select(look.brush_above); canvas.Select(look.pen_above); } else { canvas.Select(look.brush_below); canvas.Select(look.pen_below); } canvas.DrawPolygon(arrow, ARRAY_SIZE(arrow)); const Font &name_font = *dialog_look.list.font_bold; const Font &small_font = *dialog_look.small_font; PixelScalar left = rc.left + line_height + Layout::FastScale(2); // Format title row TCHAR altitude_buffer[32]; StaticString<256> buffer; buffer.clear(); if (elevation_available) { RoughAltitude relative_arrival_altitude = item.reach.direct - item.elevation; FormatRelativeUserAltitude(fixed((short)relative_arrival_altitude), altitude_buffer, ARRAY_SIZE(altitude_buffer)); buffer.AppendFormat(_T("%s %s, "), altitude_buffer, _("AGL")); } FormatUserAltitude(fixed(item.reach.direct), altitude_buffer, ARRAY_SIZE(altitude_buffer)); buffer.AppendFormat(_T("%s %s"), altitude_buffer, _("MSL")); // Draw title row canvas.Select(name_font); canvas.DrawClippedText(left, rc.top + Layout::FastScale(2), rc, buffer); // Format comment row if (reach_relevant) { buffer.Format(_T("%s: "), _("around terrain")); if (elevation_available) { RoughAltitude relative_arrival_altitude = item.reach.terrain - item.elevation; FormatRelativeUserAltitude(fixed((short)relative_arrival_altitude), altitude_buffer, ARRAY_SIZE(altitude_buffer)); buffer.AppendFormat(_T("%s %s, "), altitude_buffer, _("AGL")); } FormatUserAltitude(fixed(item.reach.terrain), altitude_buffer, ARRAY_SIZE(altitude_buffer)); buffer.AppendFormat(_T("%s %s, "), altitude_buffer, _("MSL")); } else if (elevation_available && (int)item.reach.direct >= (int)item.elevation && item.reach.terrain_valid == ReachResult::Validity::UNREACHABLE) { buffer.UnsafeFormat(_T("%s "), _("Unreachable due to terrain.")); } else { buffer.clear(); } buffer += _("Arrival altitude incl. safety height"); // Draw comment row canvas.Select(small_font); canvas.DrawClippedText(left, rc.top + name_font.GetHeight() + Layout::FastScale(4), rc, buffer); }
/** * Paints the traffic symbols on the given canvas * @param canvas The canvas to paint on */ void FlarmTrafficWindow::PaintRadarTarget(Canvas &canvas, const FlarmTraffic &traffic, unsigned i) { // Save relative East/North fixed x = traffic.relative_east; fixed y = -traffic.relative_north; // Calculate the distance in pixels fixed scale = RangeScale(traffic.distance); // Don't display distracting, far away targets in WarningMode if (WarningMode() && !traffic.HasAlarm() && scale == fixed(radius)) return; // x and y are not between 0 and 1 (distance will be handled via scale) if (!traffic.distance.IsZero()) { x /= traffic.distance; y /= traffic.distance; } else { x = fixed(0); y = fixed(0); } if (!enable_north_up) { // Rotate x and y to have a track up display FastRotation::Pair p = fr.Rotate(x, y); x = fixed(p.first); y = fixed(p.second); } // Calculate screen coordinates sc[i].x = radar_mid.x + iround(x * scale); sc[i].y = radar_mid.y + iround(y * scale); const Color *text_color; const Pen *target_pen, *circle_pen; const Brush *target_brush, *arrow_brush; bool hollow_brush = false; unsigned circles = 0; // Set the arrow color depending on alarm level switch (traffic.alarm_level) { case FlarmTraffic::AlarmType::LOW: case FlarmTraffic::AlarmType::INFO_ALERT: text_color = &look.default_color; target_pen = circle_pen = &look.warning_pen; target_brush = &look.warning_brush; arrow_brush = &look.default_brush; circles = 1; break; case FlarmTraffic::AlarmType::IMPORTANT: case FlarmTraffic::AlarmType::URGENT: text_color = &look.default_color; target_pen = circle_pen = &look.alarm_pen; target_brush = &look.alarm_brush; arrow_brush = &look.default_brush; circles = 2; break; case FlarmTraffic::AlarmType::NONE: if (WarningMode()) { text_color = &look.passive_color; target_pen = &look.passive_pen; arrow_brush = &look.passive_brush; hollow_brush = true; } else { // Search for team color const FlarmColor team_color = FlarmFriends::GetFriendColor(traffic.id); // If team color found -> draw a colored circle around the target if (team_color != FlarmColor::NONE) { switch (team_color) { case FlarmColor::GREEN: circle_pen = &look.team_pen_green; break; case FlarmColor::BLUE: circle_pen = &look.team_pen_blue; break; case FlarmColor::YELLOW: circle_pen = &look.team_pen_yellow; break; case FlarmColor::MAGENTA: circle_pen = &look.team_pen_magenta; break; default: break; } circles = 1; } if (!small && static_cast<unsigned> (selection) == i) { text_color = &look.selection_color; target_brush = arrow_brush = &look.selection_brush; target_pen = &look.selection_pen; } else { hollow_brush = true; if (traffic.IsPassive()) { text_color = &look.passive_color; target_pen = &look.passive_pen; arrow_brush = &look.passive_brush; } else { text_color = &look.default_color; target_pen = &look.default_pen; arrow_brush = &look.default_brush; } } } break; } if (circles > 0) { canvas.SelectHollowBrush(); canvas.Select(*circle_pen); canvas.DrawCircle(sc[i].x, sc[i].y, Layout::FastScale(small ? 8 : 16)); if (circles == 2) canvas.DrawCircle(sc[i].x, sc[i].y, Layout::FastScale(small ? 10 : 19)); } // Create an arrow polygon RasterPoint Arrow[5]; if (small) { Arrow[0].x = -3; Arrow[0].y = 4; Arrow[1].x = 0; Arrow[1].y = -5; Arrow[2].x = 3; Arrow[2].y = 4; Arrow[3].x = 0; Arrow[3].y = 2; Arrow[4].x = -3; Arrow[4].y = 4; } else { Arrow[0].x = -6; Arrow[0].y = 8; Arrow[1].x = 0; Arrow[1].y = -10; Arrow[2].x = 6; Arrow[2].y = 8; Arrow[3].x = 0; Arrow[3].y = 5; Arrow[4].x = -6; Arrow[4].y = 8; } // Rotate and shift the arrow PolygonRotateShift(Arrow, 5, sc[i].x, sc[i].y, traffic.track - (enable_north_up ? Angle::Zero() : heading)); // Select pen and brush canvas.Select(*target_pen); if (hollow_brush) canvas.SelectHollowBrush(); else canvas.Select(*target_brush); // Draw the polygon canvas.DrawPolygon(Arrow, 5); if (small) { if (!WarningMode() || traffic.HasAlarm()) PaintTargetInfoSmall(canvas, traffic, i, *text_color, *arrow_brush); return; } // if warning exists -> don't draw vertical speeds if (WarningMode()) return; // if vertical speed to small or negative -> skip this one if (side_display_type == SIDE_INFO_VARIO && (!traffic.climb_rate_avg30s_available || traffic.climb_rate_avg30s < fixed(0.5) || traffic.IsPowered())) return; // Select font canvas.SetBackgroundTransparent(); canvas.Select(look.side_info_font); // Format string TCHAR tmp[10]; if (side_display_type == SIDE_INFO_VARIO) FormatUserVerticalSpeed(traffic.climb_rate_avg30s, tmp, false); else FormatRelativeUserAltitude(traffic.relative_altitude, tmp, true); PixelSize sz = canvas.CalcTextSize(tmp); // Draw vertical speed shadow canvas.SetTextColor(COLOR_WHITE); canvas.DrawText(sc[i].x + Layout::FastScale(11) + 1, sc[i].y - sz.cy / 2 + 1, tmp); canvas.DrawText(sc[i].x + Layout::FastScale(11) - 1, sc[i].y - sz.cy / 2 - 1, tmp); // Select color canvas.SetTextColor(*text_color); // Draw vertical speed canvas.DrawText(sc[i].x + Layout::FastScale(11), sc[i].y - sz.cy / 2, tmp); }