lib/sparklines.rb in sparklines-0.4.6 vs lib/sparklines.rb in sparklines-0.4.7
- old
+ new
@@ -72,19 +72,19 @@
Licensed under the MIT license.
=end
class Sparklines
- VERSION = '0.4.6'
+ VERSION = '0.4.7'
@@label_margin = 5.0
@@pointsize = 10.0
class << self
- # Does the actual plotting of the graph.
- # Calls the appropriate subclass based on the :type argument.
+ # Does the actual plotting of the graph.
+ # Calls the appropriate subclass based on the :type argument.
# Defaults to 'smooth'
def plot(data=[], options={})
defaults = {
:type => 'smooth',
:height => 14,
@@ -97,30 +97,30 @@
:below_color => 'grey',
:background_color => 'white',
:share_color => 'red',
:remain_color => 'lightgrey',
:min_color => 'blue',
- :max_color => 'green',
- :last_color => 'red',
+ :max_color => 'green',
+ :last_color => 'red',
:std_dev_color => '#efefef',
-
+
:has_min => false,
:has_max => false,
:has_last => false,
:has_std_dev => false,
-
+
:label => nil
}
# HACK for HashWithIndifferentAccess
options_sym = Hash.new
options.keys.each do |key|
options_sym[key.to_sym] = options[key]
end
options_sym = defaults.merge(options_sym)
-
+
# Call the appropriate method for actual plotting.
sparkline = self.new(data, options_sym)
if %w(area bar pie smooth discrete whisker).include? options_sym[:type]
sparkline.send options_sym[:type]
else
@@ -145,37 +145,37 @@
##
# Creates a continuous area sparkline. Relevant options.
#
# :step - An integer that determines the distance between each point on the sparkline. Defaults to 2.
- #
+ #
# :height - An integer that determines what the height of the sparkline will be. Defaults to 14
- #
+ #
# :upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50.
- #
+ #
# :has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false.
- #
+ #
# :has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false.
- #
+ #
# :has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false.
- #
+ #
# :min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue.
- #
+ #
# :max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green.
- #
+ #
# :last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red.
- #
+ #
# :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red.
- #
+ #
# :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray.
def area
step = @options[:step].to_f
height = @options[:height].to_f
background_color = @options[:background_color]
-
+
create_canvas((@norm_data.size - 1) * step + 4, height, background_color)
upper = @options[:upper].to_f
has_min = @options[:has_min]
@@ -190,12 +190,12 @@
coords = [[0,(height - 3 - upper/(101.0/(height-4)))]]
i=0
@norm_data.each do |r|
- coords.push [(2 + i), (height - 3 - r/(101.0/(height-4)))]
- i += step
+ coords.push [(2 + i), (height - 3 - r/(101.0/(height-4)))]
+ i += step
end
coords.push [(@norm_data.size - 1) * step + 4, (height - 3 - upper/(101.0/(height-4)))]
# TODO Refactor! Should take a block and do both.
#
@@ -223,14 +223,14 @@
# so a max dot can be displayed
@draw.define_clip_path('all') do
@draw.rectangle(0,0,@canvas.columns,@canvas.rows)
end
@draw.clip_path('all')
-
+
drawbox(coords[@norm_data.index(@norm_data.min)+1], 1, min_color) if has_min == true
drawbox(coords[@norm_data.index(@norm_data.max)+1], 1, max_color) if has_max == true
-
+
drawbox(coords[-2], 1, last_color) if has_last == true
@draw.draw(@canvas)
@canvas.to_blob
end
@@ -242,22 +242,22 @@
step = @options[:step].to_f
height = @options[:height].to_f
background_color = @options[:background_color]
create_canvas(@norm_data.length * step + 2, height, background_color)
-
+
upper = @options[:upper].to_f
below_color = @options[:below_color]
above_color = @options[:above_color]
i = 1
@norm_data.each_with_index do |r, index|
color = (r >= upper) ? above_color : below_color
@draw.stroke('transparent')
@draw.fill(color)
- @draw.rectangle( i, @canvas.rows,
- i + step - 2, @canvas.rows - ( (r / @maximum_value) * @canvas.rows) )
+ @draw.rectangle( i, @canvas.rows,
+ i + step - 2, @canvas.rows - ( (r / @maximum_value) * @canvas.rows) )
i += step
end
@draw.draw(@canvas)
@canvas.to_blob
@@ -281,26 +281,26 @@
upper = @options[:upper].to_f
background_color = @options[:background_color]
step = @options[:step].to_f
width = @norm_data.size * step - 1
-
+
create_canvas(@norm_data.size * step - 1, height, background_color)
-
+
below_color = @options[:below_color]
above_color = @options[:above_color]
std_dev_color = @options[:std_dev_color]
drawstddevbox(width,height,std_dev_color) if @options[:has_std_dev] == true
i = 0
@norm_data.each do |r|
- color = (r >= upper) ? above_color : below_color
- @draw.stroke(color)
- @draw.line(i, (@canvas.rows - r/(101.0/(height-4))-4).to_f,
- i, (@canvas.rows - r/(101.0/(height-4))).to_f)
- i += step
+ color = (r >= upper) ? above_color : below_color
+ @draw.stroke(color)
+ @draw.line(i, (@canvas.rows - r/(101.0/(height-4))-4).to_f,
+ i, (@canvas.rows - r/(101.0/(height-4))).to_f)
+ i += step
end
@draw.draw(@canvas)
@canvas.to_blob
end
@@ -318,31 +318,31 @@
def pie
diameter = @options[:diameter].to_f
background_color = @options[:background_color]
create_canvas(diameter, diameter, background_color)
-
+
share_color = @options[:share_color]
remain_color = @options[:remain_color]
percent = @norm_data[0]
-
+
# Adjust the radius so there's some edge left in the pie
r = diameter/2.0 - 2
@draw.fill(remain_color)
@draw.ellipse(r + 2, r + 2, r , r , 0, 360)
@draw.fill(share_color)
# Special exceptions
if percent == 0
- # For 0% return blank
- @draw.draw(@canvas)
- return @canvas.to_blob
+ # For 0% return blank
+ @draw.draw(@canvas)
+ return @canvas.to_blob
elsif percent == 100
- # For 100% just draw a full circle
- @draw.ellipse(r + 2, r + 2, r , r , 0, 360)
- @draw.draw(@canvas)
- return @canvas.to_blob
+ # For 100% just draw a full circle
+ @draw.ellipse(r + 2, r + 2, r , r , 0, 360)
+ @draw.draw(@canvas)
+ return @canvas.to_blob
end
# Okay, this part is as confusing as hell, so pay attention:
# This line determines the horizontal portion of the point on the circle where the X-Axis
# should end. It's caculated by taking the center of the on-image circle and adding that
@@ -400,34 +400,34 @@
def smooth
step = @options[:step].to_f
height = @options[:height].to_f
width = ((@norm_data.size - 1) * step).to_f
-
+
background_color = @options[:background_color]
create_canvas(width, height, background_color)
-
+
min_color = @options[:min_color]
max_color = @options[:max_color]
last_color = @options[:last_color]
has_min = @options[:has_min]
has_max = @options[:has_max]
has_last = @options[:has_last]
line_color = @options[:line_color]
has_std_dev = @options[:has_std_dev]
std_dev_color = @options[:std_dev_color]
-
+
drawstddevbox(width,height,std_dev_color) if has_std_dev == true
@draw.stroke(line_color)
coords = []
i=0
@norm_data.each do |r|
coords.push [ i, (height - 3 - r/(101.0/(height-4))) ]
i += step
end
-
+
if @options[:underneath_color]
closed_polygon(height, width, coords)
else
open_ended_polyline(coords)
end
@@ -437,36 +437,36 @@
drawbox(coords[-1], 2, last_color) if has_last == true
@draw.draw(@canvas)
@canvas.to_blob
end
-
+
##
- # Creates a whisker sparkline to track on/off type data. There are five states:
- # on, off, no value, exceptional on, exceptional off. On values create an up
- # whisker and off values create a down whisker. Exceptional values may be
+ # Creates a whisker sparkline to track on/off type data. There are five states:
+ # on, off, no value, exceptional on, exceptional off. On values create an up
+ # whisker and off values create a down whisker. Exceptional values may be
# colored differently than regular values to indicate, for example, a shut out.
# No value produces an empty row to indicate a tie.
- #
- # * results - an array of integer values between -2 and 2. -2 is exceptional
+ #
+ # * results - an array of integer values between -2 and 2. -2 is exceptional
# down, 1 is regular down, 0 is no value, 1 is up, and 2 is exceptional up.
# * options - a hash that takes parameters
#
# :height - height of the sparkline
#
# :whisker_color - the color of regular whiskers; defaults to black
#
# :exception_color - the color of exceptional whiskers; defaults to red
-
+
def whisker
# step = @options[:step].to_f
height = @options[:height].to_f
background_color = @options[:background_color]
create_canvas(@data.size * 2 - 1, height, background_color)
-
+
whisker_color = @options[:whisker_color] || 'black'
exception_color = @options[:exception_color] || 'red'
on_row = (@canvas.rows/2.0 - 1).ceil
off_row = (@canvas.rows/2.0).floor
@@ -479,11 +479,11 @@
end
y_mid_point = (r >= 1) ? on_row : off_row
y_end_point = y_mid_point
- if ( r > 0)
+ if ( r > 0)
y_end_point = 0
end
if ( r < 0 )
y_end_point = @canvas.rows
@@ -493,11 +493,11 @@
@draw.line( i, y_mid_point, i, y_end_point )
i += 2
end
@draw.draw(@canvas)
- @canvas.to_blob
+ @canvas.to_blob
end
##
# Draw the error Sparkline.
@@ -510,43 +510,49 @@
@draw.draw(@canvas)
@canvas.to_blob
end
-private
+ private
def normalize_data
- @minumum_value = @data.min
- @maximum_value = @data.max
- abs_min = @minumum_value.abs
+ @minimum_value = @data.min
+ @maximum_value = @data.max
if @options[:type].to_s == 'pie'
@norm_data = @data
else
- @norm_data = @data.map { |value| value = (value+abs_min).to_f / (@maximum_value+abs_min) * 100.0 }
+ @norm_data = @data.map do |value|
+ value = ((value.to_f - @minimum_value)/(@maximum_value - @minimum_value)) * 100.0
+ end
end
end
##
# :arr - an array of points (represented as two element arrays)
-
+
def open_ended_polyline(arr)
0.upto(arr.length - 2) { |i|
@draw.line(arr[i][0], arr[i][1], arr[i+1][0], arr[i+1][1])
}
end
-
+
# Fills in the area under the line (used for a smooth graph)
def closed_polygon(height, width, coords)
return if @options[:underneath_color].nil?
list = []
- list << [0, height]
+ # Start off screen so completed polygon doesn't show
+ list << [-1, height + 1]
+ list << [coords.first.first - 1, coords.first.last]
+ # Now the normal coords
list << coords
- list << [width, height]
+ # Close offscreen
+ list << [coords.last.first + 1, coords.last.last]
+ list << [width + 1, height + 1]
@draw.fill( @options[:underneath_color] )
@draw.polygon( *list.flatten )
end
-
+
##
# Create an image to draw on and a drawable to do the drawing with.
#
# TODO Refactor into smaller methods
@@ -580,19 +586,19 @@
@font = @options[:font] if @options.has_key?(:font)
@draw.fill = 'black'
@draw.font = @font if @font
@draw.gravity = Magick::WestGravity
- @draw.annotate( @canvas,
- @label_width, 1.0,
- w - @label_and_data_last_width + @@label_margin, h - calculate_caps_height/2.0,
- @options[:label])
+ @draw.annotate( @canvas,
+ @label_width, 1.0,
+ w - @label_and_data_last_width + @@label_margin, h - calculate_caps_height/2.0,
+ @options[:label])
@draw.fill = 'red'
- @draw.annotate( @canvas,
- @data_last_width, 1.0,
- w - @data_last_width - @@label_margin * 2.0, h - calculate_caps_height/2.0,
- @data.last.to_s)
+ @draw.annotate( @canvas,
+ @data_last_width, 1.0,
+ w - @data_last_width - @@label_margin * 2.0, h - calculate_caps_height/2.0,
+ @data.last.to_s)
end
end
##
# Utility to draw a coloured box