module Rubyvis
class Layout
# Alias for Rubyvis::Layout::Arc
def self.Arc
Rubyvis::Layout::Arc
end
# Implements a layout for arc diagrams. An arc diagram is a network
# visualization with a one-dimensional layout of nodes, using circular arcs to
# render links between nodes. For undirected networks, arcs are rendering on a
# single side; this makes arc diagrams useful as annotations to other
# two-dimensional network layouts, such as rollup, matrix or table layouts. For
# directed networks, links in opposite directions can be rendered on opposite
# sides using directed(true).
#
#
Arc layouts are particularly sensitive to node ordering; for best results,
# order the nodes such that related nodes are close to each other. A poor
# (e.g., random) order may result in large arcs with crossovers that impede
# visual processing. A future improvement to this layout may include automatic reordering using, e.g., spectral graph layout or simulated annealing.
#
# This visualization technique is related to that developed by M. Wattenberg, {Arc Diagrams: Visualizing Structure in Strings}[http://www.research.ibm.com/visual/papers/arc-diagrams.pdf] in IEEE InfoVis, 2002.
# However, this implementation is limited to simple node-link networks, as
# opposed to structures with hierarchical self-similarity (such as strings).
# As with other network layouts, three mark prototypes are provided:
#
# - node - for rendering nodes; typically a Rubyvis::Dot
#
- link - for rendering links; typically a Rubyvis::Line
#
- node_label - for rendering node labels; typically a Rubyvis::Label
#
#
# For more details on how this layout is structured and can be customized,
# see Rubyvis::Layout::Network
class Arc < Network
@properties=Network.properties.dup
attr_accessor :_interpolate # :nodoc:
attr_accessor :_directed # :nodoc:
attr_accessor :_reverse # :nodoc:
def initialize
super
@_interpolate=nil # cached interpolate
@_directed=nil # cached directed
@_reverse=nil # cached reverse
@_sort=nil
that=self
@link.data(lambda {|_p|
s=_p.source_node;t=_p.target_node
that._reverse != (that._directed or (s.breadth < t.breadth)) ? [s, t] : [t, s]
}).interpolate(lambda{ that._interpolate})
end
def build_implied(s) # :nodoc:
return true if network_build_implied(s)
# Cached
nodes = s.nodes
orient = s.orient
sort = @_sort
index = Rubyvis.range(nodes.size)
w = s.width
h = s.height
r = [w,h].min / 2.0
# /* Sort the nodes. */
if (sort)
index.sort! {|a,b| sort.call(nodes[a],nodes[b])}
end
#/** @private Returns the mid-angle, given the breadth. */
mid_angle=lambda do |b|
case orient
when "top"
-Math::PI / 2.0
when "bottom"
Math::PI / 2.0
when "left"
Math::PI
when "right"
0
when "radial"
(b - 0.25) * 2.0 * Math::PI
end
end
# /** @private Returns the x-position, given the breadth. */
x= lambda do |b|
case orient
when "top"
b * w
when "bottom"
b * w
when "left"
0;
when "right"
w;
when "radial"
w / 2.0 + r * Math.cos(mid_angle.call(b))
end
end
# /** @private Returns the y-position, given the breadth. */
y=lambda do |b|
case orient
when "top"
0;
when "bottom"
h;
when "left"
b* h
when "right"
b * h
when "radial"
h / 2.0 + r * Math.sin(mid_angle.call(b))
end
end
#/* Populate the x, y and mid-angle attributes. */
nodes.each_with_index do |nod, i|
n=nodes[index[i]]
n.breadth=(i+0.5) / nodes.size
b=n.breadth
n.x=x[b]
n.y=y[b]
n.mid_angle=mid_angle[b]
end
@_directed = s.directed
@_interpolate = s.orient == "radial" ? "linear" : "polar"
@_reverse = s.orient == "right" or s.orient == "top"
end
##
# :attr: orient
# The orientation. The default orientation is "bottom", which means that nodes will be positioned from bottom-to-top in the order they are specified in the
# nodes property. The following orientations are supported:
#
# - left - left-to-right.
#
- right - right-to-left.
#
- top - top-to-bottom.
#
- bottom - bottom-to-top.
#
- radial - radially, starting at 12 o'clock and proceeding clockwise.
##
# :attr: directed
# Whether this arc digram is directed (bidirectional); only applies to
# non-radial orientations. By default, arc digrams are undirected, such that
# all arcs appear on one side. If the arc digram is directed, then forward
# links are drawn on the conventional side (the same as as undirected
# links--right, left, bottom and top for left, right, top and bottom,
# respectively), while reverse links are drawn on the opposite side.
attr_accessor_dsl :orient, :directed
# Default properties for arc layouts. By default, the orientation is "bottom".
def self.defaults
Arc.new.mark_extend(Network.defaults).
orient("bottom")
end
# Specifies an optional sort function. The sort function follows the same
# comparator contract required by Rubyvis::Dom::Node.sort(). Specifying a sort
# function provides an alternative to sort the nodes as they are specified by
# the nodes property; the main advantage of doing this is that the
# comparator function can access implicit fields populated by the network
# layout, such as the link_degree.
#
# Note that arc diagrams are particularly sensitive to order. This is
# referred to as the seriation problem, and many different techniques exist to
# find good node orders that emphasize clusters, such as spectral layout and
# simulated annealing.
def sort(f=nil,&block)
f||=block
@_sort=f
self
end
end
end
end