chromium/ash/hud_display/graph.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_HUD_DISPLAY_GRAPH_H_
#define ASH_HUD_DISPLAY_GRAPH_H_

#include <vector>

#include "ash/hud_display/hud_constants.h"
#include "base/containers/ring_buffer.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"

struct SkPoint;

namespace gfx {
class Canvas;
}

namespace ash {
namespace hud_display {

class Graph {
 public:
  // Graph screen size (that is used during layout) should match (ring buffer
  // size - 1) to prevent scaling, because RingBuffer always keeps one element
  // unused.
  using Data = base::RingBuffer<float, kHUDGraphWidth + 1>;

  enum class Baseline {
    kBaselineBottom,  // Positive values will be drawn from the bottom border
                      // up.
    kBaselineTop,     // Positive values will be drawn from the top border down.
  };

  // Whether to draw the graph as a filled polygon.
  enum class Fill {
    kNone,
    kSolid,
  };

  enum class Style {
    kLines,
    kSkyline,
  };

  // |max_data_points| must be less than the ring buffer size.
  Graph(size_t max_data_points,
        Baseline baseline,
        Fill fill,
        Style style,
        SkColor color);
  ~Graph();

  Graph(const Graph&) = delete;
  Graph& operator=(const Graph&) = delete;

  // |value| must be normalized to [0,1]. When graphs are drawn stacked,
  // the full stack must be normalized.
  // |unscaled_value| is used to label graph values to the user.
  void AddValue(float value, float unscaled_value);
  void Layout(const gfx::Rect& graph_bounds, const Graph* base);
  void Draw(gfx::Canvas* canvas) const;
  void UpdateLastValue(float value, float unscaled_value);

  const std::vector<SkPoint>& top_path() const { return top_path_; }

  // Returns number of data points displayed on the graph.
  size_t max_data_points() const { return max_data_points_; }

  SkColor color() const { return color_; }

  // Returns value from |unscaled_data_|.
  // |index| is always interpreted as "negative", i.e. "0" - current data, "1"
  // - previous graph data, 2 - two steps "ago". I.e. it's number of graph
  // points from the right graph edge.
  float GetUnscaledValueAt(size_t index) const;

  // Returns true if |data_| is populated at the given index.
  // |index| is always interpreted as "negative", i.e. "0" - current data, "1"
  // - previous graph data, 2 - two steps ago. I.e. it's number of graph
  // points from the right graph edge.
  bool IsFilledIndex(size_t index) const;

  // Reset the data.
  void Reset();

#if !defined(NDEBUG)
  // Returns string representation os this object for debug.
  std::string DebugDump(const std::string& name) const;
#endif

 private:
  const Baseline baseline_;
  const Fill fill_;
  const Style style_;
  const SkColor color_;

  // Result of last Layout() call.
  gfx::Rect graph_bounds_;

  // Paths are measured from the top left corner.
  // Partial graph is assumed to be right-justified.
  // For kBaselineBottom |top_path_| has y values that are less than
  // |bottom_path_|. (And opposite for the kBaselineTop.)
  // Paths are calculated by Layout() from the |data_|.
  std::vector<SkPoint> top_path_;
  std::vector<SkPoint> bottom_path_;
  // Bottom path style should follow base graph style.
  Style bottom_path_style_ = Style::kLines;

  Data data_;
  Data unscaled_data_;
  size_t max_data_points_ = 0;
};

}  // namespace hud_display
}  // namespace ash

#endif  // ASH_HUD_DISPLAY_GRAPH_H_