gruff 0.16.0-java → 0.19.0-java

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +26 -0
  3. data/.github/workflows/ci.yml +9 -6
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +0 -6
  6. data/CHANGELOG.md +32 -0
  7. data/README.md +7 -3
  8. data/gruff.gemspec +2 -4
  9. data/lib/gruff/area.rb +3 -1
  10. data/lib/gruff/bar.rb +26 -24
  11. data/lib/gruff/base.rb +214 -93
  12. data/lib/gruff/bezier.rb +7 -8
  13. data/lib/gruff/{box_plot.rb → box.rb} +12 -6
  14. data/lib/gruff/bubble.rb +99 -0
  15. data/lib/gruff/candlestick.rb +22 -14
  16. data/lib/gruff/dot.rb +5 -6
  17. data/lib/gruff/helper/bar_conversion.rb +1 -5
  18. data/lib/gruff/helper/bar_mixin.rb +25 -0
  19. data/lib/gruff/helper/bar_value_label.rb +0 -22
  20. data/lib/gruff/helper/stacked_mixin.rb +16 -0
  21. data/lib/gruff/histogram.rb +8 -5
  22. data/lib/gruff/line.rb +47 -27
  23. data/lib/gruff/mini/bar.rb +1 -1
  24. data/lib/gruff/mini/legend.rb +13 -9
  25. data/lib/gruff/mini/pie.rb +2 -2
  26. data/lib/gruff/net.rb +26 -13
  27. data/lib/gruff/pie.rb +7 -2
  28. data/lib/gruff/renderer/bezier.rb +1 -1
  29. data/lib/gruff/renderer/circle.rb +5 -3
  30. data/lib/gruff/renderer/dash_line.rb +1 -1
  31. data/lib/gruff/renderer/dot.rb +26 -15
  32. data/lib/gruff/renderer/line.rb +1 -1
  33. data/lib/gruff/renderer/polygon.rb +1 -1
  34. data/lib/gruff/renderer/polyline.rb +4 -2
  35. data/lib/gruff/renderer/renderer.rb +0 -4
  36. data/lib/gruff/renderer/text.rb +7 -1
  37. data/lib/gruff/scatter.rb +52 -55
  38. data/lib/gruff/side_bar.rb +28 -24
  39. data/lib/gruff/side_stacked_bar.rb +36 -41
  40. data/lib/gruff/spider.rb +7 -2
  41. data/lib/gruff/stacked_area.rb +8 -3
  42. data/lib/gruff/stacked_bar.rb +48 -40
  43. data/lib/gruff/store/xy_data.rb +8 -9
  44. data/lib/gruff/store/xy_pointsizes_data.rb +60 -0
  45. data/lib/gruff/version.rb +1 -1
  46. data/lib/gruff.rb +3 -4
  47. metadata +9 -5
data/lib/gruff/base.rb CHANGED
@@ -45,14 +45,15 @@ module Gruff
45
45
  # Blank space below the legend. Default is +20+.
46
46
  attr_writer :legend_margin
47
47
 
48
- # A hash of names for the individual columns, where the key is the array
49
- # index for the column this label represents.
50
- #
51
- # Not all columns need to be named.
48
+ # Truncates labels if longer than max specified.
49
+ attr_writer :label_max_size
50
+
51
+ # How truncated labels visually appear if they exceed {#label_max_size=}.
52
52
  #
53
- # @example
54
- # { 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008 }
55
- attr_writer :labels
53
+ # - +:absolute+ - does not show trailing dots to indicate truncation. This is the default.
54
+ # - +:trailing_dots+ - shows trailing dots to indicate truncation (note that {#label_max_size=}
55
+ # must be greater than 3).
56
+ attr_writer :label_truncation_style
56
57
 
57
58
  # Set a label for the bottom of the graph.
58
59
  attr_writer :x_axis_label
@@ -72,25 +73,9 @@ module Gruff
72
73
  # Set increment of the horizontal marking lines.
73
74
  attr_writer :y_axis_increment
74
75
 
75
- # Height of staggering between labels (Bar graph only).
76
- attr_writer :label_stagger_height
77
-
78
- # Truncates labels if longer than max specified.
79
- attr_writer :label_max_size
80
-
81
- # How truncated labels visually appear if they exceed {#label_max_size=}.
82
- #
83
- # - +:absolute+ - does not show trailing dots to indicate truncation. This is the default.
84
- # - +:trailing_dots+ - shows trailing dots to indicate truncation (note that {#label_max_size=}
85
- # must be greater than 3).
86
- attr_writer :label_truncation_style
87
-
88
76
  # Get or set the list of colors that will be used to draw the bars or lines.
