gruff 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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)
@@ -503,11 +550,13 @@ module Gruff
503
550
  attr_reader :renderer
504
551
 
505
552
  # Perform data manipulation before calculating chart measurements
506
- def setup_data # :nodoc:
553
+ def setup_data
507
554
  if @y_axis_increment && !@hide_line_markers
508
555
  self.maximum_value = [@y_axis_increment, maximum_value, (maximum_value / @y_axis_increment).round * @y_axis_increment].max
509
556
  self.minimum_value = [minimum_value, (minimum_value / @y_axis_increment).round * @y_axis_increment].min
510
557
  end
558
+
559
+ sort_data if @sort # Sort data with avg largest values set first (for display)
511
560
  end
512
561
 
513
562
  # Calculates size of drawable area and generates normalized data.
@@ -518,7 +567,6 @@ module Gruff
518
567
  def setup_drawing
519
568
  calculate_spread
520
569
  calculate_increment
521
- sort_data if @sort # Sort data with avg largest values set first (for display)
522
570
  set_colors
523
571
  normalize
524
572
  setup_graph_measurements
@@ -583,8 +631,7 @@ module Gruff
583
631
  # Calculates size of drawable area, general font dimensions, etc.
584
632
 
585
633
  def setup_graph_measurements
586
- margin_on_right = graph_right_margin
587
- @graph_right = @raw_columns - margin_on_right
634
+ @graph_right = setup_right_margin
588
635
  @graph_left = setup_left_margin
589
636
  @graph_top = setup_top_margin
590
637
  @graph_bottom = setup_bottom_margin
@@ -599,7 +646,7 @@ module Gruff
599
646
  # X Axis
600
647
  # Centered vertically and horizontally by setting the
601
648
  # 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
649
+ x_axis_label_y_coordinate = @graph_bottom + (LABEL_MARGIN * 2) + labels_caps_height
603
650
 
604
651
  text_renderer = Gruff::Renderer::Text.new(renderer, @x_axis_label, font: @marker_font)
605
652
  text_renderer.add_to_render_queue(@raw_columns, 1.0, 0.0, x_axis_label_y_coordinate)
@@ -621,9 +668,7 @@ module Gruff
621
668
  # Draw horizontal line markers and annotate with numbers
622
669
  (0..marker_count).each do |index|
623
670
  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
671
+ draw_marker_horizontal_line(y)
627
672
 
628
673
  unless @hide_line_numbers
629
674
  marker_label = (BigDecimal(index.to_s) * BigDecimal(@increment.to_s)) + BigDecimal(minimum_value.to_s)
@@ -634,6 +679,25 @@ module Gruff
634
679
  end
635
680
  end
636
681
 
682
+ def draw_marker_horizontal_line(y)
683
+ Gruff::Renderer::Line.new(renderer, color: @marker_color).render(@graph_left, y, @graph_right, y)
684
+ Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(@graph_left, y + 1, @graph_right, y + 1) if @marker_shadow_color
685
+ end
686
+
687
+ def draw_marker_vertical_line(x, tick_mark_mode: false)
688
+ if tick_mark_mode
689
+ Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_bottom + 5)
690
+ if @marker_shadow_color
691
+ Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_bottom + 5)
692
+ end
693
+ else
694
+ Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_top)
695
+ if @marker_shadow_color
696
+ Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_top)
697
+ end
698
+ end
699
+ end
700
+
637
701
  # Return a calculation of center
638
702
  def center(size)
639
703
  (@raw_columns - size) / 2
@@ -651,7 +715,7 @@ module Gruff
651
715
 
652
716
  current_y_offset = begin
653
717
  if @legend_at_bottom
654
- @graph_bottom + @legend_margin + legend_caps_height + LABEL_MARGIN + (@x_axis_label ? (LABEL_MARGIN * 2) + marker_caps_height : 0)
718
+ @graph_bottom + @legend_margin + labels_caps_height + LABEL_MARGIN + (@x_axis_label ? (LABEL_MARGIN * 2) + marker_caps_height : 0)
655
719
  else
656
720
  hide_title? ? @top_margin + @title_margin : @top_margin + @title_margin + title_caps_height
657
721
  end
