gruff 0.3.4 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.3.6
2
+
3
+ * Fixed manifest to list dot graph [theirishpenguin]
4
+ * Fixed color cycling error [Gunnar Wolf]
5
+ * Handle case where a line graph data set only has one value [Ron Colwill]
6
+
7
+ == 0.3.5
8
+
9
+ * Added dot graph from Erik Andrejko
10
+
1
11
  == 0.3.4
2
12
 
3
13
  * Reverted DEBUG=true. Will add a check in the release process so this doesn't happen again.
data/Manifest.txt CHANGED
@@ -39,6 +39,7 @@ lib/gruff/bar_conversion.rb
39
39
  lib/gruff/base.rb
40
40
  lib/gruff/bullet.rb
41
41
  lib/gruff/deprecated.rb
42
+ lib/gruff/dot.rb
42
43
  lib/gruff/line.rb
43
44
  lib/gruff/mini/bar.rb
44
45
  lib/gruff/mini/legend.rb
@@ -63,6 +64,7 @@ test/test_area.rb
63
64
  test/test_bar.rb
64
65
  test/test_base.rb
65
66
  test/test_bullet.rb
67
+ test/test_dot.rb
66
68
  test/test_legend.rb
67
69
  test/test_line.rb
68
70
  test/test_mini_bar.rb
data/Rakefile CHANGED
@@ -21,6 +21,7 @@ task :verify => :package do
21
21
  if system %(ruby -e "require 'pkg/gruff-#{Gruff::VERSION}/lib/gruff'")
22
22
  puts "\nThe library files are present"
23
23
  end
24
+ raise "\n*** Gruff::Base::DEBUG must be set to false for releases ***\n\n" if Gruff::Base::DEBUG
24
25
  end
25
26
 
26
27
  task :release => :verify
data/lib/gruff.rb CHANGED
@@ -5,6 +5,7 @@
5
5
  area
6
6
  bar
7
7
  line
8
+ dot
8
9
  pie
9
10
  spider
10
11
  net
@@ -24,4 +25,4 @@
24
25
  require File.dirname(__FILE__) + "/gruff/#{filename}"
25
26
  end
26
27
 
27
- # TODO bullet
28
+ # TODO bullet
data/lib/gruff/bar.rb CHANGED
@@ -3,6 +3,9 @@ require File.dirname(__FILE__) + '/bar_conversion'
3
3
 
4
4
  class Gruff::Bar < Gruff::Base
5
5
 
6
+ # Spacing factor applied between bars
7
+ attr_accessor :bar_spacing
8
+
6
9
  def draw
7
10
  # Labels will be centered over the left of the bar if
8
11
  # there are more labels than columns. This is basically the same
@@ -21,8 +24,9 @@ protected
21
24
  # Setup spacing.
22
25
  #
23
26
  # Columns sit side-by-side.
24
- spacing_factor = 0.9 # space between the bars
27
+ @bar_spacing ||= 0.9 # space between the bars
25
28
  @bar_width = @graph_width / (@column_count * @data.length).to_f
29
+ padding = (@bar_width * (1 - @bar_spacing)) / 2
26
30
 
27
31
  @d = @d.stroke_opacity 0.0
28
32
 
@@ -54,8 +58,8 @@ protected
54
58
  data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
55
59
  # Use incremented x and scaled y
56
60
  # x
57
- left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index)))
58
- right_x = left_x + @bar_width * spacing_factor
61
+ left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index))) + padding
62
+ right_x = left_x + @bar_width * @bar_spacing
59
63
  # y
60
64
  conv = []
61
65
  conversion.getLeftYRightYscaled( data_point, conv )
data/lib/gruff/base.rb CHANGED
@@ -20,7 +20,7 @@ require File.dirname(__FILE__) + '/deprecated'
20
20
  module Gruff
21
21
 
22
22
  # This is the version of Gruff you are using.
23
- VERSION = '0.3.4'
23
+ VERSION = '0.3.6'
24
24
 
25
25
  class Base
26
26
 
@@ -36,9 +36,13 @@ module Gruff
36
36
  DATA_COLOR_INDEX = 2
37
37
 
38
38
  # Space around text elements. Mostly used for vertical spacing
39
- LEGEND_MARGIN = TITLE_MARGIN = LABEL_MARGIN = 10.0
39
+ LEGEND_MARGIN = TITLE_MARGIN = 20.0
40
+ LABEL_MARGIN = 10.0
41
+ DEFAULT_MARGIN = 20.0
40
42
 
41
43
  DEFAULT_TARGET_WIDTH = 800
