gruff 0.3.4 → 0.3.6

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