89
77
  attr_accessor :colors
90
78
 
91
- # Set the large title of the graph displayed at the top.
92
- attr_writer :title
93
-
94
79
  # Prevent drawing of line markers. Default is +false+.
95
80
  attr_writer :hide_line_markers
96
81
 
@@ -107,9 +92,6 @@ module Gruff
107
92
  # to +"No Data."+.
108
93
  attr_writer :no_data_message
109
94
 
110
- # Display the legend under the graph. Default is +false+.
111
- attr_writer :legend_at_bottom
112
-
113
95
  # Set the color of the auxiliary lines.
114
96
  attr_writer :marker_color
115
97
 
@@ -127,6 +109,9 @@ module Gruff
127
109
  # first. This does not affect the legend. Default is +false+.
128
110
  attr_writer :sorted_drawing
129
111
 
112
+ # Display the legend under the graph. Default is +false+.
113
+ attr_writer :legend_at_bottom
114
+
130
115
  # Optionally set the size of the colored box by each item in the legend.
131
116
  # Default is +20.0+.
132
117
  #
@@ -204,9 +189,9 @@ module Gruff
204
189
  @no_data_message = 'No Data'
205
190
 
206
191
  @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = @legend_at_bottom = false
207
- @label_stagger_height = 0
208
192
  @label_max_size = 0
209
193
  @label_truncation_style = :absolute
194
+ @label_rotation = 0
210
195
 
211
196
  @x_axis_increment = nil
212
197
  @x_axis_label = @y_axis_label = nil
@@ -217,6 +202,67 @@ module Gruff
217
202
  end
218
203
  protected :initialize_attributes
219
204
 
205
+ # A hash of names for the individual columns, where the key is the array
206
+ # index for the column this label represents.
207
+ # Not all columns need to be named with hash.
208
+ #
209
+ # Or, an array corresponding to the data values.
210
+ #
211
+ # @param labels [Hash, Array] the labels.
212
+ #
213
+ # @example
214
+ # g = Gruff::Bar.new
215
+ # g.labels = { 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008 }
216
+ #
217
+ # g = Gruff::Bar.new
218
+ # g.labels = ['2005', nil, nil, '2006', nil, nil, '2007', nil, nil, '2008'] # same labels for columns
219
+ def labels=(labels)
220
+ if labels.is_a?(Array)
221
+ labels = labels.each_with_index.each_with_object({}) do |(label, index), hash|
222
+ hash[index] = label
223
+ end
224
+ end
225
+
226
+ @labels = labels
227
+ end
228
+
229
+ # Set a rotation for labels. You can use Default is +0+.
230
+ # You can use a rotation between +0.0+ and +45.0+, or between +0.0+ and +-45.0+.
231
+ #
232
+ # @param rotation [Numeric] the rotation.
233
+ #
234
+ def label_rotation=(rotation)
235
+ raise ArgumentError, 'rotation must be between 0.0 and 45.0 or between 0.0 and -45.0' if rotation > 45.0 || rotation < -45.0
236
+
237
+ @label_rotation = rotation.to_f
238
+ end
239
+
240
+ # Height of staggering between labels.
241
+ # @deprecated
242
+ def label_stagger_height=(_value)
243
+ warn '#label_stagger_height= is deprecated. It is no longer effective.'
244
+ end
245
+
246
+ # Set the large title of the graph displayed at the top.
247
+ # You can draw a multi-line title by putting a line break in the string
248
+ # or by setting an array as argument.
249
+ #
250
+ # @param title [String, Array] the title.
251
+ #
252
+ # @example
253
+ # g = Gruff::Bar.new
254
+ # g.title = "The graph title"
255
+ #
256
+ # g = Gruff::Bar.new
257
+ # g.title = ['The first line of title', 'The second line of title']
258
+ def title=(title)
259
+ if title.is_a?(Array)
260
+ title = title.join("\n")
261
+ end
262
+
263
+ @title = title
264
+ end
265
+
220
266
  # Sets the top, bottom, left and right margins to +margin+.
221
267
  #
