Exemplo n.º 1
0
void
MapWindow::DrawTask(Canvas &canvas)
{
  if (task == NULL)
    return;

  /* RLD bearing is invalid if GPS not connected and in non-sim mode,
   but we can still draw targets */
  bool draw_bearing = Basic().track_available;
  bool draw_route = draw_bearing;

  if (draw_bearing) {
    if (Calculated().planned_route.size()>2) {
      draw_bearing = false;
    } else {
      draw_route = false;
    }
  }

  ProtectedTaskManager::Lease task_manager(*task);
  const AbstractTask *task = task_manager->GetActiveTask();
  if (task && task->CheckTask()) {
    RenderTaskPoint::TargetVisibility target_visibility =
        IsNearSelf() ? RenderTaskPoint::ACTIVE : RenderTaskPoint::ALL;

    OZRenderer ozv(look.task, airspace_renderer.GetLook(),
                              GetMapSettings().airspace);
    RenderTaskPoint tpv(canvas,
                        render_projection,
                        look.task,
                        /* we're accessing the OrderedTask here,
                           which may be invalid at this point, but it
                           will be used only if active, so it's ok */
                        task_manager->GetOrderedTask().GetTaskProjection(),
                        ozv, draw_bearing,
                        target_visibility,
                        Basic().location);
    TaskRenderer dv(tpv, render_projection.GetScreenBounds());
    dv.Draw(*task);
  }

  if (draw_route)
    DrawRoute(canvas);
}
void
GlueMapWindow::UpdateMapScale()
{
  /* not using MapWindowBlackboard here because these methods are
     called by the main thread */
  const DerivedInfo &calculated = CommonInterface::Calculated();
  MapSettings &settings = CommonInterface::SetMapSettings();
  const bool circling =
    CommonInterface::GetUIState().display_mode == DisplayMode::CIRCLING;

  if (circling && settings.circle_zoom_enabled)
    return;

  if (!IsNearSelf())
    return;

  fixed distance = calculated.auto_zoom_distance;
  if (settings.auto_zoom_enabled && positive(distance)) {
    // Calculate distance percentage between plane symbol and map edge
    // 50: centered  100: at edge of map
    int auto_zoom_factor = circling
      ? 50
      : 100 - settings.glider_screen_position;

    // Leave 5% of full distance for target display
    auto_zoom_factor -= 5;
    // Adjust to account for map scale units
    auto_zoom_factor *= 8;

    distance /= fixed(auto_zoom_factor) / 100;

    // Clip map auto zoom range to reasonable values
    distance = Clamp(distance, fixed(525),
                     settings.max_auto_zoom_distance / 10);

    visible_projection.SetFreeMapScale(distance);
    settings.cruise_scale = visible_projection.GetScale();

    OnProjectionModified();
  }
}
void
GlueMapWindow::UpdateProjection()
{
  const PixelRect rc = GetClientRect();

  /* not using MapWindowBlackboard here because these methods are
     called by the main thread */
  const NMEAInfo &basic = CommonInterface::Basic();
  const DerivedInfo &calculated = CommonInterface::Calculated();
  const MapSettings &settings_map = CommonInterface::GetMapSettings();
  const bool circling =
    CommonInterface::GetUIState().display_mode == DisplayMode::CIRCLING;

  const RasterPoint center = rc.GetCenter();

  if (circling || !IsNearSelf())
    visible_projection.SetScreenOrigin(center.x, center.y);
  else if (settings_map.cruise_orientation == DisplayOrientation::NORTH_UP ||
           settings_map.cruise_orientation == DisplayOrientation::WIND_UP) {
    RasterPoint offset{0, 0};
    if (settings_map.glider_screen_position != 50 &&
        settings_map.map_shift_bias != MapShiftBias::NONE) {
      fixed x = fixed(0);
      fixed y = fixed(0);
      if (settings_map.map_shift_bias == MapShiftBias::TRACK) {
        if (basic.track_available &&
            basic.ground_speed_available &&
             /* 8 m/s ~ 30 km/h */
            basic.ground_speed > fixed(8)) {
          auto angle = basic.track.Reciprocal() - visible_projection.GetScreenAngle();

          const auto sc = angle.SinCos();
          x = sc.first;
          y = sc.second;
        }
      } else if (settings_map.map_shift_bias == MapShiftBias::TARGET) {
        if (calculated.task_stats.current_leg.solution_remaining.IsDefined()) {
          auto angle = calculated.task_stats.current_leg.solution_remaining
              .vector.bearing.Reciprocal() - visible_projection.GetScreenAngle();

          const auto sc = angle.SinCos();
          x = sc.first;
          y = sc.second;
        }
      }
      fixed position_factor = fixed(50 - settings_map.glider_screen_position) / 100;
      offset.x = PixelScalar(x * (rc.right - rc.left) * position_factor);
      offset.y = PixelScalar(y * (rc.top - rc.bottom) * position_factor);
      offset_history.Add(offset);
      offset = offset_history.GetAverage();
    }
    visible_projection.SetScreenOrigin(center.x + offset.x, center.y + offset.y);
  } else
    visible_projection.SetScreenOrigin(center.x,
        ((rc.top - rc.bottom) * settings_map.glider_screen_position / 100) + rc.bottom);

  if (!IsNearSelf()) {
    /* no-op - the Projection's location is updated manually */
  } else if (circling && calculated.thermal_locator.estimate_valid) {
    const fixed d_t = calculated.thermal_locator.estimate_location.Distance(basic.location);
    if (!positive(d_t)) {
      SetLocationLazy(basic.location);
    } else {
      const fixed d_max = Double(visible_projection.GetMapScale());
      const fixed t = std::min(d_t, d_max)/d_t;
      SetLocation(basic.location.Interpolate(calculated.thermal_locator.estimate_location,
                                               t));
    }
  } else if (basic.location_available)
    // Pan is off
    SetLocationLazy(basic.location);
  else if (!visible_projection.IsValid() && terrain != nullptr)
    /* if there's no GPS fix yet and no home waypoint, start at the
       map center, to avoid showing a fully white map, which confuses
       users */
    SetLocation(terrain->GetTerrainCenter());

  visible_projection.UpdateScreenBounds();
}
Exemplo n.º 4
0
void
GlueMapWindow::UpdateProjection()
{
  const PixelRect rc = GetClientRect();

  /* not using MapWindowBlackboard here because these methods are
     called by the main thread */
  const NMEAInfo &basic = CommonInterface::Basic();
  const DerivedInfo &calculated = CommonInterface::Calculated();
  const MapSettings &settings_map = CommonInterface::GetMapSettings();

  RasterPoint center;
  center.x = (rc.left + rc.right) / 2;
  center.y = (rc.top + rc.bottom) / 2;

  if (InCirclingMode() || !IsNearSelf())
    visible_projection.SetScreenOrigin(center.x, center.y);
  else if (settings_map.cruise_orientation == NORTHUP) {
    RasterPoint offset{0, 0};
    if (settings_map.glider_screen_position != 50 &&
        settings_map.map_shift_bias != MAP_SHIFT_BIAS_NONE) {
      fixed x = fixed_zero;
      fixed y = fixed_zero;
      if (settings_map.map_shift_bias == MAP_SHIFT_BIAS_TRACK) {
        if (basic.track_available &&
            basic.ground_speed_available &&
             /* 8 m/s ~ 30 km/h */
            basic.ground_speed > fixed_int_constant(8)) {
          const auto sc = basic.track.Reciprocal().SinCos();
          x = sc.first;
          y = sc.second;
        }
      } else if (settings_map.map_shift_bias == MAP_SHIFT_BIAS_TARGET) {
        if (calculated.task_stats.current_leg.solution_remaining.IsDefined()) {
          const auto sc =calculated.task_stats.current_leg.solution_remaining
            .vector.bearing.Reciprocal().SinCos();
          x = sc.first;
          y = sc.second;
        }
      }
      fixed position_factor = fixed(50 - settings_map.glider_screen_position) / 100;
      offset.x = PixelScalar(x * (rc.right - rc.left) * position_factor);
      offset.y = PixelScalar(y * (rc.top - rc.bottom) * position_factor);
      offset_history.Add(offset);
      offset = offset_history.GetAverage();
    }
    visible_projection.SetScreenOrigin(center.x + offset.x, center.y + offset.y);
  } else
    visible_projection.SetScreenOrigin(center.x,
        ((rc.top - rc.bottom) * settings_map.glider_screen_position / 100) + rc.bottom);

  if (!IsNearSelf()) {
    /* no-op - the Projection's location is updated manually */
  } else if (InCirclingMode() && calculated.thermal_locator.estimate_valid) {
    const fixed d_t = calculated.thermal_locator.estimate_location.Distance(basic.location);
    if (!positive(d_t)) {
      SetLocationLazy(basic.location);
    } else {
      const fixed d_max = Double(visible_projection.GetMapScale());
      const fixed t = std::min(d_t, d_max)/d_t;
      SetLocation(basic.location.Interpolate(calculated.thermal_locator.estimate_location,
                                               t));
    }
  } else if (basic.location_available)
    // Pan is off
    SetLocationLazy(basic.location);

  visible_projection.UpdateScreenBounds();
}
Exemplo n.º 5
0
/**
 * Draw the final glide groundline (and shading) to the buffer
 * and copy the transparent buffer to the canvas
 * @param canvas The drawing canvas
 * @param rc The area to draw in
 * @param buffer The drawing buffer
 */
