require 'pureimage' =begin rdoc A library (in Ruby!) for generating sparklines. Can be used to write to a file or make a web service with Rails or other Ruby CGI apps. Idea and much of the outline for the source lifted directly from {Joe Gregorio's Python Sparklines web service script}[http://bitworking.org/projects/sparklines]. Requires the pureimage library. ==Authors {Dan Nugent}[mailto:nugend@gmail.com] Original port from Python Sparklines library. {Geoffrey Grosenbach}[mailto:boss@topfunky.com] -- http://topfunky.com -- http://newbieonrails.topfunky.com -- Conversion to module and addition of functions for using with Rails. Also changed functions to use Rails-style option hashes for parameters. {Kazuhiro YOSHIDA}[mailto:moriq@moriq.com] -- http://www.moriq.com -- Replace required image library RMagick to pureimage. ==License Licensed under the MIT license. =end module Spark $VERSION = '0.2.0' BLACK = 0x000000 WHITE = 0xffffff DARK_GREEN = 0x008000 DARK_BLUE = 0x000080 GRAY = 0x808080 RED = 0xff0000 LIGHT_RED = 0xff8080 LIGHT_GREEN = 0x80ff80 LIGHT_BLUE = 0x8080ff # Does the actually plotting of the graph. Calls the appropriate function based on the :type value passed. Defaults to 'smooth.' def Spark.plot(results=[], options={}) defaults = { :type => 'smooth', :height => 14, :upper => 50, :diameter => 20, :step => 2, :above_color => LIGHT_RED, :below_color => GRAY, :background_color => WHITE, :share_color => LIGHT_BLUE, :remain_color => GRAY, :min_color => LIGHT_BLUE, :max_color => LIGHT_GREEN, :last_color => LIGHT_RED, :has_min => false, :has_max => false, :has_last => false } # This symbol->string->symbol is kind of awkward. Is there a more elegant way? # moriq: see HashWithIndifferentAccess (in active_support). # Convert all symbol keys to strings defaults.keys.reverse.each do |key| defaults[key.to_s] = defaults[key] end options.keys.reverse.each do |key| options[key.to_s] = options[key] end options = defaults.merge(options) # Convert options string keys back to symbols options.keys.reverse.each do |key| options[key.to_sym] = options[key] end # Call the appropriate function for actual plotting #self.send('smooth', results, options) self.send(options[:type], results, options) end # Writes a graph to disk with the specified filename, or "spark.png" def Spark.plot_to_file(filename="spark.png", results=[], options={}) File.open( filename, 'wb' ) do |png| png << Spark.plot( results, options) end end # Creates a smooth sparkline # # * results - an array of integer values between 0 and 100 inclusive # # * options - a hash that takes these optional parameters: # # :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 # # :has_min - Determines whether a dot will be drawn at the lowest value or not. Defaulst to false. # # :has_max - Determines whether a dot will be drawn at the highest value or not. Defaulst to false. # # :has_last - Determines whether a dot will be drawn at the last value or not. Defaulst 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. def self.smooth(results, options) step = options[:step].to_i height = options[:height].to_i 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] draw = Image.new((results.size - 1) * step + 5, height.to_i, options[:background_color]) draw.color = GRAY coords = [] i=0 results.each do |r| coords.push [ 2 + i, (height - 3 - r/(101.0/(height-4))).to_i ] i += step end my_polyline(draw, coords) if has_min == true min_pt = coords[results.index(results.min)] draw.fill_rect(min_pt[0]-2, min_pt[1]-2, 4, 4, min_color) end if has_max == true max_pt = coords[results.index(results.max)] draw.fill_rect(max_pt[0]-2, max_pt[1]-2, 4, 4, max_color) end if has_last == true last_pt = coords[-1] draw.fill_rect(last_pt[0]-2, last_pt[1]-2, 4, 4, last_color) end png = PNGIO.new require 'stringio' blob = '' png.write(draw, StringIO.new(blob)) blob end # polyline function. # # * draw - a Image object. # # * arr - an array of points (represented as two element arrays) def self.my_polyline (draw, arr) i = 0 while i < arr.size - 1 draw.draw_line(arr[i][0], arr[i][1], arr[i+1][0], arr[i+1][1]) i += 1 end end end if __FILE__ == $0 Spark.plot_to_file("spark.png", [1,25,33,46,89,90,85,77,42], :type => 'smooth', :step => 2, :height => 20) end