Skip to content

Instantly share code, notes, and snippets.

@melborne
Created February 3, 2010 10:42
Show Gist options
  • Save melborne/293539 to your computer and use it in GitHub Desktop.
Save melborne/293539 to your computer and use it in GitHub Desktop.
GraphViz wrapper to make animation gif
require "graphviz"
require "RMagick"
class GraphAz
attr_accessor :graph, :nodes, :edges, :gnode, :gedge
def initialize(name= :G, *opt, &blk)
@graph = GraphViz.new(name, *opt, &blk)
@gnode, @gedge = @graph.node, @graph.edge
@nodes, @edges = [], {}
@@lap, @laps = 0, []
end
def [](attr)
@graph[attr]
end
def []=(attr, val)
@graph[attr] = val
end
def add(s, attrs={})
names = s.split(/\s*=>\s*/).map { |n| n.sub(/^:/, '') }
names.map! { |name| @nodes << node = @graph.add_node(name, attrs); node }
names.each_cons(2) do |a, b|
@edges["#{a.name}_#{b.name}"] = @graph.add_edge(a, b)
end
@graph
end
def node(*nodes, opts)
nodes.each do |node|
opts.each { |attr, val| @graph.get_node(node)[attr] = val }
end
end
def edge(*edges, opts)
@edges.each do |name, edge|
next unless edges.empty? or edges.include?(name)
opts.each { |attr, val| edge.set { |e| e.send(attr, val) } }
end
end
def print_graph(args={}, &blk)
@graph.output(args, &blk)
end
def lap(args={:png => "out#{@@lap}.png"}, &blk)
print_graph(args, &blk)
@@lap += 1
@laps << args[:png]
end
def write(opts={})
puts 'processing...'
opts = {:delay => 50, :filename => 'out.gif', :delete => true}.merge(opts)
list = Magick::ImageList.new(*@laps)
list.delay = opts[:delay]
list.write(opts[:filename])
puts "'#{opts[:filename]}' created!"
rescue Exception => e
puts "Failed attempt to create animation gif : #{e}"
ensure
@laps.each { |f| File.delete(f) if opts[:delete] }
@laps.clear
end
end
if __FILE__ == $0
ga = GraphAz.new(:Dijkstra, :type => 'graph')
ga['rankdir'] = 'LR'
ga['label'] = "Dijkstra's Algorithm"
ga['bgcolor'] = 'whitesmoke'
routes = [
':S => :A => :G',
':S => :B => :D => :G',
':S => :C => :D',
':A => :B => :C'
]
edge_labels = {
'2' => ['A_B', 'S_C', 'B_D'],
'3' => ['B_C'],
'4' => ['S_B', 'D_G'],
'5' => ['S_A'],
'6' => ['C_D', 'A_G']
}
routes.each { |route| ga.add route }
edge_labels.each { |label, edges| ga.edge(*edges, :label => label) }
ga.lap
color = 'blueviolet'
ga.node('S', :style => 'filled', :fillcolor => color)
ga.lap
ga.edge('S_B', :color => color)
ga.lap
ga.node('B', :style => 'filled', :fillcolor => color)
ga.lap
ga.edge('B_D', :color => color)
ga.lap
ga.node('D', :style => 'filled', :fillcolor => color)
ga.lap
ga.edge('D_G', :color => color)
ga.lap
ga.node('G', :style => 'filled', :fillcolor => color)
ga.lap
ga.write
end
require "graphaz"
ga = GraphAz.new(:RubyClasses)
ga[:label] = "Ruby Class Map"
ga[:fontsize] = 20
ga[:fontcolor] = 'darkviolet'
ga[:bgcolor] = "whitesmoke"
gnode_set = {
:shape => 'Mrecord',
:style => 'filled',
:height => 0.8,
:width => 1,
:fontcolor => 'white'
}
gnode_set.each { |attr, val| ga.gnode[attr] = val }
ga.gedge[:style] = 'dotted'
obj, mod, cla, sta, ins_a, ins_b, ins_c, ker, modA, modB =
"Object\nclass", "Module\nclass", "Class\nclass", "Standard\nclass", "Instance a", "Instance b", "Instance c", "Kernel\nmodule", "module A", "module B"
routes = [
"#{obj} => #{mod} => #{cla}",
"#{cla} => #{sta}",
"#{cla} => #{obj}",
"#{cla} => #{mod}",
"#{sta} => #{ins_a}",
"#{sta} => #{ins_b}",
"#{sta} => #{ins_c}",
"#{mod} => #{ker}",
"#{mod} => #{modA}",
"#{mod} => #{modB}",
"#{ker} => #{obj}"
]
def fill_with(color)
{:style => 'filled', :fillcolor => color }
end
routes.each { |route| ga.add route }
ga.node( ins_a, ins_b, ins_c, modA, modB, ker, :height => 0.5, :width => 0.7, :shape => 'ellipse' )
ga.lap
ga.node( cla, fill_with(:steelblue) )
ga.lap
ga.edge( "#{cla}_#{mod}", "#{cla}_#{obj}", "#{cla}_#{sta}", :style => 'bold', :color => 'steelblue' )
ga.lap
ga.node( obj, mod, sta, fill_with(:steelblue) )
ga.lap
ga.edge( "#{sta}_#{ins_a}", "#{sta}_#{ins_b}", "#{sta}_#{ins_c}", "#{mod}_#{modA}", "#{mod}_#{modB}", "#{mod}_#{ker}", :style => 'bold', :color => 'steelblue' )
ga.lap
ga.node( ins_a, ins_b, ins_c, modA, modB, ker, fill_with(:lightblue).merge(:fontcolor => 'midnightblue') )
ga.lap
ga.edge( "#{ker}_#{obj}", :style => 'none', :color => 'darkviolet' )
ga.lap
ga.node( cla, fill_with(:steelblue) )
ga.lap
ga.node( obj, fill_with(:maroon) )
ga.lap
ga.edge( "#{obj}_#{mod}", :style => 'bold', :color => 'maroon' )
ga.lap
ga.node( mod, fill_with(:maroon) )
ga.lap
ga.edge( "#{mod}_#{cla}", :style => 'bold', :color => 'maroon' )
ga.lap
ga.node( cla, fill_with(:maroon) )
ga.lap
ga.write
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment