gruff 0.17.0 → 0.18.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 +1 -1
- data/CHANGELOG.md +7 -0
- data/README.md +6 -2
- data/lib/gruff/area.rb +1 -1
- data/lib/gruff/bar.rb +2 -3
- data/lib/gruff/base.rb +29 -26
- data/lib/gruff/bezier.rb +1 -1
- data/lib/gruff/{box_plot.rb → box.rb} +5 -5
- data/lib/gruff/bubble.rb +99 -0
- data/lib/gruff/candlestick.rb +1 -1
- data/lib/gruff/histogram.rb +0 -1
- data/lib/gruff/line.rb +27 -6
- data/lib/gruff/mini/legend.rb +4 -0
- data/lib/gruff/pie.rb +1 -1
- data/lib/gruff/renderer/circle.rb +3 -1
- data/lib/gruff/scatter.rb +36 -41
- data/lib/gruff/side_bar.rb +2 -3
- data/lib/gruff/stacked_area.rb +1 -1
- data/lib/gruff/store/xy_data.rb +8 -9
- data/lib/gruff/store/xy_pointsizes_data.rb +60 -0
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +3 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b7a28fa80f8c598a927ce29242c2014a38cc21b1fc1fc43a80285239b448176
|
4
|
+
data.tar.gz: 13a94cca6581eebccafd48de9d48daa1acd4c085989cd996a6054fd63bfa8f8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 165988fc75b87fcd15f789ee9b178cbbe4ddc32cd0090f62cdcd36041806223b65c59406cd855038e0881ffb0b260df146aec74de7caff5ee0fc32df01447268
|
7
|
+
data.tar.gz: c5dcff59b2f8ab733fc99bafee1472ab0c014fbb106bec26fca167880b585cc9e61a6cc00b4e08aa02d39a2fe92d2291a7363a13ed16673a06898edcd15fb081
|
data/.github/workflows/ci.yml
CHANGED
@@ -37,7 +37,7 @@ jobs:
|
|
37
37
|
matrix:
|
38
38
|
ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1']
|
39
39
|
imagemagick-version:
|
40
|
-
- { full: 7.1.0-
|
40
|
+
- { full: 7.1.0-39, major-minor: '7.0' }
|
41
41
|
name: Ruby ${{ matrix.ruby-version }}
|
42
42
|
steps:
|
43
43
|
- uses: actions/checkout@v3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.18.0
|
4
|
+
- Add Gruff::Bubble (#604)
|
5
|
+
- Rename Gruff::BoxPlot to Gruff::Box (#603)
|
6
|
+
- Mark Gruff::Scatter#disable_significant_rounding_x_axis= as deprecated (#602)
|
7
|
+
- Mark Gruff::Scatter#x_label_margin= as deprecated (#601)
|
8
|
+
- Mark Gruff::Scatter#use_vertical_x_labels= as deprecated (#600)
|
9
|
+
|
3
10
|
## 0.17.0
|
4
11
|
- Add Gruff::Base#label_rotation= method to rotate bottom label (#599)
|
5
12
|
- Mark #label_stagger_height= as deprecated (#598)
|
data/README.md
CHANGED
@@ -126,14 +126,18 @@ In progress!
|
|
126
126
|
|
127
127
|
![Histogram chart](https://raw.github.com/topfunky/gruff/master/test/expected/histogram.png)
|
128
128
|
|
129
|
-
### Box
|
129
|
+
### Box chart
|
130
130
|
|
131
|
-
![Box
|
131
|
+
![Box chart](https://raw.github.com/topfunky/gruff/master/test/expected/box.png)
|
132
132
|
|
133
133
|
### Candlestick
|
134
134
|
|
135
135
|
![Candlestick](https://raw.github.com/topfunky/gruff/master/test/expected/candlestick.png)
|
136
136
|
|
137
|
+
### Bubble chart
|
138
|
+
|
139
|
+
![Bubble chart](https://raw.github.com/topfunky/gruff/master/test/expected/bubble.png)
|
140
|
+
|
137
141
|
## Documentation
|
138
142
|
|
139
143
|
http://www.rubydoc.info/github/topfunky/gruff/frames
|
data/lib/gruff/area.rb
CHANGED
data/lib/gruff/bar.rb
CHANGED
@@ -114,7 +114,6 @@ private
|
|
114
114
|
minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
|
115
115
|
)
|
116
116
|
|
117
|
-
group_spacing = @group_spacing * @scale
|
118
117
|
group_left_x = @graph_left
|
119
118
|
|
120
119
|
normalized_group_bars.each_with_index do |group_bars, group_index|
|
@@ -140,7 +139,7 @@ private
|
|
140
139
|
label_center = group_left_x + ((right_x - group_left_x) / 2.0)
|
141
140
|
draw_label(label_center, group_index)
|
142
141
|
|
143
|
-
group_left_x = right_x + padding + group_spacing
|
142
|
+
group_left_x = right_x + padding + @group_spacing
|
144
143
|
end
|
145
144
|
|
146
145
|
# Draw the last label if requested
|
@@ -148,7 +147,7 @@ private
|
|
148
147
|
end
|
149
148
|
|
150
149
|
def calculate_spacing
|
151
|
-
@
|
150
|
+
@group_spacing * (column_count - 1)
|
152
151
|
end
|
153
152
|
|
154
153
|
def proc_text_metrics
|
data/lib/gruff/base.rb
CHANGED
@@ -607,8 +607,8 @@ module Gruff
|
|
607
607
|
end
|
608
608
|
|
609
609
|
def calculate_spread
|
610
|
-
@spread = maximum_value - minimum_value
|
611
|
-
@spread = @spread > 0 ? @spread : 1
|
610
|
+
@spread = maximum_value.to_f - minimum_value.to_f
|
611
|
+
@spread = @spread > 0 ? @spread : 1.0
|
612
612
|
end
|
613
613
|
|
614
614
|
def hide_title?
|
@@ -663,7 +663,7 @@ module Gruff
|
|
663
663
|
def draw_line_markers
|
664
664
|
return if @hide_line_markers
|
665
665
|
|
666
|
-
increment_scaled = @graph_height / (@spread / @increment)
|
666
|
+
increment_scaled = (@graph_height / (@spread / @increment)).to_f
|
667
667
|
|
668
668
|
# Draw horizontal line markers and annotate with numbers
|
669
669
|
(0..marker_count).each do |index|
|
@@ -766,25 +766,14 @@ module Gruff
|
|
766
766
|
text_renderer.add_to_render_queue(@raw_columns, 1.0, 0, @top_margin)
|
767
767
|
end
|
768
768
|
|
769
|
-
# Draws column labels below graph, centered over
|
770
|
-
def draw_label(
|
769
|
+
# Draws column labels below graph, centered over x
|
770
|
+
def draw_label(x, index, gravity = Magick::NorthGravity, &block)
|
771
771
|
draw_unique_label(index) do
|
772
|
-
|
773
|
-
|
774
|
-
|
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
|
772
|
+
if x >= @graph_left && x <= @graph_right
|
773
|
+
y = @graph_bottom
|
774
|
+
x_offset, y_offset = calculate_label_offset(@marker_font, @labels[index], LABEL_MARGIN, @label_rotation)
|
786
775
|
|
787
|
-
draw_label_at(1.0, 1.0, x_offset, y_offset, @labels[index], gravity: gravity, rotation: @label_rotation)
|
776
|
+
draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, @labels[index], gravity: gravity, rotation: @label_rotation)
|
788
777
|
yield if block
|
789
778
|
end
|
790
779
|
end
|
@@ -831,10 +820,6 @@ module Gruff
|
|
831
820
|
@theme_options = {}
|
832
821
|
end
|
833
822
|
|
834
|
-
def scale(value)
|
835
|
-
value * @scale
|
836
|
-
end
|
837
|
-
|
838
823
|
def clip_value_if_greater_than(value, max_value)
|
839
824
|
value > max_value ? max_value : value
|
840
825
|
end
|
@@ -1123,14 +1108,32 @@ module Gruff
|
|
1123
1108
|
# Try to use a number of horizontal lines that will come out even.
|
1124
1109
|
#
|
1125
1110
|
# TODO Do the same for larger numbers...100, 75, 50, 25
|
1126
|
-
@increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1
|
1111
|
+
@increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1.0
|
1127
1112
|
else
|
1128
1113
|
# TODO: Make this work for negative values
|
1129
1114
|
self.marker_count = (@spread / @y_axis_increment).to_i
|
1130
|
-
@increment = @y_axis_increment
|
1115
|
+
@increment = @y_axis_increment.to_f
|
1131
1116
|
end
|
1132
1117
|
end
|
1133
1118
|
|
1119
|
+
def calculate_label_offset(font, label, margin, rotation)
|
1120
|
+
width = calculate_width(font, label, rotation: rotation)
|
1121
|
+
height = calculate_height(font, label, rotation: rotation)
|
1122
|
+
x_offset = begin
|
1123
|
+
case rotation
|
1124
|
+
when 0
|
1125
|
+
0
|
1126
|
+
when 0..45
|
1127
|
+
width / 2.0
|
1128
|
+
when -45..0
|
1129
|
+
-(width / 2.0)
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
y_offset = (height / 2.0) > margin ? (height / 2.0) : margin
|
1133
|
+
|
1134
|
+
[x_offset, y_offset]
|
1135
|
+
end
|
1136
|
+
|
1134
1137
|
# Used for degree <=> radian conversions
|
1135
1138
|
def deg2rad(angle)
|
1136
1139
|
(angle * Math::PI) / 180.0
|
data/lib/gruff/bezier.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Here's how to set up a Gruff::
|
4
|
+
# Here's how to set up a Gruff::Box.
|
5
5
|
#
|
6
|
-
# g = Gruff::
|
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::
|
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
|
|
@@ -90,7 +90,7 @@ private
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def normalized_boxes
|
93
|
-
@normalized_boxes ||= store.norm_data.map { |data| Gruff::
|
93
|
+
@normalized_boxes ||= store.norm_data.map { |data| Gruff::Box::BoxData.new(data.label, data.points, data.color) }
|
94
94
|
end
|
95
95
|
|
96
96
|
def column_count
|
@@ -98,7 +98,7 @@ private
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def calculate_spacing
|
101
|
-
|
101
|
+
column_count - 1
|
102
102
|
end
|
103
103
|
|
104
104
|
# @private
|
data/lib/gruff/bubble.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::Bubble.
|
5
|
+
#
|
6
|
+
# g = Gruff::Bubble.new
|
7
|
+
# g.title = 'Bubble plot'
|
8
|
+
# g.write('bubble.png')
|
9
|
+
#
|
10
|
+
class Gruff::Bubble < Gruff::Scatter
|
11
|
+
# Specifies the filling opacity in area graph. Default is +0.6+.
|
12
|
+
attr_writer :fill_opacity
|
13
|
+
|
14
|
+
# Specifies the stroke width in line. Default is +1.0+.
|
15
|
+
attr_writer :stroke_width
|
16
|
+
|
17
|
+
# The first parameter is the name of the dataset. The next two are the
|
18
|
+
# x and y axis data points contain in their own array in that respective
|
19
|
+
# order. The 4th argument represents sizes of points.
|
20
|
+
# The final parameter is the color.
|
21
|
+
#
|
22
|
+
# Can be called multiple times with different datasets for a multi-valued
|
23
|
+
# graph.
|
24
|
+
#
|
25
|
+
# If the color argument is nil, the next color from the default theme will
|
26
|
+
# be used.
|
27
|
+
#
|
28
|
+
# @note If you want to use a preset theme, you must set it before calling {#data}.
|
29
|
+
#
|
30
|
+
# @param name [String, Symbol] containing the name of the dataset.
|
31
|
+
# @param x_data_points [Array] An Array of x-axis data points.
|
32
|
+
# @param y_data_points [Array] An Array of y-axis data points.
|
33
|
+
# @param point_sizes [Array] An Array of sizes for points.
|
34
|
+
# @param color [String] The hex string for the color of the dataset. Defaults to nil.
|
35
|
+
#
|
36
|
+
# @raise [ArgumentError] Data points contain nil values.
|
37
|
+
# This error will get raised if either the x or y axis data points array
|
38
|
+
# contains a +nil+ value. The graph will not make an assumption
|
39
|
+
# as how to graph +nil+.
|
40
|
+
# @raise [ArgumentError] +x_data_points+ is empty.
|
41
|
+
# This error is raised when the array for the x-axis points are empty
|
42
|
+
# @raise [ArgumentError] +y_data_points+ is empty.
|
43
|
+
# This error is raised when the array for the y-axis points are empty.
|
44
|
+
# @raise [ArgumentError] +point_sizes+ is empty.
|
45
|
+
# This error is raised when the array for the point_sizes are empty
|
46
|
+
# @raise [ArgumentError] +x_data_points.length != y_data_points.length+.
|
47
|
+
# Error means that the x and y axis point arrays do not match in length.
|
48
|
+
# @raise [ArgumentError] +x_data_points.length != point_sizes.length+.
|
49
|
+
# Error means that the x and point_sizes arrays do not match in length.
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# g = Gruff::Bubble.new
|
53
|
+
# g.title = "Bubble Graph"
|
54
|
+
# g.data :A, [-1, 19, -4, -23], [-35, 21, 23, -4], [4.5, 1.0, 2.1, 0.9]
|
55
|
+
#
|
56
|
+
def data(name, x_data_points = [], y_data_points = [], point_sizes = [], color = nil)
|
57
|
+
# make sure it's an array
|
58
|
+
x_data_points = Array(x_data_points)
|
59
|
+
y_data_points = Array(y_data_points)
|
60
|
+
point_sizes = Array(point_sizes)
|
61
|
+
|
62
|
+
raise ArgumentError, 'Data Points contain nil Value!' if x_data_points.include?(nil) || y_data_points.include?(nil)
|
63
|
+
raise ArgumentError, 'x_data_points is empty!' if x_data_points.empty?
|
64
|
+
raise ArgumentError, 'y_data_points is empty!' if y_data_points.empty?
|
65
|
+
raise ArgumentError, 'point_sizes is empty!' if point_sizes.empty?
|
66
|
+
raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
|
67
|
+
raise ArgumentError, 'x_data_points.length != point_sizes.length!' if x_data_points.length != point_sizes.length
|
68
|
+
|
69
|
+
store.add(name, x_data_points, y_data_points, point_sizes, color)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def initialize_store
|
75
|
+
@store = Gruff::Store.new(Gruff::Store::XYPointsizeData)
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize_attributes
|
79
|
+
super
|
80
|
+
|
81
|
+
@fill_opacity = 0.6
|
82
|
+
@stroke_width = 1.0
|
83
|
+
end
|
84
|
+
|
85
|
+
def draw_graph
|
86
|
+
store.norm_data.each do |data_row|
|
87
|
+
data_row.coordinate_and_pointsizes.each do |x_value, y_value, point_size|
|
88
|
+
next if y_value.nil? || x_value.nil?
|
89
|
+
|
90
|
+
new_x = @graph_left + (x_value * @graph_width)
|
91
|
+
new_y = @graph_bottom - (y_value * @graph_height)
|
92
|
+
diameter = @graph_width / (@marker_count * x_increment) * point_size.to_f
|
93
|
+
|
94
|
+
Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: @stroke_width, opacity: @fill_opacity)
|
95
|
+
.render(new_x, new_y, new_x - (diameter / 2.0), new_y)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/gruff/candlestick.rb
CHANGED
data/lib/gruff/histogram.rb
CHANGED
data/lib/gruff/line.rb
CHANGED
@@ -81,6 +81,30 @@ class Gruff::Line < Gruff::Base
|
|
81
81
|
@reference_lines[:baseline][:color] = new_value
|
82
82
|
end
|
83
83
|
|
84
|
+
# Input the data in the graph.
|
85
|
+
#
|
86
|
+
# Parameters are an array where the first element is the name of the dataset
|
87
|
+
# and the value is an array of values to plot.
|
88
|
+
#
|
89
|
+
# Can be called multiple times with different datasets for a multi-valued
|
90
|
+
# graph.
|
91
|
+
#
|
92
|
+
# If the color argument is nil, the next color from the default theme will
|
93
|
+
# be used.
|
94
|
+
#
|
95
|
+
# @param name [String, Symbol] The name of the dataset.
|
96
|
+
# @param data_points [Array] The array of dataset.
|
97
|
+
# @param color [String] The color for drawing graph of dataset.
|
98
|
+
#
|
99
|
+
# @note
|
100
|
+
# If you want to use a preset theme, you must set it before calling {#data}.
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
|
104
|
+
def data(name, data_points = [], color = nil)
|
105
|
+
store.add(name, nil, data_points, color)
|
106
|
+
end
|
107
|
+
|
84
108
|
# This method allows one to plot a dataset with both X and Y data.
|
85
109
|
#
|
86
110
|
# @overload dataxy(name, x_data_points = [], y_data_points = [], color = nil)
|
@@ -131,7 +155,7 @@ class Gruff::Line < Gruff::Base
|
|
131
155
|
raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
|
132
156
|
|
133
157
|
# call the existing data routine for the x/y data.
|
134
|
-
store.add(name, y_data_points, color
|
158
|
+
store.add(name, x_data_points, y_data_points, color)
|
135
159
|
end
|
136
160
|
|
137
161
|
private
|
@@ -177,6 +201,7 @@ private
|
|
177
201
|
def draw_graph
|
178
202
|
# Check to see if more than one datapoint was given. NaN can result otherwise.
|
179
203
|
@x_increment = column_count > 1 ? @graph_width / (column_count - 1) : @graph_width
|
204
|
+
@x_increment = @x_increment.to_f
|
180
205
|
|
181
206
|
@reference_lines.each_value do |curr_reference_line|
|
182
207
|
draw_horizontal_reference_line(curr_reference_line) if curr_reference_line.key?(:norm_value)
|
@@ -194,7 +219,7 @@ private
|
|
194
219
|
# use the old method: equally spaced points along the x-axis
|
195
220
|
@graph_left + (@x_increment * index)
|
196
221
|
else
|
197
|
-
|
222
|
+
@graph_left + (x_data * @graph_width)
|
198
223
|
end
|
199
224
|
end
|
200
225
|
draw_label_for_x_data(x_data, new_x, index)
|
@@ -287,10 +312,6 @@ private
|
|
287
312
|
end
|
288
313
|
end
|
289
314
|
|
290
|
-
def get_x_coord(x_data_point, width, offset)
|
291
|
-
(x_data_point * width) + offset
|
292
|
-
end
|
293
|
-
|
294
315
|
def contains_one_point_only?(data_row)
|
295
316
|
data_row.y_points.compact.count == 1
|
296
317
|
end
|
data/lib/gruff/mini/legend.rb
CHANGED
data/lib/gruff/pie.rb
CHANGED
@@ -38,7 +38,7 @@ class Gruff::Pie < Gruff::Base
|
|
38
38
|
|
39
39
|
# Can be used to make the pie start cutting slices at the top (-90.0)
|
40
40
|
# or at another angle. Default is +-90.0+, which starts at 3 o'clock.
|
41
|
-
# @deprecated Please use
|
41
|
+
# @deprecated Please use {#start_degree=} instead.
|
42
42
|
def zero_degree=(value)
|
43
43
|
warn '#zero_degree= is deprecated. Please use `start_degree` attribute instead'
|
44
44
|
@start_degree = value
|
@@ -3,15 +3,17 @@
|
|
3
3
|
module Gruff
|
4
4
|
# @private
|
5
5
|
class Renderer::Circle
|
6
|
-
def initialize(renderer, color:, width: 1.0)
|
6
|
+
def initialize(renderer, color:, width: 1.0, opacity: 1.0)
|
7
7
|
@renderer = renderer
|
8
8
|
@color = color
|
9
9
|
@width = width
|
10
|
+
@opacity = opacity
|
10
11
|
end
|
11
12
|
|
12
13
|
def render(origin_x, origin_y, perim_x, perim_y)
|
13
14
|
@renderer.draw.push
|
14
15
|
@renderer.draw.fill(@color)
|
16
|
+
@renderer.draw.fill_opacity(@opacity)
|
15
17
|
@renderer.draw.stroke(@color)
|
16
18
|
@renderer.draw.stroke_width(@width)
|
17
19
|
@renderer.draw.circle(origin_x, origin_y, perim_x, perim_y)
|
data/lib/gruff/scatter.rb
CHANGED
@@ -24,24 +24,35 @@ class Gruff::Scatter < Gruff::Base
|
|
24
24
|
attr_writer :circle_radius
|
25
25
|
attr_writer :stroke_width
|
26
26
|
|
27
|
-
# Allow disabling the significant rounding when labeling the X axis.
|
28
|
-
# This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
|
29
|
-
attr_writer :disable_significant_rounding_x_axis
|
30
|
-
|
31
27
|
# Allow for vertical marker lines.
|
32
28
|
attr_writer :show_vertical_markers
|
33
29
|
|
34
|
-
# Allow using vertical labels in the X axis (and setting the label margin).
|
35
|
-
attr_writer :x_label_margin
|
36
|
-
attr_writer :use_vertical_x_labels
|
37
|
-
|
38
30
|
# Allow enabling vertical lines. When you have a lot of data, they can work great.
|
39
|
-
# @deprecated Please use
|
31
|
+
# @deprecated Please use {#show_vertical_markers=} instead.
|
40
32
|
def enable_vertical_line_markers=(value)
|
41
33
|
warn '#enable_vertical_line_markers= is deprecated. Please use `show_vertical_markers` attribute instead'
|
42
34
|
@show_vertical_markers = value
|
43
35
|
end
|
44
36
|
|
37
|
+
# Allow using vertical labels in the X axis.
|
38
|
+
# @deprecated Please use {Gruff::Base#label_rotation=} instead.
|
39
|
+
def use_vertical_x_labels=(_value)
|
40
|
+
warn '#use_vertical_x_labels= is deprecated. It is no longer effective. Please use `#label_rotation=` instead'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Allow using vertical labels in the X axis (and setting the label margin).
|
44
|
+
# @deprecated
|
45
|
+
def x_label_margin=(_value)
|
46
|
+
warn '#x_label_margin= is deprecated. It is no longer effective.'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Allow disabling the significant rounding when labeling the X axis.
|
50
|
+
# This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
|
51
|
+
# @deprecated
|
52
|
+
def disable_significant_rounding_x_axis=(_value)
|
53
|
+
warn '#disable_significant_rounding_x_axis= is deprecated. It is no longer effective.'
|
54
|
+
end
|
55
|
+
|
45
56
|
# The first parameter is the name of the dataset. The next two are the
|
46
57
|
# x and y axis data points contain in their own array in that respective
|
47
58
|
# order. The final parameter is the color.
|
@@ -55,11 +66,10 @@ class Gruff::Scatter < Gruff::Base
|
|
55
66
|
# @note If you want to use a preset theme, you must set it before calling {#data}.
|
56
67
|
#
|
57
68
|
# @param name [String, Symbol] containing the name of the dataset.
|
58
|
-
# @param x_data_points [Array] An Array of
|
59
|
-
# @param y_data_points [Array] An Array of
|
69
|
+
# @param x_data_points [Array] An Array of x-axis data points.
|
70
|
+
# @param y_data_points [Array] An Array of y-axis data points.
|
60
71
|
# @param color [String] The hex string for the color of the dataset. Defaults to nil.
|
61
72
|
#
|
62
|
-
#
|
63
73
|
# @raise [ArgumentError] Data points contain nil values.
|
64
74
|
# This error will get raised if either the x or y axis data points array
|
65
75
|
# contains a +nil+ value. The graph will not make an assumption
|
@@ -88,7 +98,7 @@ class Gruff::Scatter < Gruff::Base
|
|
88
98
|
raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
|
89
99
|
|
90
100
|
# Call the existing data routine for the x/y axis data
|
91
|
-
store.add(name, y_data_points, color
|
101
|
+
store.add(name, x_data_points, y_data_points, color)
|
92
102
|
end
|
93
103
|
|
94
104
|
alias dataxy data
|
@@ -102,16 +112,11 @@ private
|
|
102
112
|
def initialize_attributes
|
103
113
|
super
|
104
114
|
|
105
|
-
@baseline_x_color = @baseline_y_color = 'red'
|
106
|
-
@baseline_x_value = @baseline_y_value = nil
|
107
115
|
@circle_radius = nil
|
108
|
-
@disable_significant_rounding_x_axis = false
|
109
116
|
@show_vertical_markers = false
|
110
117
|
@marker_x_count = nil
|
111
118
|
@maximum_x_value = @minimum_x_value = nil
|
112
119
|
@stroke_width = nil
|
113
|
-
@use_vertical_x_labels = false
|
114
|
-
@x_label_margin = nil
|
115
120
|
end
|
116
121
|
|
117
122
|
def setup_drawing
|
@@ -141,11 +146,11 @@ private
|
|
141
146
|
data_row.coordinates.each do |x_value, y_value|
|
142
147
|
next if y_value.nil? || x_value.nil?
|
143
148
|
|
144
|
-
new_x =
|
145
|
-
new_y = @
|
149
|
+
new_x = @graph_left + (x_value * @graph_width)
|
150
|
+
new_y = @graph_bottom - (y_value * @graph_height)
|
146
151
|
|
147
152
|
# Reset each time to avoid thin-line errors
|
148
|
-
stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
|
153
|
+
stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4.0), 5.0)
|
149
154
|
circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
|
150
155
|
Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
|
151
156
|
end
|
@@ -155,7 +160,7 @@ private
|
|
155
160
|
def calculate_spread
|
156
161
|
super
|
157
162
|
@x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
|
158
|
-
@x_spread = @x_spread > 0 ? @x_spread : 1
|
163
|
+
@x_spread = @x_spread > 0 ? @x_spread : 1.0
|
159
164
|
end
|
160
165
|
|
161
166
|
def normalize
|
@@ -169,32 +174,27 @@ private
|
|
169
174
|
super
|
170
175
|
return if @hide_line_markers
|
171
176
|
|
172
|
-
increment_x_scaled = @graph_width / (@x_spread / x_increment)
|
177
|
+
increment_x_scaled = (@graph_width / (@x_spread / x_increment)).to_f
|
173
178
|
|
174
179
|
# Draw vertical line markers and annotate with numbers
|
175
180
|
(0..marker_x_count).each do |index|
|
176
181
|
# TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
|
177
182
|
if @show_vertical_markers
|
178
|
-
draw_marker_vertical_line(@graph_left +
|
183
|
+
draw_marker_vertical_line(@graph_left + (index * increment_x_scaled))
|
179
184
|
end
|
180
185
|
|
181
186
|
unless @hide_line_numbers
|
182
187
|
marker_label = (BigDecimal(index.to_s) * BigDecimal(x_increment.to_s)) + BigDecimal(@minimum_x_value.to_s)
|
183
|
-
y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
|
184
|
-
x_offset = get_x_coord(index, increment_x_scaled, @graph_left)
|
185
|
-
|
186
188
|
label = x_axis_label(marker_label, x_increment)
|
187
|
-
|
188
|
-
|
189
|
-
|
189
|
+
x = @graph_left + (increment_x_scaled * index)
|
190
|
+
y = @graph_bottom
|
191
|
+
x_offset, y_offset = calculate_label_offset(@marker_font, label, LABEL_MARGIN, @label_rotation)
|
192
|
+
|
193
|
+
draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, label, rotation: @label_rotation)
|
190
194
|
end
|
191
195
|
end
|
192
196
|
end
|
193
197
|
|
194
|
-
def get_x_coord(x_data_point, width, offset)
|
195
|
-
(x_data_point * width) + offset
|
196
|
-
end
|
197
|
-
|
198
198
|
def marker_x_count
|
199
199
|
# TODO: Do the same for larger numbers...100, 75, 50, 25
|
200
200
|
@marker_x_count ||= begin
|
@@ -215,15 +215,10 @@ private
|
|
215
215
|
def x_increment
|
216
216
|
@x_increment ||= begin
|
217
217
|
if @x_axis_increment.nil?
|
218
|
-
|
219
|
-
unless @disable_significant_rounding_x_axis
|
220
|
-
increment = significant(increment)
|
221
|
-
end
|
218
|
+
@x_spread > 0 ? significant(@x_spread / marker_x_count) : 1.0
|
222
219
|
else
|
223
|
-
|
220
|
+
@x_axis_increment.to_f
|
224
221
|
end
|
225
|
-
|
226
|
-
increment
|
227
222
|
end
|
228
223
|
end
|
229
224
|
end
|
data/lib/gruff/side_bar.rb
CHANGED
@@ -111,7 +111,6 @@ private
|
|
111
111
|
minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
|
112
112
|
)
|
113
113
|
|
114
|
-
group_spacing = @group_spacing * @scale
|
115
114
|
group_left_y = @graph_top
|
116
115
|
|
117
116
|
normalized_group_bars.each_with_index do |group_bars, group_index|
|
@@ -137,7 +136,7 @@ private
|
|
137
136
|
label_center = group_left_y + (bars_width / 2.0)
|
138
137
|
draw_label(label_center, group_index)
|
139
138
|
|
140
|
-
group_left_y = right_y + padding + group_spacing
|
139
|
+
group_left_y = right_y + padding + @group_spacing
|
141
140
|
end
|
142
141
|
end
|
143
142
|
|
@@ -176,7 +175,7 @@ private
|
|
176
175
|
end
|
177
176
|
|
178
177
|
def calculate_spacing
|
179
|
-
@
|
178
|
+
@group_spacing * (column_count - 1)
|
180
179
|
end
|
181
180
|
|
182
181
|
def proc_text_metrics
|
data/lib/gruff/stacked_area.rb
CHANGED
data/lib/gruff/store/xy_data.rb
CHANGED
@@ -3,18 +3,17 @@
|
|
3
3
|
module Gruff
|
4
4
|
class Store
|
5
5
|
# @private
|
6
|
-
class XYData < Struct.new(:label, :
|
7
|
-
def initialize(label, y_points, color
|
8
|
-
|
9
|
-
|
6
|
+
class XYData < Struct.new(:label, :x_points, :y_points, :color)
|
7
|
+
def initialize(label, x_points, y_points, color)
|
8
|
+
y_points = Array(y_points)
|
9
|
+
x_points = x_points ? Array(x_points) : Array.new(y_points.length)
|
10
|
+
raise ArgumentError, 'x_points.length != y_points.length!' if x_points.length != y_points.length
|
11
|
+
|
12
|
+
super(label.to_s, x_points, y_points, color)
|
10
13
|
end
|
11
14
|
|
12
15
|
alias points y_points
|
13
16
|
|
14
|
-
def x_points
|
15
|
-
self[:x_points] || Array.new(y_points.length)
|
16
|
-
end
|
17
|
-
|
18
17
|
def coordinates
|
19
18
|
x_points.zip(y_points)
|
20
19
|
end
|
@@ -53,7 +52,7 @@ module Gruff
|
|
53
52
|
y.nil? ? nil : (y.to_f - minimum_y.to_f) / spread_y
|
54
53
|
end
|
55
54
|
|
56
|
-
self.class.new(label, norm_y_points, color
|
55
|
+
self.class.new(label, norm_x_points, norm_y_points, color)
|
57
56
|
end
|
58
57
|
end
|
59
58
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gruff
|
4
|
+
class Store
|
5
|
+
# @private
|
6
|
+
class XYPointsizeData < Struct.new(:label, :x_points, :y_points, :point_sizes, :color)
|
7
|
+
def initialize(label, x_points, y_points, point_sizes, color)
|
8
|
+
y_points = Array(y_points)
|
9
|
+
x_points = x_points ? Array(x_points) : Array.new(y_points.length)
|
10
|
+
raise ArgumentError, 'x_points.length != y_points.length!' if x_points.length != y_points.length
|
11
|
+
raise ArgumentError, 'x_points.length != point_sizes.length!' if x_points.length != point_sizes.length
|
12
|
+
|
13
|
+
super(label.to_s, x_points, y_points, point_sizes, color)
|
14
|
+
end
|
15
|
+
|
16
|
+
alias points y_points
|
17
|
+
|
18
|
+
def coordinate_and_pointsizes
|
19
|
+
x_points.zip(y_points, point_sizes)
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty?
|
23
|
+
y_points.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def columns
|
27
|
+
y_points.length
|
28
|
+
end
|
29
|
+
|
30
|
+
def min
|
31
|
+
y_points.compact.min
|
32
|
+
end
|
33
|
+
alias min_y min
|
34
|
+
|
35
|
+
def max
|
36
|
+
y_points.compact.max
|
37
|
+
end
|
38
|
+
alias max_y max
|
39
|
+
|
40
|
+
def min_x
|
41
|
+
x_points.compact.min
|
42
|
+
end
|
43
|
+
|
44
|
+
def max_x
|
45
|
+
x_points.compact.max
|
46
|
+
end
|
47
|
+
|
48
|
+
def normalize(minimum_x:, minimum_y:, spread_x:, spread_y:)
|
49
|
+
norm_x_points = x_points.map do |x|
|
50
|
+
x.nil? ? nil : (x.to_f - minimum_x.to_f) / spread_x
|
51
|
+
end
|
52
|
+
norm_y_points = y_points.map do |y|
|
53
|
+
y.nil? ? nil : (y.to_f - minimum_y.to_f) / spread_y
|
54
|
+
end
|
55
|
+
|
56
|
+
self.class.new(label, norm_x_points, norm_y_points, point_sizes, color)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/gruff/version.rb
CHANGED
data/lib/gruff.rb
CHANGED
@@ -26,7 +26,8 @@ module Gruff
|
|
26
26
|
autoload :Area, Gruff.libpath('area')
|
27
27
|
autoload :Bar, Gruff.libpath('bar')
|
28
28
|
autoload :Bezier, Gruff.libpath('bezier')
|
29
|
-
autoload :
|
29
|
+
autoload :Box, Gruff.libpath('box')
|
30
|
+
autoload :Bubble, Gruff.libpath('bubble')
|
30
31
|
autoload :Bullet, Gruff.libpath('bullet')
|
31
32
|
autoload :Candlestick, Gruff.libpath('candlestick')
|
32
33
|
autoload :Dot, Gruff.libpath('dot')
|
@@ -60,6 +61,7 @@ module Gruff
|
|
60
61
|
class Store
|
61
62
|
autoload :BasicData, Gruff.libpath('store/basic_data')
|
62
63
|
autoload :XYData, Gruff.libpath('store/xy_data')
|
64
|
+
autoload :XYPointsizeData, Gruff.libpath('store/xy_pointsizes_data')
|
63
65
|
end
|
64
66
|
|
65
67
|
module Mini
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gruff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geoffrey Grosenbach
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-06-
|
12
|
+
date: 2022-06-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rmagick
|
@@ -165,7 +165,8 @@ files:
|
|
165
165
|
- lib/gruff/bar.rb
|
166
166
|
- lib/gruff/base.rb
|
167
167
|
- lib/gruff/bezier.rb
|
168
|
-
- lib/gruff/
|
168
|
+
- lib/gruff/box.rb
|
169
|
+
- lib/gruff/bubble.rb
|
169
170
|
- lib/gruff/bullet.rb
|
170
171
|
- lib/gruff/candlestick.rb
|
171
172
|
- lib/gruff/dot.rb
|
@@ -204,6 +205,7 @@ files:
|
|
204
205
|
- lib/gruff/store/basic_data.rb
|
205
206
|
- lib/gruff/store/store.rb
|
206
207
|
- lib/gruff/store/xy_data.rb
|
208
|
+
- lib/gruff/store/xy_pointsizes_data.rb
|
207
209
|
- lib/gruff/themes.rb
|
208
210
|
- lib/gruff/version.rb
|
209
211
|
- rails_generators/gruff/gruff_generator.rb
|