rubyvis 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,14 +1,61 @@
1
1
  module Rubyvis
2
+ #
3
+ # :section: colors/Ramp.js
4
+
5
+
6
+ # Returns a linear color ramp from the specified <tt>start</tt> color to the
7
+ # specified <tt>end</tt> color. The color arguments may be specified either as
8
+ # <tt>string</tt>s or as Rubyvis::Color. This is equivalent to:
9
+ #
10
+ # <pre> pv.Scale.linear().domain(0, 1).range(...)</pre>
11
+ def self.ramp(*arguments)
12
+ start, _end, dummy = arguments
13
+ scale = Rubyvis.Scale.linear
14
+ scale.range(*arguments)
15
+ scale
16
+ end
17
+
18
+ # :section: colors/Colors.js
19
+
2
20
  # Alias for Rubyvis::Colors
3
21
  def self.Colors
4
22
  Rubyvis::Colors
5
23
  end
24
+
25
+ # Returns a new categorical color encoding using the specified colors. The
26
+ # arguments to this method are an array of colors; see Rubyvis.color(). For
27
+ # example, to create a categorical color encoding using the <tt>species</tt>
28
+ # attribute:
29
+ #
30
+ # <pre>Rubyvis.colors("red", "green", "blue").by(lambda{|d| d.species})</pre>
31
+ #
32
+ # The result of this expression can be used as a fill- or stroke-style
33
+ # property. This assumes that the data's <tt>species</tt> attribute is a
34
+ # string.
35
+ #
6
36
  def self.colors(*args)
7
37
  scale=Rubyvis::Scale.ordinal
8
38
  scale.range(*args)
9
39
  scale
10
40
  end
41
+
42
+ # A collection of standard color palettes for categorical encoding.
11
43
  module Colors
44
+
45
+ # Returns a new 10-color scheme. The arguments to this constructor are
46
+ # optional, and equivalent to calling Rubyvis::Scale::Ordinal.domain. The
47
+ # following colors are used:
48
+ #
49
+ # <div style="background:#1f77b4;">#1f77b4</div>
50
+ # <div style="background:#ff7f0e;">#ff7f0e</div>
51
+ # <div style="background:#2ca02c;">#2ca02c</div>
52
+ # <div style="background:#d62728;">#d62728</div>
53
+ # <div style="background:#9467bd;">#9467bd</div>
54
+ # <div style="background:#8c564b;">#8c564b</div>
55
+ # <div style="background:#e377c2;">#e377c2</div>
56
+ # <div style="background:#7f7f7f;">#7f7f7f</div>
57
+ # <div style="background:#bcbd22;">#bcbd22</div>
58
+ # <div style="background:#17becf;">#17becf</div>
12
59
  def self.category10(*arguments)
