Class: RubyTUI::Widgets::Chart

Inherits:
Object
  • Object
show all
Includes:
RubyTUI::Widget
Defined in:
lib/rubytui/widgets/chart.rb

Overview

Chart renders line charts using Canvas (Braille) for high-resolution plotting. Supports multiple datasets, labeled axes, and automatic scaling.

Examples:

ds = RubyTUI::Widgets::Dataset.new(name: "load", data: [[0, 1], [1, 3], [2, 2]])
chart = RubyTUI::Widgets::Chart.new(datasets: [ds], x_axis: ["t", 0, 2], y_axis: ["y", 0, 4])
frame.render_widget(chart, area)

See Also:

Instance Method Summary collapse

Constructor Details

#initialize(datasets: [], x_axis: nil, y_axis: nil, style: Style::DEFAULT, axis_style: nil, label_style: nil, block: nil) ⇒ Chart

Returns a new instance of Chart.

Parameters:

  • datasets (Array<Dataset>) (defaults to: []) —

    series to plot; datasets with fewer than 2 points are skipped (default: [])

  • x_axis (Array(String, Numeric, Numeric), nil) (defaults to: nil) —

    [label, min, max]; +min+/+max+ may be nil for bounds computed from the data. When set, one row is reserved for the axis and the label is drawn centered on it (default: nil, bounds computed from the data and no row reserved)

  • y_axis (Array(String, Numeric, Numeric), nil) (defaults to: nil) —

    [label, min, max]; +min+/+max+ may be nil for bounds computed from the data. When set, the min/max values are drawn left of a vertical axis line and the label's first character at mid-height (default: nil, no y axis; bounds computed from the data)

  • style (Style) (defaults to: Style::DEFAULT) —

    currently unused by rendering (default: Style::DEFAULT)

  • axis_style (Style, nil) (defaults to: nil) —

    axis line style (default: bright black foreground)

  • label_style (Style, nil) (defaults to: nil) —

    style of axis labels and min/max values (default: white foreground)

  • block (Widgets::Block, nil) (defaults to: nil) —

    optional wrapping block



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/rubytui/widgets/chart.rb', line 44

def initialize(
  datasets: [],
  x_axis: nil,        # [label, min, max] or nil for auto
  y_axis: nil,        # [label, min, max] or nil for auto
  style: Style::DEFAULT,
  axis_style: nil,
  label_style: nil,
  block: nil
)
  @datasets = datasets
  @x_axis = x_axis
  @y_axis = y_axis
  @style = style
  @axis_style = axis_style || Style.new.fg(Color::BRIGHT_BLACK)
  @label_style = label_style || Style.new.fg(Color::WHITE)
  @block = block
end

Instance Method Details

#render(area, buf) ⇒ void

This method returns an undefined value.

Render the axes and draw each dataset as connected line segments on a RubyTUI::Widgets::Canvas.

Nothing is drawn, not even block, when there are no datasets.

Parameters:

  • area (Rect) —

    the rectangular area to render into

  • buf (Buffer) —

    the buffer to write cells into



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rubytui/widgets/chart.rb', line 69

def render(area, buf)
  return if area.empty? || @datasets.empty?

  render_area = area
  if @block
    @block.render(area, buf)
    render_area = @block.inner(area)
  end

  return if render_area.empty?

  # Reserve space for axes
  y_label_width = compute_y_label_width
  x_label_height = @x_axis ? 1 : 0
  y_label_area_width = y_label_width > 0 ? y_label_width + 1 : 0

  chart_area = Rect.new(
    x: render_area.x + y_label_area_width,
    y: render_area.y,
    width: [render_area.width - y_label_area_width, 0].max,
    height: [render_area.height - x_label_height, 0].max
  )

  return if chart_area.empty?

  # Compute data bounds
  x_min, x_max = compute_x_bounds
  y_min, y_max = compute_y_bounds

  # Draw axes
  render_y_axis(render_area, chart_area, buf, y_min, y_max)
  render_x_axis(render_area, chart_area, buf, x_min, x_max)

  # Draw data using Canvas
  canvas = Canvas.new(
    x_bounds: [x_min, x_max],
    y_bounds: [y_min, y_max]
  )

  @datasets.each do |ds|
    points = ds.data
    next if points.length < 2

    # Draw lines between consecutive points
    points.each_cons(2) do |(x1, y1), (x2, y2)|
      canvas.line(x1, y1, x2, y2, style: ds.style)
    end
  end

  canvas.render(chart_area, buf)
end