gruff 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +5 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +0 -6
- data/CHANGELOG.md +19 -0
- data/gruff.gemspec +2 -3
- data/lib/gruff/area.rb +2 -0
- data/lib/gruff/bar.rb +26 -23
- data/lib/gruff/base.rb +198 -81
- data/lib/gruff/bezier.rb +2 -0
- data/lib/gruff/box_plot.rb +7 -1
- data/lib/gruff/candlestick.rb +21 -13
- data/lib/gruff/dot.rb +4 -5
- data/lib/gruff/helper/bar_conversion.rb +1 -5
- data/lib/gruff/helper/bar_mixin.rb +25 -0
- data/lib/gruff/helper/bar_value_label.rb +0 -22
- data/lib/gruff/helper/stacked_mixin.rb +16 -0
- data/lib/gruff/histogram.rb +8 -4
- data/lib/gruff/line.rb +2 -5
- data/lib/gruff/mini/legend.rb +9 -9
- data/lib/gruff/net.rb +16 -11
- data/lib/gruff/pie.rb +6 -1
- data/lib/gruff/renderer/dot.rb +25 -14
- data/lib/gruff/renderer/renderer.rb +0 -4
- data/lib/gruff/renderer/text.rb +7 -1
- data/lib/gruff/scatter.rb +20 -18
- data/lib/gruff/side_bar.rb +28 -23
- data/lib/gruff/side_stacked_bar.rb +36 -41
- data/lib/gruff/spider.rb +7 -2
- data/lib/gruff/stacked_area.rb +7 -2
- data/lib/gruff/stacked_bar.rb +46 -38
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +0 -3
- metadata +10 -9
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
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
#
|
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
|
-
#
|
54
|
-
#
|
55
|
-
|
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
|
-
#
|
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
|
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
|
-
|
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) +
|
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 +
|
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
|
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
|
-
|
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
|
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
|
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)
|
834
|
+
def scale(value)
|
760
835
|
value * @scale
|
761
836
|
end
|
762
837
|
|
763
|
-
|
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)
|
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
|
-
|
855
|
-
|
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
|
-
|
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 +
|
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
|
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
|
-
|
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
|
-
|
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
data/lib/gruff/box_plot.rb
CHANGED
@@ -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) /
|
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
|
data/lib/gruff/candlestick.rb
CHANGED
@@ -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) /
|
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)
|
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
|
-
@
|
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::
|
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
|