44
+
45
+ THOUSAND_SEPARATOR = ','
42
46
 
43
47
  # Blank space above the graph
44
48
  attr_accessor :top_margin
@@ -51,6 +55,12 @@ module Gruff
51
55
 
52
56
  # Blank space to the left of the graph
53
57
  attr_accessor :left_margin
58
+
59
+ # Blank space below the title
60
+ attr_accessor :title_margin
61
+
62
+ # Blank space below the legend
63
+ attr_accessor :legend_margin
54
64
 
55
65
  # A hash of names for the individual columns, where the key is the array
56
66
  # index for the column this label represents.
@@ -170,8 +180,6 @@ module Gruff
170
180
  # Looks for Bitstream Vera as the default font. Expects an environment var
171
181
  # of MAGICK_FONT_PATH to be set. (Uses RMagick's default font otherwise.)
172
182
  def initialize(target_width=DEFAULT_TARGET_WIDTH)
173
- @top_margin = @bottom_margin = @left_margin = @right_margin = 20.0
174
-
175
183
  if not Numeric === target_width
176
184
  geometric_width, geometric_height = target_width.split('x')
177
185
  @columns = geometric_width.to_f
@@ -215,6 +223,10 @@ module Gruff
215
223
  @marker_font_size = 21.0
216
224
  @legend_font_size = 20.0
217
225
  @title_font_size = 36.0
226
+
227
+ @top_margin = @bottom_margin = @left_margin = @right_margin = DEFAULT_MARGIN
228
+ @legend_margin = LEGEND_MARGIN
229
+ @title_margin = TITLE_MARGIN
218
230
 
219
231
  @legend_box_size = 20.0
220
232
 
@@ -253,14 +265,23 @@ module Gruff
253
265
  @colors << colorname
254
266
  end
255
267
 
256
- # Replace the entire color list with a new array of colors. You need to
257
- # have one more color than the number of datasets you intend to draw. Also
268
+ # Replace the entire color list with a new array of colors. Also
258
269
  # aliased as the colors= setter method.
259
270
  #
271
+ # If you specify fewer colors than the number of datasets you intend
272
+ # to draw, 'increment_color' will cycle through the array, reusing
273
+ # colors as needed.
274
+ #
275
+ # Note that (as with the 'theme' method), you should set up your color
276
+ # list before you send your data (via the 'data' method). Calls to the
277
+ # 'data' method made prior to this call will use whatever color scheme
278
+ # was in place at the time data was called.
279
+ #
260
280
  # Example:
261
281
  # replace_colors ['#cc99cc', '#d9e043', '#34d8a2']
262
282
  def replace_colors(color_list=[])
263
283
  @colors = color_list
284
+ @color_index = 0
264
285
  end
265
286
 
266
287
  # You can set a theme manually. Assign a hash to this method before you
@@ -382,14 +403,14 @@ module Gruff
382
403
  def theme_pastel
383
404
  # Colors
384
405
  @colors = [
385
- '#a9dada', # blue
386
- '#aedaa9', # green
387
- '#daaea9', # peach
388
- '#dadaa9', # yellow
389
- '#a9a9da', # dk purple
390
- '#daaeda', # purple
391
- '#dadada' # grey
392
- ]
406
+ '#a9dada', # blue
407
+ '#aedaa9', # green
408
+ '#daaea9', # peach
409
+ '#dadaa9', # yellow
410
+ '#a9a9da', # dk purple
411
+ '#daaeda', # purple
412
+ '#dadada' # grey
413
+ ]
393
414
 
394
415
  self.theme = {
395
416
  :colors => @colors,
@@ -403,13 +424,13 @@ module Gruff
403
424
  def theme_greyscale
404
425
  # Colors
405
426
  @colors = [
406
- '#282828', #
407
- '#383838', #
408
- '#686868', #
409
- '#989898', #
410
- '#c8c8c8', #
411
- '#e8e8e8', #
412
- ]
427
+ '#282828', #
428
+ '#383838', #
429
+ '#686868', #
430
+ '#989898', #
431
+ '#c8c8c8', #
432
+ '#e8e8e8', #
433
+ ]
413
434
 
