minichart 0.2.0 → 0.3.2

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: 767c1bdbf55e5aedb689c5286535fb84d3c48cf2c1c054b01da97237dee3437a
4
- data.tar.gz: ea0b462ccfd166eea1df2e2a4ad08f32060c713a06f1aea36170079bc90a3671
3
+ metadata.gz: 3c2af3f30a91b6f557ec75c02d6250153919290dd962c318bd58008600068b22
4
+ data.tar.gz: b00bd9ad950b213a7430178d55673aed0b9f4ee80b3b82f20a456d16f2d91b5d
5
5
  SHA512:
6
- metadata.gz: 57524a378c6ce4df0ad08d4edc134630d3ae6ebbb35e68638f11926ab61f4c3b3f6e8e0861d49213de54d619d52df2cb74c4813e2349409e9e444b0ddca2cb44
7
- data.tar.gz: 45820a40054e716bfe412d661ba814ea4fb8589b39145fbeff6a079841ea83990d2adbaf6733c328378627133eaaa5891f5e87d268f66abab69a412727cbc29a
6
+ metadata.gz: e183bdd8e7876c9f58503a3edcc80ffaf1fa1d03fde54d400de153e894bb3e51012f30d7b87683c887c2f482cedc331d57ea14fc5ddc4ae4f5c318e2bdc30a22
7
+ data.tar.gz: fbca221fb51fa31ee37b1532c2a4d0d7f28b44d94aeaf3c47b7c8744d68dc0188a44341fc265ba286c84eea479350be3d0be4b45d32a5b2dd5152917677f3ce1
data/README.md CHANGED
@@ -8,6 +8,28 @@ Create SVG mini charts with Ruby
8
8
 
9
9
  ---
10
10
 
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)
30
+
31
+ ---
32
+
11
33
  ## Install
12
34
 
13
35
  ```shell
@@ -33,15 +55,13 @@ Initialize a chart with data, and optional options:
33
55
 
34
56
  ```ruby
35
57
  data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9]
36
- line = LineChart.new data, color: 'blue'
37
- bars = BarChart.new data, color: 'blue'
38
- area = AreaChart.new data, color: 'blue'
58
+ chart = AreaChart.new data, color: 'blue'
39
59
  ```
40
60
 
41
61
  Get the full SVG output by calling `#render`:
42
62
 
43
63
  ```ruby
44
- puts area.render
64
+ puts chart.render
45
65
  #=> <?xml version="1.0" standalone="no"?>
46
66
  # <svg> ... </svg>
47
67
  ```
@@ -49,42 +69,194 @@ puts area.render
49
69
  Save it to file, by calling `#save`:
50
70
 
51
71
  ```ruby
52
- area.save "my-chart.svg"
72
+ chart.save "my-chart.svg"
53
73
  ```
54
74
 
55
75
  Get its inner SVG string by calling `#to_s`:
56
76
 
57
77
  ```ruby
58
- puts area.to_s
78
+ puts chart.to_s
59
79
  #=> <polyline fill="blue" stroke="blue" stroke-width="2" points="..."/>
60
80
  ```
61
81
 
62
82
 
63
83
  The objects returned from all the mini chart classes are [Victor::SVG][2] objects, so they support all methods supported by it as well.
64
84
 
