rubyvis 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,10 @@
1
+ === 0.3.3 / 2010-11-23
2
+
3
+ * Implemented Rubyvis::Layout::Pack, Rubyvis::Layout::Indent and Rubyvis::Flatten
4
+ * Implemeted Rubyvis::Mark.title()
5
+ * New examples: bubble charts and circle packing
6
+ * Better documentation for Network
7
+
1
8
  === 0.3.2 / 2010-11-23
2
9
 
3
10
  * Updated examples
@@ -10,8 +10,10 @@ examples/area_interpolation.rb
10
10
  examples/bar_column_chart.rb
11
11
  examples/barley/barley.rb
12
12
  examples/barley/barley_data.rb
13
+ examples/bubble_charts.rb
13
14
  examples/cars/cars.rb
14
15
  examples/cars/cars_data.rb
16
+ examples/circle_packing.rb
15
17
  examples/crimea/crimea_data.rb
16
18
  examples/crimea/crimea_grouped_bar.rb
17
19
  examples/crimea/crimea_line.rb
@@ -23,6 +25,7 @@ examples/fixtures/tipsy.gif
23
25
  examples/grouped_charts.rb
24
26
  examples/icicle.rb
25
27
  examples/image.rb
28
+ examples/indent.rb
26
29
  examples/line.rb
27
30
  examples/line_and_step.rb
28
31
  examples/line_interpolation.rb
@@ -39,6 +42,7 @@ lib/rubyvis.rb
39
42
  lib/rubyvis/color/color.rb
40
43
  lib/rubyvis/color/colors.rb
41
44
  lib/rubyvis/dom.rb
45
+ lib/rubyvis/flatten.rb
42
46
  lib/rubyvis/format.rb
43
47
  lib/rubyvis/format/date.rb
44
48
  lib/rubyvis/format/number.rb
@@ -48,7 +52,9 @@ lib/rubyvis/javascript_behaviour.rb
48
52
  lib/rubyvis/layout.rb
49
53
  lib/rubyvis/layout/cluster.rb
50
54
  lib/rubyvis/layout/hierarchy.rb
55
+ lib/rubyvis/layout/indent.rb
51
56
  lib/rubyvis/layout/network.rb
57
+ lib/rubyvis/layout/pack.rb
52
58
  lib/rubyvis/layout/partition.rb
53
59
  lib/rubyvis/layout/stack.rb
54
60
  lib/rubyvis/layout/treemap.rb