414
435
  self.theme = {
415
436
  :colors => @colors,
@@ -451,7 +472,7 @@ module Gruff
451
472
  # TODO Doesn't work with stacked bar graphs
452
473
  # Original: @maximum_value = larger_than_max?(data_point, index) ? max(data_point, index) : @maximum_value
453
474
  @maximum_value = larger_than_max?(data_point) ? data_point : @maximum_value
454
- @has_data = true if @maximum_value > 0
475
+ @has_data = true if @maximum_value >= 0
455
476
 
456
477
  @minimum_value = less_than_min?(data_point) ? data_point : @minimum_value
457
478
  @has_data = true if @minimum_value < 0
@@ -489,7 +510,7 @@ module Gruff
489
510
  debug {
490
511
  # Outer margin
491
512
  @d.rectangle( @left_margin, @top_margin,
492
- @raw_columns - @right_margin, @raw_rows - @bottom_margin)
513
+ @raw_columns - @right_margin, @raw_rows - @bottom_margin)
493
514
  # Graph area box
494
515
  @d.rectangle( @graph_left, @graph_top, @graph_right, @graph_bottom)
495
516
  }
@@ -549,58 +570,58 @@ module Gruff
549
570
 
550
571
  def setup_graph_measurements
551
572
  @marker_caps_height = @hide_line_markers ? 0 :
552
- calculate_caps_height(@marker_font_size)
573
+ calculate_caps_height(@marker_font_size)
553
574
  @title_caps_height = @hide_title ? 0 :
554
- calculate_caps_height(@title_font_size)
575
+ calculate_caps_height(@title_font_size)
555
576
  @legend_caps_height = @hide_legend ? 0 :
556
- calculate_caps_height(@legend_font_size)
577
+ calculate_caps_height(@legend_font_size)
557
578
 
558
579
  if @hide_line_markers
559
580
  (@graph_left,
560
- @graph_right_margin,
561
- @graph_bottom_margin) = [@left_margin, @right_margin, @bottom_margin]
581
+ @graph_right_margin,
582
+ @graph_bottom_margin) = [@left_margin, @right_margin, @bottom_margin]
562
583
  else
563
584
  longest_left_label_width = 0
564
585
  if @has_left_labels
