bool AbstractTask::UpdateIdle(const AircraftState &state, const GlidePolar &glide_polar) { if (TaskStarted() && task_behaviour.calc_cruise_efficiency) { fixed val = fixed_one; if (CalcCruiseEfficiency(state, glide_polar, val)) stats.cruise_efficiency = std::max(ce_lpf.update(val), fixed_zero); } else { stats.cruise_efficiency = ce_lpf.reset(fixed_one); } if (TaskStarted() && task_behaviour.calc_effective_mc) { fixed val = glide_polar.GetMC(); if (CalcEffectiveMC(state, glide_polar, val)) stats.effective_mc = std::max(em_lpf.update(val), fixed_zero); } else { stats.effective_mc = em_lpf.reset(glide_polar.GetMC()); } if (task_behaviour.calc_glide_required) UpdateStatsGlide(state, glide_polar); else stats.glide_required = fixed_zero; // error return false; }
bool AbstractTask::UpdateIdle(const AircraftState &state, const GlidePolar &glide_polar) { if (stats.start.task_started && task_behaviour.calc_cruise_efficiency && glide_polar.IsValid()) { fixed val = fixed(1); if (CalcCruiseEfficiency(state, glide_polar, val)) stats.cruise_efficiency = std::max(ce_lpf.Update(val), fixed(0)); } else { stats.cruise_efficiency = ce_lpf.Reset(fixed(1)); } if (stats.start.task_started && task_behaviour.calc_effective_mc && glide_polar.IsValid()) { fixed val = glide_polar.GetMC(); if (CalcEffectiveMC(state, glide_polar, val)) stats.effective_mc = std::max(em_lpf.Update(val), fixed(0)); } else { stats.effective_mc = em_lpf.Reset(glide_polar.GetMC()); } if (task_behaviour.calc_glide_required && glide_polar.IsValid()) UpdateStatsGlide(state, glide_polar); else stats.glide_required = fixed(0); // error return false; }
void RenderMacCready(Canvas &canvas, const PixelRect rc, const ChartLook &chart_look, const GlidePolar &glide_polar) { ChartRenderer chart(chart_look, canvas, rc); if (!glide_polar.IsValid()) { chart.DrawNoData(); return; } chart.ScaleXFromValue(0); chart.ScaleXFromValue(MAX_MACCREADY); chart.ScaleYFromValue(0); chart.ScaleYFromValue(glide_polar.GetVMax()); chart.DrawXGrid(Units::ToSysVSpeed(1), 1, ChartRenderer::UnitFormat::NUMERIC); chart.DrawYGrid(Units::ToSysSpeed(10), 10, ChartRenderer::UnitFormat::NUMERIC); GlidePolar gp = glide_polar; double m = 0; double m_last; gp.SetMC(m); double v_last = gp.GetVBestLD(); double vav_last = 0; do { m_last = m; m+= MAX_MACCREADY/STEPS_MACCREADY; gp.SetMC(m); const double v = gp.GetVBestLD(); const double vav = gp.GetAverageSpeed(); chart.DrawLine(m_last, v_last, m, v, ChartLook::STYLE_BLACK); chart.DrawLine(m_last, vav_last, m, vav, ChartLook::STYLE_BLUETHINDASH); v_last = v; vav_last = vav; } while (m<MAX_MACCREADY); // draw current MC setting chart.DrawLine(glide_polar.GetMC(), 0, glide_polar.GetMC(), glide_polar.GetVMax(), ChartLook::STYLE_REDTHICKDASH); // draw labels and other overlays gp.SetMC(0.9*MAX_MACCREADY); chart.DrawLabel(_T("Vopt"), 0.9*MAX_MACCREADY, gp.GetVBestLD()); gp.SetMC(0.9*MAX_MACCREADY); chart.DrawLabel(_T("Vave"), 0.9*MAX_MACCREADY, gp.GetAverageSpeed()); chart.DrawYLabel(_T("V"), Units::GetSpeedName()); chart.DrawXLabel(_T("MC"), Units::GetVerticalSpeedName()); RenderGlidePolarInfo(canvas, rc, chart_look, glide_polar); }
bool UnorderedTask::CalcBestMC(const AircraftState &aircraft, const GlidePolar &glide_polar, fixed& best) const { TaskPoint *tp = GetActiveTaskPoint(); if (!tp) { best = glide_polar.GetMC(); return false; } TaskBestMc bmc(tp, aircraft, task_behaviour.glide, glide_polar); return bmc.search(glide_polar.GetMC(), best); }
bool OrderedTask::CalcEffectiveMC(const AircraftState &aircraft, const GlidePolar &glide_polar, fixed &val) const { if (AllowIncrementalBoundaryStats(aircraft)) { TaskEffectiveMacCready bce(task_points, active_task_point, aircraft, task_behaviour.glide, glide_polar); val = bce.search(glide_polar.GetMC()); return true; } else { val = glide_polar.GetMC(); return false; } }
/** * Specialisation based on simplified theoretical MC cross-country * speeds. Assumes cruise at best LD (ignoring wind) for current MC * setting, climb rate at MC setting, with direct descent possible * at sink rate of cruise. */ explicit AirspaceAircraftPerformance(const GlidePolar &polar) :vertical_tolerance(0), cruise_speed(polar.GetVBestLD()), cruise_descent(polar.GetSBestLD()), descent_rate(polar.GetSMax()), climb_rate(polar.GetMC()), max_speed(polar.GetVMax()) { assert(polar.IsValid()); }
bool AbstractTask::CalcEffectiveMC(const AircraftState &state_now, const GlidePolar &glide_polar, fixed &val) const { val = glide_polar.GetMC(); return true; }
bool OrderedTask::CalcBestMC(const AircraftState &aircraft, const GlidePolar &glide_polar, fixed &best) const { // note setting of lower limit on mc TaskBestMc bmc(task_points, active_task_point, aircraft, task_behaviour.glide, glide_polar); return bmc.search(glide_polar.GetMC(), best); }
bool AbstractTask::UpdateAutoMC(GlidePolar &glide_polar, const AircraftState& state, fixed fallback_mc) { if (!positive(fallback_mc)) fallback_mc = glide_polar.GetMC(); if (!TaskStarted(true) || !task_behaviour.auto_mc) { ResetAutoMC(); return false; } if (task_behaviour.auto_mc_mode == TaskBehaviour::AutoMCMode::CLIMBAVERAGE) { stats.mc_best = mc_lpf.reset(fallback_mc); mc_lpf_valid = true; trigger_auto = false; return false; } fixed mc_found; if (CalcBestMC(state, glide_polar, mc_found)) { // improved solution found, activate auto fg mode if (mc_found > stats.mc_best) trigger_auto = true; } else { // no solution even at mc=0, deactivate auto fg mode trigger_auto = false; } if (!trigger_auto && task_behaviour.auto_mc_mode == TaskBehaviour::AutoMCMode::FINALGLIDE && stats.mc_best >= fixed(0.05)) { /* no solution, but forced final glide AutoMacCready - converge to zero */ mc_found = fixed_zero; trigger_auto = true; } if (trigger_auto && mc_lpf_valid) { // smooth out updates stats.mc_best = std::max(mc_lpf.update(mc_found), fixed_zero); glide_polar.SetMC(stats.mc_best); } else { // reset lpf so will be smooth next time it becomes active stats.mc_best = mc_lpf.reset(fallback_mc); mc_lpf_valid = true; } return trigger_auto; }
static void CheckTotal(const AircraftState &aircraft, const TaskStats &stats, const TaskWaypoint &start, const TaskWaypoint &tp1, const TaskWaypoint &finish) { const fixed min_arrival_alt1 = tp1.GetWaypoint().elevation + task_behaviour.safety_height_arrival; const fixed min_arrival_alt2 = finish.GetWaypoint().elevation + task_behaviour.safety_height_arrival; const GeoVector vector0 = start.GetWaypoint().location.DistanceBearing(tp1.GetWaypoint().location); const GeoVector vector1 = aircraft.location.DistanceBearing(tp1.GetWaypoint().location); const GeoVector vector2 = tp1.GetWaypoint().location.DistanceBearing(finish.GetWaypoint().location); const fixed ld = glide_polar.GetBestLD(); const fixed height_consumption1 = vector1.distance / ld; const fixed height_consumption2 = vector2.distance / ld; const ElementStat &total = stats.total; const GlideResult &solution_remaining = total.solution_remaining; const fixed distance_nominal = vector0.distance + vector2.distance; const fixed distance_ahead = vector1.distance + vector2.distance; ok1(equals(stats.distance_nominal, distance_nominal)); ok1(equals(stats.distance_min, distance_nominal)); ok1(equals(stats.distance_max, distance_nominal)); ok1(!total.vector_remaining.IsValid()); ok1(solution_remaining.IsOk()); ok1(equals(solution_remaining.vector.distance, distance_ahead)); ok1(equals(solution_remaining.height_glide, distance_ahead / ld)); fixed alt_required_at_1 = std::max(min_arrival_alt1, min_arrival_alt2 + height_consumption2); fixed alt_required_at_aircraft = alt_required_at_1 + height_consumption1; ok1(equals(solution_remaining.GetRequiredAltitudeWithDrift(), alt_required_at_aircraft)); ok1(equals(solution_remaining.altitude_difference, aircraft.altitude - alt_required_at_aircraft)); ok1(equals(solution_remaining.height_climb, positive(glide_polar.GetMC()) ? alt_required_at_aircraft - aircraft.altitude : fixed(0))); }
void AbstractTask::UpdateGlideSolutions(const AircraftState &state, const GlidePolar &glide_polar) { GlideSolutionRemaining(state, glide_polar, stats.total.solution_remaining, stats.current_leg.solution_remaining); if (positive(glide_polar.GetMC())) { GlidePolar polar_mc0 = glide_polar; polar_mc0.SetMC(fixed_zero); GlideSolutionRemaining(state, polar_mc0, stats.total.solution_mc0, stats.current_leg.solution_mc0); } else { // no need to re-calculate, just copy stats.total.solution_mc0 = stats.total.solution_remaining; stats.current_leg.solution_mc0 = stats.current_leg.solution_remaining; } GlideSolutionTravelled(state, glide_polar, stats.total.solution_travelled, stats.current_leg.solution_travelled); GlideSolutionPlanned(state, glide_polar, stats.total.solution_planned, stats.current_leg.solution_planned, stats.total.remaining_effective, stats.current_leg.remaining_effective, stats.total.solution_remaining, stats.current_leg.solution_remaining); CalculatePirker(stats.total.pirker, stats.total.planned, stats.total.remaining_effective); CalculatePirker(stats.current_leg.pirker, stats.current_leg.planned, stats.current_leg.remaining_effective); Copy(stats.current_leg.remaining, stats.current_leg.solution_remaining); Copy(stats.current_leg.travelled, stats.current_leg.solution_travelled); Copy(stats.current_leg.planned, stats.current_leg.solution_planned); stats.total.gradient = ::AngleToGradient(CalcGradient(state)); stats.current_leg.gradient = ::AngleToGradient(CalcLegGradient(state)); }
void InputEvents::eventNearestAirspaceDetails(gcc_unused const TCHAR *misc) { const MoreData &basic = CommonInterface::Basic(); const DerivedInfo &calculated = CommonInterface::Calculated(); const ComputerSettings &settings_computer = CommonInterface::GetComputerSettings(); ProtectedAirspaceWarningManager *airspace_warnings = GetAirspaceWarnings(); if (airspace_warnings != NULL && !airspace_warnings->warning_empty()) { // Prevent the dialog from closing itself without active warning // This is relevant if there are only acknowledged airspaces in the list // AutoClose will be reset when the dialog is closed again by hand dlgAirspaceWarningsShowModal(*XCSoarInterface::main_window, *airspace_warnings); return; } const AircraftState aircraft_state = ToAircraftState(basic, calculated); AirspaceVisiblePredicate visible(settings_computer.airspace, CommonInterface::GetMapSettings().airspace, aircraft_state); GlidePolar polar = settings_computer.polar.glide_polar_task; polar.SetMC(max(polar.GetMC(),fixed_one)); AirspaceAircraftPerformanceGlide perf(polar); AirspaceSoonestSort ans(aircraft_state, perf, fixed(1800), visible); const AbstractAirspace* as = ans.find_nearest(airspace_database); if (!as) { return; } dlgAirspaceDetails(*as, airspace_warnings); // clear previous warning if any XCSoarInterface::main_window->popup.Acknowledge(PopupMessage::MSG_AIRSPACE); // TODO code: No control via status data (ala DoStatusMEssage) // - can we change this? // Message::AddMessage(5000, Message::MSG_AIRSPACE, text); }
/** * Specialisation of AirspaceAircraftPerformance for tasks where * part of the path is in cruise, part in final glide. This is * intended to be used temporarily only. * * This simplifies the path by assuming flight is constant altitude * or descent to the task point elevation. */ AirspaceAircraftPerformance(const GlidePolar &polar, const GlideResult &solution) :vertical_tolerance(0.001), cruise_speed(positive(solution.time_elapsed) ? solution.vector.distance / solution.time_elapsed : fixed(1)), cruise_descent(positive(solution.time_elapsed) ? (positive(solution.height_climb) ? -solution.height_climb : solution.height_glide) / solution.time_elapsed : fixed(0)), descent_rate(polar.GetSBestLD()), climb_rate(positive(solution.time_elapsed) && positive(solution.height_climb) ? polar.GetMC() : fixed(0)), max_speed(cruise_speed) { assert(polar.IsValid()); assert(solution.IsOk()); assert(solution.IsAchievable()); }
static void CheckLeg(const TaskWaypoint &tp, const AircraftState &aircraft, const TaskStats &stats) { const GeoPoint destination = tp.GetWaypoint().location; const fixed safety_height = GetSafetyHeight(tp); const fixed min_arrival_alt = tp.GetWaypoint().elevation + safety_height; const GeoVector vector = aircraft.location.DistanceBearing(destination); const fixed ld = glide_polar.GetBestLD(); const fixed height_above_min = aircraft.altitude - min_arrival_alt; const fixed height_consumption = vector.distance / ld; const ElementStat &leg = stats.current_leg; const GlideResult &solution_remaining = leg.solution_remaining; ok1(leg.vector_remaining.IsValid()); ok1(equals(leg.vector_remaining.distance, vector.distance)); ok1(equals(leg.vector_remaining.bearing, vector.bearing)); ok1(solution_remaining.IsOk()); ok1(solution_remaining.vector.IsValid()); ok1(equals(solution_remaining.vector.distance, vector.distance)); ok1(equals(solution_remaining.vector.bearing, vector.bearing)); ok1(equals(solution_remaining.height_glide, height_consumption)); ok1(equals(solution_remaining.altitude_difference, height_above_min - height_consumption)); ok1(equals(solution_remaining.GetRequiredAltitudeWithDrift(), min_arrival_alt + height_consumption)); if (height_above_min >= height_consumption) { /* straight glide */ ok1(equals(solution_remaining.height_climb, 0)); } else if (positive(glide_polar.GetMC())) { /* climb required */ ok1(equals(solution_remaining.height_climb, height_consumption - height_above_min)); } else { /* climb required, but not possible (MC=0) */ ok1(equals(solution_remaining.height_climb, 0)); } }
void RenderClimbChart(Canvas &canvas, const PixelRect rc, const ChartLook &chart_look, const FlightStatistics &fs, const GlidePolar &glide_polar) { ChartRenderer chart(chart_look, canvas, rc); if (fs.thermal_average.IsEmpty()) { chart.DrawNoData(); return; } fixed MACCREADY = glide_polar.GetMC(); chart.ScaleYFromData(fs.thermal_average); chart.ScaleYFromValue(MACCREADY + fixed(0.5)); chart.ScaleYFromValue(fixed(0)); chart.ScaleXFromValue(fixed(-1)); chart.ScaleXFromValue(fixed(fs.thermal_average.GetCount())); chart.DrawYGrid(Units::ToSysVSpeed(fixed(1)), ChartLook::STYLE_THINDASHPAPER, fixed(1), true); chart.DrawBarChart(fs.thermal_average); chart.DrawLine(fixed(0), MACCREADY, fixed(fs.thermal_average.GetCount()), MACCREADY, ChartLook::STYLE_REDTHICK); chart.DrawLabel(_T("MC"), std::max(fixed(0.5), fs.thermal_average.GetGradient() - fixed(1)), MACCREADY); chart.DrawTrendN(fs.thermal_average, ChartLook::STYLE_BLUETHIN); chart.DrawXLabel(_T("n")); chart.DrawYLabel(_T("w"), Units::GetVerticalSpeedName()); }
static void Test(const fixed distance, const fixed altitude, const SpeedVector wind) { const GeoVector vector(distance, Angle::Zero()); const GlideState state(vector, fixed(2000), fixed(2000) + altitude, wind); const GlideResult result = MacCready::Solve(glide_settings, glide_polar, state); const fixed ld_ground = glide_polar.GetLDOverGround(vector.bearing, wind); const fixed mc = glide_polar.GetMC(); const fixed v_climb_progress = mc * ld_ground - state.head_wind; const fixed initial_glide_distance = state.altitude_difference * ld_ground; if (initial_glide_distance >= distance || (!positive(mc) && !positive(v_climb_progress))) { /* reachable by pure glide */ ok1(result.validity == GlideResult::Validity::OK); const fixed best_speed = glide_polar.GetBestGlideRatioSpeed(state.head_wind); const fixed best_sink = glide_polar.SinkRate(best_speed); const fixed ld_ground2 = positive(mc) ? ld_ground : (best_speed - state.head_wind) / best_sink; const fixed height_glide = distance / ld_ground2; const fixed height_climb = fixed(0); const fixed altitude_difference = altitude - height_glide; ok1(equals(result.head_wind, wind.norm)); ok1(equals(result.vector.distance, distance)); ok1(equals(result.height_climb, height_climb)); ok1(equals(result.height_glide, height_glide)); ok1(equals(result.altitude_difference, altitude_difference)); return; } if (!positive(v_climb_progress)) { /* excessive wind */ ok1(result.validity == GlideResult::Validity::WIND_EXCESSIVE); return; } /* const fixed drifted_distance = (distance - initial_glide_distance) * state.head_wind / v_climb_progress; */ const fixed drifted_height_climb = (distance - initial_glide_distance) * mc / v_climb_progress; const fixed drifted_height_glide = drifted_height_climb + state.altitude_difference; const fixed height_glide = drifted_height_glide; const fixed altitude_difference = altitude - height_glide; const fixed height_climb = drifted_height_climb; const fixed time_climb = height_climb / mc; const fixed time_glide = height_glide / glide_polar.GetSBestLD(); const fixed time_elapsed = time_climb + time_glide; /* more tolerance with strong wind because this unit test doesn't optimise pure glide */ const int accuracy = positive(altitude) && positive(wind.norm) ? (wind.norm > fixed(5) ? 5 : 10) : ACCURACY; ok1(result.validity == GlideResult::Validity::OK); ok1(equals(result.head_wind, wind.norm)); ok1(equals(result.vector.distance, distance)); ok1(equals(result.height_climb, height_climb, accuracy)); ok1(equals(result.height_glide, height_glide, accuracy)); ok1(equals(result.altitude_difference, altitude_difference, accuracy)); ok1(equals(result.time_elapsed, time_elapsed, accuracy)); }
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); }
void RenderVarioHistogram(Canvas &canvas, const PixelRect rc, const ChartLook &chart_look, const FlightStatistics &fs, const GlidePolar &glide_polar) { ChartRenderer chart(chart_look, canvas, rc); const auto acc = std::max(fs.vario_cruise_histogram.GetAccumulator(), fs.vario_circling_histogram.GetAccumulator()); if (!acc) { chart.DrawNoData(); return; } const auto scale = std::max(fs.vario_cruise_histogram.GetMaxY(), fs.vario_circling_histogram.GetMaxY()) * 1.2; chart.ScaleXFromValue(0); chart.ScaleXFromValue(scale); const auto s = -glide_polar.GetSBestLD(); const auto mc = glide_polar.GetMC(); chart.ScaleYFromValue(fs.vario_cruise_histogram.GetMinX()); chart.ScaleYFromValue(fs.vario_cruise_histogram.GetMaxX()); chart.ScaleYFromValue(s); chart.ScaleYFromValue(mc); // draw red area at higher than cruise sink rate, blue area above mc { PixelRect rc_upper = chart.GetChartRect(); rc_upper.bottom = chart.ScreenY(mc); DrawVerticalGradient(canvas, rc_upper, chart_look.color_positive, COLOR_WHITE, COLOR_WHITE); } { PixelRect rc_lower = chart.GetChartRect(); rc_lower.top = chart.ScreenY(s); DrawVerticalGradient(canvas, rc_lower, COLOR_WHITE, chart_look.color_negative, COLOR_WHITE); } canvas.SelectNullPen(); canvas.Select(chart_look.black_brush); chart.DrawFilledLineGraph(fs.vario_circling_histogram, true); canvas.Select(chart_look.blank_brush); chart.DrawFilledLineGraph(fs.vario_cruise_histogram, true); // draw these after shaded regions, so they overlay chart.DrawLineGraph(fs.vario_cruise_histogram, ChartLook::STYLE_GREEN, true); chart.DrawLineGraph(fs.vario_circling_histogram, ChartLook::STYLE_RED, true); // draw current MC setting chart.DrawLine(0, mc, scale, mc, ChartLook::STYLE_REDTHICKDASH); chart.DrawLine(0, s, scale, s, ChartLook::STYLE_BLUETHINDASH); // draw labels and other overlays chart.DrawYGrid(Units::ToSysVSpeed(1), 1, ChartRenderer::UnitFormat::NUMERIC); const double tref = chart.GetXMin()*0.1+chart.GetXMax()*0.9; chart.DrawLabel(_T("MC"), tref, mc); chart.DrawLabel(_T("S cruise"), tref, s); chart.DrawYLabel(_T("w"), Units::GetVerticalSpeedName()); }