222
268
  # @param margin [Numeric] The margin size.
@@ -320,7 +366,8 @@ module Gruff
320
366
  # You can set a theme manually. Assign a hash to this method before you
321
367
  # send your data.
322
368
  #
323
- # graph.theme = {
369
+ # g = Gruff::Bar.new
370
+ # g.theme = {
324
371
  # colors: %w(orange purple green white red),
325
372
  # marker_color: 'blue',
326
373
  # background_colors: ['black', 'grey'],
@@ -357,7 +404,7 @@ module Gruff
357
404
  self.marker_color = @theme_options[:marker_color]
358
405
  self.font_color = @theme_options[:font_color] || @marker_color
359
406
 
360
- @colors = @theme_options[:colors]
407
+ @colors = @theme_options[:colors].dup
361
408
  @marker_shadow_color = @theme_options[:marker_shadow_color]
362
409
 
363
410
  @renderer = Gruff::Renderer.new(@columns, @rows, @scale, @theme_options)
@@ -422,7 +469,8 @@ module Gruff
422
469
  #
423
470
  # Set it after you have given all your data to the graph object.
424
471
  def minimum_value
425
- (@minimum_value || store.min).to_f
472
+ min = [0.0, store.min.to_f].min
473
+ (@minimum_value || min).to_f
426
474
  end
427
475
  attr_writer :minimum_value
428
476
 
@@ -503,11 +551,13 @@ module Gruff
503
551
  attr_reader :renderer
504
552
 
505
553
  # Perform data manipulation before calculating chart measurements
506
- def setup_data # :nodoc:
554
+ def setup_data
507
555
  if @y_axis_increment && !@hide_line_markers
508
556
  self.maximum_value = [@y_axis_increment, maximum_value, (maximum_value / @y_axis_increment).round * @y_axis_increment].max
509
557
  self.minimum_value = [minimum_value, (minimum_value / @y_axis_increment).round * @y_axis_increment].min
510
558
  end
559
+
560
+ sort_data if @sort # Sort data with avg largest values set first (for display)
511
561
  end
512
562
 
513
563
  # Calculates size of drawable area and generates normalized data.
@@ -518,7 +568,6 @@ module Gruff
518
568
  def setup_drawing
519
569
  calculate_spread
520
570
  calculate_increment
521
- sort_data if @sort # Sort data with avg largest values set first (for display)
522
571
  set_colors
523
572
  normalize
524
573
  setup_graph_measurements
@@ -559,8 +608,8 @@ module Gruff
559
608
  end
560
609
 
561
610
  def calculate_spread
562
- @spread = maximum_value - minimum_value
563
- @spread = @spread > 0 ? @spread : 1
611
+ @spread = maximum_value.to_f - minimum_value.to_f
612
+ @spread = @spread > 0 ? @spread : 1.0
564
613
  end
565
614
 
566
615
  def hide_title?
@@ -583,8 +632,7 @@ module Gruff
583
632
  # Calculates size of drawable area, general font dimensions, etc.
584
633
 
585
634
  def setup_graph_measurements
586
- margin_on_right = graph_right_margin
587
- @graph_right = @raw_columns - margin_on_right
635
+ @graph_right = setup_right_margin
588
636
  @graph_left = setup_left_margin
589
637
  @graph_top = setup_top_margin
590
638
  @graph_bottom = setup_bottom_margin
@@ -599,7 +647,7 @@ module Gruff
599
647
  # X Axis
600
648
  # Centered vertically and horizontally by setting the
601
649
  # height to 1.0 and the width to the width of the graph.
602
- x_axis_label_y_coordinate = @graph_bottom + (LABEL_MARGIN * 2) + marker_caps_height
650
+ x_axis_label_y_coordinate = @graph_bottom + (LABEL_MARGIN * 2) + labels_caps_height
603
651
 
604
652
  text_renderer = Gruff::Renderer::Text.new(renderer, @x_axis_label, font: @marker_font)
605
653
  text_renderer.add_to_render_queue(@raw_columns, 1.0, 0.0, x_axis_label_y_coordinate)
@@ -616,14 +664,12 @@ module Gruff
616
664
  def draw_line_markers
617
665
  return if @hide_line_markers
618
666
 
619
- increment_scaled = @graph_height / (@spread / @increment)
667
+ increment_scaled = (@graph_height / (@spread / @increment)).to_f
620
668
 
