File: arc.rb

package info (click to toggle)
ruby-rubyvis 0.6.1%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 1,808 kB
  • ctags: 679
  • sloc: ruby: 11,114; makefile: 2
file content (183 lines) | stat: -rw-r--r-- 6,775 bytes parent folder | download | duplicates (3)
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