@@ -0,0 +1,60 @@
1
+ # Bubble charts, such as those provided by "Many Eyes":http://manyeyes.alphaworks.ibm.com/manyeyes/page/Bubble_Chart.html, encode data in the area of circles. Although less perceptually accurate than bar charts, they can pack hundreds of values into a small space. A similar technique is the Dorling cartogram, where circles are positioned according to geography rather than arbitrarily. Here we compare the file sizes of the Rubyvis library files
2
+ $:.unshift(File.dirname(__FILE__)+"/../lib")
3
+ require 'rubyvis'
4
+
5
+
6
+ def get_files(path)
7
+ h={}
8
+ Dir.glob("#{path}/*").each {|e|
9
+ next if File.expand_path(e)=~/pkg|web|vendor|doc|~/
10
+ pa=File.expand_path(e)
11
+ if File.stat(pa).directory?
12
+ h[File.basename(pa)]=get_files(pa)
13
+ else
14
+ h[File.basename(pa)]=File.stat(pa).size
15
+ end
16
+ }
17
+ h
18
+ end
19
+
20
+ files=get_files(File.dirname(__FILE__)+"/../lib")
21
+
22
+ classes = Rubyvis.nodes(Rubyvis.flatten(files).leaf(lambda {|v| v.is_a? Numeric}).array)
23
+
24
+
25
+ classes[1,classes.size-1].each {|d|
26
+ #p d.node_value.keys
27
+ d.node_name = "/" + d.node_value[:keys].join("/")
28
+ i = d.node_name.rindex("/")
29
+ class << d
30
+ attr_accessor :class_name, :package_name
31
+ end
32
+ d.class_name = d.node_name[i+1,d.node_name.size-(i+1)].gsub(".rb","")
33
+ d.package_name = d.node_name[0,i]
34
+ d.node_value = d.node_value[:value]
35
+ }
36
+ # For pretty number formatting.
37
+ format = Rubyvis.Format.number
38
+
39
+ vis = Rubyvis::Panel.new.
40
+ width(600)
41
+ .height(600)
42
+ c20=Rubyvis::Colors.category20()
43
+ vis.add(pv.Layout.Pack)
44
+ .top(-50)
45
+ .bottom(-50)
46
+ .nodes(classes)
47
+ .size(lambda {|d| d.node_value})
48
+ .spacing(0)
49
+ .order(nil)
50
+ .node.add(Rubyvis::Dot)
51
+ .fill_style(lambda {|d| c20.scale(d.package_name)})
52
+ .stroke_style(lambda {|d| c20.scale(d.package_name).darker})
53
+ .visible(lambda {|d| d.parent_node})
54
+ .title(lambda {|d| d.node_name + ": " + format.format(d.node_value)})
55
+ .anchor("center").add(pv.Label)
56
+ .text(lambda {|d| d.class_name[0, Math.sqrt(d.node_value).to_i / 8]})
57
+
58
+ vis.render();
59
+ puts vis.to_svg
60
+
@@ -0,0 +1,54 @@
1
+ # = Circle Packing
2
+ # Enclosure diagrams are also space-filling, using containment rather than adjacency to represent the hierarchy. As with adjacency diagrams, the size of any node in the tree is quickly revealed. Although circle packing does not use space as efficiently as a treemap, the “wasted” space effectively reveals the hierarchy. At the same time, node sizes can be rapidly compared using area judgments.
3
+ # By flattening the hierarchy, the pack layout can also be used to create "bubble charts":bubble_charts.html.
4
+ # This example uses RBP API.
5
+
6
+
7
+ $:.unshift(File.dirname(__FILE__)+"/../lib")
8
+ require 'rubyvis'
9
+
10
+
11
+ def get_files(path)
12
+ h={}
13
+ Dir.glob("#{path}/*").each {|e|
14
+ next if File.expand_path(e)=~/pkg|web|vendor|doc|~/
15
+ pa=File.expand_path(e)
16
+ if File.stat(pa).directory?
17
+ h[File.basename(pa)]=get_files(pa)
18
+ else
19
+ h[File.basename(pa)]=File.stat(pa).size
20
+ end
21
+ }
22
+ h
23
+ end
24
+
25
+ files=get_files(File.dirname(__FILE__)+"/../lib/")
26
+
27
+ format = Rubyvis::Format.number();
28
+
29
+ vis = Rubyvis::Panel.new do
30
+ width 600
31
+ height 796
32
+ margin 2
33
+ layout_pack do
34
+ nodes Rubyvis.dom(files).root("Rubyvis").nodes
35
+ size(lambda {|d| d.node_value})
36
+ node.dot do
37
+ fill_style {|d|
38
+ d.first_child ? "rgba(31, 119, 180, 0.25)" : "#ff7f0e"
39
+ }
40
+ title {|d|
41
+ d.node_name.to_s + (d.first_child ? "" : ": " + format.format(d.node_value))}
42
+
43
+ line_width 1
44
+ end
45
+ node_label.label do
46
+ visible {|d| !d.first_child}
47
+ text {|d| d.node_name[0, Math.sqrt(d.node_value) / 10]}
48
+ end
49
+ end
50
+ end
51
+
52
+ vis.render()
53
+ puts vis.to_svg
54
+
@@ -0,0 +1,71 @@
1
+ # = Indented Tree
2
+ # Indented trees are widely-used to represent file systems, among other applications. Although indented trees require much vertical space and do not easily facilitate multiscale inference, they do allow efficient interactive exploration of the tree to find a specific node.
3
+ # In addition, the indent layout allows rapid scanning of node labels, and multivariate data such as file size can be displayed adjacent to the hierarchy.
4
+ #
5
+ # Uses Protovis API
6
+ $:.unshift(File.dirname(__FILE__)+"/../lib")
7
+ require 'rubyvis'
8
+
9
+
10
+ def get_files(path)
11
+ h={}
12
+ Dir.glob("#{path}/*").each {|e|
13
+ next if File.expand_path(e)=~/pkg|web|vendor|doc|~/
14
+ pa=File.expand_path(e)
15
+ if File.stat(pa).directory?
16
+ h[File.basename(pa)]=get_files(pa)
17
+ else
18
+ h[File.basename(pa)]=File.stat(pa).size
19
+ end
20
+ }
21
+ h
22
+ end
23
+
24
+ files=get_files(File.dirname(__FILE__)+"/../")
25
+
26
+
27
+ root = Rubyvis.dom(files)
28
+ .root("rubyvis")
29
+ .sort(lambda {|a,b| a.node_name<=>b.node_name})
30
+
31
+ #/* Recursively compute the package sizes. */
32
+ root.visit_after {|n,i|
33
+ if (n.first_child)
34
+ n.node_value= Rubyvis.sum(n.child_nodes , lambda {|nn| nn.node_value})
35
+ end
36
+ }
37
+
38
+ def t(d)
39
+ d.parent_node ? (t(d.parent_node) + "." + d.node_name) : d.node_name
40
+ end
41
+
42
+ vis = Rubyvis::Panel.new()
43
+ .width(260)
44
+ .height((root.nodes.size + 1)* 12)
45
+ .margin(5)
46
+
47
+ layout = vis.add(pv.Layout.Indent)
48
+ .nodes(lambda {root.nodes})
49
+ .depth(12)
50
+ .breadth(12)
51
+
52
+ layout.link.add(pv.Line)
53
+
54
+ node = layout.node.add(pv.Panel)
55
+ .top(lambda {|n| n.y - 6})
56
+ .height(12)
57
+ .right(6)
58
+ .strokeStyle(nil)
59
+
60
+ node.anchor("left").add(pv.Dot)
61
+ .strokeStyle("#1f77b4")
62
+ .fillStyle(lambda {|n| n.first_child ? "#aec7e8" : "#ff7f0e"})
63
+ .title(lambda {|d| t(d)})
64
+ .anchor("right").add(pv.Label)
65
+ .text(lambda {|n| n.node_name})
66
+
67
+ node.anchor("right").add(pv.Label)
68
+ .text(lambda {|n| (n.node_value >> 10).to_s + "KB"})
69
+
70
+ vis.render()
71
+ puts vis.to_svg
@@ -8,6 +8,7 @@ require 'rubyvis/internals'
8
8
  require 'rubyvis/sceneelement'
9
9
  require 'rubyvis/property'
10
10
  require 'rubyvis/nest'
11
+ require 'rubyvis/flatten'
11
12
 
12
13
  require 'rubyvis/javascript_behaviour'
13
14
  require 'rubyvis/format'
@@ -29,7 +30,7 @@ require 'rubyvis/mark/shorcut_methods'
29
30
  module Rubyvis
30
31
  @document=nil
31
32
  # Rubyvis version
32
- VERSION = '0.3.2'
33
+ VERSION = '0.3.3'
33
34
  # Protovis API on which current Rubyvis is based
34
35
  PROTOVIS_API_VERSION='3.3'
35
36
  # You actually can do it! http://snipplr.com/view/2137/uses-for-infinity-in-ruby/
@@ -110,8 +110,10 @@ module Rubyvis
110
110
  attr_accessor :angle
111
111
  attr_accessor :start_angle
112
112
  attr_accessor :outer_radius
113
- attr_accessor :inner_radius
114
-
113
+ attr_accessor :inner_radius
114
+ attr_accessor :radius
115
+ attr_accessor :_p
116
+ attr_accessor :n
115
117
 
116
118
  # Constructs a DOM node for the specified value. Instances of this class are
117
119
  # not typically created directly; instead they are generated from a JavaScript
