ruby-graphviz 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,323 @@
1
+ # Copyright (C) 2003, 2004, 2005, 2006, 2007 Gregoire Lejeune <gregoire.lejeune@free.fr>
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+
17
+ require 'tempfile'
18
+ require 'mkmf'
19
+
20
+ require 'graphviz/node'
21
+ require 'graphviz/edge'
22
+ require 'graphviz/attrs'
23
+ require 'graphviz/constants'
24
+
25
+ class GraphViz
26
+ include Constants
27
+
28
+ public
29
+
30
+ ## Var: Output format (dot, png, jpeg, ...)
31
+ @format
32
+ ## Var: Output file name
33
+ @filename
34
+ ## Var: program to use (dot|twopi)
35
+ @prog
36
+ ## Var: program path
37
+ @path
38
+
39
+ ## Var: Graph name
40
+ @name
41
+
42
+ ## Var: defined attributs
43
+ @graph
44
+ @node
45
+ @edge
46
+ attr_accessor :node, :edge
47
+
48
+ @elements_order
49
+
50
+ ##
51
+ # In:
52
+ # - xNodeName : Name of the new node
53
+ # - *hOpt : Node options
54
+ # Out:
55
+ # - xNodeName as In
56
+ #
57
+ def add_node( xNodeName, *hOpt )
58
+ @hoNodes[xNodeName] = GraphViz::Node::new( xNodeName )
59
+
60
+ if hOpt.nil? == false and hOpt[0].nil? == false
61
+ hOpt[0].each do |xKey, xValue|
62
+ @hoNodes[xNodeName][xKey.to_s] = xValue
63
+ end
64
+ end
65
+
66
+ @elements_order.push( {
67
+ "type" => "node",
68
+ "name" => xNodeName,
69
+ "value" => @hoNodes[xNodeName]
70
+ } )
71
+
72
+ return( @hoNodes[xNodeName] )
73
+ end
74
+
75
+ def add_edge( oNodeOne, oNodeTwo, *hOpt )
76
+ oEdge = GraphViz::Edge::new( oNodeOne, oNodeTwo )
77
+
78
+ if hOpt.nil? == false and hOpt[0].nil? == false
79
+ hOpt[0].each do |xKey, xValue|
80
+ oEdge[xKey.to_s] = xValue
81
+ end
82
+ end
83
+
84
+ @elements_order.push( {
85
+ "type" => "edge",
86
+ "value" => oEdge
87
+ } )
88
+
89
+ @loEdges.push( oEdge )
90
+ return( oEdge )
91
+ end
92
+
93
+ def add_graph( xGraphName, *hOpt )
94
+ @hoGraphs[xGraphName] = GraphViz::new( xGraphName, "parent" => self, "type" => @oGraphType )
95
+
96
+ if hOpt.nil? == false and hOpt[0].nil? == false
97
+ hOpt[0].each do |xKey, xValue|
98
+ @hoGraphs[xGraphName][xKey.to_s] = xValue
99
+ end
100
+ end
101
+
102
+ @elements_order.push( {
103
+ "type" => "graph",
104
+ "name" => xGraphName,
105
+ "value" => @hoGraphs[xGraphName]
106
+ } )
107
+
108
+ return( @hoGraphs[xGraphName] )
109
+ end
110
+
111
+ def []=( xAttrName, xValue )
112
+ @graph[xAttrName] = xValue
113
+ end
114
+
115
+ def []( xAttrName )
116
+ return( @graph[xAttrName].clone )
117
+ end
118
+
119
+ def output( *hOpt )
120
+ xDOTScript = ""
121
+ xLastType = nil
122
+ xSeparator = ""
123
+ xData = ""
124
+
125
+ @elements_order.each { |kElement|
126
+ if xLastType.nil? == true or xLastType != kElement["type"]
127
+
128
+ if xData.length > 0
129
+ case xLastType
130
+ when "graph_attr"
131
+ xDOTScript << " " + xData + ";\n"
132
+
133
+ when "node_attr"
134
+ xDOTScript << " node [" + xData + "];\n"
135
+
136
+ when "edge_attr"
137
+ xDOTScript << " edge [" + xData + "];\n"
138
+ end
139
+ end
140
+
141
+ xSeparator = ""
142
+ xData = ""
143
+ end
144
+
145
+ xLastType = kElement["type"]
146
+
147
+ case kElement["type"]
148
+ when "graph_attr"
149
+ xData << xSeparator + kElement["name"] + " = \"" + kElement["value"] + "\""
150
+ xSeparator = "; "
151
+
152
+ when "node_attr"
153
+ xData << xSeparator + kElement["name"] + " = \"" + kElement["value"] + "\""
154
+ xSeparator = ", "
155
+
156
+ when "edge_attr"
157
+ xData << xSeparator + kElement["name"] + " = \"" + kElement["value"] + "\""
158
+ xSeparator = ", "
159
+
160
+ when "node"
161
+ xDOTScript << " " + kElement["value"].output() + "\n"
162
+
163
+ when "edge"
164
+ xDOTScript << " " + kElement["value"].output( @oGraphType ) + "\n"
165
+
166
+ when "graph"
167
+ xDOTScript << kElement["value"].output() + "\n"
168
+
169
+ else
170
+ raise ArgumentError, "Don't know what to do with element type '#{kElement['type']}'"
171
+ end
172
+ }
173
+
174
+ xDOTScript << "}"
175
+
176
+ if @oParentGraph.nil? == false
177
+ xDOTScript = "subgraph #{@name} {\n" << xDOTScript
178
+
179
+ return( xDOTScript )
180
+ else
181
+ if hOpt.nil? == false and hOpt[0].nil? == false
182
+ hOpt[0].each do |xKey, xValue|
183
+ case xKey.to_s
184
+ when "output"
185
+ if FORMATS.index( xValue ).nil? == true
186
+ raise ArgumentError, "output format '#{xValue}' invalid"
187
+ end
188
+ @format = xValue
189
+ when "file"
190
+ @filename = xValue
191
+ when "use"
192
+ if PROGRAMS.index( xValue ).nil? == true
193
+ raise ArgumentError, "can't use '#{xValue}'"
194
+ end
195
+ @prog = xValue
196
+ when "path"
197
+ @path = xValue
198
+ else
199
+ raise ArgumentError, "option #{xKey.to_s} unknown"
200
+ end
201
+ end
202
+ end
203
+
204
+ xDOTScript = "#{@oGraphType} #{@name} {\n" << xDOTScript
205
+
206
+ if @format != "none"
207
+ ## Act: Save script and send it to dot
208
+ t = Tempfile::open( File.basename($0) + "." )
209
+ t.print( xDOTScript )
210
+ t.close
211
+
212
+ #cmd = find_executable( @prog )
213
+ #cmd = find_executable0( @prog )
214
+ cmd = find_executable( )
215
+ if cmd == nil
216
+ raise StandardError, "GraphViz not installed or #{@prog} not in PATH. Install GraphViz or use the 'path' option"
217
+ end
218
+
219
+ xFile = ""
220
+ xFile = "-o #{@filename}" if @filename.nil? == false
221
+ xCmd = "#{cmd} -T#{@format} #{xFile} #{t.path}"
222
+
223
+ f = IO.popen( xCmd )
224
+ print f.readlines
225
+ f.close
226
+ else
227
+ puts xDOTScript
228
+ end
229
+ end
230
+ end
231
+
232
+ def set_position( xType, xKey, xValue )
233
+ @elements_order.push( {
234
+ "type" => "#{xType}_attr",
235
+ "name" => xKey,
236
+ "value" => xValue
237
+ } )
238
+ end
239
+
240
+ ## ----------------------------------------------------------------------------
241
+
242
+ private
243
+
244
+ ## Var: Nodes, Edges and Graphs tables
245
+ @hoNodes
246
+ @loEdges
247
+ @hoGraphs
248
+
249
+ ## Var: Parent graph
250
+ @oParentGraph
251
+
252
+ ## Var: Type de graphe (orient� ou non)
253
+ @oGraphType
254
+
255
+ def initialize( xGraphName, *hOpt )
256
+ @format = "dot"
257
+ @filename = nil
258
+ @prog = "dot"
259
+ @path = nil
260
+ @name = xGraphName
261
+
262
+ @elements_order = Array::new()
263
+
264
+ @oParentGraph = nil
265
+ @oGraphType = "digraph"
266
+
267
+ @hoNodes = Hash::new()
268
+ @loEdges = Array::new()
269
+ @hoGraphs = Hash::new()
270
+
271
+ @node = GraphViz::Attrs::new( self, "node", NODESATTRS )
272
+ @edge = GraphViz::Attrs::new( self, "edge", EDGESATTRS )
273
+ @graph = GraphViz::Attrs::new( self, "graph", GRAPHSATTRS )
274
+
275
+ if hOpt.nil? == false and hOpt[0].nil? == false
276
+ hOpt[0].each do |xKey, xValue|
277
+ case xKey.to_s
278
+ when "output"
279
+ if FORMATS.index( xValue ).nil? == true
280
+ raise ArgumentError, "output format '#{xValue}' invalid"
281
+ end
282
+ @format = xValue
283
+ when "use"
284
+ if PROGRAMS.index( xValue ).nil? == true
285
+ raise ArgumentError, "can't use '#{xValue}'"
286
+ end
287
+ @prog = xValue
288
+ when "file"
289
+ @filename = xValue
290
+ when "parent"
291
+ @oParentGraph = xValue
292
+ when "type"
293
+ if GRAPHTYPE.index( xValue ).nil? == true
294
+ raise ArgumentError, "graph type '#{xValue}' unknow"
295
+ end
296
+ @oGraphType = xValue
297
+ when "path"
298
+ @path = xValue
299
+ else
300
+ self[xKey.to_s] = xValue
301
+ end
302
+ end
303
+ end
304
+ end
305
+
306
+ def find_executable( )
307
+ cmd = find_executable0( @prog )
308
+ if cmd == nil and @path != nil
309
+ __cmd = File.join( @path, @prog )
310
+ cmd = __cmd if File.executable?( __cmd )
311
+ end
312
+ return cmd
313
+ end
314
+ # def find_executable(bin, path = nil)
315
+ # path = (path || ENV['PATH']).split(File::PATH_SEPARATOR)
316
+ # file = nil
317
+ # path.each do |dir|
318
+ # return file if File.executable?(file = File.join(dir, bin))
319
+ # end
320
+ # nil
321
+ # end
322
+ end
323
+
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2004, 2005, 2006, 2007 Gregoire Lejeune <gregoire.lejeune@free.fr>
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+
17
+ class GraphViz
18
+ class Attrs
19
+ @data
20
+ @name
21
+ @attributs
22
+ @graphviz
23
+
24
+ attr_accessor :data
25
+
26
+ def initialize( gviz, name, attributs )
27
+ @name = name
28
+ @attributs = attributs
29
+ @data = Hash::new( )
30
+ @graphviz = gviz
31
+ end
32
+
33
+ def []( xKey )
34
+ if @data.key?( xKey.to_s ) == false
35
+ nil
36
+ end
37
+ @data[xKey.to_s]
38
+ end
39
+
40
+ def []=( xKey, xValue )
41
+ if @attributs.index( xKey.to_s ).nil? == true
42
+ raise ArgumentError, "#{@name} attribut '#{xKey.to_s}' invalid"
43
+ end
44
+ @data[xKey.to_s] = xValue
45
+
46
+ if @graphviz.nil? == false
47
+ @graphviz.set_position( @name, xKey.to_s, xValue )
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,290 @@
1
+ # Copyright (C) 2004, 2005, 2006, 2007 Gregoire Lejeune <gregoire.lejeune@free.fr>
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+
17
+ module Constants
18
+ ## Const: Output formats
19
+ FORMATS = [
20
+ "dot",
21
+ "ps",
22
+ "mif",
23
+ "hpgl",
24
+ "pcl",
25
+ "gif",
26
+ "imap",
27
+ "ismap",
28
+ "cmap",
29
+ "jpeg",
30
+ "png",
31
+ "xpm",
32
+ "pic",
33
+ "gd",
34
+ "gd2",
35
+ "wbmp",
36
+ "vrml",
37
+ "vtx",
38
+ "mp",
39
+ "fig",
40
+ "svg",
41
+ "plain",
42
+ "text",
43
+ "canon",
44
+ "none"
45
+ ]
46
+
47
+ ## Const: programs
48
+ PROGRAMS = [
49
+ "dot",
50
+ "neato",
51
+ "twopi"
52
+ ]
53
+
54
+ ## Const: type de graphs
55
+ GRAPHTYPE = [
56
+ "digraph",
57
+ "graph"
58
+ ]
59
+
60
+ # E, N, G, S and C represent edges, nodes, the root graph, subgraphs and cluster subgraphs, respectively
61
+ GENCS_ATTRS = {
62
+ "Damping" => { "UseBy" => "G", "Type" => "double" },
63
+ "URL" => { "UseBy" => "ENGC", "Type" => "escString, string" },
64
+ "arrowhead" => { "UseBy" => "E", "Type" => "arrowType" },
65
+ "arrowsize" => { "UseBy" => "E", "Type" => "double" },
66
+ "arrowtail" => { "UseBy" => "E", "Type" => "arrowType" },
67
+ "bb" => { "UseBy" => "G", "Type" => "rect" },
68
+ "bgcolor" => { "UseBy" => "GC", "Type" => "color" },
69
+ "bottomlabel" => { "UseBy" => "N", "Type" => "string" },
70
+ "center" => { "UseBy" => "G", "Type" => "bool" },
71
+ "clusterrank" => { "UseBy" => "G", "Type" => "clusterMode" },
72
+ "color" => { "UseBy" => "ENC", "Type" => "color, colorList" },
73
+ "comment" => { "UseBy" => "ENG", "Type" => "string" },
74
+ "compound" => { "UseBy" => "G", "Type" => "bool" },
75
+ "concentrate" => { "UseBy" => "G", "Type" => "bool" },
76
+ "constraint" => { "UseBy" => "E", "Type" => "bool" },
77
+ "decorate" => { "UseBy" => "E", "Type" => "bool" },
78
+ "defaultdist" => { "UseBy" => "G", "Type" => "double" },
79
+ "dim" => { "UseBy" => "G", "Type" => "int" },
80
+ "dir" => { "UseBy" => "E", "Type" => "dirType" },
81
+ "distortion" => { "UseBy" => "N", "Type" => "double" },
82
+ "dpi" => { "UseBy" => "G", "Type" => "double" },
83
+ "epsilon" => { "UseBy" => "G", "Type" => "double" },
84
+ "fillcolor" => { "UseBy" => "NC", "Type" => "color" },
85
+ "fixedsize" => { "UseBy" => "N", "Type" => "bool" },
86
+ "fontcolor" => { "UseBy" => "ENGC", "Type" => "color" },
87
+ "fontname" => { "UseBy" => "ENGC", "Type" => "string" },
88
+ "fontpath" => { "UseBy" => "G", "Type" => "string" },
89
+ "fontsize" => { "UseBy" => "ENGC", "Type" => "double" },
90
+ "group" => { "UseBy" => "N", "Type" => "string" },
91
+ "headURL" => { "UseBy" => "E", "Type" => "escString" },
92
+ "headclip" => { "UseBy" => "E", "Type" => "bool" },
93
+ "headhref" => { "UseBy" => "E", "Type" => "escString" },
94
+ "headlabel" => { "UseBy" => "E", "Type" => "lblString" },
95
+ "headport" => { "UseBy" => "E", "Type" => "portPos" },
96
+ "headtarget" => { "UseBy" => "E", "Type" => "escString" },
97
+ "headtooltip" => { "UseBy" => "E", "Type" => "escString" },
98
+ "height" => { "UseBy" => "N", "Type" => "double" },
99
+ "href" => { "UseBy" => "E", "Type" => "escString" },
100
+ "label" => { "UseBy" => "ENGC", "Type" => "lblString" },
101
+ "html" => { "UseBy" => "N", "Type" => "lblString" }, # API extension
102
+ "labelangle" => { "UseBy" => "E", "Type" => "double" },
103
+ "labeldistance" => { "UseBy" => "E", "Type" => "double" },
104
+ "labelfloat" => { "UseBy" => "E", "Type" => "bool" },
105
+ "labelfontcolor" => { "UseBy" => "E", "Type" => "color" },
106
+ "labelfontname" => { "UseBy" => "E", "Type" => "string" },
107
+ "labelfontsize" => { "UseBy" => "E", "Type" => "double" },
108
+ "labeljust" => { "UseBy" => "GC", "Type" => "string" },
109
+ "labelloc" => { "UseBy" => "GC", "Type" => "string" },
110
+ "layer" => { "UseBy" => "EN", "Type" => "layerRange" },
111
+ "layers" => { "UseBy" => "G", "Type" => "layerList" },
112
+ "layersep" => { "UseBy" => "G", "Type" => "string" },
113
+ "len" => { "UseBy" => "E", "Type" => "double" },
114
+ "lhead" => { "UseBy" => "E", "Type" => "string" },
115
+ "lp" => { "UseBy" => "EGC", "Type" => "point" },
116
+ "ltail" => { "UseBy" => "E", "Type" => "string" },
117
+ "margin" => { "UseBy" => "NG", "Type" => "double, pointf" },
118
+ "maxiter" => { "UseBy" => "G", "Type" => "int" },
119
+ "mclimit" => { "UseBy" => "G", "Type" => "double" },
120
+ "mindist" => { "UseBy" => "G", "Type" => "double" },
121
+ "minlen" => { "UseBy" => "E", "Type" => "int" },
122
+ "mode" => { "UseBy" => "G", "Type" => "string" },
123
+ "model" => { "UseBy" => "G", "Type" => "string" },
124
+ "nodesep" => { "UseBy" => "G", "Type" => "double" },
125
+ "nojustify" => { "UseBy" => "GCNE", "Type" => "bool" },
126
+ "normalize" => { "UseBy" => "G", "Type" => "bool" },
127
+ "nslimitnslimit1" => { "UseBy" => "G", "Type" => "double" },
128
+ "ordering" => { "UseBy" => "G", "Type" => "string" },
129
+ "orientation" => { "UseBy" => "N", "Type" => "double" },
130
+ "orientation" => { "UseBy" => "G", "Type" => "string" },
131
+ "outputorder" => { "UseBy" => "G", "Type" => "outputMode" },
132
+ "overlap" => { "UseBy" => "G", "Type" => "string, bool" },
133
+ "pack" => { "UseBy" => "G", "Type" => "bool, int" },
134
+ "packmode" => { "UseBy" => "G", "Type" => "packMode" },
135
+ "page" => { "UseBy" => "G", "Type" => "pointf" },
136
+ "pagedir" => { "UseBy" => "G", "Type" => "pagedir" },
137
+ "pencolor" => { "UseBy" => "C", "Type" => "color" },
138
+ "peripheries" => { "UseBy" => "NC", "Type" => "int" },
139
+ "pin" => { "UseBy" => "N", "Type" => "bool" },
140
+ "pos" => { "UseBy" => "EN", "Type" => "point, splineType" },
141
+ "quantum" => { "UseBy" => "G", "Type" => "double" },
142
+ "rank" => { "UseBy" => "S", "Type" => "rankType" },
143
+ "rankdir" => { "UseBy" => "G", "Type" => "rankdir" },
144
+ "ranksep" => { "UseBy" => "G", "Type" => "double" },
145
+ "ratio" => { "UseBy" => "G", "Type" => "double, string" },
146
+ "rects" => { "UseBy" => "N", "Type" => "rect" },
147
+ "regular" => { "UseBy" => "N", "Type" => "bool" },
148
+ "remincross" => { "UseBy" => "G", "Type" => "bool" },
149
+ "resolution" => { "UseBy" => "G", "Type" => "double" },
150
+ "root" => { "UseBy" => "GN", "Type" => "string, bool" },
151
+ "rotate" => { "UseBy" => "G", "Type" => "int" },
152
+ "samehead" => { "UseBy" => "E", "Type" => "string" },
153
+ "sametail" => { "UseBy" => "E", "Type" => "string" },
154
+ "samplepoints" => { "UseBy" => "G", "Type" => "int" },
155
+ "searchsize" => { "UseBy" => "G", "Type" => "int" },
156
+ "sep" => { "UseBy" => "G", "Type" => "double" },
157
+ "shape" => { "UseBy" => "N", "Type" => "shape" },
158
+ "shapefile" => { "UseBy" => "N", "Type" => "string" },
159
+ "showboxes" => { "UseBy" => "ENG", "Type" => "int" },
160
+ "sides" => { "UseBy" => "N", "Type" => "int" },
161
+ "size" => { "UseBy" => "G", "Type" => "pointf" },
162
+ "skew" => { "UseBy" => "N", "Type" => "double" },
163
+ "splines" => { "UseBy" => "G", "Type" => "bool, string" },
164
+ "start" => { "UseBy" => "G", "Type" => "startType" },
165
+ "style" => { "UseBy" => "ENC", "Type" => "style" },
166
+ "stylesheet" => { "UseBy" => "G", "Type" => "string" },
167
+ "tailURL" => { "UseBy" => "E", "Type" => "escString" },
168
+ "tailclip" => { "UseBy" => "E", "Type" => "bool" },
169
+ "tailhref" => { "UseBy" => "E", "Type" => "escString" },
170
+ "taillabel" => { "UseBy" => "E", "Type" => "lblString" },
171
+ "tailport" => { "UseBy" => "E", "Type" => "portPos" },
172
+ "tailtarget" => { "UseBy" => "E", "Type" => "escString" },
173
+ "tailtooltip" => { "UseBy" => "E", "Type" => "escString" },
174
+ "target" => { "UseBy" => "ENGC", "Type" => "escString, string" },
175
+ "tooltip" => { "UseBy" => "NE", "Type" => "escString" },
176
+ "toplabel" => { "UseBy" => "N", "Type" => "string" },
177
+ "truecolor" => { "UseBy" => "G", "Type" => "bool" },
178
+ "vertices" => { "UseBy" => "N", "Type" => "pointfList" },
179
+ "viewport" => { "UseBy" => "G", "Type" => "viewPort" },
180
+ "voro_margin" => { "UseBy" => "G", "Type" => "double" },
181
+ "weight" => { "UseBy" => "E", "Type" => "double" },
182
+ "width" => { "UseBy" => "N", "Type" => "double" },
183
+ "z" => { "UseBy" => "N", "Type" => "double" }
184
+ }
185
+
186
+ ## Const: Graph attributs
187
+ GRAPHSATTRS = [
188
+ "bgcolor",
189
+ "center",
190
+ "clusterrank",
191
+ "color",
192
+ "comment",
193
+ "compound",
194
+ "concentrate",
195
+ "fillcolor",
196
+ "fontcolor",
197
+ "fontname",
198
+ "fontpath",
199
+ "fontsize",
200
+ "label",
201
+ "labeljust",
202
+ "labelloc",
203
+ "layer",
204
+ "margin",
205
+ "mclimit",
206
+ "nodesep",
207
+ "nslimit",
208
+ "nslimit1",
209
+ "ordering",
210
+ "orientation",
211
+ "page",
212
+ "pagedir",
213
+ "quantum",
214
+ "rank",
215
+ "rankdir",
216
+ "ranksep",
217
+ "ratio",
218
+ "remincross",
219
+ "rotate",
220
+ "samplepoints",
221
+ "searchsize",
222
+ "size",
223
+ "style",
224
+ "URL"
225
+ ]
226
+
227
+ ## Const: Node attributs
228
+ NODESATTRS = [
229
+ "bottomlabel",
230
+ "color",
231
+ "comment",
232
+ "distortion",
233
+ "fillcolor",
234
+ "fixedsize",
235
+ "fontcolor",
236
+ "fontname",
237
+ "fontsize",
238
+ "group",
239
+ "height",
240
+ "label", "html",
241
+ "layer",
242
+ "orientation",
243
+ "peripheries",
244
+ "regular",
245
+ "shape",
246
+ "shapefile",
247
+ "sides",
248
+ "skew",
249
+ "style",
250
+ "toplabel",
251
+ "URL",
252
+ "width",
253
+ "z"
254
+ ]
255
+
256
+ ## Const: Edge attributs
257
+ EDGESATTRS = [
258
+ "arrowhead",
259
+ "arrowsize",
260
+ "arrowtail",
261
+ "color",
262
+ "constraint",
263
+ "decorate",
264
+ "dir",
265
+ "fontcolor",
266
+ "fontname",
267
+ "fontsize",
268
+ "headlabel",
269
+ "headport",
270
+ "hearURL",
271
+ "label",
272
+ "labelangle",
273
+ "labeldistance",
274
+ "labelfloat",
275
+ "labelfontcolor",
276
+ "labelfontname",
277
+ "labelfontsize",
278
+ "layer",
279
+ "lhead",
280
+ "ltail",
281
+ "minlen",
282
+ "samehead",
283
+ "sametail",
284
+ "style",
285
+ "taillael",
286
+ "tailport",
287
+ "tailURL",
288
+ "weight"
289
+ ]
290
+ end