class DYI::Chart::LineChart

@since 0.0.0

Constants

CHART_TYPES
DEFAULT_MARKERS

@since 1.2.0

Attributes

axis_back_canvas[R]
axis_front_canvas[R]
back_canvas[R]

@since 1.3.0

chart_back_canvas[R]
chart_front_canvas[R]
chart_region[R]

@since 1.2.0

data_label_canvas[R]

@since 1.3.0

guid_front_canvas[R]

@since 1.2.0

legend_canvas[R]
scale_canvas[R]
x_scale_canvas[R]

@since 1.2.0

y_scale_canvas[R]

@since 1.2.0

Public Class Methods

new(*args) click to toggle source

@since 1.1.0

Calls superclass method DYI::Chart::Base::new
# File lib/dyi/chart/line_chart.rb, line 176
def initialize(*args)
  super
  init_container
end

Public Instance Methods

__org_chart_type__(index = nil)
Alias for: chart_type
area_chart_brush(color) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 171
def area_chart_brush(color)
  Drawing::Brush.new(:color => color)
end
back_translate_value() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 119
def back_translate_value
  {
    :dx => (Length.new_or_nil(_3d_settings[:dx]) || Length.new(24)),
    :dy => (Length.new_or_nil(_3d_settings[:dy]) || Length.new(-8))
  }
end
bar_chart_brush(color, bar_width=nil) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 162
def bar_chart_brush(color, bar_width=nil)
  if represent_3d?
    bar_width ||= chart_width * bar_width_ratio / data.records_size
    Drawing::CylinderBrush.new(:color => color, :ry => bar_width * (back_translate_value[:dy] * bar_width_ratio).quo(back_translate_value[:dx] * 2))
  else
    Drawing::Brush.new(:color => color)
  end
end
chart_height() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 150
def chart_height
  height - margin_top - margin_bottom
end
chart_type(index = nil) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 107
def chart_type(index = nil)
  if index
    (chart_types && chart_types[index]) || __org_chart_type__
  else
    __org_chart_type__
  end
end
Also aliased as: __org_chart_type__
chart_width() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 146
def chart_width
  width - margin_left - margin_right
end
dropshadow_blur_std() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 126
def dropshadow_blur_std
  dropshadow_settings[:blur_std] || 4
end
dropshadow_dx() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 130
def dropshadow_dx
  dropshadow_settings[:dx] || back_translate_value[:dx] / 2
end
dropshadow_dy() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 134
def dropshadow_dy
  dropshadow_settings[:dy] || back_translate_value[:dy] / 2
end
line_chart_pen(color) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 154
def line_chart_pen(color)
  if represent_3d?
    Drawing::CubicPen.new({:color => color, :width => line_width}.merge(s_3d_pen_options))
  else
    Drawing::Pen.new(:color => color, :width => line_width, :stroke_linecap => 'square')
  end
end
margin_bottom() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 97
def margin_bottom
  chart_margins[:bottom] || Length.new(32)
end
margin_left() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 101
def margin_left
  chart_margins[:left] || Length.new(64)
end
margin_right() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 93
def margin_right
  chart_margins[:right] || Length.new(64)
end
margin_top() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 89
def margin_top
  chart_margins[:top] || Length.new(16)
end
s_3d_pen_options() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 115
def s_3d_pen_options
  {:background_opacity => 0.3}.merge(_3d_settings)
end
use_y_second_axis?(index = nil) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 138
def use_y_second_axis?(index = nil)
  if index
    use_y_second_axises && use_y_second_axises[index]
  else
    use_y_second_axises && use_y_second_axises.any?
  end
end

Private Instance Methods

chart_color(index) click to toggle source

@since 1.0.0

# File lib/dyi/chart/line_chart.rb, line 521
def chart_color(index)
  if chart_colors
    color = chart_colors[index]
  end
  color || DEFAULT_CHART_COLOR[index % DEFAULT_CHART_COLOR.size]
