void
ThermalBandComputer::Compute(const MoreData &basic,
                             const DerivedInfo &calculated,
                             ThermalBandInfo &tbi,
                             const ComputerSettings &settings)
{
  if (!basic.NavAltitudeAvailable())
    return;

  const fixed h_safety =
    settings.task.route_planner.safety_height_terrain +
    calculated.GetTerrainBaseFallback();

  tbi.working_band_height = basic.TE_altitude - h_safety;
  if (negative(tbi.working_band_height)) {
    tbi.working_band_fraction = fixed(0);
    return;
  }

  const fixed max_height = tbi.max_thermal_height;
  if (positive(max_height))
    tbi.working_band_fraction = tbi.working_band_height / max_height;
  else
    tbi.working_band_fraction = fixed(1);

  tbi.working_band_ceiling = std::max(max_height + h_safety,
                                      basic.TE_altitude);


  last_vario_available.FixTimeWarp(basic.brutto_vario_available);
  if (basic.brutto_vario_available.Modified(last_vario_available)) {
    last_vario_available = basic.brutto_vario_available;

    // JMW TODO accuracy: Should really work out dt here,
    //           but i'm assuming constant time steps

    if (tbi.max_thermal_height == fixed(0))
      tbi.max_thermal_height = tbi.working_band_height;

    // only do this if in thermal and have been climbing
    if (calculated.circling && calculated.turning &&
        positive(calculated.average))
      tbi.Add(tbi.working_band_height, basic.brutto_vario);
  }
}
void
ThermalBandRenderer::_DrawThermalBand(const MoreData &basic,
                                      const DerivedInfo& calculated,
                                      const ComputerSettings &settings_computer,
                                      ChartRenderer &chart,
                                      const TaskBehaviour& task_props,
                                      const bool is_infobox,
                                      const OrderedTaskBehaviour *ordered_props) const
{
  const ThermalBandInfo &thermal_band = calculated.thermal_band;

  // calculate height above safety height
  fixed hoffset = task_props.route_planner.safety_height_terrain +
    calculated.GetTerrainBaseFallback();

  fixed h = fixed(0);
  if (basic.NavAltitudeAvailable()) {
    h = basic.nav_altitude - hoffset;
    chart.ScaleYFromValue(h);
  }

  bool draw_start_height = false;
  fixed hstart = fixed(0);

  draw_start_height = ordered_props
    && calculated.ordered_task_stats.task_valid
    && ordered_props->start_constraints.max_height != 0
    && calculated.terrain_valid;
  if (draw_start_height) {
    hstart = fixed(ordered_props->start_constraints.max_height);
    if (ordered_props->start_constraints.max_height_ref == AltitudeReference::AGL &&
        calculated.terrain_valid)
      hstart += calculated.terrain_altitude;

    hstart -= hoffset;
    chart.ScaleYFromValue(hstart);
  }

  // no thermalling has been done above safety height
  if (!positive(calculated.thermal_band.max_thermal_height))
    return;

  // calculate averages
  int numtherm = 0;

  fixed Wmax = fixed(0);
  fixed Wav = fixed(0);
  fixed Wt[ThermalBandInfo::NUMTHERMALBUCKETS];
  fixed ht[ThermalBandInfo::NUMTHERMALBUCKETS];

  for (unsigned i = 0; i < ThermalBandInfo::NUMTHERMALBUCKETS; ++i) {
    if (thermal_band.thermal_profile_n[i] < 6) 
      continue;

    if (positive(thermal_band.thermal_profile_w[i])) {
      // height of this thermal point [0,mth]
      // requires 5 items in bucket before displaying, to eliminate kinks
      fixed wthis = thermal_band.thermal_profile_w[i] / thermal_band.thermal_profile_n[i];
      ht[numtherm] = i * calculated.thermal_band.max_thermal_height 
        / ThermalBandInfo::NUMTHERMALBUCKETS;
      Wt[numtherm] = wthis;
      Wmax = std::max(Wmax, wthis);
      Wav+= wthis;
      numtherm++;
    }
  }
  chart.ScaleXFromValue(Wmax);
  if (!numtherm)
    return;
  chart.ScaleXFromValue(fixed(1.5)*Wav/numtherm);

  if ((!draw_start_height) && (numtherm<=1))
    // don't display if insufficient statistics
    // but do draw if start height needs to be drawn
    return;

  const Pen *fpen = is_infobox ? NULL : &look.pen;

  // position of thermal band
  if (numtherm > 1) {
    std::vector< std::pair<fixed, fixed> > thermal_profile;
    thermal_profile.reserve(numtherm);
    for (int i = 0; i < numtherm; ++i)
      thermal_profile.emplace_back(Wt[i], ht[i]);

    if (!is_infobox) {
#ifdef ENABLE_OPENGL
      const GLEnable blend(GL_BLEND);
#endif
      chart.DrawFilledY(thermal_profile, look.brush, fpen);
    } else
      chart.DrawFilledY(thermal_profile, look.brush, fpen);
  }

  // position of thermal band
  if (basic.NavAltitudeAvailable()) {
    const Pen &pen = is_infobox && look.inverse
      ? look.white_pen : look.black_pen;
    chart.DrawLine(fixed(0), h,
                   settings_computer.polar.glide_polar_task.GetMC(), h, pen);

    if (is_infobox && look.inverse)
      chart.GetCanvas().SelectWhiteBrush();
    else
      chart.GetCanvas().SelectBlackBrush();
    chart.DrawDot(settings_computer.polar.glide_polar_task.GetMC(),
                  h, Layout::Scale(2));
  }
}