tlconnor-scruffy 0.2.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/CHANGES.txt +115 -0
  2. data/LICENCE.txt +20 -0
  3. data/Manifest.txt +74 -0
  4. data/README.txt +66 -0
  5. data/lib/scruffy.rb +30 -0
  6. data/lib/scruffy/components.rb +22 -0
  7. data/lib/scruffy/components/axes.rb +23 -0
  8. data/lib/scruffy/components/background.rb +24 -0
  9. data/lib/scruffy/components/base.rb +57 -0
  10. data/lib/scruffy/components/data_markers.rb +41 -0
  11. data/lib/scruffy/components/graphs.rb +52 -0
  12. data/lib/scruffy/components/grid.rb +57 -0
  13. data/lib/scruffy/components/label.rb +17 -0
  14. data/lib/scruffy/components/legend.rb +147 -0
  15. data/lib/scruffy/components/style_info.rb +22 -0
  16. data/lib/scruffy/components/title.rb +19 -0
  17. data/lib/scruffy/components/value_markers.rb +25 -0
  18. data/lib/scruffy/components/viewport.rb +37 -0
  19. data/lib/scruffy/formatters.rb +233 -0
  20. data/lib/scruffy/graph.rb +205 -0
  21. data/lib/scruffy/graph_state.rb +29 -0
  22. data/lib/scruffy/helpers.rb +13 -0
  23. data/lib/scruffy/helpers/canvas.rb +41 -0
  24. data/lib/scruffy/helpers/layer_container.rb +119 -0
  25. data/lib/scruffy/helpers/marker_helper.rb +25 -0
  26. data/lib/scruffy/helpers/meta.rb +5 -0
  27. data/lib/scruffy/helpers/point_container.rb +99 -0
  28. data/lib/scruffy/layers.rb +28 -0
  29. data/lib/scruffy/layers/all_smiles.rb +137 -0
  30. data/lib/scruffy/layers/area.rb +46 -0
  31. data/lib/scruffy/layers/average.rb +67 -0
  32. data/lib/scruffy/layers/bar.rb +73 -0
  33. data/lib/scruffy/layers/base.rb +211 -0
  34. data/lib/scruffy/layers/box.rb +114 -0
  35. data/lib/scruffy/layers/line.rb +46 -0
  36. data/lib/scruffy/layers/multi.rb +74 -0
  37. data/lib/scruffy/layers/multi_bar.rb +51 -0
  38. data/lib/scruffy/layers/pie.rb +123 -0
  39. data/lib/scruffy/layers/pie_slice.rb +119 -0
  40. data/lib/scruffy/layers/scatter.rb +29 -0
  41. data/lib/scruffy/layers/sparkline_bar.rb +39 -0
  42. data/lib/scruffy/layers/stacked.rb +87 -0
  43. data/lib/scruffy/rasterizers.rb +14 -0
  44. data/lib/scruffy/rasterizers/batik_rasterizer.rb +39 -0
  45. data/lib/scruffy/rasterizers/mini_magick_rasterizer.rb +24 -0
  46. data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +27 -0
  47. data/lib/scruffy/renderers.rb +23 -0
  48. data/lib/scruffy/renderers/axis_legend.rb +41 -0
  49. data/lib/scruffy/renderers/base.rb +95 -0
  50. data/lib/scruffy/renderers/cubed.rb +44 -0
  51. data/lib/scruffy/renderers/cubed3d.rb +53 -0
  52. data/lib/scruffy/renderers/empty.rb +22 -0
  53. data/lib/scruffy/renderers/pie.rb +20 -0
  54. data/lib/scruffy/renderers/reversed.rb +17 -0
  55. data/lib/scruffy/renderers/sparkline.rb +10 -0
  56. data/lib/scruffy/renderers/split.rb +48 -0
  57. data/lib/scruffy/renderers/standard.rb +37 -0
  58. data/lib/scruffy/themes.rb +177 -0
  59. data/lib/scruffy/version.rb +9 -0
  60. data/test/graph_creation_test.rb +286 -0
  61. data/test/test_helper.rb +2 -0
  62. metadata +150 -0