end
create_vector_image() click to toggle source
Calls superclass method DYI::Chart::Base#create_vector_image
# File lib/dyi/chart/line_chart.rb, line 187
def create_vector_image
  super

  main_series_data = []
  sub_series_data = []
  @bar_series = []
  data.values_size.times do |i|
    main_series_data.push(*data.series(i)[display_range]) unless use_y_second_axis?(i)
    sub_series_data.push(*data.series(i)[display_range]) if use_y_second_axis?(i)
    @bar_series.push(i) if chart_type(i) == :bar
  end
  settings =
    moderate_axis(
      main_series_data,
      chart_height,
      axis_settings[:min],
      axis_settings[:max],
      axis_settings[:scale_count])
  sub_settings =
    moderate_sub_axis(
      sub_series_data,
      settings,
      second_axis_settings[:min],
      second_axis_settings[:max]) if use_y_second_axis?

  [:stackedbar, :bar, :area, :line].each do |chart_type|
    data.values_size.times do |i|
      if chart_type(i) == chart_type
        draw_chart(i, chart_type(i), chart_color(i), use_y_second_axis?(i) ? sub_settings : settings)
      end
    end
  end

  draw_axis(settings, sub_settings)
  texts = legend_texts # || data_columns.map{|i| data.column_title(i)}
  draw_legend(texts, legend_shapes)
end
default_legend_point() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 183
def default_legend_point
  Coordinate.new(margin_left, 0)
end
draw_area(id, color, settings) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 439
def draw_area(id, color, settings)
  values = data.series(id)
  return if values.compact.size == 0
  display_records_size = values[display_range].size
  non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
  first_index = values.each_with_index {|value, i| break i if value}
  brush = area_chart_brush(color)

  x = order_position_on_chart(margin_left, chart_width, display_records_size, first_index - non_display_size, x_axis_type)
  y = value_position_on_chart(margin_top, settings, settings[:min], true)
  if show_data_label?
    label_pen = Drawing::Pen.black_pen(:font => data_label_font)
    font_size = label_pen.font ? label_pen.font.draw_size : Font::DEFAULT_SIZE
  end
  polygone = brush.draw_polygon(@chart_front_canvas, [x, y], @chart_options) {|polygon|
    (first_index...values.size).each do |i|
      x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type)
      y = value_position_on_chart(margin_top, settings, values[i], true)
      polygon.line_to([x, y])
      if show_data_label?
        label_pen.draw_text(data_label_canvas, [x, y - font_size * 0.25], data_label_format ? values[i].strfnum(data_label_format) : values[i].to_s, :text_anchor => 'middle')
      end
    end
    y = value_position_on_chart(margin_top, settings, settings[:min], true)
    polygon.line_to([x, y])
  }
  polygone.translate(back_translate_value[:dx], back_translate_value[:dy]) if represent_3d?
end
draw_axis(settings, sub_settings) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 256
def draw_axis(settings, sub_settings)
  line_options = {:linecap => 'square'}
  line_pen = represent_3d? ? Drawing::CubicPen.new(line_options.merge(s_3d_pen_options)) : Drawing::Pen.new(line_options)
  sub_pen = represent_3d? ? Drawing::Pen.new : line_pen
  text_pen = Drawing::Pen.new(:font => axis_font)
  text_margin = axis_font && (axis_font.draw_size.quo(4)) || Font::DEFAULT_SIZE.quo(4)

  draw_y_axis(line_pen)
  draw_x_axis(line_pen, sub_pen, text_pen, text_margin)
  draw_scale(sub_pen, text_pen, settings, sub_settings, text_margin)
end
draw_bar(id, color, settings) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 468
def draw_bar(id, color, settings)
  bar_group = Shape::ShapeGroup.new(@chart_options).draw_on(@chart_front_canvas)
  values = data.series(id)
  return if values.compact.size == 0
  display_records_size = values[display_range].size
  non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
  bar_width = chart_width * bar_width_ratio / display_records_size / (@bar_series.size + (@bar_series.size - 1) * bar_seriese_interval)

  brush = bar_chart_brush(color, bar_width)

  if show_data_label?
    label_pen = Drawing::Pen.black_pen(:font => data_label_font)
    font_size = label_pen.font ? label_pen.font.draw_size : Font::DEFAULT_SIZE
  end
  values.each_with_index do |value, i|
    next if value.nil?
    x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type, bar_width_ratio) + bar_width * (1 + bar_seriese_interval) * @bar_series.index(id)
    y = value_position_on_chart(margin_top, settings, value, true)
    brush = bar_chart_brush(data.has_field?(:color) ? data.records[i].color : color, bar_width) if data.has_field?(:color)
    brush.draw_rectangle(bar_group, [x, y], bar_width, height - margin_bottom - y)
    if show_data_label?
      label_pen.draw_text(data_label_canvas, [x + bar_width * 0.5, y - font_size * 0.4], data_label_format ? value.strfnum(data_label_format) : value.to_s, :text_anchor => 'middle')
    end
  end
  bar_group.translate(back_translate_value[:dx] / 2, back_translate_value[:dy] / 2) if represent_3d?
