Class: Gruff::Line

Inherits:
Base
  • Object
show all
Defined in:
lib/gruff/line.rb

Overview

Here's how to make a Gruff::Line.

g = Gruff::Line.new
g.title = "A Line Graph"
g.data 'Fries', [20, 23, 19, 8]
g.data 'Hamburgers', [50, 19, 99, 29]
g.write("line.png")

There are also other options described below, such as #baseline_value, #baseline_color, #hide_dots, and #hide_lines.

Constant Summary

Constants inherited from Base

Base::DEFAULT_MARGIN, Base::DEFAULT_TARGET_WIDTH, Base::LABEL_MARGIN, Base::LEGEND_MARGIN

Instance Attribute Summary collapse

Attributes inherited from Base

#bold_title, #bottom_margin, #center_labels_over_point, #colors, #font, #font_color, #has_left_labels, #hide_legend, #hide_line_markers, #hide_line_numbers, #hide_title, #label_max_size, #label_stagger_height, #label_truncation_style, #labels, #left_margin, #legend_at_bottom, #legend_box_size, #legend_font_size, #legend_margin, #marker_color, #marker_count, #marker_font_size, #marker_shadow_color, #maximum_value, #minimum_value, #no_data_message, #right_margin, #sort, #sorted_drawing, #title, #title_font, #title_font_size, #title_margin, #top_margin, #use_data_label, #x_axis_increment, #x_axis_label, #y_axis_increment, #y_axis_label

Instance Method Summary collapse

Methods inherited from Base

#add_color, #data, #margins=, #replace_colors, #theme=, #theme_37signals, #theme_greyscale, #theme_keynote, #theme_odeo, #theme_pastel, #theme_rails_keynote, #to_blob, #write

Constructor Details

#initialize(*args) ⇒ Line

Call with target pixel width of graph (800, 400, 300), and/or false to omit lines (points only).

g = Gruff::Line.new(400) # 400px wide with lines
g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)

The preferred way is to call #hide_dots or #hide_lines instead.

Raises:

  • (ArgumentError)

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/gruff/line.rb', line 73

def initialize(*args)
  raise ArgumentError, 'Wrong number of arguments' if args.length > 2

  if args.empty? || (!args.first.is_a?(Numeric) && !args.first.is_a?(String))
    super()
  else
    super args.shift
  end

  @reference_lines = {}
  @reference_line_default_color = 'red'
  @reference_line_default_width = 5

  @hide_dots = @hide_lines = false
  @maximum_x_value = nil
  @minimum_x_value = nil

  @dot_style = 'circle'

  @show_vertical_markers = false

  @store = Gruff::Store.new(Gruff::Store::XYData)
end

Instance Attribute Details

#dot_radiusObject

Returns the value of attribute dot_radius


28
29
30
# File 'lib/gruff/line.rb', line 28

def dot_radius
  @dot_radius
end

#dot_styleObject

default is 'circle', other options include square.


31
32
33
# File 'lib/gruff/line.rb', line 31

def dot_style
  @dot_style
end

#hide_dotsObject

Hide parts of the graph to fit more datapoints, or for a different appearance.


34
35
36
# File 'lib/gruff/line.rb', line 34

def hide_dots
  @hide_dots
end

#hide_linesObject

Hide parts of the graph to fit more datapoints, or for a different appearance.


34
35
36
# File 'lib/gruff/line.rb', line 34

def hide_lines
  @hide_lines
end

#line_widthObject

Dimensions of lines and dots; calculated based on dataset size if left unspecified.


27
28
29
# File 'lib/gruff/line.rb', line 27

def line_width
  @line_width
end

#maximum_x_valueObject

accessors for support of xy data.


40
41
42
# File 'lib/gruff/line.rb', line 40

def maximum_x_value
  @maximum_x_value
end

#minimum_x_valueObject

accessors for support of xy data.


37
38
39
# File 'lib/gruff/line.rb', line 37

def minimum_x_value
  @minimum_x_value
end

#reference_line_default_colorObject