@@ -0,0 +1,128 @@
1
+ module Rubyvis
2
+ # Returns a {@link pv.Flatten} operator for the specified map. This is a
3
+ # convenience factory method, equivalent to <tt>new pv.Flatten(map)</tt>.
4
+ def self.flatten(map)
5
+ Flatten.new(map)
6
+ end
7
+ # Represents a flatten operator for the specified array. Flattening
8
+ # allows hierarchical maps to be flattened into an array. The levels in the
9
+ # input tree are specified by <i>key</i> functions.
10
+ #
11
+ # <p>For example, consider the following hierarchical data structure of Barley
12
+ # yields, from various sites in Minnesota during 1931-2:
13
+ #
14
+ # <pre>{ 1931: {
15
+ # Manchuria: {
16
+ # "University Farm": 27.00,
17
+ # "Waseca": 48.87,
18
+ # "Morris": 27.43,
19
+ # ... },
20
+ # Glabron: {
21
+ # "University Farm": 43.07,
22
+ # "Waseca": 55.20,
23
+ # ... } },
24
+ # 1932: {
25
+ # ... } }</pre>
26
+ #
27
+ # To facilitate visualization, it may be useful to flatten the tree into a
28
+ # tabular array:
29
+ #
30
+ # <pre>var array = pv.flatten(yields)
31
+ # .key("year")
32
+ # .key("variety")
33
+ # .key("site")
34
+ # .key("yield")
35
+ # .array();</pre>
36
+ #
37
+ # This returns an array of object elements. Each element in the array has
38
+ # attributes corresponding to this flatten operator's keys:
39
+ #
40
+ # <pre>{ site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
41
+ # { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
42
+ # { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
43
+ # { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
44
+ # { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...</pre>
45
+ #
46
+ # <p>The flatten operator is roughly the inverse of the {@link pv.Nest} and
47
+ # {@link pv.Tree} operators.
48
+ #
49
+ class Flatten
50
+ def initialize(map)
51
+ @map=map
52
+ @keys=[]
53
+ @leaf=nil
54
+ end
55
+ # Flattens using the specified key function. Multiple keys may be added to the
56
+ # flatten; the tiers of the underlying tree must correspond to the specified
57
+ # keys, in order. The order of the returned array is undefined; however, you
58
+ # can easily sort it.
59
+ #
60
+ # @param {string} key the key name.
61
+ # @param {function} [f] an optional value map function.
62
+ # @returns {pv.Nest} this.
63
+
64
+ def key(k, f=nil)
65
+ @keys.push(OpenStruct.new({:name=>key,:value=>f}))
66
+ @leaf=nil
67
+ self
68
+ end
69
+
70
+ # Flattens using the specified leaf function. This is an alternative to
71
+ # specifying an explicit set of keys; the tiers of the underlying tree will be
72
+ # determined dynamically by recursing on the values, and the resulting keys
73
+ # will be stored in the entries <tt>keys</tt> attribute. The leaf function must
74
+ # return true for leaves, and false for internal nodes.
75
+ #
76
+ # @param {function} f a leaf function.
77
+ # @returns {pv.Nest} this.
78
+ def leaf(f)
79
+ @keys.clear
80
+ @leaf=f
81
+ self
82
+ end
83
+ def recurse(value,i)
84
+ if @leaf.call(value)
85
+ @entries.push({:keys=>@stack.dup, :value=>value})
86
+ else
87
+ value.each {|key,v|
88
+ @stack.push(key)
89
+ recurse(v, i+1)
90
+ @stack.pop
91
+ }
92
+ end
93
+ end
94
+ def visit(value,i)
95
+ if (i < @keys.size - 1)
96
+ value.each {|key,v|
97
+ @stack.push(key)
98
+ visit(v,i+1)
99
+ @stack.pop
100
+ }
101
+ else
102
+ @entries.push(@stack+value)
103
+ end
104
+ end
105
+ # Returns the flattened array. Each entry in the array is an object; each
106
+ # object has attributes corresponding to this flatten operator's keys.
107
+ #
108
+ # @returns an array of elements from the flattened map.
109
+ def array
110
+ @entries=[]
111
+ @stack=[]
112
+ if @leaf
113
+ recurse(@map,0)
114
+ return @entries
115
+ end
116
+ visit(@map,0)
117
+ @entries.map {|stack|
118
+ m={}
119
+ @keys.each_with_index {|k,i|
120
+ v=@stack[i]
121
+ m[k.name]=k.value ? k.value.js_call(self,v) : v
122
+ }
123
+ m
124
+ }
125
+ end
126
+ alias :to_a :array
127
+ end
128
+ end
@@ -37,5 +37,7 @@ require 'rubyvis/layout/hierarchy'
37
37
  require 'rubyvis/layout/treemap'
38
38
  require 'rubyvis/layout/partition'
39
39
  require 'rubyvis/layout/cluster'
40
+ require 'rubyvis/layout/indent'
41
+ require 'rubyvis/layout/pack'
40
42
 
41
43
 
