class Seafoam::GraphvizWriter
A writer from graphs to the Graphviz DOT format, including all the formatting.
Constants
- EDGE_COLORS
Color theme.
- NODE_COLORS
Public Class Methods
new(stream)
click to toggle source
# File lib/seafoam/graphviz_writer.rb, line 5 def initialize(stream) @stream = stream end
Public Instance Methods
write_graph(graph, hidpi = false, draw_blocks = false)
click to toggle source
Write a graph.
# File lib/seafoam/graphviz_writer.rb, line 10 def write_graph(graph, hidpi = false, draw_blocks = false) inline_attrs = {} attrs = {} attrs[:dpi] = 200 if hidpi attrs[:bgcolor] = 'white' @stream.puts 'digraph G {' @stream.puts " graph #{write_attrs(attrs)};" write_nodes inline_attrs, graph write_edges inline_attrs, graph write_blocks graph if draw_blocks @stream.puts '}' end
Private Instance Methods
quote(string)
click to toggle source
Quote and escape a string.
# File lib/seafoam/graphviz_writer.rb, line 204 def quote(string) string = string.to_s string = string.gsub('\\', '\\\\') string = string.gsub('"', '\\"') "\"#{string}\"" end
shade(attrs)
click to toggle source
Return attributes for a node or edge modified to 'shade' them in terms the spotlight functionality - so basically make them light grey.
# File lib/seafoam/graphviz_writer.rb, line 190 def shade(attrs) attrs = attrs.dup attrs[:color] = ICE_STONE attrs[:fontcolor] = ICE_STONE attrs[:fillcolor] = DUST attrs end
write_attrs(attrs)
click to toggle source
Write a hash of key-value attributes into the DOT format.
# File lib/seafoam/graphviz_writer.rb, line 199 def write_attrs(attrs) '[' + attrs.reject { |_k, v| v.nil? }.map { |k, v| "#{k}=#{quote(v)}" }.join(',') + ']' end
write_blocks(graph)
click to toggle source
Write basic block outlines.
# File lib/seafoam/graphviz_writer.rb, line 171 def write_blocks(graph) graph.blocks.each do |block| @stream.puts " subgraph cluster_block#{block.id} {" @stream.puts ' fontname = "Arial";' @stream.puts " label = \"B#{block.id}\";" @stream.puts ' style=dotted;' block.nodes.each do |node| next if node.props[:hidden] || node.props[:inlined] @stream.puts " node#{node.id};" end @stream.puts ' }' end end
write_edge(inline_attrs, edge)
click to toggle source
# File lib/seafoam/graphviz_writer.rb, line 107 def write_edge(inline_attrs, edge) # We're going to build up a hash of Graphviz drawing attributes. attrs = {} label = edge.props[:label] if edge.from.props[:out_annotation] label = "#{label} #{edge.from.props[:out_annotation]}" end attrs[:label] = label # Basic edge attributes. attrs[:fontname] = 'arial' color = EDGE_COLORS[edge.props[:kind]] attrs[:color] = EDGE_COLORS[edge.props[:kind]] attrs[:fontcolor] = color # Properties depending on the kind of edge. case edge.props[:kind] when 'control' attrs[:penwidth] = 2 when 'loop' attrs[:penwidth] = 4 when 'info' attrs[:style] = 'dashed' end # Reversed edges. attrs[:dir] = 'back' if edge.props[:reverse] # Convert attributes to shaded if any edges involved are shaded. attrs = shade(attrs) if edge.nodes.any? { |n| n.props[:spotlight] == 'shaded' } # Does this edge come from an inlined node? if edge.from.props[:inlined] # An inlined edge is drawn as a new version of the from-node and an # edge from that new version directly to the to-node. With only one # user it's a short edge and the from-node is show directly above the # to-node. if edge.to.props[:spotlight] == 'shaded' # Draw inlined edges to shaded nodes as invisible. node_attrs = { label: '', style: 'invis' } else # Get attributes from when we went through nodes earlier. node_attrs = inline_attrs[edge.from.id] end # Inlined nodes skip the arrow for simplicity. attrs[:arrowhead] = 'none' attrs[:fontsize] = '8' # Declare a new node just for this user. @stream.puts " inline#{edge.from.id}x#{edge.to.id} #{write_attrs(node_attrs)};" # Declare the edge. @stream.puts " inline#{edge.from.id}x#{edge.to.id} -> node#{edge.to.id} #{write_attrs(attrs)};" else # Declare the edge. @stream.puts " node#{edge.from.id} -> node#{edge.to.id} #{write_attrs(attrs)};" end end
write_edges(inline_attrs, graph)
click to toggle source
Write edge declarations.
# File lib/seafoam/graphviz_writer.rb, line 93 def write_edges(inline_attrs, graph) graph.edges.each do |edge| # Skip the edge if it's from a node that is hidden and it doesn't point # to a shaded node. next if edge.from.props[:hidden] && edge.to.props[:spotlight] != 'shaded' # Skip the edge if it's to a node that is hidden and it doesn't come # from a shaded node. next if edge.to.props[:hidden] && edge.from.props[:spotlight] != 'shaded' write_edge inline_attrs, edge end end
write_node(inline_attrs, node)
click to toggle source
# File lib/seafoam/graphviz_writer.rb, line 32 def write_node(inline_attrs, node) # We're going to build up a hash of Graphviz drawing attributes. attrs = {} # The node is hidden, and it's not going to be inlined above any # other node. if node.props[:hidden] && !node.props[:inlined] # If the node has any adjacent nodes that are not hidden, and are # shaded, then we need to declare the node but make it invisible so # the edge appears, pointing off into space, but the node does not. if node.adjacent.any? { |a| !a.props[:hidden] && a.props[:spotlight] == 'shaded' } attrs[:style] = 'invis' attrs[:label] = '' @stream.puts " node#{node.id} #{write_attrs(attrs)};" end else # This is a visible node. # Give it a label. if node.props[:label] attrs[:label] = "#{node.id} #{node.props[:label]}" else # If we really still don't have a label, just use the ID. attrs[:label] = node.id.to_s end # Basic attributes for a node. attrs[:shape] = 'rectangle' attrs[:fontname] = 'Arial' attrs[:style] = 'filled' attrs[:color] = 'black' # Color depends on the kind of node. back_color, fore_color = NODE_COLORS[node.props[:kind]] attrs[:fillcolor] = back_color attrs[:fontcolor] = fore_color if node.props[:inlined] # If the node is to be inlined then draw it smaller and a different # shape. attrs[:shape] = 'oval' attrs[:fontsize] = '8' # Just record these attributes for where it's used by other nodes # so it can be drawn above them - don't actually declare a node. inline_attrs[node.id] = attrs else attrs[:shape] = 'diamond' if node.props[:kind] == 'calc' # If the node is shaded, convert the attributes to the shaded # version. attrs = shade(attrs) if node.props[:spotlight] == 'shaded' # Declare the node. @stream.puts " node#{node.id} #{write_attrs(attrs)};" end end end
write_nodes(inline_attrs, graph)
click to toggle source
Write node declarations.
# File lib/seafoam/graphviz_writer.rb, line 26 def write_nodes(inline_attrs, graph) graph.nodes.each_value do |node| write_node inline_attrs, node end end