end
draw_chart(id, chart_type, color, settings) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 396
def draw_chart(id, chart_type, color, settings)
  case chart_type
    when :line then draw_line(id, color, settings)
    when :area then draw_area(id, color, settings)
    when :bar then draw_bar(id, color, settings)
    when :stackedbar then draw_stackedbar(id, color, settings)
  end
end
draw_line(id, color, settings) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 405
def draw_line(id, color, settings)
  values = data.series(id)
  return if values.compact.size == 0
  display_records_size = values[display_range].size
  non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
  first_index = values.each_with_index {|value, i| break i if value}
  pen_options = {:color => color, :width => line_width}
  pen = line_chart_pen(color)

  x = order_position_on_chart(margin_left, chart_width, display_records_size, first_index - non_display_size, x_axis_type)
  y = value_position_on_chart(margin_top, settings, values[first_index], true)
  if show_data_label?
    label_pen = Drawing::Pen.black_pen(:font => data_label_font)
    font_size = label_pen.font ? label_pen.font.draw_size : Font::DEFAULT_SIZE
    label_pen.draw_text(data_label_canvas, [x, y - font_size * 0.25], data_label_format ? values[first_index].strfnum(data_label_format) : values[first_index].to_s, :text_anchor => 'middle')
  end
  pen.linejoin = 'bevel'
  polyline = pen.draw_polyline(@chart_front_canvas, [x, y], @chart_options) {|polyline|
               ((first_index + 1)...values.size).each do |i|
                 x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type)
                 y = value_position_on_chart(margin_top, settings, values[i], true)
                 polyline.line_to([x, y])
               end
               if show_data_label?
                 label_pen.draw_text(data_label_canvas, [x, y - font_size * 0.25], data_label_format ? values[i].strfnum(data_label_format) : values[i].to_s, :text_anchor => 'middle')
               end
             }
  if show_markers?
    marker_type = (markers && markers[id % markers.size]) || DEFAULT_MARKERS[id % DEFAULT_MARKERS.size]
    polyline.set_marker(:all, marker_type, :size => marker_size)
  end
  pen.linejoin = 'bevel'