Returns the value of attribute reference_line_default_color


20
21
22
# File 'lib/gruff/line.rb', line 20

def reference_line_default_color
  @reference_line_default_color
end

#reference_line_default_widthObject

Returns the value of attribute reference_line_default_width


21
22
23
# File 'lib/gruff/line.rb', line 21

def reference_line_default_width
  @reference_line_default_width
end

#reference_linesObject

Allow for reference lines ( which are like baseline … just allowing for more & on both axes ).


19
20
21
# File 'lib/gruff/line.rb', line 19

def reference_lines
  @reference_lines
end

#show_vertical_markersObject

Allow for vertical marker lines.


24
25
26
# File 'lib/gruff/line.rb', line 24

def show_vertical_markers
  @show_vertical_markers
end

Instance Method Details

#baseline_colorObject


55
56
57
58
59
# File 'lib/gruff/line.rb', line 55

def baseline_color
  if @reference_lines.key?(:baseline)
    @reference_lines[:baseline][:color]
  end
end

#baseline_color=(new_value) ⇒ Object


61
62
63
64
# File 'lib/gruff/line.rb', line 61

def baseline_color=(new_value)
  @reference_lines[:baseline] ||= {}
  @reference_lines[:baseline][:color] = new_value
end

#baseline_valueObject

Get the value if somebody has defined it.


43
44
45
46
47
# File 'lib/gruff/line.rb', line 43

def baseline_value
  if @reference_lines.key?(:baseline)
    @reference_lines[:baseline][:value]
  end
end

#baseline_value=(new_value) ⇒ Object

Set a value for a baseline reference line..


50
51
52
53
# File 'lib/gruff/line.rb', line 50

def baseline_value=(new_value)
  @reference_lines[:baseline] ||= {}
  @reference_lines[:baseline][:value] = new_value
end

#dataxy(name, x_data_points = [], y_data_points = [], color = nil) ⇒ Object #dataxy(name, xy_data_points = [], color = nil) ⇒ Object

Note:
  • if (x_data_points.length != y_data_points.length) an error is returned.

  • if the color argument is nil, the next color from the default theme will be used.

  • if you want to use a preset theme, you must set it before calling #dataxy.

This method allows one to plot a dataset with both X and Y data.

Examples:

g = Gruff::Line.new
g.title = "X/Y Dataset"
g.dataxy("Apples", [1,3,4,5,6,10], [1, 2, 3, 4, 4, 3])
g.dataxy("Bapples", [1,3,4,5,7,9], [1, 1, 2, 2, 3, 3])
g.dataxy("Capples", [[1,1],[2,3],[3,4],[4,5],[5,7],[6,9]])

# you can still use the old data method too if you want:
g.data("Capples", [1, 1, 2, 2, 3, 3])

# labels will be drawn at the x locations of the keys passed in.
In this example the lables are drawn at x positions 2, 4, and 6:
g.labels = {0 => '2003', 2 => '2004', 4 => '2005', 6 => '2006'}
# The 0 => '2003' label will be ignored since it is outside the chart range.

Overloads:

  • #dataxy(name, x_data_points = [], y_data_points = [], color = nil) ⇒ Object

    Parameters:

    • name (String)

      the title of the dataset.

    • x_data_points (Array) (defaults to: [])

      an array containing the x data points for the graph.

    • y_data_points (Array) (defaults to: [])

      an array containing the y data points for the graph.

    • color (String) (defaults to: nil)

      hex number indicating the line color as an RGB triplet.

  • #dataxy(name, xy_data_points = [], color = nil) ⇒ Object

    Parameters:

    • name (String)

      the title of the dataset.

    • xy_data_points (Array) (defaults to: [])

      an array containing both x and y data points for the graph.

    • color (String) (defaults to: nil)

      hex number indicating the line color as an RGB triplet.

Raises:

  • (ArgumentError)

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/gruff/line.rb', line 131

