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.
Files changed (46) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +3 -0
  3. data/Manifest.txt +44 -0
  4. data/README.txt +64 -0
  5. data/Rakefile +16 -0
  6. data/examples/crimea-line.rb +64 -0
  7. data/examples/first.rb +17 -0
  8. data/examples/second.rb +14 -0
  9. data/lib/rubyvis.rb +43 -0
  10. data/lib/rubyvis/color/color.rb +241 -0
  11. data/lib/rubyvis/color/colors.rb +40 -0
  12. data/lib/rubyvis/color/ramp.rb +0 -0
  13. data/lib/rubyvis/format.rb +19 -0
  14. data/lib/rubyvis/format/date.rb +18 -0
  15. data/lib/rubyvis/format/number.rb +31 -0
  16. data/lib/rubyvis/internals.rb +215 -0
  17. data/lib/rubyvis/javascript_behaviour.rb +64 -0
  18. data/lib/rubyvis/label.rb +0 -0
  19. data/lib/rubyvis/mark.rb +528 -0
  20. data/lib/rubyvis/mark/anchor.rb +22 -0
  21. data/lib/rubyvis/mark/area.rb +34 -0
  22. data/lib/rubyvis/mark/bar.rb +23 -0
  23. data/lib/rubyvis/mark/label.rb +17 -0
  24. data/lib/rubyvis/mark/line.rb +14 -0
  25. data/lib/rubyvis/mark/panel.rb +87 -0
  26. data/lib/rubyvis/mark/rule.rb +28 -0
  27. data/lib/rubyvis/scale.rb +34 -0
  28. data/lib/rubyvis/scale/linear.rb +5 -0
  29. data/lib/rubyvis/scale/ordinal.rb +52 -0
  30. data/lib/rubyvis/scale/quantitative.rb +263 -0
  31. data/lib/rubyvis/scene/svg_bar.rb +31 -0
  32. data/lib/rubyvis/scene/svg_label.rb +51 -0
  33. data/lib/rubyvis/scene/svg_panel.rb +117 -0
  34. data/lib/rubyvis/scene/svg_rule.rb +29 -0
  35. data/lib/rubyvis/scene/svg_scene.rb +118 -0
  36. data/lib/rubyvis/sceneelement.rb +44 -0
  37. data/lib/rubyvis/transform.rb +25 -0
  38. data/spec/internal_spec.rb +146 -0
  39. data/spec/javascript_behaviour_spec.rb +64 -0
  40. data/spec/panel_spec.rb +8 -0
  41. data/spec/scale_linear_spec.rb +121 -0
  42. data/spec/scale_spec.rb +8 -0
  43. data/spec/spec.opts +3 -0
  44. data/spec/spec_helper.rb +27 -0
  45. metadata +160 -0
  46. metadata.gz.sig +2 -0
