fix security issue
[selectricity] / lib / gruff-0.2.8 / lib / gruff / pie.rb
1
2 require File.dirname(__FILE__) + '/base'
3
4 class Gruff::Pie < Gruff::Base
5
6   TEXT_OFFSET_PERCENTAGE = 0.15
7
8   # Can be used to make the pie start cutting slices at the top (-90.0)
9   # or at another angle. Default is 0.0, which starts at 3 o'clock.
10   attr_accessor :zero_degree
11   
12   def initialize_ivars
13     super
14     @zero_degree = 0.0
15   end
16
17   def draw
18     @hide_line_markers = true
19     
20     super
21
22     return unless @has_data
23
24     diameter = @graph_height
25     radius = ([@graph_width, @graph_height].min / 2.0) * 0.8
26     top_x = @graph_left + (@graph_width - diameter) / 2.0
27     center_x = @graph_left + (@graph_width / 2.0)
28     center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
29     total_sum = sums_for_pie()
30     prev_degrees = @zero_degree
31
32     # Use full data since we can easily calculate percentages
33     @data.sort{ |a, b| a[DATA_VALUES_INDEX][0] <=> b[DATA_VALUES_INDEX][0] }.each do |data_row|
34       if data_row[DATA_VALUES_INDEX][0] > 0
35         @d = @d.stroke data_row[DATA_COLOR_INDEX]
36         @d = @d.fill 'transparent'
37         @d.stroke_width(radius) # stroke width should be equal to radius. we'll draw centered on (radius / 2)
38
39         current_degrees = (data_row[DATA_VALUES_INDEX][0] / total_sum) * 360.0 
40
41         # ellipse will draw the the stroke centered on the first two parameters offset by the second two.
42         # therefore, in order to draw a circle of the proper diameter we must center the stroke at
43         # half the radius for both x and y
44         @d = @d.ellipse(center_x, center_y, 
45                   radius / 2.0, radius / 2.0,
46                   prev_degrees, prev_degrees + current_degrees + 0.5) # <= +0.5 'fudge factor' gets rid of the ugly gaps
47                   
48         half_angle = prev_degrees + ((prev_degrees + current_degrees) - prev_degrees) / 2
49
50         # End the string with %% to escape the single %.
51         # RMagick must use sprintf with the string and % has special significance.
52         label_string = ((data_row[DATA_VALUES_INDEX][0] / total_sum) * 100.0).round.to_s + '%%'
53         @d = draw_label(center_x,center_y, 
54                     half_angle, radius + (radius * TEXT_OFFSET_PERCENTAGE), 
55                     label_string)
56       
57         prev_degrees += current_degrees
58       end
59     end
60
61     # TODO debug a circle where the text is drawn...
62     
63     @d.draw(@base_image)
64   end
65
66 private
67
68   ##
69   # Labels are drawn around a slightly wider ellipse to give room for 
70   # labels on the left and right.
71   def draw_label(center_x, center_y, angle, radius, amount)
72     # TODO Don't use so many hard-coded numbers
73     r_offset = 20.0      # The distance out from the center of the pie to get point
74     x_offset = center_x # + 15.0 # The label points need to be tweaked slightly
75     y_offset = center_y  # This one doesn't though
76     radius_offset = (radius + r_offset)
77     ellipse_factor = radius_offset * 0.15
78     x = x_offset + ((radius_offset + ellipse_factor) * Math.cos(angle.deg2rad))
79     y = y_offset + (radius_offset * Math.sin(angle.deg2rad))
80     
81     # Draw label
82     @d.fill = @font_color
83     @d.font = @font if @font
84     @d.pointsize = scale_fontsize(@marker_font_size)
85     @d.stroke = 'transparent'
86     @d.font_weight = BoldWeight
87     @d.gravity = CenterGravity
88     @d.annotate_scaled( @base_image, 
89                       0, 0,
90                       x, y, 
91                       amount, @scale)
92   end
93
94   def sums_for_pie
95     total_sum = 0.0
96     @data.collect {|data_row| total_sum += data_row[DATA_VALUES_INDEX][0] }
97     total_sum
98   end
99
100 end
101
102
103 class Float
104   # Used for degree => radian conversions
105   def deg2rad
106     self * (Math::PI/180.0)
107   end
108 end

Benjamin Mako Hill || Want to submit a patch?