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
|
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 <tt>directed(true)</tt>.
#
# <p>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:<ul>
#
# <li><tt>node</tt> - for rendering nodes; typically a Rubyvis::Dot
# <li><tt>link</tt> - for rendering links; typically a Rubyvis::Line
# <li><tt>node_label</tt> - for rendering node labels; typically a Rubyvis::Label
#
# </ul>
# 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
# <tt>nodes</tt> property. The following orientations are supported:<ul>
#
# <li>left - left-to-right.
# <li>right - right-to-left.
# <li>top - top-to-bottom.
# <li>bottom - bottom-to-top.
# <li>radial - radially, starting at 12 o'clock and proceeding clockwise.</ul>
##
# :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 <tt>nodes</tt> property; the main advantage of doing this is that the
# comparator function can access implicit fields populated by the network
# layout, such as the <tt>link_degree</tt>.
#
# <p>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
|