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,114 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::Box
3
+ #
4
+ # Author:: Brasten Sager
5
+ # Date:: August 6th, 2006
6
+ #
7
+ # Standard bar graph.
8
+ class Box < Base
9
+
10
+ # Draw box plot.
11
+ def draw(svg, coords, options = {})
12
+ coords.each_with_index do |coord,idx|
13
+ x, y, bar_height = (coord.first), coord.last, 1#(height - coord.last)
14
+
15
+ valh = max_value + min_value * -1 #value_height
16
+ maxh = max_value * height / valh #positive area height
17
+ minh = min_value * height / valh #negative area height
18
+ #puts "height = #{height} and max_value = #{max_value} and min_value = #{min_value} and y = #{y} and point = #{points[idx]}"
19
+
20
+ #if points[idx] > 0
21
+ # bar_height = points[idx]*maxh/max_value
22
+ #else
23
+ # bar_height = points[idx]*minh/min_value
24
+ #end
25
+
26
+ #puts " y = #{y} and point = #{points[idx]}"
27
+ #svg.g(:transform => "translate(-#{relative(0.5)}, -#{relative(0.5)})") {
28
+ # svg.rect( :x => x, :y => y, :width => @bar_width + relative(1), :height => bar_height + relative(1),
29
+ # :style => "fill: black; fill-opacity: 0.15; stroke: none;" )
30
+ # svg.rect( :x => x+relative(0.5), :y => y+relative(2), :width => @bar_width + relative(1), :height => bar_height - relative(0.5),
31
+ # :style => "fill: black; fill-opacity: 0.15; stroke: none;" )
32
+ #
33
+ #}
34
+
35
+ svg.line(:x1=>x+@bar_width/2,:x2=>x+@bar_width/2,:y1=>y[0],:y2=>y[4], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
36
+ svg.line(:x1=>x+@bar_width/4,:x2=>x+@bar_width/4*3,:y1=>y[0],:y2=>y[0], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
37
+ svg.line(:x1=>x+@bar_width/4,:x2=>x+@bar_width/4*3,:y1=>y[4],:y2=>y[4], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
38
+ svg.rect( :x => x, :y => y[1], :width => @bar_width, :height => (y[1]-y[3])*-1,
39
+ :fill => color.to_s, 'style' => "opacity: #{opacity}; stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1;" )
40
+ svg.line(:x1=>x,:x2=>x+@bar_width,:y1=>y[2],:y2=>y[2], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
41
+ #svg.rect( :x => x, :y => y, :width => @bar_width, :height => bar_height,
42
+ # :fill => color.to_s, 'style' => "opacity: #{opacity}; stroke: none;" )
43
+ end
44
+ end
45
+
46
+
47
+ # Returns the highest value in any of this container's layers.
48
+ #
49
+ # If padding is set to :padded, a 15% padding is added to the highest value.
50
+ def top_value(padding=nil) # :nodoc:
51
+ topval = points[0].max
52
+ points.each do |point_set|
53
+ topval = ( (topval < point_set.max) ? point_set.max : topval )
54
+ end
55
+ #topval = layers.inject(0) { |max, layer| (max = ((max < layer.top_value) ? layer.top_value : max)) unless layer.top_value.nil?; max }
56
+ below_zero = (topval <= 0)
57
+ topval = padding == :padded ? (topval + ((topval - bottom_value(nil)) * 0.15)) : topval
58
+ (below_zero && topval > 0) ? 0 : topval
59
+ end
60
+
61
+ # Returns the lowest value in any of this container's layers.
62
+ #
63
+ # If padding is set to :padded, a 15% padding is added below the lowest value.
64
+ # If the lowest value is greater than zero, then the padding will not cross the zero line, preventing
65
+ # negative values from being introduced into the graph purely due to padding.
66
+ def bottom_value(padding=nil) # :nodoc:
67
+ botval = points[0].min
68
+ points.each do |point_set|
69
+ botval = ( (botval>point_set.min) ? point_set.min : botval )
70
+ end
71
+ #botval = layers.inject(0) do |min, layer|
72
+ # (min = ((min > layer.bottom_value) ? layer.bottom_value : min)) unless layer.bottom_value.nil?
73
+ # min
74
+ #end
75
+ above_zero = (botval >= 0)
76
+ botval = (botval - ((top_value(nil) - botval) * 0.15)) if padding == :padded
77
+
78
+ # Don't introduce negative values solely due to padding.
79
+ # A user-provided value must be negative before padding will extend into negative values.
80
+ (above_zero && botval < 0) ? 0 : botval
81
+ end
82
+
83
+ protected
84
+
85
+ # Due to the size of the bar graph, X-axis coords must
86
+ # be squeezed so that the bars do not hang off the ends
87
+ # of the graph.
88
+ #
89
+ # Unfortunately this just mean that bar-graphs and most other graphs
90
+ # end up on different points. Maybe adding a padding to the coordinates
91
+ # should be a graph-wide thing?
92
+ #
93
+ # Update : x-axis coords for lines and area charts should now line
94
+ # up with the center of bar charts.
95
+
96
+ def generate_coordinates(options = {})
97
+ @bar_width = (width / points.size) * 0.9
98
+ options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
99
+
100
+ #TODO more array work with index, try to rework to be accepting of hashes
101
+ coords = (0...points.size).map do |idx|
102
+ x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5) - (@bar_width * 0.5)
103
+ y_coords = []
104
+ points[idx].each do |point|
105
+ relative_percent = ((point == min_value) ? 0 : ((point - min_value) / (max_value - min_value).to_f))
106
+ y_coord = (height - (height * relative_percent))
107
+ y_coords << y_coord
108
+ end
109
+ [x_coord, y_coords]
110
+ end
111
+ coords
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,46 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::Line
3
+ #
4
+ # Author:: Brasten Sager
5
+ # Date:: August 7th, 2006
6
+ #
7
+ # Line graph.
8
+ class Line < Base
9
+
10
+ # Renders line graph.
11
+ #
12
+ # Options:
13
+ # See initialize()
14
+ def draw(svg, coords, options={})
15
+
16
+ # Include options provided when the object was created
17
+ options.merge!(@options)
18
+
19
+ stroke_width = (options[:relativestroke]) ? relative(options[:stroke_width]) : options[:stroke_width]
20
+ style = (options[:style]) ? options[:style] : ''
21
+
22
+ if options[:shadow]
23
+ svg.g(:class => 'shadow', :transform => "translate(#{relative(0.5)}, #{relative(0.5)})") {
24
+ svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'transparent',
25
+ :stroke => 'black', 'stroke-width' => stroke_width,
26
+ :style => 'fill-opacity: 0; stroke-opacity: 0.35' )
27
+
28
+ if options[:dots]
29
+ coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last + relative(0.9), :r => stroke_width,
30
+ :style => "stroke-width: #{stroke_width}; stroke: black; opacity: 0.35;" ) }
31
+ end
32
+ }
33
+ end
34
+
35
+
36
+ svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none', :stroke => @color.to_s,
37
+ 'stroke-width' => stroke_width, :style => style )
38
+
39
+ if options[:dots]
40
+ coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last, :r => stroke_width,
41
+ :style => "stroke-width: #{stroke_width}; stroke: #{color.to_s}; fill: #{color.to_s}" ) }
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,74 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::Multi
3
+ #
4
+ # Author:: Jeremy Green
5
+ # Date:: July 29th, 2009
6
+ #
7
+ # Based on:: Scruffy::Layers::Stacked by
8
+ # Author:: Brasten Sager
9
+ # Date:: August 12th, 2006
10
+ #
11
+ # Provides a generic way for displaying multiple bar graphs side by side.
12
+ class Multi < Base
13
+ include Scruffy::Helpers::LayerContainer
14
+
15
+ # Returns new Multi graph.
16
+ #
17
+ # You can provide a block for easily adding layers during (just after) initialization.
18
+ # Example:
19
+ # Multi.new do |multi|
20
+ # multi << Scruffy::Layers::Line.new( ... )
21
+ # multi.add(:multi_bar, 'My Bar', [...])
22
+ # end
23
+ #
24
+ # The initialize method passes itself to the block, and since multi is a LayerContainer,
25
+ # layers can be added just as if they were being added to Graph.
26
+ def initialize(options={}, &block)
27
+ super(options)
28
+
29
+ block.call(self) # Allow for population of data with a block during initialization.
30
+ end
31
+
32
+ # Overrides Base#render to fiddle with layers' points to achieve a multi effect.
33
+ def render(svg, options = {})
34
+ #TODO ensure this works with new points
35
+ #current_points = points
36
+ layers.each_with_index do |layer,i|
37
+
38
+ #real_points = layer.points
39
+ #layer.points = current_points
40
+ layer_options = options.dup
41
+
42
+ layer_options[:num_bars] = layers.size
43
+ layer_options[:position] = i
44
+ layer_options[:color] = layer.preferred_color || layer.color || options[:theme].next_color
45
+ layer.render(svg, layer_options)
46
+
47
+ options.merge(layer_options)
48
+
49
+ #layer.points = real_points
50
+ #layer.points.each_with_index { |val, idx| current_points[idx] -= val }
51
+ end
52
+ end
53
+
54
+ # A multi graph has many data sets. Return legend information for all of them.
55
+ def legend_data
56
+ if relevant_data?
57
+ retval = []
58
+ layers.each do |layer|
59
+ retval << layer.legend_data
60
+ end
61
+ retval
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
67
+ # TODO, special points accessor
68
+
69
+
70
+ def points=(val)
71
+ throw ArgumentsError, "Multi layers cannot accept points, only other layers."
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,51 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::MultiBar
3
+ #
4
+ # Author:: Jeremy Green
5
+ # Date:: July 29th, 2009
6
+ #
7
+ # Based on:: Scruffy::Layers::Bar by
8
+ # Author:: Brasten Sager
9
+ # Date:: August 6th, 2006
10
+ #
11
+ # Standard multi_bar graph.
12
+ class MultiBar < Bar
13
+
14
+
15
+
16
+ protected
17
+
18
+ # Due to the size of the bar graph, X-axis coords must
19
+ # be squeezed so that the bars do not hang off the ends
20
+ # of the graph.
21
+ #
22
+ # Unfortunately this just mean that bar-graphs and most other graphs
23
+ # end up on different points. Maybe adding a padding to the coordinates
24
+ # should be a graph-wide thing?
25
+ #
26
+ # Update : x-axis coords for lines and area charts should now line
27
+ # up with the center of bar charts.
28
+
29
+ def generate_coordinates(options = {})
30
+ @point_width = (width / points.size)
31
+ @point_space = @point_width * 0.1
32
+ @point_width = @point_width - @point_space
33
+ @bar_width = @point_width/options[:num_bars]
34
+ @bar_space = @bar_width * 0.1
35
+ @bar_width = @bar_width * 0.9
36
+
37
+ #options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
38
+
39
+ #TODO more array work with index, try to rework to be accepting of hashes
40
+ coords = (0...points.size).map do |idx|
41
+
42
+ x_coord = (@point_width * idx) + @point_space/2 + @point_space*idx + @bar_width * options[:position] + @bar_space*(options[:position].to_f - 0.5)
43
+
44
+ relative_percent = ((points[idx] == min_value) ? 0 : ((points[idx] - min_value) / (max_value - min_value).to_f))
45
+ y_coord = (height - (height * relative_percent))
46
+ [x_coord, y_coord]
47
+ end
48
+ coords
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,123 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::Pie
3
+ #
4
+ # Author:: A.J. Ostman
5
+ # Date:: August 15, 2006
6
+ #
7
+ # Provides a container for pie slice.
8
+ class Pie < Base
9
+ include Scruffy::Helpers::LayerContainer
10
+
11
+ # Basic Example:
12
+ #
13
+ # graph = Scruffy::Graph.new
14
+ # graph.title = "Snack Preference"
15
+ # graph.renderer = Scruffy::Renderers::Pie.new
16
+ # graph.add :pie, 'Example', {'Apples' => 90, 'Orange' => 60, 'Taco' => 30}
17
+ #
18
+ # Or, using a block to add slices:
19
+ #
20
+ # graph = Scruffy::Graph.new
21
+ # graph.title = "Snack Preference"
22
+ # graph.renderer = Scruffy::Renderers::Pie.new
23
+ # graph.add :pie do |pie|
24
+ # pie.add :pie_slice, 'Apple', [90]
25
+ # pie.add :pie_slice, 'Orange', [60]
26
+ # pie.add :pie_slice, 'Taco', [30]
27
+ # end
28
+ #
29
+ # Another Example:
30
+ # graph.title = "Scruff-Pac!"
31
+ # graph.renderer = Scruffy::Renderers::Pie.new
32
+ # graph.add :pie, :diameter => 40, :degree_offset => 30 do |pie|
33
+ # pie.add :pie_slice, '', [160], :preferred_color => "yellow", :shadow => true,
34
+ # :shadow_x => -1, :shadow_y => 1, :shadow_color=>"black", :shadow_opacity => 0.4
35
+ # pie.add :pie_slice, '', [50], :preferred_color => "green", :explode => 5, :diameter => 20,
36
+ # :shadow => true, :shadow_x => -1, :shadow_y => 1, :shadow_color => "black", :shadow_opacity => 0.4
37
+ # end
38
+ #
39
+ # graph.add :pie, :diameter => 3, :center_x => 48, :center_y=> 37, :degree_offset => 20 do |pie|
40
+ # pie.add :pie_slice, '', [160], :preferred_color => "blue", :stroke => "black"
41
+ # end
42
+
43
+
44
+ # Setup Constants
45
+ RADIANS = Math::PI/180
46
+
47
+ attr_accessor :diameter
48
+ attr_accessor :percent_used
49
+ attr_accessor :degree_offset
50
+ attr_accessor :scaler
51
+ attr_accessor :center_x, :center_y
52
+
53
+
54
+ # The initialize method passes itself to the block, and since Pie is a
55
+ # LayerContainer, layers (pie slice) can be added just as if they were being
56
+ # added to Graph.
57
+ def initialize(options = {}, &block)
58
+ super(options)
59
+
60
+ # Allow for population of data with a block during initialization.
61
+ if block
62
+ block.call(self)
63
+ else
64
+ # Otherwise, just iterate over the points, adding the slices
65
+ if @points.class == Hash
66
+ @points.keys.each {|k|
67
+ self.add :pie_slice, k.to_s, [@points[k]]}
68
+ end
69
+ if @points.class == Array
70
+ @points.each {|v|
71
+ self.add :pie_slice, '', [v]}
72
+ end
73
+ end
74
+ end
75
+
76
+
77
+ # Overrides Base#render to fiddle with layers' points to achieve a stacked
78
+ # effect.
79
+ def render(svg, options = {})
80
+ # #current_points = points.dup
81
+
82
+ @scaler = 1
83
+ total = 0
84
+
85
+ layers.each do |layer|
86
+ total += layer.sum_values
87
+ end
88
+
89
+ @scaler = 100.0 / total
90
+
91
+ @percent_used = 30
92
+
93
+ layers.each do |layer|
94
+ layer_options = options.dup
95
+ layer_options = layer_options.merge(@options)
96
+ layer_options = layer_options.merge(layer.options)
97
+ layer_options[:scaler] = @scaler
98
+ layer_options[:percent_used] = @percent_used
99
+ @percent_used += @scaler * layer.sum_values
100
+ layer_options[:color] = layer.preferred_color || layer.color || options[:theme].next_color
101
+
102
+ layer.render(svg, layer_options)
103
+ end
104
+ end
105
+
106
+ # A stacked graph has many data sets. Return legend information for all of them.
107
+ def legend_data
108
+ if relevant_data?
109
+ retval = []
110
+ layers.each do |layer|
111
+ retval << layer.legend_data
112
+ end
113
+ retval
114
+ else
115
+ nil
116
+ end
117
+ end
118
+
119
+ def points=(val)
120
+ throw ArgumentsError, "Pie layers cannot accept points, only pie slices."
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,119 @@
1
+ module Scruffy::Layers
2
+ # ==Scruffy::Layers::PieSlice
3
+ #
4
+ # Author:: A.J. Ostman
5
+ # Date:: August 14th, 2006
6
+ #
7
+ # Basic Pie Chart Slice..
8
+
9
+ class PieSlice < Base
10
+
11
+ # Setup Constants
12
+ RADIANS = Math::PI / 180
13
+ MARKER_OFFSET_RATIO = 1.2
14
+ MARKER_FONT_SIZE = 6
15
+
16
+ attr_accessor :diameter
17
+ attr_accessor :percent_used
18
+ attr_accessor :degree_offset
19
+ attr_accessor :scaler
20
+ attr_accessor :center_x, :center_y
21
+
22
+ def draw(svg, coords, options = {})
23
+ # Scaler is the multiplier to normalize the values to a percentage across
24
+ # the Pie Chart
25
+ @scaler = options[:scaler] || 1
26
+
27
+ # Degree Offset is degrees by which the pie chart is twisted when it
28
+ # begins
29
+ @degree_offset = options[:degree_offset] || @options[:degree_offset] || 0
30
+
31
+ # Percent Used keeps track of where in the pie chart we are
32
+ @percent_used = options[:percent_used] || @options[:percent_used] || 0
33
+
34
+ # Diameter of the pie chart defaults to 80% of the height
35
+ @diameter = relative(options[:diameter]) || relative(@options[:diameter]) || relative(80.0)
36
+
37
+ # Stroke
38
+ stroke = options[:stroke] || @options[:stroke] || "none"
39
+
40
+ # Shadow
41
+ shadow = options[:shadow] || @options[:shadow_] || false
42
+ shadow_x = relative(options[:shadow_x]) || relative(@options[:shadow_x]) || relative(-0.5)
43
+ shadow_y = relative(options[:shadow_y]) || relative(@options[:shadow_y]) || relative(0.5)
44
+ shadow_color = options[:shadow_color] || @options[:shadow_color] || "white"
45
+ shadow_opacity = options[:shadow_opacity] || @options[:shadow_opacity] || 0.06
46
+
47
+ # Coordinates for the center of the pie chart.
48
+ @center_x = relative_width(options[:center_x]) || relative_width(@options[:center_x]) || relative_width(50)
49
+ @center_y = relative_height(options[:center_y]) || relative_height(@options[:center_y]) || relative_height(50)
50
+ radius = @diameter / 2.0
51
+
52
+ # Graphing calculated using percent of graph. We later multiply by 3.6 to
53
+ # convert to 360 degree system.
54
+ percent = @scaler * sum_values
55
+
56
+
57
+ # Calculate the Radian Start Point
58
+ radians_start = ((@percent_used * 3.6) + @degree_offset) * RADIANS
59
+ # Calculate the Radian End Point
60
+ radians_end = ((@percent_used + percent) * 3.6 + @degree_offset) * RADIANS
61
+
62
+ radians_mid_point = radians_start + ((radians_end - radians_start) / 2)
63
+
64
+ if options[:explode]
65
+ @center_x = @center_x + (Math.sin(radians_mid_point) * relative(options[:explode]))
66
+ @center_y = @center_y - (Math.cos(radians_mid_point) * relative(options[:explode]))
67
+ end
68
+
69
+
70
+ # Calculate the beginning coordinates
71
+ x_start = @center_x + (Math.sin(radians_start) * radius)
72
+ y_start = @center_y - (Math.cos(radians_start) * radius)
73
+
74
+ # Calculate the End Coords
75
+ x_end = @center_x + (Math.sin(radians_end) * radius)
76
+ y_end = @center_y - (Math.cos(radians_end) * radius)
77
+
78
+
79
+
80
+ # If percentage is really really close to 100% then draw a circle instead!
81
+ if percent >= 99.9999
82
+
83
+ if shadow
84
+ svg.circle(:cx => "#{@center_x + shadow_x}", :cy => "#{@center_y + shadow_y}", :r=>"#{radius}",:stroke => "none",
85
+ :fill => shadow_color.to_s, :style => "fill-opacity: #{shadow_opacity.to_s};")
86
+ end
87
+
88
+ svg.circle(:cx => "#{@center_x}", :cy => "#{@center_y}", :r=>"#{radius}",:stroke => stroke, :fill => color.to_s)
89
+
90
+ else
91
+ if shadow
92
+ svg.path(:d => "M#{@center_x + shadow_x},#{@center_y + shadow_y} L#{x_start + shadow_x},#{y_start + shadow_y} A#{radius},#{radius} 0, #{percent >= 50 ? '1' : '0'}, 1, #{x_end + shadow_x} #{y_end + shadow_y} Z",
93
+ :fill => shadow_color.to_s, :style => "fill-opacity: #{shadow_opacity.to_s};")
94
+ end
95
+
96
+ svg.path(:d => "M#{@center_x},#{@center_y} L#{x_start},#{y_start} A#{radius},#{radius} 0, #{percent >= 50 ? '1' : '0'}, 1, #{x_end} #{y_end} Z",
97
+ :stroke => stroke, :fill => color.to_s)
98
+ end
99
+
100
+ text_x = @center_x + (Math.sin(radians_mid_point) * radius * MARKER_OFFSET_RATIO)
101
+ text_y = @center_y - (Math.cos(radians_mid_point) * radius * MARKER_OFFSET_RATIO)
102
+
103
+ svg.text("#{sprintf('%d', percent)}%",
104
+ :x => text_x,
105
+ :y => text_y + relative(MARKER_FONT_SIZE / 2),
106
+ 'font-size' => relative(MARKER_FONT_SIZE),
107
+ 'font-family' => options[:theme].font_family,
108
+ :fill => (options[:theme].marker || 'black').to_s,
109
+ 'text-anchor' => 'middle')
110
+ end
111
+
112
+ protected
113
+ def generate_coordinates(options = {})
114
+ # Coordinate Generation didn't make much sense here. Overridden just
115
+ # because Brasten said this would be overridden.
116
+ end
117
+ end
118
+
119
+ end