@@ -705,11 +769,22 @@ module Gruff
705
769
  # Draws column labels below graph, centered over x_offset
706
770
  def draw_label(x_offset, index, gravity = Magick::NorthGravity, &block)
707
771
  draw_unique_label(index) do
708
- y_offset = @graph_bottom + LABEL_MARGIN
709
- y_offset += @label_stagger_height if index.odd?
772
+ y_offset = @graph_bottom
710
773
 
711
774
  if x_offset >= @graph_left && x_offset <= @graph_right
712
- draw_label_at(1.0, 1.0, x_offset, y_offset, @labels[index], gravity)
775
+ width = calculate_width(@marker_font, @labels[index], rotation: @label_rotation)
776
+ height = calculate_height(@marker_font, @labels[index], rotation: @label_rotation)
777
+ case @label_rotation
778
+ when 0
779
+ x_offset
780
+ when 0..45
781
+ x_offset += (width / 2.0)
782
+ when -45..0
783
+ x_offset -= (width / 2.0)
784
+ end
785
+ y_offset += (height / 2.0) > LABEL_MARGIN ? (height / 2.0) : LABEL_MARGIN
786
+
787
+ draw_label_at(1.0, 1.0, x_offset, y_offset, @labels[index], gravity: gravity, rotation: @label_rotation)
713
788
  yield if block
714
789
  end
715
790
  end
@@ -725,17 +800,17 @@ module Gruff
725
800
  end
726
801
  end
727
802
 
728
- def draw_label_at(width, height, x, y, text, gravity = Magick::NorthGravity)
803
+ def draw_label_at(width, height, x, y, text, gravity: Magick::NorthGravity, rotation: 0)
729
804
  label_text = truncate_label_text(text)
730
- text_renderer = Gruff::Renderer::Text.new(renderer, label_text, font: @marker_font)
805
+ text_renderer = Gruff::Renderer::Text.new(renderer, label_text, font: @marker_font, rotation: rotation)
731
806
  text_renderer.add_to_render_queue(width, height, x, y, gravity)
732
807
  end
733
808
 
734
809
  # 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)
810
+ def draw_value_label(width, height, x_offset, y_offset, data_point, gravity: Magick::CenterGravity)
736
811
  return if @hide_line_markers
737
812
 
738
- draw_label_at(width, height, x_offset, y_offset, data_point, gravity)
813
+ draw_label_at(width, height, x_offset, y_offset, data_point, gravity: gravity)
739
814
  end
740
815
 
741
816
  # Shows an error message because you have no data.
@@ -756,20 +831,15 @@ module Gruff
756
831
  @theme_options = {}
757
832
  end
758
833
 
759
- def scale(value) # :nodoc:
834
+ def scale(value)
760
835
  value * @scale
761
836
  end
762
837
 
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:
838
+ def clip_value_if_greater_than(value, max_value)
769
839
  value > max_value ? max_value : value
770
840
  end
771
841
 
772
- def significant(i) # :nodoc:
842
+ def significant(i)
773
843
  return 1.0 if i == 0 # Keep from going into infinite loop
774
844
 
775
845
  inc = BigDecimal(i.to_s)
@@ -816,6 +886,10 @@ module Gruff
816
886
  hide_bottom_label_area? ? 0 : calculate_caps_height(@marker_font)
817
887
  end
818
888
 
889
+ def labels_caps_height
890
+ hide_bottom_label_area? ? 0 : calculate_labels_height(@marker_font)
891
+ end
892
+
819
893
  def title_caps_height
820
894
  hide_title? ? 0 : calculate_caps_height(@title_font) * @title.lines.to_a.size
821
895
  end
@@ -824,21 +898,6 @@ module Gruff
824
898
  @hide_legend ? 0 : calculate_caps_height(@legend_font)
825
899
  end
826
900
 
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
901
  def setup_left_margin
843
902
  return @left_margin if hide_left_label_area?
844
903
 
@@ -851,10 +910,62 @@ module Gruff
851
910
  end
852
911
  longest_left_label_width = calculate_width(@marker_font, truncate_label_text(text))
