class Petrinet::GraphvizBuilder
Public Class Methods
new(net, transition_vector_by_transition_name, place_name_by_place_index, state_vector)
click to toggle source
# File lib/petrinet/graphviz_builder.rb, line 6 def initialize(net, transition_vector_by_transition_name, place_name_by_place_index, state_vector) @net = net @transition_vectors_by_transition_name = transition_vector_by_transition_name @place_name_by_place_index = place_name_by_place_index @state_vector = state_vector end
Public Instance Methods
svg()
click to toggle source
Generates an SVG for a net
# File lib/petrinet/graphviz_builder.rb, line 14 def svg dot_source = dot dotfile = Tempfile.new("petrinet.dot") dotfile.write(dot_source) dotfile.close svgfile = Tempfile.new('petrinet.svg') # circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopi `dot -T svg -Kdot #{dotfile.path} -o #{svgfile.path}` `cat #{svgfile.path}` svg = svgfile.read processed_svg(svg) end
Private Instance Methods
dot()
click to toggle source
# File lib/petrinet/graphviz_builder.rb, line 36 def dot transition_vectors_by_transition_name = Hash[@transition_vectors_by_transition_name.sort] place_name_by_place_index = Hash[@place_name_by_place_index.sort] dot = <<-EOS digraph PetriNet { graph [ bgcolor=white, labeljust=l, labelloc=t, nodesep=0.5, penwidth=0, ranksep=0.5, style=filled ]; node [label="\\N"]; EOS place_name_by_place_index.each do |place_index, place_name| marking = @state_vector[place_index] dot += <<-EOS subgraph "cluster_place_#{place_name}" { graph [ label="#{place_name}", ]; node [shape=circle]; "place_#{place_name}" [ label=#{marking}, width=0.75 ]; } EOS end transition_vectors_by_transition_name.each do |transition_name, transition_vectors| fillcolor = if @net.prefire_transition_name == transition_name 'green' else @net.fireable?(transition_name) ? 'red' : 'black' end dot += <<-EOS subgraph "cluster_transition_#{transition_name}" { graph [ label="#{transition_name}", ]; node [ shape=box, fillcolor="#{fillcolor}", style="solid, filled", height=0.1, width=0.5 ]; "transition_#{transition_name}" [ label="", height=0.1, width=0.5 ]; } EOS transition_vectors.each do |transition_vector| transition_vector.each_with_index do |direction, place_index| place_name = place_name_by_place_index[place_index] raise "No place_name for index #{place_index}: #{place_name_by_place_index}" if place_name.nil? if direction < 0 dot += %Q{ "place_#{place_name}" -> "transition_#{transition_name}"\n} elsif direction > 0 dot += %Q{ "transition_#{transition_name}" -> "place_#{place_name}"\n} end end end end dot += "}\n" dot end
draw_tokens(doc)
click to toggle source
Replaces the place labels (which are numbers) with black dots.
# File lib/petrinet/graphviz_builder.rb, line 114 def draw_tokens(doc) # place radius (outer) pr = 6 # place radius (inner = with padding) pri = pr * 0.8 texts = doc.search('text') texts.each do |text| circle = (text.parent.search('ellipse') || text.parent.search('circle'))[0] if circle cx = circle[:cx].to_i cy = circle[:cy].to_i case text.text when '0' when '1' text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx}" cy="#{cy}" r="#{pri}" />} when '2' text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - pr}" cy="#{cy}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + pr}" cy="#{cy}" r="#{pri}" />} when '3' fx_bot = 1 fy_bot = Math.tan(rad(30)) fx_top = 0 fy_top = 1 / Math.cos(rad(30)) text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />} when '4' fx_bot = fy_bot = fx_top = fy_top = 1 text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />} when '5' fx_bot = fy_bot = fx_top = fy_top = 2 * Math.sin(rad(45)) text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />} text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx}" cy="#{cy}" r="#{pri}" />} else raise "Cannot draw dots for #{text.text} tokens" end text.remove end end doc end
processed_svg(svg)
click to toggle source
# File lib/petrinet/graphviz_builder.rb, line 29 def processed_svg(svg) doc = Nokogiri::XML(svg) doc = draw_tokens(doc) doc = remove_rectangles(doc) doc.to_xml end
rad(y)
click to toggle source
# File lib/petrinet/graphviz_builder.rb, line 174 def rad(y) y % 360 * Math::PI / 180 end
remove_rectangles(doc)
click to toggle source
# File lib/petrinet/graphviz_builder.rb, line 166 def remove_rectangles(doc) polygons = doc.xpath('//svg:polygon[@fill="#ffffff" and @stroke="#000000"]', 'svg' => 'http://www.w3.org/2000/svg') polygons.each do |polygon| polygon.remove end doc end