gruff 0.13.0-java → 0.16.0-java
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 +79 -0
- data/.rubocop.yml +29 -31
- data/CHANGELOG.md +43 -0
- data/README.md +11 -5
- data/gruff.gemspec +8 -10
- data/lib/gruff/accumulator_bar.rb +4 -2
- data/lib/gruff/area.rb +9 -12
- data/lib/gruff/bar.rb +46 -31
- data/lib/gruff/base.rb +236 -207
- data/lib/gruff/bezier.rb +6 -8
- data/lib/gruff/box_plot.rb +174 -0
- data/lib/gruff/bullet.rb +17 -16
- data/lib/gruff/candlestick.rb +112 -0
- data/lib/gruff/dot.rb +14 -14
- data/lib/gruff/font.rb +42 -0
- data/lib/gruff/helper/bar_conversion.rb +5 -5
- data/lib/gruff/helper/bar_value_label.rb +26 -20
- data/lib/gruff/helper/stacked_mixin.rb +4 -3
- data/lib/gruff/histogram.rb +9 -7
- data/lib/gruff/line.rb +96 -83
- data/lib/gruff/mini/bar.rb +9 -6
- data/lib/gruff/mini/legend.rb +16 -12
- data/lib/gruff/mini/pie.rb +9 -6
- data/lib/gruff/mini/side_bar.rb +9 -6
- data/lib/gruff/net.rb +16 -22
- data/lib/gruff/patch/rmagick.rb +0 -1
- data/lib/gruff/patch/string.rb +2 -1
- data/lib/gruff/pie.rb +42 -75
- data/lib/gruff/renderer/bezier.rb +8 -9
- data/lib/gruff/renderer/circle.rb +8 -9
- data/lib/gruff/renderer/dash_line.rb +10 -10
- data/lib/gruff/renderer/dot.rb +15 -14
- data/lib/gruff/renderer/ellipse.rb +8 -9
- data/lib/gruff/renderer/line.rb +8 -11
- data/lib/gruff/renderer/polygon.rb +9 -10
- data/lib/gruff/renderer/polyline.rb +8 -9
- data/lib/gruff/renderer/rectangle.rb +11 -8
- data/lib/gruff/renderer/renderer.rb +25 -40
- data/lib/gruff/renderer/text.rb +21 -37
- data/lib/gruff/scatter.rb +86 -85
- data/lib/gruff/side_bar.rb +50 -37
- data/lib/gruff/side_stacked_bar.rb +26 -35
- data/lib/gruff/spider.rb +52 -28
- data/lib/gruff/stacked_area.rb +20 -16
- data/lib/gruff/stacked_bar.rb +44 -22
- data/lib/gruff/store/store.rb +6 -10
- data/lib/gruff/store/xy_data.rb +2 -0
- data/lib/gruff/themes.rb +6 -6
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +70 -57
- data/rails_generators/gruff/templates/controller.rb +1 -1
- metadata +15 -32
- data/.rubocop_todo.yml +0 -182
- data/.travis.yml +0 -23
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/lib/gruff/photo_bar.rb +0 -93
- data/lib/gruff/scene.rb +0 -198
- data/lib/gruff/store/custom_data.rb +0 -36
data/lib/gruff/base.rb
CHANGED
@@ -54,20 +54,18 @@ module Gruff
|
|
54
54
|
# { 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008 }
|
55
55
|
attr_writer :labels
|
56
56
|
|
57
|
-
# Used internally for spacing.
|
58
|
-
#
|
59
|
-
# By default, labels are centered over the point they represent.
|
60
|
-
attr_writer :center_labels_over_point
|
61
|
-
|
62
|
-
# Used internally for horizontal graph types. Default is +false+.
|
63
|
-
attr_writer :has_left_labels
|
64
|
-
|
65
57
|
# Set a label for the bottom of the graph.
|
66
58
|
attr_writer :x_axis_label
|
67
59
|
|
68
60
|
# Set a label for the left side of the graph.
|
69
61
|
attr_writer :y_axis_label
|
70
62
|
|
63
|
+
# Allow passing lambda to format labels for x axis.
|
64
|
+
attr_writer :x_axis_label_format
|
65
|
+
|
66
|
+
# Allow passing lambda to format labels for y axis.
|
67
|
+
attr_writer :y_axis_label_format
|
68
|
+
|
71
69
|
# Set increment of the vertical marking lines.
|
72
70
|
attr_writer :x_axis_increment
|
73
71
|
|
@@ -93,15 +91,6 @@ module Gruff
|
|
93
91
|
# Set the large title of the graph displayed at the top.
|
94
92
|
attr_writer :title
|
95
93
|
|
96
|
-
# Same as {#font=} but for the title.
|
97
|
-
attr_writer :title_font
|
98
|
-
|
99
|
-
# Specifies whether to draw the title bolded or not. Default is +true+.
|
100
|
-
attr_writer :bold_title
|
101
|
-
|
102
|
-
# Specifies the text color.
|
103
|
-
attr_writer :font_color
|
104
|
-
|
105
94
|
# Prevent drawing of line markers. Default is +false+.
|
106
95
|
attr_writer :hide_line_markers
|
107
96
|
|
@@ -118,21 +107,9 @@ module Gruff
|
|
118
107
|
# to +"No Data."+.
|
119
108
|
attr_writer :no_data_message
|
120
109
|
|
121
|
-
# Set the font size of the large title at the top of the graph. Default is +36+.
|
122
|
-
attr_writer :title_font_size
|
123
|
-
|
124
|
-
# Optionally set the size of the font. Based on an 800x600px graph.
|
125
|
-
# Default is +20+.
|
126
|
-
#
|
127
|
-
# Will be scaled down if the graph is smaller than 800px wide.
|
128
|
-
attr_writer :legend_font_size
|
129
|
-
|
130
110
|
# Display the legend under the graph. Default is +false+.
|
131
111
|
attr_writer :legend_at_bottom
|
132
112
|
|
133
|
-
# The font size of the labels around the graph. Default is +21+.
|
134
|
-
attr_writer :marker_font_size
|
135
|
-
|
136
113
|
# Set the color of the auxiliary lines.
|
137
114
|
attr_writer :marker_color
|
138
115
|
|
@@ -156,12 +133,6 @@ module Gruff
|
|
156
133
|
# Will be scaled down if graph is smaller than 800px wide.
|
157
134
|
attr_writer :legend_box_size
|
158
135
|
|
159
|
-
# Allow passing lambdas to format labels for x axis.
|
160
|
-
attr_writer :x_axis_label_format
|
161
|
-
|
162
|
-
# Allow passing lambdas to format labels for y axis.
|
163
|
-
attr_writer :y_axis_label_format
|
164
|
-
|
165
136
|
# If one numerical argument is given, the graph is drawn at 4/3 ratio
|
166
137
|
# according to the given width (+800+ results in 800x600, +400+ gives 400x300,
|
167
138
|
# etc.).
|
@@ -180,8 +151,11 @@ module Gruff
|
|
180
151
|
@columns.freeze
|
181
152
|
@rows.freeze
|
182
153
|
|
154
|
+
@has_left_labels = false
|
155
|
+
@center_labels_over_point = true
|
156
|
+
|
183
157
|
initialize_graph_scale
|
184
|
-
|
158
|
+
initialize_attributes
|
185
159
|
initialize_store
|
186
160
|
|
187
161
|
self.theme = Themes::KEYNOTE
|
@@ -209,7 +183,7 @@ module Gruff
|
|
209
183
|
#
|
210
184
|
# This makes it possible to set defaults in a subclass but still allow
|
211
185
|
# developers to change this values in their program.
|
212
|
-
def
|
186
|
+
def initialize_attributes
|
213
187
|
@marker_count = nil
|
214
188
|
@maximum_value = @minimum_value = nil
|
215
189
|
@labels = {}
|
@@ -217,13 +191,9 @@ module Gruff
|
|
217
191
|
@sorted_drawing = false
|
218
192
|
@title = nil
|
219
193
|
|
220
|
-
@title_font =
|
221
|
-
@
|
222
|
-
@
|
223
|
-
|
224
|
-
@marker_font_size = 21.0
|
225
|
-
@legend_font_size = 20.0
|
226
|
-
@title_font_size = 36.0
|
194
|
+
@title_font = Gruff::Font.new(size: 36.0, bold: true)
|
195
|
+
@marker_font = Gruff::Font.new(size: 21.0)
|
196
|
+
@legend_font = Gruff::Font.new(size: 20.0)
|
227
197
|
|
228
198
|
@top_margin = @bottom_margin = @left_margin = @right_margin = DEFAULT_MARGIN
|
229
199
|
@legend_margin = LEGEND_MARGIN
|
@@ -234,8 +204,6 @@ module Gruff
|
|
234
204
|
@no_data_message = 'No Data'
|
235
205
|
|
236
206
|
@hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = @legend_at_bottom = false
|
237
|
-
@center_labels_over_point = true
|
238
|
-
@has_left_labels = false
|
239
207
|
@label_stagger_height = 0
|
240
208
|
@label_max_size = 0
|
241
209
|
@label_truncation_style = :absolute
|
@@ -247,7 +215,7 @@ module Gruff
|
|
247
215
|
@x_axis_label_format = nil
|
248
216
|
@y_axis_label_format = nil
|
249
217
|
end
|
250
|
-
protected :
|
218
|
+
protected :initialize_attributes
|
251
219
|
|
252
220
|
# Sets the top, bottom, left and right margins to +margin+.
|
253
221
|
#
|
@@ -262,8 +230,62 @@ module Gruff
|
|
262
230
|
# @param font_path [String] The path to font.
|
263
231
|
#
|
264
232
|
def font=(font_path)
|
265
|
-
@
|
266
|
-
|
233
|
+
@title_font.path = font_path unless @title_font.path
|
234
|
+
@marker_font.path = font_path
|
235
|
+
@legend_font.path = font_path
|
236
|
+
end
|
237
|
+
|
238
|
+
# Same as {#font=} but for the title.
|
239
|
+
#
|
240
|
+
# @param font_path [String] The path to font.
|
241
|
+
#
|
242
|
+
def title_font=(font_path)
|
243
|
+
@title_font.path = font_path
|
244
|
+
end
|
245
|
+
|
246
|
+
# Set the font size of the large title at the top of the graph. Default is +36+.
|
247
|
+
#
|
248
|
+
# @param value [Numeric] title font size
|
249
|
+
#
|
250
|
+
def title_font_size=(value)
|
251
|
+
@title_font.size = value
|
252
|
+
end
|
253
|
+
|
254
|
+
# The font size of the labels around the graph. Default is +21+.
|
255
|
+
#
|
256
|
+
# @param value [Numeric] marker font size
|
257
|
+
#
|
258
|
+
def marker_font_size=(value)
|
259
|
+
@marker_font.size = value
|
260
|
+
end
|
261
|
+
|
262
|
+
# Optionally set the size of the font. Based on an 800x600px graph.
|
263
|
+
# Default is +20+.
|
264
|
+
#
|
265
|
+
# Will be scaled down if the graph is smaller than 800px wide.
|
266
|
+
#
|
267
|
+
# @param value [Numeric] legend font size
|
268
|
+
#
|
269
|
+
def legend_font_size=(value)
|
270
|
+
@legend_font.size = value
|
271
|
+
end
|
272
|
+
|
273
|
+
# Specifies whether to draw the title bolded or not. Default is +true+.
|
274
|
+
#
|
275
|
+
# @param value [Boolean] specifies whether to draw the title bolded or not.
|
276
|
+
#
|
277
|
+
def bold_title=(value)
|
278
|
+
@title_font.bold = value
|
279
|
+
end
|
280
|
+
|
281
|
+
# Specifies the text color.
|
282
|
+
#
|
283
|
+
# @param value [String] color
|
284
|
+
#
|
285
|
+
def font_color=(value)
|
286
|
+
@title_font.color = value
|
287
|
+
@marker_font.color = value
|
288
|
+
@legend_font.color = value
|
267
289
|
end
|
268
290
|
|
269
291
|
# Add a color to the list of available colors for lines.
|
@@ -332,12 +354,13 @@ module Gruff
|
|
332
354
|
}
|
333
355
|
@theme_options = defaults.merge options
|
334
356
|
|
357
|
+
self.marker_color = @theme_options[:marker_color]
|
358
|
+
self.font_color = @theme_options[:font_color] || @marker_color
|
359
|
+
|
335
360
|
@colors = @theme_options[:colors]
|
336
|
-
@marker_color = @theme_options[:marker_color]
|
337
361
|
@marker_shadow_color = @theme_options[:marker_shadow_color]
|
338
|
-
@font_color = @theme_options[:font_color] || @marker_color
|
339
362
|
|
340
|
-
Gruff::Renderer.
|
363
|
+
@renderer = Gruff::Renderer.new(@columns, @rows, @scale, @theme_options)
|
341
364
|
end
|
342
365
|
|
343
366
|
# Apply Apple's keynote theme.
|
@@ -399,7 +422,7 @@ module Gruff
|
|
399
422
|
#
|
400
423
|
# Set it after you have given all your data to the graph object.
|
401
424
|
def minimum_value
|
402
|
-
@minimum_value || store.min
|
425
|
+
(@minimum_value || store.min).to_f
|
403
426
|
end
|
404
427
|
attr_writer :minimum_value
|
405
428
|
|
@@ -409,7 +432,7 @@ module Gruff
|
|
409
432
|
# If you use this, you must set it after you have given all your data to
|
410
433
|
# the graph object.
|
411
434
|
def maximum_value
|
412
|
-
@maximum_value || store.max
|
435
|
+
(@maximum_value || store.max).to_f
|
413
436
|
end
|
414
437
|
attr_writer :maximum_value
|
415
438
|
|
@@ -439,8 +462,8 @@ module Gruff
|
|
439
462
|
def to_image
|
440
463
|
@to_image ||= begin
|
441
464
|
draw
|
442
|
-
|
443
|
-
|
465
|
+
renderer.finish
|
466
|
+
renderer.image
|
444
467
|
end
|
445
468
|
end
|
446
469
|
|
@@ -456,32 +479,34 @@ module Gruff
|
|
456
479
|
end
|
457
480
|
end
|
458
481
|
|
459
|
-
|
460
|
-
|
461
|
-
# Overridden by subclasses to do the actual plotting of the graph.
|
462
|
-
#
|
463
|
-
# Subclasses should start by calling super() for this method.
|
482
|
+
# Draw a graph.
|
464
483
|
def draw
|
484
|
+
setup_data
|
485
|
+
|
465
486
|
# Maybe should be done in one of the following functions for more granularity.
|
466
487
|
unless data_given?
|
467
488
|
draw_no_data
|
468
489
|
return
|
469
490
|
end
|
470
491
|
|
471
|
-
setup_data
|
472
492
|
setup_drawing
|
473
493
|
|
474
494
|
draw_legend
|
475
495
|
draw_line_markers
|
476
496
|
draw_axis_labels
|
477
497
|
draw_title
|
498
|
+
draw_graph
|
478
499
|
end
|
479
500
|
|
501
|
+
protected
|
502
|
+
|
503
|
+
attr_reader :renderer
|
504
|
+
|
480
505
|
# Perform data manipulation before calculating chart measurements
|
481
506
|
def setup_data # :nodoc:
|
482
507
|
if @y_axis_increment && !@hide_line_markers
|
483
|
-
self.maximum_value = [@y_axis_increment, maximum_value, (maximum_value
|
484
|
-
self.minimum_value = [minimum_value, (minimum_value
|
508
|
+
self.maximum_value = [@y_axis_increment, maximum_value, (maximum_value / @y_axis_increment).round * @y_axis_increment].max
|
509
|
+
self.minimum_value = [minimum_value, (minimum_value / @y_axis_increment).round * @y_axis_increment].min
|
485
510
|
end
|
486
511
|
end
|
487
512
|
|
@@ -520,7 +545,7 @@ module Gruff
|
|
520
545
|
@marker_count ||= begin
|
521
546
|
count = nil
|
522
547
|
(3..7).each do |lines|
|
523
|
-
if @spread
|
548
|
+
if @spread % lines == 0.0
|
524
549
|
count = lines and break
|
525
550
|
end
|
526
551
|
end
|
@@ -533,8 +558,8 @@ module Gruff
|
|
533
558
|
store.normalize(minimum: minimum_value, spread: @spread)
|
534
559
|
end
|
535
560
|
|
536
|
-
def calculate_spread
|
537
|
-
@spread = maximum_value
|
561
|
+
def calculate_spread
|
562
|
+
@spread = maximum_value - minimum_value
|
538
563
|
@spread = @spread > 0 ? @spread : 1
|
539
564
|
end
|
540
565
|
|
@@ -547,28 +572,24 @@ module Gruff
|
|
547
572
|
end
|
548
573
|
|
549
574
|
def hide_left_label_area?
|
550
|
-
@hide_line_markers
|
575
|
+
@hide_line_markers && @y_axis_label.nil?
|
551
576
|
end
|
552
577
|
|
553
578
|
def hide_bottom_label_area?
|
554
|
-
@hide_line_markers
|
579
|
+
@hide_line_markers && @x_axis_label.nil?
|
555
580
|
end
|
556
581
|
|
557
582
|
##
|
558
583
|
# Calculates size of drawable area, general font dimensions, etc.
|
559
584
|
|
560
585
|
def setup_graph_measurements
|
561
|
-
@marker_caps_height = setup_marker_caps_height
|
562
|
-
@title_caps_height = setup_title_caps_height
|
563
|
-
@legend_caps_height = setup_legend_caps_height
|
564
|
-
|
565
586
|
margin_on_right = graph_right_margin
|
566
587
|
@graph_right = @raw_columns - margin_on_right
|
567
588
|
@graph_left = setup_left_margin
|
568
589
|
@graph_top = setup_top_margin
|
569
590
|
@graph_bottom = setup_bottom_margin
|
570
591
|
|
571
|
-
@graph_width = @
|
592
|
+
@graph_width = @graph_right - @graph_left
|
572
593
|
@graph_height = @graph_bottom - @graph_top
|
573
594
|
end
|
574
595
|
|
@@ -578,17 +599,16 @@ module Gruff
|
|
578
599
|
# X Axis
|
579
600
|
# Centered vertically and horizontally by setting the
|
580
601
|
# height to 1.0 and the width to the width of the graph.
|
581
|
-
x_axis_label_y_coordinate = @graph_bottom + LABEL_MARGIN +
|
602
|
+
x_axis_label_y_coordinate = @graph_bottom + (LABEL_MARGIN * 2) + marker_caps_height
|
582
603
|
|
583
|
-
|
584
|
-
text_renderer = Gruff::Renderer::Text.new(@x_axis_label, font: @font, size: @marker_font_size, color: @font_color)
|
604
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, @x_axis_label, font: @marker_font)
|
585
605
|
text_renderer.add_to_render_queue(@raw_columns, 1.0, 0.0, x_axis_label_y_coordinate)
|
586
606
|
end
|
587
607
|
|
588
608
|
if @y_axis_label
|
589
609
|
# Y Axis, rotated vertically
|
590
|
-
text_renderer = Gruff::Renderer::Text.new(@y_axis_label, font: @
|
591
|
-
text_renderer.add_to_render_queue(1.0, @raw_rows, @left_margin +
|
610
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, @y_axis_label, font: @marker_font, rotation: -90)
|
611
|
+
text_renderer.add_to_render_queue(1.0, @raw_rows, @left_margin + (marker_caps_height / 2.0), 0.0, Magick::CenterGravity)
|
592
612
|
end
|
593
613
|
end
|
594
614
|
|
@@ -596,19 +616,19 @@ module Gruff
|
|
596
616
|
def draw_line_markers
|
597
617
|
return if @hide_line_markers
|
598
618
|
|
599
|
-
increment_scaled = @graph_height
|
619
|
+
increment_scaled = @graph_height / (@spread / @increment)
|
600
620
|
|
601
621
|
# Draw horizontal line markers and annotate with numbers
|
602
622
|
(0..marker_count).each do |index|
|
603
|
-
y = @graph_top + @graph_height - index
|
623
|
+
y = @graph_top + @graph_height - (index * increment_scaled)
|
604
624
|
|
605
|
-
|
606
|
-
|
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
|
607
627
|
|
608
628
|
unless @hide_line_numbers
|
609
|
-
marker_label = BigDecimal(index.to_s) * BigDecimal(@increment.to_s) + BigDecimal(minimum_value.to_s)
|
629
|
+
marker_label = (BigDecimal(index.to_s) * BigDecimal(@increment.to_s)) + BigDecimal(minimum_value.to_s)
|
610
630
|
label = y_axis_label(marker_label, @increment)
|
611
|
-
text_renderer = Gruff::Renderer::Text.new(label, font: @
|
631
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font)
|
612
632
|
text_renderer.add_to_render_queue(@graph_left - LABEL_MARGIN, 1.0, 0.0, y, Magick::EastGravity)
|
613
633
|
end
|
614
634
|
end
|
@@ -626,45 +646,46 @@ module Gruff
|
|
626
646
|
|
627
647
|
legend_labels = store.data.map(&:label)
|
628
648
|
legend_square_width = @legend_box_size # small square with color of this item
|
629
|
-
|
649
|
+
legend_label_lines = calculate_legend_label_widths_for_each_line(legend_labels, legend_square_width)
|
650
|
+
line_height = [legend_caps_height, legend_square_width].max + @legend_margin
|
630
651
|
|
631
|
-
current_x_offset = center(label_widths.first.sum)
|
632
652
|
current_y_offset = begin
|
633
653
|
if @legend_at_bottom
|
634
|
-
@graph_bottom + @legend_margin +
|
654
|
+
@graph_bottom + @legend_margin + legend_caps_height + LABEL_MARGIN + (@x_axis_label ? (LABEL_MARGIN * 2) + marker_caps_height : 0)
|
635
655
|
else
|
636
|
-
hide_title? ? @top_margin + @title_margin : @top_margin + @title_margin +
|
656
|
+
hide_title? ? @top_margin + @title_margin : @top_margin + @title_margin + title_caps_height
|
637
657
|
end
|
638
658
|
end
|
639
659
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
# Wrap to next line and shrink available graph dimensions
|
665
|
-
current_y_offset += line_height
|
660
|
+
index = 0
|
661
|
+
legend_label_lines.each do |(legend_labels_width, legend_labels_line)|
|
662
|
+
current_x_offset = center(legend_labels_width)
|
663
|
+
|
664
|
+
legend_labels_line.each do |legend_label|
|
665
|
+
unless legend_label.empty?
|
666
|
+
legend_label_width = calculate_width(@legend_font, legend_label)
|
667
|
+
|
668
|
+
# Draw label
|
669
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, legend_label, font: @legend_font)
|
670
|
+
text_renderer.add_to_render_queue(legend_label_width,
|
671
|
+
legend_square_width,
|
672
|
+
current_x_offset + (legend_square_width * 1.7),
|
673
|
+
current_y_offset,
|
674
|
+
Magick::CenterGravity)
|
675
|
+
|
676
|
+
# Now draw box with color of this dataset
|
677
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: store.data[index].color)
|
678
|
+
rect_renderer.render(current_x_offset,
|
679
|
+
current_y_offset,
|
680
|
+
current_x_offset + legend_square_width,
|
681
|
+
current_y_offset + legend_square_width)
|
682
|
+
|
683
|
+
current_x_offset += legend_label_width + (legend_square_width * 2.7)
|
666
684
|
end
|
685
|
+
index += 1
|
667
686
|
end
|
687
|
+
|
688
|
+
current_y_offset += line_height
|
668
689
|
end
|
669
690
|
end
|
670
691
|
|
@@ -672,32 +693,24 @@ module Gruff
|
|
672
693
|
def draw_title
|
673
694
|
return if hide_title?
|
674
695
|
|
675
|
-
|
676
|
-
font_weight = @bold_title ? Magick::BoldWeight : Magick::NormalWeight
|
677
|
-
font_size = @title_font_size
|
678
|
-
|
679
|
-
metrics = Renderer::Text.metrics(@title, font, font_size, font_weight)
|
696
|
+
metrics = text_metrics(@title_font, @title)
|
680
697
|
if metrics.width > @raw_columns
|
681
|
-
|
698
|
+
@title_font.size = @title_font.size * (@raw_columns / metrics.width) * 0.95
|
682
699
|
end
|
683
|
-
|
700
|
+
|
701
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, @title, font: @title_font)
|
684
702
|
text_renderer.add_to_render_queue(@raw_columns, 1.0, 0, @top_margin)
|
685
703
|
end
|
686
704
|
|
687
705
|
# Draws column labels below graph, centered over x_offset
|
688
|
-
|
689
|
-
# TODO Allow WestGravity as an option
|
690
|
-
def draw_label(x_offset, index, gravity = Magick::NorthGravity)
|
706
|
+
def draw_label(x_offset, index, gravity = Magick::NorthGravity, &block)
|
691
707
|
draw_unique_label(index) do
|
692
708
|
y_offset = @graph_bottom + LABEL_MARGIN
|
693
|
-
|
694
|
-
# TESTME
|
695
|
-
# FIXME: Consider chart types other than bar
|
696
|
-
# TODO: See if index.odd? is the best stragegy
|
697
709
|
y_offset += @label_stagger_height if index.odd?
|
698
710
|
|
699
711
|
if x_offset >= @graph_left && x_offset <= @graph_right
|
700
712
|
draw_label_at(1.0, 1.0, x_offset, y_offset, @labels[index], gravity)
|
713
|
+
yield if block
|
701
714
|
end
|
702
715
|
end
|
703
716
|
end
|
@@ -714,24 +727,30 @@ module Gruff
|
|
714
727
|
|
715
728
|
def draw_label_at(width, height, x, y, text, gravity = Magick::NorthGravity)
|
716
729
|
label_text = truncate_label_text(text)
|
717
|
-
text_renderer = Gruff::Renderer::Text.new(label_text, font: @
|
730
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, label_text, font: @marker_font)
|
718
731
|
text_renderer.add_to_render_queue(width, height, x, y, gravity)
|
719
732
|
end
|
720
733
|
|
721
734
|
# Draws the data value over the data point in bar graphs
|
722
|
-
def draw_value_label(x_offset, y_offset, data_point,
|
723
|
-
return if @hide_line_markers
|
735
|
+
def draw_value_label(width, height, x_offset, y_offset, data_point, gravity = Magick::CenterGravity)
|
736
|
+
return if @hide_line_markers
|
724
737
|
|
725
|
-
|
726
|
-
text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
|
738
|
+
draw_label_at(width, height, x_offset, y_offset, data_point, gravity)
|
727
739
|
end
|
728
740
|
|
729
741
|
# Shows an error message because you have no data.
|
730
742
|
def draw_no_data
|
731
|
-
|
743
|
+
font = @title_font.dup
|
744
|
+
font.size = 80
|
745
|
+
font.bold = false
|
746
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, @no_data_message, font: font)
|
732
747
|
text_renderer.render(@raw_columns, @raw_rows, 0, 0, Magick::CenterGravity)
|
733
748
|
end
|
734
749
|
|
750
|
+
def draw_graph
|
751
|
+
raise 'Should implement this method at inherited class.'
|
752
|
+
end
|
753
|
+
|
735
754
|
# Resets everything to defaults (except data).
|
736
755
|
def reset_themes
|
737
756
|
@theme_options = {}
|
@@ -747,7 +766,7 @@ module Gruff
|
|
747
766
|
end
|
748
767
|
|
749
768
|
def clip_value_if_greater_than(value, max_value) # :nodoc:
|
750
|
-
|
769
|
+
value > max_value ? max_value : value
|
751
770
|
end
|
752
771
|
|
753
772
|
def significant(i) # :nodoc:
|
@@ -793,16 +812,16 @@ module Gruff
|
|
793
812
|
|
794
813
|
private
|
795
814
|
|
796
|
-
def
|
797
|
-
hide_bottom_label_area? ? 0 : calculate_caps_height(@
|
815
|
+
def marker_caps_height
|
816
|
+
hide_bottom_label_area? ? 0 : calculate_caps_height(@marker_font)
|
798
817
|
end
|
799
818
|
|
800
|
-
def
|
801
|
-
hide_title? ? 0 : calculate_caps_height(@
|
819
|
+
def title_caps_height
|
820
|
+
hide_title? ? 0 : calculate_caps_height(@title_font) * @title.lines.to_a.size
|
802
821
|
end
|
803
822
|
|
804
|
-
def
|
805
|
-
@hide_legend ? 0 : calculate_caps_height(@
|
823
|
+
def legend_caps_height
|
824
|
+
@hide_legend ? 0 : calculate_caps_height(@legend_font)
|
806
825
|
end
|
807
826
|
|
808
827
|
def graph_right_margin
|
@@ -813,7 +832,11 @@ module Gruff
|
|
813
832
|
# Make space for half the width of the rightmost column label.
|
814
833
|
# Might be greater than the number of columns if between-style bar markers are used.
|
815
834
|
last_label = @labels.keys.max.to_i
|
816
|
-
|
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
|
817
840
|
end
|
818
841
|
|
819
842
|
def setup_left_margin
|
@@ -821,34 +844,32 @@ module Gruff
|
|
821
844
|
|
822
845
|
text = begin
|
823
846
|
if @has_left_labels
|
824
|
-
@labels.values.reduce('') { |value, memo|
|
847
|
+
@labels.values.reduce('') { |value, memo| value.to_s.length > memo.to_s.length ? value : memo }
|
825
848
|
else
|
826
|
-
y_axis_label(maximum_value
|
849
|
+
y_axis_label(maximum_value, @increment)
|
827
850
|
end
|
828
851
|
end
|
829
|
-
longest_left_label_width = calculate_width(@
|
830
|
-
longest_left_label_width *= 1.25 if @has_left_labels
|
852
|
+
longest_left_label_width = calculate_width(@marker_font, truncate_label_text(text))
|
831
853
|
|
832
854
|
# Shift graph if left line numbers are hidden
|
833
|
-
line_number_width =
|
855
|
+
line_number_width = !@has_left_labels && (@hide_line_markers || @hide_line_numbers) ? 0.0 : (longest_left_label_width + LABEL_MARGIN)
|
834
856
|
|
835
|
-
@left_margin + line_number_width + (@y_axis_label.nil? ? 0.0 :
|
857
|
+
@left_margin + line_number_width + (@y_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2))
|
836
858
|
end
|
837
859
|
|
838
860
|
def setup_top_margin
|
839
861
|
# When @hide title, leave a title_margin space for aesthetics.
|
840
862
|
# Same with @hide_legend
|
841
863
|
@top_margin +
|
842
|
-
(hide_title? ? @title_margin :
|
843
|
-
(
|
864
|
+
(hide_title? ? @title_margin : title_caps_height + @title_margin) +
|
865
|
+
(@hide_legend || @legend_at_bottom ? @legend_margin : calculate_legend_height + @legend_margin)
|
844
866
|
end
|
845
867
|
|
846
868
|
def setup_bottom_margin
|
847
|
-
graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin +
|
869
|
+
graph_bottom_margin = hide_bottom_label_area? ? @bottom_margin : @bottom_margin + marker_caps_height + LABEL_MARGIN
|
848
870
|
graph_bottom_margin += (calculate_legend_height + @legend_margin) if @legend_at_bottom
|
849
871
|
|
850
|
-
x_axis_label_height = @x_axis_label.nil? ? 0.0 :
|
851
|
-
# FIXME: Consider chart types other than bar
|
872
|
+
x_axis_label_height = @x_axis_label.nil? ? 0.0 : marker_caps_height + (LABEL_MARGIN * 2)
|
852
873
|
@raw_rows - graph_bottom_margin - x_axis_label_height - @label_stagger_height
|
853
874
|
end
|
854
875
|
|
@@ -868,29 +889,31 @@ module Gruff
|
|
868
889
|
# Return a formatted string representing a number value that should be
|
869
890
|
# printed as a label.
|
870
891
|
def label(value, increment)
|
871
|
-
label =
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
892
|
+
label = begin
|
893
|
+
if increment
|
894
|
+
if increment >= 10 || (increment * 1) == (increment * 1).to_i.to_f
|
895
|
+
sprintf('%0i', value)
|
896
|
+
elsif increment >= 1.0 || (increment * 10) == (increment * 10).to_i.to_f
|
897
|
+
sprintf('%0.1f', value)
|
898
|
+
elsif increment >= 0.1 || (increment * 100) == (increment * 100).to_i.to_f
|
899
|
+
sprintf('%0.2f', value)
|
900
|
+
elsif increment >= 0.01 || (increment * 1000) == (increment * 1000).to_i.to_f
|
901
|
+
sprintf('%0.3f', value)
|
902
|
+
elsif increment >= 0.001 || (increment * 10_000) == (increment * 10_000).to_i.to_f
|
903
|
+
sprintf('%0.4f', value)
|
904
|
+
else
|
905
|
+
value.to_s
|
906
|
+
end
|
907
|
+
elsif (@spread % (marker_count == 0 ? 1 : marker_count) == 0) || !@y_axis_increment.nil?
|
908
|
+
value.to_i.to_s
|
909
|
+
elsif @spread > 10.0
|
910
|
+
sprintf('%0i', value)
|
911
|
+
elsif @spread >= 3.0
|
912
|
+
sprintf('%0.2f', value)
|
913
|
+
else
|
914
|
+
value.to_s
|
915
|
+
end
|
916
|
+
end
|
894
917
|
|
895
918
|
parts = label.split('.')
|
896
919
|
parts[0] = parts[0].commify
|
@@ -914,44 +937,31 @@ module Gruff
|
|
914
937
|
end
|
915
938
|
|
916
939
|
def calculate_legend_label_widths_for_each_line(legend_labels, legend_square_width)
|
917
|
-
|
918
|
-
|
940
|
+
label_widths = [[]]
|
941
|
+
label_lines = [[]]
|
919
942
|
legend_labels.each do |label|
|
920
|
-
width = calculate_width(@
|
921
|
-
label_width = width + legend_square_width * 2.7
|
943
|
+
width = calculate_width(@legend_font, label)
|
944
|
+
label_width = width + (legend_square_width * 2.7)
|
922
945
|
label_widths.last.push label_width
|
946
|
+
label_lines.last.push label
|
923
947
|
|
924
948
|
if label_widths.last.sum > (@raw_columns * 0.9)
|
925
949
|
label_widths.push [label_widths.last.pop]
|
950
|
+
label_lines.push [label_lines.last.pop]
|
926
951
|
end
|
927
952
|
end
|
928
953
|
|
929
|
-
label_widths
|
954
|
+
label_widths.map(&:sum).zip(label_lines)
|
930
955
|
end
|
931
956
|
|
932
957
|
def calculate_legend_height
|
933
958
|
return 0.0 if @hide_legend
|
934
959
|
|
935
960
|
legend_labels = store.data.map(&:label)
|
936
|
-
|
937
|
-
|
938
|
-
legend_height = 0.0
|
939
|
-
|
940
|
-
legend_labels.each_with_index do |legend_label, _index|
|
941
|
-
next if legend_label.empty?
|
942
|
-
|
943
|
-
label_widths.first.shift
|
944
|
-
if label_widths.first.empty?
|
945
|
-
label_widths.shift
|
946
|
-
line_height = [@legend_caps_height, legend_square_width].max + @legend_margin
|
947
|
-
unless label_widths.empty?
|
948
|
-
# Wrap to next line and shrink available graph dimensions
|
949
|
-
legend_height += line_height
|
950
|
-
end
|
951
|
-
end
|
952
|
-
end
|
961
|
+
legend_label_lines = calculate_legend_label_widths_for_each_line(legend_labels, @legend_box_size)
|
962
|
+
line_height = [legend_caps_height, @legend_box_size].max
|
953
963
|
|
954
|
-
|
964
|
+
(line_height * legend_label_lines.count) + (@legend_margin * (legend_label_lines.count - 1))
|
955
965
|
end
|
956
966
|
|
957
967
|
# Returns the height of the capital letter 'X' for the current font and
|
@@ -959,8 +969,19 @@ module Gruff
|
|
959
969
|
#
|
960
970
|
# Not scaled since it deals with dimensions that the regular scaling will
|
961
971
|
# handle.
|
962
|
-
def calculate_caps_height(
|
963
|
-
|
972
|
+
def calculate_caps_height(font)
|
973
|
+
calculate_height(font, 'X')
|
974
|
+
end
|
975
|
+
|
976
|
+
# Returns the height of a string at this point size.
|
977
|
+
#
|
978
|
+
# Not scaled since it deals with dimensions that the regular scaling will
|
979
|
+
# handle.
|
980
|
+
def calculate_height(font, text)
|
981
|
+
text = text.to_s
|
982
|
+
return 0 if text.empty?
|
983
|
+
|
984
|
+
metrics = text_metrics(font, text)
|
964
985
|
metrics.height
|
965
986
|
end
|
966
987
|
|
@@ -968,20 +989,24 @@ module Gruff
|
|
968
989
|
#
|
969
990
|
# Not scaled since it deals with dimensions that the regular
|
970
991
|
# scaling will handle.
|
971
|
-
def calculate_width(
|
992
|
+
def calculate_width(font, text)
|
972
993
|
text = text.to_s
|
973
994
|
return 0 if text.empty?
|
974
995
|
|
975
|
-
metrics =
|
996
|
+
metrics = text_metrics(font, text)
|
976
997
|
metrics.width
|
977
998
|
end
|
978
999
|
|
1000
|
+
def text_metrics(font, text)
|
1001
|
+
Gruff::Renderer::Text.new(renderer, text, font: font).metrics
|
1002
|
+
end
|
1003
|
+
|
979
1004
|
def calculate_increment
|
980
1005
|
if @y_axis_increment.nil?
|
981
1006
|
# Try to use a number of horizontal lines that will come out even.
|
982
1007
|
#
|
983
1008
|
# TODO Do the same for larger numbers...100, 75, 50, 25
|
984
|
-
@increment =
|
1009
|
+
@increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1
|
985
1010
|
else
|
986
1011
|
# TODO: Make this work for negative values
|
987
1012
|
self.marker_count = (@spread / @y_axis_increment).to_i
|
@@ -989,9 +1014,13 @@ module Gruff
|
|
989
1014
|
end
|
990
1015
|
end
|
991
1016
|
|
992
|
-
# Used for degree
|
1017
|
+
# Used for degree <=> radian conversions
|
993
1018
|
def deg2rad(angle)
|
994
|
-
angle *
|
1019
|
+
(angle * Math::PI) / 180.0
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
def rad2deg(angle)
|
1023
|
+
(angle / Math::PI) * 180.0
|
995
1024
|
end
|
996
1025
|
end
|
997
1026
|
|