class Charma::Chart

Public Class Methods

new(opts) click to toggle source
# File lib/charma/chart.rb, line 5
def initialize(opts)
  @opts = opts
end

Public Instance Methods

abs_x_positoin(v, rc, xrange) click to toggle source
# File lib/charma/chart.rb, line 57
def abs_x_positoin(v, rc, xrange)
  rx, min, max = [ v, *xrange ].map{ |e| scale_value(:x, e) }
  (rx-min) * rc.w / (max-min) + rc.x
end
abs_y_positoin(v, rc, yrange) click to toggle source
# File lib/charma/chart.rb, line 62
def abs_y_positoin(v, rc, yrange)
  ry, min, max = [ v, *yrange ].map{ |e| scale_value(:y, e) }
  (ry-min) * rc.h / (max-min) + rc.bottom
end
bottom_legend?() click to toggle source
# File lib/charma/chart.rb, line 21
def bottom_legend?
  has_legend?
end
colors(n) click to toggle source
# File lib/charma/chart.rb, line 87
def colors(n)
  case n
  when 0, 1
    return ["666666"]
  else
    f = ->(t0){
      t = t0 % 3
      v = case t
      when 0..1 then t
      when 1..2 then 2-t
      else 0
      end
      "%02x" % (v**0.5*255).round
    }
    Array.new(n){ |i|
      t = i*3.0/n+0.5
      [f[t],f[t+1],f[t+2]].join
    }
  end
end
draw_samesize_texts( pdf, rects, texts, opts={} ) click to toggle source
# File lib/charma/chart.rb, line 108
def draw_samesize_texts( pdf, rects, texts, opts={} )
  pdf.save_graphics_state do
    size = texts.zip(rects).map{ |txt,rc|
      w = pdf.width_of(txt, size:1)
      h = pdf.height_of(txt, size:1)
      [rc.w.to_f/w ,rc.h.to_f/h].min
    }.min
    texts.zip(rects).each do |txt, rc|
      draw_text( pdf, rc, txt, size:size, **opts )
    end
  end
end
draw_text( pdf, rect, text, opts = {} ) click to toggle source
# File lib/charma/chart.rb, line 76
def draw_text( pdf, rect, text, opts = {} )
  pdf.text_box( text,
    at:rect.topleft,
    width:rect.w,
    height:rect.h,
    align: (opts[:align] || :center),
    valign: (opts[:valign] || :center),
    size: (opts[:size] || rect.h),
    overflow: :shrink_to_fit )
end
fill_rect( pdf, rect, col ) click to toggle source
# File lib/charma/chart.rb, line 67
def fill_rect( pdf, rect, col )
  pdf.save_graphics_state do
    pdf.fill{
      pdf.fill_color( col )
      pdf.rectangle( [rect.x, rect.y], rect.w, rect.h )
    }
  end
end
has_legend?() click to toggle source
# File lib/charma/chart.rb, line 25
def has_legend?
  @opts[:series].any?{ |s| ! s[:name].nil? }
end
render_legend( pdf, rect ) click to toggle source
# File lib/charma/chart.rb, line 131
def render_legend( pdf, rect )
  names = @opts[:series].map.with_index{ |e,ix| e[:name] || "series #{ix}" }
  rects = rect.hsplit( *([1]*names.size) )
  name_areas, bar_areas = rects.map{ |rc| rc.hsplit(1,1) }.transpose
  draw_samesize_texts( pdf, name_areas, names, align: :right )
  cols = colors(names.size)
  bar_areas.zip(cols).each do |rc0, col|
    _, rc1, = rc0.vsplit(1,1,1)
    rc, = rc1.hsplit(2,1)
    fill_rect( pdf, rc, col )
  end
end
render_rottext( pdf, rect, text ) click to toggle source
# File lib/charma/chart.rb, line 121
def render_rottext( pdf, rect, text )
  pdf.rotate(90, origin: rect.center) do
    rc = rect.rot90
    w = pdf.width_of(text, size:1)
    h = pdf.height_of(text, size:1)
    size = [rc.w.to_f/w ,rc.h.to_f/h].min
    pdf.draw_text( text, size:size, at:rc.bottomleft )
  end
end
render_y_grid(pdf, area, yrange, yvalues) click to toggle source
# File lib/charma/chart.rb, line 170
def render_y_grid(pdf, area, yrange, yvalues)
  pdf.save_graphics_state do
    pdf.line_width = 0.5
    yvalues.each do |v|
      if v==0
        pdf.stroke_color "000000"
        pdf.undash
      else
        pdf.stroke_color "888888"
        pdf.dash([2,2])
      end
      abs_y = abs_y_positoin( v, area, yrange )
      pdf.stroke_horizontal_line area.x, area.right, at: abs_y
    end
  end
end
render_yticks(pdf, area, yrange, yvalues) click to toggle source
# File lib/charma/chart.rb, line 160
def render_yticks(pdf, area, yrange, yvalues)
  h = (area.h / yvalues.size) * 0.7
  rects = yvalues.map{ |v|
    abs_y = abs_y_positoin( v, area, yrange )
    Rect.new( area.x, abs_y + h/2, area.w*0.9, h )
  }
  svalues = yvalues.map{ |v| "%g " % v }
  draw_samesize_texts( pdf, rects, svalues, align: :right )
end
scale_type(sym) click to toggle source
# File lib/charma/chart.rb, line 29
def scale_type(sym)
  key = :"#{sym}_scale"
  case @opts[key]
  when :log10
    :log10
  else
    :linear
  end
end
scale_value(axis, v) click to toggle source
# File lib/charma/chart.rb, line 39
def scale_value(axis, v)
  case scale_type(axis)
  when :log10
    Math.log10(v)
  else
    v
  end
end
stroke_rect( pdf, rect ) click to toggle source
# File lib/charma/chart.rb, line 9
def stroke_rect( pdf, rect )
  pdf.stroke{
    pdf.rectangle( [rect.x, rect.y], rect.w, rect.h )
  }
end
tick_unit(v) click to toggle source
# File lib/charma/chart.rb, line 144
def tick_unit(v)
  base = (10**Math.log10(v).round).to_f
  man = v/base
  return 0.5*base if man<0.6
  return base if man<1.2
  base*2
end
tick_values(axis, range) click to toggle source
# File lib/charma/chart.rb, line 152
def tick_values(axis, range)
  min, max = range.minmax.map{ |e| scale_value( axis, e ) }
  unit = tick_unit((max - min) * 0.1)
  i_low = (min / unit).ceil
  i_hi = (max / unit).floor
  (i_low..i_hi).map{ |i| unscale_value( axis, i*unit ) }
end
unscale_value(axis, v) click to toggle source
# File lib/charma/chart.rb, line 48
def unscale_value(axis, v)
  case scale_type(axis)
  when :log10
    10.0**v
  else
    v
  end
end
values(sym) click to toggle source
# File lib/charma/chart.rb, line 15
def values(sym)
  @opts[:series].map{ |s|
    s[sym].map(&:to_f)
  }
end