class GraphViz
Constants
for ruby-graphviz
GraphViz::Constants::FORMATS: the possible output formats
"bmp", "canon", "dot", "xdot", "cmap", "dia", "eps", "fig", "gd", "gd2", "gif", "gtk", "hpgl", "ico", "imap", "cmapx", "imap_np", "cmapx_np", "ismap", "jpeg", "jpg", "jpe", "mif", "mp", "pcl", "pdf", "pic", "plain", "plain-ext", "png", "ps", "ps2", "svg", "svgz", "tga", "tiff", "tif", "vml", "vmlz", "vrml", "vtx", "wbmp", "xlib", "none"
GraphViz::Constants::PROGRAMS: The possible programs
"dot", "neato", "twopi", "fdp", "circo"
GraphViz::Constants::GRAPHTYPE The possible types of graph
"digraph", "graph"
The single letter codes used in constructors map as follows:
G => The root graph, with GRAPHATTRS E => Edge, with EDGESATTRS N => Node, with NODESATTRS S => subgraph C => cluster
>> x = “hellont\l"world"”
> “hellont\l"world"”¶ ↑
>> puts x.inspect.gsub( “\\”, “\” ) “hellontl"world"”
OR
>> x = 'hellontl“world”'
> “hello\n\t\l"world"”¶ ↑
>> puts x.inspect.gsub( “\\”, “\” ) “hellontl"world"”
bool
For the bool type,
-
TRUE values are represented by “true” or “yes” (case-insensitive), true and any non-zero integer
-
FALSE values by “false”, “no” or empty string (case-insensitive), false and zero.
Example
graph[:center] = "true"
or
graph[:center] = true
or
graph[:center] = 23
TODO: Remove
spliteType or point
spline ( ';' spline )* where spline = (endp)? (startp)? point (triple)+ and triple = point point point and endp = “e,%f,%f” and startp = “s,%f,%f”
If a spline has points p1 p2 p3 … pn, (n = 1 (mod 3)), the points correspond to the control points of a B-spline from p1 to pn. If startp is given, it touches one node of the edge, and the arrowhead goes from p1 to startp. If startp is not given, p1 touches a node. Similarly for pn and endp.
Attributes
This accessor allow you to set global edges attributes
This accessor allow you to set global edges attributes
This accessor allow you to set global graph attributes
This accessor allow you to set global graph attributes
This accessor allow you to set global nodes attributes
This accessor allow you to set global nodes attributes
Public Class Methods
Change default options (:use, :path, :errors and :output)
# File lib/graphviz.rb, line 724 def self.default( hOpts ) hOpts.each do |k, v| case k.to_s when "use" @@prog = v when "path" @@path = v.split( "," ).map{ |x| x.strip } when "errors" @@errors = v when "extlibs" @@extlibs = v.split( "," ).map{ |x| x.strip } else warn "Invalid option #{k}!" end end end
# File lib/graphviz/nothugly.rb, line 54 def self.libxslt_transform(xml, xsl) LibXML::XML.default_load_external_dtd = false LibXML::XML.default_substitute_entities = false stylesheet_doc = LibXML::XML::Document.file(xsl) stylesheet = LibXSLT::XSLT::Stylesheet.new(stylesheet_doc) xml_doc = LibXML::XML::Document.file(xml) stylesheet.apply(xml_doc).to_s end
Create a new graph object
Options :
-
:output : Output format (GraphViz::Constants::FORMATS) (default : dot)
-
:file : Output file name (default : nil)
-
:use : Program to use (GraphViz::Constants::PROGRAMS) (default : dot)
-
:path : Program PATH
-
:parent : Parent graph (default : nil)
-
:type : Graph type (GraphViz::Constants::GRAPHTYPE) (default : digraph)
-
:errors : DOT error level (default 1)
-
0 = Error + Warning
-
1 = Error
-
2 = none
-
# File lib/graphviz.rb, line 824 def initialize( xGraphName, hOpts = {}, &block ) @filename = nil @name = xGraphName.to_s @format = @@format @prog = @@prog @path = @@path @errors = @@errors @extlibs = @@extlibs @output = {} @nothugly = false @strict = false @scale = nil @inverty = nil @no_layout = nil @reduce_graph = nil @Lg = nil @LO = nil @Ln = nil @LU = nil @LC = nil @LT = nil @elements_order = GraphViz::Elements::new() @oParentGraph = nil @oGraphType = "digraph" @hoNodes = Hash::new() @loEdges = Array::new() @hoGraphs = Hash::new() @node = GraphViz::Attrs::new( self, "node", NODESATTRS ) @edge = GraphViz::Attrs::new( self, "edge", EDGESATTRS ) @graph = GraphViz::Attrs::new( self, "graph", GRAPHSATTRS ) hOpts.each do |xKey, xValue| case xKey.to_s when "use" if PROGRAMS.index( xValue.to_s ).nil? raise ArgumentError, "can't use '#{xValue}'" end @prog = xValue.to_s when "parent" @oParentGraph = xValue when "type" if GRAPHTYPE.index( xValue.to_s ).nil? raise ArgumentError, "graph type '#{xValue}' unknow" end @oGraphType = xValue.to_s when "path" @path = xValue.split( "," ).map{ |x| x.strip } when "strict" @strict = (xValue ? true : false) when "errors" @errors = xValue when "extlibs" @extlibs = xValue.split( "," ).map{ |x| x.strip } else self[xKey.to_s] = xValue.to_s end end yield( self ) if( block ) end
Transform to pretty up the SVG output
For more information, see hokstad.com/making-graphviz-output-pretty-with-xsl and hokstad.com/making-graphviz-output-pretty-with-xsl-updated
You can use the :nothugly option to GraphViz#output
:
graph.output( :svg => "myGraph.svg", :nothugly => true )
Or directly on an SVG output graph :
GraphViz.nothugly( "myGraph.svg" )
# File lib/graphviz/nothugly.rb, line 33 def self.nothugly( file, save = true ) xsl = File.join( File.dirname(File.expand_path(__FILE__)), "nothugly", "nothugly.xsl" ) out = self.send(XSLT_METHOD, file, xsl) if save fname = File.join( File.dirname(File.expand_path(file)), File.basename(file)) File.open( fname, "w" ) { |io| io.print out } else return out end end
# File lib/graphviz.rb, line 741 def self.options( hOpts ) GraphViz::default( hOpts ) end
Create a new graph from a GraphViz
File
Options :
-
:output : Output format (GraphViz::Constants::FORMATS) (default : dot)
-
:file : Output file name (default : none)
-
:use : Program to use (GraphViz::Constants::PROGRAMS) (default : dot)
-
:path : Program PATH
# File lib/graphviz.rb, line 755 def self.parse( xFile, hOpts = {}, &block ) graph = Dot2Ruby::new( hOpts[:path], nil, nil ).eval( xFile ) yield( graph ) if( block and graph ) return graph end
Create a new graph from a GraphViz
File
Options :
-
:output : Output format (GraphViz::Constants::FORMATS) (default : dot)
-
:file : Output file name (default : none)
-
:use : Program to use (GraphViz::Constants::PROGRAMS) (default : dot)
-
:path : Program PATH
# File lib/graphviz.rb, line 769 def self.parse_string( str, hOpts = {}, &block ) graph = Dot2Ruby::new(hOpts[:path], nil, nil).eval_string(str) yield(graph) if(block and graph) return graph end
# File lib/graphviz/nothugly.rb, line 47 def self.xml_xslt_transform(xml, xsl) xslt = XML::XSLT.new() xslt.xml = xml xslt.xsl = xsl xslt.serve() end
Private Class Methods
Create a new directed graph
See also GraphViz::new
# File lib/graphviz.rb, line 917 def self.digraph( xGraphName, hOpts = {}, &block ) new( xGraphName, hOpts.symbolize_keys.merge( {:type => "digraph"} ), &block ) end
Create a random graph.
# File lib/graphviz.rb, line 929 def self.generate(num_nodes, num_edges, directed = false, weight_range = (1..1)) g = nil if directed g = GraphViz.digraph(:G) else g = GraphViz.graph(:G) end nodes = (1..num_nodes).map{ |e| e.to_s } g.add_nodes(nodes) edges = [] nodes.each do |head| nodes.each do |tail| if (directed and head != tail) or (head < tail) edges << {:head => head, :tail => tail, :weight => weight_range.to_a.shuffle[0]} end end end edges.shuffle! (num_edges - 1).times do |i| g.add_edges(edges[i][:head], edges[i][:tail], :label => edges[i][:weight].to_s, :weight => edges[i][:weight]) end return g end
Create a new undirected graph
See also GraphViz::new
# File lib/graphviz.rb, line 908 def self.graph( xGraphName, hOpts = {}, &block ) new( xGraphName, hOpts.symbolize_keys.merge( {:type => "graph"} ), &block ) end
Create a new strict directed graph
See also GraphViz::new
# File lib/graphviz.rb, line 924 def self.strict_digraph( xGraphName, hOpts = {}, &block ) new( xGraphName, hOpts.symbolize_keys.merge( {:type => "digraph", :strict => true} ), &block ) end
Public Instance Methods
Create an edge between the current cluster and the node or cluster oNode
# File lib/graphviz.rb, line 670 def <<( oNode ) raise( ArgumentError, "Edge between root graph and node or cluster not allowed!" ) if self.pg.nil? if( oNode.class == Array ) oNode.each do |no| self << no end else return GraphViz::commonGraph( oNode, self ).add_edges( self, oNode ) end end
Get the value of the graph attribute xAttrName
# File lib/graphviz.rb, line 394 def []( xAttrName ) if Hash === xAttrName xAttrName.each do |key, value| self[key] = value end else attr = @graph[xAttrName] if attr.nil? return nil else return( @graph[xAttrName].clone ) end end end
Set value xValue
to the graph attribute xAttrName
# File lib/graphviz.rb, line 386 def []=( xAttrName, xValue ) xValue = xValue.to_s if xValue.class == Symbol @graph[xAttrName] = xValue end
Add nodes and edges defined by a Hash
# File lib/graphviz.rb, line 332 def add(h) if h.kind_of? Hash h.each do |node, data| add_hash_edge(node, data) end end end
# File lib/graphviz.rb, line 191 def add_edge( oNodeOne, oNodeTwo, hOpts = {} ) if oNodeTwo.kind_of? Array or oNodeOne.kind_of? Array raise ArgumentError, "use `add_edges' to add several edges at the same time" end add_edges(oNodeOne, oNodeTwo, hOpts) end
Create a new edge
In:
-
node_one : First node (or node list)
-
node_two : Second
Node
(or node list) -
options :
Edge
attributes
# File lib/graphviz.rb, line 204 def add_edges( node_one, node_two, options = {} ) if( node_one.class == Array ) node_one.each do |no| add_edges( no, node_two, options ) end else if( node_two.class == Array ) node_two.each do |nt| add_edges( node_one, nt, options ) end else edge = GraphViz::Edge::new( node_one, node_two, self ) edge.index = @elements_order.size_of( "edge" ) options.each do |xKey, xValue| edge[xKey.to_s] = xValue end @elements_order.push( { "type" => "edge", "value" => edge } ) @loEdges.push( edge ) return( edge ) end end end
Create a new graph
In:
-
xGraphName : Graph name
-
hOpts : Graph attributes
# File lib/graphviz.rb, line 269 def add_graph( xGraphName = nil, hOpts = {}, &block ) if xGraphName.kind_of?(GraphViz) xGraphID = xGraphName.id @hoGraphs[xGraphID] = xGraphName.clone @hoGraphs[xGraphID].type = @oGraphType @hoGraphs[xGraphID].pg = self xGraphName = xGraphID else if xGraphName.kind_of?(Hash) hOpts = xGraphName xGraphName = nil end if xGraphName.nil? xGraphID = String.random(11) xGraphName = "" else xGraphID = xGraphName end @hoGraphs[xGraphID] = GraphViz::new( xGraphName, {:parent => self, :type => @oGraphType}, &block ) hOpts.each do |xKey, xValue| @hoGraphs[xGraphID][xKey.to_s] = xValue end end @elements_order.push( { "type" => "graph", "name" => xGraphName, "value" => @hoGraphs[xGraphID] } ) return( @hoGraphs[xGraphID] ) end
# File lib/graphviz.rb, line 85 def add_node( xNodeName, hOpts = {} ) if xNodeName.kind_of? Array raise ArgumentError, "use `add_nodes' to add several nodes at the same time" end return add_nodes(xNodeName, hOpts) end
Create a new node
In:
-
xNodeName : Name of the new node
-
hOpts :
Node
attributes
Return the GraphViz::Node
object created
# File lib/graphviz.rb, line 99 def add_nodes(node_name, options = {}) if node_name.kind_of? Array node_name.each { |n| add_nodes(n, options.clone) } else node = @hoNodes[node_name] if node.nil? @hoNodes[node_name] = GraphViz::Node::new( node_name, self ) @hoNodes[node_name].index = @elements_order.size_of( "node" ) unless options.keys.include?(:label) or options.keys.include?("label") options[:label] = node_name end @elements_order.push( { "type" => "node", "name" => node_name, "value" => @hoNodes[node_name] } ) node = @hoNodes[node_name] end options.each do |xKey, xValue| @hoNodes[node_name][xKey.to_s] = xValue end return node end end
# File lib/graphviz.rb, line 627 def append_attributes_and_types(script) script_data = DOTScriptData.new @elements_order.each { |kElement| is_new_type = script_data.type != kElement["type"] if is_new_type script << script_data unless script_data.type.nil? or script_data.empty? script_data = DOTScriptData.new(kElement["type"]) end # Modified by Brandon Coleman verify value is NOT NULL kElement["value"] or raise ArgumentError, "#{kElement["name"]} is nil!" case kElement["type"] when "graph_attr", "node_attr", "edge_attr" script_data.add_attribute(kElement["name"], kElement["value"].to_gv) when "node", "graph" script << kElement["value"].output() when "edge" script << " " + kElement["value"].output( @oGraphType ) else raise ArgumentError, "Don't know what to do with element type '#{kElement['type']}'" end } script << script_data unless script_data.type.nil? or script_data.empty? end
Return a new completed graph
# File lib/graphviz.rb, line 776 def complete GraphViz.parse_string( root_graph.output( :dot => String ) ) end
Complete the current graph
# File lib/graphviz.rb, line 781 def complete! # TODO: Keep options complete end
Return true if the graph is directed.
# File lib/graphviz.rb, line 787 def directed? not((/digraph/ =~ "bla digraph bla").nil?) end
# File lib/graphviz.rb, line 418 def each_attribut(&b) warn "`GraphViz#each_attribut` is deprecated, please use `GraphViz#each_attribute`" each_attribute(&b) end
Calls block once for each attribute of the graph, passing the name and value to the block as a two-element array.
# File lib/graphviz.rb, line 413 def each_attribute(&b) @graph.each do |k,v| yield(k,v) end end
Allow you to traverse edges
# File lib/graphviz.rb, line 237 def each_edge( &block ) if block_given? @loEdges.each do |edge| yield(edge) end else return @loEdges end end
Allow you to traverse graphs
# File lib/graphviz.rb, line 319 def each_graph( &block ) if block_given? @hoGraphs.each do |name, graph| yield( name, graph ) end else return @hoGraphs end end
Allow you to traverse nodes
# File lib/graphviz.rb, line 176 def each_node( &block ) if block_given? @hoNodes.each do |name, node| yield( name, node ) end else return( @hoNodes ) end end
Get the number of edges
# File lib/graphviz.rb, line 250 def edge_count @loEdges.size end
# File lib/graphviz.rb, line 156 def enumerate_nodes nodes = @hoNodes.keys each_graph { |_, g| child_nodes = g.enumerate_nodes nodes += child_nodes } return nodes end
Returns the first node found in the entire graph, starting from the root graph
# File lib/graphviz.rb, line 140 def find_node(name) root = root_graph return root.search_node(name) end
Return the edge object for the given index
# File lib/graphviz.rb, line 257 def get_edge_at_index( index ) element = @elements_order[index, "edge"] (element.nil?) ? nil : element["value"] end
Return the graph object for the given name (or nil)
# File lib/graphviz.rb, line 308 def get_graph( xGraphName, &block ) graph = @hoGraphs[xGraphName] || nil yield( graph ) if( block and graph ) return graph end
Return the node object for the given name (or nil) in the current graph
# File lib/graphviz.rb, line 131 def get_node( xNodeName, &block ) node = @hoNodes[xNodeName] || nil yield( node ) if( block and node ) return node end
Return the node object for the given index
# File lib/graphviz.rb, line 168 def get_node_at_index( index ) element = @elements_order[index, "node"] (element.nil?) ? nil : element["value"] end
Get the number of graphs
# File lib/graphviz.rb, line 353 def graph_count @hoGraphs.size end
# File lib/graphviz.rb, line 791 def has_parent_graph? not @oParentGraph.nil? end
Get the graph name
# File lib/graphviz.rb, line 662 def name @name.clone end
Get the number of nodes
# File lib/graphviz.rb, line 187 def node_count @hoNodes.size end
Generate the graph
Options :
-
:output : Output format (GraphViz::Constants::FORMATS)
-
:file : Output file name
-
:use : Program to use (GraphViz::Constants::PROGRAMS)
-
:path : Program PATH
-
:<format> => <file> : <file> can be
-
:errors : DOT error level (default 1)
-
0 = Error + Warning
-
1 = Error
-
2 = none
-
# File lib/graphviz.rb, line 447 def output( hOpts = {} ) xDOTScript = DOTScript.new lNotHugly = [] append_attributes_and_types(xDOTScript) xDOTScript << "}" if has_parent_graph? xDOTScript.make_subgraph("#{GraphViz.escape(@name, :unquote_empty => true)}") else hOutput = {} hOpts.each do |xKey, xValue| xValue = xValue.to_s unless xValue.nil? or [Class, TrueClass, FalseClass].include?(xValue.class) case xKey.to_s when "use" if PROGRAMS.index( xValue ).nil? raise ArgumentError, "can't use '#{xValue}'" end @prog = xValue when "path" @path = xValue && xValue.split( "," ).map{ |x| x.strip } when "errors" @errors = xValue when "extlib" @extlibs = xValue.split( "," ).map{ |x| x.strip } when "scale" # Scale input by 'v' (=72) @scale = xValue when "inverty" # Invert y coordinate in output @inverty = xValue when "no_layout" # No layout mode 'v' (=1) @no_layout = xValue when "reduce" # Reduce graph @reduce_graph = xValue when "Lg" # Don't use grid @Lg = xValue when "LO" # Use old attractive force @LO = xValue when "Ln" # Set number of iterations to i @Ln = xValue when "LU" # Set unscaled factor to i @LU = xValue when "LC" # Set overlap expansion factor to v @LC = xValue when "LT" # Set temperature (temperature factor) to v @LT = xValue when "nothugly" begin require 'graphviz/nothugly' @nothugly = true rescue LoadError warn "You must install ruby-xslt or libxslt-ruby to use nothugly option!" @nothugly = false end else if FORMATS.index( xKey.to_s ).nil? raise ArgumentError, "output format '#{xValue}' invalid" end hOutput[xKey.to_s] = xValue end end @output = hOutput if hOutput.size > 0 xStict = ((@strict && @oGraphType == "digraph") ? "strict " : "") xDOTScript.prepend( "#{xStict}#{@oGraphType} #{GraphViz.escape(@name, :unquote_empty => true)} {" ) xOutputString = (@filename == String || @output.any? {|format, file| file == String }) xOutput = "" if @format.to_s == "none" or @output.any? {|fmt, fn| fmt.to_s == "none"} if xOutputString xOutput << xDOTScript else xFileName = @output["none"] || @filename open( xFileName, "w" ) do |h| h.puts xDOTScript end end end if (@format.to_s != "none" and not @format.nil?) or (@output.any? {|format, file| format != "none" } and @output.size > 0) ## Act: Save script and send it to dot t = Tempfile::open( File.basename(__FILE__) ) t.print( xDOTScript ) t.close cmd = find_executable( @prog, @path ) if cmd == nil raise StandardError, "GraphViz not installed or #{@prog} not in PATH. Install GraphViz or use the 'path' option" end xOutputWithFile = [] xOutputWithoutFile = [] unless @format.nil? or @format == "none" lNotHugly << @filename if @format.to_s == "svg" and @nothugly if @filename.nil? or @filename == String xOutputWithoutFile = ["-T#{@format}"] else xOutputWithFile = ["-T#{@format}", "-o#{@filename}"] end end @output.each_except( :key => ["none"] ) do |format, file| lNotHugly << file if format.to_s == "svg" and @nothugly if file.nil? or file == String xOutputWithoutFile += ["-T#{format}"] else xOutputWithFile += ["-T#{format}", "-o#{file}"] end end xExternalLibraries = @extlibs.map { |lib| "-l#{lib}" } xOtherOptions = [] xOtherOptions << "-s#{@scale}" if @scale xOtherOptions << "-y" if @inverty xOtherOptions << "-n#{@no_layout}" if @no_layout xOtherOptions << "-x" if @reduce_graph xOtherOptions << "-Lg" if @Lg xOtherOptions << "-LO" if @LO xOtherOptions << "-Ln#{@Ln}" if @Ln xOtherOptions << "-LU#{@LU}" if @LU xOtherOptions << "-LC#{@LC}" if @LC xOtherOptions << "-LT#{@LT}" if @LT tmpPath = if IS_JRUBY t.path elsif IS_CYGWIN begin IO.popen("cygpath", "-w", t.path).chomp rescue warn "cygpath is not installed!" t.path end else t.path end xCmd = [cmd, "-q#{@errors}"] + xExternalLibraries + xOtherOptions + xOutputWithFile + xOutputWithoutFile + [tmpPath] xOutput << output_from_command( xCmd ) end # Not Hugly lNotHugly.each do |f| if f.nil? or f == String xOutput = GraphViz.nothugly( xOutput, false ) else GraphViz.nothugly( f, true ) end end if xOutputString xOutput else print xOutput end end end
Return the root graph
# File lib/graphviz.rb, line 695 def root_graph return( (self.pg.nil?) ? self : self.pg.root_graph ) end
Return the first node found in the current graph, and it subgraphs
# File lib/graphviz.rb, line 146 def search_node(name) n = get_node(name) return n unless n.nil? each_graph { |_, g| n = g.search_node(name) return n unless n.nil? } return nil end
Create a new graph from the current subgraph
# File lib/graphviz.rb, line 424 def to_graph graph = self.clone graph.pg = nil return graph end
# File lib/graphviz.rb, line 655 def to_s self.output(:none => String) end
Return the graph type (graph digraph)
# File lib/graphviz.rb, line 343 def type @oGraphType end
Private Instance Methods
Edge
between a node and a Hash
Used by GraphViz#add
# File lib/graphviz.rb, line 892 def add_hash_edge(node, hash) if hash.kind_of? Hash hash.each do |nt, data| add_edges(node, nt) add_hash_edge(nt, data) end else add_edges(node, hash) end end