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,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