minichart 0.1.0 → 0.3.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/README.md +241 -11
- data/lib/minichart.rb +11 -3
- data/lib/minichart/base.rb +63 -0
- data/lib/minichart/charts/area_chart.rb +27 -0
- data/lib/minichart/charts/bar_chart.rb +33 -0
- data/lib/minichart/charts/chart.rb +28 -0
- data/lib/minichart/charts/line_chart.rb +23 -0
- data/lib/minichart/id_generator.rb +14 -0
- data/lib/minichart/meters/horizontal_bar_meter.rb +80 -0
- data/lib/minichart/meters/meter.rb +65 -0
- data/lib/minichart/meters/vertical_bar_meter.rb +80 -0
- data/lib/minichart/version.rb +1 -1
- metadata +15 -9
- data/lib/minichart/bar_chart.rb +0 -29
- data/lib/minichart/chart.rb +0 -67
- data/lib/minichart/line_chart.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6109cfc57cb909c2ba0a9df4e184bc4319220399b774ff2098b728bf4a41f57e
|
4
|
+
data.tar.gz: 32cd8ff3c3260d08f81106c3ac74037e36990d4601eb8ff7e40c8840d44b3ca3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63e01d19bd4539ccf1283a98bee8f941a7e270ee0f1a2956aed20ecbab21661e7923420588c28f0aa90027cfc6fadc88f5f90b5776ff8dfa3523c9faf85c03ba
|
7
|
+
data.tar.gz: 1861ffff3d852e85fbf9b8c334d091f6dd40ce823086ae4215edb95c96f20a5f9e79e4da5caaf51b35956a8da92ca9de054ee35aae67c84884c02ad5f59af628
|
data/README.md
CHANGED
@@ -1,16 +1,36 @@
|
|
1
|
-
Minichart - SVG Chart Generator
|
2
|
-
==================================================
|
1
|
+
# Minichart - SVG Chart Generator
|
3
2
|
|
4
3
|
---
|
5
4
|
|
6
5
|
Create SVG mini charts with Ruby
|
7
6
|
|
7
|
+

