class TTY::Pie

Constants

FULL_CIRCLE_DEGREES
LEGEND_LEFT_SPACE
LEGEND_LINE_SPACE
POINT_SYMBOL
VERSION

Attributes

aspect_ratio[R]
center_x[R]
center_y[R]
colors[R]
cursor[R]
fill[R]
left[R]
legend[R]
radius[R]
top[R]

Public Class Methods

new(data: [], top: nil, left: nil, radius: 10, legend: {}, fill: POINT_SYMBOL, aspect_ratio: 2, colors: []) click to toggle source

Create pie chart

@example

data = [ { name: "BTC", value: 5977, fill: "*" } ]
pie_chart = TTY::Pie.new(data: data, radius: 2)

@param [Array] data

the data to display in each slice

@param [Integer] top @param [Integer] left @param [Integer] radius @param [Hash,Boolean] legend @param [String] fill @param [Float] aspect_ratio

@api public

# File lib/tty/pie.rb, line 51
def initialize(data: [], top: nil, left: nil, radius: 10,
               legend: {}, fill: POINT_SYMBOL, aspect_ratio: 2,
               colors: [])
  @data = data.dup
  @top = top
  @left = left
  @radius = radius
  @legend = legend
  @fill = Array(fill)
  @colors = Array(colors)
  @aspect_ratio = aspect_ratio
  @center_x = (left || 0) + radius * aspect_ratio
  @center_y = (top || 0) + radius

  @pastel = Pastel.new(enabled: !!colors)
  @cursor = TTY::Cursor
end

Public Instance Methods

<<(item)
Alias for: add
add(item) click to toggle source

Add a data item

@param [Hash]

@return [self]

@api public

# File lib/tty/pie.rb, line 100
def add(item)
  @data << item
  self
end
Also aliased as: <<
clear() click to toggle source

Reset data

@api public

# File lib/tty/pie.rb, line 174
def clear
  @data = []
  self
end
Also aliased as: reset
data_items() click to toggle source

Convert data into DataItems

@return [Array]

@api private

# File lib/tty/pie.rb, line 83
def data_items
  total_value = total
  @data.each_with_index.map do |item, i|
    percent = (item[:value] * 100) / total_value.to_f
    color_fill = item[:fill] || fill[i % fill.size]
    color = colors && !colors.empty? ? colors[i % colors.size] : item.fetch(:color, false)
    DataItem.new(item[:name], item[:value], percent, color, color_fill)
  end
end
render() click to toggle source

Draw a pie based on the provided data

@return [String]

@api public

# File lib/tty/pie.rb, line 121
def render
  items = data_items
  return "" if items.empty?
  angles = data_angles(items)
  output = []

  labels = items.map { |item| item.to_label(legend) }
  label_vert_space  = legend_line
  label_horiz_space = legend_left
  label_offset  = labels.size / 2
  label_boundry = label_vert_space * label_offset
  labels_range  = (-label_boundry..label_boundry).step(label_vert_space)

  (-radius..radius).each do |y|
    width = (Math.sqrt(radius * radius - y * y) * aspect_ratio).round
    width = width.zero? ? (radius / aspect_ratio).round : width

    output << " " * (center_x - width) if top.nil?
    (-width..width).each do |x|
      angle = radian_to_degree(Math.atan2(x, y))
      item = items[select_data_item(angle, angles)]
      if !top.nil?
        output << cursor.move_to(center_x + x, center_y + y)
      end
      if item.color
        output << @pastel.decorate(item.fill, item.color)
      else
        output << item.fill
      end
    end

    if legend
      if !top.nil?
        output << cursor.move_to(center_x + aspect_ratio * radius + label_horiz_space, center_y + y)
      end
      if labels_range.include?(y)
        if top.nil?
          output << " " * ((center_x - (left.to_i + width)) + label_horiz_space)
        end
        output << labels[label_offset + y / label_vert_space]
      end
    end

    output << "\n"
  end

  output.join
end
Also aliased as: to_s
reset()
Alias for: clear
to_s()
Alias for: render
total() click to toggle source

Total for the data items

@return [Integer]

@api private

# File lib/tty/pie.rb, line 74
def total
  @data.inject(0) { |sum, item| sum += item[:value]; sum }
end
update(data) click to toggle source

Replace current data with new set

@param [Array]

@api public

# File lib/tty/pie.rb, line 111
def update(data)
  @data = data
  self
end

Private Instance Methods

data_angles(items) click to toggle source

All angles from the data to slice the pie

@return [Array]

@api private

# File lib/tty/pie.rb, line 187
def data_angles(items)
  start_angle = 0
  items.reduce([]) do |acc, item|
    acc << start_angle + item.angle
    start_angle += item.angle
    acc
  end
end
legend_left() click to toggle source

The space between a legend and a chart

@return [Integer]

@api private

# File lib/tty/pie.rb, line 201
def legend_left
  legend ? legend.fetch(:left, LEGEND_LEFT_SPACE) : LEGEND_LEFT_SPACE
end
legend_line() click to toggle source

The space between each legend item

@return [Integer]

@api private

# File lib/tty/pie.rb, line 210
def legend_line
  (legend ? legend.fetch(:line, LEGEND_LINE_SPACE) : LEGEND_LINE_SPACE) + 1
end
radian_to_degree(radians) click to toggle source

Convert radians to degrees

@param [Float] radians

@return [Float]

@api private

# File lib/tty/pie.rb, line 230
def radian_to_degree(radians)
  radians * 180 / Math::PI
end
select_data_item(angle, angles) click to toggle source

Select data item index based on angle

@return [Integer]

@api private

# File lib/tty/pie.rb, line 219
def select_data_item(angle, angles)
  angles.index { |a| (FULL_CIRCLE_DEGREES / 2 - angle) < a }
end