minichart 0.2.0 → 0.3.2

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