File Coverage

blib/lib/Soar/WM/Grapher.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #
2             # This file is part of Soar-WM-Grapher
3             #
4             # This software is copyright (c) 2012 by Nathan Glenn.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             package Soar::WM::Grapher;
10              
11             # ABSTRACT: Utility for creating graphs of Soar's working memory
12 2     2   26735 use strict;
  2         4  
  2         87  
13 2     2   11 use warnings;
  2         6  
  2         167  
14 2     2   23 use Carp;
  2         6  
  2         196  
15 2     2   1868 use Soar::WM qw(wm_root_from_file);
  2         66555  
  2         112  
16 2     2   2177 use GraphViz;
  0            
  0            
17             use base qw(Exporter);
18             our @EXPORT_OK = qw(wm_graph);
19             use feature 'state';
20              
21             our $VERSION = '0.02'; # VERSION
22              
23             # print wm_graph(@ARGV)->as_svg('lawyers.svg') unless caller;
24             if ( !caller ) {
25             my $wm = Soar::WM->new( file => shift() );
26             my $g = wm_graph(
27             $wm, @ARGV,
28             layout => 'twopi',
29             ratio => 'compress',
30             overlap => 'scale'
31             );
32             $g->as_svg('wm_graph.svg');
33             }
34              
35             # wm_graph(@ARGV) unless caller;
36              
37             sub wm_graph {
38             my ( $wm, $id, $depth, @graph_args ) = @_;
39             if ( !( $wm && $id && $depth ) ) {
40             carp 'Usage: get_graph(filename, wme_id, depth)';
41             return;
42             }
43             if ( $depth < 1 ) {
44             carp 'depth argument must be 1 or more';
45             return;
46             }
47              
48             my $wme = $wm->get_wme($id);
49             my $g = GraphViz->new(@graph_args); #edge=>{arrowhead=>'none'}
50              
51             #begin graph by adding first WME
52             $g->add_node( name => $wme->id, color => 'red' );
53             return _recurse( $wme, $depth, $g );
54             }
55              
56             #recursively create GraphViz object
57             sub _recurse {
58             my ( $wme, $depth, $g ) = @_;
59              
60             #counter is used to prevent nodes from having the same name. Should work as long as
61             #the number of nodes with the same name doesn't exceed your machine's integer size.
62             state $counter = 0;
63              
64             #base case: depth is 0
65             return if !$depth;
66             $depth--;
67              
68             #iterate attributes and their values
69             for my $att ( @{ $wme->atts } ) {
70             for my $val ( @{ $wme->vals($att) } ) {
71             if ( ref $val eq 'Soar::WM::Element' ) {
72              
73             # print "edge from " . $wme->id . " to " . $val->id . " named $att\n";
74             #add an edge from parent to att value; label edge with att name
75             $g->add_edge( $wme->id => $val->id, label => $att );
76             _recurse( $val, $depth, $g );
77             }
78             else {
79             #add value as a node, making sure its name is unique.
80             my $val_node_name = $val . $counter;
81             $counter++;
82             $g->add_node( label => $val, name => $val_node_name );
83             $g->add_edge( $wme->id => $val_node_name, label => $att );
84             }
85             }
86             }
87             return $g;
88             }
89              
90             1;
91              
92             __END__