gratr 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Grater.xcf +0 -0
  2. data/README +328 -0
  3. data/Rakefile +220 -0
  4. data/examples/graph_self.rb +54 -0
  5. data/examples/module_graph.jpg +0 -0
  6. data/examples/module_graph.rb +12 -0
  7. data/examples/self_graph.jpg +0 -0
  8. data/examples/visualize.jpg +0 -0
  9. data/examples/visualize.rb +8 -0
  10. data/install.rb +49 -0
  11. data/lib/gratr.rb +33 -0
  12. data/lib/gratr/adjacency_graph.rb +230 -0
  13. data/lib/gratr/base.rb +34 -0
  14. data/lib/gratr/biconnected.rb +116 -0
  15. data/lib/gratr/chinese_postman.rb +123 -0
  16. data/lib/gratr/common.rb +73 -0
  17. data/lib/gratr/comparability.rb +92 -0
  18. data/lib/gratr/digraph.rb +113 -0
  19. data/lib/gratr/digraph_distance.rb +185 -0
  20. data/lib/gratr/dot.rb +90 -0
  21. data/lib/gratr/edge.rb +145 -0
  22. data/lib/gratr/graph.rb +315 -0
  23. data/lib/gratr/graph_api.rb +82 -0
  24. data/lib/gratr/import.rb +44 -0
  25. data/lib/gratr/labels.rb +103 -0
  26. data/lib/gratr/maximum_flow.rb +107 -0
  27. data/lib/gratr/rdot.rb +326 -0
  28. data/lib/gratr/search.rb +409 -0
  29. data/lib/gratr/strong_components.rb +127 -0
  30. data/lib/gratr/undirected_graph.rb +153 -0
  31. data/tests/TestBiconnected.rb +53 -0
  32. data/tests/TestChinesePostman.rb +53 -0
  33. data/tests/TestComplement.rb +54 -0
  34. data/tests/TestDigraph.rb +333 -0
  35. data/tests/TestDigraphDistance.rb +138 -0
  36. data/tests/TestEdge.rb +171 -0
  37. data/tests/TestInspection.rb +57 -0
  38. data/tests/TestMultiEdge.rb +57 -0
  39. data/tests/TestNeighborhood.rb +64 -0
  40. data/tests/TestProperties.rb +160 -0
  41. data/tests/TestSearch.rb +257 -0
  42. data/tests/TestStrongComponents.rb +85 -0
  43. data/tests/TestTriagulated.rb +137 -0
  44. data/tests/TestUndirectedGraph.rb +219 -0
  45. metadata +92 -0