853
912
 
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)
913
+ line_number_width = begin
914
+ if !@has_left_labels && (@hide_line_markers || @hide_line_numbers)
915
+ 0.0
916
+ else
917
+ longest_left_label_width + LABEL_MARGIN
918
+ end
919
+ end
920
+ y_axis_label_width = @y_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2)
921
+
922
+ bottom_label_width = extra_left_room_for_long_label
923
+
924
+ margin = line_number_width + y_axis_label_width
925
+ @left_margin + (margin > bottom_label_width ? margin : bottom_label_width)
926
+ end
927
+
928
+ def setup_right_margin
929
+ @raw_columns - (@hide_line_markers ? @right_margin : @right_margin + extra_right_room_for_long_label)
930
+ end
931
+
932
+ def extra_left_room_for_long_label
933
+ if require_extra_side_margin?
934
+ width = calculate_width(@marker_font, truncate_label_text(@labels[0]), rotation: @label_rotation)
935
+ case @label_rotation
936
+ when 0
937
+ width / 2.0
938
+ when 0..45
939
+ 0
940
+ when -45..0
941
+ width
942
+ end
943
+ else
944
+ 0
945
+ end
946
+ end
947
+
948
+ def extra_right_room_for_long_label
949
+ # Make space for half the width of the rightmost column label.
950
+ # Might be greater than the number of columns if between-style bar markers are used.
951
+ last_label = @labels.keys.max.to_i
952
+ if last_label >= (column_count - 1) && require_extra_side_margin?
953
+ width = calculate_width(@marker_font, truncate_label_text(@labels[last_label]), rotation: @label_rotation)
954
+ case @label_rotation
955
+ when 0
956
+ width / 2.0
957
+ when 0..45
958
+ width
959
+ when -45..0
960
+ 0
961
+ end
962
+ else
963
+ 0
964
+ end
965
+ end
856
966
 
857
- @left_margin + line_number_width + (@y_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2))
967
+ def require_extra_side_margin?
968
+ !hide_bottom_label_area? && @center_labels_over_point
858
969
  end
859
970
 
860
971
  def setup_top_margin
@@ -866,11 +977,11 @@ module Gruff
866
977
  end
867
978
 
868
979
  def setup_bottom_margin
869
- graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin + marker_caps_height + LABEL_MARGIN
980
+ graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin + labels_caps_height + LABEL_MARGIN
870
981
  graph_bottom_margin += (calculate_legend_height + @legend_margin) if @legend_at_bottom
871
982
 
872
983
  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
984
+ @raw_rows - graph_bottom_margin - x_axis_label_height
874
985
  end
875
986
 
876
987
  def truncate_label_text(text)
@@ -973,32 +1084,38 @@ module Gruff
973
1084
  calculate_height(font, 'X')
974
1085
  end
975
1086
 
1087
+ def calculate_labels_height(font)
1088
+ @labels.values.map { |label| calculate_height(font, label, rotation: @label_rotation) }.max || marker_caps_height
1089
+ end
1090
+
976
1091
  # Returns the height of a string at this point size.
977
1092
  #
978
1093
  # Not scaled since it deals with dimensions that the regular scaling will
979
1094
  # handle.
980
- def calculate_height(font, text)
1095
+ def calculate_height(font, text, rotation: 0)
981
1096
  text = text.to_s
982
1097
  return 0 if text.empty?
983
1098
 
984
- metrics = text_metrics(font, text)
985
- metrics.height
1099
+ metrics = text_metrics(font, text, rotation: rotation)
1100
+ # Calculate manually because it does not return the height after rotation.
1101
+ (metrics.width * Math.sin(deg2rad(rotation))).abs + (metrics.height * Math.cos(deg2rad(rotation))).abs
986
1102
  end
987
1103
 
988
1104
  # Returns the width of a string at this point size.
989
1105
  #
990
1106
  # Not scaled since it deals with dimensions that the regular
991
1107
  # scaling will handle.
992
- def calculate_width(font, text)
1108
+ def calculate_width(font, text, rotation: 0)
993
1109
  text = text.to_s
994
1110
  return 0 if text.empty?
995
1111
 