@@ -0,0 +1,22 @@
1
+ module Rubyvis
2
+ def self.Anchor(target)
3
+ Rubyvis::Anchor.new(target)
4
+ end
5
+
6
+ class Anchor < Mark
7
+ @properties=Mark.properties
8
+ attr_accessor_dsl :name, :text_baseline, :text_align
9
+ def type
10
+ 'area'
11
+ end
12
+ def initialize(target)
13
+ super()
14
+ self.target=target
15
+ self.parent=target.parent
16
+ end
17
+ def _extend(proto)
18
+ self.proto=proto
19
+ return self
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ module Rubyvis
2
+ def self.Area
3
+ Rubyvis::Area
4
+ end
5
+
6
+ class Area < Mark
7
+ @properties=Mark.properties
8
+ attr_accessor_dsl :width, :height, :line_width, :stroke_style, :fill_style, :segmented, :interpolate, :tension
9
+ def type
10
+ 'area'
11
+ end
12
+ def defaults
13
+ sd=super
14
+ return sd.merge({:line_width=>1.5, :fill_style=>pv.Colors.category20.by(pv.parent), :interpolate=>'linear', :tension=>0.7})
15
+ end
16
+ def build_implied(s)
17
+ s.heigth=0 if s.height.nil?
18
+ s.width=0 if s.width.nil?
19
+ super(s)
20
+ end
21
+ def self.anchor(name)
22
+ Mark.anchor(name).interpolate(lambda {
23
+ self.scene.target[self.index].interpolate
24
+ }).eccentricity(lambda {
25
+ self.scene.target[self.index].eccentricity
26
+ }).tension(lambda {
27
+ self.scene.target[self.index].tension
28
+ })
29
+ end
30
+ def anchor
31
+ (self.class).anchor(name)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ module Rubyvis
2
+ def self.Bar
3
+ Rubyvis::Bar
4
+ end
5
+ class Bar < Mark
6
+ def type
7
+ "bar"
8
+ end
9
+
10
+ @properties=Mark.properties
11
+ attr_accessor_dsl :width, :height, :line_width, :stroke_style, :fill_style
12
+ def defaults
13
+ sd=super
14
+ return sd.merge({:line_width=>1.5, :fill_style=>Rubyvis.Colors.category20().by(Rubyvis.parent)})
15
+ end
16
+ def svg_render_pre
17
+ "<rect fill='#{pr_svg(:fill_style)}' height='#{pr_svg(:height)}' width='#{pr_svg(:width)}' x='#{pr_svg(:left)}' y='#{pr_svg(:top)}'>"
18
+ end
19
+ def svg_render_post
20
+ "</rect>"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module Rubyvis
2
+ def self.Label
3
+ Rubyvis::Label
4
+ end
5
+
6
+ class Label < Mark
7
+ @properties=Mark.properties
8
+ attr_accessor_dsl :text, :font, :text_angle, :text_style, :text_align, :text_baseline, :text_margin, :text_decoration, :text_shadow
9
+ def type
10
+ 'label'
11
+ end
12
+ def defaults
13
+ sd=super
14
+ return sd.merge({:events=>'none', :text=>lambda{|x| puts "->#{x}"; x}, :font=>"10px sans-serif", :text_angle=>0, :text_style=>pv.color('black'), :text_align=>'left', :text_baseline=>'bottom', :text_margin=>3})
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module Rubyvis
2
+ def self.Line
3
+ Rubyvis::Line
4
+ end
5
+
6
+ class Line < Mark
7
+ @properties=Mark.properties
8
+ attr_accessor_dsl :line_width, :line_join, :stroke_style, :fill_style, :segmented, :interporale, :eccentricity, :tension
9
+ def defaults
10
+ sd=super
11
+ return sd.merge({:line_join=>'miter', :line_width=>1.5, :stroke_style=>RubyVis::Color.category10().by(pv.parent), :interpolate=>'linear', :eccentricity=>0, :tension=>7})
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,87 @@
1
+ module Rubyvis
2
+ def self.Panel
3
+ Rubyvis::Panel
4
+ end
5
+ class Panel < Bar
6
+ def type
7
+ "panel"
8
+ end
9
+
10
+ @properties=Bar.properties
11
+ attr_accessor_dsl :transform, :overflow, :canvas
12
+ attr_accessor :children, :root
13
+ def initialize
14
+ super
15
+ @children=[]
16
+ @root=self
17
+ end
18
+ def defaults
19
+ sd=super
20
+ return sd.merge({:fill_style=>nil, :overflow=>'visible', :canvas=>Rubyvis.document.add_element("canvas")})
21
+ end
22
+ def add(type)
23
+ child=type.new
24
+ child.parent=self
25
+ child.root=root
26
+ child.child_index=children.size
27
+ children.push(child)
28
+ child
29
+ end
30
+
31
+ attr_reader :_canvas
32
+ def bind
33
+ super
34
+ children.each {|c|
35
+ c.bind()
36
+ }
37
+ end
38
+ def build_instance(s)
39
+ super(s)
40
+ return if !s.visible
41
+ s.children=[] if !s.children
42
+ scale=self.scale*s.transform.k
43
+ n=self.children.size
44
+ Mark.index=-1
45
+ n.times {|i|
46
+ child=children[i]
47
+ child.scene=s.children[i]
48
+ child.scale=scale
49
+ child.build()
50
+ }
51
+ n.times {|i|
52
+ child=children[i]
53
+ s.children[i]=child.scene
54
+ child.scene=nil
55
+ child.scale=nil
56
+ }
57
+ s.children=s.children[0,n]
58
+ end
59
+ def build_implied(s)
60
+ if(!self.parent)
61
+ c=s.canvas
62
+ if(c)
63
+ if s.width.nil?
64
+ w=Rubyvis.css(c,'width')
65
+ s.width=w-s.left-s.right
66
+ end
67
+ if s.height.nil?
68
+ w=Rubyvis.css(c,'height')
69
+ s.height=h-s.top-s.bottom
70
+ end
71
+ else
72
+ @_canvas||={}
73
+ cache=@_canvas
74
+ if(!(c=cache[self.index]))
75
+ c=cache[this.index]=Rubyvis.add_element('span')
76
+ end
77
+ end
78
+ s.canvas=c
79
+ end
80
+
81
+ s.transform=Rubyvis.Transform.identity if (s.transform.nil?)
82
+ super(s)
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,28 @@
1
+ module Rubyvis
2
+ def self.Rule
3
+ Rubyvis::Rule
4
+ end
5
+ class Rule < Mark
6
+ @properties=Mark.properties
7
+ attr_accessor_dsl :width, :height, :line_width, :stroke_style
8
+ def defaults
9
+ sd=super
10
+ return sd.merge({:line_width=>1,:stroke_style=>pv.color('black'),:antialias=>false})
11
+ end
12
+ def type
13
+ 'rule'
14
+ end
15
+ def build_implied(s)
16
+ l=s.left
17
+ r=s.right
18
+ t=s.top
19
+ b=s.bottom
20
+ if((!s.width.nil?) or ((l.nil?) and (r.nil?)) or ((!r.nil?) or (!l.nil?)))
21
+ s.height=0
22
+ else
23
+ s.width=0
24
+ end
25
+ super(s)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ module Rubyvis
2
+ def self.Scale
3
+ Rubyvis::Scale
4
+ end
5
+ module Scale
6
+ def self.quantitative(*args)
7
+ Quantitative.new(*args)
8
+ end
9
+ def self.linear(*args)
10
+ Linear.new(*args)
11
+ end
12
+ def self.ordinal(*args)
13
+ Ordinal.new(*args)
14
+ end
15
+ def self.interpolator(start,_end)
16
+ if start.is_a? Numeric
17
+ return lambda {|t| t*(_end-start)+start}
18
+ end
19
+ start=Rubyvis.color(start).rgb()
20
+ _end = Rubyvis.color(_end).rgb()
21
+ return lambda {|t|
22
+ a=start.a*(1-t)+_end.a*t
23
+ a=0 if a<1e-5
24
+ return (start.a == 0) ? Rubyvis.rgb(_end.r, _end.g, _end.b, a) : ((_end.a == 0) ? pv.rgb(start.r, start.g, start.b, a) : Rubyvis.rgb(
25
+ (start.r * (1 - t) + _end.r * t).round,
26
+ (start.g * (1 - t) + _end.g * t).round,
27
+ (start.b * (1 - t) + _end.b * t).round, a))
28
+ }
29
+ end
30
+ end
31
+ end
32
+ require 'rubyvis/scale/quantitative.rb'
33
+ require 'rubyvis/scale/linear.rb'
34
+ require 'rubyvis/scale/ordinal.rb'
@@ -0,0 +1,5 @@
1
+ module Rubyvis
2
+
3
+ class Scale::Linear < Rubyvis::Scale::Quantitative
4
+ end
5
+ end
@@ -0,0 +1,52 @@
1
+ module Rubyvis
2
+ class Scale::Ordinal
3
+ include Rubyvis::Scale
4
+
5
+ def initialize(*args)
6
+ @d=[] # domain
7
+ @i={}
8
+ @r=[]
9
+ @band=0
10
+ domain(*args)
11
+ end
12
+ def scale(x)
13
+ if @i[x].nil?
14
+ @d.push(x)
15
+ @i[x]=@d.size-1
16
+ end
17
+ @r[@i[x] % @r.size]
18
+ end
19
+ def domain(*arguments)
20
+ array,f=arguments[0],arguments[1]
21
+ if(arguments.size>0)
22
+ array= (array.is_a? Array) ? ((arguments.size>1) ? array.map(&f) : array) : arguments.dup
23
+ @d=array.uniq
24
+ i=pv.numerate(d)
25
+ return self
26
+ end
27
+ @d
28
+ end
29
+ def range(*arguments)
30
+ array, f = arguments[0],arguments[1]
31
+ if(arguments.size>0)
32
+ @r=(array.is_a? Array) ? ((arguments.size>1) ? array.map(&f) : array) : arguments.dup
33
+ if @r[0].is_a? String
34
+ @r=@r.map {|i| pv.color(i)}
35
+ end
36
+ self
37
+ end
38
+ @r
39
+ end
40
+ def split(min,max)
41
+ step=(max-min).quo(domain().size)
42
+ @r=pv.range(min+step.quo(2),max,step)
43
+ self
44
+ end
45
+ def by(*arguments)
46
+ f,dummy=arguments
47
+ t=self
48
+ by=lambda { t.scale(f.js_apply(self,arguments))}
49
+ by
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,263 @@
1
+ module Rubyvis
2
+ class Scale::Quantitative
3
+ include Rubyvis::Scale
4
+ attr_reader :l
5
+ def initialize(*args)
6
+ @d=[0,1] # domain
7
+ @l=[0,1] # transformed domain
8
+ @r=[0,1] # default range
9
+ @i=[Rubyvis.identity] # default interpolator
10
+ @type=:to_f # default type
11
+ @n=false
12
+ @f=Rubyvis.identity # default forward transformation
13
+ @g=Rubyvis.identity
14
+ @tick_format=lambda {|x| x.to_f}
15
+ domain(*args)
16
+ end
17
+ def new_date(x=nil)
18
+ x.nil? ? Date.today : DateTime.civil(x)
19
+ end
20
+
21
+ def scale(x)
22
+ j=Rubyvis.search(@d,x)
23
+ j=-j-2 if (j<0)
24
+ j=[0,[@i.size-1,j].min].max
25
+ # p @l
26
+ # puts "Primero #{j}: #{@f.call(x) - @l[j]}"
27
+ # puts "Segundo #{(@l[j + 1] - @l[j])}"
28
+ @i[j].call((@f.call(x) - @l[j]) .quo(@l[j + 1] - @l[j]));
29
+ end
30
+ def [](x)
31
+ scale(x)
32
+ end
33
+ def transform(forward, inverse)
34
+ @f=lambda {|x| @n ? -forward.call(-x) : forward.call(x); }
35
+ @g=lambda {|y| @n ? -inverse.call(-y) : inverse.call(y); }
36
+ @l=@d.map{|v| @f.call(v)}
37
+ self
38
+ end
39
+
40
+
41
+ def domain(*arguments)
42
+ array,min,max=arguments
43
+ if (arguments.size>0)
44
+ if array.is_a? Array
45
+ min = pv.identity if (arguments.size < 2)
46
+ max = min if (arguments.size < 3)
47
+ o = array.size>0 && [array[0]].min
48
+ @d = array.size ? [Rubyvis.min(array, min), Rubyvis.max(array, max)] : [];
49
+ else
50
+ o = array;
51
+ @d = arguments.map {|i| i.to_f}
52
+ end
53
+ if !@d.size
54
+ @d = [-Infinity, Infinity];
55
+ elsif (@d.size == 1)
56
+ @d = [@d.first, @d.first]
57
+ end
58
+ @n = (@d.first<0 or @d.last<0)
59
+ @l=@d.map{|v| @f.call(v)}
60
+ @type = (o.is_a? Date) ? new_date : :to_f;
61
+ return self
62
+ end
63
+ # TODO: Fix this.
64
+ @d.map{|v|
65
+ case @type
66
+ when :to_f
67
+ v.to_f
68
+ when :to_date
69
+ Date.new(v)
70
+ else
71
+ v
72
+ end
73
+ }
74
+ end
75
+
76
+
77
+ def range(*arguments)
78
+ if (arguments.size>0)
79
+ @r = arguments.dup
80
+ if (@r.size==0)
81
+ @r = [-Infinity, Infinity];
82
+ elsif (@r.size == 1)
83
+ @r = [@r[0], @r[0]]
84
+ end
85
+ @i=(@r.size-1).times.map do |j|
86
+ Rubyvis::Scale.interpolator(@r[j], @r[j + 1]);
87
+ end
88
+ return self
89
+ end
90
+ @r
91
+ end
92
+
93
+ def invert(y)
94
+ j=Rubyvis.search(@r, y)
95
+ j=-j-2 if j<0
96
+ j = [0, [@i.size - 1, j].min].max
97
+
98
+ @g.call(@l[j] + (y - @r[j]).quo(@r[j + 1] - @r[j]) * (@l[j + 1] - @l[j]));
99
+ end
100
+
101
+ def type(v=nil)
102
+ return @type if v.nil?
103
+ case @type
104
+ when Numeric
105
+ v.to_f
106
+ when Date
107
+ raise "Not implemented yet"
108
+ end
109
+ end
110
+ # TODO: FIX this func
111
+ def ticks(*arguments)
112
+ m = arguments[0]
113
+ start = @d.first
114
+ _end = @d.last
115
+ reverse = _end < start
116
+ min = reverse ? _end : start
117
+ max = reverse ? start : _end
118
+ span = max - min
119
+
120
+ # Special case: empty, invalid or infinite span.
121
+ if (!span or (span.is_a? Float and span.infinite?))
122
+ @tick_format= Rubyvis.Format.date("%x") if (@type == newDate)
123
+ return [type(min)];
124
+ end
125
+ # From here, �chaos!
126
+ #/* Special case: dates. */
127
+ if (@type == new_date)
128
+ #/* Floor the date d given the precision p. */
129
+ lambda do |d,p|
130
+ case(p)
131
+ when 31536e6
132
+ d.setMonth(0);
133
+ when 2592e6
134
+ d.setDate(1);
135
+ when 6048e5
136
+ if (p == 6048e5)
137
+ d.setDate(d.getDate() - d.getDay());
138
+ end
139
+ when 864e5
140
+ d.setHours(0);
141
+ when 36e5
142
+ d.setMinutes(0);
143
+ when 6e4
144
+ d.setSeconds(0);
145
+ when 1e3
146
+ d.setMilliseconds(0);
147
+ end
148
+ end
149
+
150
+ precision, format, increment, step = 1,1,1,1
151
+ if (span >= 3 * 31536e6)
152
+ precision = 31536e6;
153
+ format = "%Y";
154
+ increment = lambda {|d| d.setFullYear(d.getFullYear() + step); };
155
+ elsif (span >= 3 * 2592e6)
156
+ precision = 2592e6;
157
+ format = "%m/%Y";
158
+ increment = lambda {|d| d.setMonth(d.getMonth() + step); };
159
+ elsif (span >= 3 * 6048e5)
160
+ precision = 6048e5;
161
+ format = "%m/%d";
162
+ increment = lambda {|d| d.setDate(d.getDate() + 7 * step); };
163
+ elsif (span >= 3 * 864e5)
164
+ precision = 864e5;
165
+ format = "%m/%d";
166
+ increment = lambda {|d| d.setDate(d.getDate() + step); };
167
+ elsif (span >= 3 * 36e5)
168
+ precision = 36e5;
169
+ format = "%I:%M %p";
170
+ increment = lambda {|d| d.setHours(d.getHours() + step); };
171
+ elsif (span >= 3 * 6e4)
172
+ precision = 6e4;
173
+ format = "%I:%M %p";
174
+ increment = lambda {|d| d.setMinutes(d.getMinutes() + step); };
175
+ elsif (span >= 3 * 1e3)
176
+ precision = 1e3;
177
+ format = "%I:%M:%S";
178
+ increment = lambda {|d| d.setSeconds(d.getSeconds() + step); };
179
+ else
180
+ precision = 1;
181
+ format = "%S.%Qs";
182
+ increment = lambda {|d| d.setTime(d.getTime() + step); };
183
+ end
184
+ @tick_format = pv.Format.date(format);
185
+
186
+ date = Date.new(min)
187
+ dates = []
188
+ #floor(date, precision);
189
+
190
+ # If we'd generate too many ticks, skip some!.
191
+ n = span.quo(precision)
192
+ if (n > 10)
193
+ case (precision)
194
+ when 36e5
195
+ step = (n > 20) ? 6 : 3;
196
+ date.setHours(Math.floor(date.getHours() / step) * step);
197
+ when 2592e6
198
+ step = 3; # seasons
199
+ date.setMonth(Math.floor(date.getMonth() / step) * step);
200
+ when 6e4
201
+ step = (n > 30) ? 15 : ((n > 15) ? 10 : 5);
202
+ date.setMinutes(Math.floor(date.getMinutes() / step) * step);
203
+ when 1e3
204
+ step = (n > 90) ? 15 : ((n > 60) ? 10 : 5);
205
+ date.setSeconds(Math.floor(date.getSeconds() / step) * step);
206
+ when 1
207
+ step = (n > 1000) ? 250 : ((n > 200) ? 100 : ((n > 100) ? 50 : ((n > 50) ? 25 : 5)));
208
+ date.setMilliseconds(Math.floor(date.getMilliseconds() / step) * step);
209
+ else
210
+ step = pv.logCeil(n / 15, 10);
211
+ if (n / step < 2)
212
+ step =step.quo(5)
213
+ elsif (n / step < 5)
214
+ step = step.quo(2)
215
+ end
216
+ date.setFullYear((date.getFullYear().quo(step)).floor * step);
217
+ end
218
+ end
219
+
220
+ while (true)
221
+ increment(date);
222
+ break if (date > max)
223
+ dates.push(Date.new(date));
224
+ end
225
+ return reverse ? dates.reverse() : dates;
226
+ end
227
+
228
+ # Normal case: numbers.
229
+ m = 10 if (arguments.size==0)
230
+
231
+ step = pv.log_floor(span.quo(m), 10)
232
+ err = m.quo(span.quo(step))
233
+ if (err <= 0.15)
234
+ step = step*10
235
+ elsif (err <= 0.35)
236
+ step = step*5
237
+ elsif (err <= 0.75)
238
+ step = step*2
239
+ end
240
+ start = (min.quo(step)).ceil * step
241
+ _end = (max.quo(step)).floor * step
242
+ @tick_format= pv.Format.number().fraction_digits([0, -(pv.log(step, 10) + 0.01).floor].max);
243
+ ticks = pv.range(start, _end + step, step);
244
+ return reverse ? ticks.reverse() : ticks;
245
+ end
246
+ def nice
247
+ if (@d.size!=2)
248
+ return self;
249
+ end
250
+ start=@d.first
251
+ _end=@d[@d.size-1]
252
+ reverse=_end<start
253
+ min=reverse ? _end : start
254
+ max = reverse ? start : _end
255
+ span=max-min
256
+ return self if(!span or span.infinite?)
257
+ step=10**((Math::log(span).quo(Math::log(10))).round-1)
258
+ @d=[(min.quo(step)).floor*step, (max.quo(step)).ceil*step]
259
+ @d.reverse if reverse
260
+ @l=@d.map {|v| @f.call(v)}
261
+ end
262
+ end
263
+ end