rubyvis 0.2.2 → 0.3.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 +10 -0
- data/Manifest.txt +7 -0
- data/Rakefile +2 -10
- data/examples/antibiotics/antibiotics_scatter.rb +4 -5
- data/examples/antibiotics/antibiotics_wedge.rb +0 -1
- data/examples/treemap/treemap.rb +29 -0
- data/examples/treemap/treemap_data.rb +285 -0
- data/lib/rubyvis.rb +2 -1
- data/lib/rubyvis/dom.rb +313 -0
- data/lib/rubyvis/layout.rb +8 -0
- data/lib/rubyvis/layout/hierarchy.rb +247 -0
- data/lib/rubyvis/layout/network.rb +228 -0
- data/lib/rubyvis/layout/stack.rb +2 -2
- data/lib/rubyvis/layout/treemap.rb +357 -0
- data/lib/rubyvis/mark.rb +10 -8
- data/lib/rubyvis/mark/anchor.rb +1 -1
- data/lib/rubyvis/mark/area.rb +1 -1
- data/lib/rubyvis/mark/bar.rb +1 -1
- data/lib/rubyvis/mark/dot.rb +1 -1
- data/lib/rubyvis/mark/image.rb +1 -1
- data/lib/rubyvis/mark/label.rb +1 -1
- data/lib/rubyvis/mark/line.rb +1 -1
- data/lib/rubyvis/mark/panel.rb +1 -1
- data/lib/rubyvis/mark/rule.rb +1 -1
- data/lib/rubyvis/mark/wedge.rb +1 -1
- data/lib/rubyvis/scene/svg_curve.rb +0 -1
- data/lib/rubyvis/sceneelement.rb +1 -1
- data/spec/dom_spec.rb +218 -0
- data/web/Rakefile +1 -1
- metadata +16 -25
- metadata.gz.sig +0 -0
data/lib/rubyvis/layout/stack.rb
CHANGED
@@ -79,7 +79,7 @@ module Rubyvis
|
|
79
79
|
attr_accessor :_x, :_y, :_values, :prop
|
80
80
|
|
81
81
|
def self.defaults
|
82
|
-
Stack.new.
|
82
|
+
Stack.new.mark_extend(Layout.defaults).orient("bottom-left").offset("zero").layers([[]])
|
83
83
|
end
|
84
84
|
|
85
85
|
# Constructs a new, empty stack layout. Layouts are not typically constructed
|
@@ -288,7 +288,7 @@ module Rubyvis
|
|
288
288
|
end
|
289
289
|
def add(type)
|
290
290
|
that = @that
|
291
|
-
that.add( Rubyvis.Panel ).data(lambda { that.layers() }).add(type).
|
291
|
+
that.add( Rubyvis.Panel ).data(lambda { that.layers() }).add(type).mark_extend( self )
|
292
292
|
end
|
293
293
|
end
|
294
294
|
|
@@ -0,0 +1,357 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
class Layout
|
3
|
+
def self.Treemap
|
4
|
+
Rubyvis::Layout::Treemap
|
5
|
+
end
|
6
|
+
|
7
|
+
# Implements a space-filling rectangular layout, with the hierarchy
|
8
|
+
# represented via containment. Treemaps represent nodes as boxes, with child
|
9
|
+
# nodes placed within parent boxes. The size of each box is proportional
|
10
|
+
# to the size of the node in the tree. This particular algorithm is taken from Bruls,
|
11
|
+
# D.M., C. Huizing, and J.J. van Wijk, <a
|
12
|
+
# href="http://www.win.tue.nl/~vanwijk/stm.pdf">"Squarified Treemaps"</a> in
|
13
|
+
# <i>Data Visualization 2000, Proceedings of the Joint Eurographics and IEEE
|
14
|
+
# TCVG Sumposium on Visualization</i>, 2000, pp. 33-42.
|
15
|
+
#
|
16
|
+
# <p>The meaning of the exported mark prototypes changes slightly in the
|
17
|
+
# space-filling implementation:<ul>
|
18
|
+
#
|
19
|
+
# <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar}. The node
|
20
|
+
# data is populated with <tt>dx</tt> and <tt>dy</tt> attributes, in addition to
|
21
|
+
# the standard <tt>x</tt> and <tt>y</tt> position attributes.
|
22
|
+
#
|
23
|
+
# <p><li><tt>leaf</tt> - for rendering leaf nodes only, with no fill or stroke
|
24
|
+
# style by default; typically a {@link pv.Panel} or another layout!
|
25
|
+
#
|
26
|
+
# <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
|
27
|
+
# in the arrangement of the space-filling nodes.
|
28
|
+
#
|
29
|
+
# <p><li><tt>label</tt> - for rendering node labels; typically a
|
30
|
+
# {@link pv.Label}.
|
31
|
+
#
|
32
|
+
# </ul>For more details on how to use this layout, see
|
33
|
+
# {@link pv.Layout.Hierarchy}.
|
34
|
+
#
|
35
|
+
class Treemap < Hierarchy
|
36
|
+
@properties=Hierarchy.properties.dup
|
37
|
+
def initialize
|
38
|
+
super
|
39
|
+
@size=lambda {|d| d.node_value.to_f}
|
40
|
+
@node.stroke_style("#fff").
|
41
|
+
fill_style("rgba(31, 119, 180, .25)").
|
42
|
+
width(lambda {|n| n.dx}).
|
43
|
+
height(lambda {|n| n.dy })
|
44
|
+
|
45
|
+
@node_label.
|
46
|
+
visible(lambda {|n| !n.first_child }).
|
47
|
+
left(lambda {|n| n.x + (n.dx / 2.0) }).
|
48
|
+
top(lambda {|n| n.y + (n.dy / 2.0) }).
|
49
|
+
text_align("center").
|
50
|
+
text_angle(lambda {|n| n.dx > n.dy ? 0 : -Math::PI / 2.0 })
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def leaf
|
55
|
+
m=Rubyvis::Mark.new.
|
56
|
+
mark_extend(self.node).
|
57
|
+
fill_style(nil).
|
58
|
+
stroke_style(nil).
|
59
|
+
visible(lambda {|n| !n.first_child })
|
60
|
+
m.parent = self
|
61
|
+
m
|
62
|
+
end
|
63
|
+
def link
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# :attr: round
|
69
|
+
# Whether node sizes should be rounded to integer values. This has a similar
|
70
|
+
# effect to setting <tt>antialias(false)</tt> for node values, but allows the
|
71
|
+
# treemap algorithm to accumulate error related to pixel rounding.
|
72
|
+
#
|
73
|
+
# @type boolean
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
##
|
78
|
+
# :attr: padding_left
|
79
|
+
# The left inset between parent add child in pixels. Defaults to 0.
|
80
|
+
#
|
81
|
+
# @type number
|
82
|
+
# @see #padding
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
##
|
87
|
+
# :attr: padding_rigth
|
88
|
+
# The right inset between parent add child in pixels. Defaults to 0.
|
89
|
+
#
|
90
|
+
# @type number
|
91
|
+
# @name pv.Layout.Treemap.prototype.paddingRight
|
92
|
+
# @see #padding
|
93
|
+
|
94
|
+
|
95
|
+
##
|
96
|
+
# :attr: padding_top
|
97
|
+
# The top inset between parent and child in pixels. Defaults to 0.
|
98
|
+
#
|
99
|
+
# @type number
|
100
|
+
# @name pv.Layout.Treemap.prototype.paddingTop
|
101
|
+
# @see #padding
|
102
|
+
|
103
|
+
|
104
|
+
##
|
105
|
+
# :attr: padding_bottom
|
106
|
+
# The bottom inset between parent and child in pixels. Defaults to 0.
|
107
|
+
#
|
108
|
+
# @type number
|
109
|
+
# @name pv.Layout.Treemap.prototype.paddingBottom
|
110
|
+
# @see #padding
|
111
|
+
|
112
|
+
|
113
|
+
##
|
114
|
+
# :attr: mode
|
115
|
+
# The treemap algorithm. The default value is "squarify". The "slice-and-dice"
|
116
|
+
# algorithm may also be used, which alternates between horizontal and vertical
|
117
|
+
# slices for different depths. In addition, the "slice" and "dice" algorithms
|
118
|
+
# may be specified explicitly to control whether horizontal or vertical slices
|
119
|
+
# are used, which may be useful for nested treemap layouts.
|
120
|
+
#
|
121
|
+
# @type string
|
122
|
+
# @name pv.Layout.Treemap.prototype.mode
|
123
|
+
# @see <a
|
124
|
+
# href="ftp://ftp.cs.umd.edu/pub/hcil/Reports-Abstracts-Bibliography/2001-06html/2001-06.pdf"
|
125
|
+
# >"Ordered Treemap Layouts"</a> by B. Shneiderman & M. Wattenberg, IEEE
|
126
|
+
# InfoVis 2001.
|
127
|
+
|
128
|
+
|
129
|
+
##
|
130
|
+
# :attr: order
|
131
|
+
# The sibling node order. A <tt>null</tt> value means to use the sibling order
|
132
|
+
# specified by the nodes property as-is; "reverse" will reverse the given
|
133
|
+
# order. The default value "ascending" will sort siblings in ascending order of
|
134
|
+
# size, while "descending" will do the reverse. For sorting based on data
|
135
|
+
# attributes other than size, use the default <tt>null</tt> for the order
|
136
|
+
# property, and sort the nodes beforehand using the {@link pv.Dom} operator.
|
137
|
+
#
|
138
|
+
# @type string
|
139
|
+
# @name pv.Layout.Treemap.prototype.order
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
attr_accessor_dsl :round, :padding_left, :padding_right, :padding_top, :padding_bottom, :mode, :order
|
145
|
+
|
146
|
+
# Default propertiess for treemap layouts. The default mode is "squarify" and the default order is "ascending".
|
147
|
+
def self.defaults
|
148
|
+
Rubyvis::Layout::Treemap.new.mark_extend(Rubyvis::Layout::Hierarchy.defaults).
|
149
|
+
mode("squarify"). # squarify, slice-and-dice, slice, dice
|
150
|
+
order('ascending') # ascending, descending, reverse, nil
|
151
|
+
end
|
152
|
+
|
153
|
+
# Alias for setting the left, right, top and bottom padding properties
|
154
|
+
# simultaneously.
|
155
|
+
def padding(n)
|
156
|
+
padding_left(n).padding_right(n).padding_top(n).padding_bottom(n)
|
157
|
+
end
|
158
|
+
def _size(d)
|
159
|
+
@size.call(d)
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Specifies the sizing function. By default, the size function uses the
|
164
|
+
# +node_value+ attribute of nodes as a numeric value:
|
165
|
+
# <p>The sizing function is invoked for each leaf node in the tree, per the
|
166
|
+
# <tt>nodes</tt> property. For example, if the tree data structure represents a
|
167
|
+
# file system, with files as leaf nodes, and each file has a <tt>bytes</tt>
|
168
|
+
# attribute, you can specify a size function as:
|
169
|
+
#
|
170
|
+
# <pre> .size(function(d) d.bytes)</pre>
|
171
|
+
#
|
172
|
+
# @param {function} f the new sizing function.
|
173
|
+
# @returns {pv.Layout.Treemap} this.
|
174
|
+
|
175
|
+
def size(f)
|
176
|
+
@size=Rubyvis.functor(f)
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
def build_implied(s)
|
181
|
+
return nil if hierarchy_build_implied(s)
|
182
|
+
|
183
|
+
that=self
|
184
|
+
nodes = s.nodes
|
185
|
+
root = nodes[0]
|
186
|
+
stack = Mark.stack
|
187
|
+
|
188
|
+
left = s.padding_left
|
189
|
+
right = s.padding_right
|
190
|
+
top = s.padding_top
|
191
|
+
bottom = s.padding_bottom
|
192
|
+
left||=0
|
193
|
+
right||=0
|
194
|
+
top||=0
|
195
|
+
bottom||=0
|
196
|
+
size=lambda {|n| n.size}
|
197
|
+
round = s.round ?
|
198
|
+
lambda {|a| a.round } :
|
199
|
+
lambda {|a| a.to_f}
|
200
|
+
mode = s.mode
|
201
|
+
|
202
|
+
slice=lambda { |row, sum, horizontal, x, y, w, h|
|
203
|
+
# puts "slice:#{sum},#{horizontal},#{x},#{y},#{w},#{h}"
|
204
|
+
d=0
|
205
|
+
row.size.times {|i|
|
206
|
+
n=row[i]
|
207
|
+
# puts "i:#{i},d:#{d}"
|
208
|
+
if horizontal
|
209
|
+
n.x = x + d
|
210
|
+
n.y = y
|
211
|
+
d += n.dx = round.call(w * n.size / sum.to_f)
|
212
|
+
n.dy = h
|
213
|
+
else
|
214
|
+
n.x = x
|
215
|
+
n.y = y + d
|
216
|
+
n.dx = w
|
217
|
+
d += n.dy = round.call(h * n.size / sum.to_f)
|
218
|
+
end
|
219
|
+
# puts "n.x:#{n.x}, n.y:#{n.y}, n.dx:#{n.dx}, n.dy:#{n.dy}"
|
220
|
+
}
|
221
|
+
|
222
|
+
|
223
|
+
if (row.last) # correct on-axis rounding error
|
224
|
+
n=row.last
|
225
|
+
if (horizontal)
|
226
|
+
n.dx += w - d
|
227
|
+
else
|
228
|
+
n.dy += h - d
|
229
|
+
end
|
230
|
+
end
|
231
|
+
}
|
232
|
+
|
233
|
+
ratio=lambda {|row, l|
|
234
|
+
rmax = -Infinity
|
235
|
+
rmin = Infinity
|
236
|
+
s = 0
|
237
|
+
row.each_with_index {|v,i|
|
238
|
+
r = v.size
|
239
|
+
rmin = r if (r < rmin)
|
240
|
+
rmax = r if (r > rmax)
|
241
|
+
s += r
|
242
|
+
}
|
243
|
+
s = s * s
|
244
|
+
l = l * l
|
245
|
+
[l * rmax / s.to_f, s.to_f / (l * rmin)].max
|
246
|
+
}
|
247
|
+
|
248
|
+
layout=lambda {|n,i|
|
249
|
+
x = n.x + left
|
250
|
+
y = n.y + top
|
251
|
+
w = n.dx - left - right
|
252
|
+
h = n.dy - top - bottom
|
253
|
+
|
254
|
+
# puts "Layout: '#{n.node_name}', #{n.x}, #{n.y}, #{n.dx}, #{n.dy}"
|
255
|
+
#/* Assume squarify by default. */
|
256
|
+
if (mode != "squarify")
|
257
|
+
slice.call(n.child_nodes, n.size, ( mode == "slice" ? true : mode == "dice" ? false : (i & 1)!=0), x, y, w, h)
|
258
|
+
else
|
259
|
+
row = []
|
260
|
+
mink = Infinity
|
261
|
+
l = [w,h].min
|
262
|
+
k = w * h / n.size.to_f
|
263
|
+
#/* Abort if the size is nonpositive. */
|
264
|
+
|
265
|
+
if (n.size > 0)
|
266
|
+
#/* Scale the sizes to fill the current subregion. */
|
267
|
+
n.visit_before {|n,i| n.size *= k }
|
268
|
+
|
269
|
+
#/** @private Position the specified nodes along one dimension. */
|
270
|
+
position=lambda {|row|
|
271
|
+
horizontal = w == l
|
272
|
+
sum = Rubyvis.sum(row, size)
|
273
|
+
r = l>0 ? round.call(sum / l.to_f) : 0
|
274
|
+
slice.call(row, sum, horizontal, x, y, horizontal ? w : r, horizontal ? r : h)
|
275
|
+
if horizontal
|
276
|
+
y += r
|
277
|
+
h -= r
|
278
|
+
else
|
279
|
+
x += r
|
280
|
+
w -= r
|
281
|
+
end
|
282
|
+
l = [w, h].min
|
283
|
+
horizontal
|
284
|
+
}
|
285
|
+
|
286
|
+
children = n.child_nodes.dup # copy
|
287
|
+
while (children.size>0) do
|
288
|
+
child = children[children.size - 1]
|
289
|
+
if (child.size==0)
|
290
|
+
children.pop
|
291
|
+
next
|
292
|
+
end
|
293
|
+
row.push(child)
|
294
|
+
|
295
|
+
k = ratio.call(row, l)
|
296
|
+
|
297
|
+
if (k <= mink)
|
298
|
+
children.pop
|
299
|
+
mink = k
|
300
|
+
else
|
301
|
+
row.pop
|
302
|
+
position.call(row)
|
303
|
+
row.clear
|
304
|
+
mink = Infinity
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
#/* correct off-axis rounding error */
|
309
|
+
|
310
|
+
if (position.call(row))
|
311
|
+
row.each {|v|
|
312
|
+
v.dy+=h
|
313
|
+
}
|
314
|
+
else
|
315
|
+
row.each {|v|
|
316
|
+
v.dx+=w
|
317
|
+
}
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
}
|
322
|
+
|
323
|
+
|
324
|
+
stack.unshift(nil)
|
325
|
+
root.visit_after {|nn,i|
|
326
|
+
nn.depth = i
|
327
|
+
nn.x = nn.y = nn.dx = nn.dy = 0
|
328
|
+
if nn.first_child
|
329
|
+
nn.size=Rubyvis.sum(nn.child_nodes, lambda {|v| v.size})
|
330
|
+
else
|
331
|
+
stack[0]=nn
|
332
|
+
nn.size=that._size(stack[0])
|
333
|
+
end
|
334
|
+
}
|
335
|
+
stack.shift()
|
336
|
+
|
337
|
+
#/* Sort. */
|
338
|
+
|
339
|
+
case s.order
|
340
|
+
when 'ascending'
|
341
|
+
root.sort(lambda {|a,b| a.size<=>b.size})
|
342
|
+
when 'descending'
|
343
|
+
root.sort(lambda {|a,b| b.size<=>a.size})
|
344
|
+
when 'reverse'
|
345
|
+
root.reverse
|
346
|
+
end
|
347
|
+
# /* Recursively compute the layout. */
|
348
|
+
root.x = 0;
|
349
|
+
root.y = 0;
|
350
|
+
root.dx = s.width
|
351
|
+
root.dy = s.height
|
352
|
+
root.visit_before {|n,i| layout.call(n,i)}
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
data/lib/rubyvis/mark.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module Rubyvis
|
3
2
|
# Constructs a new mark with default properties. Marks, with the exception of
|
4
3
|
# the root panel, are not typically constructed directly; instead, they are
|
@@ -488,7 +487,7 @@ module Rubyvis
|
|
488
487
|
# type of mark as this mark. (Note that for inheritance to be useful,
|
489
488
|
# properties with the same name on different mark types should
|
490
489
|
# have equivalent meaning.)
|
491
|
-
def
|
490
|
+
def mark_extend(proto)
|
492
491
|
@proto=proto
|
493
492
|
@target=proto.target
|
494
493
|
self
|
@@ -572,9 +571,9 @@ module Rubyvis
|
|
572
571
|
# * @param {function} type the type of mark to add; a constructor, such as
|
573
572
|
# +Rubyvis::Bar+
|
574
573
|
# * @returns {Mark} the new mark.
|
575
|
-
# * @see #
|
574
|
+
# * @see #mark_extend
|
576
575
|
def add(type)
|
577
|
-
parent.add(type).
|
576
|
+
parent.add(type).mark_extend(self)
|
578
577
|
end
|
579
578
|
# Returns an anchor with the specified name. All marks support the five
|
580
579
|
# standard anchor names:
|
@@ -930,7 +929,7 @@ module Rubyvis
|
|
930
929
|
mark.index=nil
|
931
930
|
end while(mark=mark.parent)
|
932
931
|
end
|
933
|
-
def context(scene,index,f) # :nodoc:
|
932
|
+
def context(scene, index, f) # :nodoc:
|
934
933
|
proto=Mark
|
935
934
|
stack=Mark.stack
|
936
935
|
oscene=Mark.scene
|
@@ -1036,14 +1035,17 @@ module Rubyvis
|
|
1036
1035
|
end
|
1037
1036
|
end
|
1038
1037
|
|
1039
|
-
|
1040
|
-
|
1041
1038
|
# Evaluates the specified array of properties for the specified
|
1042
1039
|
# instance <tt>s</tt> in the scene graph.
|
1043
1040
|
#
|
1044
1041
|
# @param s a node in the scene graph; the instance of the mark to build.
|
1045
1042
|
# @param properties an array of properties.
|
1046
|
-
|
1043
|
+
|
1044
|
+
def build_properties(ss,props) # :nodoc:
|
1045
|
+
mark_build_properties(ss,props)
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def mark_build_properties(ss, props) # :nodoc:
|
1047
1049
|
#p props
|
1048
1050
|
props.each do |prop|
|
1049
1051
|
v=prop.value
|
data/lib/rubyvis/mark/anchor.rb
CHANGED
data/lib/rubyvis/mark/area.rb
CHANGED
@@ -211,7 +211,7 @@ module Rubyvis
|
|
211
211
|
end
|
212
212
|
def self.defaults
|
213
213
|
a= Rubyvis::Colors.category20
|
214
|
-
Area.new.
|
214
|
+
Area.new.mark_extend(Mark.defaults).line_width(1.5).fill_style(lambda {a.scale(self.parent.index)}).interpolate('linear').tension(0.7)
|
215
215
|
end
|
216
216
|
def anchor(name)
|
217
217
|
area_anchor(name)
|
data/lib/rubyvis/mark/bar.rb
CHANGED
@@ -61,7 +61,7 @@ module Rubyvis
|
|
61
61
|
# style is a categorical color.
|
62
62
|
def self.defaults
|
63
63
|
a=Rubyvis.Colors.category20()
|
64
|
-
Bar.new.
|
64
|
+
Bar.new.mark_extend(Mark.defaults).line_width(1.5).fill_style( lambda {
|
65
65
|
a.scale(self.parent.index)
|
66
66
|
})
|
67
67
|
end
|