tlconnor-scruffy 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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