sankey 0.0.0
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.
- data/LICENSE +20 -0
- data/README.rdoc +11 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/examples/coffee.rb +23 -0
- data/lib/generator.rb +233 -0
- data/lib/imagedata.rb +11 -0
- data/lib/point.rb +20 -0
- data/lib/process.rb +83 -0
- data/lib/processvertex.rb +18 -0
- data/lib/reagent.rb +40 -0
- data/lib/renderers/renderer.rb +7 -0
- data/lib/renderers/rvg.rb +65 -0
- data/lib/sankey.rb +3 -0
- data/lib/vertex.rb +9 -0
- data/sankey.gemspec +59 -0
- metadata +80 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Jan Stępień
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
desc "Run rdoc"
|
5
|
+
task :rdoc do
|
6
|
+
print `rdoc -a -S -N -w 2 -U -o doc/rdoc README.rdoc lib`
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'jeweler'
|
11
|
+
Jeweler::Tasks.new do |gemspec|
|
12
|
+
gemspec.name = 'sankey'
|
13
|
+
gemspec.summary = 'Sankey diagrams generator'
|
14
|
+
gemspec.description = 'A small tool which generates Sankey diagrams.'
|
15
|
+
gemspec.email = 'jstepien@users.sourceforge.net'
|
16
|
+
gemspec.homepage = "http://github.com/jstepien/sankey"
|
17
|
+
gemspec.authors = ['Jan Stępień']
|
18
|
+
gemspec.files.exclude '.gitignore'
|
19
|
+
gemspec.add_dependency 'rmagick'
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
23
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/examples/coffee.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/ruby -I../lib
|
2
|
+
require 'sankey'
|
3
|
+
require 'renderers/rvg'
|
4
|
+
|
5
|
+
g = Sankey::Generator.new
|
6
|
+
beans = Sankey::Reagent.new 100
|
7
|
+
ground_coffee = Sankey::Reagent.new 100
|
8
|
+
water = Sankey::Reagent.new 200
|
9
|
+
beverage = Sankey::Reagent.new 280
|
10
|
+
crema = Sankey::Reagent.new 20
|
11
|
+
espresso_machine = Sankey::Process.new
|
12
|
+
grinder = Sankey::Process.new
|
13
|
+
beans.drain = grinder
|
14
|
+
water.drain = espresso_machine
|
15
|
+
ground_coffee.drain = espresso_machine
|
16
|
+
ground_coffee.source = grinder
|
17
|
+
beverage.source = espresso_machine
|
18
|
+
crema.source = espresso_machine
|
19
|
+
g.processes.push espresso_machine
|
20
|
+
g.processes.push grinder
|
21
|
+
g.go!
|
22
|
+
p = Sankey::Renderers::RVG.new g.data, "espresso.png"
|
23
|
+
p.render
|
data/lib/generator.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'processvertex'
|
2
|
+
require 'point'
|
3
|
+
require 'imagedata'
|
4
|
+
|
5
|
+
module Sankey
|
6
|
+
class Generator
|
7
|
+
attr_reader :processes, :data
|
8
|
+
|
9
|
+
ProcessWidth = 40
|
10
|
+
ProcessLayerStep = 100
|
11
|
+
ProcessSideStep = 50
|
12
|
+
Margin = 5
|
13
|
+
InputReagentsStep = 20
|
14
|
+
OutputReagentsStep = 20
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@processes = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def go!
|
21
|
+
@processes_vertices = {}
|
22
|
+
@drawn_reagents = []
|
23
|
+
@vertices = []
|
24
|
+
@input_reagent_offset = Margin
|
25
|
+
@below_next_process_offset = {}
|
26
|
+
@process_input_offset = {}
|
27
|
+
@process_output_left_offset = {}
|
28
|
+
@process_output_right_offset = {}
|
29
|
+
get_max_distances_to_processes
|
30
|
+
to_be_drawn = []
|
31
|
+
input_reagents.each { |r| to_be_drawn.push r }
|
32
|
+
until to_be_drawn.empty? do
|
33
|
+
STDERR.puts "to draw"
|
34
|
+
to_be_drawn.each { |r| STDERR.puts " " + r.to_s + " " + r.name }
|
35
|
+
not_drawn = []
|
36
|
+
to_be_drawn.each { |r| not_drawn += recursively_draw r }
|
37
|
+
to_be_drawn = not_drawn.uniq
|
38
|
+
end
|
39
|
+
make_coordinates_positive
|
40
|
+
w, h = get_size
|
41
|
+
@data = ImageData.new @vertices, w, h
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Moves all vertices so that there are no negative coordinates.
|
47
|
+
def make_coordinates_positive
|
48
|
+
min_y = @vertices[0].points[0].y
|
49
|
+
min_x = @vertices[0].points[0].x
|
50
|
+
@vertices.each do |v|
|
51
|
+
v.points.each do |p|
|
52
|
+
min_y = [min_y, p.y].min
|
53
|
+
min_x = [min_x, p.x].min
|
54
|
+
end
|
55
|
+
end
|
56
|
+
min_x -= Margin
|
57
|
+
STDERR.puts "Vector: #{-min_x} -#{-min_y}."
|
58
|
+
vector = [-min_x, -min_y]
|
59
|
+
@vertices.each do |v|
|
60
|
+
v.points.map! do |p|
|
61
|
+
p + vector
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_max_distances_to_processes(reagent = nil, level = 0)
|
67
|
+
if reagent.nil?
|
68
|
+
@max_distances_to_processes = {}
|
69
|
+
input_reagents.each { |r| get_max_distances_to_processes r }
|
70
|
+
elsif !reagent.drain.nil?
|
71
|
+
new_level = level + 1
|
72
|
+
if @max_distances_to_processes[reagent.drain].nil? or
|
73
|
+
@max_distances_to_processes[reagent.drain] < new_level
|
74
|
+
@max_distances_to_processes[reagent.drain] = new_level
|
75
|
+
end
|
76
|
+
reagent.drain.output.each do |r|
|
77
|
+
get_max_distances_to_processes r, new_level
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def draw_process(process)
|
83
|
+
layer = @max_distances_to_processes[process] - 1
|
84
|
+
height = process.total_reagents_mass
|
85
|
+
corner = Point.new (layer+1)*(ProcessLayerStep+ProcessWidth) + Margin,
|
86
|
+
Margin
|
87
|
+
v = ProcessVertex.new corner, corner + [0, height],
|
88
|
+
corner + [ProcessWidth, height], corner + [ProcessWidth, 0]
|
89
|
+
@vertices.push v
|
90
|
+
@processes_vertices[process] = v
|
91
|
+
# Initialize @below_next_process_offset
|
92
|
+
process.input.each do |r|
|
93
|
+
unless r.source.nil?
|
94
|
+
@below_next_process_offset[r.source] = Margin + height
|
95
|
+
break
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def recursively_draw(reagent)
|
101
|
+
return [] if @drawn_reagents.include? reagent
|
102
|
+
STDERR.puts " drawing " + reagent.name if reagent.name != ""
|
103
|
+
if reagent.drain.nil?
|
104
|
+
if reagent.source.is_first_in_output_queue reagent
|
105
|
+
draw_output_reagent reagent
|
106
|
+
reagent.source.remove_from_output_queue reagent
|
107
|
+
else
|
108
|
+
STDERR.puts " later"
|
109
|
+
return [reagent]
|
110
|
+
end
|
111
|
+
elsif reagent.source.nil?
|
112
|
+
if reagent.drain.is_first_in_input_queue reagent
|
113
|
+
draw_input_reagent reagent
|
114
|
+
reagent.drain.remove_from_input_queue reagent
|
115
|
+
else
|
116
|
+
STDERR.puts " later"
|
117
|
+
return [reagent]
|
118
|
+
end
|
119
|
+
else
|
120
|
+
if reagent.drain.is_first_in_input_queue(reagent) and
|
121
|
+
reagent.source.is_first_in_output_queue(reagent)
|
122
|
+
draw_middle_reagent reagent
|
123
|
+
reagent.source.remove_from_output_queue reagent
|
124
|
+
reagent.drain.remove_from_input_queue reagent
|
125
|
+
else
|
126
|
+
#STDERR.puts reagent.drain.is_first_in_input_queue(reagent)
|
127
|
+
#STDERR.puts reagent.source.is_first_in_output_queue(reagent)
|
128
|
+
STDERR.puts " later"
|
129
|
+
return [reagent]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
@drawn_reagents.push reagent
|
133
|
+
to_draw_later = []
|
134
|
+
unless reagent.drain.nil?
|
135
|
+
reagent.drain.output.each { |r| to_draw_later += recursively_draw r }
|
136
|
+
end
|
137
|
+
to_draw_later
|
138
|
+
end
|
139
|
+
|
140
|
+
def draw_middle_reagent(reagent)
|
141
|
+
left_height = reagent.source.total_reagents_mass *
|
142
|
+
reagent.source.fraction(reagent)
|
143
|
+
right_height = reagent.drain.total_reagents_mass *
|
144
|
+
reagent.drain.fraction(reagent)
|
145
|
+
[reagent.source, reagent.drain].each do |p|
|
146
|
+
draw_process p unless @processes_vertices.include? p
|
147
|
+
end
|
148
|
+
@process_input_offset[reagent.drain] ||= 0
|
149
|
+
@process_output_left_offset[reagent.source] ||= 0
|
150
|
+
left_corner = @processes_vertices[reagent.source].output_edge[:top] +
|
151
|
+
[0, @process_output_left_offset[reagent.source]]
|
152
|
+
right_corner = @processes_vertices[reagent.drain].input_edge[:top] +
|
153
|
+
[0, @process_input_offset[reagent.drain]]
|
154
|
+
v = Vertex.new
|
155
|
+
v.points.push left_corner
|
156
|
+
v.points.push left_corner + [0, left_height]
|
157
|
+
v.points.push right_corner + [0, right_height]
|
158
|
+
v.points.push right_corner
|
159
|
+
@vertices.push v
|
160
|
+
@process_input_offset[reagent.drain] += right_height
|
161
|
+
@process_output_left_offset[reagent.source] += left_height
|
162
|
+
end
|
163
|
+
|
164
|
+
def draw_input_reagent(reagent)
|
165
|
+
draw_process reagent.drain unless @processes_vertices.include? reagent.drain
|
166
|
+
height = reagent.drain.total_reagents_mass * reagent.drain.fraction(reagent)
|
167
|
+
@process_input_offset[reagent.drain] ||= 0
|
168
|
+
v = Vertex.new
|
169
|
+
if @process_input_offset[reagent.drain] == 0 and reagent.joins_later
|
170
|
+
left_corner = @processes_vertices[reagent.drain].input_edge[:top] +
|
171
|
+
[-ProcessSideStep, -height]
|
172
|
+
else
|
173
|
+
left_corner = Point.new Margin, @input_reagent_offset
|
174
|
+
end
|
175
|
+
v.points.push left_corner
|
176
|
+
v.points.push left_corner + [5, height / 2]
|
177
|
+
v.points.push left_corner + [0, height]
|
178
|
+
v.points.push @processes_vertices[reagent.drain].input_edge[:top] +
|
179
|
+
[0, @process_input_offset[reagent.drain] + height]
|
180
|
+
v.points.push @processes_vertices[reagent.drain].input_edge[:top] +
|
181
|
+
[0, @process_input_offset[reagent.drain]]
|
182
|
+
@vertices.push v
|
183
|
+
@input_reagent_offset += height + InputReagentsStep
|
184
|
+
@process_input_offset[reagent.drain] += height
|
185
|
+
end
|
186
|
+
|
187
|
+
def draw_output_reagent(reagent)
|
188
|
+
height = reagent.source.total_reagents_mass *
|
189
|
+
reagent.source.fraction(reagent)
|
190
|
+
@process_output_left_offset[reagent.source] ||= 0
|
191
|
+
@process_output_right_offset[reagent.source] ||= 0
|
192
|
+
right_offset = @process_output_right_offset[reagent.source] +
|
193
|
+
(@below_next_process_offset[reagent.source] ||= 0)
|
194
|
+
left_corner = @processes_vertices[reagent.source].output_edge[:top] +
|
195
|
+
[0, @process_output_left_offset[reagent.source]]
|
196
|
+
v = Vertex.new
|
197
|
+
v.points.push left_corner
|
198
|
+
v.points.push left_corner + [0, height]
|
199
|
+
v.points.push left_corner +
|
200
|
+
[ProcessLayerStep, right_offset + height]
|
201
|
+
v.points.push left_corner +
|
202
|
+
[ProcessLayerStep + 5, right_offset + height / 2]
|
203
|
+
v.points.push left_corner +
|
204
|
+
[ProcessLayerStep, right_offset]
|
205
|
+
@vertices.push v
|
206
|
+
@process_output_left_offset[reagent.source] += height
|
207
|
+
@process_output_right_offset[reagent.source] += height + InputReagentsStep
|
208
|
+
end
|
209
|
+
|
210
|
+
def input_reagents
|
211
|
+
return @input_reagents if defined? @input_reagents
|
212
|
+
@input_reagents = []
|
213
|
+
@processes.each do |process|
|
214
|
+
process.input.each do |reagent|
|
215
|
+
@input_reagents.push reagent if reagent.source.nil?
|
216
|
+
end
|
217
|
+
end
|
218
|
+
@input_reagents.uniq!
|
219
|
+
@input_reagents
|
220
|
+
end
|
221
|
+
|
222
|
+
def get_size
|
223
|
+
w = h = 0
|
224
|
+
@vertices.each do |vertex|
|
225
|
+
vertex.points.each do |point|
|
226
|
+
w = point.x + Margin if point.x + Margin > w
|
227
|
+
h = point.y + Margin if point.y + Margin > h
|
228
|
+
end
|
229
|
+
end
|
230
|
+
return w, h
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
data/lib/imagedata.rb
ADDED
data/lib/point.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sankey
|
2
|
+
class Point
|
3
|
+
attr_reader :x, :y
|
4
|
+
|
5
|
+
def initialize(x, y)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
end
|
9
|
+
|
10
|
+
def +(val)
|
11
|
+
if val.is_a? Array and 2 == val.count
|
12
|
+
x = val[0]
|
13
|
+
y = val[1]
|
14
|
+
else
|
15
|
+
throw "Invalid argument"
|
16
|
+
end
|
17
|
+
Point.new @x + x, @y + y
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/process.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Sankey
|
2
|
+
class Process
|
3
|
+
attr_reader :input, :output
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@input = []
|
7
|
+
@output = []
|
8
|
+
@input_reagents_queue = {}
|
9
|
+
@output_reagents_queue = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def fraction(reagent)
|
13
|
+
side = nil
|
14
|
+
[@output, @input].each { |io| side = io if io.include? reagent }
|
15
|
+
throw "Reagent isn't present in this process" if side.nil?
|
16
|
+
sum = 0
|
17
|
+
side.each { |r| sum += r.size }
|
18
|
+
reagent.size * 1.0 / sum
|
19
|
+
end
|
20
|
+
|
21
|
+
def total_reagents_mass
|
22
|
+
check_conservation_of_mass
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_output(reagent, order = nil)
|
26
|
+
@output.push reagent
|
27
|
+
@output_reagents_queue[order] = reagent unless order.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_input(reagent, order = nil)
|
31
|
+
@input.push reagent
|
32
|
+
@input_reagents_queue[order] = reagent unless order.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_from_input_queue(reagent)
|
36
|
+
key_to_be_removed = nil
|
37
|
+
@input_reagents_queue.each do |key, val|
|
38
|
+
key_to_be_removed = key if val == reagent
|
39
|
+
end
|
40
|
+
@input_reagents_queue.delete key_to_be_removed
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove_from_output_queue(reagent)
|
44
|
+
key_to_be_removed = nil
|
45
|
+
@output_reagents_queue.each do |key, val|
|
46
|
+
key_to_be_removed = key if val == reagent
|
47
|
+
end
|
48
|
+
@output_reagents_queue.delete key_to_be_removed
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_first_in_input_queue(reagent)
|
52
|
+
min_key = nil
|
53
|
+
return true unless @input_reagents_queue.values.include? reagent
|
54
|
+
@input_reagents_queue.each do |key, val|
|
55
|
+
#STDERR.puts "#{key} -> #{val.name}"
|
56
|
+
min_key ||= key
|
57
|
+
min_key = [min_key, key].min
|
58
|
+
end
|
59
|
+
@input_reagents_queue[min_key] == reagent
|
60
|
+
end
|
61
|
+
|
62
|
+
def is_first_in_output_queue(reagent)
|
63
|
+
min_key = nil
|
64
|
+
return true unless @output_reagents_queue.values.include? reagent
|
65
|
+
@output_reagents_queue.each do |key, val|
|
66
|
+
min_key ||= key
|
67
|
+
min_key = [min_key, key].min
|
68
|
+
end
|
69
|
+
@output_reagents_queue[min_key] == reagent
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def check_conservation_of_mass
|
75
|
+
input_sum = output_sum = 0
|
76
|
+
@input.each { |r| input_sum += r.size }
|
77
|
+
@output.each { |r| output_sum += r.size }
|
78
|
+
throw 'Conservation of mass constraint failed: ' +
|
79
|
+
"#{input_sum} != #{output_sum}" if input_sum != output_sum
|
80
|
+
input_sum
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'vertex'
|
2
|
+
|
3
|
+
module Sankey
|
4
|
+
class ProcessVertex < Vertex
|
5
|
+
def initialize(a, b, c, d)
|
6
|
+
super()
|
7
|
+
[a, b, c, d].each { |x| @points.push x }
|
8
|
+
end
|
9
|
+
|
10
|
+
def input_edge
|
11
|
+
{:bottom => @points[1], :top => @points[0]}
|
12
|
+
end
|
13
|
+
|
14
|
+
def output_edge
|
15
|
+
{:bottom => @points[2], :top => @points[3]}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/reagent.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Sankey
|
2
|
+
class Reagent
|
3
|
+
attr_reader :size, :source, :drain, :name
|
4
|
+
|
5
|
+
def initialize(size, name = '')
|
6
|
+
@size = size
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def source=(process)
|
11
|
+
process.add_output self
|
12
|
+
@source = process
|
13
|
+
end
|
14
|
+
|
15
|
+
def drain=(process)
|
16
|
+
process.add_input self
|
17
|
+
@drain = process
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_drain(process, order = nil)
|
21
|
+
process.add_input self, order
|
22
|
+
@drain = process
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_source(process, order = nil)
|
26
|
+
process.add_output self, order
|
27
|
+
@source= process
|
28
|
+
end
|
29
|
+
|
30
|
+
def joins_later
|
31
|
+
drain.input.each do |r|
|
32
|
+
next if r == self
|
33
|
+
if r.source != nil
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'renderers/renderer'
|
2
|
+
require 'rvg/rvg'
|
3
|
+
require 'imagedata'
|
4
|
+
|
5
|
+
module Sankey::Renderers
|
6
|
+
class RVG
|
7
|
+
include Renderer
|
8
|
+
include Sankey
|
9
|
+
|
10
|
+
def initialize(data, filename = nil, args = {})
|
11
|
+
throw "data isn't ImageData" unless data.is_a? ImageData
|
12
|
+
@grid = args[:grid] || false
|
13
|
+
@vertices = data.vertices
|
14
|
+
@filename = filename
|
15
|
+
@width = args[:width] || data.width
|
16
|
+
@height = args[:height] || data.height
|
17
|
+
@width_ratio = 1.0 * @width / data.width
|
18
|
+
@height_ratio = 1.0 * @height / data.height
|
19
|
+
end
|
20
|
+
|
21
|
+
def render
|
22
|
+
canvas = Magick::ImageList.new
|
23
|
+
canvas.new_image(@width, @height)
|
24
|
+
draw_grid canvas if @grid
|
25
|
+
@vertices.each { |v| draw_vertex canvas, v }
|
26
|
+
if @filename.nil?
|
27
|
+
return canvas.to_blob { self.format = 'PNG' }
|
28
|
+
else
|
29
|
+
canvas.write @filename
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def draw_grid(canvas)
|
36
|
+
grid = Magick::Draw.new
|
37
|
+
grid.stroke = 'lightgray'
|
38
|
+
grid.stroke_width = 1
|
39
|
+
0.upto @width/@grid/@width_ratio do |x|
|
40
|
+
grid.line @width_ratio*(x*@grid+@grid-1), 0,
|
41
|
+
(x*@grid+@grid-1)*@width_ratio, @height
|
42
|
+
end
|
43
|
+
0.upto @height/@grid/@height_ratio do |x|
|
44
|
+
grid.line 0, (x*@grid+@grid-1)*@height_ratio, @width,
|
45
|
+
(x*@grid+@grid-1)*@height_ratio
|
46
|
+
end
|
47
|
+
grid.draw canvas
|
48
|
+
end
|
49
|
+
|
50
|
+
def draw_vertex(canvas, v)
|
51
|
+
vertex = Magick::Draw.new
|
52
|
+
vertex.stroke = 'black'
|
53
|
+
vertex.stroke_width = 1
|
54
|
+
first_x = prev_x = v.points.first.x * @width_ratio
|
55
|
+
first_y = prev_y = v.points.first.y * @height_ratio
|
56
|
+
v.points.each do |p|
|
57
|
+
vertex.line prev_x, prev_y, p.x * @width_ratio, p.y * @height_ratio
|
58
|
+
prev_x = p.x * @width_ratio
|
59
|
+
prev_y = p.y * @height_ratio
|
60
|
+
end
|
61
|
+
vertex.line prev_x, prev_y, first_x, first_y
|
62
|
+
vertex.draw canvas
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/sankey.rb
ADDED
data/lib/vertex.rb
ADDED
data/sankey.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{sankey}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jan Stępień"]
|
12
|
+
s.date = %q{2010-02-18}
|
13
|
+
s.description = %q{A small tool which generates Sankey diagrams.}
|
14
|
+
s.email = %q{jstepien@users.sourceforge.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE",
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"examples/coffee.rb",
|
25
|
+
"lib/generator.rb",
|
26
|
+
"lib/imagedata.rb",
|
27
|
+
"lib/point.rb",
|
28
|
+
"lib/process.rb",
|
29
|
+
"lib/processvertex.rb",
|
30
|
+
"lib/reagent.rb",
|
31
|
+
"lib/renderers/renderer.rb",
|
32
|
+
"lib/renderers/rvg.rb",
|
33
|
+
"lib/sankey.rb",
|
34
|
+
"lib/vertex.rb",
|
35
|
+
"sankey.gemspec"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/jstepien/sankey}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.5}
|
41
|
+
s.summary = %q{Sankey diagrams generator}
|
42
|
+
s.test_files = [
|
43
|
+
"examples/coffee.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_runtime_dependency(%q<rmagick>, [">= 0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<rmagick>, [">= 0"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<rmagick>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sankey
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Jan St\xC4\x99pie\xC5\x84"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-18 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rmagick
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: A small tool which generates Sankey diagrams.
|
26
|
+
email: jstepien@users.sourceforge.net
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.rdoc
|
37
|
+
- Rakefile
|
38
|
+
- VERSION
|
39
|
+
- examples/coffee.rb
|
40
|
+
- lib/generator.rb
|
41
|
+
- lib/imagedata.rb
|
42
|
+
- lib/point.rb
|
43
|
+
- lib/process.rb
|
44
|
+
- lib/processvertex.rb
|
45
|
+
- lib/reagent.rb
|
46
|
+
- lib/renderers/renderer.rb
|
47
|
+
- lib/renderers/rvg.rb
|
48
|
+
- lib/sankey.rb
|
49
|
+
- lib/vertex.rb
|
50
|
+
- sankey.gemspec
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/jstepien/sankey
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --charset=UTF-8
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.5
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Sankey diagrams generator
|
79
|
+
test_files:
|
80
|
+
- examples/coffee.rb
|