13
60
  scale = Rubyvis.colors(
14
61
  "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
@@ -16,6 +63,30 @@ module Rubyvis
16
63
  scale.domain(*arguments) if arguments.size>0
17
64
  scale
18
65
  end
66
+
67
+ # Returns a new alternative 19-color scheme. The arguments to this constructor
68
+ # are optional, and equivalent to calling
69
+ # Rubyvis::Scale::Ordinal.domain. The following colors are used:
70
+ #
71
+ # <div style="background:#9c9ede;">#9c9ede</div>
72
+ # <div style="background:#7375b5;">#7375b5</div>
73
+ # <div style="background:#4a5584;">#4a5584</div>
74
+ # <div style="background:#cedb9c;">#cedb9c</div>
75
+ # <div style="background:#b5cf6b;">#b5cf6b</div>
76
+ # <div style="background:#8ca252;">#8ca252</div>
77
+ # <div style="background:#637939;">#637939</div>
78
+ # <div style="background:#e7cb94;">#e7cb94</div>
79
+ # <div style="background:#e7ba52;">#e7ba52</div>
80
+ # <div style="background:#bd9e39;">#bd9e39</div>
81
+ # <div style="background:#8c6d31;">#8c6d31</div>
82
+ # <div style="background:#e7969c;">#e7969c</div>
83
+ # <div style="background:#d6616b;">#d6616b</div>
84
+ # <div style="background:#ad494a;">#ad494a</div>
85
+ # <div style="background:#843c39;">#843c39</div>
86
+ # <div style="background:#de9ed6;">#de9ed6</div>
87
+ # <div style="background:#ce6dbd;">#ce6dbd</div>
88
+ # <div style="background:#a55194;">#a55194</div>
89
+ # <div style="background:#7b4173;">#7b4173</div>
19
90
  def self.category19(*arguments)
20
91
  scale = Rubyvis.colors(
21
92
  "#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b",
@@ -25,6 +96,33 @@ module Rubyvis
25
96
  scale.domain(*arguments) if arguments.size>0
26
97
  scale
27
98
  end
99
+
100
+
101
+ # Returns a new 20-color scheme. The arguments to this constructor are
102
+ # optional, and equivalent to calling Rubyvis::Scale::Ordinal.domain. The
103
+ # following colors are used:
104
+ #
105
+ # <div style="background:#1f77b4;">#1f77b4</div>
106
+ # <div style="background:#aec7e8;">#aec7e8</div>
107
+ # <div style="background:#ff7f0e;">#ff7f0e</div>
108
+ # <div style="background:#ffbb78;">#ffbb78</div>
109
+ # <div style="background:#2ca02c;">#2ca02c</div>
110
+ # <div style="background:#98df8a;">#98df8a</div>
111
+ # <div style="background:#d62728;">#d62728</div>
112
+ # <div style="background:#ff9896;">#ff9896</div>
113
+ # <div style="background:#9467bd;">#9467bd</div>
114
+ # <div style="background:#c5b0d5;">#c5b0d5</div>
115
+ # <div style="background:#8c564b;">#8c564b</div>
116
+ # <div style="background:#c49c94;">#c49c94</div>
117
+ # <div style="background:#e377c2;">#e377c2</div>
118
+ # <div style="background:#f7b6d2;">#f7b6d2</div>
119
+ # <div style="background:#7f7f7f;">#7f7f7f</div>
120
+ # <div style="background:#c7c7c7;">#c7c7c7</div>
121
+ # <div style="background:#bcbd22;">#bcbd22</div>
122
+ # <div style="background:#dbdb8d;">#dbdb8d</div>
123
+ # <div style="background:#17becf;">#17becf</div>
124
+ # <div style="background:#9edae5;">#9edae5</div>
125
+
28
126
  def self.category20(*arguments)
29
127
  scale = Rubyvis.colors(
30
128
  "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c",
@@ -1,225 +1,300 @@
1
1
  module Rubyvis
2
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=Rubyvis.identity
22
- end
23
- def x(f)
24
- @_x=Rubyvis.functor(f)
25
- return self
26
- end
27
- def y(f)
28
- @_y=Rubyvis.functor(f)
29
- return self
3
+ # Alias for Rubyvis::Layout::Stack
4
+ def self.Stack
5
+ Rubyvis::Layout::Stack
30
6
  end
31
- def values(f=nil)
32
- if f.nil?
33
- @values
34
- else
35
- @_values=Rubyvis.functor(f)
36
- return self
37
- end
38
- end
39
-
40
7
 
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
8
+ # Implements a layout for stacked visualizations, ranging from simple
9
+ # stacked bar charts to more elaborate "streamgraphs" composed of stacked
10
+ # areas. Stack layouts uses length as a visual encoding, as opposed to
11
+ # position, as the layers do not share an aligned axis.
12
+ #
13
+ # <p>Marks can be stacked vertically or horizontally. For example,
14
+ #
15
+ # vis.add(Rubyvis::Layout::Stack)
16
+ # .layers([[1, 1.2, 1.7, 1.5, 1.7],
17
+ # [.5, 1, .8, 1.1, 1.3],
18
+ # [.2, .5, .8, .9, 1]])
19
+ # .x(lambda { index * 35})
20
+ # .y(lambda {|d| d * 40})
21
+ # .layer.add(Rubyvis::Area)
22
+ #
23
+ # specifies a vertically-stacked area chart, using the default "bottom-left"
24
+ # orientation with "zero" offset. This visualization can be easily changed into
25
+ # a streamgraph using the "wiggle" offset, which attempts to minimize change in
26
+ # slope weighted by layer thickness. See the offset property for more
27
+ # supported streamgraph algorithms.
28
+ #
29
+ # <p>In the simplest case, the layer data can be specified as a two-dimensional
30
+ # array of numbers. The <tt>x</tt> and <tt>y</tt> psuedo-properties are used to
31
+ # define the thickness of each layer at the given position, respectively; in
32
+ # the above example of the "bottom-left" orientation, the <tt>x</tt> and
33
+ # <tt>y</tt> psuedo-properties are equivalent to the <tt>left</tt> and
34
+ # <tt>height</tt> properties that you might use if you implemented a stacked
35
+ # area by hand.
36
+ #
37
+ # <p>The advantage of using the stack layout is that the baseline, i.e., the
38
+ # <tt>bottom</tt> property is computed automatically using the specified offset
39
+ # algorithm. In addition, the order of layers can be computed using a built-in
40
+ # algorithm via the <tt>order</tt> property.
41
+ #
42
+ # <p>With the exception of the "expand" <tt>offset</tt>, the stack layout does
43
+ # not perform any automatic scaling of data; the values returned from
44
+ # <tt>x</tt> and <tt>y</tt> specify pixel sizes. To simplify scaling math, use
45
+ # this layout in conjunction with Rubyvis::Scale.linea} or similar.
46
+ #
47
+ # <p>In other cases, the <tt>values</tt> psuedo-property can be used to define
48
+ # the data more flexibly. As with a typical panel &amp; area, the
49
+ # <tt>layers</tt> property corresponds to the data in the enclosing panel,
50
+ # while the <tt>values</tt> psuedo-property corresponds to the data for the
51
+ # area within the panel. For example, given an array of data values:
52
+ #
53
+ # crimea = [
54
+ # { date: "4/1854", wounds: 0, other: 110, disease: 110 },
55
+ # { date: "5/1854", wounds: 0, other: 95, disease: 105 },
56
+ # { date: "6/1854", wounds: 0, other: 40, disease: 95 },
57
+ # ...
58
+ #
59
+ # and a corresponding array of series names:
60
+ #
61
+ # causes = [:wounds, :other, :disease]
62
+ #
63
+ # Separate layers can be defined for each cause like so:
64
+ #
65
+ # vis.add(pv.Layout.Stack)
66
+ # .layers(causes)
67
+ # .values(crimea)
68
+ # .x(lambda {|d| x.scale(d[:date]})
69
+ # .y(lambda {|d,dp| y.scale(d[dp])})
70
+ # .layer.add(pv.Area)
71
+ #
72
+ # As with the panel &amp; area case, the datum that is passed to the
73
+ # psuedo-properties <tt>x</tt> and <tt>y</tt> are the values (an element in
74
+ # <tt>crimea</tt>); the second argument is the layer data (a string in
75
+ # <tt>causes</tt>). Additional arguments specify the data of enclosing panels, if any.
76
+ class Stack < Rubyvis::Layout
77
+ @properties=Panel.properties.dup
78
+
79
+ attr_accessor :_x, :_y, :_values, :prop
80
+ attr_accessor_dsl :orient,:offset, :order, :layers
81
+ def self.defaults
82
+ Stack.new.extend(Layout.defaults).orient("bottom-left").offset("zero").layers([[]])
61
83
  end
62
- h = self.parent.send(horizontal ? "height" : "width")
63
- x = []
64
- y = []
65
- dy = []
66
84
 
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
85
+ # Constructs a new, empty stack layout. Layouts are not typically constructed
86
+ # directly; instead, they are added to an existing panel via
87
+ # Rubyvis::Mark.add
88
+ def initialize
89
+ super
90
+ @none=lambda {nil}
91
+ @prop = {"t"=> @none, "l"=> @none, "r"=> @none, "b"=> @none, "w"=> @none, "h"=> @none}
92
+ @values=nil
93
+ @_x=lambda {return 0}
94
+ @_y=lambda {return 0}
95
+ @_values=Rubyvis.identity
96
+ end
97
+ def x(f)
98
+ @_x=Rubyvis.functor(f)
99
+ return self
100
+ end
101
+ def y(f)
102
+ @_y=Rubyvis.functor(f)
103
+ return self
104
+ end
105
+ def values(f=nil)
106
+ if f.nil?
107
+ @values
108
+ else
109
+ @_values=Rubyvis.functor(f)
110
+ return self
111
+ end
112
+ end
73
113
 
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
114
 
95
- # order
96
- _index=nil
97
- case (s.order)
98
- when "inside-out"
99
- max = dy.map {|v| Rubyvis.max.index(v) }
100
- map = pv.range(n).sort {|a,b| return max[a] - max[b]}
101
- sums = dy.map {|v| Rubyvis.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
+ def proxy(name)
116
+ that=self
117
+ return lambda {
118
+ a=that.prop[name].js_call(self, self.parent.index, self.index);
119
+ puts "proxy(#{name}): #{a}" if $DEBUG
120
+ a
115
121
  }
116
- _index = bottoms.reverse+tops
117
-
118
- when "reverse"
119
- _index = Rubyvis.range(n - 1, -1, -1)
120
- else
121
- _index = Rubyvis.range(n)
122
122
  end
123
123
 
124
- #/* offset */
125
- case (s.offset)
126
- when "silohouette"
124
+ def build_implied(s)
125
+ # puts "Build stack" if $DEBUG
126
+ panel_build_implied(s)
127
+ data = s.layers
128
+ n = data.size
129
+ m = nil
130
+ orient = s.orient
131
+ if orient =~/^(top|bottom)\b/
132
+ horizontal=true
133
+ else
134
+ horizontal=false
135
+ end
136
+ h = self.parent.send(horizontal ? "height" : "width")
137
+ x = []
138
+ y = []
139
+ dy = []
140
+
141
+ #
142
+ # Iterate over the data, evaluating the values, x and y functions. The
143
+ # context in which the x and y psuedo-properties are evaluated is a
144
+ # pseudo-mark that is a grandchild of this layout.
145
+ #
146
+ stack = Rubyvis::Mark.stack
147
+
148
+ o = OpenStruct.new({:parent=> OpenStruct.new({:parent=> self})})
149
+ stack.unshift(nil)
150
+ values = []
151
+ n.times {|i|
152
+ dy[i] = []
153
+ y[i] = []
154
+ o.parent.index = i
155
+ stack[0] = data[i]
156
+ values[i] = self._values.js_apply(o.parent, stack);
157
+ m = values[i].size if (i==0)
158
+ stack.unshift(nil)
127
159
  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;
160
+ stack[0] = values[i][j]
161
+ o.index = j
162
+ x[j] = self._x.js_apply(o, stack) if i==0
163
+ dy[i][j] = self._y.js_apply(o, stack)
133
164
  }
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
165
+ stack.shift()
166
+ }
167
+ stack.shift()
140
168
 
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]}
169
+ # order
170
+ _index=nil
171
+ case (s.order)
172
+ when "inside-out"
173
+ max = dy.map {|v| Rubyvis.max.index(v) }
174
+ map = pv.range(n).sort {|a,b| return max[a] - max[b]}
175
+ sums = dy.map {|v| Rubyvis.sum(v)}
176
+ top = 0
177
+ bottom = 0
178
+ tops = []
179
+ bottoms = []
146
180
  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
181
+ j = map[i]
182
+ if (top < bottom)
183
+ top += sums[j];
184
+ tops.push(j);
185
+ else
186
+ bottom += sums[j];
187
+ bottoms.push(j);
188
+ end
189
+ }
190
+ _index = bottoms.reverse+tops
191
+
192
+ when "reverse"
193
+ _index = Rubyvis.range(n - 1, -1, -1)
194
+ else
195
+ _index = Rubyvis.range(n)
196
+ end
197
+
198
+ #/* offset */
199
+ case (s.offset)
200
+ when "silohouette"
201
+ m.times {|j|
202
+ o = 0;
203
+ n.times {|i|
204
+ o += dy[i][j]
151
205
  }
152
- s2 += s3 * dy[_index[i]][j]
206
+ y[_index[0]][j] = (h - o) / 2.0;
153
207
  }
