gruff 0.14.0-java → 0.17.0-java
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 +28 -12
- data/.gitignore +1 -0
- data/.rubocop.yml +20 -24
- data/CHANGELOG.md +52 -0
- data/README.md +10 -3
- data/gruff.gemspec +9 -10
- data/lib/gruff/accumulator_bar.rb +1 -1
- data/lib/gruff/area.rb +6 -4
- data/lib/gruff/bar.rb +53 -31
- data/lib/gruff/base.rb +292 -184
- data/lib/gruff/bezier.rb +4 -2
- data/lib/gruff/box_plot.rb +180 -0
- data/lib/gruff/bullet.rb +6 -6
- data/lib/gruff/candlestick.rb +120 -0
- data/lib/gruff/dot.rb +11 -12
- data/lib/gruff/font.rb +3 -0
- data/lib/gruff/helper/bar_conversion.rb +6 -10
- data/lib/gruff/helper/bar_mixin.rb +25 -0
- data/lib/gruff/helper/bar_value_label.rb +24 -40
- data/lib/gruff/helper/stacked_mixin.rb +19 -1
- data/lib/gruff/histogram.rb +9 -5
- data/lib/gruff/line.rb +49 -48
- data/lib/gruff/mini/legend.rb +11 -11
- data/lib/gruff/net.rb +23 -18
- data/lib/gruff/patch/rmagick.rb +0 -1
- data/lib/gruff/patch/string.rb +1 -0
- data/lib/gruff/pie.rb +26 -12
- data/lib/gruff/renderer/dash_line.rb +3 -2
- data/lib/gruff/renderer/dot.rb +28 -15
- data/lib/gruff/renderer/line.rb +1 -3
- data/lib/gruff/renderer/rectangle.rb +6 -2
- data/lib/gruff/renderer/renderer.rb +4 -8
- data/lib/gruff/renderer/text.rb +7 -1
- data/lib/gruff/scatter.rb +64 -56
- data/lib/gruff/side_bar.rb +64 -30
- data/lib/gruff/side_stacked_bar.rb +43 -54
- data/lib/gruff/spider.rb +52 -18
- data/lib/gruff/stacked_area.rb +18 -8
- data/lib/gruff/stacked_bar.rb +59 -29
- data/lib/gruff/store/xy_data.rb +2 -0
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +67 -58
- metadata +17 -16
- data/.rubocop_todo.yml +0 -116
- data/lib/gruff/scene.rb +0 -200
- data/lib/gruff/store/custom_data.rb +0 -36
data/lib/gruff/scene.rb
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'observer'
|
4
|
-
|
5
|
-
# A scene is a non-linear graph that assembles layers together to tell a story.
|
6
|
-
# Layers are folders with appropriately named files (see below). You can group
|
7
|
-
# layers and control them together or just set their values individually.
|
8
|
-
#
|
9
|
-
# Examples:
|
10
|
-
#
|
11
|
-
# * A city scene that changes with the time of day and the weather conditions.
|
12
|
-
# * A traffic map that shows red lines on streets that are crowded and green on free-flowing ones.
|
13
|
-
#
|
14
|
-
# g = Gruff::Scene.new("500x100", "path/to/city_scene_directory")
|
15
|
-
#
|
16
|
-
# # Define order of layers, back to front
|
17
|
-
# g.layers = %w(background haze sky clouds)
|
18
|
-
#
|
19
|
-
# # Define groups that will be controlled by the same input
|
20
|
-
# g.weather_group = %w(clouds)
|
21
|
-
# g.time_group = %w(background sky)
|
22
|
-
#
|
23
|
-
# # Set values for the layers or groups
|
24
|
-
# g.weather = "cloudy"
|
25
|
-
# g.time = Time.now
|
26
|
-
# g.haze = true
|
27
|
-
#
|
28
|
-
# # Write the final graph to disk
|
29
|
-
# g.write "hazy_daytime_city_scene.png"
|
30
|
-
#
|
31
|
-
# There are several rules that will magically select a layer when possible.
|
32
|
-
#
|
33
|
-
# * Numbered files will be selected according to the closest value that is less than the input value.
|
34
|
-
# * +'true.png'+ and +'false.png'+ will be used as booleans.
|
35
|
-
# * Other named files will be used if the input matches the filename (without the filetype extension).
|
36
|
-
# * If there is a file named +'default.png'+, it will be used unless other input values are set for the corresponding layer.
|
37
|
-
class Gruff::Scene < Gruff::Base
|
38
|
-
# An array listing the folder names that will be rendered, from back to front.
|
39
|
-
#
|
40
|
-
# @example
|
41
|
-
# g.layers = %w(sky clouds buildings street people)
|
42
|
-
attr_reader :layers
|
43
|
-
|
44
|
-
def initialize(target_width, base_dir)
|
45
|
-
@base_dir = base_dir
|
46
|
-
@groups = {}
|
47
|
-
@layers = []
|
48
|
-
super target_width
|
49
|
-
end
|
50
|
-
|
51
|
-
def draw
|
52
|
-
# Join all the custom paths and filter out the empty ones
|
53
|
-
image_paths = @layers.map(&:path).reject(&:empty?)
|
54
|
-
images = Magick::ImageList.new(*image_paths)
|
55
|
-
renderer.background_image = images.flatten_images
|
56
|
-
end
|
57
|
-
|
58
|
-
def layers=(ordered_list)
|
59
|
-
ordered_list.each do |layer_name|
|
60
|
-
@layers << Gruff::Layer.new(@base_dir, layer_name)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Group layers to input values
|
65
|
-
#
|
66
|
-
# g.weather_group = ["sky", "sea", "clouds"]
|
67
|
-
#
|
68
|
-
# Set input values
|
69
|
-
#
|
70
|
-
# g.weather = "cloudy"
|
71
|
-
#
|
72
|
-
def method_missing(method_name, *args)
|
73
|
-
case method_name.to_s
|
74
|
-
when /^(\w+)_group=$/
|
75
|
-
add_group Regexp.last_match(1), *args
|
76
|
-
return
|
77
|
-
when /^(\w+)=$/
|
78
|
-
set_input Regexp.last_match(1), args.first
|
79
|
-
return
|
80
|
-
end
|
81
|
-
super
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def add_group(input_name, layer_names)
|
87
|
-
@groups[input_name] = Gruff::Group.new(input_name, @layers.select { |layer| layer_names.include?(layer.name) })
|
88
|
-
end
|
89
|
-
|
90
|
-
def set_input(input_name, input_value)
|
91
|
-
if !@groups[input_name].nil?
|
92
|
-
@groups[input_name].send_updates(input_value)
|
93
|
-
elsif chosen_layer = @layers.find { |layer| layer.name == input_name }
|
94
|
-
chosen_layer.update input_value
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# @private
|
100
|
-
class Gruff::Group
|
101
|
-
include Observable
|
102
|
-
attr_reader :name
|
103
|
-
|
104
|
-
def initialize(folder_name, layers)
|
105
|
-
@name = folder_name
|
106
|
-
layers.each do |layer|
|
107
|
-
layer.observe self
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def send_updates(value)
|
112
|
-
changed
|
113
|
-
notify_observers value
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# @private
|
118
|
-
class Gruff::Layer
|
119
|
-
attr_reader :name
|
120
|
-
|
121
|
-
def initialize(base_dir, folder_name)
|
122
|
-
@base_dir = base_dir.to_s
|
123
|
-
@name = folder_name.to_s
|
124
|
-
@filenames = Dir.open(File.join(base_dir, folder_name)).entries.select { |file| file =~ /^[^.]+\.png$/ }.sort
|
125
|
-
@selected_filename = select_default
|
126
|
-
end
|
127
|
-
|
128
|
-
# Register this layer so it receives updates from the group
|
129
|
-
def observe(obj)
|
130
|
-
obj.add_observer self
|
131
|
-
end
|
132
|
-
|
133
|
-
# Choose the appropriate filename for this layer, based on the input
|
134
|
-
def update(value)
|
135
|
-
@selected_filename = begin
|
136
|
-
case value.to_s
|
137
|
-
when /^(true|false)$/
|
138
|
-
select_boolean value
|
139
|
-
when /^(\w|\s)+$/
|
140
|
-
select_string value
|
141
|
-
when /^-?(\d+\.)?\d+$/
|
142
|
-
select_numeric value
|
143
|
-
when /(\d\d):(\d\d):\d\d/
|
144
|
-
select_time "#{Regexp.last_match(1)}#{Regexp.last_match(2)}"
|
145
|
-
else
|
146
|
-
select_default
|
147
|
-
end
|
148
|
-
end
|
149
|
-
# Finally, try to use 'default' if we're still blank
|
150
|
-
@selected_filename ||= select_default
|
151
|
-
end
|
152
|
-
|
153
|
-
# Returns the full path to the selected image, or a blank string
|
154
|
-
def path
|
155
|
-
unless @selected_filename.nil? || @selected_filename.empty?
|
156
|
-
return File.join(@base_dir, @name, @selected_filename)
|
157
|
-
end
|
158
|
-
|
159
|
-
''
|
160
|
-
end
|
161
|
-
|
162
|
-
private
|
163
|
-
|
164
|
-
# Match "true.png" or "false.png"
|
165
|
-
def select_boolean(value)
|
166
|
-
file_exists_or_blank value.to_s
|
167
|
-
end
|
168
|
-
|
169
|
-
# Match -5 to _5.png
|
170
|
-
def select_numeric(value)
|
171
|
-
file_exists_or_blank value.to_s.gsub('-', '_')
|
172
|
-
end
|
173
|
-
|
174
|
-
def select_time(value)
|
175
|
-
times = @filenames.map { |filename| filename.gsub('.png', '') }
|
176
|
-
times.each_with_index do |time, index|
|
177
|
-
if (time > value) && (index > 0)
|
178
|
-
return "#{times[index - 1]}.png"
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
"#{times.last}.png"
|
183
|
-
end
|
184
|
-
|
185
|
-
# Match "partly cloudy" to "partly_cloudy.png"
|
186
|
-
def select_string(value)
|
187
|
-
file_exists_or_blank value.to_s.gsub(' ', '_')
|
188
|
-
end
|
189
|
-
|
190
|
-
def select_default
|
191
|
-
@filenames.include?('default.png') ? 'default.png' : ''
|
192
|
-
end
|
193
|
-
|
194
|
-
# Returns the string "#{filename}.png", if it exists.
|
195
|
-
#
|
196
|
-
# Failing that, it returns default.png, or '' if that doesn't exist.
|
197
|
-
def file_exists_or_blank(filename)
|
198
|
-
@filenames.include?("#{filename}.png") ? "#{filename}.png" : select_default
|
199
|
-
end
|
200
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gruff
|
4
|
-
class Store
|
5
|
-
# @private
|
6
|
-
class CustomData < Struct.new(:label, :points, :color, :custom)
|
7
|
-
def initialize(label, points, color, custom = nil)
|
8
|
-
super(label.to_s, Array(points), color, custom)
|
9
|
-
end
|
10
|
-
|
11
|
-
def empty?
|
12
|
-
points.empty?
|
13
|
-
end
|
14
|
-
|
15
|
-
def columns
|
16
|
-
points.length
|
17
|
-
end
|
18
|
-
|
19
|
-
def min
|
20
|
-
points.compact.min
|
21
|
-
end
|
22
|
-
|
23
|
-
def max
|
24
|
-
points.compact.max
|
25
|
-
end
|
26
|
-
|
27
|
-
def normalize(minimum:, spread:)
|
28
|
-
norm_points = points.map do |point|
|
29
|
-
point.nil? ? nil : (point.to_f - minimum.to_f) / spread
|
30
|
-
end
|
31
|
-
|
32
|
-
self.class.new(label, norm_points, color, custom)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|