565
586
  longest_left_label_width = calculate_width(@marker_font_size,
566
- labels.values.inject('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
587
+ labels.values.inject('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
567
588
  else
568
589
  longest_left_label_width = calculate_width(@marker_font_size,
569
- label(@maximum_value.to_f))
590
+ label(@maximum_value.to_f))
570
591
  end
571
592
 
572
593
  # Shift graph if left line numbers are hidden
573
594
  line_number_width = @hide_line_numbers && !@has_left_labels ?
574
595
  0.0 :
575
- (longest_left_label_width + LABEL_MARGIN * 2)
596
+ (longest_left_label_width + LABEL_MARGIN * 2)
576
597
 
577
598
  @graph_left = @left_margin +
578
- line_number_width +
579
- (@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
599
+ line_number_width +
600
+ (@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
580
601
 
581
602
  # Make space for half the width of the rightmost column label.
582
603
  # Might be greater than the number of columns if between-style bar markers are used.
583
604
  last_label = @labels.keys.sort.last.to_i
584
605
  extra_room_for_long_label = (last_label >= (@column_count-1) && @center_labels_over_point) ?
585
606
  calculate_width(@marker_font_size, @labels[last_label]) / 2.0 :
586
- 0
607
+ 0
587
608
  @graph_right_margin = @right_margin + extra_room_for_long_label
588
609
 
589
610
  @graph_bottom_margin = @bottom_margin +
590
- @marker_caps_height + LABEL_MARGIN
611
+ @marker_caps_height + LABEL_MARGIN
591
612
  end
592
613
 
593
614
  @graph_right = @raw_columns - @graph_right_margin
594
615
  @graph_width = @raw_columns - @graph_left - @graph_right_margin
595
616
 
596
- # When @hide title, leave a TITLE_MARGIN space for aesthetics.
617
+ # When @hide title, leave a title_margin space for aesthetics.
597
618
  # Same with @hide_legend
598
619
  @graph_top = @top_margin +
599
- (@hide_title ? TITLE_MARGIN : @title_caps_height + TITLE_MARGIN * 2) +
600
- (@hide_legend ? LEGEND_MARGIN : @legend_caps_height + LEGEND_MARGIN * 2)
620
+ (@hide_title ? title_margin : @title_caps_height + title_margin ) +
621
+ (@hide_legend ? legend_margin : @legend_caps_height + legend_margin)
601
622
 
602
623
  x_axis_label_height = @x_axis_label.nil? ? 0.0 :
603
- @marker_caps_height + LABEL_MARGIN
624
+ @marker_caps_height + LABEL_MARGIN
604
625
  @graph_bottom = @raw_rows - @graph_bottom_margin - x_axis_label_height
605
626
  @graph_height = @graph_bottom - @graph_top
606
627
  end
@@ -620,9 +641,9 @@ module Gruff
620
641
  @d.pointsize = scale_fontsize(@marker_font_size)
621
642
  @d.gravity = NorthGravity
622
643
  @d = @d.annotate_scaled( @base_image,
623
- @raw_columns, 1.0,
624
- 0.0, x_axis_label_y_coordinate,
625
- @x_axis_label, @scale)
644
+ @raw_columns, 1.0,
645
+ 0.0, x_axis_label_y_coordinate,
646
+ @x_axis_label, @scale)
626
647
  debug { @d.line 0.0, x_axis_label_y_coordinate, @raw_columns, x_axis_label_y_coordinate }
627
648
  end
628
649
 
@@ -631,9 +652,9 @@ module Gruff
631
652
  @d.rotation = 90.0
632
653
  @d.gravity = CenterGravity
633
654
  @d = @d.annotate_scaled( @base_image,
634
- 1.0, @raw_rows,
635
- @left_margin + @marker_caps_height / 2.0, 0.0,
636
- @y_axis_label, @scale)
655
+ 1.0, @raw_rows,
656
+ @left_margin + @marker_caps_height / 2.0, 0.0,
657
+ @y_axis_label, @scale)
637
658
  @d.rotation = -90.0
638
659
  end
639
660
  end
@@ -674,8 +695,7 @@ module Gruff
674
695
  (0..@marker_count).each do |index|
675
696
  y = @graph_top + @graph_height - index.to_f * @increment_scaled
676
697
 
677
- @d = @d.stroke(@marker_color)
678
- @d = @d.stroke_width 1
698
+ @d = @d.fill(@marker_color)
679
699
  @d = @d.line(@graph_left, y, @graph_right, y)
680
700
 
681
701
  marker_label = index * @increment + @minimum_value.to_f
@@ -689,9 +709,9 @@ module Gruff
689
709
 
690
710
  # Vertically center with 1.0 for the height
691
711
  @d = @d.annotate_scaled( @base_image,
692
- @graph_left - LABEL_MARGIN, 1.0,
693
- 0.0, y,
694
- label(marker_label), @scale)
712
+ @graph_left - LABEL_MARGIN, 1.0,
713
+ 0.0, y,
714
+ label(marker_label), @scale)
695
715
  end
696
716
  end
697
717
 
@@ -765,8 +785,8 @@ module Gruff
765
785
 
766
786
  current_x_offset = center(sum(label_widths.first))
767
787
  current_y_offset = @hide_title ?
768
- @top_margin + LEGEND_MARGIN :
769
- @top_margin + TITLE_MARGIN + @title_caps_height + LEGEND_MARGIN
788
+ @top_margin + title_margin :
789
+ @top_margin + title_margin + @title_caps_height
770
790
 
771
791
  @legend_labels.each_with_index do |legend_label, index|
772
792
 
@@ -778,17 +798,17 @@ module Gruff
778
798
  @d.font_weight = NormalWeight
779
799
  @d.gravity = WestGravity
780
800
  @d = @d.annotate_scaled( @base_image,
781
- @raw_columns, 1.0,
782
- current_x_offset + (legend_square_width * 1.7), current_y_offset,
783
- legend_label.to_s, @scale)
801
+ @raw_columns, 1.0,
802
+ current_x_offset + (legend_square_width * 1.7), current_y_offset,
803
+ legend_label.to_s, @scale)
784
804
 
785
805
  # Now draw box with color of this dataset
786
806
  @d = @d.stroke('transparent')
787
807
  @d = @d.fill @data[index][DATA_COLOR_INDEX]
788
808
  @d = @d.rectangle(current_x_offset,
789
- current_y_offset - legend_square_width / 2.0,
790
- current_x_offset + legend_square_width,
791
- current_y_offset + legend_square_width / 2.0)
809
+ current_y_offset - legend_square_width / 2.0,
810
+ current_x_offset + legend_square_width,
811
+ current_y_offset + legend_square_width / 2.0)
792
812
 
793
813
  @d.pointsize = @legend_font_size
794
814
  metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
@@ -801,7 +821,7 @@ module Gruff
801
821
 
802
822
  label_widths.shift
803
823
  current_x_offset = center(sum(label_widths.first)) unless label_widths.empty?
804
- line_height = [@legend_caps_height, legend_square_width].max + LEGEND_MARGIN
824
+ line_height = [@legend_caps_height, legend_square_width].max + legend_margin
805
825
  if label_widths.length > 0