end
draw_scale(line_pen, text_pen, settings, sub_settings, text_margin) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 282
def draw_scale(line_pen, text_pen, settings, sub_settings, text_margin)
  if settings[:min] == settings[:min_scale_value] - settings[:scale_interval]
    y = value_position_on_chart(margin_top, settings, settings[:min], true)
    if use_y_second_axis? || main_y_axis == :left
      text_pen.draw_text(
        @y_scale_canvas,
        [margin_left - text_margin, y],
        main_y_axis == :left ? settings[:min] : sub_settings[:min],
        :text_anchor=>'end',
        :format => (main_y_axis == :left ? axis_format : second_axis_format))
    end
    if use_y_second_axis? || main_y_axis == :right
      text_pen.draw_text(
        @y_scale_canvas,
        [width - margin_right + text_margin, y],
        main_y_axis == :right ? settings[:min] : sub_settings[:min],
        :format => (main_y_axis == :right ? axis_format : second_axis_format))
    end
  end

  if represent_3d? && (use_y_second_axis? || main_y_axis == :left)
    line_pen.draw_line(
      @axis_back_canvas,
      [margin_left, height - margin_bottom],
      [margin_left + back_translate_value[:dx], height - margin_bottom + back_translate_value[:dy]])
  end

  sub_axis_value = sub_settings[:min_scale_value] if use_y_second_axis?
  settings[:min_scale_value].step(settings[:max], settings[:scale_interval]) do |value|
    y = value_position_on_chart(margin_top, settings, value, true)

    if settings[:min] != value
      if represent_3d?
        if use_y_second_axis? || main_y_axis == :left
          draw_y_scale_line(
            line_pen,
            [margin_left, y],
            [margin_left + back_translate_value[:dx], y + back_translate_value[:dy]])
        end
        draw_y_scale_line(
          line_pen,
          [margin_left + back_translate_value[:dx], y + back_translate_value[:dy]],
          [width - margin_right + back_translate_value[:dx], y + back_translate_value[:dy]])
        if use_y_second_axis? || main_y_axis == :right
          draw_y_scale_line(
            line_pen,
            [width - margin_right + back_translate_value[:dx], y + back_translate_value[:dy]],
            [width - margin_right, y])
        end
      else
        line_pen.dasharray = '2,6'
        draw_y_scale_line(line_pen, [margin_left, y], [width - margin_right, y])
        line_pen.dasharray = nil
      end
    end

    if use_y_second_axis? || main_y_axis == :left
      text_pen.draw_text(
        @y_scale_canvas,
        [margin_left - text_margin, y],
        main_y_axis == :left ? value : sub_axis_value,
        :alignment_baseline=>'middle',
        :text_anchor=>'end',
        :font=>axis_font,
        :format => (main_y_axis == :left ? axis_format : second_axis_format))
    end

    if use_y_second_axis? || main_y_axis == :right
      text_pen.draw_text(
        @y_scale_canvas,
        [width - margin_right + text_margin, y],
        main_y_axis == :right ? value : sub_axis_value,
        :alignment_baseline=>'middle',
        :font=>axis_font,
        :format => (main_y_axis == :right ? axis_format : second_axis_format))
    end
    sub_axis_value += sub_settings[:scale_interval] if use_y_second_axis?
  end
end
draw_stackedbar(id, color, settings) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 495
      def draw_stackedbar(id, color, settings)
        bar_group = Shape::ShapeGroup.new(@chart_options).draw_on(@chart_front_canvas)

        values = data.series(id)
        return if values.compact.size == 0
        display_records_size = values[display_range].size
        non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : values.size)
        bar_width = chart_width * bar_width_ratio / display_records_size

        brush = bar_chart_brush(color, bar_width)

        @stacked_values ||= []

        values.each_with_index do |value, i|
          @stacked_values[i] ||= 0
          next if value.nil?
          x = order_position_on_chart(margin_left, chart_width, display_records_size, i - non_display_size, x_axis_type, bar_width_ratio)
          y = value_position_on_chart(margin_top, settings, (@stacked_values[i] += value), true)
          bar_height = value_position_on_chart(margin_top, settings, (@stacked_values[i] - value), true) - y
#          brush.color = data[:$color][i] if data[:$color]
          brush.draw_rectangle(bar_group, [x, y], bar_width,  bar_height)
        end
        bar_group.translate(back_translate_value[:dx] / 2, back_translate_value[:dy] / 2) if represent_3d?
      end
draw_x_axis(main_pen, sub_pen, text_pen, text_margin) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 371
def draw_x_axis(main_pen, sub_pen, text_pen, text_margin)
  main_pen.draw_line(represent_3d? ? @axis_back_canvas : @axis_front_canvas, [margin_left, height - margin_bottom], [width - margin_right, height - margin_bottom])
  display_records_size = data.records[display_range].size
  non_display_size = display_range.begin + (display_range.begin >= 0 ? 0 : data.records_size)

  display_records_size.times do |i|
    next unless needs_x_scale?(i, display_records_size)
    text_x = order_position_on_chart(margin_left, chart_width, display_records_size, i, x_axis_type)
    scale_x = x_axis_type == :range ? order_position_on_chart(margin_left, chart_width, display_records_size + 1, i) : text_x
    text_pen.draw_text(
      @x_scale_canvas,
      [text_x, height - margin_bottom + text_margin],
      format_x_label(data.name_values[i + non_display_size]),
      :text_anchor => 'middle', :alignment_baseline => 'top') if show_x_labels?

    if x_axis_type == :point || display_records_size <= max_x_label_count
      sub_pen.draw_line_on_direction(
        @guid_front_canvas,
        [scale_x, height - margin_bottom],
        0,
        -(axis_font ? axis_font.draw_size : Font::DEFAULT_SIZE) * 0.5) if i > 0 && i < display_records_size - (x_axis_type == :range ? 0 : 1)
    end
  end
