minichart 0.2.0 → 0.2.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 +4 -4
- data/README.md +77 -16
- data/lib/minichart.rb +8 -4
- data/lib/minichart/base.rb +65 -0
- data/lib/minichart/{area_chart.rb → charts/area_chart.rb} +1 -1
- data/lib/minichart/{bar_chart.rb → charts/bar_chart.rb} +29 -29
- data/lib/minichart/charts/chart.rb +28 -0
- data/lib/minichart/{line_chart.rb → charts/line_chart.rb} +1 -1
- data/lib/minichart/id_generator.rb +14 -0
- data/lib/minichart/meters/horizontal_bar_meter.rb +91 -0
- data/lib/minichart/meters/meter.rb +8 -0
- data/lib/minichart/version.rb +1 -1
- metadata +9 -5
- data/lib/minichart/chart.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 144b47573abadc9f4bfe5e41dea006bb24705db37dc93767353ba62d389bdd1d
|
4
|
+
data.tar.gz: 7c4936f6947307fd106b1e0700fff88c9ad7f5721dfc50dfd23ca3051a79b7b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c66ae222164ccc21a98cd8eddd790f54d114897ed6374b7e36fd242d16364e49ccec532e580d3a180d094c48fd27853f7b01bd85c81e7b8a4af8254a595af191
|
7
|
+
data.tar.gz: 6d19ce05f98de008d26127b8046512b66e1c68af0972e3b66f15ce3a6bfe4ac8a99a6de76457757e34903e3012b757ba31eeba688a018393304ca8cff41a1e74
|
data/README.md
CHANGED
@@ -33,15 +33,13 @@ Initialize a chart with data, and optional options:
|
|
33
33
|
|
34
34
|
```ruby
|
35
35
|
data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9]
|
36
|
-
|
37
|
-
bars = BarChart.new data, color: 'blue'
|
38
|
-
area = AreaChart.new data, color: 'blue'
|
36
|
+
chart = AreaChart.new data, color: 'blue'
|
39
37
|
```
|
40
38
|
|
41
39
|
Get the full SVG output by calling `#render`:
|
42
40
|
|
43
41
|
```ruby
|
44
|
-
puts
|
42
|
+
puts chart.render
|
45
43
|
#=> <?xml version="1.0" standalone="no"?>
|
46
44
|
# <svg> ... </svg>
|
47
45
|
```
|
@@ -49,42 +47,105 @@ puts area.render
|
|
49
47
|
Save it to file, by calling `#save`:
|
50
48
|
|
51
49
|
```ruby
|
52
|
-
|
50
|
+
chart.save "my-chart.svg"
|
53
51
|
```
|
54
52
|
|
55
53
|
Get its inner SVG string by calling `#to_s`:
|
56
54
|
|
57
55
|
```ruby
|
58
|
-
puts
|
56
|
+
puts chart.to_s
|
59
57
|
#=> <polyline fill="blue" stroke="blue" stroke-width="2" points="..."/>
|
60
58
|
```
|
61
59
|
|
62
60
|
|
63
61
|
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
62
|
|
63
|
+
## Chart Types
|
64
|
+
|
65
|
+
### Line Chart
|
66
|
+
|
67
|
+
<img src='examples/line_chart.svg' align='right'>
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
LineChart.new data, height: 50, background: '#eee',
|
71
|
+
aspect_ratio: 5, color: 'green'
|
72
|
+
```
|
73
|
+
|
74
|
+
### Bar Chart
|
75
|
+
|
76
|
+
<img src='examples/bar_chart.svg' align='right'>
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
BarChart.new data, height: 50, background: '#eee',
|
80
|
+
aspect_ratio: 5, color: 'green'
|
81
|
+
```
|
82
|
+
|
83
|
+
### Area Chart
|
84
|
+
|
85
|
+
<img src='examples/area_chart.svg' align='right'>
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
AreaChart.new data, height: 50, background: '#eee',
|
89
|
+
aspect_ratio: 5, color: 'green'
|
90
|
+
```
|
91
|
+
|
92
|
+
### Horizontal Bar Meter
|
93
|
+
|
94
|
+
<img src='examples/multiple_horizontal_bars.svg' align='right'>
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
positive = HorizontalBarMeter.new 70,
|
98
|
+
height: 20, width: 250, background: '#9f9', color: 'green'
|
99
|
+
|
100
|
+
negative = HorizontalBarMeter.new -80,
|
101
|
+
height: 20, width: 250, background: '#f99', color: 'red'
|
102
|
+
|
103
|
+
dual = HorizontalBarMeter.new 80,
|
104
|
+
height: 20, width: 250, background: '#99f', color: 'blue',
|
105
|
+
mode: :dual, zero_line: true
|
106
|
+
```
|
107
|
+
|
108
|
+
Meter charts support [additional options](#meter-options).
|
109
|
+
|
65
110
|
## Options
|
66
111
|
|
67
|
-
|
112
|
+
### Basic Options
|
113
|
+
|
114
|
+
All chart classes support a second hash argument for options
|
68
115
|
|
69
116
|
```ruby
|
70
117
|
chart = LineChart.new data, options
|
71
118
|
```
|
72
119
|
|
73
|
-
| Option | Default | Description
|
74
|
-
| -------------- | ---------------------------- |
|
75
|
-
| `background` |
|
76
|
-
| `color` | #333
|
77
|
-
| `aspect_ratio` | 3
|
78
|
-
| `height` | 100
|
79
|
-
| `width` | Calculated by `aspect_ratio` | Chart width in pixels
|
80
|
-
| `stroke` | 2
|
81
|
-
| `style` | *None* | Style hash for the SVG
|
120
|
+
| Option | Default | Description |
|
121
|
+
| -------------- | ---------------------------- | ------------------------------------------------------------ |
|
122
|
+
| `background` | `"white"` | Chart background color |
|
123
|
+
| `color` | `"#333"` | Chart color |
|
124
|
+
| `aspect_ratio` | `3` | Set automatic width |
|
125
|
+
| `height` | `100` | Chart height in pixels |
|
126
|
+
| `width` | Calculated by `aspect_ratio` | Chart width in pixels |
|
127
|
+
| `stroke` | `2` | Line stroke width.<br />In `BarChart` determines the gap between bars |
|
128
|
+
| `style` | *None* | Style hash for the SVG |
|
129
|
+
|
130
|
+
|
131
|
+
### Meter Options
|
82
132
|
|
133
|
+
Meter charts support these options in additon to the basic options:
|
134
|
+
|
135
|
+
| Option | Default | Description |
|
136
|
+
| ------------------ | --------- | ------------------------------------------------------------ |
|
137
|
+
| `mode` | `:auto` | Display mode.<br />Can be `:positive`, `:negative`, `:auto` or `:dual` |
|
138
|
+
| `max` | `100` | The absolute maximum value.<br />This number should be positive even for nengative charts |
|
139
|
+
| `zero_line` | `false` | If true, mark the zero line |
|
140
|
+
| `zero_line_stroke` | `4` | Width of the zero line |
|
141
|
+
| `zero_line_color` | `"black"` | Color of the zero line |
|
83
142
|
|
84
143
|
## Examples
|
85
144
|
|
86
145
|
See more examples (code and SVG output) in the [examples folder][1].
|
87
146
|
|
147
|
+
---
|
148
|
+
|
88
149
|
|
89
150
|
[1]: https://github.com/DannyBen/minichart/tree/master/examples#examples
|
90
151
|
[2]: https://github.com/DannyBen/victor
|
data/lib/minichart.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'victor'
|
2
|
-
require 'minichart/
|
3
|
-
require 'minichart/
|
4
|
-
require 'minichart/
|
5
|
-
require 'minichart/
|
2
|
+
require 'minichart/id_generator'
|
3
|
+
require 'minichart/base'
|
4
|
+
require 'minichart/charts/chart'
|
5
|
+
require 'minichart/charts/line_chart'
|
6
|
+
require 'minichart/charts/bar_chart'
|
7
|
+
require 'minichart/charts/area_chart'
|
8
|
+
require 'minichart/meters/meter'
|
9
|
+
require 'minichart/meters/horizontal_bar_meter'
|
6
10
|
|
7
11
|
require 'byebug' if ENV['BYEBUG']
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Minichart
|
2
|
+
# Base class for all Minichart classes
|
3
|
+
class Base < Victor::SVGBase
|
4
|
+
attr_reader :aspect_ratio, :background, :color, :data,
|
5
|
+
:height, :stroke, :style, :width, :opts
|
6
|
+
|
7
|
+
def initialize(data, opts = {})
|
8
|
+
@data, @opts = data, opts
|
9
|
+
|
10
|
+
super height: height, width: width, style: style, viewBox: viewbox
|
11
|
+
element :rect, x: 0, y: 0, width: width, height: height, fill: background
|
12
|
+
|
13
|
+
clip_path_id = IDGenerator.next
|
14
|
+
setup_clip_path clip_path_id
|
15
|
+
|
16
|
+
element :g, clip_path: "url(##{clip_path_id})" do
|
17
|
+
build
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup_clip_path(id)
|
22
|
+
element :defs do
|
23
|
+
element :clipPath, id: id do
|
24
|
+
element :rect, width: width, height: height
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def background
|
30
|
+
opts[:background] ||= 'white'
|
31
|
+
end
|
32
|
+
|
33
|
+
def aspect_ratio
|
34
|
+
opts[:aspect_ratio] ||= 3
|
35
|
+
end
|
36
|
+
|
37
|
+
def height
|
38
|
+
opts[:height] ||= 100
|
39
|
+
end
|
40
|
+
|
41
|
+
def width
|
42
|
+
opts[:width] ||= (aspect_ratio * height).round
|
43
|
+
end
|
44
|
+
|
45
|
+
def stroke
|
46
|
+
opts[:stroke] ||= 2
|
47
|
+
end
|
48
|
+
|
49
|
+
def style
|
50
|
+
opts[:style] ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def color
|
54
|
+
opts[:color] ||= '#333'
|
55
|
+
end
|
56
|
+
|
57
|
+
def viewbox
|
58
|
+
"0 0 #{width} #{height}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def build
|
62
|
+
raise NotImplementedError, "#build is not implemented"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,29 +1,29 @@
|
|
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
|
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
|
+
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 ||= 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
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Minichart
|
2
|
+
# Base class for charts with data series
|
3
|
+
class Chart < Base
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def inverted_points(opts={})
|
8
|
+
normalized_points(opts).map { |point| [point[0], 1-point[1]] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def normalized_points(opts={})
|
12
|
+
x_point_count = opts[:x_point_count] || data.count-1
|
13
|
+
|
14
|
+
range = (data.max - data.min).to_f
|
15
|
+
x_width = 1 / (x_point_count).to_f
|
16
|
+
result = []
|
17
|
+
|
18
|
+
data.each_with_index do |y, index|
|
19
|
+
x = index*x_width
|
20
|
+
y = (y - data.min) / range
|
21
|
+
result << [x,y]
|
22
|
+
end
|
23
|
+
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Minichart
|
2
|
+
class HorizontalBarMeter < Meter
|
3
|
+
def build
|
4
|
+
draw_bar
|
5
|
+
draw_zero_line if zero_line
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def draw_bar
|
11
|
+
x = if mode == :negative
|
12
|
+
width - bar_width
|
13
|
+
elsif mode == :dual
|
14
|
+
middle = width * 0.5
|
15
|
+
value >= 0 ? middle : middle - bar_width
|
16
|
+
else
|
17
|
+
0
|
18
|
+
end
|
19
|
+
|
20
|
+
element :rect, x: x, y: 0, height: height, width: bar_width, style: style
|
21
|
+
end
|
22
|
+
|
23
|
+
def draw_zero_line
|
24
|
+
x = if mode == :negative
|
25
|
+
width-zero_line_stroke
|
26
|
+
elsif mode == :dual
|
27
|
+
width / 2 - zero_line_stroke / 2
|
28
|
+
else
|
29
|
+
0
|
30
|
+
end
|
31
|
+
|
32
|
+
element :rect, x: x, y: 0,
|
33
|
+
height: height, width: zero_line_stroke,
|
34
|
+
fill: zero_line_color
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def mode
|
39
|
+
opts[:mode] ||= :auto
|
40
|
+
|
41
|
+
if opts[:mode] == :auto
|
42
|
+
value >= 0 ? :positive : :negative
|
43
|
+
else
|
44
|
+
opts[:mode]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def max
|
49
|
+
opts[:max] ||= 100
|
50
|
+
end
|
51
|
+
|
52
|
+
def zero_line
|
53
|
+
opts[:zero_line]
|
54
|
+
end
|
55
|
+
|
56
|
+
def zero_line_stroke
|
57
|
+
opts[:zero_line_stroke] || 6
|
58
|
+
end
|
59
|
+
|
60
|
+
def zero_line_color
|
61
|
+
opts[:zero_line_color] || 'black'
|
62
|
+
end
|
63
|
+
|
64
|
+
def width_factor
|
65
|
+
width / max.to_f
|
66
|
+
end
|
67
|
+
|
68
|
+
def clamped_value
|
69
|
+
case mode
|
70
|
+
when :positive
|
71
|
+
value.clamp 0, max
|
72
|
+
when :negative
|
73
|
+
value.clamp -max, 0
|
74
|
+
when :dual
|
75
|
+
value.clamp -max, max
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def bar_width
|
80
|
+
if mode == :dual
|
81
|
+
clamped_value.abs * width_factor * 0.5
|
82
|
+
else
|
83
|
+
clamped_value.abs * width_factor
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def style
|
88
|
+
{ fill: color, stroke_width: stroke, stroke: background }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/minichart/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minichart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danny Ben Shitrit
|
@@ -32,10 +32,14 @@ extra_rdoc_files: []
|
|
32
32
|
files:
|
33
33
|
- README.md
|
34
34
|
- lib/minichart.rb
|
35
|
-
- lib/minichart/
|
36
|
-
- lib/minichart/
|
37
|
-
- lib/minichart/
|
38
|
-
- lib/minichart/
|
35
|
+
- lib/minichart/base.rb
|
36
|
+
- lib/minichart/charts/area_chart.rb
|
37
|
+
- lib/minichart/charts/bar_chart.rb
|
38
|
+
- lib/minichart/charts/chart.rb
|
39
|
+
- lib/minichart/charts/line_chart.rb
|
40
|
+
- lib/minichart/id_generator.rb
|
41
|
+
- lib/minichart/meters/horizontal_bar_meter.rb
|
42
|
+
- lib/minichart/meters/meter.rb
|
39
43
|
- lib/minichart/version.rb
|
40
44
|
homepage: https://github.com/DannyBen/minichart
|
41
45
|
licenses:
|
data/lib/minichart/chart.rb
DELETED
@@ -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
|