gruff 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|

|
128
128
|
|
129
|
-
### Box
|
129
|
+
### Box chart
|
130
130
|
|
131
|
-

|
132
132
|
|
133
133
|
### Candlestick
|
134
134
|
|
135
135
|

|
136
136
|
|
137
|
+
### Bubble chart
|
138
|
+
|
139
|
+

|
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
|