rubyvis 0.1.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.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
|
+
|