621
669
  # Draw horizontal line markers and annotate with numbers
622
670
  (0..marker_count).each do |index|
623
671
  y = @graph_top + @graph_height - (index * increment_scaled)
624
-
625
- Gruff::Renderer::Line.new(renderer, color: @marker_color).render(@graph_left, y, @graph_right, y)
626
- Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(@graph_left, y + 1, @graph_right, y + 1) if @marker_shadow_color
672
+ draw_marker_horizontal_line(y)
627
673
 
628
674
  unless @hide_line_numbers
629
675
  marker_label = (BigDecimal(index.to_s) * BigDecimal(@increment.to_s)) + BigDecimal(minimum_value.to_s)
@@ -634,6 +680,25 @@ module Gruff
634
680
  end
635
681
  end
636
682
 
683
+ def draw_marker_horizontal_line(y)
684
+ Gruff::Renderer::Line.new(renderer, color: @marker_color).render(@graph_left, y, @graph_right, y)
685
+ Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(@graph_left, y + 1, @graph_right, y + 1) if @marker_shadow_color
686
+ end
687
+
688
+ def draw_marker_vertical_line(x, tick_mark_mode: false)
689
+ if tick_mark_mode
690
+ Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_bottom + 5)
691
+ if @marker_shadow_color
692
+ Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_bottom + 5)
693
+ end
694
+ else
695
+ Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_top)
696
+ if @marker_shadow_color
697
+ Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_top)
698
+ end
699
+ end
700
+ end
701
+
637
702
  # Return a calculation of center
638
703
  def center(size)
639
704
  (@raw_columns - size) / 2
@@ -651,7 +716,7 @@ module Gruff
651
716
 
652
717
  current_y_offset = begin
653
718
  if @legend_at_bottom
654
- @graph_bottom + @legend_margin + legend_caps_height + LABEL_MARGIN + (@x_axis_label ? (LABEL_MARGIN * 2) + marker_caps_height : 0)
719
+ @graph_bottom + @legend_margin + labels_caps_height + LABEL_MARGIN + (@x_axis_label ? (LABEL_MARGIN * 2) + marker_caps_height : 0)
655
720
  else
656
721
  hide_title? ? @top_margin + @title_margin : @top_margin + @title_margin + title_caps_height
657
722
  end
@@ -702,14 +767,14 @@ module Gruff
702
767
  text_renderer.add_to_render_queue(@raw_columns, 1.0, 0, @top_margin)
703
768
  end
704
769
 
705
- # Draws column labels below graph, centered over x_offset
706
- def draw_label(x_offset, index, gravity = Magick::NorthGravity, &block)
770
+ # Draws column labels below graph, centered over x
771
+ def draw_label(x, index, gravity = Magick::NorthGravity, &block)
707
772
  draw_unique_label(index) do
708
- y_offset = @graph_bottom + LABEL_MARGIN
709
- y_offset += @label_stagger_height if index.odd?
773
+ if x >= @graph_left && x <= @graph_right
774
+ y = @graph_bottom
775
+ x_offset, y_offset = calculate_label_offset(@marker_font, @labels[index], LABEL_MARGIN, @label_rotation)
710
776
 
711
- if x_offset >= @graph_left && x_offset <= @graph_right
712
- draw_label_at(1.0, 1.0, x_offset, y_offset, @labels[index], gravity)
777
+ draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, @labels[index], gravity: gravity, rotation: @label_rotation)
713
778
  yield if block
714
779
  end
715
780
  end
@@ -725,17 +790,17 @@ module Gruff
725
790
  end
726
791
  end
727
792
 
728
- def draw_label_at(width, height, x, y, text, gravity = Magick::NorthGravity)
793
+ def draw_label_at(width, height, x, y, text, gravity: Magick::NorthGravity, rotation: 0)
729
794
  label_text = truncate_label_text(text)
730
- text_renderer = Gruff::Renderer::Text.new(renderer, label_text, font: @marker_font)
795
+ text_renderer = Gruff::Renderer::Text.new(renderer, label_text, font: @marker_font, rotation: rotation)
731
796
  text_renderer.add_to_render_queue(width, height, x, y, gravity)
732
797
  end
733
798
 
734
799
  # Draws the data value over the data point in bar graphs