@@ -0,0 +1,29 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::Line
3
+ #
4
+ # Author:: Mat Schaffer
5
+ # Date:: March 20th, 2007
6
+ #
7
+ # Simple scatter graph
8
+ class Scatter < Base
9
+
10
+ include Scruffy::Helpers::Marker
11
+
12
+ # Renders scatter graph.
13
+ def draw(svg, coords, options={})
14
+
15
+ options.merge!(@options)
16
+
17
+ if options[:shadow]
18
+ svg.g(:class => 'shadow', :transform => "translate(#{relative(0.5)}, #{relative(0.5)})") {
19
+ coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last + relative(0.9), :r => relative(2),
20
+ :style => "stroke-width: #{relative(2)}; stroke: black; opacity: 0.35;" ) }
21
+ }
22
+ end
23
+
24
+ coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last, :r => relative(2),
25
+ :style => "stroke-width: #{relative(2)}; stroke: #{color.to_s}; fill: #{color.to_s}" ) }
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,39 @@
1
+ module Scruffy
2
+ module Layers
3
+ # Experimental, do not use.
4
+ class SparklineBar < Base
5
+
6
+ def draw(svg, coords, options = {})
7
+ zero_point = @height / 2.0
8
+
9
+ coords.each do |coord|
10
+ x, y, bar_height = (coord.first-(@bar_width * 0.5)), coord.last, (height - coord.last)
11
+
12
+ bar_color = (y > zero_point) ? 'black' : 'red'
13
+ bar_height = (bar_height - zero_point)
14
+
15
+ #y = (bar_height < 0) ?
16
+
17
+ # svg.rect( :x => x, :y => zero_point, :width => @bar_width, :height => ,
18
+ # :fill => bar_color, :stroke => 'none', 'style' => "opacity: #{opacity}" )
19
+ end
20
+ end
21
+
22
+ protected
23
+ def generate_coordinates(options = {})
24
+ @bar_width = (width / points.size) * 0.9
25
+ options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
26
+
27
+ #TODO iteration by index on points. Needs to change
28
+ coords = (0...points.size).map do |idx|
29
+ x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5)
30
+
31
+ relative_percent = ((points[idx] == min_value) ? 0 : ((points[idx] - min_value) / (max_value - min_value).to_f))
32
+ y_coord = (height - (height * relative_percent))
33
+ [x_coord, y_coord]
34
+ end
35
+ coords
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,87 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::Stacked
3
+ #
4
+ # Author:: Brasten Sager
5
+ # Date:: August 12th, 2006
6
+ #
7
+ # Provides a generic way for stacking graphs. This may or may not
8
+ # do what you'd expect under every situation, but it at least kills
9
+ # a couple birds with one stone (stacked bar graphs and stacked area
10
+ # graphs work fine).
11
+ class Stacked < Base
12
+ include Scruffy::Helpers::LayerContainer
13
+
14
+ # Returns new Stacked graph.
15
+ #
16
+ # You can provide a block for easily adding layers during (just after) initialization.
17
+ # Example:
18
+ # Stacked.new do |stacked|
19
+ # stacked << Scruffy::Layers::Line.new( ... )
20
+ # stacked.add(:bar, 'My Bar', [...])
21
+ # end
22
+ #
23
+ # The initialize method passes itself to the block, and since stacked is a LayerContainer,
24
+ # layers can be added just as if they were being added to Graph.
25
+ def initialize(options={}, &block)
26
+ super(options)
27
+
28
+ block.call(self) # Allow for population of data with a block during initialization.
29
+ end
30
+
31
+ # Overrides Base#render to fiddle with layers' points to achieve a stacked effect.
32
+ def render(svg, options = {})
33
+ #TODO ensure this works with new points
34
+ current_points = points
35
+
36
+ layers.each do |layer|
37
+ real_points = layer.points
38
+ layer.points = current_points
39
+ layer_options = options.dup
40
+ layer_options[:color] = layer.preferred_color || layer.color || options[:theme].next_color
41
+ layer.render(svg, layer_options)
42
+ options.merge(layer_options)
43
+ layer.points = real_points
44
+
45
+ layer.points.each_with_index { |val, idx| current_points[idx] -= val }
46
+ end
47
+ end
48
+
49
+ # A stacked graph has many data sets. Return legend information for all of them.
50
+ def legend_data
51
+ if relevant_data?
52
+ retval = []
53
+ layers.each do |layer|
54
+ retval << layer.legend_data
55
+ end
56
+ retval
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+ # TODO, special points accessor
63
+ def points
64
+ longest_arr = layers.inject(nil) do |longest, layer|
65
+ longest = layer.points if (longest.nil? || longest.size < layer.points.size)
66
+ end
67
+
68
+ summed_points = (0...longest_arr.size).map do |idx|
69
+ layers.inject(nil) do |sum, layer|
70
+ if sum.nil? && !layer.points[idx].nil?
71
+ sum = layer.points[idx]
72
+ elsif !layer.points[idx].nil?
73
+ sum += layer.points[idx]
74
+ end
75
+
76
+ sum
77
+ end
78
+ end
79
+
80
+ summed_points
81
+ end
82
+
83
+ def points=(val)
84
+ throw ArgumentsError, "Stacked layers cannot accept points, only other layers."
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,14 @@
1
+ # ===Scruffy Rasterizers
2
+ #
3
+ # Author:: Brasten Sager
4
+ # Date:: August 10th, 2006
5
+ #
6
+ # These handle the job of rasterizing SVG images to other image formats.
7
+ # At the moment, only RMagickRasterizer exists, but others may soon follow.
8
+ #
9
+ # I'm somewhat interesting in finding a way to integrate Apache Batik, as it's
10
+ # SVG rendering seems to be superior to ImageMagick's.
11
+ module Scruffy::Rasterizers; end
12
+
13
+ require 'scruffy/rasterizers/rmagick_rasterizer.rb'
14
+ require 'scruffy/rasterizers/batik_rasterizer.rb'
@@ -0,0 +1,39 @@
1
+ module Scruffy::Rasterizers
2
+ # == Scruffy::Rasterizers::BatikRasterizer
3
+ #
4
+ # Author:: Brasten Sager
5
+ # Date:: August 14th, 2006
6
+ #
7
+ # Purely experimental. Can be used to rasterize SVG graphs with
8
+ # Apache Batik.
9
+ class BatikRasterizer
10
+ # Returns new BatikRasterizer.
11
+ #
12
+ # Options:
13
+ # command:: Command needed to execute Batik. (ie: 'java -classpath {...}')
14
+ # temp_folder:: Folder for storing temporary files being passed between Scruffy and Batik.
15
+ def initialize(options={})
16
+ @command = options[:command]
17
+ @temp_folder = options[:temp_folder]
18
+ end
19
+
20
+ # Rasterize graph.
21
+ #
22
+ # Options:
23
+ # as:: Image format to generate (PNG, JPG, et al.)
24
+ def rasterize(svg, options={})
25
+ File.open(@temp_folder + '/temp_svg.svg', 'w') { |file|
26
+ file.write(svg)
27
+ }
28
+
29
+ `#{@command} -d #{@temp_folder} -m image/#{options[:as].downcase} #{@temp_folder}/temp_svg.svg`
30
+
31
+ image = ""
32
+ File.open(@temp_folder + '/temp_svg.' + options[:as].downcase, 'r') { |file|
33
+ image = file.read
34
+ }
35
+
36
+ image
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ module Scruffy::Rasterizers
2
+
3
+ # == MiniMagickRasterizer
4
+ #
5
+ # Author:: Tim Connor
6
+ # Date:: Feb 9th, 2010
7
+ #
8
+ # The MiniMagickRasterizer converts SVG graphs to images using MiniMagick.
9
+ class MiniMagickRasterizer
10
+ def rasterize(svg, options={})
11
+ require 'mini_magick'
12
+
13
+ image = MiniMagick::Image::from_blob(svg)
14
+
15
+ image.format(options[:as]) do |cmd|
16
+ cmd.background "#0000" # Transparent background
17
+ end if options[:as]
18
+
19
+ image.write(options[:to]) if options[:to]
20
+ image.to_blob
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Scruffy::Rasterizers
2
+
3
+ # == RMagickRasterizer
4
+ #
5
+ # Author:: Brasten Sager
6
+ # Date:: August 14th, 2006
7
+ #
8
+ # The RMagickRasterizer converts SVG graphs to images using ImageMagick.
9
+ class RMagickRasterizer
10
+ def rasterize(svg, options={})
11
+ # I know this seems weird, I'm open to suggestions.
12
+ # I didn't want RMagick required unless absolutely necessary.
13
+ require 'RMagick'
14
+
15
+ image = Magick::Image::from_blob(svg)[0]
16
+
17
+ # Removed for now
18
+ # image.resize!(options[:size][0], options[:size][1], Magick::BoxFilter, 1.25) if options[:actual_size]
19
+
20
+ if options[:to]
21
+ image.write(options[:to]) { self.format = options[:as] }
22
+ end
23
+
24
+ image.to_blob { self.format = options[:as] }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ # ===Scruffy Renderers
2
+ #
3
+ # Author:: Brasten Sager
4
+ # Date:: August 14th, 2006
5
+ #
6
+ # Renderers piece the entire graph together from a collection
7
+ # of components. Creating new renderers allows you to create
8
+ # entirely new layouts for your graphs.
9
+ #
10
+ # Scruffy::Renderers::Base contains the basic functionality needed
11
+ # by a layout. The easiest way to create a new layout is by subclassing
12
+ # Base.
13
+ module Scruffy::Renderers; end
14
+
15
+ require 'scruffy/renderers/base'
16
+ require 'scruffy/renderers/empty'
17
+ require 'scruffy/renderers/standard'
18
+ require 'scruffy/renderers/axis_legend'
19
+ require 'scruffy/renderers/reversed'
20
+ require 'scruffy/renderers/cubed'
21
+ require 'scruffy/renderers/split'
22
+ require 'scruffy/renderers/cubed3d'
23
+ require 'scruffy/renderers/pie'
@@ -0,0 +1,41 @@
1
+ module Scruffy::Renderers
2
+ class AxisLegend < Empty
3
+
4
+ def define_layout
5
+ super do |components|
6
+ components << Scruffy::Components::Title.new(:title, :position => [5, 2], :size => [90, 7])
7
+
8
+
9
+ components << Scruffy::Components::Viewport.new(:view, :position => [6, 22], :size => [90, 66]) do |graph|
10
+ graph << Scruffy::Components::ValueMarkers.new(:values, :position => [0, 2], :size => [8, 89])
11
+ graph << Scruffy::Components::Grid.new(:grid, :position => [10, 0], :size => [90, 89], :stroke_width => 1)
12
+ graph << Scruffy::Components::VGrid.new(:vgrid, :position => [10, 0], :size => [90, 89], :stroke_width => 1)
13
+ graph << Scruffy::Components::DataMarkers.new(:labels, :position => [10, 92], :size => [90, 8])
14
+ graph << Scruffy::Components::Graphs.new(:graphs, :position => [10, 0], :size => [90, 89])
15
+ end
16
+ components << Scruffy::Components::YLegend.new(:y_legend, :position => [1, 26], :size => [5, 66])
17
+ components << Scruffy::Components::XLegend.new(:x_legend, :position => [5, 92], :size => [90, 6])
18
+ components << Scruffy::Components::Legend.new(:legend, :position => [5, 13], :size => [90, 6])
19
+ end
20
+ end
21
+
22
+ protected
23
+ def hide_values
24
+ super
25
+ component(:view).position[0] = -10
26
+ component(:view).size[0] = 100
27
+ end
28
+
29
+ def labels
30
+ [component(:view).component(:labels)]
31
+ end
32
+
33
+ def values
34
+ [component(:view).component(:values)]
35
+ end
36
+
37
+ def grids
38
+ [component(:view).component(:grid),component(:view).component(:vgrid)]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,95 @@
1
+ require 'builder'
2
+ module Scruffy::Renderers
3
+ # ===Scruffy::Renderers::Base
4
+ #
5
+ # Author:: Brasten Sager
6
+ # Date:: August 14th, 2006
7
+ #
8
+ # Provides all the base functionality needed to render a graph, but
9
+ # does not provide a default layout.
10
+ #
11
+ # For a basic layout, see Scruffy::Renderers::Standard.
12
+ class Base
13
+ include Scruffy::Helpers::Canvas
14
+
15
+ attr_accessor :options
16
+
17
+ def initialize(options = {})
18
+ self.components = []
19
+ self.options = options
20
+ define_layout
21
+ end
22
+
23
+ # Renders the graph and all components.
24
+ def render(options = {})
25
+ options[:graph_id] ||= 'scruffy_graph'
26
+ options[:complexity] ||= (global_complexity || :normal)
27
+
28
+ # Allow subclasses to muck with components prior to renders.
29
+ rendertime_renderer = self.clone
30
+ rendertime_renderer.instance_eval { before_render if respond_to?(:before_render) }
31
+
32
+ svg = Builder::XmlMarkup.new(:indent => 2)
33
+ unless options[:no_doctype_header]
34
+ svg.instruct!
35
+ svg.instruct! 'DOCTYPE', 'svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" type'
36
+ end
37
+ svg.svg(:xmlns => "http://www.w3.org/2000/svg", 'xmlns:xlink' => "http://www.w3.org/1999/xlink", :width => options[:size].first, :height => options[:size].last) {
38
+ svg.g(:id => options[:graph_id]) {
39
+ rendertime_renderer.components.each do |component|
40
+ component.render(svg,
41
+ bounds_for( options[:size], component.position, component.size ),
42
+ options)
43
+ end
44
+ }
45
+ }
46
+ svg.target!
47
+ end
48
+
49
+ def before_render
50
+ if self.options
51
+ set_values(self.options[:values]) if (self.options[:values] && self.options[:values] != :hide)
52
+ hide_grid if (self.options[:grid] == :hide)
53
+ hide_values if (self.options[:values] == :hide)
54
+ hide_labels if (self.options[:labels] == :hide)
55
+ end
56
+ end
57
+
58
+ def method_missing(sym, *args)
59
+ self.options = {} if self.options.nil?
60
+
61
+ if args.size > 0
62
+ self.options[sym] = args[0]
63
+ else
64
+ return self.options[sym]
65
+ end
66
+ end
67
+
68
+ protected
69
+ def hide_grid
70
+ grids.each { |grid| grid.visible = false }
71
+ end
72
+
73
+ def set_values(val)
74
+ values.each { |value| value.markers = val }
75
+ grids.each { |grid| grid.markers = val }
76
+ end
77
+
78
+ def hide_values
79
+ values.each { |value| value.visible = false }
80
+ end
81
+
82
+ def hide_labels
83
+ labels.each { |label| label.visible = false }
84
+ end
85
+
86
+ private
87
+ def global_complexity
88
+ if Kernel.const_defined? "SCRUFFY_COMPLEXITY"
89
+ SCRUFFY_COMPLEXITY
90
+ else
91
+ nil
92
+ end
93
+ end
94
+ end # base
95
+ end
@@ -0,0 +1,44 @@
1
+ module Scruffy::Renderers
2
+ # ===Scruffy::Renderers::Cubed
3
+ #
4
+ # Author:: Brasten Sager
5
+ # Date:: August 14th, 2006
6
+ #
7
+ # Graph layout consisting of four separate graphs arranged in a 2x2 grid.
8
+ class Cubed < Empty
9
+ VIEWPORT_SIZE = [35, 30]
10
+ VIEWPORTS = { :top_left => [10, 25],
11
+ :top_right => [55, 25],
12
+ :bottom_left => [10, 65],
13
+ :bottom_right => [55, 65] }
14
+
15
+ # Returns a Cubed instance.
16
+ def define_layout
17
+ super do |components|
18
+ components << Scruffy::Components::Title.new(:title, :position => [5, 2], :size => [90, 7])
19
+
20
+ VIEWPORTS.each_pair do |category, position|
21
+ components << Scruffy::Components::Viewport.new(category, :position => position,
22
+ :size => VIEWPORT_SIZE, &graph_block(category))
23
+ end
24
+
25
+ components << Scruffy::Components::Legend.new(:legend, :position => [5, 13], :size => [90, 5])
26
+ end
27
+ end
28
+
29
+ private
30
+ # Returns a typical graph layout.
31
+ #
32
+ # These are squeezed into viewports.
33
+ def graph_block(graph_filter)
34
+ block = Proc.new { |components|
35
+ components << Scruffy::Components::Grid.new(:grid, :position => [10, 0], :size => [90, 89])
36
+ components << Scruffy::Components::ValueMarkers.new(:value_markers, :position => [0, 2], :size => [8, 89])
37
+ components << Scruffy::Components::DataMarkers.new(:data_markers, :position => [10, 92], :size => [90, 8])
38
+ components << Scruffy::Components::Graphs.new(:graphs, :position => [10, 0], :size => [90, 89], :only => graph_filter)
39
+ }
40
+
41
+ block
42
+ end
43
+ end
44
+ end