void ThermalAssistantWindow::PaintRadarBackground(Canvas &canvas, fixed max_lift) const { canvas.Clear(look.background_color); canvas.SelectHollowBrush(); canvas.Select(look.inner_circle_pen); canvas.DrawCircle(mid.x, mid.y, radius / 2); canvas.Select(look.outer_circle_pen); canvas.DrawCircle(mid.x, mid.y, radius); if (small) return; canvas.SetTextColor(COLOR_BLACK); canvas.Select(look.circle_label_font); canvas.SetBackgroundColor(look.background_color); canvas.SetBackgroundOpaque(); TCHAR lift_string[10]; FormatUserVerticalSpeed(max_lift, lift_string, ARRAY_SIZE(lift_string)); PixelSize s = canvas.CalcTextSize(lift_string); canvas.text(mid.x - s.cx / 2, mid.y + radius - s.cy * 0.75, lift_string); FormatUserVerticalSpeed(fixed_zero, lift_string, ARRAY_SIZE(lift_string)); s = canvas.CalcTextSize(lift_string); canvas.text(mid.x - s.cx / 2, mid.y + radius / 2 - s.cy * 0.75, lift_string); canvas.SetBackgroundTransparent(); }
void MacCreadyEditPanel::Prepare(ContainerWindow &parent, const PixelRect &rc) { LoadWindow(call_back_table, parent, _T("IDR_XML_INFOBOXMACCREADYEDIT")); TCHAR caption[16]; WndButton *button = (WndButton *)form.FindByName(_T("cmdPlusBig")); assert(button != NULL); FormatUserVerticalSpeed( Units::ToSysVSpeed(GetUserVerticalSpeedStep() * 5), caption, false); button->SetCaption(caption); button = (WndButton *)form.FindByName(_T("cmdPlusSmall")); assert(button != NULL); FormatUserVerticalSpeed( Units::ToSysVSpeed(GetUserVerticalSpeedStep()), caption, false); button->SetCaption(caption); button = (WndButton *)form.FindByName(_T("cmdMinusBig")); assert(button != NULL); FormatUserVerticalSpeed( Units::ToSysVSpeed(-GetUserVerticalSpeedStep() * 5), caption, false); button->SetCaption(caption); button = (WndButton *)form.FindByName(_T("cmdMinusSmall")); assert(button != NULL); FormatUserVerticalSpeed( Units::ToSysVSpeed(-GetUserVerticalSpeedStep()), caption, false); button->SetCaption(caption); }
void MapItemListRenderer::Draw(Canvas &canvas, const PixelRect rc, const TrafficMapItem &item, const DialogLook &dialog_look, const TrafficLook &traffic_look) { const PixelScalar line_height = rc.bottom - rc.top; const FlarmTraffic traffic = item.traffic; // Now render the text information const Font &name_font = *dialog_look.list.font; const Font &small_font = *dialog_look.small_font; PixelScalar left = rc.left + line_height + Layout::FastScale(2); const FlarmRecord *record = FlarmDetails::LookupRecord(item.traffic.id); StaticString<256> title_string; if (record && !StringIsEmpty(record->pilot)) title_string = record->pilot.c_str(); else title_string = _("FLARM Traffic"); // Append name to the title, if it exists if (traffic.HasName()) { title_string.append(_T(", ")); title_string.append(traffic.name); } canvas.Select(name_font); canvas.text_clipped(left, rc.top + Layout::FastScale(2), rc, title_string); StaticString<256> info_string; if (record && !StringIsEmpty(record->plane_type)) info_string = record->plane_type; else info_string = FlarmTraffic::GetTypeString(item.traffic.type); // Generate the line of info about the target, if it's available if (traffic.altitude_available) { TCHAR tmp[15]; FormatUserAltitude(traffic.altitude, tmp, 15); info_string.AppendFormat(_T(", %s: %s"), _("Altitude"), tmp); } if (traffic.climb_rate_avg30s_available) { TCHAR tmp[15]; FormatUserVerticalSpeed(traffic.climb_rate_avg30s, tmp, 15); info_string.AppendFormat(_T(", %s: %s"), _("Vario"), tmp); } canvas.Select(small_font); canvas.text_clipped(left, rc.top + name_font.GetHeight() + Layout::FastScale(4), rc, info_string); RasterPoint pt = { (PixelScalar)(rc.left + line_height / 2), (PixelScalar)(rc.top + line_height / 2) }; // Render the representation of the traffic icon TrafficRenderer::Draw(canvas, traffic_look, traffic, traffic.track, item.color, pt); }
static void Draw(Canvas &canvas, PixelRect rc, const ThermalMapItem &item, RoughTimeDelta utc_offset, const TwoTextRowsRenderer &row_renderer, const MapLook &look) { const unsigned line_height = rc.GetHeight(); const unsigned text_padding = Layout::GetTextPadding(); const ThermalSource &thermal = item.thermal; const PixelPoint pt(rc.left + line_height / 2, rc.top + line_height / 2); look.thermal_source_icon.Draw(canvas, pt); rc.left += line_height + text_padding; row_renderer.DrawFirstRow(canvas, rc, _("Thermal")); StaticString<256> buffer; TCHAR lift_buffer[32]; FormatUserVerticalSpeed(thermal.lift_rate, lift_buffer, 32); int timespan = BrokenDateTime::NowUTC().GetSecondOfDay() - (int)thermal.time; if (timespan < 0) timespan += 24 * 60 * 60; buffer.Format(_T("%s: %s - left %s ago (%s)"), _("Avg. lift"), lift_buffer, FormatTimespanSmart(timespan).c_str(), FormatLocalTimeHHMM((int)thermal.time, utc_offset).c_str()); row_renderer.DrawSecondRow(canvas, rc, buffer); }
static void SetVSpeed(InfoBoxData &data, fixed value) { TCHAR buffer[32]; FormatUserVerticalSpeed(value, buffer, false); data.SetValue(buffer[0] == _T('+') ? buffer + 1 : buffer); data.SetValueUnit(Units::current.vertical_speed_unit); }
/** * Updates all the dialogs fields, that are changing frequently. * e.g. climb speed, distance, height */ void FlarmTrafficDetailsWidget::UpdateChanging(const MoreData &basic) { TCHAR tmp[40]; const TCHAR *value; const FlarmTraffic* target = basic.flarm.traffic.FindTraffic(target_id); bool target_ok = target && target->IsDefined(); // Fill distance/direction field if (target_ok) { FormatUserDistanceSmart(target->distance, tmp, 20, fixed(1000)); TCHAR *p = tmp + _tcslen(tmp); *p++ = _T(' '); FormatAngleDelta(p, 20, target->Bearing() - basic.track); value = tmp; } else value = _T("--"); SetText(DISTANCE, value); // Fill altitude field if (target_ok) { TCHAR *p = tmp; if (target->altitude_available) { FormatUserAltitude(target->altitude, p, 20); p += _tcslen(p); *p++ = _T(' '); } Angle dir = Angle::FromXY(target->distance, target->relative_altitude); FormatVerticalAngleDelta(p, 20, dir); value = tmp; } else value = _T("--"); SetText(ALTITUDE, value); // Fill climb speed field if (target_ok && target->climb_rate_avg30s_available) { FormatUserVerticalSpeed(target->climb_rate_avg30s, tmp, 20); value = tmp; } else value = _T("--"); SetText(VARIO, value); }
void InfoBoxContentSpark::SetVSpeedComment(InfoBoxData &data, const TraceVariableHistory &var) { if (var.empty()) return; TCHAR sTmp[32]; FormatUserVerticalSpeed(var.last(), sTmp, ARRAY_SIZE(sTmp)); data.SetComment(sTmp); data.SetCustom(); }
/** * Updates all the dialogs fields, that are changing frequently. * e.g. climb speed, distance, height */ static void UpdateChanging() { TCHAR tmp[20]; const FlarmTraffic* target = XCSoarInterface::Basic().flarm.traffic.FindTraffic(target_id); bool target_ok = target && target->IsDefined(); // Fill distance field if (target_ok) FormatUserDistanceSmart(target->distance, tmp, 20, fixed(1000)); else _tcscpy(tmp, _T("--")); SetFormValue(*wf, _T("prpDistance"), tmp); // Fill horizontal direction field if (target_ok) FormatAngleDelta(tmp, ARRAY_SIZE(tmp), target->Bearing() - CommonInterface::Basic().track); else _tcscpy(tmp, _T("--")); SetFormValue(*wf, _T("prpDirectionH"), tmp); // Fill altitude field if (target_ok && target->altitude_available) FormatUserAltitude(target->altitude, tmp, 20); else _tcscpy(tmp, _T("--")); SetFormValue(*wf, _T("prpAltitude"), tmp); // Fill vertical direction field if (target_ok) { Angle dir = Angle::Radians((fixed)atan2(target->relative_altitude, target->distance)).AsDelta(); FormatVerticalAngleDelta(tmp, ARRAY_SIZE(tmp), dir); } else _tcscpy(tmp, _T("--")); SetFormValue(*wf, _T("prpDirectionV"), tmp); // Fill climb speed field if (target_ok && target->climb_rate_avg30s_available) FormatUserVerticalSpeed(target->climb_rate_avg30s, tmp, 20); else _tcscpy(tmp, _T("--")); SetFormValue(*wf, _T("prpVSpeed"), tmp); }
void MapItemListRenderer::Draw(Canvas &canvas, const PixelRect rc, const ThermalMapItem &item, const DialogLook &dialog_look, const MapLook &look) { const PixelScalar line_height = rc.bottom - rc.top; const ThermalSource &thermal = item.thermal; RasterPoint pt = { PixelScalar(rc.left + line_height / 2), PixelScalar(rc.top + line_height / 2) }; look.thermal_source_icon.Draw(canvas, pt); const Font &name_font = *dialog_look.list.font; const Font &small_font = *dialog_look.small_font; canvas.SetTextColor(COLOR_BLACK); PixelScalar left = rc.left + line_height + Layout::FastScale(2); canvas.Select(name_font); canvas.text_clipped(left, rc.top + Layout::FastScale(2), rc, _("Thermal")); StaticString<256> buffer; TCHAR lift_buffer[32], time_buffer[32], timespan_buffer[32]; FormatUserVerticalSpeed(thermal.lift_rate, lift_buffer, 32); FormatSignedTimeHHMM(time_buffer, TimeLocal((int)thermal.time)); int timespan = BrokenDateTime::NowUTC().GetSecondOfDay() - (int)thermal.time; if (timespan < 0) timespan += 24 * 60 * 60; FormatTimespanSmart(timespan_buffer, timespan); buffer.Format(_T("%s: %s"), _("Avg. lift"), lift_buffer); buffer.append(_T(" - ")); buffer.AppendFormat(_("left %s ago"), timespan_buffer); buffer.AppendFormat(_T(" (%s)"), time_buffer); canvas.Select(small_font); canvas.text_clipped(left, rc.top + name_font.GetHeight() + Layout::FastScale(4), rc, buffer); }
void FlarmTrafficControl::PaintClimbRate(Canvas &canvas, PixelRect rc, fixed climb_rate) const { // Paint label canvas.Select(look.info_labels_font); const unsigned label_width = canvas.CalcTextSize(_("Vario")).cx; canvas.DrawText(rc.right - label_width, rc.top, _("Vario")); // Format climb rate TCHAR buffer[20]; Unit unit = Units::GetUserVerticalSpeedUnit(); FormatUserVerticalSpeed(climb_rate, 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); UPixelScalar 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 int max_height = std::max(unit_height, value_height); const int y = rc.top + look.info_units_font.GetHeight() + max_height; // Paint value canvas.DrawText(rc.right - unit_width - space_width - value_width, y - value_height, buffer); // Paint unit canvas.Select(look.info_units_font); UnitSymbolRenderer::Draw(canvas, RasterPoint(rc.right - unit_width, y - unit_height), unit, look.unit_fraction_pen); }
// MacCready // Adjusts MacCready settings // up, down, auto on, auto off, auto toggle, auto show void InputEvents::eventMacCready(const TCHAR *misc) { if (protected_task_manager == NULL) return; const GlidePolar &polar = CommonInterface::GetComputerSettings().polar.glide_polar_task; fixed mc = polar.GetMC(); TaskBehaviour &task_behaviour = CommonInterface::SetComputerSettings().task; if (StringIsEqual(misc, _T("up"))) { mc = std::min(mc + Units::ToSysVSpeed(GetUserVerticalSpeedStep()), fixed(5)); ActionInterface::SetManualMacCready(mc); } else if (StringIsEqual(misc, _T("down"))) { mc = std::max(mc - Units::ToSysVSpeed(GetUserVerticalSpeedStep()), fixed_zero); ActionInterface::SetManualMacCready(mc); } else if (StringIsEqual(misc, _T("auto toggle"))) { task_behaviour.auto_mc = !task_behaviour.auto_mc; Profile::Set(szProfileAutoMc, task_behaviour.auto_mc); } else if (StringIsEqual(misc, _T("auto on"))) { task_behaviour.auto_mc = true; Profile::Set(szProfileAutoMc, true); } else if (StringIsEqual(misc, _T("auto off"))) { task_behaviour.auto_mc = false; Profile::Set(szProfileAutoMc, false); } else if (StringIsEqual(misc, _T("auto show"))) { if (task_behaviour.auto_mc) { Message::AddMessage(_("Auto. MacCready on")); } else { Message::AddMessage(_("Auto. MacCready off")); } } else if (StringIsEqual(misc, _T("show"))) { TCHAR Temp[100]; FormatUserVerticalSpeed(mc, Temp, false); Message::AddMessage(_("MacCready "), Temp); } }
static void Draw(Canvas &canvas, PixelRect rc, const TrafficMapItem &item, const TwoTextRowsRenderer &row_renderer, const TrafficLook &traffic_look, const TrafficList *traffic_list) { const unsigned line_height = rc.GetHeight(); const unsigned text_padding = Layout::GetTextPadding(); const FlarmTraffic *traffic = traffic_list == nullptr ? nullptr : traffic_list->FindTraffic(item.id); const PixelPoint pt(rc.left + line_height / 2, rc.top + line_height / 2); // Render the representation of the traffic icon if (traffic != nullptr) TrafficRenderer::Draw(canvas, traffic_look, *traffic, traffic->track, item.color, pt); rc.left += line_height + text_padding; // Now render the text information const FlarmNetRecord *record = FlarmDetails::LookupRecord(item.id); StaticString<256> title_string; if (record && !StringIsEmpty(record->pilot)) title_string = record->pilot.c_str(); else title_string = _("FLARM Traffic"); // Append name to the title, if it exists const TCHAR *callsign = FlarmDetails::LookupCallsign(item.id); if (callsign != nullptr && !StringIsEmpty(callsign)) { title_string.append(_T(", ")); title_string.append(callsign); } row_renderer.DrawFirstRow(canvas, rc, title_string); StaticString<256> info_string; if (record && !StringIsEmpty(record->plane_type)) info_string = record->plane_type; else if (traffic != nullptr) info_string = FlarmTraffic::GetTypeString(traffic->type); else info_string = _("Unknown"); // Generate the line of info about the target, if it's available if (traffic != nullptr) { if (traffic->altitude_available) info_string.AppendFormat(_T(", %s: %s"), _("Altitude"), FormatUserAltitude(traffic->altitude).c_str()); if (traffic->climb_rate_avg30s_available) { TCHAR tmp[15]; FormatUserVerticalSpeed(traffic->climb_rate_avg30s, tmp, 15); info_string.AppendFormat(_T(", %s: %s"), _("Vario"), tmp); } } row_renderer.DrawSecondRow(canvas, rc, info_string); }
/** * 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); }
/** * 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 InfoBoxData::SetCommentFromVerticalSpeed(fixed new_value, bool include_sign) { FormatUserVerticalSpeed(new_value, comment.buffer(), true, include_sign); }
/** * Draws the FLARM traffic icons onto the given canvas * @param canvas Canvas for drawing */ void MapWindow::DrawFLARMTraffic(Canvas &canvas, const PixelPoint aircraft_pos) const { // Return if FLARM icons on moving map are disabled if (!GetMapSettings().show_flarm_on_map) return; // Return if FLARM data is not available const TrafficList &flarm = Basic().flarm.traffic; if (flarm.IsEmpty()) return; const WindowProjection &projection = render_projection; // if zoomed in too far out, dont draw traffic since it will be too close to // the glider and so will be meaningless (serves only to clutter, cant help // the pilot) if (projection.GetMapScale() > 7300) return; canvas.Select(*traffic_look.font); // Circle through the FLARM targets for (auto it = flarm.list.begin(), end = flarm.list.end(); it != end; ++it) { const FlarmTraffic &traffic = *it; if (!traffic.location_available) continue; // Save the location of the FLARM target GeoPoint target_loc = traffic.location; // Points for the screen coordinates for the icon, name and average climb PixelPoint sc, sc_name, sc_av; // If FLARM target not on the screen, move to the next one if (!projection.GeoToScreenIfVisible(target_loc, sc)) continue; // Draw the name 16 points below the icon sc_name = sc; sc_name.y -= Layout::Scale(20); // Draw the average climb value above the icon sc_av = sc; sc_av.y += Layout::Scale(5); TextInBoxMode mode; mode.shape = LabelShape::OUTLINED; // JMW TODO enhancement: decluttering of FLARM altitudes (sort by max lift) int dx = sc_av.x - aircraft_pos.x; int dy = sc_av.y - aircraft_pos.y; // only draw labels if not close to aircraft if (dx * dx + dy * dy > Layout::Scale(30 * 30)) { // If FLARM callsign/name available draw it to the canvas if (traffic.HasName() && !StringIsEmpty(traffic.name)) TextInBox(canvas, traffic.name, sc_name.x, sc_name.y, mode, GetClientRect()); if (traffic.climb_rate_avg30s >= 0.1) { // If average climb data available draw it to the canvas TCHAR label_avg[100]; FormatUserVerticalSpeed(traffic.climb_rate_avg30s, label_avg, false); TextInBox(canvas, label_avg, sc_av.x, sc_av.y, mode, GetClientRect()); } } auto color = FlarmFriends::GetFriendColor(traffic.id); TrafficRenderer::Draw(canvas, traffic_look, traffic, traffic.track - projection.GetScreenAngle(), color, sc); } }
/** * 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 }
void VarioBarRenderer::Draw(Canvas &canvas, const PixelRect &rc, const MoreData &basic, const DerivedInfo &calculated, const GlidePolar &glide_polar, const bool vario_bar_avg_enabled) const { #ifdef ENABLE_OPENGL const ScopeAlphaBlend alpha_blend; #endif RasterPoint VarioBar[6] = { { 0, 0 }, { 9, -9 }, { 18, 0 }, { 18, 0 }, { 9, 0 }, { 0, 0 } }; RasterPoint VarioBarAvg[4] = { { 0, 0 }, { 9, -9 }, { 9, 0 }, { 0, 0 } }; RasterPoint clipping_arrow[6] = { { 0, 0 }, { 9, 9 }, { 18, 0 }, { 18, 6 }, { 9, 15 }, { 0, 6 } }; RasterPoint clipping_arrow_av[4] = { { 0, 0 }, { 9, 9 }, { 9, 15 }, { 0, 6 } }; RasterPoint mc_arrow[6] = { { 0, 0 }, { 9, -9 }, { 18, 0 }, { 18, -2 }, { 9, -11 }, { 0, -2 } }; TCHAR Value[10]; const int y0 = (rc.bottom + rc.top) / 2; /* NOTE: size_divisor replaces the fixed value 9 that was used throughout * the code sink which caused fixed size rendering regardless of * map size (except the effects of Layout::Scale()). This method * is not usable with variable map sizes (e.g. because of the cross-section * area). size_divisor is used to introduce a screen size dependent scaling. * That workaround is an ugly hack and needs a rework. */ const auto size_divisor = std::max((fixed) Layout::Scale(70 / (rc.bottom - rc.top)), fixed(0.15)); PixelScalar dy_variobar = 0; PixelScalar dy_variobar_av = 0; PixelScalar dy_variobar_mc = 0; PixelScalar clipping_arrow_offset = Layout::Scale(4); PixelScalar clipping_arrow_av_offset = Layout::Scale(4); // PixelScalar clipping_arrow_mc_offset = Layout::Scale(4); auto vario_gross = basic.brutto_vario; FormatUserVerticalSpeed(vario_gross, Value, false, true); canvas.Select(*look.font); const PixelSize text_size = canvas.CalcTextSize(Value); auto vario_avg = calculated.average; // cut vario_gross at +- 5 meters/s if (vario_gross > fixed(5)) vario_gross = fixed(5); if (vario_gross < fixed(-5)) vario_gross = fixed(-5); int Offset = (int)(vario_gross / size_divisor); Offset = Layout::Scale(Offset); if (!positive(vario_gross)) { VarioBar[1].y = Layout::Scale(9); dy_variobar = text_size.cy + 2; } else { VarioBar[1].y = -Layout::Scale(9); clipping_arrow[1].y = -clipping_arrow[1].y; clipping_arrow[3].y = -clipping_arrow[3].y; clipping_arrow[4].y = -clipping_arrow[4].y; clipping_arrow[5].y = -clipping_arrow[5].y; clipping_arrow_offset = -clipping_arrow_offset; dy_variobar = -1; } // cut vario_avg at +- 5 meters/s if (vario_avg > fixed(5)) vario_avg = fixed(5); if (vario_avg < fixed(-5)) vario_avg = fixed(-5); int OffsetAvg = int(vario_avg / size_divisor); OffsetAvg = Layout::Scale(OffsetAvg); if (!positive(vario_avg)) { VarioBarAvg[1].y = Layout::Scale(9); dy_variobar_av = text_size.cy + 2; } else { VarioBarAvg[1].y = -Layout::Scale(9); clipping_arrow_av[1].y = -clipping_arrow_av[1].y; clipping_arrow_av[2].y = -clipping_arrow_av[2].y; clipping_arrow_av[3].y = -clipping_arrow_av[3].y; clipping_arrow_av_offset = -clipping_arrow_av_offset; dy_variobar_av = -1; } //clip MC Value auto mc_val = glide_polar.GetMC(); if (mc_val > fixed(5)) mc_val = fixed(5); if (!positive(mc_val)) { dy_variobar_mc = text_size.cy + 2; }else{ dy_variobar_mc = -1; } int OffsetMC = int(mc_val / size_divisor); OffsetMC = Layout::Scale(OffsetMC); for (unsigned i = 0; i < 6; i++) { VarioBar[i].y += y0 + dy_variobar; VarioBar[i].x = rc.right - Layout::Scale(VarioBar[i].x); } VarioBar[0].y -= Offset; VarioBar[1].y -= Offset; VarioBar[2].y -= Offset; for (unsigned i = 0; i < 4; i++) { VarioBarAvg[i].y += y0 + dy_variobar_av; VarioBarAvg[i].x = rc.right-Layout::Scale(VarioBarAvg[i].x); } VarioBarAvg[0].y -= OffsetAvg; VarioBarAvg[1].y -= OffsetAvg; // prepare mc arrow for (unsigned i = 0; i < 6; i++) { mc_arrow[i].y = Layout::Scale(mc_arrow[i].y) + y0 - OffsetMC + dy_variobar_mc; mc_arrow[i].x = rc.right - Layout::Scale(mc_arrow[i].x); } // prepare clipping arrow for (unsigned i = 0; i < 6; i++) { clipping_arrow[i].y = Layout::Scale(clipping_arrow[i].y) + y0 - Offset + clipping_arrow_offset + dy_variobar; clipping_arrow[i].x = rc.right - Layout::Scale(clipping_arrow[i].x); } // prepare clipping avg for (unsigned i = 0; i < 4; i++) { clipping_arrow_av[i].y = Layout::Scale(clipping_arrow_av[i].y) + y0 - OffsetAvg + clipping_arrow_av_offset + dy_variobar_av; clipping_arrow_av[i].x = rc.right-Layout::Scale(clipping_arrow_av[i].x); } // draw actual vario bar if (!positive(vario_gross)) { canvas.Select(look.pen_sink); canvas.Select(look.brush_sink); } else { canvas.Select(look.pen_climb); canvas.Select(look.brush_climb); } canvas.DrawPolygon(VarioBar, 6); // draw clipping arrow if (vario_gross <= fixed(-5) || vario_gross >= fixed(5)) canvas.DrawPolygon(clipping_arrow, 6); // draw avg vario bar if (!positive(vario_avg) && vario_bar_avg_enabled) { canvas.Select(look.pen_sink); canvas.Select(look.brush_sink_avg); } else { canvas.Select(look.pen_climb); canvas.Select(look.brush_climb_avg); } canvas.DrawPolygon(VarioBarAvg, 4); if (vario_avg <= fixed(-5.0) || vario_avg >= fixed(5.0)) canvas.DrawPolygon(clipping_arrow_av, 4); //draw MC arrow canvas.Select(look.pen_mc); canvas.Select(look.brush_mc); canvas.DrawPolygon(mc_arrow, 6); //draw text canvas.SetTextColor(COLOR_BLACK); canvas.SetBackgroundColor(COLOR_WHITE); TextInBoxMode style; style.shape = LabelShape::ROUNDED_BLACK; style.move_in_view = true; if (text_size.cx < Layout::Scale(18)) { style.align = TextInBoxMode::Alignment::RIGHT; TextInBox(canvas, Value, rc.right, y0, style, rc); } else TextInBox(canvas, Value, rc.right-Layout::Scale(18), y0, style, rc); }
/** * 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); }