735
- def draw_value_label(width, height, x_offset, y_offset, data_point, gravity = Magick::CenterGravity)
800
+ def draw_value_label(width, height, x_offset, y_offset, data_point, gravity: Magick::CenterGravity)
736
801
  return if @hide_line_markers
737
802
 
738
- draw_label_at(width, height, x_offset, y_offset, data_point, gravity)
803
+ draw_label_at(width, height, x_offset, y_offset, data_point, gravity: gravity)
739
804
  end
740
805
 
741
806
  # Shows an error message because you have no data.
@@ -756,20 +821,11 @@ module Gruff
756
821
  @theme_options = {}
757
822
  end
758
823
 
759
- def scale(value) # :nodoc:
760
- value * @scale
761
- end
762
-
763
- # Return a comparable fontsize for the current graph.
764
- def scale_fontsize(value)
765
- value * @scale
766
- end
767
-
768
- def clip_value_if_greater_than(value, max_value) # :nodoc:
824
+ def clip_value_if_greater_than(value, max_value)
769
825
  value > max_value ? max_value : value
770
826
  end
771
827
 
772
- def significant(i) # :nodoc:
828
+ def significant(i)
773
829
  return 1.0 if i == 0 # Keep from going into infinite loop
774
830
 
775
831
  inc = BigDecimal(i.to_s)
@@ -816,6 +872,10 @@ module Gruff
816
872
  hide_bottom_label_area? ? 0 : calculate_caps_height(@marker_font)
817
873
  end
818
874
 
875
+ def labels_caps_height
876
+ hide_bottom_label_area? ? 0 : calculate_labels_height(@marker_font)
877
+ end
878
+
819
879
  def title_caps_height
820
880
  hide_title? ? 0 : calculate_caps_height(@title_font) * @title.lines.to_a.size
821
881
  end
@@ -824,21 +884,6 @@ module Gruff
824
884
  @hide_legend ? 0 : calculate_caps_height(@legend_font)
825
885
  end
826
886
 
827
- def graph_right_margin
828
- @hide_line_markers ? @right_margin : @right_margin + extra_room_for_long_label
829
- end
830
-
831
- def extra_room_for_long_label
832
- # Make space for half the width of the rightmost column label.
833
- # Might be greater than the number of columns if between-style bar markers are used.
834
- last_label = @labels.keys.max.to_i
835
- if last_label >= (column_count - 1) && @center_labels_over_point
836
- calculate_width(@marker_font, truncate_label_text(@labels[last_label])) / 2.0
837
- else
838
- 0
839
- end
840
- end
841
-
842
887
  def setup_left_margin
843
888
  return @left_margin if hide_left_label_area?
844
889
 
@@ -851,10 +896,62 @@ module Gruff
851
896
  end
852
897
  longest_left_label_width = calculate_width(@marker_font, truncate_label_text(text))
853
898
 
854
- # Shift graph if left line numbers are hidden
855
- line_number_width = !@has_left_labels && (@hide_line_markers || @hide_line_numbers) ? 0.0 : (longest_left_label_width + LABEL_MARGIN)
899
+ line_number_width = begin
900
+ if !@has_left_labels && (@hide_line_markers || @hide_line_numbers)
901
+ 0.0
902
+ else
903
+ longest_left_label_width + LABEL_MARGIN
904
+ end
905
+ end
906
+ y_axis_label_width = @y_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2)
907
+
908
+ bottom_label_width = extra_left_room_for_long_label
909
+
910
+ margin = line_number_width + y_axis_label_width
911
+ @left_margin + (margin > bottom_label_width ? margin : bottom_label_width)
912
+ end
856
913
 
857
- @left_margin + line_number_width + (@y_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2))
914
+ def setup_right_margin
915
+ @raw_columns - (@hide_line_markers ? @right_margin : @right_margin + extra_right_room_for_long_label)
916
+ end
917
+
918
+ def extra_left_room_for_long_label
919
+ if require_extra_side_margin?
920
+ width = calculate_width(@marker_font, truncate_label_text(@labels[0]), rotation: @label_rotation)
921
+ case @label_rotation
922
+ when 0
923
+ width / 2.0
924
+ when 0..45
925
+ 0
926
+ when -45..0
927
+ width
928
+ end
929
+ else
930
+ 0
931
+ end
932
+ end
933
+
934
+ def extra_right_room_for_long_label
935
+ # Make space for half the width of the rightmost column label.
936
+ # Might be greater than the number of columns if between-style bar markers are used.
937
+ last_label = @labels.keys.max.to_i
938
+ if last_label >= (column_count - 1) && require_extra_side_margin?
939
+ width = calculate_width(@marker_font, truncate_label_text(@labels[last_label]), rotation: @label_rotation)
940
+ case @label_rotation
941
+ when 0
942
+ width / 2.0
943
+ when 0..45
944
+ width
945
+ when -45..0
946
+ 0
947
+ end
948
+ else
949
+ 0
950
+ end
951
+ end
952
+
953
+ def require_extra_side_margin?
954
+ !hide_bottom_label_area? && @center_labels_over_point
858
955
  end