154
- o -= (s1!=0) ? s2 / s1.to_f * dx : 0
155
- y[_index[0]][j] = o
208
+
209
+ when "wiggle"
210
+ o = 0;
211
+ n.times {|i| o += dy[i][0] }
156
212
 
157
- }
158
- when "expand"
213
+ y[_index[0]][0] = o = (h - o) / 2.0
214
+
215
+ (1...m).each {|j|
216
+ s1 = 0
217
+ s2 = 0
218
+ dx = x[j] - x[j - 1]
219
+ n.times {|i| s1 += dy[i][j]}
220
+ n.times {|i|
221
+
222
+ s3 = (dy[_index[i]][j] - dy[_index[i]][j - 1]) / (2.0 * dx)
223
+ i.times {|k|
224
+ s3 += (dy[_index[k]][j] - dy[_index[k]][j - 1]) / dx.to_f
225
+ }
226
+ s2 += s3 * dy[_index[i]][j]
227
+ }
228
+ o -= (s1!=0) ? s2 / s1.to_f * dx : 0
229
+ y[_index[0]][j] = o
230
+
231
+ }
232
+ when "expand"
233
+ m.times {|j|
234
+ y[_index[0]][j] = 0
235
+
236
+ k = 0
237
+ n.times {|i|k += dy[i][j]}
238
+ if (k!=0)
239
+ k = h / k.to_f
240
+ n.times {|i| dy[i][j] *= k}
241
+ else
242
+ k = h / n.to_f
243
+ n.times { dy[i][j] = k}
244
+ end
245
+ }
246
+ else
247
+ m.times {|j| y[_index[0]][j] = 0}
248
+ end
249
+
250
+ # Propagate the offset to the other series. */
159
251
  m.times {|j|
160
- y[_index[0]][j] = 0
252
+ o = y[_index[0]][j]
253
+ (1...n).each {|i|
161
254
 
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
255
+ o += dy[_index[i - 1]][j]
256
+ y[_index[i]][j] = o
171
257
  }
172
- else
173
- m.times {|j| y[_index[0]][j] = 0}
258
+ }
259
+
260
+ # /* Find the property definitions for dynamic substitution. */
261
+
262
+ i = orient.index("-")
263
+ pdy = horizontal ? "h" : "w"
264
+ px = i < 0 ? (horizontal ? "l" : "b") : orient[i + 1,1]
265
+ py = orient[0,1]
266
+
267
+ @values=values
268
+
269
+ @prop.each {|k,v|
270
+ @prop[k]=@none
271
+ }
272
+ # puts "stack: x:#{px}, y:#{py}, dy:#{pdy}" if $DEBUG
273
+ @prop[px] =lambda {|i1,j| x[j]}
274
+ @prop[py] =lambda {|i1,j| y[i1][j]}
275
+ @prop[pdy]=lambda {|i1,j| dy[i1][j]}
174
276
  end