void
MapWindow::DrawTerrainAbove(Canvas &canvas)
{
  // Don't draw at all if
  // .. no GPS fix
  // .. not flying
  // .. feature disabled
  // .. feature inaccessible
  if (!Basic().location_available
      || !Calculated().flight.flying
      || GetComputerSettings().features.final_glide_terrain == FeaturesSettings::FinalGlideTerrain::OFF
      || route_planner == nullptr)
    return;

  // Create a visitor for the Reach code
  TriangleCompound visitor(render_projection);

  // Fill the TriangleCompound with all TriangleFans in range
  route_planner->AcceptInRange(render_projection.GetScreenBounds(), visitor);

  // Exit early if not fans found
  if (visitor.fans.empty())
    return;

  // @todo: update this rendering

  // Don't draw shade if
  // .. shade feature disabled
  // .. pan mode activated
  if (GetComputerSettings().features.final_glide_terrain == FeaturesSettings::FinalGlideTerrain::SHADE &&
      IsNearSelf()) {

#ifdef ENABLE_OPENGL

    const ScopeVertexPointer vp(&visitor.fans.points[0]);

    const GLEnable stencil_test(GL_STENCIL_TEST);
    glClear(GL_STENCIL_BUFFER_BIT);

    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

    glStencilFunc(GL_ALWAYS, 1, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    COLOR_WHITE.Set();
    visitor.fans.DrawFill(canvas);

    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glStencilFunc(GL_NOTEQUAL, 1, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

    const GLBlend blend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    canvas.Clear(Color(255, 255, 255, 77));

#elif defined(USE_GDI)

    // Get a buffer for drawing a mask
    Canvas &buffer = buffer_canvas;

    // Set the pattern colors
    buffer.SetBackgroundOpaque();
    buffer.SetBackgroundColor(COLOR_WHITE);
    buffer.SetTextColor(Color(0xd0, 0xd0, 0xd0));

    // Paint the whole buffer canvas with a pattern brush (small dots)
    buffer.Clear(look.above_terrain_brush);

    // Select the TerrainLine pen
    buffer.SelectHollowBrush();
    buffer.Select(look.reach_pen_thick);
    buffer.SetBackgroundColor(Color(0xf0, 0xf0, 0xf0));

    // Draw the TerrainLine polygons
    visitor.fans.DrawOutline(buffer);

    // Select a white brush (will later be transparent)
    buffer.SelectNullPen();
    buffer.SelectWhiteBrush();

    // Draw the TerrainLine polygons to remove the
    // brush pattern from the polygon areas
    visitor.fans.DrawFill(buffer);

    // Copy everything non-white to the buffer
    canvas.CopyTransparentWhite(0, 0,
                                render_projection.GetScreenWidth(),
                                render_projection.GetScreenHeight(),
                                buffer, 0, 0);

    /* skip the separate terrain line step below, because we have done
       it already */
    return;

#endif

  }

  if (visitor.fans.size() == 1) {
    /* only one fan: we can draw a simple polygon */

#ifdef ENABLE_OPENGL
    const ScopeVertexPointer vp(&visitor.fans.points[0]);
    look.reach_pen.Bind();
#else
    // Select the TerrainLine pen
    canvas.SelectHollowBrush();
    canvas.Select(look.reach_pen);
    canvas.SetBackgroundOpaque();
    canvas.SetBackgroundColor(COLOR_WHITE);

    // drop out extraneous line from origin
#endif

    // Draw the TerrainLine polygon

    visitor.fans.DrawOutline(canvas);

#ifdef ENABLE_OPENGL
    look.reach_pen.Unbind();
#endif
  } else {
    /* more than one fan (turning reach enabled): we have to use a
       stencil to draw the outline, because the fans may overlap */

#ifdef ENABLE_OPENGL
  const ScopeVertexPointer vp(&visitor.fans.points[0]);

  glEnable(GL_STENCIL_TEST);
  glClear(GL_STENCIL_BUFFER_BIT);

  glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

  glStencilFunc(GL_ALWAYS, 1, 1);
  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

  COLOR_WHITE.Set();
  visitor.fans.DrawFill(canvas);

  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  glStencilFunc(GL_NOTEQUAL, 1, 1);
  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

  look.reach_pen_thick.Bind();
  visitor.fans.DrawOutline(canvas);
  look.reach_pen_thick.Unbind();

  glDisable(GL_STENCIL_TEST);

#elif defined(USE_GDI) || defined(USE_MEMORY_CANVAS)

  // Get a buffer for drawing a mask
  Canvas &buffer = buffer_canvas;

  // Paint the whole buffer canvas white ( = transparent)
  buffer.ClearWhite();

  // Select the TerrainLine pen
  buffer.SelectHollowBrush();
  buffer.Select(look.reach_pen_thick);
  buffer.SetBackgroundOpaque();
  buffer.SetBackgroundColor(Color(0xf0, 0xf0, 0xf0));

  // Draw the TerrainLine polygons
  visitor.fans.DrawOutline(buffer);

  // Select a white brush (will later be transparent)
  buffer.SelectNullPen();
  buffer.SelectWhiteBrush();

  // Draw the TerrainLine polygons again to remove
  // the lines connecting all the polygons
  //
  // This removes half of the TerrainLine line width !!
  visitor.fans.DrawFill(buffer);

  // Copy everything non-white to the buffer
  canvas.CopyTransparentWhite(0, 0,
                              render_projection.GetScreenWidth(),
                              render_projection.GetScreenHeight(),
                              buffer, 0, 0);

#endif
  }
}