859
956
 
860
957
  def setup_top_margin
@@ -866,11 +963,11 @@ module Gruff
866
963
  end
867
964
 
868
965
  def setup_bottom_margin
869
- graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin + marker_caps_height + LABEL_MARGIN
966
+ graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin + labels_caps_height + LABEL_MARGIN
870
967
  graph_bottom_margin += (calculate_legend_height + @legend_margin) if @legend_at_bottom
871
968
 
872
969
  x_axis_label_height = @x_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2)
873
- @raw_rows - graph_bottom_margin - x_axis_label_height - @label_stagger_height
970
+ @raw_rows - graph_bottom_margin - x_axis_label_height
874
971
  end
875
972
 
876
973
  def truncate_label_text(text)
@@ -973,32 +1070,38 @@ module Gruff
973
1070
  calculate_height(font, 'X')
974
1071
  end
975
1072
 
1073
+ def calculate_labels_height(font)
1074
+ @labels.values.map { |label| calculate_height(font, label, rotation: @label_rotation) }.max || marker_caps_height
1075
+ end
1076
+
976
1077
  # Returns the height of a string at this point size.
977
1078
  #
978
1079
  # Not scaled since it deals with dimensions that the regular scaling will
979
1080
  # handle.
980
- def calculate_height(font, text)
1081
+ def calculate_height(font, text, rotation: 0)
981
1082
  text = text.to_s
982
1083
  return 0 if text.empty?
983
1084
 
984
- metrics = text_metrics(font, text)
985
- metrics.height
1085
+ metrics = text_metrics(font, text, rotation: rotation)
1086
+ # Calculate manually because it does not return the height after rotation.
1087
+ (metrics.width * Math.sin(deg2rad(rotation))).abs + (metrics.height * Math.cos(deg2rad(rotation))).abs
986
1088
  end
987
1089
 
988
1090
  # Returns the width of a string at this point size.
989
1091
  #
990
1092
  # Not scaled since it deals with dimensions that the regular
991
1093
  # scaling will handle.
992
- def calculate_width(font, text)
1094
+ def calculate_width(font, text, rotation: 0)
993
1095
  text = text.to_s
994
1096
  return 0 if text.empty?
995
1097
 
996
- metrics = text_metrics(font, text)
997
- metrics.width
1098
+ metrics = text_metrics(font, text, rotation: rotation)
1099
+ # Calculate manually because it does not return the width after rotation.
1100
+ (metrics.width * Math.cos(deg2rad(rotation))).abs - (metrics.height * Math.sin(deg2rad(rotation))).abs
998
1101
  end
999
1102
 
1000
- def text_metrics(font, text)
1001
- Gruff::Renderer::Text.new(renderer, text, font: font).metrics
1103
+ def text_metrics(font, text, rotation: 0)
1104
+ Gruff::Renderer::Text.new(renderer, text, font: font, rotation: rotation).metrics
1002
1105
  end
1003
1106
 
1004
1107
  def calculate_increment
@@ -1006,14 +1109,32 @@ module Gruff
1006
1109
  # Try to use a number of horizontal lines that will come out even.
1007
1110
  #
1008
1111
  # TODO Do the same for larger numbers...100, 75, 50, 25
1009
- @increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1
1112
+ @increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1.0
1010
1113
  else
1011
1114
  # TODO: Make this work for negative values
1012
1115
  self.marker_count = (@spread / @y_axis_increment).to_i
1013
- @increment = @y_axis_increment
1116
+ @increment = @y_axis_increment.to_f
1014
1117
  end
1015
1118
  end
1016
1119
 
