minichart 0.1.1 → 0.3.1

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