@@ -0,0 +1,81 @@
1
+ module Rubyvis
2
+ class Layout
3
+ # Alias for Rubyvis::Layout::Indent
4
+ def self.Indent
5
+ Rubyvis::Layout::Indent
6
+ end
7
+ # Implements a hierarchical layout using the indent algorithm. This
8
+ # layout implements a node-link diagram where the nodes are presented in
9
+ # preorder traversal, and nodes are indented based on their depth from the
10
+ # root. This technique is used ubiquitously by operating systems to represent
11
+ # file directories; although it requires much vertical space, indented trees
12
+ # allow efficient <i>interactive</i> exploration of trees to find a specific
13
+ # node. In addition they allow rapid scanning of node labels, and multivariate
14
+ # data such as file sizes can be displayed adjacent to the hierarchy.
15
+ #
16
+ # <p>The indent layout can be configured using the <tt>depth</tt> and
17
+ # <tt>breadth</tt> properties, which control the increments in pixel space for
18
+ # each indent and row in the layout. This layout does not support multiple
19
+ # orientations; the root node is rendered in the top-left, while
20
+ # <tt>breadth</tt> is a vertical offset from the top, and <tt>depth</tt> is a
21
+ # horizontal offset from the left.
22
+ #
23
+ # <p>For more details on how to use this layout, see
24
+ # Rubyvis::Layout::Hierarchy
25
+ class Indent < Hierarchy
26
+ @properties=Hierarchy.properties.dup
27
+ # Constructs a new, empty indent layout. Layouts are not typically constructed
28
+ # directly; instead, they are added to an existing panel via
29
+ # Rubyvis::Mark.add
30
+ def initialize
31
+ super
32
+ @link.interpolate("step-after")
33
+ end
34
+
35
+ ##
36
+ # :attr: depth
37
+ # The horizontal offset between different levels of the tree; defaults to 15.
38
+ #
39
+
40
+ ##
41
+ # :attr: breadth
42
+ # The vertical offset between nodes; defaults to 15.
43
+ #
44
+
45
+ attr_accessor_dsl :depth, :breadth
46
+
47
+ # Default properties for indent layouts. By default the depth and breadth
48
+ # offsets are 15 pixels.
49
+ def self.defaults
50
+ Rubyvis::Layout::Indent.new.mark_extend(Rubyvis::Layout::Hierarchy.defaults).
51
+ depth(15).
52
+ breadth(15)
53
+ end
54
+
55
+ def position(n, breadth, depth)
56
+ n.x = @ax + depth * @dspace
57
+ depth+=1
58
+ n.y = @ay + breadth * @bspace
59
+ breadth+=1
60
+ n.mid_angle = 0
61
+ c=n.first_child
62
+ while c
63
+ breadth=position(c,breadth,depth)
64
+ c=c.next_sibling
65
+ end
66
+ breadth;
67
+ end
68
+ private :position
69
+ def build_implied(s)
70
+ return nil if hierarchy_build_implied(s)
71
+ nodes = s.nodes
72
+ @bspace = s.breadth
73
+ @dspace = s.depth
74
+ @ax = 0
75
+ @ay = 0
76
+ position(nodes[0], 1, 1)
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -75,7 +75,22 @@ module Rubyvis
75
75
  # @see Rubyvis::Layout::Rollup
76
76
  class Network < Rubyvis::Layout
77
77
  @properties=Layout.properties.dup
78
- attr_accessor :node, :link, :node_label
78
+ # The node prototype. This prototype is intended to be used with a
79
+ # Dot mark in conjunction with the link prototype.
80
+ attr_accessor :node
81
+ # The link prototype, which renders edges between source nodes and target
82
+ # nodes. This prototype is intended to be used with a Line mark in
83
+ # conjunction with the node prototype.
84
+ attr_accessor :link
85
+ # The node label prototype, which renders the node name adjacent to the node.
86
+ # This prototype is provided as an alternative to using the anchor on the
87
+ # node mark; it is primarily intended to be used with radial node-link
88
+ # layouts, since it provides a convenient mechanism to set the text angle.
89
+ #
90
+ # NOTE FOR PROTOVIS USERS: The original name of method was +label+
91
+ # but it was replaced to not conflict with rubyvis shortcut
92
+ # method Mark.label()
93
+ attr_accessor :node_label
79
94
  attr_accessor :_id
80
95
  def initialize
81
96
  super
@@ -84,9 +99,8 @@ module Rubyvis
84
99
  @link=_link
85
100
  @node_label=_node_label
86
101
  end
87
- # The node prototype. This prototype is intended to be used with a
88
- # Dot mark in conjunction with the link prototype.
89
- def _node
102
+
103
+ def _node #:nodoc:
90
104
  that=self
91
105
  m=Mark.new().
92
106
  data(lambda {that.nodes}).
@@ -97,7 +111,7 @@ module Rubyvis
97
111
  m.parent = self
98
112
  m
99
113
  end
100
- module LinkAdd
114
+ module LinkAdd # :nodoc:
101
115
  attr_accessor :that
102
116
  def add(type)
103
117
  that=@that
@@ -108,11 +122,9 @@ module Rubyvis
108
122
  end
109
123
  end
110
124
 
111
- # The link prototype, which renders edges between source nodes and target
112
- # nodes. This prototype is intended to be used with a Line mark in
113
- # conjunction with the node prototype.
125
+
114
126
 
115
- def _link
127
+ def _link # :nodoc:
116
128
  that=self
117
129
  l=Mark.new().
118
130
  mark_extend(@node).
@@ -125,14 +137,8 @@ module Rubyvis
125
137
  l
126
138
  end
127
139
 
128
- # The node label prototype, which renders the node name adjacent to the node.
129
- # This prototype is provided as an alternative to using the anchor on the
130
- # node mark; it is primarily intended to be used with radial node-link
131
- # layouts, since it provides a convenient mechanism to set the text angle.
132
- #
133
- # NOTE FOR PROTOVIS USERS: The original name of method was +label+
134
- # but it was replaced to not conflict with Mark.label()
135
- def _node_label
140
+
141
+ def _node_label #:nodoc:
136
142
  that=self
137
143
  nl=Mark.new().
138
144
  mark_extend(@node).
@@ -149,12 +155,17 @@ module Rubyvis
149
155
  nl.parent = self
150
156
  nl
151
157
  end
158
+
152
159
  ##
153
- # :class: Node
154
- # Represents a node in a network layout. There is no explicit
155
- # constructor; this class merely serves to document the attributes that are
156
- # used on nodes in network layouts. (Note that hierarchical nodes place
157
- # additional requirements on node representation, vis Rubyvis::Dom::Node
160
+ # :attr: nodes
161
+ #
162
+ # an array of objects representing nodes. Objects in this array must conform to the Rubyvis::Layout::Network::Node interface; which is
163
+ # to say, be careful to avoid naming collisions with automatic attributes such
164
+ # as <tt>index</tt> and <tt>link_degree</tt>. If the nodes property is defined
165
+ # as an array of 'primitives' (objects which doesn't respond to node_value)
166
+ # these primitives are automatically wrapped in an OpenStruct object;
167
+ # the resulting object's <tt>node_value</tt>
168
+ # attribute points to the original primitive value.
158
169
 
159
170
  attr_accessor_dsl [:nodes, lambda {|v|
160
171
  out=[]
@@ -165,6 +176,18 @@ module Rubyvis
165
176
  }
166
177
  out
167
178
  }]
