class Kovid::AsciiCharts::Chart

Constants

DEFAULT_MAX_Y_VALS
DEFAULT_MIN_Y_VALS
STEPS

Attributes

data[R]
options[R]

Public Class Methods

new(data, options = {}) click to toggle source

data is a sorted array of [x, y] pairs

# File lib/kovid/ascii_charts.rb, line 35
def initialize(data, options = {})
  @data = data
  @options = options
end

Public Instance Methods

all_ints() click to toggle source
# File lib/kovid/ascii_charts.rb, line 153
def all_ints
  scan_data unless defined? @all_ints
  @all_ints
end
draw() click to toggle source
# File lib/kovid/ascii_charts.rb, line 216
def draw
  lines.join("\n")
end
from_step(val) click to toggle source
# File lib/kovid/ascii_charts.rb, line 83
def from_step(val)
  if val == 0
    [0, 0]
  else
    order = Math.log10(val).floor.to_i
    num = (val / (10**order))
    [num, order]
  end
end
lines() click to toggle source
# File lib/kovid/ascii_charts.rb, line 212
def lines
  raise 'lines must be overridden'
end
max_xval_width() click to toggle source
# File lib/kovid/ascii_charts.rb, line 176
def max_xval_width
  scan_data unless defined? @max_xval_width
  @max_xval_width
end
max_yval() click to toggle source
# File lib/kovid/ascii_charts.rb, line 143
def max_yval
  scan_data unless defined? @max_yval
  @max_yval
end
max_yval_width() click to toggle source
# File lib/kovid/ascii_charts.rb, line 181
def max_yval_width
  scan_y_range unless defined? @max_yval_width
  @max_yval_width
end
min_yval() click to toggle source
# File lib/kovid/ascii_charts.rb, line 148
def min_yval
  scan_data unless defined? @min_yval
  @min_yval
end
nearest_step(val) click to toggle source
# File lib/kovid/ascii_charts.rb, line 102
def nearest_step(val)
  num, order = from_step(val)
  to_step(2, order) # #@@
end
next_step_down(val) click to toggle source
# File lib/kovid/ascii_charts.rb, line 117
def next_step_down(val)
  num, order = from_step(val)
  next_index = STEPS.index(num.to_i) - 1
  if next_index == -1
    STEPS.size - 1
    order -= 1
  end
  to_step(STEPS[next_index], order)
end
next_step_up(val) click to toggle source
# File lib/kovid/ascii_charts.rb, line 107
def next_step_up(val)
  num, order = from_step(val)
  next_index = STEPS.index(num.to_i) + 1
  if STEPS.size == next_index
    next_index = 0
    order += 1
  end
  to_step(STEPS[next_index], order)
end
round_value(val) click to toggle source

round to nearest step size, making sure we curtail precision to same order of magnitude as the step size to avoid 0.4 + 0.2 = 0.6000000000000001

# File lib/kovid/ascii_charts.rb, line 128
def round_value(val)
  remainder = val % step_size
  unprecised = if (remainder * 2) >= step_size
                 (val - remainder) + step_size
               else
                 val - remainder
                end
  if step_size < 1
    precision = -Math.log10(step_size).floor
    (unprecised * (10**precision)).to_i.to_f / (10**precision)
  else
    unprecised
  end
end
rounded_data() click to toggle source
# File lib/kovid/ascii_charts.rb, line 40
def rounded_data
  @rounded_data ||= data.map { |pair| [pair[0], round_value(pair[1])] }
end
scan_data() click to toggle source
# File lib/kovid/ascii_charts.rb, line 158
def scan_data
  @max_yval = 0
  @min_yval = 0
  @all_ints = true

  @max_xval_width = 1

  data.each do |pair|
    @max_yval = pair[1] if pair[1] > @max_yval
    @min_yval = pair[1] if pair[1] < @min_yval
    @all_ints = false if @all_ints && !pair[1].is_a?(Integer)

    if (xw = pair[0].to_s.length) > @max_xval_width
      @max_xval_width = xw
    end
  end
end
scan_y_range() click to toggle source
# File lib/kovid/ascii_charts.rb, line 186
def scan_y_range
  @max_yval_width = 1

  y_range.each do |yval|
    if (yw = yval.to_s.length) > @max_yval_width
      @max_yval_width = yw
    end
  end
end
step_size() click to toggle source
# File lib/kovid/ascii_charts.rb, line 44
def step_size
  unless defined? @step_size
    if options[:y_step_size]
      @step_size = options[:y_step_size]
    else
      max_y_vals = options[:max_y_vals] || DEFAULT_MAX_Y_VALS
      min_y_vals = options[:max_y_vals] || DEFAULT_MIN_Y_VALS
      y_span = (max_yval - min_yval).to_f

      step_size = nearest_step(y_span.to_f / (data.size + 1))

      if @all_ints && (step_size < 1)
        step_size = 1
      else
        while (y_span / step_size) < min_y_vals
          candidate_step_size = next_step_down(step_size)
          if @all_ints && (candidate_step_size < 1)
            break
            end ## don't go below one

          step_size = candidate_step_size
        end
      end

      # go up if we undershot, or were never over
      while (y_span / step_size) > max_y_vals
        step_size = next_step_up(step_size)
      end
      @step_size = step_size
    end
    if !@all_ints && @step_size.is_a?(Integer)
      @step_size = @step_size.to_f
    end
  end
  @step_size
end
to_step(num, order) click to toggle source
# File lib/kovid/ascii_charts.rb, line 93
def to_step(num, order)
  s = num * (10**order)
  if order < 0
    s.to_f
  else
    s
  end
end
to_string() click to toggle source
# File lib/kovid/ascii_charts.rb, line 220
def to_string
  draw
end
y_range() click to toggle source
# File lib/kovid/ascii_charts.rb, line 196
def y_range
  unless defined? @y_range
    @y_range = []
    first_y = round_value(min_yval)
    first_y -= step_size if first_y > min_yval
    last_y = round_value(max_yval)
    last_y += step_size if last_y < max_yval
    current_y = first_y
    while current_y <= last_y
      @y_range << current_y
      current_y = round_value(current_y + step_size) ## to avoid fp arithmetic oddness
    end
  end
  @y_range
end