996
- metrics = text_metrics(font, text)
997
- metrics.width
1112
+ metrics = text_metrics(font, text, rotation: rotation)
1113
+ # Calculate manually because it does not return the width after rotation.
1114
+ (metrics.width * Math.cos(deg2rad(rotation))).abs - (metrics.height * Math.sin(deg2rad(rotation))).abs
998
1115
  end
999
1116
 
1000
- def text_metrics(font, text)
1001
- Gruff::Renderer::Text.new(renderer, text, font: font).metrics
1117
+ def text_metrics(font, text, rotation: 0)
1118
+ Gruff::Renderer::Text.new(renderer, text, font: font, rotation: rotation).metrics
1002
1119
  end
1003
1120
 
1004
1121
  def calculate_increment
data/lib/gruff/bezier.rb CHANGED
@@ -25,6 +25,8 @@ private
25
25
  x_increment = @graph_width / (column_count - 1)
26
26
 
27
27
  store.norm_data.each do |data_row|
28
+ next if data_row[1].empty?
29
+
28
30
  poly_points = []
29
31
 
30
32
  data_row[1].each_with_index do |data_point, index|
@@ -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
@@ -91,6 +93,10 @@ private
91
93
  @normalized_boxes ||= store.norm_data.map { |data| Gruff::BoxPlot::BoxData.new(data.label, data.points, data.color) }
92
94
  end
93
95
 
96
+ def column_count
97
+ normalized_boxes.size
98
+ end
99
+
94
100
  def calculate_spacing
95
101
  @scale * (column_count - 1)
96
102
  end
@@ -38,6 +38,16 @@ class Gruff::Candlestick < Gruff::Base
38
38
  @spacing_factor = (1 - space_percent)
39
39
  end
40
40
 
41
+ # The sort feature is not supported in this graph.
42
+ def sort=(_value)
43
+ raise 'Not support #sort= in Gruff::Candlestick'
44
+ end
45
+
46
+ # The sort feature is not supported in this graph.
47
+ def sorted_drawing=(_value)
48
+ raise 'Not support #sorted_drawing= in Gruff::Candlestick'
49
+ end
50
+
41
51
  def data(low:, high:, open:, close:)
42
52
  super('', [low, high, open, close])
43
53
  end
@@ -53,7 +63,6 @@ private
53
63
  @up_color = '#579773'
54
64
  @down_color = '#eb5242'
55
65
 
56
- @sort = false
57
66
  @hide_legend = true
58
67
  end
59
68
 
@@ -64,7 +73,7 @@ private
64
73
  minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
65
74
  )
66
75
 
67
- width = (@graph_width - calculate_spacing) / normalized_candlesticks.size
76
+ width = (@graph_width - calculate_spacing) / column_count
68
77
  bar_width = width * @spacing_factor
69
78
  padding = width - bar_width
70
79
 
@@ -74,16 +83,7 @@ private
74
83
  center_x = (left_x + right_x) / 2.0
75
84
  color = candlestick.close >= candlestick.open ? @up_color : @down_color
76
85
 
77
- draw_label(center_x, index) do
78
- break if @hide_line_markers
79
- break unless @show_vertical_markers
80
-
81
- Gruff::Renderer::Line.new(renderer, color: @marker_color).render(center_x, @graph_bottom, center_x, @graph_top)
82
- if @marker_shadow_color
83
- Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color)
84
- .render(center_x + 1, @graph_bottom, center_x + 1, @graph_top)
85
- end
86
- end
86
+ draw_label(center_x, index) { draw_marker_vertical_line(center_x) if show_marker_vertical_line? }
87
87
 
88
88
  open_y, = conversion.get_top_bottom_scaled(candlestick.open)
89
89
  close_y, = conversion.get_top_bottom_scaled(candlestick.close)
@@ -99,13 +99,21 @@ private
99
99
  end
100
100
 
101
101
  def normalized_candlesticks
102
- @candlesticks ||= store.norm_data.map { |data| Gruff::Candlestick::CandlestickData.new(*data.points) }
102
+ @normalized_candlesticks ||= store.norm_data.map { |data| Gruff::Candlestick::CandlestickData.new(*data.points) }
103
+ end
104
+
105
+ def column_count
106
+ normalized_candlesticks.size
103
107
  end
104
108
 
105
109
  def calculate_spacing