65
- ## Options
85
+ ## Chart Types
86
+
87
+ ### Line Chart
88
+
89
+ <img src='examples/line_chart.svg' align='right' width=300>
90
+
91
+ ```ruby
92
+ LineChart.new [10, 30, 20, 40, 30], background: '#eee',
93
+ height: 50, width: 250, color: 'green'
94
+ ```
95
+
96
+ ### Bar Chart
97
+
98
+ <img src='examples/bar_chart.svg' align='right' width=300>
99
+
100
+ ```ruby
101
+ BarChart.new [10, 30, 20, 40, 30], background: '#eee',
102
+ height: 50, width: 250, color: 'green'
103
+ ```
104
+
105
+ ### Area Chart
106
+
107
+ <img src='examples/area_chart.svg' align='right' width=300>
108
+
109
+ ```ruby
110
+ AreaChart.new [10, 30, 20, 40, 30], background: '#eee',
111
+ height: 50, width: 250, color: 'green'
112
+ ```
113
+
114
+ ### Horizontal Bar Meter
115
+
116
+ <img src='examples/multiple_horizontal_bars.svg' align='right'>
117
+
118
+ ```ruby
119
+ positive = HorizontalBarMeter.new 70,
120
+ height: 20, width: 250, background: '#9f9', color: 'green'
121
+
122
+ negative = HorizontalBarMeter.new -80,
123
+ height: 20, width: 250, background: '#f99', color: 'red'
124
+
125
+ dual = HorizontalBarMeter.new 80,
126
+ height: 20, width: 250, background: '#99f', color: 'blue',
127
+ mode: :dual, notches: [0]
128
+ ```
129
+
130
+ Meter charts support [additional options](#meter-options).
131
+
132
+ ### Vertical Bar Meter
133
+
134
+ <img src='examples/multiple_vertical_bars.svg' align='right'>
135
+
136
+ ```ruby
137
+ positive = VerticalBarMeter.new 70,
138
+ width: 20, height: 250, background: '#9f9', color: 'green'
139
+
140
+ negative = VerticalBarMeter.new -80,
141
+ width: 20, height: 250, background: '#f99', color: 'red'
142
+
143
+ dual = VerticalBarMeter.new 80,
144
+ width: 20, height: 250, background: '#99f', color: 'blue',
145
+ mode: :dual, notches: [0]
146
+ ```
147
+
148
+ Meter charts support [additional options](#meter-options).
149
+
150
+ ### Horizontal Status Leds
151
+
152
+ <img src='examples/horizontal_status_leds.svg' align='right' width=150>
66
153
 
67
- All minichart classes support a second hash argument for options
154
+ ```ruby
155
+ HorizontalStatusLeds.new [1,1,-1,0,1,1,1,1,1,-1,-1,1],
156
+ background: '#ccc'
157
+ ```
158
+
159
+ Led charts support [additional options](#leds-options).
160
+
161
+ ### Vertical Status Leds
162
+
163
+ <img src='examples/vertical_status_leds.svg' align='right' width=25>
164
+
165
+ ```ruby
166
+ VerticalStatusLeds.new [1,1,1,1,-1,1,-1,1,0,1],
167
+ background: '#ccc'
168
+ ```
169
+
170
+ Led charts support [additional options](#leds-options).
171
+
172
+
173
+ ## Configuration
174
+
175
+ Chart options can be set in one of three ways.
176
+
177
+ ### Class-level default options
178
+
179
+ See or set default options for any chart class by calling its `::options` method:
180
+
181
+ ```ruby
182
+ # See all options
183
+ p AreaChart.options
184
+ #=> {:background=>"white", :height=>100, :width=>300, :stroke=>2, :style=>{}, :color=>"#66f"}
185
+
186
+ # Set a single default option
187
+ AreaChart.options[:color] = '#333'
188
+
189
+ # Set multiple options at once
190
+ AreaChart.options background: 'black', color: 'green'
191
+ ```
192
+
193
+ ### Instance initialization options
194
+
195
+ Set options by providing a hash as the second argument on initialization:
68
196
 
69
197
  ```ruby
70
- chart = LineChart.new data, options
198
+ chart = AreaChart.new data, height: 120, width: 500
71
199
  ```
72
200
 
73
- | Option | Default | Description |
74
- | -------------- | ---------------------------- | ----------------------- |
75
- | `background` | *None* | Chart background color |
76
- | `color` | #333 | Chart color |
77
- | `aspect_ratio` | 3 | Set automatic width |
78
- | `height` | 100 | Chart height in pixels |
79
- | `width` | Calculated by `aspect_ratio` | Chart width in pixels |
80
- | `stroke` | 2 | Line stroke width |
81
- | `style` | *None* | Style hash for the SVG |
201
+ ### Instance-level options
202
+
203
+ After initialization, you can still update individual options:
204
+
205
+ ```ruby
206
+ chart = AreaChart.new data
207
+ chart.options[:background] = 'yellow'
208
+ ```
209
+
210
+ ## Options Reference
211
+
212
+ ### Basic Options
82
213
 
214
+ - **background**: Chart background color.
215
+ - **color**: Chart color.
216
+ - **height**: Chart height in pixels.
217
+ - **width**: Chart width in pixels.
218
+ - **stroke**: Line stroke width. This has a different effect in different chart types.
219
+ - **style**: CSS Style hash to apply to the entire SVG.
220
+ - **padding**: Chart padding in pixels.
221
+
222
+ ### Meter Options
223
+
224
+ Meter charts support these options in additon to the basic options:
225
+
226
+ - **mode**: Display mode. Can be `:positive`, `:negative`, `:dual` or `:auto` (default).
227
+ The `:auto` mode will switch between `:positive` and `:negative` based on the
228
+ value.
229
+ - **max**: The absolute maximum value. This number should be positive even for negative
230
+ charts.
231
+ - **notches**: An array of one or more levels to place a notch marker. Use positive values
232
+ only.
233
+ - **notch_thickness**: Thickness of the notch markers.
234
+ - **notch_color**: Color of the notch markers.
235
+ - **clipping_indicator**: If true, show a marker when the value exceeds the range.
236
+ - **clipping_indicator_thickness**: Thickness of the clipping indicator.
237
+ - **clipping_indicator_color**: Color of the clipping indicator.
238
+
239
+ ### Leds Options
240
+
241
+ Led charts support these options in additon to the basic options (excluding
242
+ the `color` option):
243
+
244
+ - **positive_color**: Color to use when the value is greater than 0.
245
+ - **negative_color**: Color to use when the value is less than 0.
246
+ - **neutral_color**: Color to use when the value is 0 or nil.
247
+ - **min_opacity**: A value between 0 and 1 representing the minimum opacity that will be applied to values when they are lower than the maximum range.
83
248
 
84
249
  ## Examples
85
250
 
86
251
  See more examples (code and SVG output) in the [examples folder][1].
87
252
 
253
+ ## Contributing / Support
254
+
255
+ If you experience any issue, have a question or a suggestion, or if you wish
256
+ to contribute, feel free to [open an issue][issues].
257
+
258
+ ---
88
259
 
89
260
  [1]: https://github.com/DannyBen/minichart/tree/master/examples#examples
90
- [2]: https://github.com/DannyBen/victor
261
+ [2]: https://github.com/DannyBen/victor
262
+ [issues]: https://github.com/DannyBen/minichart/issues
@@ -1,7 +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'
6
14
 
7
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
- 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,44 @@
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.to_f
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
+ opacity: opacity(value),
37
+ fill: options[color],
38
+ stroke_width: options[:stroke],
39
+ stroke: options[:background]
40
+ }
41
+ }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ module Minichart
2
+ # Base class for led charts
3
+ class Leds < Base
4
+ class << self
5
+ def leds_defaults
6
+ @meter_defaults ||= {
7
+ positive_color: '#6f6',
8
+ negative_color: '#f66',
9
+ neutral_color: '#eee',
10
+ min_opacity: 1,
11
+ padding: 2,
12
+ }
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ # Returns opacith level for a given value
19
+ def opacity(value)
20
+ return 1 if !value or value == 0
21
+ value.abs * ((1 - options[:min_opacity]) / max) + options[:min_opacity]
22
+ end
23
+
24
+ # Returns the absolute highest or loest value.
25
+ # Used to define the availble range of values
26
+ def max
27
+ @max ||= [data.compact.max, data.compact.min.abs].max.to_f
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,50 @@
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.to_f
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
+ opacity: opacity(value),
43
+ fill: options[color],
44
+ stroke_width: options[:stroke],
45
+ stroke: options[:background]
46
+ }
47
+ }
48
+ end
49
+ end
50
+ 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.2.0"
2
+ VERSION = "0.3.2"
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.2.0
4
+ version: 0.3.2
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: 2020-05-29 00:00:00.000000000 Z
11
+ date: 2020-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: victor
@@ -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,7 +57,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
57
  requirements:
