1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
|
module Rubyvis
class Layout
# Alias for Rubyvis::Layout::Network
def self.Network
Rubyvis::Layout::Network
end
# Represents an abstract layout for network diagrams. This class
# provides the basic structure for both node-link diagrams (such as
# force-directed graph layout) and space-filling network diagrams (such as
# sunbursts and treemaps). Note that "network" here is a general term that
# includes hierarchical structures; a tree is represented using links from
# child to parent.
#
# Network layouts require the graph data structure to be defined using two
# properties:<ul>
#
# <li><tt>nodes</tt> - an array of objects representing nodes. Objects in this array must conform to the Rubyvis::Layout::Network::Node interface; which is
# to say, be careful to avoid naming collisions with automatic attributes such
# as <tt>index</tt> and <tt>link_degree</tt>.
# If the nodes property is defined
# as an array of primitives, such as numbers or strings, these primitives are
# automatically wrapped in an object; the resulting object's <tt>node_value</tt>
# attribute points to the original primitive value.
#
# <p><li><tt>links</tt> - an array of objects representing links. Objects in
# this array must conform to the Rubyvis::Layout::Network::Link interface; at a minimum, either <tt>source</tt> and <tt>target</tt> indexes or
# <tt>source_node</tt> and <tt>target_node</tt> references must be set. Note that
# if the links property is defined after the nodes property, the links can be defined in terms of <tt>this.nodes()</tt>.
#
# </ul>
#
# <p>Three standard mark prototypes are provided:<ul>
#
# <li><tt>node</tt> - for rendering nodes; typically a Rubyvis::Dot. The node mark is added directly to the layout, with the data property defined via the layout's <tt>nodes</tt> property. Properties such as <tt>stroke_style</tt> and <tt>fillStyle</tt> can be overridden to compute properties from node data dynamically.
#
# <p><li><tt>link</tt> - for rendering links; typically a Rubyvis::Line. The
# link mark is added to a child panel, whose data property is defined as
# layout's <tt>links</tt> property. The link's data property is then a
# two-element array of the source node and target node. Thus, poperties such as
# <tt>stroke_style</tt> and <tt>fill_style</tt> can be overridden to compute
# properties from either the node data (the first argument) or the link data
# (the second argument; the parent panel data) dynamically.
#
# <p><li><tt>node_label</tt> - for rendering node labels; typically a
# Rubyvis::Label. The label mark is added directly to the layout, with the
# data property defined via the layout's <tt>nodes</tt> property. Properties
# such as <tt>strokeStyle</tt> and <tt>fillStyle</tt> can be overridden to
# compute properties from node data dynamically.
#
# </ul>Note that some network implementations may not support all three
# standard mark prototypes; for example, space-filling hierarchical layouts
# typically do not use a <tt>link</tt> prototype, as the parent-child links are
# implied by the structure of the space-filling <tt>node</tt> marks. Check the
# specific network layout for implementation details.
#
# <p>Network layout properties, including <tt>nodes</tt> and <tt>links</tt>,
# are typically cached rather than re-evaluated with every call to render. This
# is a performance optimization, as network layout algorithms can be
# expensive. If the network structure changes, call {@link #reset} to clear the
# cache before rendering. Note that although the network layout properties are
# cached, child mark properties, such as the marks used to render the nodes and
# links, <i>are not</i>. Therefore, non-structural changes to the network
# layout, such as changing the color of a mark on mouseover, do not need to
# reset the layout.
#
# @see Rubyvis::Layout::Hierarchy
# @see Rubyvis::Layout::Force
# @see Rubyvis::Layout::Matrix
# @see Rubyvis::Layout::Arc
# @see Rubyvis::Layout::Rollup
class Network < Rubyvis::Layout
@properties=Layout.properties.dup
# The node prototype. This prototype is intended to be used with a
# Dot mark in conjunction with the link prototype.
attr_accessor :node
# The link prototype, which renders edges between source nodes and target
# nodes. This prototype is intended to be used with a Line mark in
# conjunction with the node prototype.
attr_accessor :link
# The node label prototype, which renders the node name adjacent to the node.
# This prototype is provided as an alternative to using the anchor on the
# node mark; it is primarily intended to be used with radial node-link
# layouts, since it provides a convenient mechanism to set the text angle.
#
# NOTE FOR PROTOVIS USERS: The original name of method was +label+
# but it was replaced to not conflict with rubyvis shortcut
# method Mark.label()
attr_accessor :node_label
attr_accessor :_id
def initialize
super
@_id=Rubyvis.id()
@node=_node
@link=_link
@node_label=_node_label
end
def _node #:nodoc:
that=self
m=Mark.new().
data(lambda {that.nodes}).
stroke_style("#1f77b4").
fill_style("#fff").
left(lambda {|n| n.x }).
top(lambda {|n| n.y })
m.parent = self
m
end
module LinkAdd # :nodoc:
attr_accessor :that
def add(type)
that=@that
return that.add(Rubyvis::Panel).
data(lambda {that.links}).
add(type).
mark_extend(self)
end
end
def _link # :nodoc:
#that=self
l=Mark.new().
mark_extend(@node).
data(lambda {|d| [d.source_node, d.target_node] }).
fill_style(nil).
line_width(lambda {|d,_p| _p.link_value * 1.5 }).
stroke_style("rgba(0,0,0,.2)")
l.extend LinkAdd
l.that=self
l
end
def _node_label #:nodoc:
#that=self
nl=Mark.new().
mark_extend(@node).
text_margin(7).
text_baseline("middle").
text(lambda {|n| n.node_name ? n.node_name : n.node_value }).
text_angle(lambda {|n|
a = n.mid_angle
Rubyvis::Wedge.upright(a) ? a : (a + Math::PI)
}).
text_align(lambda {|n|
Rubyvis::Wedge.upright(n.mid_angle) ? "left" : "right"
})
nl.parent = self
nl
end
##
# :attr: nodes
#
# an array of objects representing nodes. Objects in this array must conform to the Rubyvis::Layout::Network::Node interface; which is
# to say, be careful to avoid naming collisions with automatic attributes such
# as <tt>index</tt> and <tt>link_degree</tt>. If the nodes property is defined
# as an array of 'primitives' (objects which doesn't respond to node_value)
# these primitives are automatically wrapped in an OpenStruct object;
# the resulting object's <tt>node_value</tt>
# attribute points to the original primitive value.
attr_accessor_dsl [:nodes, lambda {|v|
out=[]
v.each_with_index {|d,i|
d=OpenStruct.new({:node_value=>d}) unless d.respond_to? :node_value
d.index=i
out.push(d)
}
out
}]
##
# :attr: links
#
# an array of objects representing links. Objects in
# this array must conform to the Rubyvis::Layout::Network::Link interface; at a
# minimum, either <tt>source</tt> and <tt>target</tt> indexes or
# <tt>source_node</tt> and <tt>target_node</tt> references must be set.
# Note that if the links property is defined after the nodes property,
# the links can be defined in terms of <tt>self.nodes()</tt>.
attr_accessor_dsl [:links, lambda {|v|
# out=[]
v.map {|d|
if !d.link_value.is_a? Numeric
d.link_value = !d.value.is_a?(Numeric) ? 1 : d.value
end
d
}
}]
# Resets the cache, such that changes to layout property definitions will be
# visible on subsequent render. Unlike normal marks (and normal layouts),
# properties associated with network layouts are not automatically re-evaluated
# on render; the properties are cached, and any expensive layout algorithms are
# only run after the layout is explicitly reset.
#
# (@returns Rubyvis::Layout::Network) self
def reset
self._id=Rubyvis.id()
self
end
# @private Skip evaluating properties if cached. */
def build_properties(s, properties) # :nodoc:
s_id=s._id
s_id||=0
if (s_id < self._id)
layout_build_properties(s,properties)
end
end
def build_implied(s) # :nodoc:
network_build_implied(s)
end
def network_build_implied(s) # :nodoc:
layout_build_implied(s)
return true if (!s._id.nil? and s._id >= self._id)
s._id= self._id
s.nodes.each do |d|
d.link_degree=0
end
s.links.each do |d|
v=d.link_value
if !d.source_node
d.source_node=s.nodes[d.source]
end
d.source_node.link_degree+=v
if !d.target_node
d.target_node=s.nodes[d.target]
end
d.target_node.link_degree+=v
end
false
end
# Represents a node in a network layout.
# This class mostly serves to document the attributes that are
# used on nodes in network layouts. (Note that hierarchical nodes place
# additional requirements on node representation, vis Rubyvis::Dom::Node.)
#
class Node
# The node index, zero-based. This attribute is populated automatically based
# on the index in the array returned by the <tt>nodes</tt> property.
#
# @type number
#/
attr_accessor :index
# The link degree; the sum of link values for all incoming and outgoing links.
# This attribute is populated automatically.
#
# @type number
attr_accessor :link_degree
# The node name; optional. If present, this attribute will be used to provide
# the text for node labels. If not present, the label text will fallback to the
# <tt>node_value</tt> attribute.
#
# @type string
attr_accessor :node_name
# 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.
# This attribute is also automatically populated if the nodes are specified as an array of 'primitives', such as strings or numbers.
attr_accessor :node_value
end
# Represents a link in a network layout.
# This class mostly serves to document the attributes that are
# used on links in network layouts. For hierarchical layouts, this class is
# used to represent the parent-child links.
#
# @see pv.Layout.Network
# @name pv.Layout.Network.Link
class Link
def initialize(opts)
@source_node=opts.delete :source_node
@target_node=opts.delete :target_node
@link_value=opts.delete :link_value
end
# The link value, or weight; optional. If not specified (or not a number), the
# default value of 1 is used.
#
# @type number
attr_accessor :link_value
# The link's source node. If not set, this value will be derived from the
# <tt>source</tt> attribute index.
#
# @type pv.Layout.Network.Node
attr_accessor :source_node
# The link's target node. If not set, this value will be derived from the
# <tt>target</tt> attribute index.
#
# @type pv.Layout.Network.Node
attr_accessor :target_node
# Alias for <tt>sourceNode</tt>, as expressed by the index of the source node.
# This attribute is not populated automatically, but may be used as a more
# convenient identification of the link's source, for example in a static JSON
# representation.
#
# @type number
attr_accessor :source
# Alias for <tt>targetNode</tt>, as expressed by the index of the target node.
# This attribute is not populated automatically, but may be used as a more
# convenient identification of the link's target, for example in a static JSON
# representation.
#
# @type number
attr_accessor :target
# Alias for <tt>linkValue</tt>. This attribute is not populated automatically,
# but may be used instead of the <tt>linkValue</tt> attribute when specifying
# links.
#
# @type number
attr_accessor :value
end
end
end
end
|