106
110
  @scale * (column_count - 1)
107
111
  end
108
112
 
113
+ def show_marker_vertical_line?
114
+ !@hide_line_markers && @show_vertical_markers
115
+ end
116
+
109
117
  # @private
110
118
  class CandlestickData < Struct.new(:low, :high, :open, :close)
111
119
  end
data/lib/gruff/dot.rb CHANGED
@@ -17,6 +17,7 @@ class Gruff::Dot < Gruff::Base
17
17
  def initialize(*)
18
18
  super
19
19
  @has_left_labels = true
20
+ @dot_style = 'circle'
20
21
  end
21
22
 
22
23
  private
@@ -39,7 +40,7 @@ private
39
40
  Gruff::Renderer::Line.new(renderer, color: @marker_color).render(@graph_left, y_pos, @graph_left + @graph_width, y_pos)
40
41
  end
41
42
 
42
- Gruff::Renderer::Circle.new(renderer, color: data_row.color).render(x_pos, y_pos, x_pos + (item_width / 3.0), y_pos)
43
+ Gruff::Renderer::Dot.new(renderer, @dot_style, color: data_row.color).render(x_pos, y_pos, item_width / 3.0)
43
44
 
44
45
  draw_label(y_pos, point_index)
45
46
  end
@@ -53,9 +54,7 @@ private
53
54
  (0..marker_count).each do |index|
54
55
  marker_label = (BigDecimal(index.to_s) * BigDecimal(@increment.to_s)) + BigDecimal(minimum_value.to_s)
55
56
  x = @graph_left + ((marker_label - minimum_value) * @graph_width / @spread)
56
-
57
- Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_bottom + 5)
58
- Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x, @graph_bottom + 1, x, @graph_bottom + 6) if @marker_shadow_color
57
+ draw_marker_vertical_line(x, tick_mark_mode: true)
59
58
 
60
59
  unless @hide_line_numbers
61
60
  label = y_axis_label(marker_label, @increment)
@@ -70,7 +69,7 @@ private
70
69
 
71
70
  def draw_label(y_offset, index)
72
71
  draw_unique_label(index) do
73
- draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], Magick::EastGravity)
72
+ draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], gravity: Magick::EastGravity)
74
73
  end
75
74
  end
76
75
  end
@@ -13,8 +13,6 @@
13
13
  #
14
14
  # @private
15
15
  class Gruff::BarConversion
16
- attr_writer :mode
17
-
18
16
  def initialize(top:, bottom:, minimum_value:, maximum_value:, spread:)
19
17
  @graph_top = top
20
18
  @graph_height = bottom - top
@@ -36,6 +34,7 @@ class Gruff::BarConversion
36
34
  end
37
35
 
38
36
  def get_top_bottom_scaled(data_point)
37
+ data_point = data_point.to_f
39
38
  result = []
40
39
 
41
40
  case @mode
@@ -52,9 +51,6 @@ class Gruff::BarConversion
52
51
  val = data_point - (@minimum_value / @spread)
53
52
  result[0] = @graph_top + (@graph_height * (1 - (val - @zero)))
54
53
  result[1] = @graph_top + (@graph_height * (1 - @zero))
55
- else
56
- result[0] = 0.0
57
- result[1] = 0.0
58
54
  end
59
55
 
60
56
  result
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @private
4
+ module Gruff::Base::BarMixin
5
+ def normalized_group_bars
6
+ @normalized_group_bars ||= begin
7
+ group_bars = Array.new(column_count) { [] }
8
+ store.norm_data.each_with_index do |data_row, row_index|
9
+ data_row.points.each_with_index do |data_point, point_index|
10
+ group_bars[point_index] << BarData.new(data_point, store.data[row_index].points[point_index], data_row.color)
11
+ end
12
+
13
+ # Adjust the number of each group with empty bar
14
+ (data_row.points.size..(column_count - 1)).each do |index|
15
+ group_bars[index] << BarData.new(0, nil, data_row.color)
16
+ end
17
+ end
18
+ group_bars
19
+ end
20
+ end
21
+
22
+ # @private
23
+ class BarData < Struct.new(:point, :value, :color)
24
+ end
25
+ end