175
277
 
176
- # Propagate the offset to the other series. */
177
- m.times {|j|
178
- o = y[_index[0]][j]
179
- (1...n).each {|i|
278
+ def layer
279
+ that=self
280
+ value = Rubyvis::Mark.new().data(lambda { that.values[self.parent.index] }).top(proxy("t")).left(proxy("l")).right(proxy("r")).
281
+ bottom(proxy("b")).
282
+ width(proxy("w")).
283
+ height(proxy("h"))
180
284
 
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] }).top(proxy("t")).left(proxy("l")).right(proxy("r")).
207
- bottom(proxy("b")).
208
- width(proxy("w")).
209
- height(proxy("h"))
210
-
211
- class << value
212
- def that=(v)
213
- @that = v
214
- end
215
- def add(type)
216
- that = @that
217
- that.add( Rubyvis.Panel ).data(lambda { that.layers() }).add(type).extend( self )
285
+ class << value # :nodoc:
286
+ def that=(v)
287
+ @that = v
288
+ end
289
+ def add(type)
290
+ that = @that
291
+ that.add( Rubyvis.Panel ).data(lambda { that.layers() }).add(type).extend( self )
292
+ end
218
293
  end
294
+
295
+ value.that=self
296
+ return value
219
297
  end
220
- value.that=self
221
- return value
222
298
  end
223
299
  end
224
- end
225
300
  end