minichart 0.1.1 → 0.3.1

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