179
+
180
+ ##
181
+ # :attr: links
182
+ #
183
+ # an array of objects representing links. Objects in
184
+ # this array must conform to the Rubyvis::Layout::Network::Link interface; at a
185
+ # minimum, either <tt>source</tt> and <tt>target</tt> indexes or
186
+ # <tt>source_node</tt> and <tt>target_node</tt> references must be set.
187
+ # Note that if the links property is defined after the nodes property,
188
+ # the links can be defined in terms of <tt>self.nodes()</tt>.
189
+
190
+
168
191
  attr_accessor_dsl [:links, lambda {|v|
169
192
  out=[]
170
193
  v.map {|d|
@@ -189,7 +212,7 @@ module Rubyvis
189
212
 
190
213
  # @private Skip evaluating properties if cached. */
191
214
 
192
- def build_properties(s, properties)
215
+ def build_properties(s, properties) # :nodoc:
193
216
  s_id=s._id
194
217
  s_id||=0
195
218
  if (s_id < self._id)
@@ -197,10 +220,10 @@ module Rubyvis
197
220
  end
198
221
  end
199
222
 
200
- def build_implied(s)
223
+ def build_implied(s) # :nodoc:
201
224
  network_build_implied(s)
202
225
  end
203
- def network_build_implied(s)
226
+ def network_build_implied(s) # :nodoc:
204
227
  layout_build_implied(s)
205
228
  return true if (!s._id.nil? and s._id >= self._id)
206
229
  s._id= self._id
@@ -224,8 +247,8 @@ module Rubyvis
224
247
  false
225
248
  end
226
249
 
227
- # Represents a node in a network layout. There is no explicit
228
- # constructor; this class merely serves to document the attributes that are
250
+ # Represents a node in a network layout.
251
+ # This class mostly serves to document the attributes that are
229
252
  # used on nodes in network layouts. (Note that hierarchical nodes place
230
253
  # additional requirements on node representation, vis Rubyvis::Dom::Node.)
231
254
  #
@@ -245,23 +268,19 @@ module Rubyvis
245
268
 
246
269
  # The node name; optional. If present, this attribute will be used to provide
247
270
  # the text for node labels. If not present, the label text will fallback to the
248
- # <tt>nodeValue</tt> attribute.
271
+ # <tt>node_value</tt> attribute.
249
272
  #
250
273
  # @type string
251
274
  attr_accessor :node_name
252
275
 
253
- # The node value; optional. If present, and no <tt>nodeName</tt> attribute is
254
- # present, the node value will be used as the label text. This attribute is
255
- # also automatically populated if the nodes are specified as an array of
256
- # primitives, such as strings or numbers.
257
- #
258
- # @type object
276
+ # The node value; optional. If present, and no <tt>node_name</tt> attribute is present, the node value will be used as the label text.
277
+ # This attribute is also automatically populated if the nodes are specified as an array of 'primitives', such as strings or numbers.
259
278
  attr_accessor :node_value
260
279
  end
261
280
 
262
281
 
263
- # Represents a link in a network layout. There is no explicit
264
- # constructor; this class merely serves to document the attributes that are
282
+ # Represents a link in a network layout.
283
+ # This class mostly serves to document the attributes that are
265
284
  # used on links in network layouts. For hierarchical layouts, this class is
266
285
  # used to represent the parent-child links.
267
286
  #
@@ -277,21 +296,21 @@ module Rubyvis
277
296
  # default value of 1 is used.
278
297
  #
279
298
  # @type number
280
- # @name pv.Layout.Network.Link.prototype.linkValue
281
- #/
299
+
300
+
282
301
  attr_accessor :link_value
283
302
 
284
303
  # The link's source node. If not set, this value will be derived from the
285
304
  # <tt>source</tt> attribute index.
286
305
  #
287
306
  # @type pv.Layout.Network.Node
288
- # @name pv.Layout.Network.Link.prototype.sourceNode
307
+
289
308
  attr_accessor :source_node
290
309
  # The link's target node. If not set, this value will be derived from the
291
310
  # <tt>target</tt> attribute index.
292
311
  #
293
312
  # @type pv.Layout.Network.Node
294
- # @name pv.Layout.Network.Link.prototype.targetNode
313
+
295
314
  attr_accessor :target_node
296
315
  # Alias for <tt>sourceNode</tt>, as expressed by the index of the source node.
297
316
  # This attribute is not populated automatically, but may be used as a more
@@ -299,7 +318,7 @@ module Rubyvis
299
318
  # representation.
300
319
  #
301
320
  # @type number
302
- # @name pv.Layout.Network.Link.prototype.source
321
+
303
322
  attr_accessor :source
304
323
  # Alias for <tt>targetNode</tt>, as expressed by the index of the target node.
305
324
  # This attribute is not populated automatically, but may be used as a more
@@ -307,7 +326,7 @@ module Rubyvis
307
326
  # representation.
308
327
  #
309
328
  # @type number
310
- # @name pv.Layout.Network.Link.prototype.target
329
+
311
330
  attr_accessor :target
312
331
  # Alias for <tt>linkValue</tt>. This attribute is not populated automatically,
313
332
  # but may be used instead of the <tt>linkValue</tt> attribute when specifying
@@ -0,0 +1,333 @@
1
+ module Rubyvis
2
+ class Layout
3
+ # Alias for Rubyvis::Layout::Indent
4
+ def self.Pack
5
+ Rubyvis::Layout::Pack
6
+ end
7
+
8
+ # Implements a hierarchical layout using circle-packing. The meaning of
9
+ # the exported mark prototypes changes slightly in the space-filling
10
+ # implementation:<ul>
11
+ #
12
+ # <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}.
13
+ #
14
+ # <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
15
+ # in the arrangement of the space-filling nodes.
16
+ #
17
+ # <p><li><tt>label</tt> - for rendering node labels; typically a
18
+ # {@link pv.Label}.
19
+ #
20
+ # </ul>The pack layout support dynamic sizing for leaf nodes, if a
21
+ # {@link #size} psuedo-property is specified. The default size function returns
22
+ # 1, causing all leaf nodes to be sized equally, and all internal nodes to be
23
+ # sized by the number of leaf nodes they have as descendants.
24
+ #
25
+ # <p>The size function can be used in conjunction with the order property,
26
+ # which allows the nodes to the sorted by the computed size. Note: for sorting
27
+ # based on other data attributes, simply use the default <tt>null</tt> for the
28
+ # order property, and sort the nodes beforehand using the {@link pv.Dom}
29
+ # operator.
30
+ #
31
+ # <p>For more details on how to use this layout, see
32
+ # {@link pv.Layout.Hierarchy}.
33
+ #
34
+ # @extends pv.Layout.Hierarchy
35
+ # @see <a href="http://portal.acm.org/citation.cfm?id=1124772.1124851"
36
+ # >"Visualization of large hierarchical data by circle packing"</a> by W. Wang,
37
+ # H. Wang, G. Dai, and H. Wang, ACM CHI 2006.
38
+ #/
39
+ class Pack < Hierarchy
40
+ @properties=Hierarchy.properties.dup
41
+ def initialize
42
+ super
43
+ @node.
44
+ shape_radius(lambda {|n| n.radius }).
45
+ stroke_style("rgb(31, 119, 180)").
46
+ fill_style("rgba(31, 119, 180, 0.25)")
47
+
48
+
49
+ @node_label.text_align("center")
50
+
51
+ @link=nil
52
+
53
+ @radius = lambda { 1 }
54
+ end
55
+
56
+
57
+ ##
58
+ # :attr: spacing
59
+ # The spacing parameter; defaults to 1, which provides a little bit of padding
60
+ # between sibling nodes and the enclosing circle. Larger values increase the
61
+ # spacing, by making the sibling nodes smaller; a value of zero makes the leaf
62
+ # nodes as large as possible, with no padding on enclosing circles.
63
+ #
64
+ # @type number
65
+
66
+ ##
67
+ # :attr: order
68
+ # The sibling node order. The default order is <tt>null</tt>, which means to
69
+ # use the sibling order specified by the nodes property as-is. A value of
70
+ # "ascending" will sort siblings in ascending order of size, while "descending"
71
+ # will do the reverse. For sorting based on data attributes other than size,
72
+ # use the default <tt>null</tt> for the order property, and sort the nodes
73
+ # beforehand using the {@link pv.Dom} operator.
74
+ #
75
+ # @see pv.Dom.Node#sort
76
+
77
+
78
+ attr_accessor_dsl :spacing, :order
79
+
80
+ ##
81
+ # Default properties for circle-packing layouts. The default spacing parameter
82
+ # is 1 and the default order is "ascending".
83
+ #
84
+ def self.defaults
85
+ Rubyvis::Layout::Pack.new.mark_extend(Rubyvis::Layout::Hierarchy.defaults).
86
+ spacing(1).
87
+ order("ascending")
88
+ end
89
+
90
+
91
+ # TODO is it possible for spacing to operate in pixel space?
92
+ # Right now it appears to be multiples of the smallest radius.
93
+
94
+ ##
95
+ # Specifies the sizing function. By default, a sizing function is disabled and
96
+ # all nodes are given constant size. The sizing function is invoked for each
97
+ # leaf node in the tree (passed to the constructor).
98
+ #
99
+ # <p>For example, if the tree data structure represents a file system, with
100
+ # files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
101
+ # specify a size function as:
102
+ #
103
+ # <pre> .size(function(d) d.bytes)</pre>
104
+ #
105
+ # As with other properties, a size function may specify additional arguments to
106
+ # access the data associated with the layout and any enclosing panels.
107
+ #
108
+ # @param {function} f the new sizing function.
109
+ # @returns {pv.Layout.Pack} this.
110
+ def size(f)
111
+ if f.is_a? Proc
112
+ @radius=lambda {|*args| Math.sqrt(f.js_apply(self,args))}
113
+ else
114
+ f=Math.sqrt(f)
115
+ @radius=lambda {f}
116
+ end
117
+ self
118
+ end
119
+ ## @private Compute the radii of the leaf nodes. #/
120
+
121
+ def radii(nodes)
122
+ stack=Mark.stack
123
+ stack.unshift(nil)
124
+ nodes.each {|c|
125
+ if !c.first_child
126
+ stack[0]=c
127
+ c.radius = @radius.js_apply(self, stack)
128
+ end
129
+ }
130
+ stack.shift
131
+ end
132
+ def pack_tree(n)
133
+ nodes = []
134
+ c=n.first_child
135
+ while(c)
136
+ c.radius=pack_tree(c) if c.first_child
137
+ c.n=c._p=c
138
+ nodes.push(c)
139
+ c=c.next_sibling
140
+ end
141
+
142
+ # Sort.
143
+ case @s.order
144
+ when "ascending"
145
+ nodes.sort {|a,b| a.radius<=>b.radius}
146
+ when 'descending'
147
+ nodes.sort {|a,b| b.radius<=>a.radius}
148
+ when 'reverse'
149
+ nodes.reverse
150
+ end
151
+
152
+ return pack_circle(nodes)
153
+ end
154
+ def bound(n)
155
+ @x_min = [n.x - n.radius, @x_min].min
156
+ @x_max = [n.x + n.radius, @x_max].max
157
+ @y_min = [n.y - n.radius, @y_min].min
158
+ @y_max = [n.y + n.radius, @y_max].max
159
+ end
160
+ def insert(a,b)
161
+ c = a.n
162
+ a.n = b
163
+ b._p = a
164
+ b.n = c
165
+ c._p = b
166
+ end
167
+ def splice(a, b)
168
+ a.n = b
169
+ b._p = a
170
+ end
171
+ def intersects(a, b)
172
+ dx = b.x - a.x
173
+ dy = b.y - a.y
174
+ dr = a.radius + b.radius
175
+ (dr * dr - dx * dx - dy * dy) > 0.001 # within epsilon
176
+ end
177
+
178
+ ## @private #/
179
+ def place(a, b, c)
180
+ da = b.radius + c.radius
181
+ db = a.radius + c.radius
182
+ dx = b.x - a.x
183
+ dy = b.y - a.y
184
+ dc = Math.sqrt(dx * dx + dy * dy)
185
+ cos = (db * db + dc * dc - da * da) / (2.0 * db * dc)
186
+
187
+ theta = Math.acos(cos)
188
+ x = cos * db
189
+ h = Math.sin(theta) * db
190
+ dx /= dc
191
+ dy /= dc
192
+ c.x = a.x + x * dx + h * dy
193
+ c.y = a.y + x * dy - h * dx
194
+ end
195
+
196
+ # @private #/
197
+ def transform(n, x, y, k)
198
+ c=n.first_child
199
+ while(c) do
200
+ c.x += n.x
201
+ c.y += n.y
202
+ transform(c, x, y, k)
203
+ c=c.next_sibling
204
+ end
205
+ n.x = x + k * n.x
206
+ n.y = y + k * n.y
207
+ n.radius *= k
208
+ n.mid_angle=0 # Undefined on protovis
209
+ end
210
+
211
+
212
+ def pack_circle(nodes)
213
+ @x_min = Infinity
214
+ @x_max = -Infinity
215
+ @y_min = Infinity
216
+ @y_max = -Infinity
217
+ a=b=c=j=k=nil
218
+
219
+
220
+ # Create first node.
221
+ a = nodes[0];
222
+ a.x = -a.radius
223
+ a.y = 0
224
+ bound(a)
225
+
226
+ # Create second node. #/
227
+ if (nodes.size > 1)
228
+ b = nodes[1]
229
+ b.x = b.radius
230
+ b.y = 0
231
+ bound(b)
232
+
233
+ # Create third node and build chain.
234
+ if (nodes.size > 2)
235
+ c = nodes[2]
236
+ place(a, b, c)
237
+ bound(c)
238
+ insert(a, c)
239
+ a._p = c
240
+ insert(c, b)
241
+ b = a.n
242
+
243
+ # Now iterate through the rest.
244
+ i=3
245
+ while(i<nodes.size) do
246
+ c=nodes[i]
247
+ place(a, b, c)
248
+
249
+ # Search for the closest intersection. #/
250
+ isect = 0
251
+ s1 = 1
252
+ s2 = 1
253
+
254
+ j=b.n
255
+ while(j!=b) do
256
+ if (intersects(j,c))
257
+ isect=1;
258
+ break;
259
+ end
260
+ j=j.n
261
+ s1+=1
262
+ end
263
+
264
+ if isect==1
265
+ k=a._p
266
+ while(k!=k._p) do
267
+ if(intersects(k,c))
268
+ if(s2<s1)
269
+ isect=-1
270
+ k=j
271
+ end
272
+ break
273
+ end
274
+ k=k._p
275
+ s2+=1
276
+ end
277
+ end
278
+
279
+
280
+ # Update node chain. #/
281
+ if (isect == 0)
282
+ insert(a, c)
283
+ b = c
284
+ bound(c)
285
+ elsif (isect > 0)
286
+ splice(a, j)
287
+ b = j
288
+ i-=1
289
+ elsif (isect < 0)
290
+ splice(j, b)
291
+ a = j
292
+ i-=1
293
+ end
294
+ i+=1
295
+ end
296
+
297
+
298
+ end
299
+ end
300
+
301
+ # Re-center the circles and return the encompassing radius. #/
302
+ cx = (@x_min + @x_max) / 2.0
303
+ cy = (@y_min + @y_max) / 2.0
304
+ cr = 0
305
+ nodes.each do |n|
306
+ n.x -= cx
307
+ n.y -= cy
308
+ cr = [cr, n.radius + Math.sqrt(n.x * n.x + n.y * n.y)].max
309
+ end
310
+ cr + @s.spacing
311
+ end
312
+
313
+
314
+ def build_implied(s)
315
+ return nil if hierarchy_build_implied(s)
316
+ @s=s
317
+ nodes = s.nodes
318
+ root = nodes[0]
319
+ radii(nodes)
320
+
321
+ # Recursively compute the layout. #/
322
+ root.x = 0
323
+ root.y = 0
324
+ root.radius = pack_tree(root)
325
+
326
+ w = self.width
327
+ h = self.height
328
+ k = 1.0 / [2.0 * root.radius / w, 2.0 * root.radius / h].max
329
+ transform(root, w / 2.0, h / 2.0, k)
330
+ end
331
+ end
332
+ end
333
+ end
@@ -460,8 +460,12 @@ module Rubyvis
460
460
  end
461
461
  end
462
462
  # Execute a block using this mark as a reference
463
+ # <b>Example</b>
464
+ # bar.execute |b|
465
+ # b.width 10
466
+ # b.add(Rubyvis::Label)
467
+ # end
463
468
  def execute(&block)
464
-
465
469
  block.arity<1 ? self.instance_eval(&block) : block.call(self)
466
470
  end
467
471
  # The mark type; a lower name. The type name controls rendering
@@ -486,7 +490,9 @@ module Rubyvis
486
490
  # or its prototype, and so on. The prototype mark need not be the same
487
491
  # type of mark as this mark. (Note that for inheritance to be useful,
488
492
  # properties with the same name on different mark types should
489
- # have equivalent meaning.)
493
+ # have equivalent meaning).
494
+ # On protovis, this method is called +extend+, but it should be changed
495
+ # because clashed with native +extend+ method
490
496
  def mark_extend(proto)
491
497
  @proto=proto
492
498
  @target=proto.target
@@ -212,5 +212,33 @@ class Rubyvis::Mark
212
212
  # See Mark for examples of use
213
213
  mark_method :layout_cluster, Rubyvis::Layout::Cluster
214
214
 
215
+ ##
216
+ # :method: layout_indent(opts,&block)
217
+ #
218
+ # Adds a Layout::Indent to current mark.
219
+ #
220
+ # If a block is provided, the context will be defined differently if
221
+ # parameter is provided
222
+ # * Without parameter: block executed inside context of new mark
223
+ # * With paramenter: block executed inside context of current mark.
224
+ # Paramenter references new mark
225
+ #
226
+ # See Mark for examples of use
227
+ mark_method :layout_indent, Rubyvis::Layout::Indent
228
+
229
+ ##
230
+ # :method: layout_pack(opts,&block)
231
+ #
232
+ # Adds a Layout::Pack to current mark.
233
+ #
234
+ # If a block is provided, the context will be defined differently if
235
+ # parameter is provided
236
+ # * Without parameter: block executed inside context of new mark
237
+ # * With paramenter: block executed inside context of current mark.
238
+ # Paramenter references new mark
239
+ #
240
+ # See Mark for examples of use
241
+ mark_method :layout_pack, Rubyvis::Layout::Pack
242
+
215
243
 
216
244
  end
@@ -86,14 +86,40 @@ module Rubyvis
86
86
 
87
87
  def self.append(e,scenes,index)
88
88
  e._scene=OpenStruct.new({:scenes=>scenes, :index=>index})
89
- #e=self.title
89
+ e=self.title(e,scenes[index])
90
90
 
91
91
  if(!e.parent)
92
92
  scenes._g.add_element(e)
93
93
  end
94
94
  return e.next_sibling_node
95
95
  end
96
-
96
+
97
+ # Applies a title tooltip to the specified element <tt>e</tt>, using the
98
+ # <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that
99
+ # this implementation does not create an SVG <tt>title</tt> element as a child
100
+ # of <tt>e</tt>; although this is the recommended standard, it is only
101
+ # supported in Opera. Instead, an anchor element is created around the element
102
+ # <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly.
103
+ #
104
+ # @param e an SVG element.
105
+ # @param s a scene node.
106
+
107
+ def self.title(e,s)
108
+ a = e.parent
109
+ a=nil if (a and (a.tag_name != "a"))
110
+ if (s.title)
111
+ if (!a)
112
+ a = self.create("a")
113
+ e.parent.replace_child(a, e) if (e.parent)
114
+ a.add_element(e)
115
+ end
116
+ a.add_attribute('xlink:title',s.title)
117
+ return a;
118
+ end
119
+ a.parent_node.replace_child(e, a) if (a)
120
+ e
121
+ end
122
+
97
123
  def self.expect(e, type, attributes, style=nil)
98
124
 
99
125
  if (e)
@@ -8,7 +8,7 @@ module Rubyvis
8
8
  end
9
9
  include Enumerable
10
10
  attr_accessor :visible
11
- attr_accessor :mark, :type, :child_index, :parent, :parent_index, :target, :defs, :data, :antialias, :line_width, :fill_style, :overflow, :width, :height, :top, :bottom, :left, :right, :title, :reverse, :stroke_style, :transform, :canvas, :_g, :events, :cursor, :children, :id, :segmented, :interpolate, :tension, :name, :text_baseline, :text_align, :text, :font, :text_angle, :text_style, :text_margin, :text_decoration, :text_shadow, :line_join, :eccentricity, :shape_size, :shape, :shape_angle, :shape_radius, :start_angle, :end_angle, :angle, :inner_radius, :outer_radius, :layers, :orient, :offset, :order,:url, :image_width, :image_height, :image, :_id, :nodes, :round, :links, :padding_left, :padding_right, :padding_top, :padding_bottom, :mode, :group
11
+ attr_accessor :mark, :type, :child_index, :parent, :parent_index, :target, :defs, :data, :antialias, :line_width, :fill_style, :overflow, :width, :height, :top, :bottom, :left, :right, :title, :reverse, :stroke_style, :transform, :canvas, :_g, :events, :cursor, :children, :id, :segmented, :interpolate, :tension, :name, :text_baseline, :text_align, :text, :font, :text_angle, :text_style, :text_margin, :text_decoration, :text_shadow, :line_join, :eccentricity, :shape_size, :shape, :shape_angle, :shape_radius, :start_angle, :end_angle, :angle, :inner_radius, :outer_radius, :layers, :orient, :offset, :order,:url, :image_width, :image_height, :image, :_id, :nodes, :round, :links, :padding_left, :padding_right, :padding_top, :padding_bottom, :mode, :group, :depth, :breadth, :spacing
12
12
 
13
13
  def []=(v,i)
14
14
  if v.is_a? Numeric
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyvis
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 2
10
- version: 0.3.2
9
+ - 3
10
+ version: 0.3.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Claudio Bustos
@@ -152,8 +152,10 @@ files:
152
152
  - examples/bar_column_chart.rb
153
153
  - examples/barley/barley.rb
154
154
  - examples/barley/barley_data.rb
155
+ - examples/bubble_charts.rb
155
156
  - examples/cars/cars.rb
156
157
  - examples/cars/cars_data.rb
158
+ - examples/circle_packing.rb
157
159
  - examples/crimea/crimea_data.rb
158
160
  - examples/crimea/crimea_grouped_bar.rb
159
161
  - examples/crimea/crimea_line.rb
@@ -165,6 +167,7 @@ files:
165
167
  - examples/grouped_charts.rb
166
168
  - examples/icicle.rb
167
169
  - examples/image.rb
170
+ - examples/indent.rb
168
171
  - examples/line.rb
169
172
  - examples/line_and_step.rb
170
173
  - examples/line_interpolation.rb
@@ -181,6 +184,7 @@ files:
181
184
  - lib/rubyvis/color/color.rb
182
185
  - lib/rubyvis/color/colors.rb
183
186
  - lib/rubyvis/dom.rb
187
+ - lib/rubyvis/flatten.rb
184
188
  - lib/rubyvis/format.rb
185
189
  - lib/rubyvis/format/date.rb
186
190
  - lib/rubyvis/format/number.rb
@@ -190,7 +194,9 @@ files:
190
194
  - lib/rubyvis/layout.rb
191
195
  - lib/rubyvis/layout/cluster.rb
192
196
  - lib/rubyvis/layout/hierarchy.rb
197
+ - lib/rubyvis/layout/indent.rb
193
198
  - lib/rubyvis/layout/network.rb
199
+ - lib/rubyvis/layout/pack.rb
194
200
  - lib/rubyvis/layout/partition.rb
195
201
  - lib/rubyvis/layout/stack.rb
196
202
  - lib/rubyvis/layout/treemap.rb
metadata.gz.sig CHANGED
Binary file