rubyvis 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +3 -0
- data/Manifest.txt +44 -0
- data/README.txt +64 -0
- data/Rakefile +16 -0
- data/examples/crimea-line.rb +64 -0
- data/examples/first.rb +17 -0
- data/examples/second.rb +14 -0
- data/lib/rubyvis.rb +43 -0
- data/lib/rubyvis/color/color.rb +241 -0
- data/lib/rubyvis/color/colors.rb +40 -0
- data/lib/rubyvis/color/ramp.rb +0 -0
- data/lib/rubyvis/format.rb +19 -0
- data/lib/rubyvis/format/date.rb +18 -0
- data/lib/rubyvis/format/number.rb +31 -0
- data/lib/rubyvis/internals.rb +215 -0
- data/lib/rubyvis/javascript_behaviour.rb +64 -0
- data/lib/rubyvis/label.rb +0 -0
- data/lib/rubyvis/mark.rb +528 -0
- data/lib/rubyvis/mark/anchor.rb +22 -0
- data/lib/rubyvis/mark/area.rb +34 -0
- data/lib/rubyvis/mark/bar.rb +23 -0
- data/lib/rubyvis/mark/label.rb +17 -0
- data/lib/rubyvis/mark/line.rb +14 -0
- data/lib/rubyvis/mark/panel.rb +87 -0
- data/lib/rubyvis/mark/rule.rb +28 -0
- data/lib/rubyvis/scale.rb +34 -0
- data/lib/rubyvis/scale/linear.rb +5 -0
- data/lib/rubyvis/scale/ordinal.rb +52 -0
- data/lib/rubyvis/scale/quantitative.rb +263 -0
- data/lib/rubyvis/scene/svg_bar.rb +31 -0
- data/lib/rubyvis/scene/svg_label.rb +51 -0
- data/lib/rubyvis/scene/svg_panel.rb +117 -0
- data/lib/rubyvis/scene/svg_rule.rb +29 -0
- data/lib/rubyvis/scene/svg_scene.rb +118 -0
- data/lib/rubyvis/sceneelement.rb +44 -0
- data/lib/rubyvis/transform.rb +25 -0
- data/spec/internal_spec.rb +146 -0
- data/spec/javascript_behaviour_spec.rb +64 -0
- data/spec/panel_spec.rb +8 -0
- data/spec/scale_linear_spec.rb +121 -0
- data/spec/scale_spec.rb +8 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +27 -0
- metadata +160 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
def self.Colors
|
3
|
+
Rubyvis::Colors
|
4
|
+
end
|
5
|
+
def self.colors(*args)
|
6
|
+
scale=Rubyvis::Scale.ordinal
|
7
|
+
scale.range(*args)
|
8
|
+
scale
|
9
|
+
end
|
10
|
+
module Colors
|
11
|
+
def pv
|
12
|
+
Rubyvis
|
13
|
+
end
|
14
|
+
def self.category10(*arguments)
|
15
|
+
scale = pv.colors(
|
16
|
+
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
|
17
|
+
"#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf")
|
18
|
+
scale.domain(*arguments);
|
19
|
+
scale
|
20
|
+
end
|
21
|
+
def self.category19(*arguments)
|
22
|
+
scale = pv.colors(
|
23
|
+
"#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b",
|
24
|
+
"#8ca252", "#637939", "#e7cb94", "#e7ba52", "#bd9e39",
|
25
|
+
"#8c6d31", "#e7969c", "#d6616b", "#ad494a", "#843c39",
|
26
|
+
"#de9ed6", "#ce6dbd", "#a55194", "#7b4173")
|
27
|
+
scale.domain(*arguments);
|
28
|
+
scale
|
29
|
+
end
|
30
|
+
def self.category20(*arguments)
|
31
|
+
scale = pv.colors(
|
32
|
+
"#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c",
|
33
|
+
"#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5",
|
34
|
+
"#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f",
|
35
|
+
"#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5")
|
36
|
+
scale.domain(*arguments);
|
37
|
+
scale
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
def self.Format
|
3
|
+
Rubyvis::Format
|
4
|
+
end
|
5
|
+
|
6
|
+
module Format
|
7
|
+
def self.re(s)
|
8
|
+
s.gsub(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/, "\\$&")
|
9
|
+
end
|
10
|
+
def self.number
|
11
|
+
Format::Number.new
|
12
|
+
end
|
13
|
+
def self.date(pattern)
|
14
|
+
Format::Date.new(pattern)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
require 'rubyvis/format/number'
|
19
|
+
require 'rubyvis/format/date'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'date'
|
2
|
+
module Rubyvis
|
3
|
+
module Format
|
4
|
+
class Date
|
5
|
+
attr_reader :pattern
|
6
|
+
def initialize(pattern)
|
7
|
+
@pattern=pattern
|
8
|
+
#@pad=Rubyvis::Format.pad
|
9
|
+
end
|
10
|
+
def format(d)
|
11
|
+
d.strftime(pattern)
|
12
|
+
end
|
13
|
+
def parse(s)
|
14
|
+
::Date.strptime(s, pattern)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
module Format
|
3
|
+
class Number
|
4
|
+
def initialize
|
5
|
+
@maxi = Infinity # default maximum integer digits
|
6
|
+
@mins = 0 # mini, including group separators
|
7
|
+
@minf = 0 # default minimum fraction digits
|
8
|
+
@maxf = 0 # default maximum fraction digits
|
9
|
+
@maxk = 1 # 10^maxf
|
10
|
+
@padi = "0" # default integer pad
|
11
|
+
@padf = "0" # default fraction pad
|
12
|
+
@padg = true # whether group separator affects integer padding
|
13
|
+
@decimal = "." # default decimal separator
|
14
|
+
@group = "," # default group separator
|
15
|
+
@np = "\u2212" # default negative prefix
|
16
|
+
@ns = "" # default negative suffix
|
17
|
+
end
|
18
|
+
def fraction_digits(*arguments)
|
19
|
+
if (arguments.size>0)
|
20
|
+
min=arguments[0]
|
21
|
+
max=arguments[1]
|
22
|
+
@minf = min.to_f
|
23
|
+
@maxf = (arguments.size > 1) ? max.to_f : @minf
|
24
|
+
@maxk = 10**@maxf
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
[minf, maxf]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
# :section: /pv-internals.js
|
3
|
+
@@id=0
|
4
|
+
def self.id
|
5
|
+
@@id+=1
|
6
|
+
end
|
7
|
+
def self.functor(f)
|
8
|
+
(f.is_a? Proc) ? f : lambda {f}
|
9
|
+
end
|
10
|
+
# :section: /data/Arrays.js
|
11
|
+
|
12
|
+
def self.map(array, f=nil)
|
13
|
+
if f
|
14
|
+
array.size.times.map {|i|
|
15
|
+
o=o_index(i)
|
16
|
+
f.js_call(o, array[i])
|
17
|
+
}
|
18
|
+
else
|
19
|
+
array.dup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def self.repeat(array, n=2)
|
23
|
+
array*n
|
24
|
+
end
|
25
|
+
def self.cross(a,b)
|
26
|
+
array = [];
|
27
|
+
a.each {|x|
|
28
|
+
b.each {|y|
|
29
|
+
array.push([x,y])
|
30
|
+
}
|
31
|
+
}
|
32
|
+
array
|
33
|
+
end
|
34
|
+
def self.blend(arrays)
|
35
|
+
# I love Ruby expresivness
|
36
|
+
arrays.inject([]) {|ac,v| ac+v}
|
37
|
+
end
|
38
|
+
def self.transpose(matrix)
|
39
|
+
out=[]
|
40
|
+
matrix.size.times do |i|
|
41
|
+
matrix[i].size.times do |j|
|
42
|
+
out[j]||=Array.new
|
43
|
+
out[j][i]=matrix[i][j]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
out
|
47
|
+
end
|
48
|
+
def self.normalize(array,f=nil)
|
49
|
+
norm=Rubyvis.map(array,f)
|
50
|
+
sum=pv.sum(norm)
|
51
|
+
norm.map {|x| x.quo(sum)}
|
52
|
+
end
|
53
|
+
def self.o_index(i)
|
54
|
+
o=OpenStruct.new :index=>i
|
55
|
+
end
|
56
|
+
def self.permute(array,indexes, f=nil)
|
57
|
+
f=Rubyvis.identity if f.nil?
|
58
|
+
indexes.map {|i| o=o_index(i); f.js_call(o, array[i])}
|
59
|
+
end
|
60
|
+
def self.numerate(keys, f=nil)
|
61
|
+
f=Rubyvis.identity if f.nil?
|
62
|
+
m = {}
|
63
|
+
keys.each_with_index {|x,i|
|
64
|
+
o=o_index(i)
|
65
|
+
m[f.js_call(o,x)]=i
|
66
|
+
}
|
67
|
+
m
|
68
|
+
end
|
69
|
+
def self.uniq(array, f=nil )
|
70
|
+
self.map(array,f).uniq
|
71
|
+
end
|
72
|
+
def self.natural_order(a,b)
|
73
|
+
a<=>b
|
74
|
+
end
|
75
|
+
def self.reverse_order(a,b)
|
76
|
+
-(a<=>b)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.search(array, value, f=nil)
|
80
|
+
f = Rubyvis.identity if (f.nil?)
|
81
|
+
low = 0
|
82
|
+
high = array.size - 1;
|
83
|
+
while (low <= high)
|
84
|
+
mid = (low + high) >> 1
|
85
|
+
midValue = f.call(array[mid]);
|
86
|
+
if (midValue < value)
|
87
|
+
low = mid + 1;
|
88
|
+
elsif (midValue > value)
|
89
|
+
high = mid - 1;
|
90
|
+
else
|
91
|
+
return mid;
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return -low - 1;
|
95
|
+
end
|
96
|
+
def self.search_index(array,value,f=nil)
|
97
|
+
i=Rubyvis.search(array,value,f)
|
98
|
+
(i < 0 ) ? (-i-1) : i;
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# :section: /data/Numbers.js
|
103
|
+
|
104
|
+
def self.range(*arguments)
|
105
|
+
start, stop, step=arguments
|
106
|
+
if (arguments.size == 1)
|
107
|
+
stop = start;
|
108
|
+
start = 0;
|
109
|
+
end
|
110
|
+
step||= 1
|
111
|
+
raise "range must be finite" if ((stop - start) / step.to_f).infinite?
|
112
|
+
array = []
|
113
|
+
i = 0
|
114
|
+
stop = stop- (stop - start) * 1e-10 #// floating point precision!
|
115
|
+
j = start + step * i
|
116
|
+
if (step < 0)
|
117
|
+
while (j > stop)
|
118
|
+
array.push(j)
|
119
|
+
i+=1
|
120
|
+
j = start + step * i
|
121
|
+
end
|
122
|
+
else
|
123
|
+
while (j < stop)
|
124
|
+
array.push(j)
|
125
|
+
i+=1
|
126
|
+
j = start + step * i
|
127
|
+
end
|
128
|
+
end
|
129
|
+
array
|
130
|
+
end
|
131
|
+
def self.random(*arguments)
|
132
|
+
start,stop,step=arguments
|
133
|
+
if (arguments.size == 1)
|
134
|
+
stop = start;
|
135
|
+
start = 0;
|
136
|
+
end
|
137
|
+
step||= 1;
|
138
|
+
return step ? ((rand() * (stop - start).quo(step)).floor * step + start) : (rand() * (stop - start) + start);
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.sum(array,f=nil)
|
142
|
+
if f.nil?
|
143
|
+
array.inject(0) {|ac,v| ac+v}
|
144
|
+
else
|
145
|
+
i=0
|
146
|
+
array.inject(0) {|ac,v|
|
147
|
+
o=o_index(i);i+=1;
|
148
|
+
ac+f.js_call(o,v);
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
def self.max(array, f=nil)
|
153
|
+
return array.size-1 if f==Rubyvis.index
|
154
|
+
f ? Rubyvis.map(array, f).max : array.max
|
155
|
+
end
|
156
|
+
def self.max_index(array,f=nil)
|
157
|
+
a2=Rubyvis.map(array,f)
|
158
|
+
max=a2.max
|
159
|
+
a2.index(max)
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.min(array, f=nil)
|
163
|
+
return array.size-1 if f==Rubyvis.index
|
164
|
+
f ? Rubyvis.map(array, f).min : array.min
|
165
|
+
end
|
166
|
+
def self.min_index(array,f=nil)
|
167
|
+
a2=Rubyvis.map(array,f)
|
168
|
+
min=a2.min
|
169
|
+
a2.index(min)
|
170
|
+
end
|
171
|
+
def self.mean(array, f=nil)
|
172
|
+
Rubyvis.sum(array,f).quo(array.size)
|
173
|
+
end
|
174
|
+
def self.median(array,f=nil)
|
175
|
+
return (array.length - 1).quo(2) if (f == pv.index)
|
176
|
+
array = Rubyvis.map(array, f).sort{|a,b| Rubyvis.natural_order(a,b)}
|
177
|
+
return array[array.size.quo(2).floor] if (array.length % 2>0)
|
178
|
+
i = array.size.quo(2);
|
179
|
+
return (array[i - 1] + array[i]).quo(2);
|
180
|
+
end
|
181
|
+
# Sum of square, really
|
182
|
+
def self.variance(array,f=nil)
|
183
|
+
return 0 if array.size==1 or array.uniq.size==1
|
184
|
+
ar=(f.nil?) ? array : Rubyvis.map(array,f)
|
185
|
+
mean=Rubyvis.mean(ar)
|
186
|
+
ar.inject(0) {|ac,v| ac+(v-mean)**2}
|
187
|
+
end
|
188
|
+
def self.deviation(array,f=nil)
|
189
|
+
Math::sqrt(self.variance(array,f) / (array.size.to_f-1))
|
190
|
+
end
|
191
|
+
def self.log(x,b)
|
192
|
+
Math::log(x).quo(Math::log(b))
|
193
|
+
end
|
194
|
+
def self.log_symmetric(x,b)
|
195
|
+
(x == 0) ? 0 : ((x < 0) ? -Rubyvis.log(-x, b) : Rubyvis.log(x, b));
|
196
|
+
end
|
197
|
+
def self.log_adjusted(x,b)
|
198
|
+
x if x.is_a? Float and !x.finite?
|
199
|
+
negative=x<0
|
200
|
+
x += (b - x) / b.to_f if (x < b)
|
201
|
+
negative ? -Rubyvis.log(x, b) : Rubyvis.log(x, b);
|
202
|
+
end
|
203
|
+
def self.log_floor(x,b)
|
204
|
+
(x>0) ? b**(Rubyvis.log(x,b).floor) : b**(-(-Rubyvis.log(-x,b)).floor)
|
205
|
+
end
|
206
|
+
def self.log_ceil(x,b)
|
207
|
+
(x > 0) ? b ** (Rubyvis.log(x, b)).ceil : -(b ** -(-Rubyvis.log(-x, b)).ceil);
|
208
|
+
end
|
209
|
+
def self.radians(degrees)
|
210
|
+
(Math::PI/180.0)*degrees
|
211
|
+
end
|
212
|
+
def self.degrees(radians)
|
213
|
+
((180.0) / Math::PI)*radians
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class TrueClass
|
2
|
+
def to_i
|
3
|
+
1
|
4
|
+
end
|
5
|
+
end
|
6
|
+
class FalseClass
|
7
|
+
def to_i
|
8
|
+
0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
unless Object.public_method_defined? :instance_exec
|
14
|
+
class Object
|
15
|
+
module InstanceExecHelper; end
|
16
|
+
include InstanceExecHelper
|
17
|
+
def instance_exec(*args, &block) # :nodoc:
|
18
|
+
begin
|
19
|
+
old_critical, Thread.critical = Thread.critical, true
|
20
|
+
n = 0
|
21
|
+
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
22
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
23
|
+
ensure
|
24
|
+
Thread.critical = old_critical
|
25
|
+
end
|
26
|
+
begin
|
27
|
+
ret = send(mname, *args)
|
28
|
+
ensure
|
29
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
30
|
+
end
|
31
|
+
ret
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
# Add javascript-like +apply+ and +call+ methods to Proc,
|
37
|
+
# called +js_apply+ and +js_call+, respectivly.
|
38
|
+
|
39
|
+
class Proc
|
40
|
+
# Apply on javascript is very flexible. Can accept more or less
|
41
|
+
# variables than explicitly defined parameters on lambda, so the method
|
42
|
+
# adds or remove elements according to lambda arity
|
43
|
+
#
|
44
|
+
def js_apply(obj,args)
|
45
|
+
arguments=args.dup
|
46
|
+
# Modify numbers of args to works with arity
|
47
|
+
min_args=self.arity>0 ? self.arity : (-self.arity)-1
|
48
|
+
if args.size > min_args and self.arity>0
|
49
|
+
arguments=arguments[0,self.arity]
|
50
|
+
elsif args.size < min_args
|
51
|
+
arguments+=[nil]*(min_args-args.size)
|
52
|
+
end
|
53
|
+
#puts "#{args}->#{arguments} (#{self.arity})"
|
54
|
+
if self.arity==0
|
55
|
+
obj.instance_eval(&self)
|
56
|
+
else
|
57
|
+
obj.instance_exec(*arguments,&self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# Same as js_apply, but using explicit arguments
|
61
|
+
def js_call(obj,*args)
|
62
|
+
js_apply(obj,args)
|
63
|
+
end
|
64
|
+
end
|
File without changes
|
data/lib/rubyvis/mark.rb
ADDED
@@ -0,0 +1,528 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
# Represents a data-driven graphical mark. The <tt>Mark</tt> class is
|
3
|
+
# the base class for all graphical marks in Protovis; it does not provide any
|
4
|
+
# specific rendering functionality, but together with {@link Panel} establishes
|
5
|
+
# the core framework.
|
6
|
+
class Mark
|
7
|
+
@properties={}
|
8
|
+
|
9
|
+
def self.property_method(name,_def)
|
10
|
+
define_method(name) do |*arguments|
|
11
|
+
v,dummy = arguments
|
12
|
+
if _def and self.scene
|
13
|
+
if arguments.size>0
|
14
|
+
defs[name]=OpenStruct.new({:id=>(v.nil?) ? 0 : Rubyvis.id, :value=> v})
|
15
|
+
return self
|
16
|
+
end
|
17
|
+
return defs[name]
|
18
|
+
end
|
19
|
+
if arguments.size>0
|
20
|
+
type=(!_def).to_i<<1 | (v.is_a? Proc).to_i
|
21
|
+
property_value(name,(type & 1 !=0) ? lambda {|*args| v.js_apply(self, args)} : v)._type=type
|
22
|
+
@_properties_types[name]=type
|
23
|
+
return self
|
24
|
+
end
|
25
|
+
i=instance()
|
26
|
+
if i.nil?
|
27
|
+
raise "No instancia para #{name}"
|
28
|
+
else
|
29
|
+
#puts "Instancia para #{name}"
|
30
|
+
|
31
|
+
i.send(name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
def property_value(name,v)
|
36
|
+
prop=OpenStruct.new({:name=>name, :id=>Rubyvis.id, :value=>v})
|
37
|
+
@_properties.delete_if{|v| v.name==name}
|
38
|
+
@_properties.push(prop)
|
39
|
+
@_properties_values[name]=v
|
40
|
+
return prop
|
41
|
+
end
|
42
|
+
def margin(n)
|
43
|
+
self.left(n).right(n).top(n).bottom(n)
|
44
|
+
end
|
45
|
+
def instance(default_index=nil)
|
46
|
+
scene=self.scene
|
47
|
+
scene||=self.parent.instance(-1).children[self.child_index]
|
48
|
+
if(default_index)
|
49
|
+
index=self.respond_to?(:index) ? self.index : default_index
|
50
|
+
else
|
51
|
+
index=scene.size-1
|
52
|
+
end
|
53
|
+
#puts "index:#{index} , scene.size:#{scene.size}, default_index:#{default_index}"
|
54
|
+
scene[index]
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def self.attr_accessor_dsl(*attr)
|
59
|
+
attr.each do |sym|
|
60
|
+
@properties[sym]=true
|
61
|
+
sym_w_sm=sym.to_s.gsub(":","")
|
62
|
+
self.property_method(sym,false)
|
63
|
+
define_method(sym.to_s+"=") {|v|
|
64
|
+
self.send(sym,v)
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_accessor :parent, :root, :index, :child_index, :scene, :proto, :target, :scale
|
70
|
+
attr_accessor_dsl :data,:visible, :left, :right, :top, :bottom, :title, :reverse, :antialias, :id
|
71
|
+
|
72
|
+
@scene=nil
|
73
|
+
@stack=[]
|
74
|
+
@index=nil
|
75
|
+
def self.properties
|
76
|
+
@properties
|
77
|
+
end
|
78
|
+
def self.index
|
79
|
+
@index
|
80
|
+
end
|
81
|
+
def self.index=(v)
|
82
|
+
@index=v
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
def self.scene
|
88
|
+
@scene
|
89
|
+
end
|
90
|
+
def self.scene=(v)
|
91
|
+
@scene=v
|
92
|
+
end
|
93
|
+
def properties
|
94
|
+
(self.class).properties
|
95
|
+
end
|
96
|
+
def Mark.stack
|
97
|
+
@stack
|
98
|
+
end
|
99
|
+
def Mark.stack=(v)
|
100
|
+
@stack=v
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize(opts=Hash.new)
|
104
|
+
@_properties_values={}
|
105
|
+
@_properties_types={}
|
106
|
+
@_properties=[]
|
107
|
+
@options=defaults.merge opts
|
108
|
+
# @stack=[]
|
109
|
+
@options.each {|k,v|
|
110
|
+
self.send("#{k}=",v) if self.respond_to? k
|
111
|
+
}
|
112
|
+
@defs={}
|
113
|
+
@child_index=-1
|
114
|
+
@index=-1
|
115
|
+
@scale=1
|
116
|
+
@scene=nil
|
117
|
+
|
118
|
+
end
|
119
|
+
def type
|
120
|
+
"mark"
|
121
|
+
end
|
122
|
+
def _properties
|
123
|
+
out={}
|
124
|
+
@_properties_values.each {|k,v|
|
125
|
+
out[k]=OpenStruct.new({:name=>k.to_s,:value=>v, :_type=>@_properties_types[k]})
|
126
|
+
}
|
127
|
+
out
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
def defaults
|
137
|
+
{:data=>lambda {|d| [d]}, :visible=>true, :antialias=>true}
|
138
|
+
end
|
139
|
+
def extend(proto)
|
140
|
+
@proto=proto
|
141
|
+
@target=target
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
def add(type)
|
146
|
+
parent.add(type).extend(self)
|
147
|
+
end
|
148
|
+
def anchor(name=nil)
|
149
|
+
name = "center" if (!name) # default anchor name
|
150
|
+
return Rubyvis::Anchor.new(self).name(name).data(lambda {
|
151
|
+
self.scene.target.map {|s| s.data} }).visible(lambda {
|
152
|
+
self.scene.target[self.index].visible
|
153
|
+
}).id(lambda {self.scene.target[self.index].id}).left(lambda {
|
154
|
+
s = self.scene.target[self.index]
|
155
|
+
w = s.width
|
156
|
+
w||=0
|
157
|
+
if ['bottom','top','center'].include?(self.name)
|
158
|
+
s.left + w / 2.0
|
159
|
+
elsif self.name=='left'
|
160
|
+
nil
|
161
|
+
else
|
162
|
+
s.left + w
|
163
|
+
end
|
164
|
+
}).top(lambda {
|
165
|
+
s = self.scene.target[self.index]
|
166
|
+
h = s.height
|
167
|
+
h||= 0
|
168
|
+
if ['left','right','center'].include? self.name
|
169
|
+
s.top+h/2.0
|
170
|
+
elsif self.name=='top'
|
171
|
+
nil
|
172
|
+
else
|
173
|
+
s.top + h
|
174
|
+
end
|
175
|
+
}).right(lambda {
|
176
|
+
s = self.scene.target[self.index]
|
177
|
+
self.name() == "left" ? s.right + (s.width ? s.width : 0) : nil;
|
178
|
+
}).bottom(lambda {
|
179
|
+
s = self.scene.target[self.index];
|
180
|
+
self.name() == "top" ? s.bottom + (s.height ? s.height : 0) : nil;
|
181
|
+
}).text_align(lambda {
|
182
|
+
if ['bottom','top','center'].include? self.name
|
183
|
+
'center'
|
184
|
+
elsif self.name=='right'
|
185
|
+
'right'
|
186
|
+
else
|
187
|
+
'left'
|
188
|
+
end
|
189
|
+
}).text_baseline(lambda {
|
190
|
+
if ['right','left','center'].include? self.name
|
191
|
+
'middle'
|
192
|
+
elsif self.name=='top'
|
193
|
+
'top'
|
194
|
+
else
|
195
|
+
'bottom'
|
196
|
+
end
|
197
|
+
})
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
def build_implied(s)
|
208
|
+
l=s.left
|
209
|
+
r=s.right
|
210
|
+
t=s.top
|
211
|
+
b=s.bottom
|
212
|
+
prop=self.properties
|
213
|
+
w = (prop[:width] and !prop[:width].nil?) ? s.width : 0
|
214
|
+
h = (prop[:height] and !prop[:height].nil?) ? s.height : 0
|
215
|
+
#puts "#{l},#{r},#{t},#{b}"
|
216
|
+
#puts "#{w},#{h}"
|
217
|
+
|
218
|
+
width=self.parent ? self.parent.width(): (w+(l.nil? ? 0 : l)+(r.nil? ? 0 :r))
|
219
|
+
if w.nil?
|
220
|
+
r||=0
|
221
|
+
l||=0
|
222
|
+
w=width-r-l
|
223
|
+
elsif r.nil?
|
224
|
+
if l.nil?
|
225
|
+
|
226
|
+
r=(width-w) / (2.0)
|
227
|
+
l=r
|
228
|
+
else
|
229
|
+
r=width-w-l
|
230
|
+
end
|
231
|
+
elsif l.nil?
|
232
|
+
l=width-w-r
|
233
|
+
end
|
234
|
+
|
235
|
+
height=self.parent ? self.parent.height(): (h+(t.nil? ? 0 : t )+(b.nil? ? 0 : b))
|
236
|
+
|
237
|
+
if h.nil?
|
238
|
+
t||=0
|
239
|
+
b||=0
|
240
|
+
h=height-t-b
|
241
|
+
elsif b.nil?
|
242
|
+
if t.nil?
|
243
|
+
t=(height-h) / 2.0
|
244
|
+
b=t
|
245
|
+
else
|
246
|
+
b=height-h-t
|
247
|
+
end
|
248
|
+
elsif t.nil?
|
249
|
+
t=height-h-b
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
s.left=l
|
255
|
+
s.right=r
|
256
|
+
s.top=t
|
257
|
+
s.bottom=b
|
258
|
+
|
259
|
+
# puts "#{l},#{r},#{t},#{b}"
|
260
|
+
|
261
|
+
s.width=w if prop[:width]
|
262
|
+
s.height=h if prop[:height]
|
263
|
+
s.text_style=Rubyvis::Color.transparent if prop[:text_style] and !s.text_style
|
264
|
+
s.fill_style=Rubyvis::Color.transparent if prop[:fill_style] and !s.fill_style
|
265
|
+
s.stroke_style=Rubyvis::Color.transparent if prop[:stroke_style] and !s.stroke_style
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
|
270
|
+
|
271
|
+
def pr_svg(name)
|
272
|
+
res=self.send(name)
|
273
|
+
if res.nil?
|
274
|
+
"none"
|
275
|
+
else
|
276
|
+
res.to_s
|
277
|
+
end
|
278
|
+
end
|
279
|
+
def render_render(mark,depth,scale)
|
280
|
+
mark.scale=scale
|
281
|
+
if (depth < @indexes.size)
|
282
|
+
@stack.unshift(nil)
|
283
|
+
if (mark.respond_to? :index)
|
284
|
+
render_instance(mark, depth, scale);
|
285
|
+
else
|
286
|
+
mark.scene.size.times {|i|
|
287
|
+
mark.index = i;
|
288
|
+
render_instance(mark, depth, scale);
|
289
|
+
}
|
290
|
+
mark.index=nil
|
291
|
+
end
|
292
|
+
stack.shift();
|
293
|
+
else
|
294
|
+
mark.build();
|
295
|
+
pv.Scene.scale = scale;
|
296
|
+
pv.Scene.update_all(mark.scene);
|
297
|
+
end
|
298
|
+
mark.scale=nil
|
299
|
+
end
|
300
|
+
def render_instance(mark,depth,scale)
|
301
|
+
s=mark.scene[mark.index]
|
302
|
+
if s.visible
|
303
|
+
child_index=@indexes[depth]
|
304
|
+
child=mark.children[child_index]
|
305
|
+
child_index.times {|i|
|
306
|
+
mark.children[i].scene=s.children[i]
|
307
|
+
}
|
308
|
+
Mark.stack[0]=s.data
|
309
|
+
|
310
|
+
if (child.scene)
|
311
|
+
render_render(child,depth+1,scale*s.transform.k)
|
312
|
+
else
|
313
|
+
child.scene=s.children[child_index]
|
314
|
+
render_render(child, depth+1,scale*s.transform.k)
|
315
|
+
child.scene=nil
|
316
|
+
end
|
317
|
+
child_index.times {|i|
|
318
|
+
mark.children[i].scene=nil
|
319
|
+
}
|
320
|
+
|
321
|
+
end
|
322
|
+
end
|
323
|
+
private :render_render, :render_instance
|
324
|
+
def bind_bind(mark)
|
325
|
+
begin
|
326
|
+
@_properties.each {|v|
|
327
|
+
k=v.name.to_s
|
328
|
+
if !@seen.has_key?(k)
|
329
|
+
@seen[k]=v
|
330
|
+
case k
|
331
|
+
when "data"
|
332
|
+
@_data=v
|
333
|
+
when "visible"
|
334
|
+
@_required.push(v)
|
335
|
+
when "id"
|
336
|
+
@_required.push(v)
|
337
|
+
else
|
338
|
+
@types[v._type].push(v)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
}
|
342
|
+
end while(mark = mark.proto)
|
343
|
+
end
|
344
|
+
attr_accessor :binds
|
345
|
+
def bind()
|
346
|
+
@seen={}
|
347
|
+
@types={1=>[],2=>[],3=>[]}
|
348
|
+
@_data=nil
|
349
|
+
@_required=[]
|
350
|
+
bind_bind(self)
|
351
|
+
@types[1].reverse!
|
352
|
+
@types[3].reverse!
|
353
|
+
mark=self
|
354
|
+
begin
|
355
|
+
properties.each {|name,v|
|
356
|
+
if !@seen[name.to_s]
|
357
|
+
@seen[name.to_s]=OpenStruct.new(:name=>name.to_s, :_type=>2, :value=>nil)
|
358
|
+
@types[2].push(@seen[name.to_s])
|
359
|
+
end
|
360
|
+
}
|
361
|
+
end while(mark=mark.proto)
|
362
|
+
@binds=OpenStruct.new({:properties=>@seen, :data=>@_data, :required=>@_required, :optional=>@types[1]+@types[2]+@types[3]
|
363
|
+
})
|
364
|
+
end
|
365
|
+
def render
|
366
|
+
parent=self.parent
|
367
|
+
@stack=Mark.stack
|
368
|
+
if parent and !self.root.scene
|
369
|
+
root.render()
|
370
|
+
return
|
371
|
+
end
|
372
|
+
@indexes=[]
|
373
|
+
mark=self
|
374
|
+
until mark.parent.nil?
|
375
|
+
indexes.unshift(mark.child_index)
|
376
|
+
end
|
377
|
+
self.bind()
|
378
|
+
while(parent and !parent.respond_to? :index) do
|
379
|
+
parent=parent.parent
|
380
|
+
end
|
381
|
+
self.context( parent ? parent.scene : nil, parent ? parent.index : -1, lambda {render_render(self.root, 0,1)})
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
def context_apply(scene,index)
|
386
|
+
Mark.scene=scene
|
387
|
+
Mark.index=index
|
388
|
+
return if(!scene)
|
389
|
+
that=scene.mark
|
390
|
+
mark=that
|
391
|
+
ancestors=[]
|
392
|
+
begin
|
393
|
+
ancestors.push(mark)
|
394
|
+
Mark.stack.push(scene[index].data)
|
395
|
+
mark.index=index
|
396
|
+
mark.scene=scene
|
397
|
+
index=scene.parent_index
|
398
|
+
scene=scene.parent
|
399
|
+
end while(mark=mark.parent)
|
400
|
+
k=1
|
401
|
+
ancestors.size.times {|ic|
|
402
|
+
i=ancestors.size-ic-1
|
403
|
+
mark=ancestors[i]
|
404
|
+
mark.scale=k
|
405
|
+
k=k*mark.scene[mark.index].transform.k
|
406
|
+
}
|
407
|
+
if (that.children)
|
408
|
+
n=than.children.size
|
409
|
+
n.times {|i|
|
410
|
+
mark=that.children[i]
|
411
|
+
mark.scene=that.scene[that.index].children[i]
|
412
|
+
mark.scale=k
|
413
|
+
}
|
414
|
+
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
def context_clear(scene,index)
|
419
|
+
return if !scene
|
420
|
+
that=scene.mark
|
421
|
+
mark=nil
|
422
|
+
if(that.children)
|
423
|
+
that.children.size.times {|i|
|
424
|
+
mark=that.children[i]
|
425
|
+
mark.scene=nil
|
426
|
+
mark.scale=nil
|
427
|
+
}
|
428
|
+
|
429
|
+
end
|
430
|
+
mark=that
|
431
|
+
begin
|
432
|
+
Mark.stack.pop
|
433
|
+
if(mark.parent)
|
434
|
+
mark.scene=nil
|
435
|
+
mark.scale=nil
|
436
|
+
end
|
437
|
+
mark.index=nil
|
438
|
+
end while(mark=mark.parent)
|
439
|
+
end
|
440
|
+
def context(scene,index,f)
|
441
|
+
proto=Mark
|
442
|
+
stack=Mark.stack
|
443
|
+
oscene=Mark.scene
|
444
|
+
oindex=Mark.index
|
445
|
+
context_clear(oscene,oindex)
|
446
|
+
context_apply(scene,index)
|
447
|
+
begin
|
448
|
+
f.js_apply(self, stack)
|
449
|
+
ensure
|
450
|
+
context_clear(scene,index)
|
451
|
+
context_apply(oscene,oindex)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def build
|
456
|
+
scene=self.scene
|
457
|
+
stack=Mark.stack
|
458
|
+
#p self.type
|
459
|
+
if(!scene)
|
460
|
+
self.scene=SceneElement.new
|
461
|
+
scene=self.scene
|
462
|
+
scene.mark=self
|
463
|
+
scene.type=self.type
|
464
|
+
scene.child_index=self.child_index
|
465
|
+
if(self.parent)
|
466
|
+
scene.parent=self.parent.scene
|
467
|
+
scene.parent_index=self.parent.index
|
468
|
+
end
|
469
|
+
end
|
470
|
+
if(self.target)
|
471
|
+
scene.target=self.target.instances(scene)
|
472
|
+
end
|
473
|
+
|
474
|
+
data=self.binds.data
|
475
|
+
#puts "stack:#{stack}"
|
476
|
+
#puts "data_value:#{data.value}"
|
477
|
+
|
478
|
+
data=(data._type & 1)>0 ? data.value.js_apply(self, stack) : data.value
|
479
|
+
#puts "data:#{data}"
|
480
|
+
|
481
|
+
stack.unshift(nil)
|
482
|
+
scene.size=data.size
|
483
|
+
data.each_with_index {|d,i|
|
484
|
+
Mark.index=self.index=i
|
485
|
+
s=scene[i]
|
486
|
+
if !s
|
487
|
+
scene[i]=s=SceneElement.new
|
488
|
+
end
|
489
|
+
stack[0]=data[i]
|
490
|
+
s.data=data[i]
|
491
|
+
build_instance(s)
|
492
|
+
}
|
493
|
+
Mark.index=-1
|
494
|
+
self.index=nil
|
495
|
+
stack.shift()
|
496
|
+
return self
|
497
|
+
end
|
498
|
+
def build_instance(s1)
|
499
|
+
build_properties(s1, self.binds.required)
|
500
|
+
if s1.visible
|
501
|
+
build_properties(s1,self.binds.optional)
|
502
|
+
build_implied(s1)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
def build_properties(ss, props)
|
506
|
+
#p props
|
507
|
+
props.each do |prop|
|
508
|
+
v=prop.value
|
509
|
+
|
510
|
+
# p "#{prop.name}=#{v}"
|
511
|
+
|
512
|
+
if prop._type==3
|
513
|
+
v=v.js_apply(self, Mark.stack)
|
514
|
+
end
|
515
|
+
ss.send((prop.name.to_s+"=").to_sym, v)
|
516
|
+
end
|
517
|
+
#p ss
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
require 'rubyvis/mark/bar'
|
523
|
+
require 'rubyvis/mark/panel'
|
524
|
+
require 'rubyvis/mark/rule'
|
525
|
+
require 'rubyvis/mark/area'
|
526
|
+
require 'rubyvis/mark/anchor'
|
527
|
+
require 'rubyvis/mark/label'
|
528
|
+
|