1120
+ def calculate_label_offset(font, label, margin, rotation)
1121
+ width = calculate_width(font, label, rotation: rotation)
1122
+ height = calculate_height(font, label, rotation: rotation)
1123
+ x_offset = begin
1124
+ case rotation
1125
+ when 0
1126
+ 0
1127
+ when 0..45
1128
+ width / 2.0
1129
+ when -45..0
1130
+ -(width / 2.0)
1131
+ end
1132
+ end
1133
+ y_offset = (height / 2.0) > margin ? (height / 2.0) : margin
1134
+
1135
+ [x_offset, y_offset]
1136
+ end
1137
+
1017
1138
  # Used for degree <=> radian conversions
1018
1139
  def deg2rad(angle)
1019
1140
  (angle * Math::PI) / 180.0
data/lib/gruff/bezier.rb CHANGED
@@ -22,9 +22,14 @@ class Gruff::Bezier < Gruff::Base
22
22
  private
23
23
 
24
24
  def draw_graph
25
- x_increment = @graph_width / (column_count - 1)
25
+ x_increment = (@graph_width / (column_count - 1)).to_f
26
+
27
+ renderer_class = RUBY_PLATFORM == 'java' ? Gruff::Renderer::Polyline : Gruff::Renderer::Bezier
28
+ stroke_width = clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4.0), 5.0)
26
29
 
27
30
  store.norm_data.each do |data_row|
31
+ next if data_row[1].empty?
32
+
28
33
  poly_points = []
29
34
 
30
35
  data_row[1].each_with_index do |data_point, index|
@@ -43,13 +48,7 @@ private
43
48
  draw_label(new_x, index)
44
49
  end
45
50
 
46
- stroke_width = clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
47
-
48
- if RUBY_PLATFORM == 'java'
49
- Gruff::Renderer::Polyline.new(renderer, color: data_row.color, width: stroke_width).render(poly_points)
50
- else
51
- Gruff::Renderer::Bezier.new(renderer, color: data_row.color, width: stroke_width).render(poly_points)
52
- end
51
+ renderer_class.new(renderer, color: data_row.color, width: stroke_width).render(poly_points)
53
52
  end
54
53
  end
55
54
  end
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Here's how to set up a Gruff::BoxPlot.
4
+ # Here's how to set up a Gruff::Box.
5
5
  #
6
- # g = Gruff::BoxPlot.new
6
+ # g = Gruff::Box.new
7
7
  # g.data "A", [2, 3, 5, 6, 8, 10, 11, 15, 17, 20, 28, 29, 33, 34, 45, 46, 49, 61]
8
8
  # g.data "B", [3, 4, 34, 35, 38, 39, 45, 60, 61, 69, 80, 130]
9
9
  # g.data "C", [4, 40, 41, 46, 57, 64, 77, 76, 79, 78, 99, 153]
10
10
  # g.write("box_plot.png")
11
11
  #
12
- class Gruff::BoxPlot < Gruff::Base
12
+ class Gruff::Box < Gruff::Base
13
13
  # Specifies the filling opacity in area graph. Default is +0.2+.
14
14
  attr_writer :fill_opacity
15
15
 
@@ -44,11 +44,13 @@ private
44
44
  minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
45
45
  )
46
46
 
47
- width = (@graph_width - calculate_spacing) / normalized_boxes.size
47
+ width = (@graph_width - calculate_spacing) / column_count
48
48
  bar_width = width * @spacing_factor
49
49
  padding = width - bar_width
50
50
 
51
51
  normalized_boxes.each_with_index do |box, index|
52
+ next if box.points.empty?
53
+
52
54
  left_x = @graph_left + (width * index) + (padding / 2.0)
53
55
  right_x = left_x + bar_width
54
56
  center_x = (left_x + right_x) / 2.0
@@ -88,11 +90,15 @@ private
88
90
  end
89
91
 
90
92
  def normalized_boxes
91
- @normalized_boxes ||= store.norm_data.map { |data| Gruff::BoxPlot::BoxData.new(data.label, data.points, data.color) }
93
+ @normalized_boxes ||= store.norm_data.map { |data| Gruff::Box::BoxData.new(data.label, data.points, data.color) }
94
+ end
95
+
96
+ def column_count
97
+ normalized_boxes.size
92
98
  end
93
99
 
94
100
  def calculate_spacing
95
- @scale * (column_count - 1)
101
+ column_count - 1
96
102
  end
97
103
 
98
104
  # @private