@@ -0,0 +1,82 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ # Copyright (c) 2002,2004,2005 by Horst Duchene
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice(s),
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
14
+ # may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AREf
20
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #++
27
+
28
+
29
+ module GRATR
30
+
31
+ # This defines the minimum set of functions required to make a graph class that can
32
+ # use the algorithms defined by this library
33
+ module GraphAPI
34
+
35
+ # Is the graph directed?
36
+ #
37
+ # This method must be implemented by the specific graph class
38
+ def directed?() raise NotImplementedError; end
39
+
40
+ # Add a vertex to the Graph and return the Graph
41
+ # An additional label l can be specified as well
42
+ #
43
+ # This method must be implemented by the specific graph class
44
+ def add_vertex!(v,l=nil) raise NotImplementedError; end
45
+
46
+ # Add an edge to the Graph and return the Graph
47
+ # u can be an object of type GRATR::Arc or u,v specifies
48
+ # a source, target pair. The last parameter is an optional label
49
+ #
50
+ # This method must be implemented by the specific graph class
51
+ def add_edge!(u,v=nil,l=nil) raise NotImplementedError; end
52
+
53
+ # Remove a vertex to the Graph and return the Graph
54
+ #
55
+ # This method must be implemented by the specific graph class
56
+ def remove_vertex!(v) raise NotImplementedError; end
57
+
58
+ # Remove an edge from the Graph and return the Graph
59
+ #
60
+ # Can be a type of GRATR::Arc or a source and target
61
+ # This method must be implemented by the specific graph class
62
+ def remove_edge!(u,v=nil) raise NotImplementedError; end
63
+
64
+ # Return the array of vertices.
65
+ #
66
+ # This method must be implemented by the specific graph class
67
+ def vertices() raise NotImplementedError; end
68
+
69
+ # Return the array of edges.
70
+ #
71
+ # This method must be implemented by the specific graph class
72
+ def edges() raise NotImplementedError; end
73
+
74
+ # Returns the edge class
75
+ def edge_class() raise NotImplementedError; end
76
+
77
+ # Return the chromatic number for this graph
78
+ # This is currently incomplete and in some cases will be NP-complete
79
+ # FIXME: Should this even be here? My gut feeling is no...
80
+ def chromatic_number() raise NotImplementedError; end
81
+ end
82
+ end
@@ -0,0 +1,44 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice(s),
8
+ # this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
13
+ # may be used to endorse or promote products derived from this software
14
+ # without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #++
27
+
28
+
29
+ require 'gratr'
30
+
31
+ # Pull all GRATR classes up into the current namespace
32
+ Arc = GRATR::Arc
33
+ Edge = GRATR::Edge
34
+ MultiArc = GRATR::MultiArc
35
+ MultiEdge = GRATR::MultiEdge
36
+ Graph = GRATR::Graph
37
+ Digraph = GRATR::Digraph
38
+ DirectedGraph = GRATR::DirectedGraph
39
+ DirectedPseudoGraph = GRATR::DirectedPseudoGraph
40
+ DirectedMultiGraph = GRATR::DirectedMultiGraph
41
+ UndirectedGraph = GRATR::UndirectedGraph
42
+ UndirectedPseudoGraph = GRATR::UndirectedPseudoGraph
43
+ UndirectedMultiGraph = GRATR::UndirectedMultiGraph
44
+ Complete = GRATR::Complete
@@ -0,0 +1,103 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright notice(s),
8
+ # this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
13
+ # may be used to endorse or promote products derived from this software
14
+ # without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #++
27
+
28
+ module GRATR
29
+ module Labels
30
+ # Return a label for an edge or vertex
31
+ def [](u) (u.kind_of? GRATR::Arc) ? edge_label(u) : vertex_label(u); end
32
+
33
+ # Set a label for an edge or vertex
34
+ def []= (u, value) (u.kind_of? GRATR::Arc) ? edge_label_set(u,value) : vertex_label_set(u, value); end
35
+
36
+ # Delete a label entirely
37
+ def delete_label(u) (u.kind_of? GRATR::Arc) ? edge_label_delete(u) : vertex_label_delete(u); end
38
+
39
+ # Get the label for an edge
40
+ def vertex_label(v) vertex_label_dict[v]; end
41
+
42
+ # Set the label for an edge
43
+ def vertex_label_set(v, l) vertex_label_dict[v] = l; self; end
44
+
45
+ # Get the label for an edge
46
+ def edge_label(u,v=nil,n=nil)
47
+ u = edge_convert(u,v,n)
48
+ edge_label_dict[u]
49
+ end
50
+
51
+ # Set the label for an edge
52
+ def edge_label_set(u, v=nil, l=nil, n=nil)
53
+ u.kind_of?(GRATR::Arc) ? l = v : u = edge_convert(u,v,n)
54
+ edge_label_dict[u] = l; self
55
+ end
56
+
57
+ # Delete all graph labels
58
+ def clear_all_labels() @vertex_labels = {}; @edge_labels = {}; end
59
+
60
+ # Delete an edge label
61
+ def edge_label_delete(u, v=nil, n=nil)
62
+ u = edge_convert(u,v,n)
63
+ edge_label_dict.delete(u)
64
+ end
65
+
66
+ # Delete a vertex label
67
+ def vertex_label_delete(v) vertex_label_dict.delete(v); end
68
+
69
+ protected
70
+
71
+ def vertex_label_dict() @vertex_labels ||= {}; end
72
+ def edge_label_dict() @edge_labels ||= {}; end
73
+
74
+ # A generic cost function. It either calls the weight function with and edge
75
+ # constructed from the two nodes, or calls the [] operator of the label
76
+ # when given a value. If no weight value is specified, the label itself is
77
+ # treated as the cost value.
78
+ #
79
+ # Note: This function will not work for Pseudo or Multi graphs at present.
80
+ # FIXME: Remove u,v interface to fix Pseudo Multi graph problems.
81
+ def cost(u,v=nil,weight=nil)
82
+ u.kind_of?(Arc) ? weight = v : u = edge_class[u,v]
83
+ case weight
84
+ when Proc : weight.call(u)
85
+ when nil : self[u]
86
+ else self[u][weight]
87
+ end
88
+ end
89
+
90
+ # An alias of cost for property retrieval in general
91
+ alias property cost
92
+
93
+ # A function to set properties specified by the user.
94
+ def property_set(u,name,value)
95
+ case name
96
+ when Proc : name.call(value)
97
+ when nil : self[u] = value
98
+ else self[u][name] = value
99
+ end
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,107 @@
1
+ #--
2
+ # Copyright (c) 2006 Shawn Patrick Garbett
3
+ # Copyright (c) 2002,2004,2005 by Horst Duchene
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice(s),
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the Shawn Garbett nor the names of its contributors
14
+ # may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ #++
28
+
29
+ require 'gratr/digraph'
30
+
31
+ module GRATR
32
+
33
+ class Network < Digraph
34
+ attr_accessor :lower, :upper, :cost, :flow
35
+
36
+ def residual(residual_capacity, cost_property, zero = 0)
37
+ r = Digraph.new
38
+ edges.each do |e1|
39
+ [e1,e1.reverse].each do |e|
40
+ rij = property(e,self.upper) - property(e,self.flow) if edge? e
41
+ rij += property(e.reverse,self.flow) - property(e.reverse,self.lower) if edge? e.reverse
42
+ r.add_edge!(e) if rij > zero
43
+ r.property_set(e,residual_capacity, rij)
44
+ r.property_set(e,cost, cost(e,cost_property))
45
+ end
46
+ end
47
+ r
48
+ end
49
+
50
+ def maximum_flow() eliminate_lower_bounds.maximum_flow_prime.restore_lower_bounds(self); end
51
+
52
+ private:
53
+
54
+ def eliminate_lower_bounds
55
+ no_lower_bounds = Digraph.new(self)
56
+ if self.upper.kind_of? Proc then
57
+ no_lower_bounds.upper = Proc.new {|e| self.upper.call(e) - property(e,self.lower) }
58
+ else
59
+ no_lower_bounds.edges.each {|e| no_lower_bounds[e][self.upper] -= property(e,self.lower)}
60
+ end
61
+ no_lower_bounds
62
+ end
63
+
64
+ def restore_lower_bounds(src)
65
+ src.edges.each do {|e| (src.flow ? src[e][src.flow] : src[e]) = property(e,self.flow) + src.property(e,self.lower) }
66
+ src
67
+ end
68
+
69
+ def maximum_flow_prime
70
+ end
71
+
72
+ end
73
+
74
+ module Graph
75
+
76
+ module MaximumFlow
77
+
78
+ # Maximum flow, it returns an array with the maximum flow and a hash of flow per edge
79
+ # Currently a highly inefficient implementation, FIXME, This should use Goldberg and Tarjan's method.
80
+ def maximum_flow(s, t, capacity = nil, zero = 0)
81
+ flow = Hash.new(zero)
82
+ available = Hash.new(zero)
83
+ total = zero
84
+ edges.each {|e| available[e] = cost(e,capacity)}
85
+ adj_positive = Proc.new do |u|
86
+ adjacent(u).select {|r| available[edge_class[u,r]] > zero}
87
+ end
88
+ while (tree = bfs_tree_from_vertex(start))[t]
89
+ route = [t]
90
+ while route[-1] != s
91
+ route << tree[route[route[-1]]]
92
+ raise ArgumentError, "No route from #{s} to #{t} possible"
93
+ end; route.reverse
94
+ amt = route.map {|e| available[e]}.min
95
+ route.each do |e|
96
+ flow[e] += amt
97
+ available[e] -= amt
98
+ end
99
+ total += amt
100
+ end
101
+
102
+ [total, flow]
103
+ end
104
+
105
+ end # MaximumFlow
106
+ end # Graph
107
+ end # GRATR
@@ -0,0 +1,326 @@
1
+ # rdot.rb
2
+ #
3
+ # $Id: rdot.rb,v 1.4 2005/03/26 15:06:36 wsdng Exp $
4
+ #
5
+ # This is a modified version of dot.rb from Dave Thomas's rdoc project. I [Horst Duchene]
6
+ # renamed it to rdot.rb to avoid collision with an installed rdoc/dot.
7
+ #
8
+ # It also supports undirected edges.
9
+
10
+ module DOT
11
+
12
+ # These glogal vars are used to make nice graph source.
13
+
14
+ $tab = ' '
15
+ $tab2 = $tab * 2
16
+
17
+ # if we don't like 4 spaces, we can change it any time
18
+
19
+ def change_tab (t)
20
+ $tab = t
21
+ $tab2 = t * 2
22
+ end
23
+
24
+ # options for node declaration
25
+
26
+ NODE_OPTS = [
27
+ # attributes due to
28
+ # http://www.graphviz.org/Documentation/dotguide.pdf
29
+ # March, 26, 2005
30
+ 'bottomlabel', # auxiliary label for nodes of shape M*
31
+ 'color', # default: black; node shape color
32
+ 'comment', # any string (format-dependent)
33
+ 'distortion', # default: 0.0; node distortion for shape=polygon
34
+ 'fillcolor', # default: lightgrey/black; node fill color
35
+ 'fixedsize', # default: false; label text has no affect on node size
36
+ 'fontcolor', # default: black; type face color
37
+ 'fontname', # default: Times-Roman; font family
38
+ 'fontsize', #default: 14; point size of label
39
+ 'group', # name of node�s group
40
+ 'height', # default: .5; height in inches
41
+ 'label', # default: node name; any string
42
+ 'layer', # default: overlay range; all, id or id:id
43
+ 'orientation', # dafault: 0.0; node rotation angle
44
+ 'peripheries', # shape-dependent number of node boundaries
45
+ 'regular', # default: false; force polygon to be regular
46
+ 'shape', # default: ellipse; node shape; see Section 2.1 and Appendix E
47
+ 'shapefile', # external EPSF or SVG custom shape file
48
+ 'sides', # default: 4; number of sides for shape=polygon
49
+ 'skew' , # default: 0.0; skewing of node for shape=polygon
50
+ 'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
51
+ 'toplabel', # auxiliary label for nodes of shape M*
52
+ 'URL', # URL associated with node (format-dependent)
53
+ 'width', # default: .75; width in inches
54
+ 'z', #default: 0.0; z coordinate for VRML output
55
+
56
+ # maintained for backward compatibility or rdot internal
57
+ 'bgcolor',
58
+ 'rank'
59
+ ]
60
+
61
+ # options for edge declaration
62
+
63
+ EDGE_OPTS = [
64
+ 'arrowhead', # default: normal; style of arrowhead at head end
65
+ 'arrowsize', # default: 1.0; scaling factor for arrowheads
66
+ 'arrowtail', # default: normal; style of arrowhead at tail end
67
+ 'color', # default: black; edge stroke color
68
+ 'comment', # any string (format-dependent)
69
+ 'constraint', # default: true use edge to affect node ranking
70
+ 'decorate', # if set, draws a line connecting labels with their edges
71
+ 'dir', # default: forward; forward, back, both, or none
72
+ 'fontcolor', # default: black type face color
73
+ 'fontname', # default: Times-Roman; font family
74
+ 'fontsize', # default: 14; point size of label
75
+ 'headlabel', # label placed near head of edge
76
+ 'headport', # n,ne,e,se,s,sw,w,nw
77
+ 'headURL', # URL attached to head label if output format is ismap
78
+ 'label', # edge label
79
+ 'labelangle', # default: -25.0; angle in degrees which head or tail label is rotated off edge
80
+ 'labeldistance', # default: 1.0; scaling factor for distance of head or tail label from node
81
+ 'labelfloat', # default: false; lessen constraints on edge label placement
82
+ 'labelfontcolor', # default: black; type face color for head and tail labels
83
+ 'labelfontname', # default: Times-Roman; font family for head and tail labels
84
+ 'labelfontsize', # default: 14 point size for head and tail labels
85
+ 'layer', # default: overlay range; all, id or id:id
86
+ 'lhead', # name of cluster to use as head of edge
87
+ 'ltail', # name of cluster to use as tail of edge
88
+ 'minlen', # default: 1 minimum rank distance between head and tail
89
+ 'samehead', # tag for head node; edge heads with the same tag are merged onto the same port
90
+ 'sametail', # tag for tail node; edge tails with the same tag are merged onto the same port
91
+ 'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
92
+ 'taillabel', # label placed near tail of edge
93
+ 'tailport', # n,ne,e,se,s,sw,w,nw
94
+ 'tailURL', # URL attached to tail label if output format is ismap
95
+ 'weight', # default: 1; integer cost of stretching an edge
96
+
97
+ # maintained for backward compatibility or rdot internal
98
+ 'id'
99
+ ]
100
+
101
+ # options for graph declaration
102
+
103
+ GRAPH_OPTS = [
104
+ 'bgcolor',
105
+ 'center', 'clusterrank', 'color', 'concentrate',
106
+ 'fontcolor', 'fontname', 'fontsize',
107
+ 'label', 'layerseq',
108
+ 'margin', 'mclimit',
109
+ 'nodesep', 'nslimit',
110
+ 'ordering', 'orientation',
111
+ 'page',
112
+ 'rank', 'rankdir', 'ranksep', 'ratio',
113
+ 'size'
114
+ ]
115
+
116
+ # a root class for any element in dot notation
117
+
118
+ class DOTSimpleElement
119
+
120
+ attr_accessor :name
121
+
122
+ def initialize (params = {})
123
+ @label = params['name'] ? params['name'] : ''
124
+ end
125
+
126
+ def to_s
127
+ @name
128
+ end
129
+ end
130
+
131
+ # an element that has options ( node, edge, or graph )
132
+
133
+ class DOTElement < DOTSimpleElement
134
+
135
+ # attr_reader :parent
136
+ attr_accessor :name, :options
137
+
138
+ def initialize (params = {}, option_list = [])
139
+ super(params)
140
+ @name = params['name'] ? params['name'] : nil
141
+ @parent = params['parent'] ? params['parent'] : nil
142
+ @options = {}
143
+ p = params.stringify_keys
144
+ option_list.each {|i| @options[i] = p[i] if p[i] }
145
+ @options['label'] ||= @name if @name != 'node'
146
+ end
147
+
148
+ def each_option
149
+ @options.each{ |i| yield i }
150
+ end
151
+
152
+ def each_option_pair
153
+ @options.each_pair{ |key, val| yield key, val }
154
+ end
155
+
156
+ #def parent=( thing )
157
+ # @parent.delete( self ) if defined?( @parent ) and @parent
158
+ # @parent = thing
159
+ #end
160
+
161
+ end
162
+
163
+
164
+ # This is used when we build nodes that have shape=record
165
+ # ports don't have options :)
166
+
167
+ class DOTPort < DOTSimpleElement
168
+
169
+ attr_accessor :label
170
+
171
+ def initialize (params = {})
172
+ super(params)
173
+ @name = params['label'] ? params['label'] : ''
174
+ end
175
+
176
+ def to_s
177
+ ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
178
+ end
179
+ end
180
+
181
+ # node element
182
+
183
+ class DOTNode < DOTElement
184
+
185
+ @ports
186
+
187
+ def initialize (params = {}, option_list = NODE_OPTS)
188
+ super(params, option_list)
189
+ @ports = params['ports'] ? params['ports'] : []
190
+ end
191
+
192
+ def each_port
193
+ @ports.each { |i| yield i }
194
+ end
195
+
196
+ def << (thing)
197
+ @ports << thing
198
+ end
199
+
200
+ def push (thing)
201
+ @ports.push(thing)
202
+ end
203
+
204
+ def pop
205
+ @ports.pop
206
+ end
207
+
208
+ def to_s (t = '')
209
+
210
+ # This code is totally incomprehensible; it needs to be replaced!
211
+
212
+ label = @options['shape'] != 'record' && @ports.length == 0 ?
213
+ @options['label'] ?
214
+ t + $tab + "label = \"#{@options['label']}\"\n" :
215
+ '' :
216
+ t + $tab + 'label = "' + " \\\n" +
217
+ t + $tab2 + "#{@options['label']}| \\\n" +
218
+ @ports.collect{ |i|
219
+ t + $tab2 + i.to_s
220
+ }.join( "| \\\n" ) + " \\\n" +
221
+ t + $tab + '"' + "\n"
222
+
223
+ t + "#{@name} [\n" +
224
+ @options.to_a.collect{ |i|
225
+ i[1] && i[0] != 'label' ?
226
+ t + $tab + "#{i[0]} = #{i[1]}" : nil
227
+ }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
228
+ label +
229
+ t + "]\n"
230
+ end
231
+
232
+ end # class DOTNode
233
+
234
+ # A subgraph element is the same to graph, but has another header in dot
235
+ # notation.
236
+
237
+ class DOTSubgraph < DOTElement
238
+
239
+ @nodes
240
+ @dot_string
241
+
242
+ def initialize (params = {}, option_list = GRAPH_OPTS)
243
+ super(params, option_list)
244
+ @nodes = params['nodes'] ? params['nodes'] : []
245
+ @dot_string = 'graph'
246
+ end
247
+
248
+ def each_node
249
+ @nodes.each{ |i| yield i }
250
+ end
251
+
252
+ def << (thing)
253
+ @nodes << thing
254
+ end
255
+
256
+ def push (thing)
257
+ @nodes.push( thing )
258
+ end
259
+
260
+ def pop
261
+ @nodes.pop
262
+ end
263
+
264
+ def to_s (t = '')
265
+ hdr = t + "#{@dot_string} #{@name} {\n"
266
+
267
+ options = @options.to_a.collect{ |name, val|
268
+ val && name != 'label' ?
269
+ t + $tab + "#{name} = #{val}" :
270
+ name ? t + $tab + "#{name} = \"#{val}\"" : nil
271
+ }.compact.join( "\n" ) + "\n"
272
+
273
+ nodes = @nodes.collect{ |i|
274
+ i.to_s( t + $tab )
275
+ }.join( "\n" ) + "\n"
276
+ hdr + options + nodes + t + "}\n"
277
+ end
278
+
279
+ end # class DOTSubgraph
280
+
281
+ # This is a graph.
282
+
283
+ class DOTDigraph < DOTSubgraph
284
+
285
+ def initialize (params = {}, option_list = GRAPH_OPTS)
286
+ super(params, option_list)
287
+ @dot_string = 'digraph'
288
+ end
289
+
290
+ end # class DOTDigraph
291
+
292
+ # This is an edge.
293
+
294
+ class DOTArc < DOTElement
295
+
296
+ attr_accessor :from, :to
297
+
298
+ def initialize (params = {}, option_list = EDGE_OPTS)
299
+ super(params, option_list)
300
+ @from = params['from'] ? params['from'] : nil
301
+ @to = params['to'] ? params['to'] : nil
302
+ end
303
+
304
+ def edge_link
305
+ '--'
306
+ end
307
+
308
+ def to_s (t = '')
309
+ t + "#{@from} #{edge_link} #{to} [\n" +
310
+ @options.to_a.collect{ |i|
311
+ i[1] && i[0] != 'label' ?
312
+ t + $tab + "#{i[0]} = #{i[1]}" :
313
+ i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil
314
+ }.compact.join( "\n" ) + "\n" + t + "]\n"
315
+ end
316
+
317
+ end # class DOTArc
318
+
319
+ class DOTDirectedArc < DOTArc
320
+
321
+ def edge_link
322
+ '->'
323
+ end
324
+
325
+ end # class DOTDirectedArc
326
+ end # module DOT