806
826
  # Wrap to next line and shrink available graph dimensions
807
827
  current_y_offset += line_height
@@ -826,9 +846,9 @@ module Gruff
826
846
  @d.font_weight = BoldWeight
827
847
  @d.gravity = NorthGravity
828
848
  @d = @d.annotate_scaled( @base_image,
829
- @raw_columns, 1.0,
830
- 0, @top_margin,
831
- @title, @scale)
849
+ @raw_columns, 1.0,
850
+ 0, @top_margin,
851
+ @title, @scale)
832
852
  end
833
853
 
834
854
  # Draws column labels below graph, centered over x_offset
@@ -847,9 +867,9 @@ module Gruff
847
867
  @d.pointsize = scale_fontsize(@marker_font_size)
848
868
  @d.gravity = NorthGravity
849
869
  @d = @d.annotate_scaled(@base_image,
850
- 1.0, 1.0,
851
- x_offset, y_offset,
852
- @labels[index], @scale)
870
+ 1.0, 1.0,
871
+ x_offset, y_offset,
872
+ @labels[index], @scale)
853
873
  @labels_seen[index] = 1
854
874
  debug { @d.line 0.0, y_offset, @raw_columns, y_offset }
855
875
  end
@@ -864,9 +884,9 @@ module Gruff
864
884
  @d.pointsize = scale_fontsize(80)
865
885
  @d.gravity = CenterGravity
866
886
  @d = @d.annotate_scaled( @base_image,
867
- @raw_columns, @raw_rows/2.0,
868
- 0, 10,
869
- @no_data_message, @scale)
887
+ @raw_columns, @raw_rows/2.0,
888
+ 0, 10,
889
+ @no_data_message, @scale)
870
890
  end
871
891
 
872
892
  # Finds the best background to render based on the provided theme options.
@@ -893,7 +913,7 @@ module Gruff
893
913
  # Use with a theme definition method to draw a gradiated background.
894
914
  def render_gradiated_background(top_color, bottom_color)
895
915
  Image.new(@columns, @rows,
896
- GradientFill.new(0, 0, 100, 0, top_color, bottom_color))
916
+ GradientFill.new(0, 0, 100, 0, top_color, bottom_color))
897
917
  end
898
918
 
899
919
  # Use with a theme to use an image (800x600 original) background.
@@ -1034,37 +1054,28 @@ module Gruff
1034
1054
  end
1035
1055
  end
1036
1056
 
1037
- # Uses the next color in your color list.
1057
+ # Returns the next color in your color list.
1038
1058
  def increment_color
1039
- if @color_index == 0
1040
- @color_index += 1
1041
- return @colors[0]
1042
- else
1043
- if @color_index < @colors.length
1044
- @color_index += 1
1045
- return @colors[@color_index - 1]
1046
- else
1047
- # Start over
1048
- @color_index = 0
1049
- return @colors[-1]
1050
- end
1051
- end
1059
+ @color_index = (@color_index + 1) % @colors.length
1060
+ return @colors[@color_index - 1]
1052
1061
  end
1053
1062
 
1054
1063
  # Return a formatted string representing a number value that should be
1055
1064
  # printed as a label.
1056
1065
  def label(value)
1057
- if (@spread.to_f % @marker_count.to_f == 0) || !@y_axis_increment.nil?
1058
- return value.to_i.to_s
1059
- end
1060
-
1061
- if @spread > 10.0
1066
+ label = if (@spread.to_f % @marker_count.to_f == 0) || !@y_axis_increment.nil?
1067
+ value.to_i.to_s
1068
+ elsif @spread > 10.0
1062
1069
  sprintf("%0i", value)
1063
1070
  elsif @spread >= 3.0
1064
1071
  sprintf("%0.2f", value)
1065
1072
  else
1066
1073
  value.to_s
1067
1074
  end
1075
+
1076
+ parts = label.split('.')
1077
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{THOUSAND_SEPARATOR}")
1078
+ parts.join('.')
1068
1079
  end
1069
1080
 
1070
1081
  # Returns the height of the capital letter 'X' for the current font and
@@ -1102,9 +1113,9 @@ module Magick
1102
1113
  scaled_height = (height * scale) >= 1 ? (height * scale) : 1
1103
1114
 
1104
1115
  self.annotate( img,
1105
- scaled_width, scaled_height,
1106
- x * scale, y * scale,
1107
- text)
1116
+ scaled_width, scaled_height,
1117
+ x * scale, y * scale,
1118
+ text)
1108
1119
  end
1109
1120
 
1110
1121
  end