|
8
|
+
|
8
9
|
---
|
9
10
|
|
10
|
-
Install
|
11
|
-
|
11
|
+
* [Install](#install)
|
12
|
+
* [Usage](#usage)
|
13
|
+
* [Chart Types](#chart-types)
|
14
|
+
* [Line Chart](#line-chart)
|
15
|
+
* [Bar Chart](#bar-chart)
|
16
|
+
* [Area Chart](#area-chart)
|
17
|
+
* [Horizontal Bar Meter](#horizontal-bar-meter)
|
18
|
+
* [Vertical Bar Meter](#vertical-bar-meter)
|
19
|
+
* [Configuration](#configuration)
|
20
|
+
* [Class-level default options](#class-level-default-options)
|
21
|
+
* [Instance initialization options](#instance-initialization-options)
|
22
|
+
* [Instance-level options](#instance-level-options)
|
23
|
+
* [Options Reference](#options-reference)
|
24
|
+
* [Basic Options](#basic-options)
|
25
|
+
* [Meter Options](#meter-options)
|
26
|
+
* [Examples](#examples)
|
12
27
|
|
13
|
-
|
28
|
+
|
29
|
+
---
|
30
|
+
|
31
|
+
## Install
|
32
|
+
|
33
|
+
```shell
|
14
34
|
$ gem install minichart
|
15
35
|
```
|
16
36
|
|
@@ -20,20 +40,230 @@ Or with bundler:
|
|
20
40
|
gem 'minichart'
|
21
41
|
```
|
22
42
|
|
23
|
-
|
24
|
-
|
43
|
+
## Usage
|
44
|
+
|
45
|
+
Require and optionally include the library:
|
25
46
|
|
26
47
|
```ruby
|
27
48
|
require 'minichart'
|
28
49
|
include Minichart
|
50
|
+
```
|
51
|
+
|
52
|
+
Initialize a chart with data, and optional options:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9]
|
56
|
+
chart = AreaChart.new data, color: 'blue'
|
57
|
+
```
|
58
|
+
|
59
|
+
Get the full SVG output by calling `#render`:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
puts chart.render
|
63
|
+
#=> <?xml version="1.0" standalone="no"?>
|
64
|
+
# <svg> ... </svg>
|
65
|
+
```
|
66
|
+
|
67
|
+
Save it to file, by calling `#save`:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
chart.save "my-chart.svg"
|
71
|
+
```
|
72
|
+
|
73
|
+
Get its inner SVG string by calling `#to_s`:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
puts chart.to_s
|
77
|
+
#=> <polyline fill="blue" stroke="blue" stroke-width="2" points="..."/>
|
78
|
+
```
|
79
|
+
|
80
|
+
|
81
|
+
The objects returned from all the mini chart classes are [Victor::SVG][2] objects, so they support all methods supported by it as well.
|
82
|
+
|
83
|
+
## Chart Types
|
84
|
+
|
85
|
+
### Line Chart
|
86
|
+
|
87
|
+
<img src='examples/line_chart.svg' align='right' width=300>
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
LineChart.new data, height: 50, background: '#eee',
|
91
|
+
aspect_ratio: 5, color: 'green'
|
92
|
+
```
|
93
|
+
|
94
|
+
### Bar Chart
|
95
|
+
|
96
|
+
<img src='examples/bar_chart.svg' align='right' width=300>
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
BarChart.new data, height: 50, background: '#eee',
|
100
|
+
aspect_ratio: 5, color: 'green'
|
101
|
+
```
|
102
|
+
|
103
|
+
### Area Chart
|
104
|
+
|
105
|
+
<img src='examples/area_chart.svg' align='right' width=300>
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
AreaChart.new data, height: 50, background: '#eee',
|
109
|
+
aspect_ratio: 5, color: 'green'
|
110
|
+
```
|
111
|
+
|
112
|
+
### Horizontal Bar Meter
|
113
|
+
|
114
|
+
<img src='examples/multiple_horizontal_bars.svg' align='right'>
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
positive = HorizontalBarMeter.new 70,
|
118
|
+
height: 20, width: 250, background: '#9f9', color: 'green'
|
119
|
+
|
120
|
+
negative = HorizontalBarMeter.new -80,
|
121
|
+
height: 20, width: 250, background: '#f99', color: 'red'
|
122
|
+
|
123
|
+
dual = HorizontalBarMeter.new 80,
|
124
|
+
height: 20, width: 250, background: '#99f', color: 'blue',
|
125
|
+
mode: :dual, zero_line: true
|
126
|
+
```
|
127
|
+
|
128
|
+
Meter charts support [additional options](#meter-options).
|
129
|
+
|
130
|
+
### Vertical Bar Meter
|
131
|
+
|
132
|
+
<img src='examples/multiple_vertical_bars.svg' align='right'>
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
positive = VerticalBarMeter.new 70,
|
136
|
+
width: 20, height: 250, background: '#9f9', color: 'green'
|
137
|
+
|
138
|
+
negative = VerticalBarMeter.new -80,
|
139
|
+
width: 20, height: 250, background: '#f99', color: 'red'
|
140
|
+
|
141
|
+
dual = VerticalBarMeter.new 80,
|
142
|
+
width: 20, height: 250, background: '#99f', color: 'blue',
|
143
|
+
mode: :dual, zero_line: true
|
144
|
+
```
|
29
145
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
146
|
+
Meter charts support [additional options](#meter-options).
|
147
|
+
|
148
|
+
|
149
|
+
## Configuration
|
150
|
+
|
151
|
+
Chart options can be set in one of three ways.
|
152
|
+
|
153
|
+
### Class-level default options
|
154
|
+
|
155
|
+
See or set default options for any chart class by calling its `::options` method:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
# See all options
|
159
|
+
p AreaChart.options
|
160
|
+
#=> {:background=>"white", :height=>100, :width=>300, :stroke=>2, :style=>{}, :color=>"#66f"}
|
161
|
+
|
162
|
+
# Set a single default option
|
163
|
+
AreaChart.options[:color] = '#333'
|
164
|
+
|
165
|
+
# Set multiple options at once
|
166
|
+
AreaChart.options background: 'black', color: 'green'
|
34
167
|
```
|
35
168
|
|
169
|
+
### Instance initialization options
|
170
|
+
|
171
|
+
Set options by providing a hash as the second argument on initialization:
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
chart = AreaChart.new data, height: 120, width: 500
|
175
|
+
```
|
176
|
+
|
177
|
+
### Instance-level options
|
178
|
+
|
179
|
+
After initialization, you can still update individual options:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
chart = AreaChart.new data
|
183
|
+
chart.options[:background] = 'yellow'
|
184
|
+
```
|
185
|
+
|
186
|
+
## Options Reference
|
187
|
+
|
188
|
+
### Basic Options
|
189
|
+
|
190
|
+
#### background
|
191
|
+
|
192
|
+
Chart background color.
|
193
|
+
|
194
|
+
#### color
|
195
|
+
|
196
|
+
Chart color.
|
197
|
+
|
198
|
+
#### height
|
199
|
+
|
200
|
+
Chart height in pixels.
|
201
|
+
|
202
|
+
#### width
|
203
|
+
|
204
|
+
Chart width in pixels.
|
205
|
+
|
206
|
+
#### stroke
|
207
|
+
|
208
|
+
Line stroke width. This has a different effect in different chart types.
|
209
|
+
|
210
|
+
#### style
|
211
|
+
|
212
|
+
CSS Style hash to apply to the entire SVG.
|
213
|
+
|
214
|
+
|
215
|
+
### Meter Options
|
216
|
+
|
217
|
+
Meter charts support these options in additon to the basic options:
|
218
|
+
|
219
|
+
#### mode
|
220
|
+
|
221
|
+
Display mode. Can be `:positive`, `:negative`, `:dual` or `:auto` (default).
|
222
|
+
|
223
|
+
The `:auto` mode will switch between `:positive` and `:negative` based on the
|
224
|
+
value.
|
225
|
+
|
226
|
+
#### max
|
227
|
+
|
228
|
+
The absolute maximum value. This number should be positive even for negative
|
229
|
+
charts.
|
230
|
+
|
231
|
+
#### notches
|
232
|
+
|
233
|
+
An array of one or more levels to place a notch marker. Use positive values
|
234
|
+
only.
|
235
|
+
|
236
|
+
#### notch_thickness
|
237
|
+
|
238
|
+
Thickness of the notch markers.
|
239
|
+
|
240
|
+
#### notch_color
|
241
|
+
|
242
|
+
Color of the notch markers.
|
243
|
+
|
244
|
+
#### clipping_indicator
|
245
|
+
|
246
|
+
If true, show a marker when the value exceeds the range.
|
247
|
+
|
248
|
+
#### clipping_indicator_thickness
|
249
|
+
|
250
|
+
Thickness of the clipping indicator.
|
251
|
+
|
252
|
+
#### clipping_indicator_color
|
253
|
+
|
254
|
+
Color of the clipping indicator.
|
255
|
+
|
256
|
+
## Examples
|
257
|
+
|
36
258
|
See more examples (code and SVG output) in the [examples folder][1].
|
37
259
|
|
260
|
+
## Contributing / Support
|
261
|
+
|
262
|
+
If you experience any issue, have a question or a suggestion, or if you wish
|
263
|
+
to contribute, feel free to [open an issue][issues].
|
264
|
+
|
265
|
+
---
|
38
266
|
|
39
267
|
[1]: https://github.com/DannyBen/minichart/tree/master/examples#examples
|
268
|
+
[2]: https://github.com/DannyBen/victor
|
269
|
+
[issues]: https://github.com/DannyBen/minichart/issues
|
data/lib/minichart.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
require 'victor'
|
2
|
-
require 'minichart/
|
3
|
-
require 'minichart/
|
4
|
-
require 'minichart/
|
2
|
+
require 'minichart/id_generator'
|
3
|
+
require 'minichart/base'
|
4
|
+
require 'minichart/charts/chart'
|
5
|
+
require 'minichart/charts/line_chart'
|
6
|
+
require 'minichart/charts/bar_chart'
|
7
|
+
require 'minichart/charts/area_chart'
|
8
|
+
require 'minichart/meters/meter'
|
9
|
+
require 'minichart/meters/vertical_bar_meter'
|
10
|
+
require 'minichart/meters/horizontal_bar_meter'
|
11
|
+
|
12
|
+
require 'byebug' if ENV['BYEBUG']
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Minichart
|
2
|
+
# Base class for all Minichart classes
|
3
|
+
class Base < Victor::SVGBase
|
4
|
+
attr_reader :data, :options
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def master_defaults
|
8
|
+
{
|
9
|
+
background: 'white',
|
10
|
+
height: 100,
|
11
|
+
width: 300,
|
12
|
+
stroke: 2,
|
13
|
+
style: {},
|
14
|
+
color: '#66f'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
# For subclasses to define
|
19
|
+
def defaults
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
|
23
|
+
def options(update_hash = nil)
|
24
|
+
@options ||= master_defaults.merge defaults
|
25
|
+
@options.merge! update_hash if update_hash
|
26
|
+
@options
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(data, user_options = {})
|
31
|
+
@data = data
|
32
|
+
@options = self.class.options.merge user_options
|
33
|
+
|
34
|
+
super viewBox: viewbox, style: options[:style]
|
35
|
+
element :rect, x: 0, y: 0,
|
36
|
+
width: options[:width], height: options[:height],
|
37
|
+
fill: options[:background], stroke_width: 0
|
38
|
+
|
39
|
+
clip_path_id = IDGenerator.next
|
40
|
+
setup_clip_path clip_path_id
|
41
|
+
|
42
|
+
element :g, clip_path: "url(##{clip_path_id})" do
|
43
|
+
build
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_clip_path(id)
|
48
|
+
element :defs do
|
49
|
+
element :clipPath, id: id do
|
50
|
+
element :rect, width: options[:width], height: options[:height]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def viewbox
|
56
|
+
"0 0 #{options[:width]} #{options[:height]}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def build
|
60
|
+
raise NotImplementedError, "#build is not implemented"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Minichart
|
2
|
+
class AreaChart < Chart
|
3
|
+
def build
|
4
|
+
element :polyline, fill: options[:color],
|
5
|
+
stroke: options[:color],
|
6
|
+
stroke_width: options[:stroke],
|
7
|
+
stroke_linejoin: :round,
|
8
|
+
points: points
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def points
|
14
|
+
result = ["0,#{options[:height]}"]
|
15
|
+
|
16
|
+
inverted_points.each do |point|
|
17
|
+
x = options[:width] *point[0]
|
18
|
+
y = options[:height] * point[1]
|
19
|
+
result << "#{x},#{y}"
|
20
|
+
end
|
21
|
+
|
22
|
+
result << "#{options[:width]},#{options[:height]}"
|
23
|
+
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Minichart
|
2
|
+
class BarChart < Chart
|
3
|
+
def build
|
4
|
+
opts = { x_point_count: data.size }
|
5
|
+
|
6
|
+
inverted_points(opts).each do |x, y|
|
7
|
+
element :rect, bar_options(x, y)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def bar_width
|
14
|
+
@bar_width ||= options[:width] / data.size
|
15
|
+
end
|
16
|
+
|
17
|
+
def bar_options(x, y)
|
18
|
+
y = y * options[:height]
|
19
|
+
bar_height = options[:height] - y
|
20
|
+
{
|
21
|
+
x: x * options[:width],
|
22
|
+
y: y,
|
23
|
+
width: bar_width,
|
24
|
+
height: bar_height,
|
25
|
+
style: {
|
26
|
+
fill: options[:color],
|
27
|
+
stroke_width: options[:stroke],
|
28
|
+
stroke: options[:background]
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Minichart
|
2
|
+
# Base class for charts with data series
|
3
|
+
class Chart < Base
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def inverted_points(opts={})
|
8
|
+
normalized_points(opts).map { |point| [point[0], 1-point[1]] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def normalized_points(opts={})
|
12
|
+
x_point_count = opts[:x_point_count] || data.count-1
|
13
|
+
|
14
|
+
range = (data.max - data.min).to_f
|
15
|
+
x_width = 1 / (x_point_count).to_f
|
16
|
+
result = []
|
17
|
+
|
18
|
+
data.each_with_index do |y, index|
|
19
|
+
x = index*x_width
|
20
|
+
y = (y - data.min) / range
|
21
|
+
result << [x,y]
|
22
|
+
end
|
23
|
+
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Minichart
|
2
|
+
class LineChart < Chart
|
3
|
+
def build
|
4
|
+
element :polyline, fill: :none,
|
5
|
+
stroke: options[:color],
|
6
|
+
stroke_width: options[:stroke],
|
7
|
+
stroke_linejoin: :round,
|
8
|
+
points: points
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def points
|
14
|
+
result = []
|
15
|
+
inverted_points.each do |point|
|
16
|
+
x = options[:width] * point[0]
|
17
|
+
y = options[:height] * point[1]
|
18
|
+
result << "#{x},#{y}"
|
19
|
+
end
|
20
|
+
result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Minichart
|
2
|
+
class HorizontalBarMeter < Meter
|
3
|
+
class << self
|
4
|
+
def defaults
|
5
|
+
meter_defaults.merge width: 300, height: 50
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
draw_bar
|
11
|
+
draw_notches if options[:notches]
|
12
|
+
draw_clipping_indicator if options[:clipping_indicator] and clipping?
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def draw_bar
|
18
|
+
x1 = x_for 0
|
19
|
+
x2 = x_for clamped_value
|
20
|
+
x = [x1, x2].min
|
21
|
+
|
22
|
+
element :rect, x: x, y: 0, height: options[:height],
|
23
|
+
width: bar_width, style: style
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw_notches
|
27
|
+
options[:notches].each do |notch|
|
28
|
+
draw_notch notch
|
29
|
+
draw_notch -notch if mode == :dual and notch != 0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def draw_notch(notch)
|
34
|
+
draw_vertical_line notch, stroke: options[:notch_thickness],
|
35
|
+
color: options[:notch_color]
|
36
|
+
end
|
37
|
+
|
38
|
+
def draw_clipping_indicator
|
39
|
+
draw_vertical_line clamped_value,
|
40
|
+
stroke: options[:clipping_indicator_thickness],
|
41
|
+
color: options[:clipping_indicator_color]
|
42
|
+
end
|
43
|
+
|
44
|
+
def draw_vertical_line(target_value, color:, stroke:)
|
45
|
+
x = x_for target_value
|
46
|
+
|
47
|
+
element :line, x1: x, x2: x, y1: 0, y2: options[:height],
|
48
|
+
stroke: color, stroke_width: stroke
|
49
|
+
end
|
50
|
+
|
51
|
+
def width_factor
|
52
|
+
options[:width] / options[:max].to_f
|
53
|
+
end
|
54
|
+
|
55
|
+
def half_width
|
56
|
+
options[:width] * 0.5
|
57
|
+
end
|
58
|
+
|
59
|
+
def bar_width
|
60
|
+
if mode == :dual
|
61
|
+
clamped_value.abs * width_factor * 0.5
|
62
|
+
else
|
63
|
+
clamped_value.abs * width_factor
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def x_for(target_value)
|
68
|
+
result = target_value.abs / options[:max].to_f * options[:width]
|
69
|
+
|
70
|
+
case mode
|
71
|
+
when :positive
|
72
|
+
result
|
73
|
+
when :negative
|
74
|
+
options[:width] - result
|
75
|
+
when :dual
|
76
|
+
target_value / options[:max].to_f * half_width + half_width
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Minichart
|
2
|
+
# Base class for charts with a single value
|
3
|
+
class Meter < Base
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def meter_defaults
|
7
|
+
@meter_defaults ||= {
|
8
|
+
height: 50,
|
9
|
+
max: 100,
|
10
|
+
notches: [],
|
11
|
+
notch_thickness: 10,
|
12
|
+
notch_color: 'black',
|
13
|
+
clipping_indicator: false,
|
14
|
+
clipping_indicator_thickness: 20,
|
15
|
+
clipping_indicator_color: 'yellow',
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def value
|
24
|
+
data
|
25
|
+
end
|
26
|
+
|
27
|
+
def clipping?
|
28
|
+
value > options[:max] || value < -options[:max]
|
29
|
+
end
|
30
|
+
|
31
|
+
def mode
|
32
|
+
@mode ||= mode!
|
33
|
+
end
|
34
|
+
|
35
|
+
def mode!
|
36
|
+
options[:mode] ||= :auto
|
37
|
+
|
38
|
+
if options[:mode] == :auto
|
39
|
+
value >= 0 ? :positive : :negative
|
40
|
+
else
|
41
|
+
options[:mode].to_sym
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def clamped_value
|
46
|
+
case mode
|
47
|
+
when :positive
|
48
|
+
value.clamp 0, options[:max]
|
49
|
+
when :negative
|
50
|
+
value.clamp -options[:max], 0
|
51
|
+
when :dual
|
52
|
+
value.clamp -options[:max], options[:max]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def style
|
57
|
+
{
|
58
|
+
fill: options[:color],
|
59
|
+
stroke_width: options[:stroke],
|
60
|
+
stroke: options[:background]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Minichart
|
2
|
+
class VerticalBarMeter < Meter
|
3
|
+
class << self
|
4
|
+
def defaults
|
5
|
+
meter_defaults.merge width: 50, height: 300
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
draw_bar
|
11
|
+
draw_notches if options[:notches]
|
12
|
+
draw_clipping_indicator if options[:clipping_indicator] and clipping?
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def draw_bar
|
18
|
+
y1 = y_for 0
|
19
|
+
y2 = y_for clamped_value
|
20
|
+
y = [y1, y2].min
|
21
|
+
|
22
|
+
element :rect, x: 0, y: y, height: bar_height,
|
23
|
+
width: options[:width], style: style
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw_notches
|
27
|
+
options[:notches].each do |notch|
|
28
|
+
draw_notch notch
|
29
|
+
draw_notch -notch if mode == :dual and notch != 0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def draw_notch(notch)
|
34
|
+
draw_horizontal_line notch, stroke: options[:notch_thickness],
|
35
|
+
color: options[:notch_color]
|
36
|
+
end
|
37
|
+
|
38
|
+
def draw_clipping_indicator
|
39
|
+
draw_horizontal_line clamped_value,
|
40
|
+
stroke: options[:clipping_indicator_thickness],
|
41
|
+
color: options[:clipping_indicator_color]
|
42
|
+
end
|
43
|
+
|
44
|
+
def draw_horizontal_line(target_value, color:, stroke:)
|
45
|
+
y = y_for target_value
|
46
|
+
|
47
|
+
element :line, x1: 0, x2: options[:width], y1: y, y2: y,
|
48
|
+
stroke: color, stroke_width: stroke
|
49
|
+
end
|
50
|
+
|
51
|
+
def height_factor
|
52
|
+
options[:height] / options[:max].to_f
|
53
|
+
end
|
54
|
+
|
55
|
+
def half_height
|
56
|
+
options[:height] * 0.5
|
57
|
+
end
|
58
|
+
|
59
|
+
def bar_height
|
60
|
+
if mode == :dual
|
61
|
+
clamped_value.abs * height_factor * 0.5
|
62
|
+
else
|
63
|
+
clamped_value.abs * height_factor
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def y_for(target_value)
|
68
|
+
result = target_value.abs / options[:max].to_f * options[:height]
|
69
|
+
|
70
|
+
case mode
|
71
|
+
when :positive
|
72
|
+
options[:height] - result
|
73
|
+
when :negative
|
74
|
+
result
|
75
|
+
when :dual
|
76
|
+
options[:height] - (target_value / options[:max].to_f * half_height + half_height)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/minichart/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minichart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danny Ben Shitrit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: victor
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.3'
|
27
27
|
description: Generate mini charts with SVG
|
28
28
|
email: db@dannyben.com
|
29
29
|
executables: []
|
@@ -32,9 +32,15 @@ extra_rdoc_files: []
|
|
32
32
|
files:
|
33
33
|
- README.md
|
34
34
|
- lib/minichart.rb
|
35
|
-
- lib/minichart/
|
36
|
-
- lib/minichart/
|
37
|
-
- lib/minichart/
|
35
|
+
- lib/minichart/base.rb
|
36
|
+
- lib/minichart/charts/area_chart.rb
|
37
|
+
- lib/minichart/charts/bar_chart.rb
|
38
|
+
- lib/minichart/charts/chart.rb
|
39
|
+
- lib/minichart/charts/line_chart.rb
|
40
|
+
- lib/minichart/id_generator.rb
|
41
|
+
- lib/minichart/meters/horizontal_bar_meter.rb
|
42
|
+
- lib/minichart/meters/meter.rb
|
43
|
+
- lib/minichart/meters/vertical_bar_meter.rb
|
38
44
|
- lib/minichart/version.rb
|
39
45
|
homepage: https://github.com/DannyBen/minichart
|
40
46
|
licenses:
|
@@ -48,14 +54,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
54
|
requirements:
|
49
55
|
- - ">="
|
50
56
|
- !ruby/object:Gem::Version
|
51
|
-
version: 2.
|
57
|
+
version: 2.5.0
|
52
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
59
|
requirements:
|
54
60
|
- - ">="
|
55
61
|
- !ruby/object:Gem::Version
|
56
62
|
version: '0'
|
57
63
|
requirements: []
|
58
|
-
rubygems_version: 3.
|
64
|
+
rubygems_version: 3.1.2
|
59
65
|
signing_key:
|
60
66
|
specification_version: 4
|
61
67
|
summary: SVG Mini Charts
|
data/lib/minichart/bar_chart.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
module Minichart
|
2
|
-
class BarChart < Chart
|
3
|
-
def build
|
4
|
-
opts = { x_point_count: data.size }
|
5
|
-
|
6
|
-
inverted_points(opts).each do |x, y|
|
7
|
-
svg.rect bar_options x, y
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def bar_width
|
14
|
-
@bar_width ||= width / data.size
|
15
|
-
end
|
16
|
-
|
17
|
-
def bar_options(x, y)
|
18
|
-
y = y*height
|
19
|
-
bar_height = height-y
|
20
|
-
{
|
21
|
-
x: x*width,
|
22
|
-
y: y,
|
23
|
-
width: bar_width,
|
24
|
-
height: bar_height,
|
25
|
-
style: { fill: color, stroke_width: stroke, stroke: background }
|
26
|
-
}
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/lib/minichart/chart.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
module Minichart
|
2
|
-
class Chart
|
3
|
-
attr_accessor :data, :aspect_ratio, :opts
|
4
|
-
|
5
|
-
def initialize(opts={})
|
6
|
-
@opts = opts
|
7
|
-
end
|
8
|
-
|
9
|
-
def save(name)
|
10
|
-
svg.rect x: 0, y: 0, width: width, height: height, fill: background
|
11
|
-
build
|
12
|
-
svg.save name
|
13
|
-
end
|
14
|
-
|
15
|
-
def build
|
16
|
-
raise NotImplementedError, "#build is not implemented"
|
17
|
-
end
|
18
|
-
|
19
|
-
def inverted_points(opts={})
|
20
|
-
normalized_points(opts).map { |point| [point[0], 1-point[1]] }
|
21
|
-
end
|
22
|
-
|
23
|
-
def normalized_points(opts={})
|
24
|
-
x_point_count = opts[:x_point_count] || data.count-1
|
25
|
-
|
26
|
-
range = (data.max - data.min).to_f
|
27
|
-
x_width = 1/(x_point_count).to_f
|
28
|
-
result = []
|
29
|
-
|
30
|
-
data.each_with_index do |y, index|
|
31
|
-
x = index*x_width
|
32
|
-
y = (y-data.min)/range
|
33
|
-
result << [x,y]
|
34
|
-
end
|
35
|
-
|
36
|
-
result
|
37
|
-
end
|
38
|
-
|
39
|
-
def width
|
40
|
-
@width ||= (aspect_ratio * height).round
|
41
|
-
end
|
42
|
-
|
43
|
-
def height
|
44
|
-
100
|
45
|
-
end
|
46
|
-
|
47
|
-
def svg
|
48
|
-
@svg ||= Victor::SVG.new viewBox: "0 0 #{width} #{height}", style: style
|
49
|
-
end
|
50
|
-
|
51
|
-
def style
|
52
|
-
@opts[:style] ||= {}
|
53
|
-
end
|
54
|
-
|
55
|
-
def background
|
56
|
-
@opts[:background] ||= '#eee'
|
57
|
-
end
|
58
|
-
|
59
|
-
def color
|
60
|
-
@opts[:color] || '#333'
|
61
|
-
end
|
62
|
-
|
63
|
-
def stroke
|
64
|
-
@opts[:stroke] ||= 2
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
data/lib/minichart/line_chart.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
module Minichart
|
2
|
-
class LineChart < Chart
|
3
|
-
def build
|
4
|
-
svg.polyline fill: :none, stroke: color, stroke_width: stroke, points: points
|
5
|
-
end
|
6
|
-
|
7
|
-
private
|
8
|
-
|
9
|
-
def points
|
10
|
-
result = []
|
11
|
-
inverted_points.each do |point|
|
12
|
-
x = width*point[0]
|
13
|
-
y = height*point[1]
|
14
|
-
result << "#{x},#{y}"
|
15
|
-
end
|
16
|
-
result
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|