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
|
module Rubyvis
class Layout
# Alias for Rubyvis::Layout::Cluster
def self.Cluster
Rubyvis::Layout::Cluster
end
# Implements a hierarchical layout using the cluster (or dendrogram)
# algorithm. This layout provides both node-link and space-filling
# implementations of cluster diagrams. In many ways it is similar to
# {@link pv.Layout.Partition}, except that leaf nodes are positioned at maximum
# depth, and the depth of internal nodes is based on their distance from their
# deepest descendant, rather than their distance from the root.
#
# <p>The cluster layout supports a "group" property, which if true causes
# siblings to be positioned closer together than unrelated nodes at the same
# depth. Unlike the partition layout, this layout does not support dynamic
# sizing for leaf nodes; all leaf nodes are the same size.
#
# <p>For more details on how to use this layout, see
# Rubyvis::Layout::Hierarchy.
#
# @see pv.Layout.Cluster.Fill
# @extends pv.Layout.Hierarchy
class Cluster < Hierarchy
include NodeLink
attr_accessor :interpolate
@properties=Hierarchy.properties.dup
# Constructs a new, empty cluster layout. Layouts are not typically
# constructed directly; instead, they are added to an existing panel via
# {@link pv.Mark#add}.
attr_accessor :interpolate
def initialize
super
@interpolate=nil
that=self
## @private Cache layout state to optimize properties. #/
@link.interpolate {that.interpolate}
end
##
# :attr: group
# The group parameter; defaults to 0, disabling grouping of siblings. If this
# parameter is set to a positive number (or true, which is equivalent to 1),
# then additional space will be allotted between sibling groups. In other
# words, siblings (nodes that share the same parent) will be positioned more
# closely than nodes at the same depth that do not share a parent.
#
# @type number
##
# :attr:orient
#
# The orientation. The default orientation is "top", which means that the root
# node is placed on the top edge, leaf nodes appear on the bottom edge, and
# internal nodes are in-between. 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, with the root at the center.</ul>
#
# @type string
##
# :attr: inner_radius
#
# The inner radius; defaults to 0. This property applies only to radial
# orientations, and can be used to compress the layout radially. Note that for
# the node-link implementation, the root node is always at the center,
# regardless of the value of this property; this property only affects internal
# and leaf nodes. For the space-filling implementation, a non-zero value of
# this property will result in the root node represented as a ring rather than
# a circle.
#
# @type number
##
# :attr: outer_radius
# The outer radius; defaults to fill the containing panel, based on the height
# and width of the layout. If the layout has no height and width specified, it
# will extend to fill the enclosing panel.
#
# @type number
attr_accessor_dsl :group, :orient, :inner_radius, :outer_radius
# Defaults for cluster layouts. The default group parameter is 0 and the
# default orientation is "top".
#
# @type pv.Layout.Cluster
def self.defaults
Cluster.new.mark_extend(Hierarchy.defaults).
group(0).
orient("top")
end
def build_implied(s)
cluster_build_implied(s)
end
def cluster_build_implied(s)
@interpolate=case s.orient
when /^(top|bottom)$/
'step-before'
when /^(left|right)$/
'step-after'
else
'linear'
end
return nil if hierarchy_build_implied(s)
root = s.nodes[0]
group = s.group
breadth =nil
depth = nil
leaf_count = 0
leaf_index = 0.5 - group / 2.0
# Count the leaf nodes and compute the depth of descendants. #/
par = nil
root.visit_after {|n,i|
#puts "#{n.node_value} #{i}"
if (n.first_child)
n.depth = 1 + Rubyvis.max(n.child_nodes, lambda {|nn| nn.depth })
else
if (group!=0 and (par != n.parent_node))
par = n.parent_node
leaf_count += group
end
leaf_count+=1
n.depth = 0
end
}
breadth = 1.0 / leaf_count
depth = 1.0 / root.depth
# Compute the unit breadth and depth of each node. #/
par = nil
root.visit_after {|n,i|
if (n.first_child)
n.breadth = Rubyvis.mean(n.child_nodes, lambda {|nn| nn.breadth })
else
if (group!=0 and (par != n.parent_node))
par = n.parent_node
leaf_index += group
end
n.breadth = breadth * leaf_index
leaf_index+=1
end
n.depth = 1 - n.depth * depth
}
# Compute breadth and depth ranges for space-filling layouts. #/
root.visit_after {|n,i|
n.min_breadth = n.first_child ? n.first_child.min_breadth : (n.breadth - breadth / 2.0)
n.max_breadth = n.first_child ? n.last_child.max_breadth : (n.breadth + breadth / 2.0)
}
root.visit_before {|n,i|
n.min_depth = n.parent_node ? n.parent_node.max_depth : 0
n.max_depth = n.parent_node ? (n.depth + root.depth) : (n.min_depth + 2 * root.depth)
}
root.min_depth = -depth
node_link_build_implied(s)
false
end
# Constructs a new, empty space-filling cluster layout. Layouts are not
# typically constructed directly; instead, they are added to an existing panel
# via {@link pv.Mark#add}.
#
# @class A variant of cluster layout that is space-filling. The meaning of the
# exported mark prototypes changes slightly in the space-filling
# implementation:<ul>
#
# <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar} for
# non-radial orientations, and a {@link pv.Wedge} for radial orientations.
#
# <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
# in the arrangement of the space-filling nodes.
#
# <p><li><tt>label</tt> - for rendering node labels; typically a
# Rubyvis::Label.
#
# </ul>For more details on how to use this layout, see
# {@link pv.Layout.Cluster}.
#
# @extends pv.Layout.Cluster
class Fill < Cluster
include Hierarchy::Fill
@properties=Cluster.properties.dup
def initialize
super
fill_constructor
end
def build_implied(s)
return nil if cluster_build_implied(s)
fill_build_implied(s)
end
end
end
end
end
|