def dataxy(name, x_data_points = [], y_data_points = [], color = nil)
  # make sure it's an array
  x_data_points = Array(x_data_points)
  y_data_points = Array(y_data_points)

  raise ArgumentError, 'x_data_points is nil!' if x_data_points.empty?

  if x_data_points.all? { |p| p.is_a?(Array) && p.size == 2 }
    x_data_points, y_data_points = x_data_points.transpose
  end

  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length

  # call the existing data routine for the x/y data.
  store.add(name, y_data_points, color, x_data_points)
end

#drawObject


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/gruff/line.rb', line 166

def draw
  super

  return unless data_given?

  # Check to see if more than one datapoint was given. NaN can result otherwise.
  @x_increment = (column_count > 1) ? (@graph_width / (column_count - 1).to_f) : @graph_width

  @reference_lines.each_value do |curr_reference_line|
    draw_horizontal_reference_line(curr_reference_line) if curr_reference_line.key?(:norm_value)
    draw_vertical_reference_line(curr_reference_line) if curr_reference_line.key?(:index)
  end

  if @show_vertical_markers
    (0..column_count).each do |column|
      x = @graph_left + @graph_width - column.to_f * @x_increment

      Gruff::Renderer::Line.new(color: @marker_color).render(x, @graph_bottom, x, @graph_top)
      #If the user specified a marker shadow color, draw a shadow just below it
      if @marker_shadow_color
        Gruff::Renderer::Line.new(color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_top)
      end
    end
  end

  store.norm_data.each do |data_row|
    prev_x = prev_y = nil

    one_point = contains_one_point_only?(data_row)

    data_row.coordinates.each_with_index do |(x_data, y_data), index|
      if x_data.nil?
        #use the old method: equally spaced points along the x-axis
        new_x = @graph_left + (@x_increment * index)
        draw_label(new_x, index)
      else
        new_x = get_x_coord(x_data, @graph_width, @graph_left)
        @labels.each do |label_pos, _|
          draw_label(@graph_left + ((label_pos - @minimum_x_value) * @graph_width) / (@maximum_x_value - @minimum_x_value), label_pos)
        end
      end
      unless y_data # we can't draw a line for a null data point, we can still label the axis though
        prev_x = prev_y = nil
        next
      end

      new_y = @graph_top + (@graph_height - y_data * @graph_height)

      # Reset each time to avoid thin-line errors
      stroke_width  = line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
      circle_radius = dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 2.5), 5.0)

      if !@hide_lines && prev_x && prev_y
        Gruff::Renderer::Line.new(color: data_row.color, width: stroke_width)
                             .render(prev_x, prev_y, new_x, new_y)
      end

      if one_point || !@hide_dots
        Gruff::Renderer::Dot.new(@dot_style, color: data_row.color, width: stroke_width).render(new_x, new_y, circle_radius)
      end

      prev_x = new_x
      prev_y = new_y
    end
  end

  Gruff::Renderer.finish
end

#draw_horizontal_reference_line(reference_line) ⇒ Object


156
157
158
159
# File 'lib/gruff/line.rb', line 156

def draw_horizontal_reference_line(reference_line)
  level = @graph_top + (@graph_height - reference_line[:norm_value] * @graph_height)
  draw_reference_line(reference_line, @graph_left, @graph_left + @graph_width, level, level)
end

#draw_reference_line(reference_line, left, right, top, bottom) ⇒ Object


148
149
150
151
152
153
154
# File 'lib/gruff/line.rb', line 148

def draw_reference_line(reference_line, left, right, top, bottom)
  config = {
    color: reference_line[:color] || @reference_line_default_color,
    width: reference_line[:width] || @reference_line_default_width
  }
  Gruff::Renderer::DashLine.new(config).render(left, top, right, bottom)
end

#draw_vertical_reference_line(reference_line) ⇒ Object


161
162
163
164
# File 'lib/gruff/line.rb', line 161

def draw_vertical_reference_line(reference_line)
  index = @graph_left + (@x_increment * reference_line[:index])
  draw_reference_line(reference_line, index, index, @graph_top, @graph_top + @graph_height)
end