rubyvis 0.1.3 → 0.1.4
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.tar.gz.sig +1 -3
- data/History.txt +9 -1
- data/Manifest.txt +5 -0
- data/README.txt +34 -6
- data/examples/antibiotics/antibiotics.rb +11 -11
- data/examples/barley/barley.rb +5 -5
- data/examples/grouped_charts.rb +67 -0
- data/examples/line_and_step.rb +61 -0
- data/examples/stacked_charts.rb +50 -0
- data/examples/third.rb +10 -6
- data/lib/rubyvis.rb +3 -1
- data/lib/rubyvis/layout.rb +25 -0
- data/lib/rubyvis/layout/stack.rb +228 -0
- data/lib/rubyvis/mark.rb +34 -33
- data/lib/rubyvis/mark/area.rb +3 -2
- data/lib/rubyvis/mark/bar.rb +4 -1
- data/lib/rubyvis/mark/dot.rb +18 -18
- data/lib/rubyvis/mark/label.rb +1 -1
- data/lib/rubyvis/mark/line.rb +2 -1
- data/lib/rubyvis/mark/panel.rb +5 -5
- data/lib/rubyvis/mark/wedge.rb +2 -1
- data/lib/rubyvis/nest.rb +5 -6
- data/lib/rubyvis/scale/log.rb +15 -9
- data/lib/rubyvis/scene/svg_area.rb +8 -8
- data/lib/rubyvis/scene/svg_line.rb +4 -4
- data/lib/rubyvis/sceneelement.rb +1 -1
- data/spec/scale_log_spec.rb +105 -0
- data/spec/spec.opts +2 -1
- metadata +9 -3
- metadata.gz.sig +0 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
class Layout
|
3
|
+
def self.Stack
|
4
|
+
Rubyvis::Layout::Stack
|
5
|
+
end
|
6
|
+
class Stack < Rubyvis::Layout
|
7
|
+
@properties=Panel.properties.dup
|
8
|
+
|
9
|
+
attr_accessor :_x, :_y, :_values, :prop
|
10
|
+
attr_accessor_dsl :orient,:offset, :order, :layers
|
11
|
+
def self.defaults
|
12
|
+
Stack.new.extend(Layout.defaults).orient("bottom-left").offset("zero").layers([[]])
|
13
|
+
end
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@none=lambda {nil}
|
17
|
+
@prop = {"t"=> @none, "l"=> @none, "r"=> @none, "b"=> @none, "w"=> @none, "h"=> @none}
|
18
|
+
@values=nil
|
19
|
+
@_x=lambda {return 0}
|
20
|
+
@_y=lambda {return 0}
|
21
|
+
@_values=pv.identity
|
22
|
+
end
|
23
|
+
def x(f)
|
24
|
+
@_x=pv.functor(f)
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
def y(f)
|
28
|
+
@_y=pv.functor(f)
|
29
|
+
return self
|
30
|
+
end
|
31
|
+
def values(f=nil)
|
32
|
+
if f.nil?
|
33
|
+
@values
|
34
|
+
else
|
35
|
+
@_values=pv.functor(f)
|
36
|
+
return self
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def proxy(name)
|
42
|
+
that=self
|
43
|
+
return lambda {
|
44
|
+
a=that.prop[name].js_call(self, self.parent.index, self.index);
|
45
|
+
puts "proxy(#{name}): #{a}" if $DEBUG
|
46
|
+
a
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_implied(s)
|
51
|
+
# puts "Build stack" if $DEBUG
|
52
|
+
panel_build_implied(s)
|
53
|
+
data = s.layers
|
54
|
+
n = data.size
|
55
|
+
m = nil
|
56
|
+
orient = s.orient
|
57
|
+
if orient =~/^(top|bottom)\b/
|
58
|
+
horizontal=true
|
59
|
+
else
|
60
|
+
horizontal=false
|
61
|
+
end
|
62
|
+
h = self.parent.send(horizontal ? "height" : "width")
|
63
|
+
x = []
|
64
|
+
y = []
|
65
|
+
dy = []
|
66
|
+
|
67
|
+
#
|
68
|
+
# Iterate over the data, evaluating the values, x and y functions. The
|
69
|
+
# context in which the x and y psuedo-properties are evaluated is a
|
70
|
+
# pseudo-mark that is a grandchild of this layout.
|
71
|
+
#
|
72
|
+
stack = Rubyvis::Mark.stack
|
73
|
+
|
74
|
+
o = OpenStruct.new({:parent=> OpenStruct.new({:parent=> self})})
|
75
|
+
stack.unshift(nil)
|
76
|
+
values = []
|
77
|
+
n.times {|i|
|
78
|
+
dy[i] = []
|
79
|
+
y[i] = []
|
80
|
+
o.parent.index = i
|
81
|
+
stack[0] = data[i]
|
82
|
+
values[i] = self._values.js_apply(o.parent, stack);
|
83
|
+
m = values[i].size if (i==0)
|
84
|
+
stack.unshift(nil)
|
85
|
+
m.times {|j|
|
86
|
+
stack[0] = values[i][j]
|
87
|
+
o.index = j
|
88
|
+
x[j] = self._x.js_apply(o, stack) if i==0
|
89
|
+
dy[i][j] = self._y.js_apply(o, stack)
|
90
|
+
}
|
91
|
+
stack.shift()
|
92
|
+
}
|
93
|
+
stack.shift()
|
94
|
+
|
95
|
+
# order
|
96
|
+
_index=nil
|
97
|
+
case (s.order)
|
98
|
+
when "inside-out"
|
99
|
+
max = dy.map {|v| pv.max.index(v) }
|
100
|
+
map = pv.range(n).sort {|a,b| return max[a] - max[b]}
|
101
|
+
sums = dy.map {|v| pv.sum(v)}
|
102
|
+
top = 0
|
103
|
+
bottom = 0
|
104
|
+
tops = []
|
105
|
+
bottoms = []
|
106
|
+
n.times {|i|
|
107
|
+
j = map[i]
|
108
|
+
if (top < bottom)
|
109
|
+
top += sums[j];
|
110
|
+
tops.push(j);
|
111
|
+
else
|
112
|
+
bottom += sums[j];
|
113
|
+
bottoms.push(j);
|
114
|
+
end
|
115
|
+
}
|
116
|
+
_index = bottoms.reverse+tops
|
117
|
+
|
118
|
+
when "reverse"
|
119
|
+
_index = pv.range(n - 1, -1, -1)
|
120
|
+
else
|
121
|
+
_index = pv.range(n)
|
122
|
+
end
|
123
|
+
|
124
|
+
#/* offset */
|
125
|
+
case (s.offset)
|
126
|
+
when "silohouette"
|
127
|
+
m.times {|j|
|
128
|
+
o = 0;
|
129
|
+
n.times {|i|
|
130
|
+
o += dy[i][j]
|
131
|
+
}
|
132
|
+
y[_index[0]][j] = (h - o) / 2.0;
|
133
|
+
}
|
134
|
+
|
135
|
+
when "wiggle"
|
136
|
+
o = 0;
|
137
|
+
n.times {|i| o += dy[i][0] }
|
138
|
+
|
139
|
+
y[_index[0]][0] = o = (h - o) / 2.0
|
140
|
+
|
141
|
+
(1...m).each {|j|
|
142
|
+
s1 = 0
|
143
|
+
s2 = 0
|
144
|
+
dx = x[j] - x[j - 1]
|
145
|
+
n.times {|i| s1 += dy[i][j]}
|
146
|
+
n.times {|i|
|
147
|
+
|
148
|
+
s3 = (dy[_index[i]][j] - dy[_index[i]][j - 1]) / (2.0 * dx)
|
149
|
+
i.times {|k|
|
150
|
+
s3 += (dy[_index[k]][j] - dy[_index[k]][j - 1]) / dx.to_f
|
151
|
+
}
|
152
|
+
s2 += s3 * dy[_index[i]][j]
|
153
|
+
}
|
154
|
+
o -= (s1!=0) ? s2 / s1.to_f * dx : 0
|
155
|
+
y[_index[0]][j] = o
|
156
|
+
|
157
|
+
}
|
158
|
+
when "expand"
|
159
|
+
m.times {|j|
|
160
|
+
y[_index[0]][j] = 0
|
161
|
+
|
162
|
+
k = 0
|
163
|
+
n.times {|i|k += dy[i][j]}
|
164
|
+
if (k!=0)
|
165
|
+
k = h / k.to_f
|
166
|
+
n.times {|i| dy[i][j] *= k}
|
167
|
+
else
|
168
|
+
k = h / n.to_f
|
169
|
+
n.times { dy[i][j] = k}
|
170
|
+
end
|
171
|
+
}
|
172
|
+
else
|
173
|
+
m.times {|j| y[_index[0]][j] = 0}
|
174
|
+
end
|
175
|
+
|
176
|
+
# Propagate the offset to the other series. */
|
177
|
+
m.times {|j|
|
178
|
+
o = y[_index[0]][j]
|
179
|
+
(1...n).each {|i|
|
180
|
+
|
181
|
+
o += dy[_index[i - 1]][j]
|
182
|
+
y[_index[i]][j] = o
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
# /* Find the property definitions for dynamic substitution. */
|
187
|
+
|
188
|
+
i = orient.index("-")
|
189
|
+
pdy = horizontal ? "h" : "w"
|
190
|
+
px = i < 0 ? (horizontal ? "l" : "b") : orient[i + 1,1]
|
191
|
+
py = orient[0,1]
|
192
|
+
|
193
|
+
@values=values
|
194
|
+
|
195
|
+
@prop.each {|k,v|
|
196
|
+
@prop[k]=@none
|
197
|
+
}
|
198
|
+
# puts "stack: x:#{px}, y:#{py}, dy:#{pdy}" if $DEBUG
|
199
|
+
@prop[px] =lambda {|i1,j| x[j]}
|
200
|
+
@prop[py] =lambda {|i1,j| y[i1][j]}
|
201
|
+
@prop[pdy]=lambda {|i1,j| dy[i1][j]}
|
202
|
+
end
|
203
|
+
|
204
|
+
def layer
|
205
|
+
that=self
|
206
|
+
value = Rubyvis::Mark.new().data(lambda { that.values[self.parent.index] })
|
207
|
+
.top(proxy("t"))
|
208
|
+
.left(proxy("l"))
|
209
|
+
.right(proxy("r"))
|
210
|
+
.bottom(proxy("b"))
|
211
|
+
.width(proxy("w"))
|
212
|
+
.height(proxy("h"))
|
213
|
+
|
214
|
+
class << value
|
215
|
+
def that=(v)
|
216
|
+
@that = v
|
217
|
+
end
|
218
|
+
def add(type)
|
219
|
+
that = @that
|
220
|
+
that.add( pv.Panel ).data(lambda { that.layers() }).add(type).extend( self )
|
221
|
+
end
|
222
|
+
end
|
223
|
+
value.that=self
|
224
|
+
return value
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
data/lib/rubyvis/mark.rb
CHANGED
@@ -6,10 +6,9 @@ module Rubyvis
|
|
6
6
|
class Mark
|
7
7
|
@properties={}
|
8
8
|
|
9
|
-
def self.property_method(name, _def, func=nil)
|
10
|
-
|
11
|
-
|
12
|
-
Mark.send(:define_method, name) do |*arguments|
|
9
|
+
def self.property_method(name, _def, func=nil, klass=nil)
|
10
|
+
return if klass.method_defined? name
|
11
|
+
klass.send(:define_method, name) do |*arguments|
|
13
12
|
v,dummy = arguments
|
14
13
|
if _def and self.scene
|
15
14
|
if arguments.size>0
|
@@ -33,16 +32,35 @@ module Rubyvis
|
|
33
32
|
if i.nil?
|
34
33
|
raise "No instance for #{self} on #{name}"
|
35
34
|
else
|
36
|
-
# puts "index:#{self.index}, name:#{name}, val:#{i.send(name)}"
|
35
|
+
# puts "index:#{self.index}, name:#{name}, val:#{i.send(name)}"
|
37
36
|
i.send(name)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
41
40
|
camel=name.to_s.gsub(/(_.)/) {|v| v[1,1].upcase}
|
42
41
|
if camel!=name
|
43
|
-
|
42
|
+
klass.send(:alias_method, camel, name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.attr_accessor_dsl(*attr)
|
47
|
+
attr.each do |sym|
|
48
|
+
if sym.is_a? Array
|
49
|
+
name,func=sym
|
50
|
+
else
|
51
|
+
name=sym
|
52
|
+
func=nil
|
53
|
+
end
|
54
|
+
|
55
|
+
@properties[name]=true
|
56
|
+
self.property_method(name,false, func, Rubyvis::Mark)
|
57
|
+
define_method(name.to_s+"=") {|v|
|
58
|
+
self.send(name,v)
|
59
|
+
}
|
44
60
|
end
|
45
61
|
end
|
62
|
+
|
63
|
+
|
46
64
|
def property_value(name,v)
|
47
65
|
prop=Property.new({:name=>name, :id=>Rubyvis.id, :value=>v})
|
48
66
|
@_properties.delete_if{|v1| v1.name==name}
|
@@ -63,23 +81,6 @@ module Rubyvis
|
|
63
81
|
end
|
64
82
|
|
65
83
|
|
66
|
-
def self.attr_accessor_dsl(*attr)
|
67
|
-
attr.each do |sym|
|
68
|
-
|
69
|
-
if sym.is_a? Array
|
70
|
-
name,func=sym
|
71
|
-
else
|
72
|
-
name=sym
|
73
|
-
func=nil
|
74
|
-
end
|
75
|
-
|
76
|
-
@properties[name]=true
|
77
|
-
self.property_method(name,false, func)
|
78
|
-
define_method(name.to_s+"=") {|v|
|
79
|
-
self.send(name,v)
|
80
|
-
}
|
81
|
-
end
|
82
|
-
end
|
83
84
|
|
84
85
|
attr_accessor :parent, :root, :index, :child_index, :scene, :proto, :target, :scale
|
85
86
|
attr_reader :_properties
|
@@ -119,11 +120,7 @@ module Rubyvis
|
|
119
120
|
end
|
120
121
|
|
121
122
|
def initialize(opts=Hash.new)
|
122
|
-
#@_properties_values={}
|
123
|
-
#@_properties_types={}
|
124
123
|
@_properties=[]
|
125
|
-
#@options=opts
|
126
|
-
# @stack=[]
|
127
124
|
opts.each {|k,v|
|
128
125
|
self.send("#{k}=",v) if self.respond_to? k
|
129
126
|
}
|
@@ -253,19 +250,22 @@ module Rubyvis
|
|
253
250
|
t=s.top
|
254
251
|
b=s.bottom
|
255
252
|
prop=self.properties
|
253
|
+
|
256
254
|
#p self
|
255
|
+
|
257
256
|
w = (prop[:width]) ? s.width : 0
|
258
257
|
h = (prop[:height]) ? s.height : 0
|
259
|
-
|
260
|
-
|
261
|
-
|
258
|
+
|
259
|
+
width=self.parent ? self.parent.width() : (w+(l.nil? ? 0 : l)+(r.nil? ? 0 : r))
|
260
|
+
#puts (self.parent)? "parent width: #{self.parent.width}" : "no parent" if $DEBUG
|
261
|
+
#p prop.sort if $DEBUG
|
262
|
+
puts "build implied #{type}: l:#{l},r:#{r},t:#{t},b:#{b}, w:#{prop[:width]} #{w},h: #{prop[:height]} #{h}, width:#{width}" if $DEBUG
|
262
263
|
if w.nil?
|
263
264
|
r||=0
|
264
265
|
l||=0
|
265
266
|
w=width-r-l
|
266
267
|
elsif r.nil?
|
267
268
|
if l.nil?
|
268
|
-
|
269
269
|
r=(width-w) / (2.0)
|
270
270
|
l=r
|
271
271
|
else
|
@@ -299,9 +299,10 @@ module Rubyvis
|
|
299
299
|
s.top=t
|
300
300
|
s.bottom=b
|
301
301
|
|
302
|
-
|
302
|
+
puts "Post->left: #{l},right:#{r},top:#{t},bottom:#{b}, width:#{w}, height:#{h}" if $DEBUG
|
303
303
|
|
304
304
|
s.width=w if prop[:width]
|
305
|
+
#puts "width:#{s.width}" if $DEBUG
|
305
306
|
s.height=h if prop[:height]
|
306
307
|
s.text_style=Rubyvis::Color.transparent if prop[:text_style] and !s.text_style
|
307
308
|
s.fill_style=Rubyvis::Color.transparent if prop[:fill_style] and !s.fill_style
|
@@ -543,7 +544,7 @@ module Rubyvis
|
|
543
544
|
def mark_build_instance(s1)
|
544
545
|
build_properties(s1, self.binds.required)
|
545
546
|
if s1.visible
|
546
|
-
build_properties(s1,self.binds.optional)
|
547
|
+
build_properties(s1, self.binds.optional)
|
547
548
|
build_implied(s1)
|
548
549
|
end
|
549
550
|
end
|
data/lib/rubyvis/mark/area.rb
CHANGED
@@ -112,7 +112,8 @@ module Rubyvis
|
|
112
112
|
area_build_instance(s)
|
113
113
|
end
|
114
114
|
def self.defaults
|
115
|
-
|
115
|
+
a= Rubyvis::Colors.category20
|
116
|
+
Area.new.extend(Mark.defaults).line_width(1.5).fill_style(lambda {a.scale(self.parent.index)}).interpolate('linear').tension(0.7)
|
116
117
|
end
|
117
118
|
def anchor(name)
|
118
119
|
area_anchor(name)
|
@@ -120,7 +121,7 @@ module Rubyvis
|
|
120
121
|
def build_implied(s)
|
121
122
|
s.heigth=0 if s.height.nil?
|
122
123
|
s.width=0 if s.width.nil?
|
123
|
-
|
124
|
+
mark_build_implied(s)
|
124
125
|
end
|
125
126
|
end
|
126
127
|
end
|
data/lib/rubyvis/mark/bar.rb
CHANGED
@@ -10,7 +10,10 @@ module Rubyvis
|
|
10
10
|
@properties=Mark.properties.dup
|
11
11
|
attr_accessor_dsl :width, :height, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
|
12
12
|
def self.defaults
|
13
|
-
|
13
|
+
a=Rubyvis.Colors.category20()
|
14
|
+
Bar.new.extend(Mark.defaults).line_width(1.5).fill_style( lambda {
|
15
|
+
a.scale(self.parent.index)
|
16
|
+
})
|
14
17
|
end
|
15
18
|
end
|
16
19
|
end
|
data/lib/rubyvis/mark/dot.rb
CHANGED
@@ -12,57 +12,57 @@ module Rubyvis
|
|
12
12
|
attr_accessor_dsl :shape, :shape_angle, :shape_radius, :shape_size, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
|
13
13
|
|
14
14
|
def self.defaults()
|
15
|
-
|
15
|
+
a=Rubyvis::Colors.category10
|
16
|
+
Dot.new().extend(Mark.defaults).shape("circle"). line_width(1.5). stroke_style(lambda {a.scale(self.parent.index)})
|
16
17
|
end
|
17
18
|
|
18
19
|
def anchor(name)
|
19
20
|
|
20
21
|
|
21
22
|
mark_anchor(name).left(lambda {
|
22
|
-
|
23
|
-
|
23
|
+
s=scene.target[self.index]
|
24
|
+
case self.name
|
24
25
|
when 'bottom' then s.left;
|
25
26
|
when 'top' then s.left;
|
26
27
|
when 'center' then s.left;
|
27
28
|
when 'left' then nil;
|
28
29
|
else
|
29
30
|
s.left+s.shape_radius
|
30
|
-
|
31
|
+
end
|
31
32
|
}).right(lambda {
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
s=scene.target[self.index]
|
34
|
+
self.name()=='left' ? s.right+s.shape_radius : nil
|
35
35
|
}).top(lambda {
|
36
36
|
s=scene.target[self.index]
|
37
|
-
|
37
|
+
case self.name
|
38
38
|
when 'left' then s.top;
|
39
39
|
when 'right' then s.top;
|
40
40
|
when 'center' then s.top;
|
41
41
|
when 'top' then nil;
|
42
42
|
else
|
43
43
|
s.top+s.shape_radius
|
44
|
-
|
44
|
+
end
|
45
45
|
}).bottom(lambda {
|
46
|
-
|
47
|
-
|
46
|
+
s=scene.target[self.index]
|
47
|
+
self.name()=='top' ? s.bottom+s.shape_radius : nil
|
48
48
|
}).text_align(lambda {
|
49
|
-
|
49
|
+
case self.name
|
50
50
|
when 'left' then 'right';
|
51
51
|
when 'bottom' then 'center';
|
52
52
|
when 'top' then 'center';
|
53
53
|
when 'center' then 'center';
|
54
54
|
else
|
55
55
|
'left'
|
56
|
-
|
56
|
+
end
|
57
57
|
}).text_baseline( lambda {
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
case self.name
|
59
|
+
when 'right' then 'middle';
|
60
|
+
when 'left' then 'middle';
|
61
|
+
when 'center' then 'middle';
|
62
62
|
when 'bottom' then 'top';
|
63
63
|
else
|
64
64
|
'bottom'
|
65
|
-
|
65
|
+
end
|
66
66
|
|
67
67
|
})
|
68
68
|
end
|