50
58
  - - ">="
51
59
  - !ruby/object:Gem::Version
52
- version: 2.4.0
60
+ version: 2.5.0
53
61
  required_rubygems_version: !ruby/object:Gem::Requirement
54
62
  requirements:
55
63
  - - ">="
@@ -1,27 +0,0 @@
1
- module Minichart
2
- class AreaChart < Chart
3
- def build
4
- 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
- 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,50 +0,0 @@
1
- module Minichart
2
- class Chart < Victor::SVG
3
- attr_reader :aspect_ratio, :background, :color, :data,
4
- :height, :stroke, :style, :width
5
-
6
- def initialize(data, opts = {})
7
- @data = data
8
- @background = opts[:background]
9
- @aspect_ratio = opts[:aspect_ratio] || 3
10
- @height = opts[:height] || 100
11
- @width = opts[:width] || (aspect_ratio * height).round
12
- @stroke = opts[:stroke] || 2
13
- @style = opts[:style] || {}
14
- @color = opts[:color] || '#333'
15
-
16
- super height: height, width: width, style: style,
17
- viewBox: "0 0 #{width} #{height}"
18
-
19
- rect x: 0, y: 0, width: width, height: height, fill: background if background
20
-
21
- build
22
- end
23
-
24
- def build
25
- raise NotImplementedError, "#build is not implemented"
26
- end
27
-
28
- protected
29
-
30
- def inverted_points(opts={})
31
- normalized_points(opts).map { |point| [point[0], 1-point[1]] }
32
- end
33
-
34
- def normalized_points(opts={})
35
- x_point_count = opts[:x_point_count] || data.count-1
36
-
37
- range = (data.max - data.min).to_f
38
- x_width = 1/(x_point_count).to_f
39
- result = []
40
-
41
- data.each_with_index do |y, index|
42
- x = index*x_width
43
- y = (y-data.min)/range
44
- result << [x,y]
45
- end
46
-
47
- result
48
- end
49
- end
50
- end