end
draw_y_axis(pen) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 268
def draw_y_axis(pen)
  if use_y_second_axis? || main_y_axis == :left
    start_point = [margin_left, height - margin_bottom]
    end_point = [margin_left, margin_top]
    pen.draw_line(represent_3d? ? @axis_back_canvas : @axis_front_canvas, start_point, end_point)
  end

  if use_y_second_axis? || main_y_axis == :right
    start_point = [width - margin_right, height - margin_bottom]
    end_point = [width - margin_right, margin_top]
    pen.draw_line(@axis_front_canvas, start_point, end_point)
  end
end
draw_y_scale_line(pen, left_point, right_point) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 362
def draw_y_scale_line(pen, left_point, right_point)
  pen.draw_line(@scale_canvas, left_point, right_point)
end
format_x_label(obj) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 549
def format_x_label(obj)
  if x_axis_format
    if obj.kind_of?(Numeric)
      obj.strfnum(x_axis_format)
    elsif obj.respond_to?(:strftime)
      obj.strftime(x_axis_format)
    else
      obj.to_s
    end
  else
    obj.to_s
  end
end
init_container() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 225
      def init_container
#        mask = Drawing::ColorEffect::Mask.new(@canvas)
#        mask.add_shapes(Shape::Rectangle.new(Drawing::Brush.new(:color => '#FFFFFF'), [margin_left, margin_top], chart_width, chart_height))
        @back_canvas = Shape::ShapeGroup.draw_on(@canvas)
        @axis_back_canvas = Shape::ShapeGroup.draw_on(@canvas)
#        @chart_front_canvas = Shape::ShapeGroup.draw_on(@canvas, :mask => "url(##{mask.id})") unless @chart_front_canvas
        chart_clip = Drawing::Clipping.new(Shape::Rectangle.new([margin_left, margin_top], width - margin_left - margin_right, height - margin_top - margin_bottom))
        @chart_region = Shape::Rectangle.new([margin_left, margin_top], width - margin_left - margin_right, height - margin_top - margin_bottom, :painting => {:stroke_width => 0})
        @chart_region.draw_on(canvas)
        unless @chart_back_canvas
          clip_container = Shape::ShapeGroup.draw_on(@canvas)
          @chart_back_canvas = Shape::ShapeGroup.draw_on(clip_container)
          clip_container.set_clipping(chart_clip)
        end
        @scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @scale_canvas
        unless @chart_front_canvas
          clip_container = Shape::ShapeGroup.draw_on(@canvas)
          clip_container.set_clipping(chart_clip)
          @chart_front_canvas = Shape::ShapeGroup.draw_on(clip_container)
        end
        @guid_front_canvas = Shape::ShapeGroup.draw_on(@canvas)
        @guid_front_canvas.set_clipping(chart_clip)
        @data_label_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @data_label_canvas
        @x_scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @x_scale_canvas
        @y_scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @y_scale_canvas
        @axis_front_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @axis_front_canvas
        @legend_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @legend_canvas
        @chart_options = {}
#        @chart_options[:filter] = "url(##{Drawing::Filter::DropShadow.new(@canvas, dropshadow_blur_std, dropshadow_dx, dropshadow_dy).id})" if show_dropshadow?
      end
legend_shapes() click to toggle source
# File lib/dyi/chart/line_chart.rb, line 528
def legend_shapes
  result = []
  (0...data.values_size).each_with_index do |id, index|
    result <<
      case chart_type(index)
      when :line, nil
        Shape::Line.create_on_start_end(
          [legend_font_size * 0.2, legend_font_size * (1.2 * index + 0.8)],
          [legend_font_size, legend_font_size * (1.2 * index + 0.8)],
          :painting => {:stroke => chart_color(index), :stroke_width => line_width})
      when :bar, :stackedbar
        nil
      when :area
        nil
      else
        nil
      end
  end
  result
end
needs_x_scale?(i, display_records_size) click to toggle source
# File lib/dyi/chart/line_chart.rb, line 366
def needs_x_scale?(i, display_records_size)
  return true if display_records_size <= max_x_label_count
  i % ((display_records_size - 1) / [max_x_label_count - 1, 1].max) == 0
end